Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
2e7144b
docs(cli): design and implementation plan for the command line and skill
Maxaubert Oct 4, 2026
56f75fd
build(cli): second main entry out/main/cli.js with build-time version
Maxaubert Oct 4, 2026
9657ffd
refactor(engine): EngineEnv provider replaces electron imports in the…
Maxaubert Oct 4, 2026
56d0031
feat(engine): dry-run output planners sharing the collision-safe naming
Maxaubert Oct 4, 2026
537637b
feat(engine): cross-process install locks, cancellable installers, li…
Maxaubert Oct 4, 2026
1eed787
feat(engine): explicit rembg install with a pinned model folder; AI r…
Maxaubert Oct 4, 2026
daeadb1
feat(cli): exit codes, JSON event schema v1 and the human reporter
Maxaubert Oct 4, 2026
4f53198
feat(cli): command catalog from the app's option catalogs, parser and…
Maxaubert Oct 4, 2026
30a2f23
feat(cli): map flag values to the app's job options with per-verb val…
Maxaubert Oct 4, 2026
0636310
feat(cli): input expansion with globs, folders, stdin and natural order
Maxaubert Oct 4, 2026
d13a92a
fix(cli): pin input paths with spaces, &, non-ASCII and flag-like names
Maxaubert Oct 4, 2026
3499e07
feat(cli): planner decides routing, skips and failures before any fil…
Maxaubert Oct 4, 2026
d2ba182
feat(cli): run file verbs end to end with events, exit codes and clea…
Maxaubert Oct 4, 2026
83725da
fix(cli): dry run and real run agree on an uncreatable --out, summary…
Maxaubert Oct 4, 2026
29a16fb
feat(cli): pdf merge, split, burst, extract-text, to-images, extract-…
Maxaubert Oct 4, 2026
31b2e40
feat(cli): filesmith generate with an output folder (current folder b…
Maxaubert Oct 4, 2026
f02ed59
fix(cli): generate refuses an uncreatable --out the same way in dry a…
Maxaubert Oct 4, 2026
45ba297
feat(cli): filesmith setup for removebg, pid, spandrel, comfy, genera…
Maxaubert Oct 4, 2026
a53c845
feat(cli): formats and doctor
Maxaubert Oct 4, 2026
8510a6b
feat(installer): filesmith on the per-user PATH with cmd and sh shims
Maxaubert Oct 4, 2026
1d5b2ba
feat(skill): Claude Code skill, filesmith skill install/status and a …
Maxaubert Oct 5, 2026
0eeb3a0
ci(release): verify the packed CLI shims, skill files and a one-file …
Maxaubert Oct 5, 2026
8419819
fix(cli): human output keeps a gap after long names, counts 1 file, n…
Maxaubert Oct 5, 2026
3f35c3a
fix(cli): human output replaces control characters in untrusted text
Maxaubert Oct 5, 2026
816e4cf
test(cli): pin 'Æble æøå.png' and ./-x.png, .\-x.png from the file's …
Maxaubert Oct 5, 2026
af5221d
fix(cli): a convert with nothing to run counts the skipped and failin…
Maxaubert Oct 5, 2026
696755b
feat(cli): generate reports the random seed it chose when --seed is o…
Maxaubert Oct 5, 2026
0fe1ef0
docs(cli): docs/cli.md and CLAUDE.md entry; bump to 0.6.0
Maxaubert Oct 5, 2026
5c0acf0
docs(skill): refresh the Settings > CLAUDE screenshot at 0.6.0
Maxaubert Oct 5, 2026
568e098
feat(engine): atomic outputs, written to a part file and renamed on s…
Maxaubert Oct 5, 2026
78b1903
fix(cli): Ctrl+C cancels in the installed build; a watchdog cleans up…
Maxaubert Oct 5, 2026
11a2805
docs(cli): Ctrl+C, unfinished outputs and the redirected-output limit
Maxaubert Oct 5, 2026
606064e
Merge remote-tracking branch 'origin/main' into feat/30-cli
Maxaubert Oct 5, 2026
8c805cd
test(cli): give the path.ps1 registry test room on slow CI runners
Maxaubert Oct 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# The sh shim must keep LF or Git Bash fails with "$'\r': command not found".
resources/cli/filesmith text eol=lf
resources/cli/*.cmd text eol=crlf
resources/cli/*.ps1 text eol=crlf
# Skill files (Task 17): their frontmatter is matched with LF in tests.
resources/skill/** text eol=lf
29 changes: 29 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ on:
- 'scripts/verify-bundle.mjs'
- 'electron-builder.yml'
- 'package.json'
- 'src/cli/**'
- 'resources/cli/**'
- 'resources/skill/**'
- 'build/installer.nsh'
- 'build/installer/**'
workflow_dispatch:

concurrency:
Expand Down Expand Up @@ -110,6 +115,30 @@ jobs:
- name: Verify bundled tools (packed app)
run: node scripts/verify-bundle.mjs dist/win-unpacked/resources --manifest dist/packed-manifest.txt

# The command line in the packed app (spec 7.3): the shim must run the
# app's own exe in Node mode, print the package.json version, pass
# doctor's core checks and convert one file. This also fails loudly if the
# runAsNode Electron fuse is ever turned off.
- name: Smoke-test the packed CLI
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$PSNativeCommandUseErrorActionPreference = $false
$shim = 'dist\win-unpacked\resources\cli\filesmith.cmd'
$v = (& $shim --version | Out-String).Trim()
if ($v -ne '${{ steps.ver.outputs.version }}') { throw "filesmith --version printed '$v'" }
$env:FILESMITH_USER_DATA = Join-Path $env:RUNNER_TEMP 'fs-ud'
$events = & $shim doctor --json | ForEach-Object { $_ | ConvertFrom-Json }
$bad = @($events | Where-Object { $_.event -eq 'check' -and $_.group -eq 'core' -and $_.status -eq 'fail' })
if ($bad.Count) { throw "doctor core checks failed: $($bad.id -join ', ')" }
$png = Join-Path $env:RUNNER_TEMP 'smoke.png'
[IO.File]::WriteAllBytes($png, [Convert]::FromBase64String('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=='))
& $shim convert $png --to webp --json | Out-Host
if ($LASTEXITCODE -ne 0) { throw "filesmith convert exited $LASTEXITCODE" }
if (-not (Test-Path (Join-Path $env:RUNNER_TEMP 'smoke.webp'))) { throw 'smoke.webp was not written' }
"- packed CLI ``$v``: doctor core checks ok, convert ok" >> $env:GITHUB_STEP_SUMMARY
$global:LASTEXITCODE = 0

- name: Check installer
id: exe
shell: pwsh
Expand Down
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ docs/mockups
# The NSIS setup kit keeps its own (Prism-derived) formatting.
build/installer
patches

# Design and plan documents: their code blocks are partial snippets, not code.
docs/superpowers
5 changes: 5 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ src/
views/ (Generate, Tools, Completed, Settings), ui/ (primitives), icons/, theme/ (tokens, CSS)
shared/ types.ts — Job, ToolId, FileKind, Options, progress events
tabs.ts — the VERB-first navigation model (rail tabs + Tools cards)
cli/ the filesmith command line: parse, plan, run, report
resources/bin/ bundled CLI binaries (gitignored; fetched by scripts, packed by electron-builder)
resources/cli/ PATH shims + path.ps1
resources/skill/ Claude Code skill
```

## Build, test, run
Expand All @@ -80,6 +83,8 @@ resources/bin/ bundled CLI binaries (gitignored; fetched by scripts, packed by e
- `npm run package` — electron-vite build + electron-builder NSIS installer to `dist/`.
- `npm run test:e2e` — Playwright end-to-end (launches the built app via `_electron`; run
`npm run build` first). Covers the preload/IPC/engine chain unit tests can't reach.
- `npm run cli -- <args>` runs the command line from `out/main/cli.js` (build first). Reference:
`docs/cli.md`. The engine never imports `electron`; it reads paths from `src/main/env.ts`.
- Releases: push to main runs `.github/workflows/release.yml` (gates, then requires a NEW
`package.json` version, builds with `fetch-binaries --pinned`, publishes `v<version>`). Bump the
version in the PR. Tool versions + SHA-256 live in `scripts/pinned-tools.mjs`; PRs touching
Expand Down
13 changes: 13 additions & 0 deletions build/installer.nsh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@
ManifestDPIAware true
!macroend

; PATH entry for the command line (spec 7.2). Outside the guard: the uninstaller
; compiles from this file and removes the entry.
!include "installer\path.nsh"

; Uninstall drops the PATH entry, except during an update: electron-builder
; runs the old uninstaller with --updated, and the new version re-adds the same
; folder at once, so the entry should not blink out in between.
!macro customUnInstall
${ifNot} ${isUpdated}
!insertmacro filesmithPathRemove
${endIf}
!macroend

; The uninstaller is compiled from this same script with BUILD_UNINSTALLER set,
; and it has none of these pages. Without the guard its pass would resize a
; window it never draws, and warn about every function it does not call.
Expand Down
2 changes: 2 additions & 0 deletions build/installer/pages.nsh
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@

; When the section ends, autoclose walks on to the finish page by itself: the
; Next button it would otherwise wait for has been hidden since .onGUIInit.
; Before that, put the command line on the per-user PATH (path.nsh).
!macro customInstall
!insertmacro filesmithPathAdd
SetAutoClose true
!macroend

Expand Down
21 changes: 21 additions & 0 deletions build/installer/path.nsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
;
; Filesmith on the per-user PATH (spec 7.2). The edit itself is path.ps1, which
; keeps REG_EXPAND_SZ and never truncates a long PATH. Only resources\cli goes
; on PATH: putting resources\bin there would shadow the user's own ffmpeg,
; magick and 7z.
;
!include "WinMessages.nsh"

!define FILESMITH_PS '"$SYSDIR\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "$INSTDIR\resources\cli\path.ps1"'

!macro filesmithPathAdd
nsExec::ExecToLog '${FILESMITH_PS} add "$INSTDIR\resources\cli"'
Pop $0
SendMessage ${HWND_BROADCAST} ${WM_SETTINGCHANGE} 0 "STR:Environment" /TIMEOUT=5000
!macroend

!macro filesmithPathRemove
nsExec::ExecToLog '${FILESMITH_PS} remove "$INSTDIR\resources\cli"'
Pop $0
SendMessage ${HWND_BROADCAST} ${WM_SETTINGCHANGE} 0 "STR:Environment" /TIMEOUT=5000
!macroend
Loading
Loading