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.
- package/README.md +32 -9
- package/package.json +13 -4
- package/src/cli/command-registry.ts +66 -62
- package/src/cli/commands/install-skills-command.ts +35 -0
- package/src/cli/index.ts +19 -44
- package/src/infrastructure/cli-parser.ts +77 -0
- package/src/services/skills-cli-driver.ts +58 -0
- package/src/services/skills-service.ts +52 -0
- package/.env +0 -2
- package/.github/workflows/manual-release.yml +0 -34
- package/.github/workflows/release.yml +0 -198
- package/.github/workflows/test.yml +0 -29
- package/.opencode/commands/opsx-apply.md +0 -152
- package/.opencode/commands/opsx-archive.md +0 -157
- package/.opencode/commands/opsx-explore.md +0 -169
- package/.opencode/commands/opsx-propose.md +0 -104
- package/.opencode/commands/opsx-sync.md +0 -140
- package/.opencode/commands/search-intelligently.md +0 -9
- package/.opencode/opencode.json +0 -20
- package/.opencode/skills/intellisearch/.version +0 -1
- package/.opencode/skills/intellisearch/SKILL.md +0 -84
- package/.opencode/skills/intellisearch/references/brave-search.md +0 -67
- package/.opencode/skills/intellisearch/references/ddg-search.md +0 -79
- package/.opencode/skills/intellisearch/references/deepwiki-tools.md +0 -246
- package/.opencode/skills/intellisearch/references/examples.md +0 -97
- package/.opencode/skills/intellisearch/references/gh-cli.md +0 -143
- package/.opencode/skills/intellisearch/references/google-search.md +0 -40
- package/.opencode/skills/intellisearch/references/search-workflow.md +0 -143
- package/.opencode/skills/intellisearch/references/workflow.md +0 -115
- package/.opencode/skills/openspec-apply-change/SKILL.md +0 -159
- package/.opencode/skills/openspec-archive-change/SKILL.md +0 -117
- package/.opencode/skills/openspec-explore/SKILL.md +0 -287
- package/.opencode/skills/openspec-propose/SKILL.md +0 -111
- package/.opencode/skills/openspec-sync-specs/SKILL.md +0 -147
- package/.serena/project.yml +0 -133
- package/AGENTS.md +0 -136
- package/CHANGELOG.md +0 -3
- package/bun.lock +0 -105
- package/docs/INSTALL.md +0 -185
- package/docs/PLAYWRIGHT_CLI_API.md +0 -306
- package/docs/python-release-for-reference.md +0 -175
- package/examples/sample_json_export.json +0 -32
- package/examples/sample_markdown_export.md +0 -221
- package/examples/usage_examples.sh +0 -222
- package/install.ps1 +0 -264
- package/install.sh +0 -275
- package/openspec/MAESTRO_MIGRATION.md +0 -180
- package/openspec/changes/archive/2026-06-08-command-spec-conformance/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-06-08-command-spec-conformance/design.md +0 -194
- package/openspec/changes/archive/2026-06-08-command-spec-conformance/proposal.md +0 -52
- package/openspec/changes/archive/2026-06-08-command-spec-conformance/specs/multi-profile-conversations/spec.md +0 -133
- package/openspec/changes/archive/2026-06-08-command-spec-conformance/tasks.md +0 -95
- package/openspec/changes/archive/2026-06-09-refactor-interactive-prompt-loop/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-06-09-refactor-interactive-prompt-loop/design.md +0 -28
- package/openspec/changes/archive/2026-06-09-refactor-interactive-prompt-loop/proposal.md +0 -22
- package/openspec/changes/archive/2026-06-09-refactor-interactive-prompt-loop/specs/interactive-prompt-loop/spec.md +0 -35
- package/openspec/changes/archive/2026-06-09-refactor-interactive-prompt-loop/tasks.md +0 -28
- package/openspec/changes/archive/2026-06-09-replace-gemini-api-placeholder-with-gemini-reverse/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-06-09-replace-gemini-api-placeholder-with-gemini-reverse/design.md +0 -323
- package/openspec/changes/archive/2026-06-09-replace-gemini-api-placeholder-with-gemini-reverse/proposal.md +0 -122
- package/openspec/changes/archive/2026-06-09-replace-gemini-api-placeholder-with-gemini-reverse/specs/no-capability-changes/spec.md +0 -29
- package/openspec/changes/archive/2026-06-09-replace-gemini-api-placeholder-with-gemini-reverse/tasks.md +0 -172
- package/openspec/changes/archive/2026-06-09-v2-install-migration/.openspec.yaml +0 -2
- package/openspec/changes/archive/2026-06-09-v2-install-migration/design.md +0 -241
- package/openspec/changes/archive/2026-06-09-v2-install-migration/proposal.md +0 -41
- package/openspec/changes/archive/2026-06-09-v2-install-migration/specs/v2-installer/spec.md +0 -205
- package/openspec/changes/archive/2026-06-09-v2-install-migration/tasks.md +0 -97
- package/openspec/changes/commander-cli-parser/.openspec.yaml +0 -2
- package/openspec/changes/commander-cli-parser/design.md +0 -107
- package/openspec/changes/commander-cli-parser/proposal.md +0 -31
- package/openspec/changes/commander-cli-parser/specs/cli/spec.md +0 -48
- package/openspec/changes/commander-cli-parser/tasks.md +0 -33
- package/openspec/changes/cross-platform-build-and-ci/.openspec.yaml +0 -2
- package/openspec/changes/cross-platform-build-and-ci/design.md +0 -213
- package/openspec/changes/cross-platform-build-and-ci/proposal.md +0 -50
- package/openspec/changes/cross-platform-build-and-ci/specs/path-and-file-mediation/spec.md +0 -335
- package/openspec/changes/cross-platform-build-and-ci/specs/release-pipeline/spec.md +0 -95
- package/openspec/changes/cross-platform-build-and-ci/tasks.md +0 -164
- package/openspec/config.yaml +0 -20
- package/openspec/specs/auth/spec.md +0 -394
- package/openspec/specs/cli/spec.md +0 -108
- package/openspec/specs/commands/spec.md +0 -409
- package/openspec/specs/configuration/spec.md +0 -130
- package/openspec/specs/conversations/spec.md +0 -139
- package/openspec/specs/domain-model/spec.md +0 -128
- package/openspec/specs/formatters/spec.md +0 -158
- package/openspec/specs/install-browser/spec.md +0 -146
- package/openspec/specs/interactive-prompt-loop/spec.md +0 -41
- package/openspec/specs/logger/spec.md +0 -71
- package/openspec/specs/mediator/spec.md +0 -72
- package/openspec/specs/multi-profile-conversations/spec.md +0 -145
- package/openspec/specs/path-utils/spec.md +0 -101
- package/openspec/specs/profiles/spec.md +0 -94
- package/openspec/specs/storage/spec.md +0 -198
- package/openspec/specs/testing/spec.md +0 -237
- package/openspec/specs/v2-installer/spec.md +0 -205
- package/openspec/specs/validators/spec.md +0 -65
- package/scripts/build.ts +0 -88
- package/scripts/clean-build.ps1 +0 -4
- package/scripts/clean-build.sh +0 -5
- package/scripts/install-browser.ps1 +0 -5
- package/scripts/install-browser.sh +0 -6
- package/scripts/lint-path-mediation.ps1 +0 -65
- package/scripts/lint-path-mediation.sh +0 -40
- package/tests/cli/auth-command.test.ts +0 -220
- package/tests/cli/command-registry.test.ts +0 -67
- package/tests/cli/continue-command.test.ts +0 -100
- package/tests/cli/delete-command.test.ts +0 -128
- package/tests/cli/export-all-command.test.ts +0 -375
- package/tests/cli/install-browser-command.test.ts +0 -48
- package/tests/cli/list-command.test.ts +0 -179
- package/tests/cli/new-command.test.ts +0 -139
- package/tests/cli/status-command.test.ts +0 -106
- package/tests/core/query-handlers.test.ts +0 -187
- package/tests/fixtures/auth-fixtures.ts +0 -98
- package/tests/fixtures/chat-fixtures.ts +0 -87
- package/tests/infrastructure/cli-table.test.ts +0 -107
- package/tests/infrastructure/formatters.test.ts +0 -202
- package/tests/infrastructure/io.test.ts +0 -197
- package/tests/infrastructure/logger.test.ts +0 -74
- package/tests/infrastructure/storage.test.ts +0 -335
- package/tests/infrastructure/validators.test.ts +0 -67
- package/tests/integration/commands/auth.test.ts +0 -186
- package/tests/integration/commands/continue.test.ts +0 -130
- package/tests/integration/commands/delete.test.ts +0 -140
- package/tests/integration/commands/export.test.ts +0 -266
- package/tests/integration/commands/fetch.test.ts +0 -238
- package/tests/integration/commands/list.test.ts +0 -281
- package/tests/integration/commands/profile.test.ts +0 -258
- package/tests/integration/commands/status.test.ts +0 -223
- package/tests/integration/installer-script-shape.test.ts +0 -67
- package/tests/parity/compare-outputs.ts +0 -359
- package/tests/parity/test-commands-parity.ps1 +0 -82
- package/tests/parity/test-commands-parity.sh +0 -61
- package/tests/services/auth-service.test.ts +0 -315
- package/tests/services/cookie-monitor.test.ts +0 -229
- package/tests/services/cookie-storage-service.test.ts +0 -230
- package/tests/services/gemini-client-wrapper.test.ts +0 -688
- package/tests/services/install-browser-service.test.ts +0 -83
- package/tests/services/playwright-cli-driver.test.ts +0 -468
- package/tests/services/profile-auth-manager.test.ts +0 -334
- package/tests/services/profile-service.test.ts +0 -224
- package/tests/setup.ts +0 -95
- package/tests/smoke/smoke.test.ts +0 -52
- package/tests/unit/config.test.ts +0 -159
- package/tests/unit/errors.test.ts +0 -119
- package/tests/unit/formatters.test.ts +0 -316
- package/tests/unit/mediator.test.ts +0 -152
- package/tests/unit/path-utils.test.ts +0 -289
- package/tsconfig.json +0 -16
package/README.md
CHANGED
|
@@ -4,13 +4,37 @@ Access and manage your Gemini web chats from the command line. GemiTerm bridges
|
|
|
4
4
|
|
|
5
5
|
## Prerequisites
|
|
6
6
|
|
|
7
|
-
- **[Bun](https://bun.sh)** runtime
|
|
7
|
+
- **[Bun](https://bun.sh)** runtime ≥ 1.0.0
|
|
8
8
|
- **Chromium Browser** — GemiTerm uses your system Chrome/Edge if available, otherwise installs Playwright's Chromium automatically
|
|
9
9
|
- **Google Account** with access to [Gemini](https://gemini.google.com)
|
|
10
10
|
|
|
11
|
+
## Quick Start (no install)
|
|
12
|
+
|
|
13
|
+
Use `bunx` to run GemiTerm directly — no install step needed:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bunx gemiterm auth # authenticate with Google
|
|
17
|
+
bunx gemiterm list # list your chats
|
|
18
|
+
```
|
|
19
|
+
|
|
11
20
|
## Installation
|
|
12
21
|
|
|
13
|
-
|
|
22
|
+
### Global install (recommended for daily use)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
bun install gemiterm -g
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Then use from anywhere:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
gemiterm auth
|
|
32
|
+
gemiterm list
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Install scripts (binary drop)
|
|
36
|
+
|
|
37
|
+
For systems without Bun — the script auto-bootstraps Bun if needed.
|
|
14
38
|
|
|
15
39
|
**Windows** (PowerShell 7+):
|
|
16
40
|
|
|
@@ -24,14 +48,9 @@ irm https://github.com/expert-vision-software/GemiTerm/releases/latest/download/
|
|
|
24
48
|
curl -fsSL https://github.com/expert-vision-software/GemiTerm/releases/latest/download/install.sh | bash
|
|
25
49
|
```
|
|
26
50
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
## Quick Start
|
|
51
|
+
See [docs/INSTALL.md](docs/INSTALL.md) for the full guide, uninstall instructions, troubleshooting, and build-from-source steps.
|
|
30
52
|
|
|
31
|
-
|
|
32
|
-
gemiterm auth # authenticate with Google
|
|
33
|
-
gemiterm list # list your chats
|
|
34
|
-
```
|
|
53
|
+
> **Upgrading from v1.4.1?** Your profiles, cookies, and default profile marker are preserved. See the "Upgrade from v1.4.1" section in [docs/INSTALL.md](docs/INSTALL.md) for details.
|
|
35
54
|
|
|
36
55
|
## Development
|
|
37
56
|
|
|
@@ -197,6 +216,10 @@ Checks for system Chrome/Edge first, falls back to installing Playwright's Chrom
|
|
|
197
216
|
gemiterm -v <command>
|
|
198
217
|
```
|
|
199
218
|
|
|
219
|
+
## Skills
|
|
220
|
+
|
|
221
|
+
GemiTerm has an associated skills repository for AI coding agents (Copilot, Claude, etc.) that provides domain-specific instructions for working with **gemiterm** and automating common workflows. There's also a fun `debate-with-gemini` skill that created an interactive turn-based conversation between your agent and gemini. See [opencode-gemiterm-skills](https://www.npmjs.com/package/opencode-gemiterm-skills).
|
|
222
|
+
|
|
200
223
|
## Configuration
|
|
201
224
|
|
|
202
225
|
### Configuration Directory
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gemiterm",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"module": "index.ts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -14,9 +14,16 @@
|
|
|
14
14
|
"publishConfig": {
|
|
15
15
|
"access": "public"
|
|
16
16
|
},
|
|
17
|
-
"
|
|
18
|
-
"
|
|
17
|
+
"bin": {
|
|
18
|
+
"gemiterm": "src/cli/index.ts"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"bun": ">=1.0.0"
|
|
19
22
|
},
|
|
23
|
+
"files": [
|
|
24
|
+
"src/",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
20
27
|
"scripts": {
|
|
21
28
|
"start": "bun run src/cli/index.ts",
|
|
22
29
|
"dev": "bun run src/cli/index.ts",
|
|
@@ -34,12 +41,14 @@
|
|
|
34
41
|
"build:windows": "bun run scripts/build.ts windows",
|
|
35
42
|
"build:release": "bun run scripts/build.ts release"
|
|
36
43
|
},
|
|
37
|
-
"
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@types/bun": "^1.3.14",
|
|
38
46
|
"typescript": "^6.0.3"
|
|
39
47
|
},
|
|
40
48
|
"dependencies": {
|
|
41
49
|
"chalk": "^5.6.2",
|
|
42
50
|
"cli-table3": "^0.6.5",
|
|
51
|
+
"commander": "^15.0.0",
|
|
43
52
|
"gemini-reverse": "^1.0.12"
|
|
44
53
|
}
|
|
45
54
|
}
|
|
@@ -1,62 +1,66 @@
|
|
|
1
|
-
import type { Mediator } from "../core/mediator.ts";
|
|
2
|
-
import type { ProfileAuthManager } from "../services/profile-auth-manager.ts";
|
|
3
|
-
import { AuthCommand } from "./commands/auth-command.ts";
|
|
4
|
-
import { ProfileCommand } from "./commands/profile-command.ts";
|
|
5
|
-
import { StatusCommand } from "./commands/status-command.ts";
|
|
6
|
-
import { ListCommand } from "./commands/list-command.ts";
|
|
7
|
-
import { FetchCommand } from "./commands/fetch-command.ts";
|
|
8
|
-
import { ContinueCommand } from "./commands/continue-command.ts";
|
|
9
|
-
import { NewCommand } from "./commands/new-command.ts";
|
|
10
|
-
import { DeleteCommand } from "./commands/delete-command.ts";
|
|
11
|
-
import { ExportCommand } from "./commands/export-command.ts";
|
|
12
|
-
import { ExportAllCommand } from "./commands/export-all-command.ts";
|
|
13
|
-
import { InstallBrowserCommand } from "./commands/install-browser-command.ts";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
readonly
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
this.register("
|
|
53
|
-
this.register("
|
|
54
|
-
this.register("
|
|
55
|
-
this.register("
|
|
56
|
-
this.register("
|
|
57
|
-
this.register("
|
|
58
|
-
this.register("
|
|
59
|
-
this.register("
|
|
60
|
-
this.register("
|
|
61
|
-
|
|
62
|
-
|
|
1
|
+
import type { Mediator } from "../core/mediator.ts";
|
|
2
|
+
import type { ProfileAuthManager } from "../services/profile-auth-manager.ts";
|
|
3
|
+
import { AuthCommand } from "./commands/auth-command.ts";
|
|
4
|
+
import { ProfileCommand } from "./commands/profile-command.ts";
|
|
5
|
+
import { StatusCommand } from "./commands/status-command.ts";
|
|
6
|
+
import { ListCommand } from "./commands/list-command.ts";
|
|
7
|
+
import { FetchCommand } from "./commands/fetch-command.ts";
|
|
8
|
+
import { ContinueCommand } from "./commands/continue-command.ts";
|
|
9
|
+
import { NewCommand } from "./commands/new-command.ts";
|
|
10
|
+
import { DeleteCommand } from "./commands/delete-command.ts";
|
|
11
|
+
import { ExportCommand } from "./commands/export-command.ts";
|
|
12
|
+
import { ExportAllCommand } from "./commands/export-all-command.ts";
|
|
13
|
+
import { InstallBrowserCommand } from "./commands/install-browser-command.ts";
|
|
14
|
+
import { InstallSkillsCommand } from "./commands/install-skills-command.ts";
|
|
15
|
+
|
|
16
|
+
export interface CliCommandContext {
|
|
17
|
+
verbose: boolean;
|
|
18
|
+
mediator: Mediator;
|
|
19
|
+
profileAuthManager: ProfileAuthManager;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface CliCommand {
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly description: string;
|
|
25
|
+
execute(args: string[], context: CliCommandContext): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class CommandRegistry {
|
|
29
|
+
private handlers = new Map<string, CliCommand>();
|
|
30
|
+
|
|
31
|
+
register(commandName: string, handler: CliCommand): void {
|
|
32
|
+
if (this.handlers.has(commandName)) {
|
|
33
|
+
throw new Error(`Command already registered: ${commandName}`);
|
|
34
|
+
}
|
|
35
|
+
this.handlers.set(commandName, handler);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getHandler(commandName: string): CliCommand | undefined {
|
|
39
|
+
return this.handlers.get(commandName);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
has(commandName: string): boolean {
|
|
43
|
+
return this.handlers.has(commandName);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getRegisteredNames(): string[] {
|
|
47
|
+
return [...this.handlers.keys()];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
registerAllCommands(): void {
|
|
51
|
+
const authCommand = new AuthCommand();
|
|
52
|
+
this.register("auth", authCommand);
|
|
53
|
+
this.register("login", authCommand);
|
|
54
|
+
this.register("profile", new ProfileCommand());
|
|
55
|
+
this.register("status", new StatusCommand());
|
|
56
|
+
this.register("list", new ListCommand());
|
|
57
|
+
this.register("fetch", new FetchCommand());
|
|
58
|
+
this.register("continue", new ContinueCommand());
|
|
59
|
+
this.register("new", new NewCommand());
|
|
60
|
+
this.register("delete", new DeleteCommand());
|
|
61
|
+
this.register("export", new ExportCommand());
|
|
62
|
+
this.register("export-all", new ExportAllCommand());
|
|
63
|
+
this.register("install-browser", new InstallBrowserCommand());
|
|
64
|
+
this.register("install-skills", new InstallSkillsCommand());
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import type { CliCommand, CliCommandContext } from "../command-registry.ts";
|
|
3
|
+
import { Logger } from "../../infrastructure/logger.ts";
|
|
4
|
+
import { InstallSkillsService, SkillsInstallError } from "../../services/skills-service.ts";
|
|
5
|
+
|
|
6
|
+
export class InstallSkillsCommand implements CliCommand {
|
|
7
|
+
readonly name = "install-skills";
|
|
8
|
+
readonly description = "Install GemiTerm opencode skills from expert-vision-software/opencode-gemiterm-skills";
|
|
9
|
+
|
|
10
|
+
async execute(_args: string[], _context: CliCommandContext): Promise<void> {
|
|
11
|
+
const logger = new Logger("install-skills-command");
|
|
12
|
+
logger.debug("Executing install-skills command");
|
|
13
|
+
const service = new InstallSkillsService({ logger });
|
|
14
|
+
|
|
15
|
+
console.log(chalk.dim("Installing GemiTerm opencode skills..."));
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const output = await service.install();
|
|
19
|
+
if (output) {
|
|
20
|
+
console.log(output);
|
|
21
|
+
}
|
|
22
|
+
console.log(chalk.green("Skills installed successfully."));
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (error instanceof SkillsInstallError) {
|
|
25
|
+
logger.error(error.message);
|
|
26
|
+
if (error.cause) {
|
|
27
|
+
logger.error(`Cause: ${error.cause.message}`);
|
|
28
|
+
}
|
|
29
|
+
console.error(chalk.red("Failed to install skills."));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
throw error;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
import { CommandRegistry } from "./command-registry.ts";
|
|
4
4
|
import { Logger } from "../infrastructure/logger.ts";
|
|
5
5
|
import { Mediator } from "../core/mediator.ts";
|
|
6
|
-
import { showHelp } from "./commands/help.ts";
|
|
7
6
|
import { GeminiClientService } from "../services/gemini-client-wrapper.ts";
|
|
8
7
|
import { CookieStorageService } from "../services/cookie-storage-service.ts";
|
|
9
8
|
import { ProfileAuthManager } from "../services/profile-auth-manager.ts";
|
|
10
9
|
import { CookieStorage, ProfileManager } from "../infrastructure/storage.ts";
|
|
11
|
-
import { getDefaultProfileName, listProfiles
|
|
10
|
+
import { getDefaultProfileName, listProfiles } from "../infrastructure/config.ts";
|
|
12
11
|
import { getPackageJson } from "../infrastructure/path-utils.ts";
|
|
12
|
+
import { parseGlobalArgs, printVersion, printHelp } from "../infrastructure/cli-parser.ts";
|
|
13
13
|
import { AuthenticationError } from "../core/errors.ts";
|
|
14
14
|
import {
|
|
15
15
|
AuthenticateCommandHandler,
|
|
@@ -30,39 +30,6 @@ import {
|
|
|
30
30
|
|
|
31
31
|
const pkg = getPackageJson(import.meta.url);
|
|
32
32
|
|
|
33
|
-
interface GlobalFlags {
|
|
34
|
-
verbose: boolean;
|
|
35
|
-
version: boolean;
|
|
36
|
-
help: boolean;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function parseGlobalFlags(args: string[]): { flags: GlobalFlags; remaining: string[] } {
|
|
40
|
-
const flags: GlobalFlags = {
|
|
41
|
-
verbose: process.env.GEMITERM_VERBOSE === "true",
|
|
42
|
-
version: false,
|
|
43
|
-
help: false,
|
|
44
|
-
};
|
|
45
|
-
const remaining: string[] = [];
|
|
46
|
-
|
|
47
|
-
for (const arg of args) {
|
|
48
|
-
if (arg === "--verbose" || arg === "-v") {
|
|
49
|
-
flags.verbose = true;
|
|
50
|
-
} else if (arg === "--version") {
|
|
51
|
-
flags.version = true;
|
|
52
|
-
} else if (arg === "--help" || arg === "-h") {
|
|
53
|
-
if (remaining.length === 0) {
|
|
54
|
-
flags.help = true;
|
|
55
|
-
} else {
|
|
56
|
-
remaining.push(arg);
|
|
57
|
-
}
|
|
58
|
-
} else {
|
|
59
|
-
remaining.push(arg);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return { flags, remaining };
|
|
64
|
-
}
|
|
65
|
-
|
|
66
33
|
async function setupMediator(mediator: Mediator): Promise<ProfileAuthManager> {
|
|
67
34
|
const logger = new Logger("mediator");
|
|
68
35
|
const cookieStorage = new CookieStorage();
|
|
@@ -159,31 +126,39 @@ async function setupMediator(mediator: Mediator): Promise<ProfileAuthManager> {
|
|
|
159
126
|
async function main(): Promise<void> {
|
|
160
127
|
const args = process.argv.slice(2);
|
|
161
128
|
const logger = new Logger("cli");
|
|
162
|
-
const { flags, remaining } = parseGlobalFlags(args);
|
|
163
129
|
|
|
164
|
-
|
|
130
|
+
let flags: ReturnType<typeof parseGlobalArgs>["flags"];
|
|
131
|
+
let subcommand: string | null;
|
|
132
|
+
let subcommandArgs: string[];
|
|
133
|
+
try {
|
|
134
|
+
({ flags, subcommand, subcommandArgs } = parseGlobalArgs(args));
|
|
135
|
+
} catch (error) {
|
|
136
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
137
|
+
console.error(message);
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const verbose = flags.verbose || process.env.GEMITERM_VERBOSE === "true";
|
|
142
|
+
if (verbose) {
|
|
165
143
|
Logger.setVerbose(true);
|
|
166
144
|
}
|
|
167
145
|
|
|
168
146
|
if (flags.version) {
|
|
169
|
-
|
|
147
|
+
printVersion(pkg.version);
|
|
170
148
|
process.exit(0);
|
|
171
149
|
}
|
|
172
150
|
|
|
173
151
|
const registry = new CommandRegistry();
|
|
174
152
|
registry.registerAllCommands();
|
|
175
153
|
|
|
176
|
-
if (
|
|
177
|
-
|
|
154
|
+
if (!subcommand) {
|
|
155
|
+
printHelp(registry);
|
|
178
156
|
process.exit(0);
|
|
179
157
|
}
|
|
180
158
|
|
|
181
159
|
const mediator = new Mediator();
|
|
182
160
|
const profileAuthManager = await setupMediator(mediator);
|
|
183
161
|
|
|
184
|
-
const subcommand = remaining[0];
|
|
185
|
-
const subcommandArgs = remaining.slice(1);
|
|
186
|
-
|
|
187
162
|
const handler = registry.getHandler(subcommand);
|
|
188
163
|
if (!handler) {
|
|
189
164
|
const known = registry.getRegisteredNames();
|
|
@@ -198,7 +173,7 @@ async function main(): Promise<void> {
|
|
|
198
173
|
}
|
|
199
174
|
|
|
200
175
|
try {
|
|
201
|
-
await handler.execute(subcommandArgs, { verbose
|
|
176
|
+
await handler.execute(subcommandArgs, { verbose, mediator, profileAuthManager });
|
|
202
177
|
} catch (error) {
|
|
203
178
|
const message = error instanceof Error ? error.message : String(error);
|
|
204
179
|
logger.error(`Command '${subcommand}' failed: ${message}`);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Command, CommanderError } from "commander";
|
|
2
|
+
import type { CommandRegistry } from "../cli/command-registry.ts";
|
|
3
|
+
import { showHelp } from "../cli/commands/help.ts";
|
|
4
|
+
|
|
5
|
+
export interface ParsedFlags {
|
|
6
|
+
verbose: boolean;
|
|
7
|
+
version: boolean;
|
|
8
|
+
help: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface ParsedArgs {
|
|
12
|
+
flags: ParsedFlags;
|
|
13
|
+
subcommand: string | null;
|
|
14
|
+
subcommandArgs: string[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function splitAtFirstNonFlag(argv: string[]): { pre: string[]; post: string[] } {
|
|
18
|
+
const pre: string[] = [];
|
|
19
|
+
const post: string[] = [];
|
|
20
|
+
let found = false;
|
|
21
|
+
for (const arg of argv) {
|
|
22
|
+
if (!found && !arg.startsWith("-")) {
|
|
23
|
+
found = true;
|
|
24
|
+
post.push(arg);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (found) {
|
|
28
|
+
post.push(arg);
|
|
29
|
+
} else {
|
|
30
|
+
pre.push(arg);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { pre, post };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function parseGlobalArgs(argv: string[]): ParsedArgs {
|
|
37
|
+
const { pre, post } = splitAtFirstNonFlag(argv);
|
|
38
|
+
const subcommand = post[0] ?? null;
|
|
39
|
+
const subcommandArgs = post.slice(1);
|
|
40
|
+
|
|
41
|
+
const program = new Command();
|
|
42
|
+
program
|
|
43
|
+
.exitOverride()
|
|
44
|
+
.allowUnknownOption(false)
|
|
45
|
+
.allowExcessArguments(true)
|
|
46
|
+
.helpOption(false)
|
|
47
|
+
.configureOutput({ writeErr: () => {} })
|
|
48
|
+
.option("-v, --verbose", "enable verbose output")
|
|
49
|
+
.option("--version", "show version number")
|
|
50
|
+
.option("-h, --help", "show help");
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
program.parse(pre, { from: "user" });
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (error instanceof CommanderError) {
|
|
56
|
+
throw new Error("gemiterm: " + error.message);
|
|
57
|
+
}
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const opts = program.opts<{ verbose?: boolean; version?: boolean; help?: boolean }>();
|
|
62
|
+
const flags: ParsedFlags = {
|
|
63
|
+
verbose: Boolean(opts.verbose),
|
|
64
|
+
version: Boolean(opts.version),
|
|
65
|
+
help: Boolean(opts.help),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return { flags, subcommand, subcommandArgs };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function printVersion(pkgVersion: string): void {
|
|
72
|
+
console.log(`gemiterm v${pkgVersion}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function printHelp(registry: CommandRegistry): void {
|
|
76
|
+
showHelp(registry);
|
|
77
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Logger } from "../infrastructure/logger.ts";
|
|
2
|
+
|
|
3
|
+
export class SkillsCliError extends Error {
|
|
4
|
+
constructor(command: string, exitCode: number, stderr: string) {
|
|
5
|
+
super(`skills '${command}' exited with code ${exitCode}: ${stderr}`);
|
|
6
|
+
this.name = "SkillsCliError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SkillsRunnerResult {
|
|
11
|
+
exitCode: number;
|
|
12
|
+
stdout: string;
|
|
13
|
+
stderr: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface SkillsRunner {
|
|
17
|
+
run(args: string[]): Promise<SkillsRunnerResult>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class BunSkillsRunner implements SkillsRunner {
|
|
21
|
+
async run(args: string[]): Promise<SkillsRunnerResult> {
|
|
22
|
+
const proc = Bun.spawn(["npx", "skills", ...args], {
|
|
23
|
+
stdout: "pipe",
|
|
24
|
+
stderr: "pipe",
|
|
25
|
+
stdin: "ignore",
|
|
26
|
+
});
|
|
27
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
28
|
+
new Response(proc.stdout).text(),
|
|
29
|
+
new Response(proc.stderr).text(),
|
|
30
|
+
proc.exited,
|
|
31
|
+
]);
|
|
32
|
+
return { exitCode: exitCode ?? -1, stdout: stdout.trim(), stderr: stderr.trim() };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface SkillsCliDriverOptions {
|
|
37
|
+
logger?: Logger;
|
|
38
|
+
runner?: SkillsRunner;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export class SkillsCliDriver {
|
|
42
|
+
private readonly logger: Logger;
|
|
43
|
+
private readonly runner: SkillsRunner;
|
|
44
|
+
|
|
45
|
+
constructor(opts: SkillsCliDriverOptions = {}) {
|
|
46
|
+
this.logger = opts.logger ?? new Logger("skills-cli-driver");
|
|
47
|
+
this.runner = opts.runner ?? new BunSkillsRunner();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async run(args: string[]): Promise<string> {
|
|
51
|
+
this.logger.debug(`Running: npx skills ${args.join(" ")}`);
|
|
52
|
+
const result = await this.runner.run(args);
|
|
53
|
+
if (result.exitCode !== 0) {
|
|
54
|
+
throw new SkillsCliError(args.join(" "), result.exitCode, result.stderr);
|
|
55
|
+
}
|
|
56
|
+
return result.stdout;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Logger } from "../infrastructure/logger.ts";
|
|
2
|
+
import { SkillsCliDriver, SkillsCliDriverOptions, SkillsCliError } from "./skills-cli-driver.ts";
|
|
3
|
+
|
|
4
|
+
export class SkillsInstallError extends Error {
|
|
5
|
+
constructor(message: string, public readonly cause?: Error) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "SkillsInstallError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface InstallSkillsServiceOptions {
|
|
12
|
+
logger?: Logger;
|
|
13
|
+
driver?: SkillsCliDriver;
|
|
14
|
+
repo?: string;
|
|
15
|
+
skills?: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class InstallSkillsService {
|
|
19
|
+
private readonly logger: Logger;
|
|
20
|
+
private readonly driver: SkillsCliDriver;
|
|
21
|
+
private readonly repo: string;
|
|
22
|
+
private readonly skills: string[];
|
|
23
|
+
|
|
24
|
+
constructor(opts: InstallSkillsServiceOptions = {}) {
|
|
25
|
+
this.logger = opts.logger ?? new Logger("install-skills-service");
|
|
26
|
+
this.repo = opts.repo ?? "expert-vision-software/opencode-gemiterm-skills";
|
|
27
|
+
this.skills = opts.skills ?? ["gemiterm", "debate-with-gemini"];
|
|
28
|
+
this.driver = opts.driver ?? new SkillsCliDriver({ logger: this.logger });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async install(): Promise<string> {
|
|
32
|
+
this.logger.info(`Installing skills from ${this.repo}: ${this.skills.join(", ")}`);
|
|
33
|
+
const args = ["add", this.repo];
|
|
34
|
+
for (const skill of this.skills) {
|
|
35
|
+
args.push("--skill", skill);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const output = await this.driver.run(args);
|
|
40
|
+
this.logger.info(`Skills installed successfully: ${output}`);
|
|
41
|
+
return output;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error instanceof SkillsCliError) {
|
|
44
|
+
throw new SkillsInstallError(error.message, error);
|
|
45
|
+
}
|
|
46
|
+
throw new SkillsInstallError(
|
|
47
|
+
`Failed to install skills: ${error instanceof Error ? error.message : String(error)}`,
|
|
48
|
+
error instanceof Error ? error : undefined,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
package/.env
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
name: Manual Release
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
workflow_dispatch:
|
|
5
|
-
inputs:
|
|
6
|
-
ref:
|
|
7
|
-
description: 'Commit hash, branch, or tag to build'
|
|
8
|
-
required: true
|
|
9
|
-
type: string
|
|
10
|
-
draft:
|
|
11
|
-
description: 'Create a draft release (recommended for testing)'
|
|
12
|
-
required: true
|
|
13
|
-
type: boolean
|
|
14
|
-
default: true
|
|
15
|
-
pre_release:
|
|
16
|
-
description: 'Mark as pre-release'
|
|
17
|
-
required: true
|
|
18
|
-
type: boolean
|
|
19
|
-
default: true
|
|
20
|
-
publish:
|
|
21
|
-
description: 'Publish to npm (DANGEROUS — only enable if you mean it)'
|
|
22
|
-
required: true
|
|
23
|
-
type: boolean
|
|
24
|
-
default: false
|
|
25
|
-
|
|
26
|
-
jobs:
|
|
27
|
-
build:
|
|
28
|
-
uses: ./.github/workflows/release.yml
|
|
29
|
-
with:
|
|
30
|
-
ref: ${{ inputs.ref }}
|
|
31
|
-
tag_name: manual-${{ inputs.ref }}-${{ github.run_id }}
|
|
32
|
-
publish: ${{ inputs.publish }}
|
|
33
|
-
draft: ${{ inputs.draft }}
|
|
34
|
-
prerelease: ${{ inputs.pre_release }}
|