ghostrail 0.0.0 → 0.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 +29 -2
- package/dist/agent/claude-code.d.ts +10 -0
- package/dist/agent/claude-code.d.ts.map +1 -1
- package/dist/agent/claude-code.js +20 -0
- package/dist/agent/claude-code.js.map +1 -1
- package/dist/agent/result.d.ts.map +1 -1
- package/dist/agent/result.js +13 -2
- package/dist/agent/result.js.map +1 -1
- package/dist/backend/container.d.ts +18 -0
- package/dist/backend/container.d.ts.map +1 -1
- package/dist/backend/container.js +33 -10
- package/dist/backend/container.js.map +1 -1
- package/dist/backend/git-credentials.d.ts +18 -0
- package/dist/backend/git-credentials.d.ts.map +1 -0
- package/dist/backend/git-credentials.js +30 -0
- package/dist/backend/git-credentials.js.map +1 -0
- package/dist/backend/local-worktree.d.ts.map +1 -1
- package/dist/backend/local-worktree.js +21 -0
- package/dist/backend/local-worktree.js.map +1 -1
- package/dist/backend/types.d.ts +12 -1
- package/dist/backend/types.d.ts.map +1 -1
- package/dist/claude-profile/classify.d.ts +41 -0
- package/dist/claude-profile/classify.d.ts.map +1 -0
- package/dist/claude-profile/classify.js +103 -0
- package/dist/claude-profile/classify.js.map +1 -0
- package/dist/claude-profile/index.d.ts +3 -0
- package/dist/claude-profile/index.d.ts.map +1 -0
- package/dist/claude-profile/index.js +3 -0
- package/dist/claude-profile/index.js.map +1 -0
- package/dist/claude-profile/setup.d.ts +33 -0
- package/dist/claude-profile/setup.d.ts.map +1 -0
- package/dist/claude-profile/setup.js +81 -0
- package/dist/claude-profile/setup.js.map +1 -0
- package/dist/cli/commands.d.ts +27 -2
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +275 -17
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +7 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/help.d.ts.map +1 -1
- package/dist/commands/help.js +9 -2
- package/dist/commands/help.js.map +1 -1
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +61 -9
- package/dist/config/load.js.map +1 -1
- package/dist/config/schema.d.ts +51 -3
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +4 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/factory/build.d.ts +24 -0
- package/dist/factory/build.d.ts.map +1 -1
- package/dist/factory/build.js +80 -11
- package/dist/factory/build.js.map +1 -1
- package/dist/loop/concurrency.d.ts +13 -0
- package/dist/loop/concurrency.d.ts.map +1 -0
- package/dist/loop/concurrency.js +27 -0
- package/dist/loop/concurrency.js.map +1 -0
- package/dist/loop/engine.d.ts.map +1 -1
- package/dist/loop/engine.js +19 -14
- package/dist/loop/engine.js.map +1 -1
- package/dist/loop/index.d.ts +2 -0
- package/dist/loop/index.d.ts.map +1 -1
- package/dist/loop/index.js +2 -0
- package/dist/loop/index.js.map +1 -1
- package/dist/loop/ports.d.ts +37 -1
- package/dist/loop/ports.d.ts.map +1 -1
- package/dist/loop/watch.d.ts +25 -0
- package/dist/loop/watch.d.ts.map +1 -0
- package/dist/loop/watch.js +45 -0
- package/dist/loop/watch.js.map +1 -0
- package/dist/publish/gh.d.ts +20 -1
- package/dist/publish/gh.d.ts.map +1 -1
- package/dist/publish/gh.js +37 -3
- package/dist/publish/gh.js.map +1 -1
- package/dist/publish/githost.d.ts +12 -0
- package/dist/publish/githost.d.ts.map +1 -1
- package/dist/publish/githost.js +14 -0
- package/dist/publish/githost.js.map +1 -1
- package/dist/publish/github-publisher.d.ts +1 -1
- package/dist/publish/github-publisher.d.ts.map +1 -1
- package/dist/publish/github-publisher.js +6 -5
- package/dist/publish/github-publisher.js.map +1 -1
- package/dist/respond/respond.d.ts +10 -1
- package/dist/respond/respond.d.ts.map +1 -1
- package/dist/respond/respond.js +37 -3
- package/dist/respond/respond.js.map +1 -1
- package/dist/secrets/env-file.d.ts +23 -0
- package/dist/secrets/env-file.d.ts.map +1 -0
- package/dist/secrets/env-file.js +70 -0
- package/dist/secrets/env-file.js.map +1 -0
- package/dist/secrets/index.d.ts +4 -0
- package/dist/secrets/index.d.ts.map +1 -0
- package/dist/secrets/index.js +4 -0
- package/dist/secrets/index.js.map +1 -0
- package/dist/secrets/load.d.ts +24 -0
- package/dist/secrets/load.d.ts.map +1 -0
- package/dist/secrets/load.js +43 -0
- package/dist/secrets/load.js.map +1 -0
- package/dist/source/linear.d.ts +51 -8
- package/dist/source/linear.d.ts.map +1 -1
- package/dist/source/linear.js +213 -23
- package/dist/source/linear.js.map +1 -1
- package/dist/triage/apply.d.ts +28 -0
- package/dist/triage/apply.d.ts.map +1 -0
- package/dist/triage/apply.js +68 -0
- package/dist/triage/apply.js.map +1 -0
- package/dist/triage/engine.d.ts +31 -0
- package/dist/triage/engine.d.ts.map +1 -0
- package/dist/triage/engine.js +122 -0
- package/dist/triage/engine.js.map +1 -0
- package/dist/triage/index.d.ts +4 -0
- package/dist/triage/index.d.ts.map +1 -0
- package/dist/triage/index.js +4 -0
- package/dist/triage/index.js.map +1 -0
- package/dist/triage/plan.d.ts +37 -0
- package/dist/triage/plan.d.ts.map +1 -0
- package/dist/triage/plan.js +75 -0
- package/dist/triage/plan.js.map +1 -0
- package/docker/factory.Dockerfile +39 -0
- package/package.json +4 -3
- package/templates/{fix-bugs-local → code-local}/README.md +3 -3
- package/templates/code-local/ghostrail.toml +44 -0
- package/templates/code-local/prompts/resolve-issue.md +31 -0
- package/templates/code-local/prompts/respond.md +27 -0
- package/templates/code-local/prompts/triage.md +51 -0
- package/templates/content-loop/ghostrail.toml +5 -2
- package/templates/content-loop/prompts/respond.md +25 -0
- package/templates/fix-bugs-local/ghostrail.toml +0 -37
- package/templates/fix-bugs-local/prompts/fix-bug.md +0 -27
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ A software factory is thin glue over three primitive layers: where work comes fr
|
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
npm i -g ghostrail
|
|
23
|
-
ghostrail init
|
|
23
|
+
ghostrail init code-local # or: content-loop
|
|
24
24
|
export LINEAR_API_KEY=... # a Linear personal API key; gh must be authed
|
|
25
25
|
ghostrail run # one tick: claim, isolate, run, gate, open PRs
|
|
26
26
|
ghostrail respond # fold new human PR comments back into branches
|
|
@@ -34,7 +34,7 @@ rules apply across every factory instance.
|
|
|
34
34
|
## Self-host and cutover
|
|
35
35
|
|
|
36
36
|
Ghostrail builds itself: [`ghostrail.toml`](./ghostrail.toml) +
|
|
37
|
-
[`prompts/
|
|
37
|
+
[`prompts/resolve-issue.md`](./prompts/resolve-issue.md) point the factory at its own repo.
|
|
38
38
|
To replace the bespoke bash factories in `echotime`, `tjwrite`, and `research`,
|
|
39
39
|
see [`docs/cutover.md`](./docs/cutover.md) and the configs in
|
|
40
40
|
[`examples/`](./examples/).
|
|
@@ -51,6 +51,33 @@ pnpm lint # lint + format check (Biome)
|
|
|
51
51
|
pnpm build # compile to dist/
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
## Releasing
|
|
55
|
+
|
|
56
|
+
Releases publish from CI on a tag, so `main` and npm cannot drift.
|
|
57
|
+
|
|
58
|
+
1. Bump `version` in `package.json` and add the release to [`CHANGELOG.md`](CHANGELOG.md).
|
|
59
|
+
2. Merge that to `main`.
|
|
60
|
+
3. Tag and push: `git tag v0.1.0 && git push origin v0.1.0`.
|
|
61
|
+
|
|
62
|
+
[`.github/workflows/release.yml`](.github/workflows/release.yml) then verifies the
|
|
63
|
+
tag matches `package.json`, runs lint/typecheck/tests/build, checks the package
|
|
64
|
+
contents (the `bin` shebang, both templates and their prompts, the Dockerfile, and
|
|
65
|
+
that every advertised command appears in `help`), and publishes with provenance.
|
|
66
|
+
|
|
67
|
+
Auth is npm **Trusted Publishing** (OIDC): the workflow exchanges its GitHub
|
|
68
|
+
Actions OIDC token for a short-lived registry token at publish time, so there is
|
|
69
|
+
no long-lived `NPM_TOKEN` secret to leak or rotate.
|
|
70
|
+
|
|
71
|
+
One-time setup on npmjs.com: open the `ghostrail` package → **Settings** →
|
|
72
|
+
**Trusted Publisher** → GitHub Actions, and point it at this repository with
|
|
73
|
+
workflow filename `release.yml`. Allowed action: `npm publish`.
|
|
74
|
+
|
|
75
|
+
Provenance (the Sigstore attestation binding a tarball to its source commit and
|
|
76
|
+
build) requires a **public** repository, so the workflow gates that flag on
|
|
77
|
+
visibility: releases cut while the repo is private publish unattested, and the
|
|
78
|
+
first release after it goes public attests automatically. Provenance is
|
|
79
|
+
per-version, so earlier versions stay unattested — it cannot be backfilled.
|
|
80
|
+
|
|
54
81
|
## License
|
|
55
82
|
|
|
56
83
|
[Apache-2.0](./LICENSE).
|
|
@@ -16,6 +16,8 @@ export interface ClaudeArgsOptions {
|
|
|
16
16
|
maxBudgetUsd?: number;
|
|
17
17
|
/** `--model`, when set. */
|
|
18
18
|
model?: string;
|
|
19
|
+
/** Explicit settings file for the run, passed as `--settings`. */
|
|
20
|
+
settingsPath?: string;
|
|
19
21
|
}
|
|
20
22
|
/**
|
|
21
23
|
* Build the `claude` CLI arguments (everything after the binary) for a headless
|
|
@@ -32,6 +34,14 @@ export declare function buildClaudeArgs(options: ClaudeArgsOptions): string[];
|
|
|
32
34
|
* and parses. Budget and timeout come from the guardrails.
|
|
33
35
|
*/
|
|
34
36
|
export declare class ClaudeCodeAgent implements Agent {
|
|
37
|
+
private readonly profileDir?;
|
|
38
|
+
/**
|
|
39
|
+
* @param profileDir Optional ghostrail Claude profile. When set, the run uses
|
|
40
|
+
* it as Claude's config home (via `HOME`/`CLAUDE_CONFIG_DIR`) and its
|
|
41
|
+
* settings.json, so factory runs get the pinned skills/CLAUDE.md and never the
|
|
42
|
+
* user's interactive config.
|
|
43
|
+
*/
|
|
44
|
+
constructor(profileDir?: string | undefined);
|
|
35
45
|
run(context: AgentContext): Promise<AgentOutcome>;
|
|
36
46
|
}
|
|
37
47
|
//# sourceMappingURL=claude-code.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/agent/claude-code.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/agent/claude-code.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAI1E,+EAA+E;AAC/E,eAAO,MAAM,WAAW,2BAA2B,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IAChC,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,CAAC;IAC/C,sDAAsD;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,MAAM,EAAE,CAwBpE;AAYD;;;;;;GAMG;AACH,qBAAa,eAAgB,YAAW,KAAK;IAO/B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC;IANxC;;;;;OAKG;gBAC0B,UAAU,CAAC,EAAE,MAAM,YAAA;IAE1C,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC;CAqCxD"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
1
2
|
import { renderPrompt } from "./prompt.js";
|
|
2
3
|
import { parseAgentResult } from "./result.js";
|
|
3
4
|
/** Path, relative to the workspace root, where the agent writes its result. */
|
|
@@ -19,6 +20,9 @@ export function buildClaudeArgs(options) {
|
|
|
19
20
|
"--add-dir",
|
|
20
21
|
options.addDir,
|
|
21
22
|
];
|
|
23
|
+
if (options.settingsPath !== undefined) {
|
|
24
|
+
args.push("--settings", options.settingsPath);
|
|
25
|
+
}
|
|
22
26
|
if (options.allowedTools.length > 0) {
|
|
23
27
|
args.push("--allowedTools", options.allowedTools.join(","));
|
|
24
28
|
}
|
|
@@ -43,6 +47,16 @@ async function readFile(backend, workspace, path) {
|
|
|
43
47
|
* and parses. Budget and timeout come from the guardrails.
|
|
44
48
|
*/
|
|
45
49
|
export class ClaudeCodeAgent {
|
|
50
|
+
profileDir;
|
|
51
|
+
/**
|
|
52
|
+
* @param profileDir Optional ghostrail Claude profile. When set, the run uses
|
|
53
|
+
* it as Claude's config home (via `HOME`/`CLAUDE_CONFIG_DIR`) and its
|
|
54
|
+
* settings.json, so factory runs get the pinned skills/CLAUDE.md and never the
|
|
55
|
+
* user's interactive config.
|
|
56
|
+
*/
|
|
57
|
+
constructor(profileDir) {
|
|
58
|
+
this.profileDir = profileDir;
|
|
59
|
+
}
|
|
46
60
|
async run(context) {
|
|
47
61
|
const { backend, workspace, item, agent, guardrails } = context;
|
|
48
62
|
const template = await readFile(backend, workspace, agent.prompt);
|
|
@@ -54,9 +68,15 @@ export class ClaudeCodeAgent {
|
|
|
54
68
|
allowedTools: agent.allowedTools,
|
|
55
69
|
addDir: ".",
|
|
56
70
|
maxBudgetUsd: guardrails.budgetUsdPerItem,
|
|
71
|
+
...(this.profileDir === undefined
|
|
72
|
+
? {}
|
|
73
|
+
: { settingsPath: join(this.profileDir, ".claude", "settings.json") }),
|
|
57
74
|
});
|
|
58
75
|
const run = await backend.exec(workspace, ["claude", ...args], {
|
|
59
76
|
timeoutMs: guardrails.timeoutMs,
|
|
77
|
+
...(this.profileDir === undefined
|
|
78
|
+
? {}
|
|
79
|
+
: { env: { HOME: this.profileDir, CLAUDE_CONFIG_DIR: join(this.profileDir, ".claude") } }),
|
|
60
80
|
});
|
|
61
81
|
if (run.timedOut) {
|
|
62
82
|
return { status: "failed", error: "agent timed out" };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/agent/claude-code.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../src/agent/claude-code.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/agent/result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../../src/agent/result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAmD1D"}
|
package/dist/agent/result.js
CHANGED
|
@@ -25,8 +25,19 @@ export function parseAgentResult(raw) {
|
|
|
25
25
|
const record = value;
|
|
26
26
|
const status = record.status;
|
|
27
27
|
switch (status) {
|
|
28
|
-
case "done":
|
|
29
|
-
|
|
28
|
+
case "done": {
|
|
29
|
+
// An optional conventional-commit type (feat/fix/docs/refactor/chore/...),
|
|
30
|
+
// lowercased word chars only; ignored if malformed.
|
|
31
|
+
const rawType = record.type;
|
|
32
|
+
const type = typeof rawType === "string" && /^[a-z]+$/.test(rawType.trim().toLowerCase())
|
|
33
|
+
? rawType.trim().toLowerCase()
|
|
34
|
+
: undefined;
|
|
35
|
+
return {
|
|
36
|
+
status: "done",
|
|
37
|
+
summary: asString(record.summary),
|
|
38
|
+
...(type === undefined ? {} : { type }),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
30
41
|
case "noop":
|
|
31
42
|
return { status: "noop" };
|
|
32
43
|
case "blocked": {
|
package/dist/agent/result.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/agent/result.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC;IACxE,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,oCAAoC,EAAE,CAAC;IAC3E,CAAC;IAED,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAE7B,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM;
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../src/agent/result.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC;IACxE,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,oCAAoC,EAAE,CAAC;IAC3E,CAAC;IAED,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAE7B,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,2EAA2E;YAC3E,oDAAoD;YACpD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;YAC5B,MAAM,IAAI,GACR,OAAO,OAAO,KAAK,QAAQ,IAAI,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;gBAC1E,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;gBAC9B,CAAC,CAAC,SAAS,CAAC;YAChB,OAAO;gBACL,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC;gBACjC,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;aACxC,CAAC;QACJ,CAAC;QACD,KAAK,MAAM;YACT,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAC5B,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;YACnC,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC5D,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,+CAA+C,EAAE,CAAC;YACtF,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC;QAC1C,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YAC3B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACpD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,mDAAmD,EAAE,CAAC;YAC1F,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;QACrC,CAAC;QACD;YACE,OAAO;gBACL,MAAM,EAAE,QAAQ;gBAChB,KAAK,EAAE,uCAAuC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE;aACvE,CAAC;IACN,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -6,8 +6,26 @@ import type { Backend, ExecOptions, ExecResult, LogLine, ProvisionSpec, Workspac
|
|
|
6
6
|
* container, so the image only needs a shell (not git); commands run via
|
|
7
7
|
* `docker exec`. Teardown force-removes the container.
|
|
8
8
|
*/
|
|
9
|
+
export interface ContainerBackendOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Environment variable names to forward from the host into every `docker exec`
|
|
12
|
+
* (via `-e`), so secrets like ANTHROPIC_API_KEY / GH_TOKEN reach the agent and
|
|
13
|
+
* gate inside the container. Only names, never values, are configured; values
|
|
14
|
+
* are read from `env` at exec time.
|
|
15
|
+
*/
|
|
16
|
+
forwardEnv?: readonly string[];
|
|
17
|
+
/** Source of forwarded values; defaults to the process environment. */
|
|
18
|
+
env?: Record<string, string | undefined>;
|
|
19
|
+
}
|
|
9
20
|
export declare class ContainerBackend implements Backend {
|
|
10
21
|
readonly kind = "container";
|
|
22
|
+
private readonly forwardEnv;
|
|
23
|
+
private readonly env;
|
|
24
|
+
constructor(options?: ContainerBackendOptions);
|
|
25
|
+
/** The forwarded keys that are actually set, as a KEY=VALUE env map. */
|
|
26
|
+
private forwardedEnv;
|
|
27
|
+
/** Merge forwarded host env under the call's own env. */
|
|
28
|
+
private withForwardedEnv;
|
|
11
29
|
/** True if the Docker CLI and daemon are reachable. */
|
|
12
30
|
static isAvailable(): Promise<boolean>;
|
|
13
31
|
provision(spec: ProvisionSpec): Promise<Workspace>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/backend/container.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"container.d.ts","sourceRoot":"","sources":["../../src/backend/container.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,OAAO,EACP,WAAW,EACX,UAAU,EACV,OAAO,EACP,aAAa,EACb,SAAS,EACV,MAAM,YAAY,CAAC;AAQpB;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CAC1C;AAED,qBAAa,gBAAiB,YAAW,OAAO;IAC9C,QAAQ,CAAC,IAAI,eAAQ;IACrB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAoB;IAC/C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqC;gBAE7C,OAAO,GAAE,uBAA4B;IAKjD,wEAAwE;IACxE,OAAO,CAAC,YAAY;IASpB,yDAAyD;IACzD,OAAO,CAAC,gBAAgB;IAMxB,uDAAuD;WAC1C,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAKtC,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC;IAuCxD,IAAI,CACF,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,CAAC;IAKtB,MAAM,CACJ,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,EAC9B,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,CAAC;IAShB,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAGpD"}
|
|
@@ -2,21 +2,39 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { mkdir, rm } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
5
6
|
import { DEFAULT_CONTAINER_IMAGE, dockerCpArgs, dockerExecArgs, dockerRmArgs, dockerRunArgs, } from "./docker.js";
|
|
7
|
+
import { CLONE_TOKEN_ENV, cloneCredentialArgs } from "./git-credentials.js";
|
|
6
8
|
import { spawnCollect } from "./proc.js";
|
|
7
9
|
const KIND = "container";
|
|
8
10
|
async function docker(args, timeoutMs) {
|
|
9
11
|
return spawnCollect(["docker", ...args], timeoutMs === undefined ? {} : { timeoutMs });
|
|
10
12
|
}
|
|
11
|
-
/**
|
|
12
|
-
* The recommended default for a shared tool: a fresh, disposable container per
|
|
13
|
-
* item, on its own branch, with a clean filesystem and nothing bleeding between
|
|
14
|
-
* runs. The repo is cloned on the host onto the new branch and copied into the
|
|
15
|
-
* container, so the image only needs a shell (not git); commands run via
|
|
16
|
-
* `docker exec`. Teardown force-removes the container.
|
|
17
|
-
*/
|
|
18
13
|
export class ContainerBackend {
|
|
19
14
|
kind = KIND;
|
|
15
|
+
forwardEnv;
|
|
16
|
+
env;
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
this.forwardEnv = options.forwardEnv ?? [];
|
|
19
|
+
this.env = options.env ?? process.env;
|
|
20
|
+
}
|
|
21
|
+
/** The forwarded keys that are actually set, as a KEY=VALUE env map. */
|
|
22
|
+
forwardedEnv() {
|
|
23
|
+
const out = {};
|
|
24
|
+
for (const key of this.forwardEnv) {
|
|
25
|
+
const value = this.env[key];
|
|
26
|
+
if (value !== undefined)
|
|
27
|
+
out[key] = value;
|
|
28
|
+
}
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
/** Merge forwarded host env under the call's own env. */
|
|
32
|
+
withForwardedEnv(options) {
|
|
33
|
+
const forwarded = this.forwardedEnv();
|
|
34
|
+
if (Object.keys(forwarded).length === 0)
|
|
35
|
+
return options;
|
|
36
|
+
return { ...options, env: { ...forwarded, ...options.env } };
|
|
37
|
+
}
|
|
20
38
|
/** True if the Docker CLI and daemon are reachable. */
|
|
21
39
|
static async isAvailable() {
|
|
22
40
|
const result = await docker(["version", "--format", "{{.Server.Version}}"], 10_000);
|
|
@@ -29,7 +47,10 @@ export class ContainerBackend {
|
|
|
29
47
|
const hostClone = join(tmpdir(), "ghostrail-cc", id);
|
|
30
48
|
await mkdir(join(tmpdir(), "ghostrail-cc"), { recursive: true });
|
|
31
49
|
try {
|
|
32
|
-
const
|
|
50
|
+
const token = this.env.GH_TOKEN ?? this.env.GITHUB_TOKEN;
|
|
51
|
+
const credArgs = cloneCredentialArgs(spec.repoPath, token !== undefined);
|
|
52
|
+
const cloneOpts = token === undefined ? {} : { env: { [CLONE_TOKEN_ENV]: token } };
|
|
53
|
+
const clone = await spawnCollect(["git", ...credArgs, "clone", "--quiet", spec.repoPath, hostClone], cloneOpts);
|
|
33
54
|
if (clone.exitCode !== 0)
|
|
34
55
|
throw new Error(`git clone failed: ${clone.stderr.trim()}`);
|
|
35
56
|
// "create" makes a new branch off the base; "attach" checks out an existing branch.
|
|
@@ -54,10 +75,12 @@ export class ContainerBackend {
|
|
|
54
75
|
}
|
|
55
76
|
}
|
|
56
77
|
exec(workspace, command, options = {}) {
|
|
57
|
-
|
|
78
|
+
const opts = this.withForwardedEnv(options);
|
|
79
|
+
return docker(dockerExecArgs(workspace.ref, command, opts), opts.timeoutMs);
|
|
58
80
|
}
|
|
59
81
|
stream(workspace, command, onLog, options = {}) {
|
|
60
|
-
const
|
|
82
|
+
const opts = this.withForwardedEnv(options);
|
|
83
|
+
const args = dockerExecArgs(workspace.ref, command, opts);
|
|
61
84
|
return spawnCollect(["docker", ...args], {
|
|
62
85
|
onLog,
|
|
63
86
|
...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/backend/container.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,uBAAuB,EACvB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAUzC,MAAM,IAAI,GAAG,WAAW,CAAC;AAEzB,KAAK,UAAU,MAAM,CAAC,IAAuB,EAAE,SAAkB;IAC/D,OAAO,YAAY,CAAC,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC;AACzF,CAAC;
|
|
1
|
+
{"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/backend/container.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token-free git auth for cloning private GitHub repos. Instead of embedding a
|
|
3
|
+
* token in the clone URL (which would persist in the workspace's `origin`), we
|
|
4
|
+
* pass an inline git credential helper that reads the token from an environment
|
|
5
|
+
* variable at clone time. The persisted `origin` stays a clean URL, so the later
|
|
6
|
+
* push (in the container) authenticates through the image's baked `gh` helper.
|
|
7
|
+
*/
|
|
8
|
+
/** Env var the inline clone credential helper reads the token from. */
|
|
9
|
+
export declare const CLONE_TOKEN_ENV = "GHOSTRAIL_GIT_TOKEN";
|
|
10
|
+
/** True for an `https://github.com/...` URL, which needs a token to clone privately. */
|
|
11
|
+
export declare function isGithubHttps(url: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Extra `git` arguments (before the subcommand) that authenticate a GitHub https
|
|
14
|
+
* clone from {@link CLONE_TOKEN_ENV}. Empty when there is no token or the URL is
|
|
15
|
+
* not a GitHub https URL (local paths and other hosts clone unchanged).
|
|
16
|
+
*/
|
|
17
|
+
export declare function cloneCredentialArgs(url: string, hasToken: boolean): string[];
|
|
18
|
+
//# sourceMappingURL=git-credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-credentials.d.ts","sourceRoot":"","sources":["../../src/backend/git-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,uEAAuE;AACvE,eAAO,MAAM,eAAe,wBAAwB,CAAC;AASrD,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,EAAE,CAG5E"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token-free git auth for cloning private GitHub repos. Instead of embedding a
|
|
3
|
+
* token in the clone URL (which would persist in the workspace's `origin`), we
|
|
4
|
+
* pass an inline git credential helper that reads the token from an environment
|
|
5
|
+
* variable at clone time. The persisted `origin` stays a clean URL, so the later
|
|
6
|
+
* push (in the container) authenticates through the image's baked `gh` helper.
|
|
7
|
+
*/
|
|
8
|
+
/** Env var the inline clone credential helper reads the token from. */
|
|
9
|
+
export const CLONE_TOKEN_ENV = "GHOSTRAIL_GIT_TOKEN";
|
|
10
|
+
/**
|
|
11
|
+
* The inline helper git runs (via `sh`) to answer a GitHub https credential
|
|
12
|
+
* request: username `x-access-token`, password from {@link CLONE_TOKEN_ENV}. The
|
|
13
|
+
* token is never in the process arguments, only in the environment.
|
|
14
|
+
*/
|
|
15
|
+
const GITHUB_CREDENTIAL_HELPER = `!f() { test "$1" = get && printf 'username=x-access-token\\npassword=%s\\n' "$${CLONE_TOKEN_ENV}"; }; f`;
|
|
16
|
+
/** True for an `https://github.com/...` URL, which needs a token to clone privately. */
|
|
17
|
+
export function isGithubHttps(url) {
|
|
18
|
+
return /^https:\/\/github\.com\//i.test(url.trim());
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Extra `git` arguments (before the subcommand) that authenticate a GitHub https
|
|
22
|
+
* clone from {@link CLONE_TOKEN_ENV}. Empty when there is no token or the URL is
|
|
23
|
+
* not a GitHub https URL (local paths and other hosts clone unchanged).
|
|
24
|
+
*/
|
|
25
|
+
export function cloneCredentialArgs(url, hasToken) {
|
|
26
|
+
if (!hasToken || !isGithubHttps(url))
|
|
27
|
+
return [];
|
|
28
|
+
return ["-c", `credential.https://github.com.helper=${GITHUB_CREDENTIAL_HELPER}`];
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=git-credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-credentials.js","sourceRoot":"","sources":["../../src/backend/git-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,uEAAuE;AACvE,MAAM,CAAC,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAErD;;;;GAIG;AACH,MAAM,wBAAwB,GAAG,iFAAiF,eAAe,SAAS,CAAC;AAE3I,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,OAAO,2BAA2B,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;AACtD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,QAAiB;IAChE,IAAI,CAAC,QAAQ,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAChD,OAAO,CAAC,IAAI,EAAE,wCAAwC,wBAAwB,EAAE,CAAC,CAAC;AACpF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-worktree.d.ts","sourceRoot":"","sources":["../../src/backend/local-worktree.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"local-worktree.d.ts","sourceRoot":"","sources":["../../src/backend/local-worktree.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,OAAO,EACP,WAAW,EACX,UAAU,EACV,OAAO,EACP,aAAa,EACb,SAAS,EACV,MAAM,YAAY,CAAC;AAIpB;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,OAAO;IAClD,QAAQ,CAAC,IAAI,oBAAQ;IACrB,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6B;IAEvD,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC;IA+CxD,IAAI,CACF,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,CAAC;IAItB,MAAM,CACJ,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,EAC9B,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,CAAC;IAIhB,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAUnD,OAAO,CAAC,WAAW;CAIpB"}
|
|
@@ -2,6 +2,8 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { mkdir, rm } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { CLONE_TOKEN_ENV, cloneCredentialArgs } from "./git-credentials.js";
|
|
5
7
|
import { spawnCollect } from "./proc.js";
|
|
6
8
|
const KIND = "local-worktree";
|
|
7
9
|
/**
|
|
@@ -17,6 +19,25 @@ export class LocalWorktreeBackend {
|
|
|
17
19
|
const id = randomUUID();
|
|
18
20
|
const dir = join(tmpdir(), "ghostrail-wt", id);
|
|
19
21
|
await mkdir(join(tmpdir(), "ghostrail-wt"), { recursive: true });
|
|
22
|
+
if (spec.clone === true) {
|
|
23
|
+
// A fresh full clone (repoPath may be a remote URL); teardown just deletes it.
|
|
24
|
+
const token = process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN;
|
|
25
|
+
const credArgs = cloneCredentialArgs(spec.repoPath, token !== undefined);
|
|
26
|
+
const cloneOpts = token === undefined ? {} : { env: { [CLONE_TOKEN_ENV]: token } };
|
|
27
|
+
const clone = await spawnCollect(["git", ...credArgs, "clone", "--quiet", spec.repoPath, dir], cloneOpts);
|
|
28
|
+
if (clone.exitCode !== 0) {
|
|
29
|
+
throw new Error(`local-clone provision failed: ${clone.stderr.trim() || clone.stdout.trim()}`);
|
|
30
|
+
}
|
|
31
|
+
const checkoutArgs = spec.mode === "attach"
|
|
32
|
+
? ["checkout", spec.branch]
|
|
33
|
+
: ["checkout", "-B", spec.branch, spec.baseBranch];
|
|
34
|
+
const checkout = await spawnCollect(["git", "-C", dir, ...checkoutArgs]);
|
|
35
|
+
if (checkout.exitCode !== 0) {
|
|
36
|
+
await rm(dir, { recursive: true, force: true });
|
|
37
|
+
throw new Error(`local-clone checkout failed: ${checkout.stderr.trim()}`);
|
|
38
|
+
}
|
|
39
|
+
return { id, backend: KIND, ref: dir, branch: spec.branch };
|
|
40
|
+
}
|
|
20
41
|
// "create" makes a new branch off the base; "attach" checks out an existing branch.
|
|
21
42
|
const addArgs = spec.mode === "attach"
|
|
22
43
|
? ["worktree", "add", dir, spec.branch]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-worktree.js","sourceRoot":"","sources":["../../src/backend/local-worktree.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"local-worktree.js","sourceRoot":"","sources":["../../src/backend/local-worktree.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAUzC,MAAM,IAAI,GAAG,gBAAgB,CAAC;AAE9B;;;;GAIG;AACH,MAAM,OAAO,oBAAoB;IACtB,IAAI,GAAG,IAAI,CAAC;IACrB,4EAA4E;IAC3D,eAAe,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE7D,KAAK,CAAC,SAAS,CAAC,IAAmB;QACjC,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC;QAC/C,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,cAAc,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEjE,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;YACxB,+EAA+E;YAC/E,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;YAC/D,MAAM,QAAQ,GAAG,mBAAmB,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC;YACzE,MAAM,SAAS,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;YACnF,MAAM,KAAK,GAAG,MAAM,YAAY,CAC9B,CAAC,KAAK,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,EAC5D,SAAS,CACV,CAAC;YACF,IAAI,KAAK,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;gBACzB,MAAM,IAAI,KAAK,CACb,iCAAiC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAC9E,CAAC;YACJ,CAAC;YACD,MAAM,YAAY,GAChB,IAAI,CAAC,IAAI,KAAK,QAAQ;gBACpB,CAAC,CAAC,CAAC,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC;gBAC3B,CAAC,CAAC,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;YACvD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;YACzE,IAAI,QAAQ,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;gBAC5B,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChD,MAAM,IAAI,KAAK,CAAC,gCAAgC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC5E,CAAC;YACD,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;QAC9D,CAAC;QAED,oFAAoF;QACpF,MAAM,OAAO,GACX,IAAI,CAAC,IAAI,KAAK,QAAQ;YACpB,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,CAAC;YACvC,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QACnE,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;QAC5E,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,oCAAoC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CACnF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC5C,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IAC9D,CAAC;IAED,IAAI,CACF,SAAoB,EACpB,OAA0B,EAC1B,UAAuB,EAAE;QAEzB,OAAO,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,CACJ,SAAoB,EACpB,OAA0B,EAC1B,KAA8B,EAC9B,UAAuB,EAAE;QAEzB,OAAO,YAAY,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,SAAoB;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACxD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,MAAM,YAAY,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5F,MAAM,YAAY,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;YACjE,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QAC5C,CAAC;QACD,MAAM,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,CAAC;IAEO,WAAW,CAAC,SAAoB,EAAE,OAAoB;QAC5D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC;QACzF,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,CAAC;IAC7B,CAAC;CACF"}
|
package/dist/backend/types.d.ts
CHANGED
|
@@ -10,7 +10,10 @@
|
|
|
10
10
|
*/
|
|
11
11
|
/** How to create an isolated workspace for one item. */
|
|
12
12
|
export interface ProvisionSpec {
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* The source git repository: a local path OR a remote URL. A URL requires
|
|
15
|
+
* `clone: true`. `git clone`/`git worktree` accept either form of local path.
|
|
16
|
+
*/
|
|
14
17
|
repoPath: string;
|
|
15
18
|
/** Existing ref to branch from (e.g. "main"). Ignored when `mode` is "attach". */
|
|
16
19
|
baseBranch: string;
|
|
@@ -21,6 +24,14 @@ export interface ProvisionSpec {
|
|
|
21
24
|
* an existing `branch` (e.g. a PR's head branch, for the respond pass).
|
|
22
25
|
*/
|
|
23
26
|
mode?: "create" | "attach";
|
|
27
|
+
/**
|
|
28
|
+
* When true, provision a **fresh full clone** of `repoPath` (which may be a
|
|
29
|
+
* remote URL) into a throwaway directory, torn down after the run, so nothing
|
|
30
|
+
* persists. When false/absent, local-worktree adds a worktree off an existing
|
|
31
|
+
* local repo. The container backend always clones; this flag is for the local
|
|
32
|
+
* backend.
|
|
33
|
+
*/
|
|
34
|
+
clone?: boolean;
|
|
24
35
|
/** Container image, for backends that need one. Ignored by local-worktree. */
|
|
25
36
|
image?: string;
|
|
26
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/backend/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/backend/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,UAAU,EAAE,MAAM,CAAC;IACnB,yFAAyF;IACzF,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC3B;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,8EAA8E;AAC9E,MAAM,WAAW,SAAS;IACxB,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,GAAG,EAAE,MAAM,CAAC;IACZ,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,2EAA2E;AAC3E,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,2CAA2C;AAC3C,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qCAAqC;AACrC,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACnD,0EAA0E;IAC1E,IAAI,CACF,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,UAAU,CAAC,CAAC;IACvB,yFAAyF;IACzF,MAAM,CACJ,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,EAC9B,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,UAAU,CAAC,CAAC;IACvB,qFAAqF;IACrF,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure classification + composition for the ghostrail Claude profile: split a
|
|
3
|
+
* CLAUDE.md into sections, judge which belong in a headless factory run, flag
|
|
4
|
+
* interactive-only settings, and compose the factory CLAUDE.md + settings.
|
|
5
|
+
*/
|
|
6
|
+
export type Verdict = "keep" | "drop";
|
|
7
|
+
export interface Section {
|
|
8
|
+
/** Heading text without the leading `#`s; "(preamble)" for text before the first heading. */
|
|
9
|
+
heading: string;
|
|
10
|
+
/** The section's full text, including its heading line. */
|
|
11
|
+
body: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ClaudeMdSection extends Section {
|
|
14
|
+
verdict: Verdict;
|
|
15
|
+
/** Why a section was flagged to drop. */
|
|
16
|
+
reason?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface SettingsKeyReport {
|
|
19
|
+
key: string;
|
|
20
|
+
/** Whether the key is carried into the factory settings (false = interactive-only). */
|
|
21
|
+
carried: boolean;
|
|
22
|
+
reason?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Split a CLAUDE.md into sections at top-level (`#`/`##`) headings. Text before
|
|
26
|
+
* the first heading becomes a `(preamble)` section. Each section's `body`
|
|
27
|
+
* includes its heading line.
|
|
28
|
+
*/
|
|
29
|
+
export declare function splitSections(content: string): Section[];
|
|
30
|
+
/** Judge each section keep/drop; a section is dropped if it names an interactive-only concern. */
|
|
31
|
+
export declare function classifyClaudeMdSections(sections: readonly Section[]): ClaudeMdSection[];
|
|
32
|
+
/** Report which top-level settings keys are carried into the factory profile. */
|
|
33
|
+
export declare function classifySettingsKeys(settings: Record<string, unknown>): SettingsKeyReport[];
|
|
34
|
+
/**
|
|
35
|
+
* Compose the factory CLAUDE.md: a fixed header followed by the bodies of the
|
|
36
|
+
* kept sections, collapsing blank runs.
|
|
37
|
+
*/
|
|
38
|
+
export declare function composeFactoryClaudeMd(sections: readonly ClaudeMdSection[], header?: string): string;
|
|
39
|
+
/** The minimal, headless-safe factory settings.json (hooks land via the declarative block later). */
|
|
40
|
+
export declare function composeFactorySettings(): Record<string, unknown>;
|
|
41
|
+
//# sourceMappingURL=classify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.d.ts","sourceRoot":"","sources":["../../src/claude-profile/classify.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAEtC,MAAM,WAAW,OAAO;IACtB,6FAA6F;IAC7F,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAgB,SAAQ,OAAO;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,uFAAuF;IACvF,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAkCD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,EAAE,CAwBxD;AAED,kGAAkG;AAClG,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,eAAe,EAAE,CAQxF;AAED,iFAAiF;AACjF,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,iBAAiB,EAAE,CAM3F;AASD;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,SAAS,eAAe,EAAE,EACpC,MAAM,GAAE,MAAuB,GAC9B,MAAM,CAMR;AAED,qGAAqG;AACrG,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEhE"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure classification + composition for the ghostrail Claude profile: split a
|
|
3
|
+
* CLAUDE.md into sections, judge which belong in a headless factory run, flag
|
|
4
|
+
* interactive-only settings, and compose the factory CLAUDE.md + settings.
|
|
5
|
+
*/
|
|
6
|
+
/** Substrings (case-insensitive) that mark a CLAUDE.md section as interactive-only. */
|
|
7
|
+
const INTERACTIVE_SIGNALS = [
|
|
8
|
+
"statusline",
|
|
9
|
+
"status line",
|
|
10
|
+
"keybinding",
|
|
11
|
+
"keyboard shortcut",
|
|
12
|
+
"slash command",
|
|
13
|
+
"/config",
|
|
14
|
+
"/fast",
|
|
15
|
+
" theme",
|
|
16
|
+
"type `!`",
|
|
17
|
+
"type !",
|
|
18
|
+
"interactive session",
|
|
19
|
+
"vs code",
|
|
20
|
+
"vscode",
|
|
21
|
+
"jetbrains",
|
|
22
|
+
"ide extension",
|
|
23
|
+
];
|
|
24
|
+
/** settings.json keys that are interactive-only and are not carried into the profile. */
|
|
25
|
+
const INTERACTIVE_SETTINGS_KEYS = new Set([
|
|
26
|
+
"statusLine",
|
|
27
|
+
"theme",
|
|
28
|
+
"keybindings",
|
|
29
|
+
"spinnerTipsEnabled",
|
|
30
|
+
"autoUpdates",
|
|
31
|
+
"editorMode",
|
|
32
|
+
"verbose",
|
|
33
|
+
"preferredNotifChannel",
|
|
34
|
+
"messageIdleNotifThresholdMs",
|
|
35
|
+
]);
|
|
36
|
+
/**
|
|
37
|
+
* Split a CLAUDE.md into sections at top-level (`#`/`##`) headings. Text before
|
|
38
|
+
* the first heading becomes a `(preamble)` section. Each section's `body`
|
|
39
|
+
* includes its heading line.
|
|
40
|
+
*/
|
|
41
|
+
export function splitSections(content) {
|
|
42
|
+
const sections = [];
|
|
43
|
+
let heading;
|
|
44
|
+
let lines = [];
|
|
45
|
+
const flush = () => {
|
|
46
|
+
if (heading !== undefined) {
|
|
47
|
+
sections.push({ heading, body: lines.join("\n") });
|
|
48
|
+
}
|
|
49
|
+
else if (lines.join("\n").trim().length > 0) {
|
|
50
|
+
sections.push({ heading: "(preamble)", body: lines.join("\n") });
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
for (const line of content.split("\n")) {
|
|
54
|
+
if (/^#{1,2}\s/.test(line)) {
|
|
55
|
+
flush();
|
|
56
|
+
heading = line.replace(/^#{1,2}\s+/, "").trim();
|
|
57
|
+
lines = [line];
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
lines.push(line);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
flush();
|
|
64
|
+
return sections;
|
|
65
|
+
}
|
|
66
|
+
/** Judge each section keep/drop; a section is dropped if it names an interactive-only concern. */
|
|
67
|
+
export function classifyClaudeMdSections(sections) {
|
|
68
|
+
return sections.map((section) => {
|
|
69
|
+
const hay = `${section.heading}\n${section.body}`.toLowerCase();
|
|
70
|
+
const signal = INTERACTIVE_SIGNALS.find((s) => hay.includes(s));
|
|
71
|
+
return signal === undefined
|
|
72
|
+
? { ...section, verdict: "keep" }
|
|
73
|
+
: { ...section, verdict: "drop", reason: `mentions "${signal.trim()}" (interactive-only)` };
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/** Report which top-level settings keys are carried into the factory profile. */
|
|
77
|
+
export function classifySettingsKeys(settings) {
|
|
78
|
+
return Object.keys(settings).map((key) => INTERACTIVE_SETTINGS_KEYS.has(key)
|
|
79
|
+
? { key, carried: false, reason: "interactive-only" }
|
|
80
|
+
: { key, carried: true });
|
|
81
|
+
}
|
|
82
|
+
const FACTORY_HEADER = `# Ghostrail factory profile
|
|
83
|
+
|
|
84
|
+
This CLAUDE.md is composed by ghostrail for headless factory runs. It is not your
|
|
85
|
+
interactive CLAUDE.md: it carries a curated subset of your global rules plus the
|
|
86
|
+
target repo's own project CLAUDE.md at run time. Regenerate it with
|
|
87
|
+
\`ghostrail claude-setup --write\`.`;
|
|
88
|
+
/**
|
|
89
|
+
* Compose the factory CLAUDE.md: a fixed header followed by the bodies of the
|
|
90
|
+
* kept sections, collapsing blank runs.
|
|
91
|
+
*/
|
|
92
|
+
export function composeFactoryClaudeMd(sections, header = FACTORY_HEADER) {
|
|
93
|
+
const kept = sections.filter((s) => s.verdict === "keep").map((s) => s.body.trim());
|
|
94
|
+
return `${[header, ...kept]
|
|
95
|
+
.join("\n\n")
|
|
96
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
97
|
+
.trim()}\n`;
|
|
98
|
+
}
|
|
99
|
+
/** The minimal, headless-safe factory settings.json (hooks land via the declarative block later). */
|
|
100
|
+
export function composeFactorySettings() {
|
|
101
|
+
return { hooks: {} };
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=classify.js.map
|