gemiterm 2.0.0 → 2.1.1
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/continue-command.ts +75 -1
- package/src/cli/commands/install-skills-command.ts +35 -0
- package/src/cli/commands/new-command.ts +62 -0
- package/src/cli/index.ts +19 -44
- package/src/cli/utils/long-arg-guard.ts +28 -0
- package/src/cli/utils/prompt-file.ts +26 -0
- package/src/infrastructure/cli-parser.ts +77 -0
- package/src/infrastructure/io.ts +9 -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
|
@@ -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
|
+
}
|
package/src/infrastructure/io.ts
CHANGED
|
@@ -114,6 +114,14 @@ function removeDir(path: string): void {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
function removeFile(path: string): void {
|
|
118
|
+
try {
|
|
119
|
+
rmSync(path, { force: true });
|
|
120
|
+
} catch (err) {
|
|
121
|
+
throw wrap("removeFile", path, err instanceof Error ? err : undefined);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
117
125
|
function renameDir(src: string, dest: string): void {
|
|
118
126
|
try {
|
|
119
127
|
renameSync(src, dest);
|
|
@@ -155,6 +163,7 @@ export {
|
|
|
155
163
|
readJsonFile,
|
|
156
164
|
writeJsonFile,
|
|
157
165
|
removeDir,
|
|
166
|
+
removeFile,
|
|
158
167
|
renameDir,
|
|
159
168
|
isDirectory,
|
|
160
169
|
listSubdirectories,
|
|
@@ -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 }}
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
name: Release
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags: ['v*']
|
|
6
|
-
workflow_call:
|
|
7
|
-
inputs:
|
|
8
|
-
ref:
|
|
9
|
-
description: 'Git ref (commit hash, branch, or tag) to build'
|
|
10
|
-
type: string
|
|
11
|
-
default: ${{ github.ref }}
|
|
12
|
-
tag_name:
|
|
13
|
-
description: 'Tag name to attach the GitHub Release to'
|
|
14
|
-
type: string
|
|
15
|
-
default: ${{ github.ref_name }}
|
|
16
|
-
publish:
|
|
17
|
-
description: 'Whether to publish to npm'
|
|
18
|
-
type: string
|
|
19
|
-
default: 'true'
|
|
20
|
-
draft:
|
|
21
|
-
description: 'Whether to create the release as a draft'
|
|
22
|
-
type: string
|
|
23
|
-
default: 'false'
|
|
24
|
-
prerelease:
|
|
25
|
-
description: 'Whether to mark the release as a pre-release'
|
|
26
|
-
type: string
|
|
27
|
-
default: 'true'
|
|
28
|
-
|
|
29
|
-
concurrency:
|
|
30
|
-
group: release
|
|
31
|
-
cancel-in-progress: true
|
|
32
|
-
|
|
33
|
-
permissions:
|
|
34
|
-
contents: write
|
|
35
|
-
|
|
36
|
-
jobs:
|
|
37
|
-
build-linux:
|
|
38
|
-
name: Build Linux
|
|
39
|
-
runs-on: ubuntu-latest
|
|
40
|
-
steps:
|
|
41
|
-
- uses: actions/checkout@v4
|
|
42
|
-
with:
|
|
43
|
-
ref: ${{ inputs.ref || github.ref }}
|
|
44
|
-
- uses: oven-sh/setup-bun@v2
|
|
45
|
-
with:
|
|
46
|
-
bun-version: 1.3.13
|
|
47
|
-
- run: bun install --frozen-lockfile
|
|
48
|
-
- run: bun run build:linux
|
|
49
|
-
- name: Verify binary
|
|
50
|
-
run: test -f dist/gemiterm
|
|
51
|
-
- name: Fail if binary < 20MB
|
|
52
|
-
run: |
|
|
53
|
-
size=$(stat -c%s dist/gemiterm)
|
|
54
|
-
minsize=$((20 * 1024 * 1024))
|
|
55
|
-
if [ "$size" -lt "$minsize" ]; then
|
|
56
|
-
echo "Binary too small: $size bytes (expected at least 20MB)"
|
|
57
|
-
exit 1
|
|
58
|
-
fi
|
|
59
|
-
- uses: actions/upload-artifact@v4
|
|
60
|
-
with:
|
|
61
|
-
name: GemiTerm-linux
|
|
62
|
-
path: dist/gemiterm
|
|
63
|
-
|
|
64
|
-
build-windows:
|
|
65
|
-
name: Build Windows
|
|
66
|
-
runs-on: windows-latest
|
|
67
|
-
steps:
|
|
68
|
-
- uses: actions/checkout@v4
|
|
69
|
-
with:
|
|
70
|
-
ref: ${{ inputs.ref || github.ref }}
|
|
71
|
-
- uses: oven-sh/setup-bun@v2
|
|
72
|
-
with:
|
|
73
|
-
bun-version: 1.3.13
|
|
74
|
-
- run: bun install --frozen-lockfile
|
|
75
|
-
- run: bun run build:windows
|
|
76
|
-
- name: Verify binary
|
|
77
|
-
run: Test-Path dist/gemiterm.exe
|
|
78
|
-
- name: Fail if binary < 20MB
|
|
79
|
-
shell: pwsh
|
|
80
|
-
run: |
|
|
81
|
-
$size = (Get-Item dist/gemiterm.exe).Length
|
|
82
|
-
$minsize = 20 * 1024 * 1024
|
|
83
|
-
if ($size -lt $minsize) {
|
|
84
|
-
Write-Host "Binary too small: $size bytes (expected at least 20MB)"
|
|
85
|
-
exit 1
|
|
86
|
-
}
|
|
87
|
-
- uses: actions/upload-artifact@v4
|
|
88
|
-
with:
|
|
89
|
-
name: GemiTerm-windows
|
|
90
|
-
path: dist/gemiterm.exe
|
|
91
|
-
|
|
92
|
-
release:
|
|
93
|
-
name: Create GitHub Release
|
|
94
|
-
runs-on: ubuntu-latest
|
|
95
|
-
needs: [build-linux, build-windows]
|
|
96
|
-
steps:
|
|
97
|
-
- uses: actions/checkout@v4
|
|
98
|
-
with:
|
|
99
|
-
ref: ${{ inputs.ref || github.ref }}
|
|
100
|
-
- uses: actions/download-artifact@v4
|
|
101
|
-
with:
|
|
102
|
-
name: GemiTerm-linux
|
|
103
|
-
path: dist
|
|
104
|
-
- uses: actions/download-artifact@v4
|
|
105
|
-
with:
|
|
106
|
-
name: GemiTerm-windows
|
|
107
|
-
path: dist
|
|
108
|
-
- name: Make Linux binary executable
|
|
109
|
-
run: chmod +x dist/gemiterm
|
|
110
|
-
- name: Extract CHANGELOG entry for release
|
|
111
|
-
id: changelog
|
|
112
|
-
run: |
|
|
113
|
-
node -e "
|
|
114
|
-
const version = '${TAG}'.replace(/^v/, '');
|
|
115
|
-
const fs = require('fs');
|
|
116
|
-
const content = fs.readFileSync('CHANGELOG.md', 'utf8');
|
|
117
|
-
const sections = content.split(/(?=^## )/m);
|
|
118
|
-
let found = false;
|
|
119
|
-
let notes = '';
|
|
120
|
-
for (const section of sections) {
|
|
121
|
-
const m = section.match(/^## \[([^\]]+)\].*?-\s*(\d{4}-\d{2}-\d{2})/s);
|
|
122
|
-
if (m && m[1].replace(/^v/, '') === version) {
|
|
123
|
-
notes = section.replace(/^## \[[^\]]+\].*?-\s*\d{4}-\d{2}-\d{2}\n/, '').trim();
|
|
124
|
-
found = true;
|
|
125
|
-
break;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
if (!found) {
|
|
129
|
-
console.error('No changelog section found for version:', version);
|
|
130
|
-
process.exit(1);
|
|
131
|
-
}
|
|
132
|
-
fs.writeFileSync('release_body.md', notes);
|
|
133
|
-
console.log('Extracted', notes.split('\n').length, 'lines for version', version);
|
|
134
|
-
"
|
|
135
|
-
env:
|
|
136
|
-
TAG: ${{ inputs.tag_name || github.ref_name }}
|
|
137
|
-
- name: Create GitHub Release
|
|
138
|
-
uses: softprops/action-gh-release@v2
|
|
139
|
-
with:
|
|
140
|
-
tag_name: ${{ inputs.tag_name || github.ref_name }}
|
|
141
|
-
body_path: release_body.md
|
|
142
|
-
draft: false
|
|
143
|
-
prerelease: ${{ contains(inputs.tag_name || github.ref_name, '-') }}
|
|
144
|
-
env:
|
|
145
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
146
|
-
- name: Upload Linux binary
|
|
147
|
-
run: |
|
|
148
|
-
TAG="${{ inputs.tag_name || github.ref_name }}"
|
|
149
|
-
gh release upload "$TAG" dist/gemiterm --clobber || true
|
|
150
|
-
env:
|
|
151
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
152
|
-
- name: Upload Windows binary
|
|
153
|
-
run: |
|
|
154
|
-
TAG="${{ inputs.tag_name || github.ref_name }}"
|
|
155
|
-
gh release upload "$TAG" dist/gemiterm.exe --clobber || true
|
|
156
|
-
env:
|
|
157
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
158
|
-
- name: Set asset labels
|
|
159
|
-
run: |
|
|
160
|
-
TAG="${{ inputs.tag_name || github.ref_name }}"
|
|
161
|
-
LINUX_ASSET_ID=$(gh release view "$TAG" --json assets -q '.assets[] | select(.name == "gemiterm") | .id')
|
|
162
|
-
if [[ -n "$LINUX_ASSET_ID" ]]; then
|
|
163
|
-
gh api -X PATCH /repos/${{ github.repository }}/releases/assets/$LINUX_ASSET_ID \
|
|
164
|
-
-f name='GemiTerm' \
|
|
165
|
-
-f label="GemiTerm $TAG - Linux x64 binary" || true
|
|
166
|
-
fi
|
|
167
|
-
WINDOWS_ASSET_ID=$(gh release view "$TAG" --json assets -q '.assets[] | select(.name == "gemiterm.exe") | .id')
|
|
168
|
-
if [[ -n "$WINDOWS_ASSET_ID" ]]; then
|
|
169
|
-
gh api -X PATCH /repos/${{ github.repository }}/releases/assets/$WINDOWS_ASSET_ID \
|
|
170
|
-
-f name='GemiTerm' \
|
|
171
|
-
-f label="GemiTerm $TAG - Windows x64 binary" || true
|
|
172
|
-
fi
|
|
173
|
-
env:
|
|
174
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
publish:
|
|
178
|
-
name: Publish to npm
|
|
179
|
-
runs-on: ubuntu-latest
|
|
180
|
-
needs: [release]
|
|
181
|
-
if: ${{ inputs.publish != 'false' }}
|
|
182
|
-
permissions:
|
|
183
|
-
contents: read
|
|
184
|
-
id-token: write
|
|
185
|
-
steps:
|
|
186
|
-
- uses: actions/checkout@v4
|
|
187
|
-
with:
|
|
188
|
-
ref: ${{ inputs.ref || github.ref }}
|
|
189
|
-
- uses: oven-sh/setup-bun@v2
|
|
190
|
-
with:
|
|
191
|
-
bun-version: 1.3.13
|
|
192
|
-
- run: bun install
|
|
193
|
-
- name: Publish to npm
|
|
194
|
-
run: |
|
|
195
|
-
echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
|
|
196
|
-
npm publish --provenance --access public
|
|
197
|
-
env:
|
|
198
|
-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
name: Test
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request: {}
|
|
5
|
-
push:
|
|
6
|
-
branches: [main]
|
|
7
|
-
|
|
8
|
-
concurrency:
|
|
9
|
-
group: ${{ github.workflow }}-${{ github.ref }}
|
|
10
|
-
cancel-in-progress: true
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
test:
|
|
14
|
-
runs-on: ubuntu-latest
|
|
15
|
-
steps:
|
|
16
|
-
- uses: actions/checkout@v4
|
|
17
|
-
- uses: oven-sh/setup-bun@v2
|
|
18
|
-
with:
|
|
19
|
-
bun-version: 1.3.13
|
|
20
|
-
- run: bun install --frozen-lockfile
|
|
21
|
-
- run: bun test
|
|
22
|
-
- run: bun run typecheck
|
|
23
|
-
- name: Enforce path-utils mediation
|
|
24
|
-
run: |
|
|
25
|
-
! grep -rn --include='*.ts' "from \"node:\\(fs\\|path\\|os\\)\"" src/ \
|
|
26
|
-
| grep -v "src/infrastructure/path-utils.ts" \
|
|
27
|
-
| grep -v "src/infrastructure/io.ts" \
|
|
28
|
-
| grep -v "src/services/install-browser-service.ts" \
|
|
29
|
-
| grep -q .
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Implement tasks from an OpenSpec change (Experimental)
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
Implement tasks from an OpenSpec change.
|
|
6
|
-
|
|
7
|
-
**Input**: Optionally specify a change name (e.g., `/opsx-apply add-auth`). If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
|
|
8
|
-
|
|
9
|
-
**Steps**
|
|
10
|
-
|
|
11
|
-
1. **Select the change**
|
|
12
|
-
|
|
13
|
-
If a name is provided, use it. Otherwise:
|
|
14
|
-
- Infer from conversation context if the user mentioned a change
|
|
15
|
-
- Auto-select if only one active change exists
|
|
16
|
-
- If ambiguous, run `openspec list --json` to get available changes and use the **AskUserQuestion tool** to let the user select
|
|
17
|
-
|
|
18
|
-
Always announce: "Using change: <name>" and how to override (e.g., `/opsx-apply <other>`).
|
|
19
|
-
|
|
20
|
-
2. **Check status to understand the schema**
|
|
21
|
-
```bash
|
|
22
|
-
openspec status --change "<name>" --json
|
|
23
|
-
```
|
|
24
|
-
Parse the JSON to understand:
|
|
25
|
-
- `schemaName`: The workflow being used (e.g., "spec-driven")
|
|
26
|
-
- `planningHome`, `changeRoot`, and `actionContext`: planning scope and edit constraints
|
|
27
|
-
- Which artifact contains the tasks (typically "tasks" for spec-driven, check status for others)
|
|
28
|
-
|
|
29
|
-
3. **Get apply instructions**
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
openspec instructions apply --change "<name>" --json
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
This returns:
|
|
36
|
-
- `contextFiles`: artifact ID -> array of concrete file paths (varies by schema)
|
|
37
|
-
- Progress (total, complete, remaining)
|
|
38
|
-
- Task list with status
|
|
39
|
-
- Dynamic instruction based on current state
|
|
40
|
-
|
|
41
|
-
**Handle states:**
|
|
42
|
-
- If `state: "blocked"` (missing artifacts): show message, suggest using `/opsx-continue`
|
|
43
|
-
- If `state: "all_done"`: congratulate, suggest archive
|
|
44
|
-
- Otherwise: proceed to implementation
|
|
45
|
-
|
|
46
|
-
**Workspace guard:** If status JSON reports `actionContext.mode: "workspace-planning"` and `allowedEditRoots` is empty, explain that full workspace apply is not supported in this slice. Treat linked repos and folders as read-only context, ask the user to select an affected area through an explicit implementation workflow, and STOP before editing files.
|
|
47
|
-
|
|
48
|
-
4. **Read context files**
|
|
49
|
-
|
|
50
|
-
Read every file path listed under `contextFiles` from the apply instructions output.
|
|
51
|
-
The files depend on the schema being used:
|
|
52
|
-
- **spec-driven**: proposal, specs, design, tasks
|
|
53
|
-
- Other schemas: follow the contextFiles from CLI output
|
|
54
|
-
|
|
55
|
-
5. **Show current progress**
|
|
56
|
-
|
|
57
|
-
Display:
|
|
58
|
-
- Schema being used
|
|
59
|
-
- Progress: "N/M tasks complete"
|
|
60
|
-
- Remaining tasks overview
|
|
61
|
-
- Dynamic instruction from CLI
|
|
62
|
-
|
|
63
|
-
6. **Implement tasks (loop until done or blocked)**
|
|
64
|
-
|
|
65
|
-
For each pending task:
|
|
66
|
-
- Show which task is being worked on
|
|
67
|
-
- Make the code changes required
|
|
68
|
-
- Keep changes minimal and focused
|
|
69
|
-
- Mark task complete in the tasks file: `- [ ]` → `- [x]`
|
|
70
|
-
- Continue to next task
|
|
71
|
-
|
|
72
|
-
**Pause if:**
|
|
73
|
-
- Task is unclear → ask for clarification
|
|
74
|
-
- Implementation reveals a design issue → suggest updating artifacts
|
|
75
|
-
- Error or blocker encountered → report and wait for guidance
|
|
76
|
-
- User interrupts
|
|
77
|
-
|
|
78
|
-
7. **On completion or pause, show status**
|
|
79
|
-
|
|
80
|
-
Display:
|
|
81
|
-
- Tasks completed this session
|
|
82
|
-
- Overall progress: "N/M tasks complete"
|
|
83
|
-
- If all done: suggest archive
|
|
84
|
-
- If paused: explain why and wait for guidance
|
|
85
|
-
|
|
86
|
-
**Output During Implementation**
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
## Implementing: <change-name> (schema: <schema-name>)
|
|
90
|
-
|
|
91
|
-
Working on task 3/7: <task description>
|
|
92
|
-
[...implementation happening...]
|
|
93
|
-
✓ Task complete
|
|
94
|
-
|
|
95
|
-
Working on task 4/7: <task description>
|
|
96
|
-
[...implementation happening...]
|
|
97
|
-
✓ Task complete
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**Output On Completion**
|
|
101
|
-
|
|
102
|
-
```
|
|
103
|
-
## Implementation Complete
|
|
104
|
-
|
|
105
|
-
**Change:** <change-name>
|
|
106
|
-
**Schema:** <schema-name>
|
|
107
|
-
**Progress:** 7/7 tasks complete ✓
|
|
108
|
-
|
|
109
|
-
### Completed This Session
|
|
110
|
-
- [x] Task 1
|
|
111
|
-
- [x] Task 2
|
|
112
|
-
...
|
|
113
|
-
|
|
114
|
-
All tasks complete! You can archive this change with `/opsx-archive`.
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
**Output On Pause (Issue Encountered)**
|
|
118
|
-
|
|
119
|
-
```
|
|
120
|
-
## Implementation Paused
|
|
121
|
-
|
|
122
|
-
**Change:** <change-name>
|
|
123
|
-
**Schema:** <schema-name>
|
|
124
|
-
**Progress:** 4/7 tasks complete
|
|
125
|
-
|
|
126
|
-
### Issue Encountered
|
|
127
|
-
<description of the issue>
|
|
128
|
-
|
|
129
|
-
**Options:**
|
|
130
|
-
1. <option 1>
|
|
131
|
-
2. <option 2>
|
|
132
|
-
3. Other approach
|
|
133
|
-
|
|
134
|
-
What would you like to do?
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
**Guardrails**
|
|
138
|
-
- Keep going through tasks until done or blocked
|
|
139
|
-
- Always read context files before starting (from the apply instructions output)
|
|
140
|
-
- If task is ambiguous, pause and ask before implementing
|
|
141
|
-
- If implementation reveals issues, pause and suggest artifact updates
|
|
142
|
-
- Keep code changes minimal and scoped to each task
|
|
143
|
-
- Update task checkbox immediately after completing each task
|
|
144
|
-
- Pause on errors, blockers, or unclear requirements - don't guess
|
|
145
|
-
- Use contextFiles from CLI output, don't assume specific file names
|
|
146
|
-
|
|
147
|
-
**Fluid Workflow Integration**
|
|
148
|
-
|
|
149
|
-
This skill supports the "actions on a change" model:
|
|
150
|
-
|
|
151
|
-
- **Can be invoked anytime**: Before all artifacts are done (if tasks exist), after partial implementation, interleaved with other actions
|
|
152
|
-
- **Allows artifact updates**: If implementation reveals design issues, suggest updating artifacts - not phase-locked, work fluidly
|