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.
- package/README.md +20 -1
- package/dist/cli/commands.d.ts +40 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +145 -0
- 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 +11 -0
- package/dist/commands/help.js.map +1 -1
- package/dist/skill/install.d.ts +56 -0
- package/dist/skill/install.d.ts.map +1 -0
- package/dist/skill/install.js +93 -0
- package/dist/skill/install.js.map +1 -0
- package/dist/vendor/skilltend/apply.d.ts +25 -0
- package/dist/vendor/skilltend/apply.d.ts.map +1 -0
- package/dist/vendor/skilltend/apply.js +85 -0
- package/dist/vendor/skilltend/apply.js.map +1 -0
- package/dist/vendor/skilltend/detect.d.ts +35 -0
- package/dist/vendor/skilltend/detect.d.ts.map +1 -0
- package/dist/vendor/skilltend/detect.js +187 -0
- package/dist/vendor/skilltend/detect.js.map +1 -0
- package/dist/vendor/skilltend/frontmatter.d.ts +5 -0
- package/dist/vendor/skilltend/frontmatter.d.ts.map +1 -0
- package/dist/vendor/skilltend/frontmatter.js +59 -0
- package/dist/vendor/skilltend/frontmatter.js.map +1 -0
- package/dist/vendor/skilltend/fsFacade.d.ts +24 -0
- package/dist/vendor/skilltend/fsFacade.d.ts.map +1 -0
- package/dist/vendor/skilltend/fsFacade.js +68 -0
- package/dist/vendor/skilltend/fsFacade.js.map +1 -0
- package/dist/vendor/skilltend/paths.d.ts +28 -0
- package/dist/vendor/skilltend/paths.d.ts.map +1 -0
- package/dist/vendor/skilltend/paths.js +24 -0
- package/dist/vendor/skilltend/paths.js.map +1 -0
- package/dist/vendor/skilltend/plan.d.ts +46 -0
- package/dist/vendor/skilltend/plan.d.ts.map +1 -0
- package/dist/vendor/skilltend/plan.js +79 -0
- package/dist/vendor/skilltend/plan.js.map +1 -0
- package/dist/vendor/skilltend/registry.d.ts +56 -0
- package/dist/vendor/skilltend/registry.d.ts.map +1 -0
- package/dist/vendor/skilltend/registry.js +129 -0
- package/dist/vendor/skilltend/registry.js.map +1 -0
- package/package.json +2 -2
- 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":"AAAA,kDAAkD;AAClD,sDAAsD;AACtD,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,6EAA6E;AAC7E,gFAAgF;AAChF,gEAAgE;AAChE,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AA8BrE;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,KAAoB;IAC3C,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IACpE,MAAM,UAAU,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAExE,IAAI,MAAmB,CAAC;IACxB,IAAI,cAAkC,CAAC;IAEvC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,MAAM,GAAG,QAAQ,CAAC;IACpB,CAAC;SAAM,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QAC/B,MAAM,GAAG,WAAW,CAAC;IACvB,CAAC;SAAM,IAAI,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,GAAG,UAAU,CAAC;QACpB,cAAc,GAAG,+DAA+D,CAAC;IACnF,CAAC;SAAM,IAAI,cAAc,CAAC,OAAO,CAAC,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;QAChE,MAAM,GAAG,QAAQ,CAAC;IACpB,CAAC;SAAM,IAAI,KAAK,EAAE,CAAC;QACjB,MAAM,GAAG,QAAQ,CAAC;IACpB,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,UAAU,CAAC;QACpB,cAAc,GAAG,8DAA8D,CAAC;IAClF,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC;AAC/E,CAAC;AAWD,qFAAqF;AACrF,MAAM,UAAU,QAAQ,CAAC,KAAoB;IAC3C,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC;IAC1C,MAAM,IAAI,GAAG;QACX,MAAM;QACN,UAAU,EAAE,IAAI;QAChB,UAAU,EAAE,KAAK,CAAC,cAAc;QAChC,SAAS,EAAE,QAAQ,CAAC,IAAI,KAAK,SAAS;KACvC,CAAC;IAEF,IAAI,QAAQ,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;IACvC,CAAC;IACD,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,QAAQ,CAAC,UAAU,IAAI,EAAE,CAAC;QACzC,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1E,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAC1C,CAAC;QACD,OAAO,KAAK;YACV,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE;YAC/B,CAAC,CAAC;gBACE,GAAG,IAAI;gBACP,MAAM,EAAE,UAAU;gBAClB,cAAc,EAAE,kDAAkD;aACnE,CAAC;IACR,CAAC;IACD,+CAA+C;IAC/C,OAAO,KAAK;QACV,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE;QAC/B,CAAC,CAAC;YACE,GAAG,IAAI;YACP,MAAM,EAAE,UAAU;YAClB,cAAc,EAAE,yEAAyE;SAC1F,CAAC;AACR,CAAC"}
|
|
@@ -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":"AAAA,kDAAkD;AAClD,0DAA0D;AAC1D,EAAE;AACF,6EAA6E;AAC7E,+EAA+E;AAC/E,6EAA6E;AAC7E,gFAAgF;AAChF,gEAAgE;AAChE,OAAO,IAAI,MAAM,WAAW,CAAC;AAmB7B,kEAAkE;AAClE,MAAM,CAAC,MAAM,sBAAsB,GAAG,UAAU,CAAC;AAEjD,+EAA+E;AAC/E,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,QAAQ,GAAG,sBAAsB;IAC3E,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC5B,CAAC;AAgCD,SAAS,MAAM,CAAC,GAAY,EAAE,GAAG,IAAc;IAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC;AACtC,CAAC;AACD,SAAS,aAAa,CAAC,GAAY;IACjC,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAC1C,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;AAChD,CAAC;AACD,SAAS,eAAe,CAAC,GAAY;IACnC,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,iBAAiB,EAAE,IAAI,EAAE,CAAC;IAC5C,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;AACxC,CAAC;AACD,SAAS,SAAS,CAAC,GAAY;IAC7B,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;IACrC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAiB;IAC1C;QACE,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,aAAa;QACpB,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QACvC,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC;QAC/D,QAAQ,EAAE,gBAAgB;KAC3B;IACD;QACE,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,QAAQ;QACf,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QACzC,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC;QACtD,QAAQ,EAAE,gBAAgB;KAC3B;IACD;QACE,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,gBAAgB;QACvB,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAC1C,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC;QACvD,QAAQ,EAAE,gBAAgB;KAC3B;IACD;QACE,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,YAAY;QACnB,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QACzC,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC;QACtD,QAAQ,EAAE,gBAAgB;KAC3B;IACD;QACE,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,OAAO;QACd,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACjC,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC;QACzD,QAAQ,EAAE,gBAAgB;KAC3B;IACD;QACE,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,UAAU;QACjB,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC;QACtD,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC;QACnE,QAAQ,EAAE,kBAAkB;KAC7B;IACD;QACE,IAAI,EAAE,aAAa;QACnB,KAAK,EAAE,aAAa;QACpB,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;QACxD,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,QAAQ,CAAC;QACrE,QAAQ,EAAE,gBAAgB;KAC3B;IACD;QACE,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,OAAO;QACd,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QACxC,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC;QACtD,QAAQ,EAAE,gBAAgB;KAC3B;IACD;QACE,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,UAAU;QACjB,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAC5D,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,QAAQ,CAAC;QACzE,QAAQ,EAAE,gBAAgB;KAC3B;IACD;QACE,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,UAAU;QACjB,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACtC,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC;QACnD,QAAQ,EAAE,aAAa;KACxB;IACD;QACE,IAAI,EAAE,KAAK;QACX,KAAK,EAAE,KAAK;QACZ,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACvD,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC;QACtD,QAAQ,EAAE,gBAAgB;KAC3B;CACF,CAAC;AAEF,sDAAsD;AACtD,MAAM,UAAU,cAAc,CAAC,CAAa,EAAE,GAAY;IACxD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,MAAM,aAAa,GAAG,QAAQ,CAAC;AAEtC,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACrD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAY,EAAE,KAAyB;IACrE,OAAO,KAAK,KAAK,QAAQ;QACvB,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC;QAClC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC9C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ghostrail",
|
|
3
|
-
"version": "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.
|