gemiterm 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/README.md +32 -9
  2. package/package.json +13 -4
  3. package/src/cli/command-registry.ts +66 -62
  4. package/src/cli/commands/install-skills-command.ts +35 -0
  5. package/src/cli/index.ts +19 -44
  6. package/src/infrastructure/cli-parser.ts +77 -0
  7. package/src/services/skills-cli-driver.ts +58 -0
  8. package/src/services/skills-service.ts +52 -0
  9. package/.env +0 -2
  10. package/.github/workflows/manual-release.yml +0 -34
  11. package/.github/workflows/release.yml +0 -198
  12. package/.github/workflows/test.yml +0 -29
  13. package/.opencode/commands/opsx-apply.md +0 -152
  14. package/.opencode/commands/opsx-archive.md +0 -157
  15. package/.opencode/commands/opsx-explore.md +0 -169
  16. package/.opencode/commands/opsx-propose.md +0 -104
  17. package/.opencode/commands/opsx-sync.md +0 -140
  18. package/.opencode/commands/search-intelligently.md +0 -9
  19. package/.opencode/opencode.json +0 -20
  20. package/.opencode/skills/intellisearch/.version +0 -1
  21. package/.opencode/skills/intellisearch/SKILL.md +0 -84
  22. package/.opencode/skills/intellisearch/references/brave-search.md +0 -67
  23. package/.opencode/skills/intellisearch/references/ddg-search.md +0 -79
  24. package/.opencode/skills/intellisearch/references/deepwiki-tools.md +0 -246
  25. package/.opencode/skills/intellisearch/references/examples.md +0 -97
  26. package/.opencode/skills/intellisearch/references/gh-cli.md +0 -143
  27. package/.opencode/skills/intellisearch/references/google-search.md +0 -40
  28. package/.opencode/skills/intellisearch/references/search-workflow.md +0 -143
  29. package/.opencode/skills/intellisearch/references/workflow.md +0 -115
  30. package/.opencode/skills/openspec-apply-change/SKILL.md +0 -159
  31. package/.opencode/skills/openspec-archive-change/SKILL.md +0 -117
  32. package/.opencode/skills/openspec-explore/SKILL.md +0 -287
  33. package/.opencode/skills/openspec-propose/SKILL.md +0 -111
  34. package/.opencode/skills/openspec-sync-specs/SKILL.md +0 -147
  35. package/.serena/project.yml +0 -133
  36. package/AGENTS.md +0 -136
  37. package/CHANGELOG.md +0 -3
  38. package/bun.lock +0 -105
  39. package/docs/INSTALL.md +0 -185
  40. package/docs/PLAYWRIGHT_CLI_API.md +0 -306
  41. package/docs/python-release-for-reference.md +0 -175
  42. package/examples/sample_json_export.json +0 -32
  43. package/examples/sample_markdown_export.md +0 -221
  44. package/examples/usage_examples.sh +0 -222
  45. package/install.ps1 +0 -264
  46. package/install.sh +0 -275
  47. package/openspec/MAESTRO_MIGRATION.md +0 -180
  48. package/openspec/changes/archive/2026-06-08-command-spec-conformance/.openspec.yaml +0 -2
  49. package/openspec/changes/archive/2026-06-08-command-spec-conformance/design.md +0 -194
  50. package/openspec/changes/archive/2026-06-08-command-spec-conformance/proposal.md +0 -52
  51. package/openspec/changes/archive/2026-06-08-command-spec-conformance/specs/multi-profile-conversations/spec.md +0 -133
  52. package/openspec/changes/archive/2026-06-08-command-spec-conformance/tasks.md +0 -95
  53. package/openspec/changes/archive/2026-06-09-refactor-interactive-prompt-loop/.openspec.yaml +0 -2
  54. package/openspec/changes/archive/2026-06-09-refactor-interactive-prompt-loop/design.md +0 -28
  55. package/openspec/changes/archive/2026-06-09-refactor-interactive-prompt-loop/proposal.md +0 -22
  56. package/openspec/changes/archive/2026-06-09-refactor-interactive-prompt-loop/specs/interactive-prompt-loop/spec.md +0 -35
  57. package/openspec/changes/archive/2026-06-09-refactor-interactive-prompt-loop/tasks.md +0 -28
  58. package/openspec/changes/archive/2026-06-09-replace-gemini-api-placeholder-with-gemini-reverse/.openspec.yaml +0 -2
  59. package/openspec/changes/archive/2026-06-09-replace-gemini-api-placeholder-with-gemini-reverse/design.md +0 -323
  60. package/openspec/changes/archive/2026-06-09-replace-gemini-api-placeholder-with-gemini-reverse/proposal.md +0 -122
  61. package/openspec/changes/archive/2026-06-09-replace-gemini-api-placeholder-with-gemini-reverse/specs/no-capability-changes/spec.md +0 -29
  62. package/openspec/changes/archive/2026-06-09-replace-gemini-api-placeholder-with-gemini-reverse/tasks.md +0 -172
  63. package/openspec/changes/archive/2026-06-09-v2-install-migration/.openspec.yaml +0 -2
  64. package/openspec/changes/archive/2026-06-09-v2-install-migration/design.md +0 -241
  65. package/openspec/changes/archive/2026-06-09-v2-install-migration/proposal.md +0 -41
  66. package/openspec/changes/archive/2026-06-09-v2-install-migration/specs/v2-installer/spec.md +0 -205
  67. package/openspec/changes/archive/2026-06-09-v2-install-migration/tasks.md +0 -97
  68. package/openspec/changes/commander-cli-parser/.openspec.yaml +0 -2
  69. package/openspec/changes/commander-cli-parser/design.md +0 -107
  70. package/openspec/changes/commander-cli-parser/proposal.md +0 -31
  71. package/openspec/changes/commander-cli-parser/specs/cli/spec.md +0 -48
  72. package/openspec/changes/commander-cli-parser/tasks.md +0 -33
  73. package/openspec/changes/cross-platform-build-and-ci/.openspec.yaml +0 -2
  74. package/openspec/changes/cross-platform-build-and-ci/design.md +0 -213
  75. package/openspec/changes/cross-platform-build-and-ci/proposal.md +0 -50
  76. package/openspec/changes/cross-platform-build-and-ci/specs/path-and-file-mediation/spec.md +0 -335
  77. package/openspec/changes/cross-platform-build-and-ci/specs/release-pipeline/spec.md +0 -95
  78. package/openspec/changes/cross-platform-build-and-ci/tasks.md +0 -164
  79. package/openspec/config.yaml +0 -20
  80. package/openspec/specs/auth/spec.md +0 -394
  81. package/openspec/specs/cli/spec.md +0 -108
  82. package/openspec/specs/commands/spec.md +0 -409
  83. package/openspec/specs/configuration/spec.md +0 -130
  84. package/openspec/specs/conversations/spec.md +0 -139
  85. package/openspec/specs/domain-model/spec.md +0 -128
  86. package/openspec/specs/formatters/spec.md +0 -158
  87. package/openspec/specs/install-browser/spec.md +0 -146
  88. package/openspec/specs/interactive-prompt-loop/spec.md +0 -41
  89. package/openspec/specs/logger/spec.md +0 -71
  90. package/openspec/specs/mediator/spec.md +0 -72
  91. package/openspec/specs/multi-profile-conversations/spec.md +0 -145
  92. package/openspec/specs/path-utils/spec.md +0 -101
  93. package/openspec/specs/profiles/spec.md +0 -94
  94. package/openspec/specs/storage/spec.md +0 -198
  95. package/openspec/specs/testing/spec.md +0 -237
  96. package/openspec/specs/v2-installer/spec.md +0 -205
  97. package/openspec/specs/validators/spec.md +0 -65
  98. package/scripts/build.ts +0 -88
  99. package/scripts/clean-build.ps1 +0 -4
  100. package/scripts/clean-build.sh +0 -5
  101. package/scripts/install-browser.ps1 +0 -5
  102. package/scripts/install-browser.sh +0 -6
  103. package/scripts/lint-path-mediation.ps1 +0 -65
  104. package/scripts/lint-path-mediation.sh +0 -40
  105. package/tests/cli/auth-command.test.ts +0 -220
  106. package/tests/cli/command-registry.test.ts +0 -67
  107. package/tests/cli/continue-command.test.ts +0 -100
  108. package/tests/cli/delete-command.test.ts +0 -128
  109. package/tests/cli/export-all-command.test.ts +0 -375
  110. package/tests/cli/install-browser-command.test.ts +0 -48
  111. package/tests/cli/list-command.test.ts +0 -179
  112. package/tests/cli/new-command.test.ts +0 -139
  113. package/tests/cli/status-command.test.ts +0 -106
  114. package/tests/core/query-handlers.test.ts +0 -187
  115. package/tests/fixtures/auth-fixtures.ts +0 -98
  116. package/tests/fixtures/chat-fixtures.ts +0 -87
  117. package/tests/infrastructure/cli-table.test.ts +0 -107
  118. package/tests/infrastructure/formatters.test.ts +0 -202
  119. package/tests/infrastructure/io.test.ts +0 -197
  120. package/tests/infrastructure/logger.test.ts +0 -74
  121. package/tests/infrastructure/storage.test.ts +0 -335
  122. package/tests/infrastructure/validators.test.ts +0 -67
  123. package/tests/integration/commands/auth.test.ts +0 -186
  124. package/tests/integration/commands/continue.test.ts +0 -130
  125. package/tests/integration/commands/delete.test.ts +0 -140
  126. package/tests/integration/commands/export.test.ts +0 -266
  127. package/tests/integration/commands/fetch.test.ts +0 -238
  128. package/tests/integration/commands/list.test.ts +0 -281
  129. package/tests/integration/commands/profile.test.ts +0 -258
  130. package/tests/integration/commands/status.test.ts +0 -223
  131. package/tests/integration/installer-script-shape.test.ts +0 -67
  132. package/tests/parity/compare-outputs.ts +0 -359
  133. package/tests/parity/test-commands-parity.ps1 +0 -82
  134. package/tests/parity/test-commands-parity.sh +0 -61
  135. package/tests/services/auth-service.test.ts +0 -315
  136. package/tests/services/cookie-monitor.test.ts +0 -229
  137. package/tests/services/cookie-storage-service.test.ts +0 -230
  138. package/tests/services/gemini-client-wrapper.test.ts +0 -688
  139. package/tests/services/install-browser-service.test.ts +0 -83
  140. package/tests/services/playwright-cli-driver.test.ts +0 -468
  141. package/tests/services/profile-auth-manager.test.ts +0 -334
  142. package/tests/services/profile-service.test.ts +0 -224
  143. package/tests/setup.ts +0 -95
  144. package/tests/smoke/smoke.test.ts +0 -52
  145. package/tests/unit/config.test.ts +0 -159
  146. package/tests/unit/errors.test.ts +0 -119
  147. package/tests/unit/formatters.test.ts +0 -316
  148. package/tests/unit/mediator.test.ts +0 -152
  149. package/tests/unit/path-utils.test.ts +0 -289
  150. package/tsconfig.json +0 -16
@@ -1,95 +0,0 @@
1
- ## ADDED Requirements
2
-
3
- ### Requirement: CI Test Runs On Pull Requests And Pushes To Main
4
- The system MUST run a test job on every `pull_request` event and every `push` to the `main` branch. The test job MUST run on `ubuntu-latest` and MUST execute `bun install --frozen-lockfile`, `bun test`, `bun run typecheck`, and a path-and-file mediation lint check in that order. A failure in any of these commands MUST cause the test job to exit non-zero and fail the workflow.
5
-
6
- #### Scenario: Pull request to main runs the test job
7
- - **WHEN** a contributor opens a pull request targeting `main`
8
- - **THEN** the `test` workflow runs on `ubuntu-latest`, executes `bun install --frozen-lockfile`, `bun test`, `bun run typecheck`, and the path-and-file mediation lint check, and the pull request cannot be merged while the test job is failing
9
-
10
- #### Scenario: Push to main runs the test job
11
- - **WHEN** commits are pushed directly to the `main` branch
12
- - **THEN** the `test` workflow runs the same commands on `ubuntu-latest` and reports success or failure based on their exit codes
13
-
14
- #### Scenario: Test failure fails CI
15
- - **WHEN** `bun test` exits with a non-zero status (one or more test cases fail)
16
- - **THEN** the test job exits non-zero and the workflow is marked as failed
17
-
18
- #### Scenario: Typecheck failure fails CI
19
- - **WHEN** `bun run typecheck` exits with a non-zero status (TypeScript reports one or more errors)
20
- - **THEN** the test job exits non-zero and the workflow is marked as failed
21
-
22
- #### Scenario: Path-and-file mediation lint failure fails CI
23
- - **WHEN** the lint check finds a `node:fs`, `node:path`, or `node:os` import in `src/` outside the allowed exemptions (see the `path-and-file-mediation` spec)
24
- - **THEN** the test job exits non-zero and the workflow is marked as failed
25
-
26
- ### Requirement: Cross-Platform Build On Version Tags
27
- The system MUST run a cross-platform build matrix whenever a tag matching the pattern `v*` is pushed, and MUST also run on `workflow_dispatch` for manual builds. The build matrix MUST include at least three jobs: `build-linux` on `ubuntu-latest`, `build-windows` on `windows-latest`, and `build-wsl` on `ubuntu-latest` producing a Linux x64 binary suitable for WSL. Each job MUST verify the build artifact exists, report its size, and upload it via `actions/upload-artifact@v4`.
28
-
29
- #### Scenario: Tag push triggers the build matrix
30
- - **WHEN** a tag matching `v*` (for example `v2.0.0`) is pushed to the repository
31
- - **THEN** the build workflow runs the `build-linux`, `build-windows`, and `build-wsl` jobs in parallel, each verifies its artifact, and each uploads the artifact via `actions/upload-artifact@v4`
32
-
33
- #### Scenario: Manual workflow dispatch accepts a custom tag
34
- - **WHEN** a maintainer triggers the build workflow via `workflow_dispatch` and supplies a `tag` input (for example `v2.0.1-rc.1`)
35
- - **THEN** the build jobs use the supplied tag value to name the output binary (for example `gemiterm-v2.0.1-rc.1`) instead of the event ref name
36
-
37
- #### Scenario: Missing build artifact fails the job
38
- - **WHEN** a build job completes but the expected binary file (for example `dist/GemiTerm` or `dist/GemiTerm.exe`) does not exist
39
- - **THEN** the build job exits non-zero and the workflow is marked as failed
40
-
41
- ### Requirement: GitHub Release Published On Version Tags
42
- The system MUST publish a GitHub Release whenever a tag matching the pattern `v*` is pushed. The release job MUST depend on (chain after) both the test and build jobs. The release MUST use `softprops/action-gh-release@v2` with auto-generated release notes, and MUST attach the `GemiTerm` (Linux) binary, the `GemiTerm.exe` (Windows) binary, the `install.sh` script, and the `install.ps1` script as release assets.
43
-
44
- #### Scenario: Tag push creates a GitHub release with all artifacts
45
- - **WHEN** a tag matching `v*` (for example `v2.0.0`) is pushed
46
- - **THEN** the test and build jobs run first, and on success the release job creates a GitHub Release named after the tag, with auto-generated release notes, attaching `GemiTerm`, `GemiTerm.exe`, `install.sh`, and `install.ps1` as release assets
47
-
48
- #### Scenario: Failed build skips the release
49
- - **WHEN** any of the upstream build jobs (test, build-linux, build-windows, or build-wsl) fails
50
- - **THEN** the release job is skipped and no GitHub Release is created
51
-
52
- ### Requirement: Build Scripts Produce A Working Executable On Bun 1.3.x
53
- The system MUST provide `bun run build`, `bun run build:linux`, `bun run build:windows`, and `bun run build:release` scripts in `package.json`. Each script MUST produce a single-file executable on Bun 1.3.x when invoked from the repository root. The output binary MUST be executable and MUST report its version when invoked with `--version`.
54
-
55
- #### Scenario: `bun run build` produces a working executable
56
- - **WHEN** a developer runs `bun run build` on Bun 1.3.x
57
- - **THEN** a single-file binary is produced (at `dist/gemiterm` on Linux/macOS or `dist/gemiterm.exe` on Windows) and invoking that binary with `--version` exits 0 and prints the version string
58
-
59
- #### Scenario: `bun run build:linux` produces an x64 Linux binary
60
- - **WHEN** a developer runs `bun run build:linux` on Bun 1.3.x
61
- - **THEN** a Linux x64 single-file binary is produced at `dist/gemiterm` and the binary is executable on a Linux x64 system
62
-
63
- #### Scenario: `bun run build:windows` produces an x64 Windows binary
64
- - **WHEN** a developer runs `bun run build:windows` on Bun 1.3.x
65
- - **THEN** a Windows x64 single-file binary is produced at `dist/gemiterm.exe` and the binary is executable on a Windows x64 system
66
-
67
- #### Scenario: `bun run build:release` produces a non-debug binary
68
- - **WHEN** a developer runs `bun run build:release` on Bun 1.3.x
69
- - **THEN** a minified, single-file binary is produced (without debug symbols or source maps) suitable for distribution
70
-
71
- ### Requirement: Clean Build Scripts Available For Both Platforms
72
- The system MUST provide `scripts/clean-build.sh` (POSIX shell) and `scripts/clean-build.ps1` (PowerShell) that remove the `dist/` directory and any other transient build artifacts. Each script MUST exit 0 on success and non-zero on error.
73
-
74
- #### Scenario: POSIX clean-build removes dist and exits 0
75
- - **WHEN** a developer runs `bash scripts/clean-build.sh` on a POSIX system
76
- - **THEN** the `dist/` directory is removed (or stays removed if already absent) and the script exits 0
77
-
78
- #### Scenario: PowerShell clean-build removes dist and exits 0
79
- - **WHEN** a developer runs `pwsh scripts/clean-build.ps1` on a system with PowerShell Core installed
80
- - **THEN** the `dist/` directory is removed (or stays removed if already absent) and the script exits 0
81
-
82
- ### Requirement: User-Facing Browser Install Scripts
83
- The system MUST provide `scripts/install-browser.sh` (POSIX shell) and `scripts/install-browser.ps1` (PowerShell) that run `bunx @playwright/cli install chromium` and verify the installation succeeded. Each script MUST exit 0 on success and non-zero on error. If Chromium is already installed, the script MUST exit 0 without re-downloading.
84
-
85
- #### Scenario: POSIX install-browser runs the install command
86
- - **WHEN** a developer runs `bash scripts/install-browser.sh` on a POSIX system with Bun installed
87
- - **THEN** the script invokes `bunx @playwright/cli install chromium`, reports success to stdout, and exits 0
88
-
89
- #### Scenario: PowerShell install-browser runs the install command
90
- - **WHEN** a developer runs `pwsh scripts/install-browser.ps1` on a system with Bun and PowerShell Core installed
91
- - **THEN** the script invokes `bunx @playwright/cli install chromium`, reports success to stdout, and exits 0
92
-
93
- #### Scenario: Browser already installed is a no-op
94
- - **WHEN** a developer runs either install-browser script and Chromium is already installed
95
- - **THEN** the script exits 0 without re-downloading Chromium and without printing an error
@@ -1,164 +0,0 @@
1
- ## 1. Extend path-utils.ts with the WSL detection, project root, and package.json helpers
2
-
3
- - [x] 1.1 Add `isWSL(): boolean` to `src/infrastructure/path-utils.ts`. The function MUST gate on `process.platform === 'linux'` and return `true` if `/proc/version` exists and contains "microsoft" or "WSL" (case-insensitive) OR `WSL_DISTRO_NAME` is set and non-empty. The function MUST return `false` on any other platform.
4
- - [x] 1.2 Add `getProjectRoot(): string` to `src/infrastructure/path-utils.ts`. The function MUST walk up from `import.meta.url` (passed in as a parameter) until it finds a directory containing `package.json`, and return the absolute path. Replaces the `__filename` / `__dirname` dance in `src/cli/index.ts:33-35`.
5
- - [x] 1.3 Add `getPackageJson(): { name: string; version: string; [key: string]: unknown }` to `src/infrastructure/path-utils.ts`. The function MUST call `getProjectRoot()` + `readTextFile(<root>/package.json)` + `JSON.parse` and return the parsed object. On any error, the function MUST return `{ name: "gemiterm", version: "unknown" }` (the fallback keeps `--version` working in a broken environment).
6
-
7
- ## 2. Create src/infrastructure/io.ts
8
-
9
- - [x] 2.1 Create `src/infrastructure/io.ts` with the following exports. Each function MUST be a thin wrapper around the equivalent `node:fs` call with consistent semantics (always-recursive `mkdir`, safe returns, clear error messages).
10
- - `ensureDir(path: string): void` — wraps `mkdirSync(path, { recursive: true })`. Throws on EACCES / EPERM with the absolute path in the error message.
11
- - `existsFile(path: string): boolean` — wraps `existsSync`.
12
- - `readTextFile(path: string): string` — wraps `readFileSync(path, 'utf-8')`. Throws a typed `IOError` with the absolute path on ENOENT / EACCES.
13
- - `safeReadTextFile(path: string): string` — wraps `readFileSync` in a try/catch; returns `""` on any error (ENOENT, EACCES, EISDIR). Includes a code comment that the `""` return conflates "file does not exist" with "file exists but is empty" and is appropriate for callers that only need a string for `.includes()` / `.trim()` checks.
14
- - `writeTextFile(path: string, content: string): void` — resolves the path, ensures the parent directory exists (recursively), and writes `content` as UTF-8. Throws a typed `IOError` on EACCES / ENOSPC.
15
- - `readJsonFile<T>(path: string): T` — wraps `readTextFile` + `JSON.parse`. Throws a typed `IOError` with a `cause` field on parse failure.
16
- - `writeJsonFile(path: string, data: unknown): void` — wraps `writeTextFile` with `JSON.stringify(data, null, 2)`.
17
- - `removeDir(path: string): void` — wraps `rmSync(path, { recursive: true, force: true })`. No-op if the path does not exist.
18
- - `renameDir(src: string, dest: string): void` — wraps `renameSync`. Throws on ENOENT (source missing) or EEXIST (dest present) with a clear message.
19
- - `isDirectory(path: string): boolean` — wraps `statSync(path).isDirectory()` in a try/catch; returns `false` if the path does not exist.
20
- - `listSubdirectories(path: string): string[]` — wraps `readdirSync` + filter to directories only. Returns `[]` if the path does not exist.
21
- - [x] 2.2 Define and export a class `IOError extends Error` with a `cause` field for the original error (when applicable). All other functions throw `IOError` instead of raw `Error`.
22
- - [x] 2.3 Add a JSDoc comment at the top of `io.ts` stating the rule: "This is the canonical home for file-system access in `src/`. No other source file in `src/` may import from `node:fs` or `node:path` directly. The only allowed exception is `src/services/install-browser-service.ts`, which may use `node:path` for the WSL mount parser."
23
-
24
- ## 3. Refactor install-browser-service.ts to use the shared platform module and io.ts
25
-
26
- - [x] 3.1 In `src/services/install-browser-service.ts`:
27
- - Delete the private `isWsl()` method (lines 143-149) and the `readFileSafe` helper (lines 173-179).
28
- - Add imports: `import { isWSL } from "../infrastructure/path-utils.ts";` and `import { existsFile, safeReadTextFile } from "../infrastructure/io.ts";`.
29
- - Replace every call to `this.isWsl()` with `isWSL()`.
30
- - Replace every call to `this.readFileSafe(path)` with `safeReadTextFile(path)`.
31
- - Replace every call to `existsSync(path)` with `existsFile(path)`.
32
- - Unify `getEdgePaths()` and `getChromePaths()` (lines 84-100) behind a single local helper `getWindowsKnownDirs()` that returns `{ programFiles, localAppData }`. The two methods collapse to ~5 lines each.
33
- - [x] 3.2 Verify the behavior is preserved. The dispatch logic in `findSystemBrowser` (lines 57-65) and `findLinuxBrowser` (lines 102-122) MUST continue to behave identically. The 5 unit tests in `tests/services/install-browser-service.test.ts` are the regression gate; the 432/432 baseline MUST be preserved.
34
- - [x] 3.3 Do NOT touch `src/services/playwright-cli-driver.ts`, `src/services/cookie-monitor.ts`, or `src/services/auth-service.ts` in this task — they are sensitive and out of scope.
35
-
36
- ## 4. Refactor storage.ts and config.ts to use io.ts
37
-
38
- - [x] 4.1 In `src/infrastructure/storage.ts`:
39
- - Drop the `node:fs` import (lines 1-8).
40
- - Add `import { ensureDir, existsFile, readJsonFile, writeTextFile, removeDir, renameDir } from "./io.ts";`.
41
- - `CookieStorage.save()` (lines 52-58) → use `writeTextFile(filePath, JSON.stringify(state, null, 2))`.
42
- - `CookieStorage.load()` (lines 60-70) → use `readJsonFile<StorageState>(filePath)`.
43
- - `CookieStorage.delete()` (lines 72-77) → use `removeDir(dir)`.
44
- - `ProfileManager.create()` (lines 91-101) → use `existsFile` + `ensureDir`.
45
- - `ProfileManager.delete()` (lines 103-116) → use `removeDir` + `existsFile` + `removeFile` (or `rmSync` on the marker).
46
- - `ProfileManager.rename()` (lines 118-131) → use `existsFile` + `renameDir`.
47
- - `ProfileManager.setDefault()` (lines 133-138) → use `existsFile`.
48
- - `ProfileManager.getStatus()` (lines 148-184) → use `existsFile`.
49
- - [x] 4.2 In `src/infrastructure/config.ts`:
50
- - Drop the `node:fs` and `node:path` imports (lines 1-2).
51
- - Add `import { ensureDir, existsFile, readTextFile, writeTextFile, isDirectory, listSubdirectories } from "./io.ts";`.
52
- - Drop the alias re-exports from `path-utils.ts` (lines 3-9). Use the `path-utils.ts` exports directly.
53
- - `getDefaultProfileName()` (lines 23-29) → use `existsFile` + `readTextFile`. Fall back to `"default"` if the marker does not exist.
54
- - `setDefaultProfileName(name)` (lines 31-36) → use `writeTextFile` + `ensureDir(dirname(marker))`.
55
- - `listProfiles()` (lines 38-47) → use `listSubdirectories` + filter on `DEFAULT_PROFILE_MARKER`.
56
- - `ensureConfigDir()` (lines 49-54) → use `ensureDir`.
57
- - The file should shrink from 64 lines to ~40.
58
- - [x] 4.3 Run `bun test tests/infrastructure/storage.test.ts` and confirm all 21 tests still pass. The 432/432 baseline MUST be maintained across the full `bun test` run.
59
-
60
- ## 5. Refactor the 4 export commands to use writeTextFile
61
-
62
- - [x] 5.1 `src/cli/commands/export-command.ts:72-82` — replace the 3-line `resolve + dirname + mkdirSync + writeFileSync` block with a single `writeTextFile(outputPath, content)` call. Drop the `node:fs` and `node:path` imports.
63
- - [x] 5.2 `src/cli/commands/export-all-command.ts:79-80` — replace `mkdirSync(outputDir, { recursive: true })` with `ensureDir(outputDir)`. Drop the `node:fs` and `node:path` imports.
64
- - [x] 5.3 `src/cli/commands/export-all-command.ts:96-104` — replace the `writeFileSync(filePath, content, "utf-8")` with `writeTextFile(filePath, content)`.
65
- - [x] 5.4 `src/cli/commands/export-all-command.ts:177-178` (the `index.md` write) — replace with `writeTextFile(indexPath, lines.join("\n"))`. The `join(outputDir, "index.md")` and `join(outputDir, "index.md")` (line 192, the `console.log`) call remain — these are path operations, which `io.ts` does not own; if the file becomes path-only, keep `node:path` import here.
66
- - [x] 5.5 `src/cli/commands/list-command.ts:138-143` — replace the `writeOutput` method body with a single `writeTextFile(path, content)` call. Drop the `node:fs` and `node:path` imports.
67
- - [x] 5.6 `src/cli/commands/fetch-command.ts:121-127` — same as 5.5. Once both 5.5 and 5.6 are converted, they are 2-line methods. Keep the duplication; do not extract a shared helper.
68
- - [x] 5.7 Run the integration tests in `tests/integration/commands/{export,list,fetch}.test.ts` and `tests/cli/export-all-command.test.ts`; confirm they all still pass.
69
-
70
- ## 6. Refactor cli/index.ts to use getPackageJson()
71
-
72
- - [x] 6.1 `src/cli/index.ts:3-5` — drop the `node:url`, `node:path`, and `node:fs` imports.
73
- - [x] 6.2 `src/cli/index.ts:33-35` — replace with a single `const pkg = getPackageJson();` call. Add `import { getPackageJson } from "../infrastructure/path-utils.ts";`.
74
- - [x] 6.3 Verify the version printed by `--version` is still `2.0.0` (the value in `package.json`). The smoke test in `tests/smoke/smoke.test.ts` is the regression gate.
75
-
76
- ## 7. Add tests for the new helpers
77
-
78
- - [x] 7.1 Extend `tests/unit/path-utils.test.ts` with the following test groups:
79
- - `isWSL` — at minimum 4 tests: returns false on non-linux, returns true when `WSL_DISTRO_NAME` is set, returns true when `/proc/version` contains "microsoft" (mock the file), returns false on native Linux.
80
- - `getProjectRoot` — 2 tests: returns the absolute path of the repo root from `import.meta.url`; returns the same path when called twice (idempotent).
81
- - `getPackageJson` — 3 tests: returns the parsed `package.json` with `name === "gemiterm"` and `version === "2.0.0"`; returns the fallback when the `package.json` is missing; returns the fallback when the `package.json` is unparseable.
82
- - [x] 7.2 Create `tests/infrastructure/io.test.ts` with the following test groups (at least 15 tests total):
83
- - `ensureDir` — creates a directory recursively; creates parent directories that do not exist; throws on EACCES.
84
- - `existsFile` — returns true for existing files; returns false for missing files; returns false for directories.
85
- - `readTextFile` — reads an existing file; throws `IOError` on ENOENT.
86
- - `safeReadTextFile` — returns the file content; returns `""` on ENOENT; returns `""` on EACCES.
87
- - `writeTextFile` — writes a new file; writes to a nested directory that does not exist; throws `IOError` on EACCES.
88
- - `readJsonFile` / `writeJsonFile` — round-trip a JSON object; throws on parse failure.
89
- - `removeDir` — removes a directory recursively; no-op on missing path.
90
- - `renameDir` — renames a directory; throws when source is missing; throws when destination exists.
91
- - `isDirectory` — returns true for a directory; returns false for a file; returns false for a missing path.
92
- - `listSubdirectories` — returns only directories; returns `[]` for missing path; excludes the configured marker file.
93
- - [x] 7.3 The 19 existing `path-utils.test.ts` tests MUST continue to pass. The 5 existing `install-browser-service.test.ts` tests MUST continue to pass. The 21 `storage.test.ts` tests MUST continue to pass. The 432/432 baseline MUST be maintained across the full `bun test` run (plus the new tests).
94
-
95
- ## 8. Fix package.json build scripts
96
-
97
- - [x] 8.1 In `package.json:20-22`, change the three `build*` scripts to drop `--outdir` and use `--outfile dist/gemiterm` instead. Add a new `build:release` script that uses `--outfile dist/gemiterm --minify`. The four final scripts MUST be: `build`, `build:linux`, `build:windows`, `build:release`.
98
- - [x] 8.2 Run `bun run build` on the current host. Verify a single-file binary is produced at `dist/gemiterm` (Linux/macOS) or `dist/gemiterm.exe` (Windows). Run the binary with `--version` and confirm it exits 0 and prints `2.0.0`.
99
- - [x] 8.3 Run `bun run build:linux` and `bun run build:windows` and confirm the cross-compile produces a Linux binary (`dist/gemiterm`, no extension) and a Windows binary (`dist/gemiterm.exe`, with extension) respectively.
100
- - [x] 8.4 Run `bun run build:release` and confirm a minified binary is produced.
101
-
102
- ## 9. Add clean-build shell scripts
103
-
104
- - [x] 9.1 Create `scripts/clean-build.sh` (POSIX) that removes `dist/` and any other transient build artifacts. The script MUST `set -e` to fail on error and MUST exit 0 when the cleanup succeeds (including when `dist/` does not exist).
105
- - [x] 9.2 Create `scripts/clean-build.ps1` (PowerShell Core) that performs the same cleanup. The script MUST use `$ErrorActionPreference = 'Stop'` and exit 0 on success.
106
- - [x] 9.3 Run `bash scripts/clean-build.sh` and `pwsh scripts/clean-build.ps1` locally and confirm both exit 0 and remove `dist/`.
107
-
108
- ## 10. Add install-browser shell scripts
109
-
110
- - [x] 10.1 Create `scripts/install-browser.sh` (POSIX) that runs `bunx @playwright/cli install chromium`, reports the command being run, captures stdout/stderr, and exits 0 on success. If Chromium is already installed, the script MUST exit 0.
111
- - [x] 10.2 Create `scripts/install-browser.ps1` (PowerShell Core) that performs the same operation.
112
- - [x] 10.3 Run both scripts locally and confirm they exit 0.
113
-
114
- ## 11. Add .github/workflows/test.yml
115
-
116
- - [x] 11.1 Create `.github/workflows/test.yml` with `on:` triggers for `pull_request` (any branch) and `push` (branches: `[main]`). Add a single `test` job that runs on `ubuntu-latest` with steps: `actions/checkout@v4`, `oven-sh/setup-bun@v2` with `bun-version: 1.3.13`, `bun install --frozen-lockfile`, `bun test`, `bun run typecheck`, and a final `Enforce path-utils mediation` step that runs:
117
-
118
- ```bash
119
- ! grep -rn --include='*.ts' "from \"node:\\(fs\\|path\\|os\\)\"" src/ \
120
- | grep -v "src/infrastructure/path-utils.ts" \
121
- | grep -v "src/services/install-browser-service.ts" \
122
- | grep -q .
123
- ```
124
-
125
- This MUST exit 0 (success) when no forbidden imports are present. The check excludes `path-utils.ts` (the canonical home) and `install-browser-service.ts` (the only legitimate cross-platform `node:path` consumer, for the WSL mount parser).
126
- - [x] 11.2 Add a `concurrency:` block at the workflow top that cancels in-progress runs on the same ref.
127
- - [x] 11.3 Confirm the workflow file is valid YAML.
128
-
129
- ## 12. Add .github/workflows/build.yml
130
-
131
- - [x] 12.1 Create `.github/workflows/build.yml` with `on:` triggers for `push` (tags: `['v*']`) and `workflow_dispatch`. Add a `concurrency:` block that cancels in-progress tag builds.
132
- - [x] 12.2 Add a `build` job with `strategy.matrix.os: [ubuntu-latest, windows-latest]` and `strategy.matrix.target: [bun-linux-x64, bun-windows-x64]` plus an extra `build-wsl` job on `ubuntu-latest` producing a Linux x64 binary. Each job MUST checkout, setup Bun 1.3.13, run `bun install --frozen-lockfile`, run the platform-specific build script, verify the binary exists, report its size, and upload via `actions/upload-artifact@v4` with stable names (`GemiTerm` for Linux/WSL, `GemiTerm.exe` for Windows).
133
- - [x] 12.3 Add a size verification step that fails the job if the binary is less than 20 MB.
134
- - [x] 12.4 Confirm the workflow file is valid YAML.
135
-
136
- ## 13. Add .github/workflows/release.yml
137
-
138
- - [x] 13.1 Create `.github/workflows/release.yml` with `on:` triggers for `push` (tags: `['v*']`) only. Add a `release` job that depends on `test.yml/test` and `build.yml/build` via the `needs:` keyword using the cross-workflow `<workflow>/<job>` pattern.
139
- - [x] 13.2 Add a `permissions:` block with `contents: write` on the release job. Add steps: `actions/checkout@v4`, `actions/download-artifact@v4` (to fetch `GemiTerm`, `GemiTerm.exe`, `install.sh`, `install.ps1`), a "Determine tag" step, and `softprops/action-gh-release@v2` with `generate_release_notes: true`.
140
- - [x] 13.3 Confirm the workflow file is valid YAML.
141
-
142
- ## 14. Repo hygiene cleanup
143
-
144
- - [x] 14.1 Remove the orphan file literally named `{` at the repo root.
145
- - [x] 14.2 Edit `.gitignore:23` and remove the bare `{` line. Leave a blank line for readability.
146
- - [x] 14.3 In `package.json`, remove the `"commander": "^15.0.0"` entry. Run `bun install` to regenerate `bun.lock` and confirm `commander` is no longer listed.
147
- - [x] 14.4 Remove the `src/commands/` directory and its `.gitkeep` file.
148
- - [x] 14.5 Run `git status` and confirm only the intended files are staged.
149
-
150
- ## 15. README updates
151
-
152
- - [x] 15.1 Add a "Building from source" section to `README.md` after the "Development" section. Document the `bun run build`, `bun run build:linux`, `bun run build:windows`, and `bun run build:release` scripts and the resulting output paths. State the minimum Bun version as 1.3.13.
153
- - [x] 15.2 Add a "Release artifacts" section to `README.md` after "Building from source". Document that the v2.0.0 release ships `GemiTerm` (Linux x64), `GemiTerm.exe` (Windows x64), `install.sh`, and `install.ps1` as GitHub Release assets.
154
- - [x] 15.3 Update the "Development" section to mention `bun run typecheck` and the `scripts/install-browser.{sh,ps1}` wrappers.
155
-
156
- ## 16. Verification
157
-
158
- - [x] 16.1 Run `bun install --frozen-lockfile` to confirm the lockfile is in sync with `package.json` after the `commander` removal.
159
- - [x] 16.2 Run `bun test` and confirm the full suite passes (the original 432 tests + the new tests in tasks 7.1 and 7.2; the new baseline should be at least 462 tests).
160
- - [x] 16.3 Run `bun run typecheck` and confirm zero TypeScript errors. The new `io.ts` exports, the refactored call sites, and the new helpers in `path-utils.ts` MUST NOT introduce any new type errors.
161
- - [x] 16.4 Run `bun run build`, `bun run build:linux`, `bun run build:windows`, and `bun run build:release` and confirm all four produce working executables.
162
- - [x] 16.5 Run `bash scripts/clean-build.sh`, `pwsh scripts/clean-build.ps1`, `bash scripts/install-browser.sh`, and `pwsh scripts/install-browser.ps1` and confirm all four exit 0.
163
- - [ ] 16.6 Open a draft pull request to confirm the `test.yml` workflow runs against the change, including the lint check. The PR MUST NOT be merged until `test.yml` is green.
164
- - [ ] 16.7 Push a `v2.0.0-rc.1` tag to confirm `release.yml` work end-to-end. Delete the tag and the draft release after verification.
@@ -1,20 +0,0 @@
1
- schema: spec-driven
2
-
3
- # Project context (optional)
4
- # This is shown to AI when creating artifacts.
5
- # Add your tech stack, conventions, style guides, domain knowledge, etc.
6
- # Example:
7
- # context: |
8
- # Tech stack: TypeScript, React, Node.js
9
- # We use conventional commits
10
- # Domain: e-commerce platform
11
-
12
- # Per-artifact rules (optional)
13
- # Add custom rules for specific artifacts.
14
- # Example:
15
- # rules:
16
- # proposal:
17
- # - Keep proposals under 500 words
18
- # - Always include a "Non-goals" section
19
- # tasks:
20
- # - Break tasks into chunks of max 2 hours