ghostrail 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +20 -1
  2. package/dist/cli/commands.d.ts +40 -0
  3. package/dist/cli/commands.d.ts.map +1 -1
  4. package/dist/cli/commands.js +145 -0
  5. package/dist/cli/commands.js.map +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +7 -1
  8. package/dist/cli.js.map +1 -1
  9. package/dist/commands/help.d.ts.map +1 -1
  10. package/dist/commands/help.js +11 -0
  11. package/dist/commands/help.js.map +1 -1
  12. package/dist/skill/install.d.ts +56 -0
  13. package/dist/skill/install.d.ts.map +1 -0
  14. package/dist/skill/install.js +93 -0
  15. package/dist/skill/install.js.map +1 -0
  16. package/dist/vendor/skilltend/apply.d.ts +25 -0
  17. package/dist/vendor/skilltend/apply.d.ts.map +1 -0
  18. package/dist/vendor/skilltend/apply.js +85 -0
  19. package/dist/vendor/skilltend/apply.js.map +1 -0
  20. package/dist/vendor/skilltend/detect.d.ts +35 -0
  21. package/dist/vendor/skilltend/detect.d.ts.map +1 -0
  22. package/dist/vendor/skilltend/detect.js +187 -0
  23. package/dist/vendor/skilltend/detect.js.map +1 -0
  24. package/dist/vendor/skilltend/frontmatter.d.ts +5 -0
  25. package/dist/vendor/skilltend/frontmatter.d.ts.map +1 -0
  26. package/dist/vendor/skilltend/frontmatter.js +59 -0
  27. package/dist/vendor/skilltend/frontmatter.js.map +1 -0
  28. package/dist/vendor/skilltend/fsFacade.d.ts +24 -0
  29. package/dist/vendor/skilltend/fsFacade.d.ts.map +1 -0
  30. package/dist/vendor/skilltend/fsFacade.js +68 -0
  31. package/dist/vendor/skilltend/fsFacade.js.map +1 -0
  32. package/dist/vendor/skilltend/paths.d.ts +28 -0
  33. package/dist/vendor/skilltend/paths.d.ts.map +1 -0
  34. package/dist/vendor/skilltend/paths.js +24 -0
  35. package/dist/vendor/skilltend/paths.js.map +1 -0
  36. package/dist/vendor/skilltend/plan.d.ts +46 -0
  37. package/dist/vendor/skilltend/plan.d.ts.map +1 -0
  38. package/dist/vendor/skilltend/plan.js +79 -0
  39. package/dist/vendor/skilltend/plan.js.map +1 -0
  40. package/dist/vendor/skilltend/registry.d.ts +56 -0
  41. package/dist/vendor/skilltend/registry.d.ts.map +1 -0
  42. package/dist/vendor/skilltend/registry.js +129 -0
  43. package/dist/vendor/skilltend/registry.js.map +1 -0
  44. package/package.json +2 -2
  45. package/skill/ghostrail/SKILL.md +145 -0
@@ -0,0 +1,46 @@
1
+ import type { SkillTarget } from "./paths.js";
2
+ import type { SkillIdentity } from "./registry.js";
3
+ export type SkillAction = "create" | "update" | "unchanged" | "conflict";
4
+ export interface TargetPlan {
5
+ target: SkillTarget;
6
+ action: SkillAction;
7
+ oldVersion: string | null;
8
+ newVersion: string | null;
9
+ isSymlink: boolean;
10
+ /** Why a `conflict` was raised — surfaced to the user. */
11
+ conflictReason?: string;
12
+ }
13
+ export interface FilePlanInput {
14
+ /** Whose skill this is — decides which on-disk files we consider ours. */
15
+ skill: SkillIdentity;
16
+ target: SkillTarget;
17
+ /** Freshly downloaded SKILL.md body. */
18
+ fetched: string;
19
+ /** Current on-disk content, or null when the file is absent. */
20
+ current: string | null;
21
+ /** True when the file or its skill directory is a symlink. */
22
+ isSymlink: boolean;
23
+ /** `--force`: overwrite user-modified files and write through symlinks. */
24
+ force: boolean;
25
+ }
26
+ /**
27
+ * Plan a `copy`/`canonical` target (a real SKILL.md file). Idempotent:
28
+ * byte-identical content is a no-op. We own files whose frontmatter `name` matches
29
+ * the supplied identity; a differing foreign file (or a symlink we'd write through) is a
30
+ * conflict the user resolves with `--force`.
31
+ */
32
+ export declare function planFile(input: FilePlanInput): TargetPlan;
33
+ export interface LinkPlanInput {
34
+ target: SkillTarget;
35
+ /** Version of the freshly downloaded skill (for reporting). */
36
+ fetchedVersion: string | null;
37
+ /** Current state of the symlink path. */
38
+ existing: {
39
+ kind: "absent" | "symlink" | "other";
40
+ linkTarget?: string | null;
41
+ };
42
+ force: boolean;
43
+ }
44
+ /** Plan a `link` target (a symlink at an agent's skill dir to the canonical dir). */
45
+ export declare function planLink(input: LinkPlanInput): TargetPlan;
46
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/vendor/skilltend/plan.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,UAAU,CAAC;AAEzE,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,0EAA0E;IAC1E,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,WAAW,CAAC;IACpB,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,8DAA8D;IAC9D,SAAS,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAyBzD;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,WAAW,CAAC;IACpB,+DAA+D;IAC/D,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,yCAAyC;IACzC,QAAQ,EAAE;QAAE,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC/E,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,qFAAqF;AACrF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAiCzD"}
@@ -0,0 +1,79 @@
1
+ // Vendored from timothyjordan/a14y @ 07ef2bbb8593
2
+ // (packages/apps/cli/src/skills/plan.ts), Apache-2.0.
3
+ //
4
+ // This directory is the future `skilltend` package: a white-label engine for
5
+ // installing and maintaining ONE agent skill across coding agents. It contains
6
+ // no skills of its own and no product name — the caller supplies the skill's
7
+ // identity. Keep changes here minimal and mechanical so the eventual extraction
8
+ // to a standalone package stays a directory move (see TJ-1464).
9
+ import path from "node:path";
10
+ import { parseSkillName, parseSkillVersion } from "./frontmatter.js";
11
+ /**
12
+ * Plan a `copy`/`canonical` target (a real SKILL.md file). Idempotent:
13
+ * byte-identical content is a no-op. We own files whose frontmatter `name` matches
14
+ * the supplied identity; a differing foreign file (or a symlink we'd write through) is a
15
+ * conflict the user resolves with `--force`.
16
+ */
17
+ export function planFile(input) {
18
+ const { skill, target, fetched, current, isSymlink, force } = input;
19
+ const newVersion = parseSkillVersion(fetched);
20
+ const oldVersion = current !== null ? parseSkillVersion(current) : null;
21
+ let action;
22
+ let conflictReason;
23
+ if (current === null) {
24
+ action = "create";
25
+ }
26
+ else if (current === fetched) {
27
+ action = "unchanged";
28
+ }
29
+ else if (isSymlink && !force) {
30
+ action = "conflict";
31
+ conflictReason = "symlink — managed elsewhere; pass --force to write through it";
32
+ }
33
+ else if (parseSkillName(current) === skill.name && !isSymlink) {
34
+ action = "update";
35
+ }
36
+ else if (force) {
37
+ action = "update";
38
+ }
39
+ else {
40
+ action = "conflict";
41
+ conflictReason = "existing file looks user-modified; pass --force to overwrite";
42
+ }
43
+ return { target, action, oldVersion, newVersion, isSymlink, conflictReason };
44
+ }
45
+ /** Plan a `link` target (a symlink at an agent's skill dir to the canonical dir). */
46
+ export function planLink(input) {
47
+ const { target, existing, force } = input;
48
+ const base = {
49
+ target,
50
+ oldVersion: null,
51
+ newVersion: input.fetchedVersion,
52
+ isSymlink: existing.kind === "symlink",
53
+ };
54
+ if (existing.kind === "absent") {
55
+ return { ...base, action: "create" };
56
+ }
57
+ if (existing.kind === "symlink") {
58
+ const points = existing.linkTarget ?? "";
59
+ if (target.linkTo && path.resolve(points) === path.resolve(target.linkTo)) {
60
+ return { ...base, action: "unchanged" };
61
+ }
62
+ return force
63
+ ? { ...base, action: "update" }
64
+ : {
65
+ ...base,
66
+ action: "conflict",
67
+ conflictReason: "symlink points elsewhere; pass --force to relink",
68
+ };
69
+ }
70
+ // A real directory or file already lives here.
71
+ return force
72
+ ? { ...base, action: "update" }
73
+ : {
74
+ ...base,
75
+ action: "conflict",
76
+ conflictReason: "a real directory exists here; pass --force to replace it with a symlink",
77
+ };
78
+ }
79
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/vendor/skilltend/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Who the skill is. Supplied by the consuming product rather than baked in, so
3
+ * this engine stays white-label: it installs whatever skill it is handed, under
4
+ * whatever name, and never mentions a product of its own.
5
+ */
6
+ export interface SkillIdentity {
7
+ /** Directory name under each agent's `skills/`, and the skill's frontmatter `name`. */
8
+ name: string;
9
+ /** Skill filename. Effectively always `SKILL.md` per the agent-skills convention. */
10
+ filename: string;
11
+ }
12
+ /** The agent-skills convention: `<skillsDir>/<name>/SKILL.md`. */
13
+ export declare const DEFAULT_SKILL_FILENAME = "SKILL.md";
14
+ /** Build an identity for `name`, defaulting the filename to the convention. */
15
+ export declare function skillIdentity(name: string, filename?: string): SkillIdentity;
16
+ export interface PathCtx {
17
+ /** Home directory (injected `os.homedir()`). */
18
+ home: string;
19
+ /** Current working directory (injected `process.cwd()`). */
20
+ cwd: string;
21
+ /** Environment (injected `process.env`) for XDG / agent-specific overrides. */
22
+ env: Record<string, string | undefined>;
23
+ }
24
+ export interface AgentEntry {
25
+ /** Stable key used by `--agent`. */
26
+ name: string;
27
+ /** Human label for output, e.g. "Claude Code". */
28
+ label: string;
29
+ /** Dirs whose existence marks the agent as configured (any match detects it). */
30
+ detectDirs(ctx: PathCtx): string[];
31
+ /** Absolute skills directory for a global (home) install. */
32
+ globalSkillsDir(ctx: PathCtx): string;
33
+ /**
34
+ * Relative skills dir for a project-local install, and the label shown in the
35
+ * harness picker.
36
+ *
37
+ * Note the asymmetry with `globalSkillsDir`: in a *project*, many agents read
38
+ * the shared cross-agent `.agents/skills` convention, while in the *home*
39
+ * directory each keeps its own. Verified against vercel-labs/skills, whose
40
+ * agent list is maintained by PRs from the agent authors themselves.
41
+ */
42
+ localDir: string;
43
+ }
44
+ export declare const AGENT_REGISTRY: AgentEntry[];
45
+ /** Absolute project-local skills dir for an agent. */
46
+ export declare function localSkillsDir(a: AgentEntry, ctx: PathCtx): string;
47
+ /** Fallback agent when nothing is auto-detected in a fresh environment. */
48
+ export declare const DEFAULT_AGENT = "claude";
49
+ export declare function agentByName(name: string): AgentEntry | undefined;
50
+ /**
51
+ * The shared cross-agent skills directory (the `.agents/skills` standard). Used
52
+ * as the canonical home for the symlink install mode: the skill lives here once
53
+ * and each agent links to it.
54
+ */
55
+ export declare function sharedSkillsDir(ctx: PathCtx, scope: "global" | "local"): string;
56
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/vendor/skilltend/registry.ts"],"names":[],"mappings":"AAeA;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,kEAAkE;AAClE,eAAO,MAAM,sBAAsB,aAAa,CAAC;AAEjD,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,SAAyB,GAAG,aAAa,CAE5F;AAED,MAAM,WAAW,OAAO;IACtB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,UAAU;IACzB,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,EAAE,CAAC;IACnC,6DAA6D;IAC7D,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAAC;IACtC;;;;;;;;OAQG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAkBD,eAAO,MAAM,cAAc,EAAE,UAAU,EA8EtC,CAAC;AAEF,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,CAAC,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,GAAG,MAAM,CAElE;AAED,2EAA2E;AAC3E,eAAO,MAAM,aAAa,WAAW,CAAC;AAEtC,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAEhE;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,CAI/E"}
@@ -0,0 +1,129 @@
1
+ // Vendored from timothyjordan/a14y @ 07ef2bbb8593
2
+ // (packages/apps/cli/src/skills/registry.ts), Apache-2.0.
3
+ //
4
+ // This directory is the future `skilltend` package: a white-label engine for
5
+ // installing and maintaining ONE agent skill across coding agents. It contains
6
+ // no skills of its own and no product name — the caller supplies the skill's
7
+ // identity. Keep changes here minimal and mechanical so the eventual extraction
8
+ // to a standalone package stays a directory move (see TJ-1464).
9
+ import path from "node:path";
10
+ /** The agent-skills convention: `<skillsDir>/<name>/SKILL.md`. */
11
+ export const DEFAULT_SKILL_FILENAME = "SKILL.md";
12
+ /** Build an identity for `name`, defaulting the filename to the convention. */
13
+ export function skillIdentity(name, filename = DEFAULT_SKILL_FILENAME) {
14
+ return { name, filename };
15
+ }
16
+ function inHome(ctx, ...segs) {
17
+ return path.join(ctx.home, ...segs);
18
+ }
19
+ function xdgConfigHome(ctx) {
20
+ const x = ctx.env.XDG_CONFIG_HOME?.trim();
21
+ return x ? x : path.join(ctx.home, ".config");
22
+ }
23
+ function claudeConfigDir(ctx) {
24
+ const c = ctx.env.CLAUDE_CONFIG_DIR?.trim();
25
+ return c ? c : inHome(ctx, ".claude");
26
+ }
27
+ function codexHome(ctx) {
28
+ const c = ctx.env.CODEX_HOME?.trim();
29
+ return c ? c : inHome(ctx, ".codex");
30
+ }
31
+ export const AGENT_REGISTRY = [
32
+ {
33
+ name: "claude",
34
+ label: "Claude Code",
35
+ detectDirs: (c) => [claudeConfigDir(c)],
36
+ globalSkillsDir: (c) => path.join(claudeConfigDir(c), "skills"),
37
+ localDir: ".claude/skills",
38
+ },
39
+ {
40
+ name: "cursor",
41
+ label: "Cursor",
42
+ detectDirs: (c) => [inHome(c, ".cursor")],
43
+ globalSkillsDir: (c) => inHome(c, ".cursor", "skills"),
44
+ localDir: ".agents/skills",
45
+ },
46
+ {
47
+ name: "copilot",
48
+ label: "GitHub Copilot",
49
+ detectDirs: (c) => [inHome(c, ".copilot")],
50
+ globalSkillsDir: (c) => inHome(c, ".copilot", "skills"),
51
+ localDir: ".agents/skills",
52
+ },
53
+ {
54
+ name: "gemini",
55
+ label: "Gemini CLI",
56
+ detectDirs: (c) => [inHome(c, ".gemini")],
57
+ globalSkillsDir: (c) => inHome(c, ".gemini", "skills"),
58
+ localDir: ".agents/skills",
59
+ },
60
+ {
61
+ name: "codex",
62
+ label: "Codex",
63
+ detectDirs: (c) => [codexHome(c)],
64
+ globalSkillsDir: (c) => path.join(codexHome(c), "skills"),
65
+ localDir: ".agents/skills",
66
+ },
67
+ {
68
+ name: "windsurf",
69
+ label: "Windsurf",
70
+ detectDirs: (c) => [inHome(c, ".codeium", "windsurf")],
71
+ globalSkillsDir: (c) => inHome(c, ".codeium", "windsurf", "skills"),
72
+ localDir: ".windsurf/skills",
73
+ },
74
+ {
75
+ name: "antigravity",
76
+ label: "Antigravity",
77
+ detectDirs: (c) => [inHome(c, ".gemini", "antigravity")],
78
+ globalSkillsDir: (c) => inHome(c, ".gemini", "antigravity", "skills"),
79
+ localDir: ".agents/skills",
80
+ },
81
+ {
82
+ name: "cline",
83
+ label: "Cline",
84
+ detectDirs: (c) => [inHome(c, ".cline")],
85
+ globalSkillsDir: (c) => inHome(c, ".agents", "skills"),
86
+ localDir: ".agents/skills",
87
+ },
88
+ {
89
+ name: "opencode",
90
+ label: "OpenCode",
91
+ detectDirs: (c) => [path.join(xdgConfigHome(c), "opencode")],
92
+ globalSkillsDir: (c) => path.join(xdgConfigHome(c), "opencode", "skills"),
93
+ localDir: ".agents/skills",
94
+ },
95
+ {
96
+ name: "roo",
97
+ label: "Roo Code",
98
+ detectDirs: (c) => [inHome(c, ".roo")],
99
+ globalSkillsDir: (c) => inHome(c, ".roo", "skills"),
100
+ localDir: ".roo/skills",
101
+ },
102
+ {
103
+ name: "zed",
104
+ label: "Zed",
105
+ detectDirs: (c) => [path.join(xdgConfigHome(c), "zed")],
106
+ globalSkillsDir: (c) => inHome(c, ".agents", "skills"),
107
+ localDir: ".agents/skills",
108
+ },
109
+ ];
110
+ /** Absolute project-local skills dir for an agent. */
111
+ export function localSkillsDir(a, ctx) {
112
+ return path.join(ctx.cwd, ...a.localDir.split("/"));
113
+ }
114
+ /** Fallback agent when nothing is auto-detected in a fresh environment. */
115
+ export const DEFAULT_AGENT = "claude";
116
+ export function agentByName(name) {
117
+ return AGENT_REGISTRY.find((a) => a.name === name);
118
+ }
119
+ /**
120
+ * The shared cross-agent skills directory (the `.agents/skills` standard). Used
121
+ * as the canonical home for the symlink install mode: the skill lives here once
122
+ * and each agent links to it.
123
+ */
124
+ export function sharedSkillsDir(ctx, scope) {
125
+ return scope === "global"
126
+ ? inHome(ctx, ".agents", "skills")
127
+ : path.join(ctx.cwd, ".agents", "skills");
128
+ }
129
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/vendor/skilltend/registry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ghostrail",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Open source software factory for coding agents. Config-driven loops, pluggable isolation backends, human merge gate.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -24,7 +24,7 @@
24
24
  "bin": {
25
25
  "ghostrail": "dist/bin.js"
26
26
  },
27
- "files": ["dist", "templates", "docker", "LICENSE", "NOTICE", "README.md"],
27
+ "files": ["dist", "templates", "skill", "docker", "LICENSE", "NOTICE", "README.md"],
28
28
  "engines": {
29
29
  "node": ">=22"
30
30
  },
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: ghostrail
3
+ description: Drive ghostrail (the software factory) from inside this repo. Use when the user types /ghostrail, or asks to set up, customize, check, or run a ghostrail factory — scaffolding ghostrail.toml and prompts, tailoring the scaffolded prompts and gate commands to this codebase, diagnosing a broken setup, or kicking off a run/respond/triage tick.
4
+ user-invocable: true
5
+ argument-hint: "[customize | init | doctor | status | run | respond | triage]"
6
+ license: Apache-2.0
7
+ metadata:
8
+ version: 0.1.0
9
+ allowed-tools:
10
+ - Read
11
+ - Write
12
+ - Edit
13
+ - Grep
14
+ - Glob
15
+ - Bash(npx ghostrail *)
16
+ - Bash(ghostrail *)
17
+ - Bash(git *)
18
+ - Bash(ls *)
19
+ - Bash(cat *)
20
+ - Bash(docker images *)
21
+ - Bash(gh auth status *)
22
+ ---
23
+
24
+ # /ghostrail — drive the factory from the repo
25
+
26
+ Ghostrail turns this repo into a software factory: it finds eligible tracker
27
+ work, isolates it, runs a sandboxed coding agent that only writes files, gates
28
+ the result against a mirror of CI, and opens a pull request. It never merges.
29
+
30
+ Dispatch on the argument. With no argument, run **doctor** and then say what the
31
+ useful next step is.
32
+
33
+ ## Resolving the CLI (do this first, every time)
34
+
35
+ Pick the first that works and reuse it for the whole session:
36
+
37
+ 1. `ghostrail --help` (a global install)
38
+ 2. `npx ghostrail --help` (published package)
39
+ 3. A local dev checkout, if this *is* the ghostrail repo: `node dist/bin.js --help`
40
+
41
+ Refer to the chosen form as `<GR>` below. If none work, say so and stop; do not
42
+ hand-roll the factory's behavior.
43
+
44
+ ---
45
+
46
+ ## customize
47
+
48
+ The headline command. `ghostrail init` scaffolds deliberately generic prompts;
49
+ this tailors them to *this* repo. Never guess: read before proposing.
50
+
51
+ 1. **Confirm the setup exists.** If there is no `ghostrail.toml`, say so and
52
+ offer to run **init** first.
53
+ 2. **Survey the repo.** Gather evidence, don't assume:
54
+ - Language and layout: manifest files, workspace/monorepo config, `src/` shape.
55
+ - **Real CI commands** — read the CI workflow (e.g. `.github/workflows/*.yml`)
56
+ and the package scripts. The gate must mirror CI, in CI's order, including
57
+ any env vars a build step needs.
58
+ - Test conventions: framework, where tests live, naming.
59
+ - House style: a `CONTRIBUTING.md`, `AGENTS.md`, `CLAUDE.md`, or lint config.
60
+ 3. **Propose edits, then apply on approval.** Show a concrete diff for:
61
+ - `prompts/resolve-issue.md` — the stack, the layout, where code and tests
62
+ live, the project's conventions, and the exact lint/typecheck/test commands
63
+ the agent should run. Keep the `.ghostrail/result.json` contract intact.
64
+ - `prompts/respond.md` — same context; keep it reading `.ghostrail/feedback.md`.
65
+ - `prompts/triage.md` — what a right-sized issue looks like in this codebase.
66
+ - `[gate].commands` — mirroring CI exactly.
67
+ - `[source]` — the team/project/label/assignee filters, if you can tell what
68
+ they should be. Ask rather than invent tracker identifiers.
69
+ 4. **Do not touch** `[output].auto_merge` (it must stay `false`) or invent
70
+ credentials.
71
+
72
+ State plainly what you changed and what still needs a human (tracker IDs, tokens).
73
+
74
+ ---
75
+
76
+ ## init
77
+
78
+ Scaffold the factory into this repo, then hand off to **customize**.
79
+
80
+ 1. Choose the template from the repo's nature, and say why:
81
+ - `code-local` — a code repo (gated by CI, opens PRs).
82
+ - `content-loop` — a writing/content repo (no code gate, draft PRs).
83
+ 2. Run `<GR> init <template>`. It is non-clobbering: existing files are kept
84
+ unless `--force`. Report what was written vs kept.
85
+ 3. Tell the user the two things the scaffold can't know (repo-specific prompt
86
+ detail and the tracker filters), and offer to run **customize** now.
87
+
88
+ ---
89
+
90
+ ## doctor
91
+
92
+ Diagnose a setup without changing anything. Check and report each, with the fix:
93
+
94
+ - **CLI** — which form resolved, and its `<GR> version`.
95
+ - **Config** — `ghostrail.toml` present, and it parses. Any ghostrail command
96
+ prints config errors with a dotted path; use that to point at the exact key.
97
+ - **Prompts** — every path referenced by `[agent].prompt`, `[respond].prompt`,
98
+ and `[triage].prompt` exists.
99
+ - **Gate** — `[gate].commands` is non-empty for a code factory, and the commands
100
+ actually exist in the project's scripts.
101
+ - **Credentials** — report which of `LINEAR_API_KEY`, `GH_TOKEN`/`GITHUB_TOKEN`,
102
+ and `ANTHROPIC_API_KEY` **or** `CLAUDE_CODE_OAUTH_TOKEN` are set. Report only
103
+ set/unset, **never print a value**. Note that `ANTHROPIC_API_KEY` wins over the
104
+ OAuth token, so it must be unset to bill a Pro/Max subscription.
105
+ - **GitHub** — `gh auth status` succeeds.
106
+ - **Container backend**, if `[backend].kind = "container"` — the configured
107
+ `image` exists locally (`docker images`). If not, it must be built.
108
+
109
+ Finish with a short ordered list of what to fix first.
110
+
111
+ ---
112
+
113
+ ## status
114
+
115
+ Read-only picture of the factory: the configured queues (source/triage
116
+ eligibility filters), and recent runs from the local store under `.factory/`
117
+ (`events.ndjson`). Summarize the last few runs and their outcomes. For a live
118
+ view, suggest `<GR> board`.
119
+
120
+ ---
121
+
122
+ ## run · respond · triage
123
+
124
+ Passthroughs. Before executing, state what will happen and confirm, because these
125
+ act on real tracker items and can open real pull requests.
126
+
127
+ - `run` — one tick over the fix queue. **If `[output].open_pr` is true, this opens
128
+ a real PR.** Suggest a dry run first (`open_pr = false`) for a new setup.
129
+ - `respond` — folds new PR and tracker comments into existing factory branches.
130
+ - `triage` — decomposes large issues into sub-issues (creates real tracker items).
131
+
132
+ Run `<GR> <command>` and summarize the resulting JSON: per item, the state
133
+ (`done` / `blocked` / `failed` / `noop` / `skipped`), the gate steps if present,
134
+ and any PR URL. On failure, quote the actual error rather than paraphrasing.
135
+
136
+ ---
137
+
138
+ ## Rules
139
+
140
+ - **Never** set `auto_merge = true` or merge a factory PR. A human owns merges.
141
+ - **Never** print or commit a secret. Report credentials as set/unset only.
142
+ - Prefer editing the scaffolded prompts over rewriting them; they already encode
143
+ the `.ghostrail/result.json` and `.ghostrail/triage.json` contracts the factory
144
+ depends on, and breaking those breaks the loop.
145
+ - When you don't know a tracker identifier (team key, project, labels), ask.