diff --git a/bun.lock b/bun.lock index 5aa95c9..3bc4249 100644 --- a/bun.lock +++ b/bun.lock @@ -14,12 +14,12 @@ }, "packages/gkit": { "name": "gkit", - "version": "0.3.3", + "version": "0.3.4", "bin": { "gkit": "dist/gkit.js", }, "devDependencies": { - "@celados/argc": "^8.0.0", + "@celados/argc": "^9.0.0", "@standard-schema/spec": "^1.0.0", "@types/bun": "^1.3.10", "@types/node": "^25.6.0", @@ -39,7 +39,7 @@ }, }, "catalog": { - "@celados/argc": "^8.0.0", + "@celados/argc": "^9.0.0", "@types/bun": "^1.3.10", "@types/node": "^25.6.0", "@valibot/to-json-schema": "^1.6.0", @@ -54,7 +54,7 @@ "yaml": "^2.9.0", }, "packages": { - "@celados/argc": ["@celados/argc@8.0.0", "https://npm.celados.com/@celados/argc/-/@celados/argc-8.0.0.tgz", { "dependencies": { "yaml": "2.9.0" }, "peerDependencies": { "@standard-schema/spec": "^1.0.0" } }, "sha512-xVY1Dqta6Z5Fhx2QpDC4udfkC6903YOniosf0clmd034+Cvn56G968RIElW6n7BXw/oVTBBZTT+HnQInxImRtw=="], + "@celados/argc": ["@celados/argc@9.0.0", "https://npm.celados.com/@celados/argc/-/@celados/argc-9.0.0.tgz", { "dependencies": { "yaml": "2.9.0" }, "peerDependencies": { "@standard-schema/spec": "^1.0.0" } }, "sha512-shfwtc+ZAyXBxtxPnG3CapcUL0c7sUJu+pbcQAe5e190lVmtyRBCG1+OCVLn9UgKvTx2g0x7fFlu9Y7jR6LPnQ=="], "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], diff --git a/package.json b/package.json index 662298b..603d563 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "@types/node": "^25.6.0", "@valibot/to-json-schema": "^1.6.0", "ajv": "^8.20.0", - "@celados/argc": "^8.0.0", + "@celados/argc": "^9.0.0", "bun-types": "^1.3.13", "dataforseo-client": "^2.0.25", "dotenv": "^17.2.2", diff --git a/packages/gkit/package.json b/packages/gkit/package.json index 8ba5de5..786fd0b 100644 --- a/packages/gkit/package.json +++ b/packages/gkit/package.json @@ -1,6 +1,6 @@ { "name": "gkit", - "version": "0.3.3", + "version": "0.3.4", "private": true, "description": "Profile-bound, agent-first growth provider CLI.", "bin": { @@ -37,7 +37,7 @@ "test:site": "bun run ./scripts/test-site-snapshot.ts" }, "devDependencies": { - "@celados/argc": "^8.0.0", + "@celados/argc": "^9.0.0", "@standard-schema/spec": "^1.0.0", "@types/bun": "^1.3.10", "@types/node": "^25.6.0", diff --git a/packages/gkit/src/capture.test.ts b/packages/gkit/src/capture.test.ts deleted file mode 100644 index 5b5a22b..0000000 --- a/packages/gkit/src/capture.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { spawnSync } from "node:child_process"; -import { fileURLToPath } from "node:url"; - -import { describe, expect, it } from "vitest"; - -const entry = fileURLToPath(new URL("../bin/gkit.js", import.meta.url)); - -// Real pipes: spawnSync hands the child a piped stdout, the shape of `gkit @skill | head`. -function run(args: string[], env: Record) { - return spawnSync("bun", [entry, ...args], { - env: { PATH: process.env.PATH, HOME: process.env.HOME, ...env }, - stdio: ["ignore", "pipe", "pipe"], - encoding: "utf8", - }); -} - -describe("output-capture guard", () => { - it("refuses an agent piping the guide, schema and help", () => { - for (const args of [["@skill"], ["--schema"], ["--help"]]) { - const r = run(args, { CLAUDE_CODE_SESSION_ID: "t" }); - expect(r.status).toBe(1); - expect(r.stdout).toBe(""); - expect(r.stderr).toContain("OUTPUT_CAPTURED"); - } - }); - - it("lets a person, the opt-out and data commands through", () => { - expect(run(["@skill"], {}).stdout).not.toBe(""); - expect(run(["@skill"], { CLAUDE_CODE_SESSION_ID: "t", ARGC_ALLOW_CAPTURE: "1" }).status).toBe(0); - expect(run(["docs"], { CLAUDE_CODE_SESSION_ID: "t" }).status).toBe(0); - }); -}); diff --git a/packages/gkit/src/cli.ts b/packages/gkit/src/cli.ts index a383f11..c4b5409 100644 --- a/packages/gkit/src/cli.ts +++ b/packages/gkit/src/cli.ts @@ -1,6 +1,5 @@ import { fileURLToPath } from "node:url"; -import { isAgentCapture } from "@celados/argc"; import { ensureTrailingNewline, formatBareSkill } from "@celados/argc/skill"; import { parseArgs, renderHelp } from "./args"; @@ -138,10 +137,6 @@ export async function main( try { const command = parseArgs(argv); - if (refuseCapturedGuide(command, argv)) { - process.exitCode = 1; - return; - } if (command.kind === "help") { await emitter.writeText(renderHelp()); process.exitCode = abortController.signal.aborted ? 130 : 0; @@ -434,36 +429,6 @@ export async function main( } } -/** - * gkit parses its own argv, so argc's output-capture guard (argc - * docs/spec-7.12-output-capture.md) is applied here: help, --schema and @skill - * must be read whole, and an agent piping them into head/grep/$(…) is refused - * before anything prints. Written to stderr, since stdout is the filter. - */ -function refuseCapturedGuide(command: ReturnType, argv: string[]): boolean { - if (command.kind !== "help" && command.kind !== "schema" && command.kind !== "skill") return false; - if (!isAgentCapture()) return false; - const line = ["gkit", ...argv.map(quoteWord)].join(" "); - const out = ["gkit", command.kind].join("-"); - process.stderr.write( - [ - `error: OUTPUT_CAPTURED: gkit ${command.kind === "skill" ? "@skill" : command.kind === "schema" ? "--schema" : "help"} is meant to be read whole, and stdout is a pipe or $(…): head/tail/grep/sed/rg would hand you a fragment that reads like the whole. Nothing ran.`, - "run it alone — the shell tool shows all of it:", - ` ${line}`, - "or keep it whole in a file, then read the whole file:", - ` ${line} > "\${TMPDIR:-/tmp}/${out}.out"`, - "", - ].join("\n"), - ); - return true; -} - -function quoteWord(word: string): string { - if (/^[A-Za-z0-9_@%+:,./-][A-Za-z0-9_@%+=:,./-]*$/.test(word)) return word; - if (!/["$`\\!]/.test(word)) return `"${word}"`; - return `'${word.replaceAll("'", `'\\''`)}'`; -} - async function renderEmbeddedSkill( path: string | null, emitter: TerminalEmitter,