tracerkit 1.11.3 → 1.13.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 +24 -10
- package/dist/bin.js +78 -24
- package/dist/index.js +2 -2
- package/dist/uninstall-9tiXr-15.js +205 -0
- package/package.json +1 -1
- package/skills/brief/SKILL.md +45 -23
- package/skills/check/SKILL.md +78 -33
- package/skills/plan/SKILL.md +81 -27
- package/skills/prd/SKILL.md +66 -96
- package/dist/uninstall-BhTMOfMb.js +0 -153
package/README.md
CHANGED
|
@@ -19,9 +19,9 @@ Named after the tracer-bullet technique from _The Pragmatic Programmer_: **Trace
|
|
|
19
19
|
|
|
20
20
|
## Why TracerKit?
|
|
21
21
|
|
|
22
|
-
AI assistants work best with small, well-scoped tasks
|
|
22
|
+
AI assistants work best with small, well-scoped tasks, not sprawling layers or flat task lists. TracerKit structures every feature as **tracer-bullet vertical slices**: each phase cuts through every layer (schema → service → API → UI → tests) and is demoable on its own. Integration problems surface early, not at the end.
|
|
23
23
|
|
|
24
|
-
The workflow is three skills: **define** (`/tk:prd`), **plan** (`/tk:plan`), **verify** (`/tk:check`). Skills are pure Markdown with zero runtime deps
|
|
24
|
+
The workflow is three skills: **define** (`/tk:prd`), **plan** (`/tk:plan`), **verify** (`/tk:check`). Skills are pure Markdown with zero runtime deps. The AI reads your specs directly, counts progress, and archives completed work. No build step, no CLI at runtime.
|
|
25
25
|
|
|
26
26
|
## Get Started
|
|
27
27
|
|
|
@@ -32,7 +32,7 @@ npm install -g tracerkit
|
|
|
32
32
|
tracerkit init
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
Skills are installed to `~/.claude/skills/`, available in every project. Safe to re-run
|
|
35
|
+
Skills are installed to `~/.claude/skills/`, available in every project. Safe to re-run: adds missing skills without overwriting ones you've modified.
|
|
36
36
|
|
|
37
37
|
<details>
|
|
38
38
|
<summary>Per-project install (team members get skills via git)</summary>
|
|
@@ -54,7 +54,7 @@ Inside Claude Code, run:
|
|
|
54
54
|
/plugin install tk@claude-plugins-official
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
Run `/reload-plugins` if needed. Skills are available immediately
|
|
57
|
+
Run `/reload-plugins` if needed. Skills are available immediately, no build step, no config.
|
|
58
58
|
|
|
59
59
|
</details>
|
|
60
60
|
|
|
@@ -91,6 +91,20 @@ AI: | Feature | Status | Age | Progress | Next
|
|
|
91
91
|
|
|
92
92
|
See [Examples](docs/examples.md) for full walkthroughs.
|
|
93
93
|
|
|
94
|
+
<details>
|
|
95
|
+
<summary>GitHub Issues as storage backend</summary>
|
|
96
|
+
|
|
97
|
+
Same skills, same workflow. Storage is configured per-project:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
tracerkit config storage github # set current project to use GitHub
|
|
101
|
+
tracerkit config github.repo org/repo # set target repo
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
PRDs and plans become GitHub Issues with `tk:prd` and `tk:plan` labels. On `/tk:check` pass, issues are closed instead of archived locally. Each project can use a different backend; local is the default. See [Configuration](docs/configuration.md) for details.
|
|
105
|
+
|
|
106
|
+
</details>
|
|
107
|
+
|
|
94
108
|
## Skills
|
|
95
109
|
|
|
96
110
|
TracerKit ships skills that take a feature from idea to verified archive.
|
|
@@ -99,19 +113,19 @@ TracerKit ships skills that take a feature from idea to verified archive.
|
|
|
99
113
|
|
|
100
114
|
Interactive interview that explores your codebase, asks scoping questions one at a time, designs deep modules, and writes a structured PRD.
|
|
101
115
|
|
|
102
|
-
**Output:** `.tracerkit/prds/<slug>.md`
|
|
116
|
+
**Output:** `.tracerkit/prds/<slug>.md` (local) or GitHub Issue with `tk:prd` label
|
|
103
117
|
|
|
104
118
|
### `/tk:plan <slug>`: Create an implementation plan
|
|
105
119
|
|
|
106
120
|
Reads a PRD and breaks it into phased **tracer-bullet vertical slices**. Each phase is a thin but complete path through every layer (schema, service, API, UI, tests), demoable on its own.
|
|
107
121
|
|
|
108
|
-
**Output:** `.tracerkit/plans/<slug>.md`
|
|
122
|
+
**Output:** `.tracerkit/plans/<slug>.md` (local) or GitHub Issue with `tk:plan` label
|
|
109
123
|
|
|
110
124
|
### `/tk:brief`: Session briefing
|
|
111
125
|
|
|
112
126
|
Shows active features, their progress, and suggested focus. Use at the start of a session to orient.
|
|
113
127
|
|
|
114
|
-
**Output:** Feature dashboard in the terminal
|
|
128
|
+
**Output:** Feature dashboard in the terminal. No files written.
|
|
115
129
|
|
|
116
130
|
### `/tk:check [slug]`: Verify and archive
|
|
117
131
|
|
|
@@ -119,15 +133,15 @@ Verifies the codebase against the plan's done-when checkboxes. Runs tests, valid
|
|
|
119
133
|
|
|
120
134
|
Without arguments, shows a feature dashboard with status and progress before asking which feature to check.
|
|
121
135
|
|
|
122
|
-
**Output:** Verdict block
|
|
136
|
+
**Output:** Verdict block appended to the plan. On `done`: archives to `.tracerkit/archives/<slug>/` (local) or closes both issues (GitHub).
|
|
123
137
|
|
|
124
138
|
## Docs
|
|
125
139
|
|
|
126
140
|
| Document | Description |
|
|
127
141
|
| ------------------------------------------------ | -------------------------------------------------- |
|
|
128
142
|
| [Examples](docs/examples.md) | Walk through end-to-end usage scenarios |
|
|
129
|
-
| [CLI Reference](docs/cli-reference.md) |
|
|
130
|
-
| [Configuration](docs/configuration.md) |
|
|
143
|
+
| [CLI Reference](docs/cli-reference.md) | Commands: init, update, config, uninstall |
|
|
144
|
+
| [Configuration](docs/configuration.md) | Storage backends, GitHub options, custom paths |
|
|
131
145
|
| [Metadata Lifecycle](docs/metadata-lifecycle.md) | Understand YAML frontmatter states and transitions |
|
|
132
146
|
| [Comparison](docs/comparison.md) | Compare TracerKit to Spec Kit, Kiro, and OpenSpec |
|
|
133
147
|
|
package/dist/bin.js
CHANGED
|
@@ -1,44 +1,98 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as e,
|
|
3
|
-
import { readFileSync as
|
|
4
|
-
import { dirname as
|
|
5
|
-
import { fileURLToPath as
|
|
6
|
-
import { homedir as
|
|
2
|
+
import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, r as c, t as l } from "./uninstall-9tiXr-15.js";
|
|
3
|
+
import { existsSync as u, readFileSync as d, statSync as f } from "node:fs";
|
|
4
|
+
import { dirname as p, join as m, resolve as h } from "node:path";
|
|
5
|
+
import { fileURLToPath as g } from "node:url";
|
|
6
|
+
import { homedir as _ } from "node:os";
|
|
7
|
+
//#region src/commands/config.ts
|
|
8
|
+
function v(e, t) {
|
|
9
|
+
let [n, r] = t;
|
|
10
|
+
return n ? r ? x(e, n, r) : b(e, n) : y(e);
|
|
11
|
+
}
|
|
12
|
+
function y(e) {
|
|
13
|
+
let t = n(e), r = t.storage === "local" ? {
|
|
14
|
+
storage: t.storage,
|
|
15
|
+
paths: t.paths
|
|
16
|
+
} : t;
|
|
17
|
+
return JSON.stringify(r, null, 2).split("\n");
|
|
18
|
+
}
|
|
19
|
+
function b(e, t) {
|
|
20
|
+
let r = C(n(e), t);
|
|
21
|
+
return r === void 0 ? [`Unknown key: ${t}`] : typeof r == "object" ? [JSON.stringify(r, null, 2)] : [String(r)];
|
|
22
|
+
}
|
|
23
|
+
function x(e, t, n) {
|
|
24
|
+
r(e, w(t, n));
|
|
25
|
+
let i = [`✓ Set ${t} = ${n}`];
|
|
26
|
+
return S(e, i), i;
|
|
27
|
+
}
|
|
28
|
+
function S(e, t) {
|
|
29
|
+
a.some((t) => u(m(e, ".claude", "skills", t))) && (i(e, n(e)), t.push("✓ Skills re-rendered"));
|
|
30
|
+
}
|
|
31
|
+
function C(e, t) {
|
|
32
|
+
let n = t.split("."), r = e;
|
|
33
|
+
for (let e of n) {
|
|
34
|
+
if (typeof r != "object" || !r) return;
|
|
35
|
+
r = r[e];
|
|
36
|
+
}
|
|
37
|
+
return r;
|
|
38
|
+
}
|
|
39
|
+
function w(e, t) {
|
|
40
|
+
let n = e.split("."), r = {}, i = r;
|
|
41
|
+
for (let e = 0; e < n.length - 1; e++) {
|
|
42
|
+
let t = {};
|
|
43
|
+
i[n[e]] = t, i = t;
|
|
44
|
+
}
|
|
45
|
+
return i[n[n.length - 1]] = t, r;
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
7
48
|
//#region src/cli.ts
|
|
8
|
-
var { version:
|
|
49
|
+
var { version: T } = JSON.parse(d(h(p(g(import.meta.url)), "..", "package.json"), "utf8")), E = Math.max(...e.map((e) => `${e.name} ${e.args}`.length)), D = [
|
|
9
50
|
"Usage: tracerkit <command> [path]",
|
|
10
51
|
"",
|
|
11
52
|
"Commands:",
|
|
12
|
-
...
|
|
53
|
+
...e.map((e) => ` ${`${e.name} ${e.args}`.padEnd(E + 2)}${e.desc}`),
|
|
13
54
|
"",
|
|
14
55
|
"Options:",
|
|
15
56
|
" --force Overwrite modified files during update",
|
|
16
57
|
" --help, -h Show this help message",
|
|
17
58
|
" --version, -v Print version",
|
|
18
59
|
"",
|
|
19
|
-
"
|
|
60
|
+
"init/update/uninstall default to the home directory when no path is given.",
|
|
61
|
+
"config defaults to the current working directory."
|
|
20
62
|
];
|
|
21
|
-
function
|
|
63
|
+
function O(e) {
|
|
64
|
+
if (!e) return !1;
|
|
65
|
+
try {
|
|
66
|
+
return f(h(e)).isDirectory();
|
|
67
|
+
} catch {
|
|
68
|
+
return !1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function k(e, t = _()) {
|
|
22
72
|
let n = e.find((e) => !e.startsWith("-"));
|
|
23
|
-
return n ?
|
|
24
|
-
}
|
|
25
|
-
function
|
|
26
|
-
if (
|
|
27
|
-
if (
|
|
28
|
-
let
|
|
29
|
-
if (
|
|
30
|
-
switch (
|
|
31
|
-
case "init": return
|
|
73
|
+
return n ? h(n) : t;
|
|
74
|
+
}
|
|
75
|
+
function A(e) {
|
|
76
|
+
if (e.includes(t.help) || e.includes("-h")) return D;
|
|
77
|
+
if (e.includes(t.version) || e.includes("-v")) return [`tracerkit/${T}`];
|
|
78
|
+
let n = e[0], r = e.slice(1);
|
|
79
|
+
if (s.includes(n)) return [`"${n}" has been removed — skills handle this now.`, "Run `tracerkit update` to get the latest skills."];
|
|
80
|
+
switch (n) {
|
|
81
|
+
case "init": return o(k(r));
|
|
32
82
|
case "update": {
|
|
33
|
-
let e =
|
|
34
|
-
return
|
|
83
|
+
let e = r.includes(t.force), n = c(k(r.filter((e) => e !== t.force)), { force: e });
|
|
84
|
+
return n.push("", "Updated to the latest TracerKit."), n.push("If using Claude Code, restart your session to load changes."), n;
|
|
85
|
+
}
|
|
86
|
+
case "config": {
|
|
87
|
+
let e = O(r[0]);
|
|
88
|
+
return v(e ? h(r[0]) : process.cwd(), e ? r.slice(1) : r);
|
|
35
89
|
}
|
|
36
|
-
case "uninstall": return
|
|
37
|
-
default: return
|
|
90
|
+
case "uninstall": return l(k(r));
|
|
91
|
+
default: return D;
|
|
38
92
|
}
|
|
39
93
|
}
|
|
40
94
|
//#endregion
|
|
41
95
|
//#region src/bin.ts
|
|
42
|
-
var
|
|
43
|
-
for (let e of
|
|
96
|
+
var j = A(process.argv.slice(2));
|
|
97
|
+
for (let e of j) console.log(e);
|
|
44
98
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
1
|
+
import { a as e, l as t, n, r, s as i, t as a } from "./uninstall-9tiXr-15.js";
|
|
2
|
+
export { e as COMMANDS, i as DEPRECATED_SKILLS, t as SKILL_NAMES, n as init, a as uninstall, r as update };
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { existsSync as e, mkdirSync as t, readFileSync as n, readdirSync as r, rmSync as i, writeFileSync as a } from "node:fs";
|
|
2
|
+
import { dirname as o, join as s } from "node:path";
|
|
3
|
+
import { createHash as c } from "node:crypto";
|
|
4
|
+
import { fileURLToPath as l } from "node:url";
|
|
5
|
+
//#region src/config.ts
|
|
6
|
+
var u = "local", d = [u, "github"], f = {
|
|
7
|
+
prds: ".tracerkit/prds",
|
|
8
|
+
plans: ".tracerkit/plans",
|
|
9
|
+
archives: ".tracerkit/archives"
|
|
10
|
+
}, p = { labels: {
|
|
11
|
+
prd: "tk:prd",
|
|
12
|
+
plan: "tk:plan"
|
|
13
|
+
} };
|
|
14
|
+
function m(t) {
|
|
15
|
+
let r = s(t, ".tracerkit", "config.json");
|
|
16
|
+
if (!e(r)) return {
|
|
17
|
+
storage: u,
|
|
18
|
+
paths: { ...f },
|
|
19
|
+
github: { ...p }
|
|
20
|
+
};
|
|
21
|
+
let i;
|
|
22
|
+
try {
|
|
23
|
+
i = JSON.parse(n(r, "utf8"));
|
|
24
|
+
} catch {
|
|
25
|
+
throw Error("Invalid .tracerkit/config.json — expected valid JSON");
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
storage: h(i.storage),
|
|
29
|
+
paths: g(i.paths),
|
|
30
|
+
github: _(i.github)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function h(e) {
|
|
34
|
+
return typeof e == "string" && d.includes(e) ? e : u;
|
|
35
|
+
}
|
|
36
|
+
function g(e) {
|
|
37
|
+
let t = b(e) ? e : {};
|
|
38
|
+
return {
|
|
39
|
+
prds: typeof t.prds == "string" ? t.prds : f.prds,
|
|
40
|
+
plans: typeof t.plans == "string" ? t.plans : f.plans,
|
|
41
|
+
archives: typeof t.archives == "string" ? t.archives : f.archives
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function _(e) {
|
|
45
|
+
let t = b(e) ? e : {}, n = b(t.labels) ? t.labels : {};
|
|
46
|
+
return {
|
|
47
|
+
...typeof t.repo == "string" ? { repo: t.repo } : {},
|
|
48
|
+
labels: {
|
|
49
|
+
prd: typeof n.prd == "string" ? n.prd : p.labels.prd,
|
|
50
|
+
plan: typeof n.plan == "string" ? n.plan : p.labels.plan
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function v(r, i) {
|
|
55
|
+
let c = s(r, ".tracerkit", "config.json");
|
|
56
|
+
t(o(c), { recursive: !0 });
|
|
57
|
+
let l = {};
|
|
58
|
+
if (e(c)) try {
|
|
59
|
+
l = JSON.parse(n(c, "utf8"));
|
|
60
|
+
} catch {
|
|
61
|
+
l = {};
|
|
62
|
+
}
|
|
63
|
+
let u = y(l, i);
|
|
64
|
+
a(c, JSON.stringify(u, null, 2) + "\n");
|
|
65
|
+
}
|
|
66
|
+
function y(e, t) {
|
|
67
|
+
let n = { ...e };
|
|
68
|
+
for (let e of Object.keys(t)) b(n[e]) && b(t[e]) ? n[e] = y(n[e], t[e]) : n[e] = t[e];
|
|
69
|
+
return n;
|
|
70
|
+
}
|
|
71
|
+
function b(e) {
|
|
72
|
+
return typeof e == "object" && !!e && !Array.isArray(e);
|
|
73
|
+
}
|
|
74
|
+
var x = [
|
|
75
|
+
"tk:brief",
|
|
76
|
+
"tk:prd",
|
|
77
|
+
"tk:plan",
|
|
78
|
+
"tk:check"
|
|
79
|
+
], S = ["tk:verify"], C = {
|
|
80
|
+
force: "--force",
|
|
81
|
+
help: "--help",
|
|
82
|
+
version: "--version"
|
|
83
|
+
}, w = [
|
|
84
|
+
"brief",
|
|
85
|
+
"progress",
|
|
86
|
+
"archive"
|
|
87
|
+
], T = [
|
|
88
|
+
{
|
|
89
|
+
name: "init",
|
|
90
|
+
args: "[path]",
|
|
91
|
+
desc: "Install skills to ~/.claude/skills/ (or [path] if given)"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "update",
|
|
95
|
+
args: "[path]",
|
|
96
|
+
desc: "Refresh unchanged files from latest version, skip modified"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "config",
|
|
100
|
+
args: "[path] [key] [value]",
|
|
101
|
+
desc: "Get or set TracerKit configuration"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: "uninstall",
|
|
105
|
+
args: "[path]",
|
|
106
|
+
desc: "Remove TracerKit skill directories, keep .tracerkit/ artifacts"
|
|
107
|
+
}
|
|
108
|
+
], E = s(o(l(import.meta.url)), "..", "skills");
|
|
109
|
+
function D(e, t = "") {
|
|
110
|
+
let n = r(e, { withFileTypes: !0 }), i = [];
|
|
111
|
+
for (let r of n) {
|
|
112
|
+
let n = t ? `${t}/${r.name}` : r.name;
|
|
113
|
+
r.isDirectory() ? i.push(...D(s(e, r.name), n)) : i.push(n);
|
|
114
|
+
}
|
|
115
|
+
return i.sort();
|
|
116
|
+
}
|
|
117
|
+
function O(e) {
|
|
118
|
+
return `.claude/skills/tk:${e}`;
|
|
119
|
+
}
|
|
120
|
+
function k(e) {
|
|
121
|
+
return e.slice(18);
|
|
122
|
+
}
|
|
123
|
+
function A(e, t) {
|
|
124
|
+
let n = e;
|
|
125
|
+
return t.paths.prds !== f.prds && (n = n.replaceAll(f.prds, t.paths.prds)), t.paths.plans !== f.plans && (n = n.replaceAll(f.plans, t.paths.plans)), t.paths.archives !== f.archives && (n = n.replaceAll(f.archives, t.paths.archives)), t.github?.repo && (n = n.replaceAll("{{github.repo}}", t.github.repo)), t.github?.labels?.prd && (n = n.replaceAll("{{github.labels.prd}}", t.github.labels.prd)), t.github?.labels?.plan && (n = n.replaceAll("{{github.labels.plan}}", t.github.labels.plan)), n;
|
|
126
|
+
}
|
|
127
|
+
function j(e, r, i) {
|
|
128
|
+
let c = i ?? D(E).map(O);
|
|
129
|
+
for (let i of c) {
|
|
130
|
+
let c = s(E, k(i)), l = s(e, i);
|
|
131
|
+
t(o(l), { recursive: !0 }), a(l, A(n(c, "utf8"), r));
|
|
132
|
+
}
|
|
133
|
+
return { copied: c };
|
|
134
|
+
}
|
|
135
|
+
function M(e) {
|
|
136
|
+
return c("sha256").update(e).digest("hex");
|
|
137
|
+
}
|
|
138
|
+
function N(t, r) {
|
|
139
|
+
let i = D(E).map(O), a = [], o = [], c = [];
|
|
140
|
+
for (let l of i) {
|
|
141
|
+
let i = s(t, l);
|
|
142
|
+
if (!e(i)) c.push(l);
|
|
143
|
+
else {
|
|
144
|
+
let e = A(n(s(E, k(l)), "utf8"), r);
|
|
145
|
+
M(Buffer.from(e)) === M(n(i)) ? a.push(l) : o.push(l);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
unchanged: a,
|
|
150
|
+
modified: o,
|
|
151
|
+
missing: c
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region src/commands/update.ts
|
|
156
|
+
function P(t, n) {
|
|
157
|
+
if (!x.some((n) => e(s(t, ".claude", "skills", n)))) throw Error("TracerKit not initialized — run `tracerkit init` first");
|
|
158
|
+
let r = m(t), { unchanged: a, modified: o, missing: c } = N(t, r), l = [];
|
|
159
|
+
for (let n of S) {
|
|
160
|
+
let r = s(t, ".claude", "skills", n);
|
|
161
|
+
e(r) && (i(r, {
|
|
162
|
+
recursive: !0,
|
|
163
|
+
force: !0
|
|
164
|
+
}), l.push(`✗ .claude/skills/${n}/ removed (deprecated)`));
|
|
165
|
+
}
|
|
166
|
+
let u = n?.force ?? !1, d = [
|
|
167
|
+
...a,
|
|
168
|
+
...c,
|
|
169
|
+
...u ? o : []
|
|
170
|
+
];
|
|
171
|
+
if (d.length > 0) {
|
|
172
|
+
j(t, r, d);
|
|
173
|
+
for (let e of a) l.push(`✓ ${e}`);
|
|
174
|
+
for (let e of c) l.push(`✓ ${e} (added)`);
|
|
175
|
+
if (u) for (let e of o) l.push(`✓ ${e} (replaced)`);
|
|
176
|
+
}
|
|
177
|
+
if (!u && o.length > 0) {
|
|
178
|
+
for (let e of o) l.push(`⚠ ${e} (skipped — modified)`);
|
|
179
|
+
l.push("", "Run `tracerkit update --force` to replace modified files with latest versions.");
|
|
180
|
+
}
|
|
181
|
+
return l;
|
|
182
|
+
}
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/commands/init.ts
|
|
185
|
+
function F(t) {
|
|
186
|
+
if (x.some((n) => e(s(t, ".claude", "skills", n)))) return P(t, { force: !1 });
|
|
187
|
+
let { copied: n } = j(t, m(t));
|
|
188
|
+
return n.map((e) => `✓ ${e}`);
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
191
|
+
//#region src/commands/uninstall.ts
|
|
192
|
+
function I(t) {
|
|
193
|
+
if (!x.some((n) => e(s(t, ".claude", "skills", n)))) throw Error("TracerKit not initialized — nothing to uninstall");
|
|
194
|
+
let n = [];
|
|
195
|
+
for (let r of x) {
|
|
196
|
+
let a = s(t, ".claude", "skills", r);
|
|
197
|
+
e(a) && (i(a, {
|
|
198
|
+
recursive: !0,
|
|
199
|
+
force: !0
|
|
200
|
+
}), n.push(`✗ .claude/skills/${r}/ removed`));
|
|
201
|
+
}
|
|
202
|
+
return n;
|
|
203
|
+
}
|
|
204
|
+
//#endregion
|
|
205
|
+
export { T as a, C as c, m as d, v as f, j as i, x as l, F as n, w as o, P as r, S as s, I as t, u };
|
package/package.json
CHANGED
package/skills/brief/SKILL.md
CHANGED
|
@@ -2,45 +2,62 @@
|
|
|
2
2
|
description: Session briefing — shows active features, progress, and suggested focus. Use at the start of a session to orient.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
**Config**: read `.tracerkit/config.json` (default: `local`). Follow matching `<!-- if:local/github -->` blocks. GitHub: use `github.repo` from config or git remote.
|
|
6
|
+
|
|
5
7
|
# Session Briefing
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
Overview of active features, progress, and suggested focus.
|
|
8
10
|
|
|
9
11
|
## Pre-loaded context
|
|
10
12
|
|
|
13
|
+
<!-- if:local -->
|
|
14
|
+
|
|
11
15
|
- Available PRDs: !`ls .tracerkit/prds/ 2>&1`
|
|
16
|
+
<!-- end:local -->
|
|
17
|
+
<!-- if:github -->
|
|
18
|
+
- Available PRDs: list open GitHub Issues with label `{{github.labels.prd}}`
|
|
19
|
+
<!-- end:github -->
|
|
12
20
|
|
|
13
21
|
## Algorithm
|
|
14
22
|
|
|
15
|
-
Follow these steps exactly to build the briefing table:
|
|
16
|
-
|
|
17
23
|
### 1. Discover features
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
<!-- if:local -->
|
|
26
|
+
|
|
27
|
+
For each `.md` file in `.tracerkit/prds/`: parse frontmatter, extract `status` and `created`. Skip `status: done`. Slug = filename without `.md`.
|
|
20
28
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
<!-- end:local -->
|
|
30
|
+
<!-- if:github -->
|
|
31
|
+
|
|
32
|
+
List open GitHub Issues with label `{{github.labels.prd}}`:
|
|
33
|
+
|
|
34
|
+
6. For each issue, parse the `<!-- tk:metadata -->` comment in the body
|
|
35
|
+
7. Extract `status` and `created` fields from the metadata
|
|
36
|
+
8. Also check labels: `tk:created`, `tk:in-progress`
|
|
37
|
+
9. Skip issues with `tk:done` label
|
|
38
|
+
10. The slug is extracted from the title: `[{{github.labels.prd}}] <slug>: ...`
|
|
39
|
+
<!-- end:github -->
|
|
26
40
|
|
|
27
41
|
### 2. Count progress from plans
|
|
28
42
|
|
|
29
|
-
|
|
43
|
+
<!-- if:local -->
|
|
44
|
+
|
|
45
|
+
For each slug with a plan at `.tracerkit/plans/<slug>.md`:
|
|
30
46
|
|
|
31
|
-
|
|
32
|
-
2. Find every `## Phase N` heading (regex: `^## Phase \d+`)
|
|
33
|
-
3. Within each phase section (until the next `## ` heading), count:
|
|
34
|
-
- Checked items: lines matching `^- \[x\] ` (case-insensitive)
|
|
35
|
-
- Unchecked items: lines matching `^- \[ \] `
|
|
36
|
-
4. Sum checked and total across all phases → `checked/total`
|
|
37
|
-
5. Find the first unchecked item (`^- \[ \] (.+)`) in the entire plan — that's the "Next" value. Strip any trailing `[tag]` markers.
|
|
47
|
+
Count `- [x]` and `- [ ]` lines under each `## Phase N` heading. Sum → `checked/total`. First unchecked item → "Next" (strip trailing `[tag]`). No plan → `—`.
|
|
38
48
|
|
|
39
|
-
|
|
49
|
+
<!-- end:local -->
|
|
50
|
+
<!-- if:github -->
|
|
51
|
+
|
|
52
|
+
For each slug, find plan issue with label `{{github.labels.plan}}` and matching title. If found:
|
|
53
|
+
|
|
54
|
+
Count `- [x]` and `- [ ]` lines under each `## Phase N` heading. Sum → `checked/total`. First unchecked item → "Next" (strip trailing `[tag]`). No plan → `—`.
|
|
55
|
+
|
|
56
|
+
<!-- end:github -->
|
|
40
57
|
|
|
41
58
|
### 3. Build the table
|
|
42
59
|
|
|
43
|
-
Sort
|
|
60
|
+
Sort by `created` ascending (no-date last). Age from `created`:
|
|
44
61
|
|
|
45
62
|
- < 7 days → `Nd` (e.g. `3d`)
|
|
46
63
|
- < 30 days → `Nw` (e.g. `2w`)
|
|
@@ -72,8 +89,13 @@ Append below the table:
|
|
|
72
89
|
|
|
73
90
|
### 5. Offer next steps
|
|
74
91
|
|
|
75
|
-
|
|
92
|
+
<!-- if:local -->
|
|
93
|
+
|
|
94
|
+
Options: continue focused feature (read plan at `.tracerkit/plans/<slug>.md`), `/tk:prd` for new feature, `/tk:check <slug>` for progress.
|
|
95
|
+
|
|
96
|
+
<!-- end:local -->
|
|
97
|
+
<!-- if:github -->
|
|
98
|
+
|
|
99
|
+
Options: continue focused feature (read plan issue), `/tk:prd` for new feature, `/tk:check <slug>` for progress.
|
|
76
100
|
|
|
77
|
-
|
|
78
|
-
- Start a new feature with `/tk:prd`
|
|
79
|
-
- Check progress on a feature with `/tk:check <slug>`
|
|
101
|
+
<!-- end:github -->
|
package/skills/check/SKILL.md
CHANGED
|
@@ -3,13 +3,21 @@ description: Verify implementation against plan. Shows progress, finds blockers,
|
|
|
3
3
|
argument-hint: '[slug]'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
**Config**: read `.tracerkit/config.json` (default: `local`). Follow matching `<!-- if:local/github -->` blocks. GitHub: use `github.repo` from config or git remote.
|
|
7
|
+
|
|
6
8
|
# Check Implementation
|
|
7
9
|
|
|
8
10
|
Check implementation against a plan. Update checks, stamp findings, transition status, and archive when done.
|
|
9
11
|
|
|
10
12
|
## Pre-loaded context
|
|
11
13
|
|
|
14
|
+
<!-- if:local -->
|
|
15
|
+
|
|
12
16
|
- Available plans: !`ls .tracerkit/plans/ 2>&1`
|
|
17
|
+
<!-- end:local -->
|
|
18
|
+
<!-- if:github -->
|
|
19
|
+
- Available plans: list open GitHub Issues with label `{{github.labels.plan}}`
|
|
20
|
+
<!-- end:github -->
|
|
13
21
|
|
|
14
22
|
## Input
|
|
15
23
|
|
|
@@ -25,38 +33,60 @@ If no argument is provided, build a summary table before asking which one to che
|
|
|
25
33
|
| <slug> | ... | 3/7 |
|
|
26
34
|
```
|
|
27
35
|
|
|
36
|
+
<!-- if:local -->
|
|
37
|
+
|
|
28
38
|
For each `.md` file in `.tracerkit/prds/`:
|
|
29
39
|
|
|
30
40
|
1. Read the file, parse YAML frontmatter (block between `---` fences)
|
|
31
41
|
2. Extract `status` — use `unknown` if missing
|
|
32
42
|
3. If `.tracerkit/plans/<slug>.md` exists, count progress (see Progress Algorithm below). Show `—` if no plan.
|
|
43
|
+
<!-- end:local -->
|
|
44
|
+
<!-- if:github -->
|
|
33
45
|
|
|
34
|
-
|
|
46
|
+
List open GitHub Issues with label `{{github.labels.prd}}`:
|
|
35
47
|
|
|
36
|
-
|
|
48
|
+
4. For each PRD issue, extract `status` from labels (`tk:created`, `tk:in-progress`)
|
|
49
|
+
5. Find matching plan issue with label `{{github.labels.plan}}` and same slug in title
|
|
50
|
+
6. If plan issue exists, count progress from checkboxes in its body (see Progress Algorithm below). Show `—` if no plan.
|
|
51
|
+
<!-- end:github -->
|
|
37
52
|
|
|
38
|
-
|
|
53
|
+
Ask which feature to verify.
|
|
39
54
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- Unchecked: lines matching `^- \[ \] `
|
|
44
|
-
3. Per-phase output: ` Phase N — title: checked/total`
|
|
45
|
-
4. Sum across all phases → `Total: checked/total`
|
|
55
|
+
## Progress Algorithm
|
|
56
|
+
|
|
57
|
+
Count `- [x]` and `- [ ]` lines under each `## Phase N` heading. Per-phase: `Phase N — title: checked/total`. Sum → `Total: checked/total`.
|
|
46
58
|
|
|
47
59
|
## Workflow
|
|
48
60
|
|
|
49
61
|
### 1. Load the plan
|
|
50
62
|
|
|
51
|
-
|
|
63
|
+
<!-- if:local -->
|
|
64
|
+
|
|
65
|
+
Read `.tracerkit/plans/<slug>.md`. If missing, list plans and ask.
|
|
66
|
+
|
|
67
|
+
<!-- end:local -->
|
|
68
|
+
<!-- if:github -->
|
|
69
|
+
|
|
70
|
+
Find plan issue: open issue with label `{{github.labels.plan}}`, title matching `[{{github.labels.plan}}] <slug>:`. If missing, list plans and ask.
|
|
71
|
+
|
|
72
|
+
<!-- end:github -->
|
|
52
73
|
|
|
53
74
|
### 2. Load the PRD
|
|
54
75
|
|
|
55
|
-
|
|
76
|
+
<!-- if:local -->
|
|
77
|
+
|
|
78
|
+
Read source PRD referenced in plan header (`> Source PRD: ...`).
|
|
79
|
+
|
|
80
|
+
<!-- end:local -->
|
|
81
|
+
<!-- if:github -->
|
|
82
|
+
|
|
83
|
+
Read source PRD issue referenced in plan body (`> Source PRD: #<number>`).
|
|
84
|
+
|
|
85
|
+
<!-- end:github -->
|
|
56
86
|
|
|
57
87
|
### 3. Fast-path: check if implementation exists
|
|
58
88
|
|
|
59
|
-
|
|
89
|
+
If primary module file(s) from Phase 1 don't exist, skip subagent — report `0/N — not yet started`, list Phase 1 done-when items, jump to Step 5.
|
|
60
90
|
|
|
61
91
|
### 3b. Launch read-only review
|
|
62
92
|
|
|
@@ -76,8 +106,17 @@ Collect findings into two categories:
|
|
|
76
106
|
|
|
77
107
|
### 3c. Update checkboxes
|
|
78
108
|
|
|
109
|
+
<!-- if:local -->
|
|
110
|
+
|
|
79
111
|
Using the subagent's report, update each checkbox in `.tracerkit/plans/<slug>.md` to `[x]` or `[ ]`.
|
|
80
112
|
|
|
113
|
+
<!-- end:local -->
|
|
114
|
+
<!-- if:github -->
|
|
115
|
+
|
|
116
|
+
Using the subagent's report, update each checkbox in the plan issue body to `[x]` or `[ ]` by editing the issue.
|
|
117
|
+
|
|
118
|
+
<!-- end:github -->
|
|
119
|
+
|
|
81
120
|
### 4. Determine outcome
|
|
82
121
|
|
|
83
122
|
Based on checks and findings, decide the status transition:
|
|
@@ -88,7 +127,7 @@ Based on checks and findings, decide the status transition:
|
|
|
88
127
|
|
|
89
128
|
### 5. Report to user
|
|
90
129
|
|
|
91
|
-
Count progress per phase
|
|
130
|
+
Count progress per phase (Progress Algorithm), then print:
|
|
92
131
|
|
|
93
132
|
```markdown
|
|
94
133
|
## Verification: <slug>
|
|
@@ -112,8 +151,17 @@ Total: checked/total
|
|
|
112
151
|
|
|
113
152
|
### 6. Stamp the plan
|
|
114
153
|
|
|
154
|
+
<!-- if:local -->
|
|
155
|
+
|
|
115
156
|
Append a verdict block at the bottom of `.tracerkit/plans/<slug>.md`:
|
|
116
157
|
|
|
158
|
+
<!-- end:local -->
|
|
159
|
+
<!-- if:github -->
|
|
160
|
+
|
|
161
|
+
Append a verdict block at the bottom of the plan issue body by editing the issue:
|
|
162
|
+
|
|
163
|
+
<!-- end:github -->
|
|
164
|
+
|
|
117
165
|
```markdown
|
|
118
166
|
---
|
|
119
167
|
|
|
@@ -129,21 +177,25 @@ If a previous verdict block exists, replace it with the new one.
|
|
|
129
177
|
|
|
130
178
|
### 7. On `done` — archive
|
|
131
179
|
|
|
132
|
-
If all checks pass and zero BLOCKERS
|
|
180
|
+
If all checks pass and zero BLOCKERS:
|
|
181
|
+
|
|
182
|
+
<!-- if:local -->
|
|
183
|
+
|
|
184
|
+
Archive to `.tracerkit/archives/<slug>/`:
|
|
185
|
+
|
|
186
|
+
1. Copy PRD → `prd.md` (set `status: done`, add `completed` timestamp in frontmatter)
|
|
187
|
+
2. Copy plan → `plan.md` (append `## Archived` with date)
|
|
188
|
+
3. Delete originals
|
|
133
189
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
- Read the PRD file
|
|
137
|
-
- In the YAML frontmatter (between `---` fences), find the `status:` line and replace its value with `done`. If no `status:` line exists, add `status: done` as a new line inside the frontmatter block.
|
|
138
|
-
- Add a `completed: <current UTC ISO 8601 timestamp>` line inside the frontmatter block (e.g. `completed: 2025-06-15T14:30:00Z`)
|
|
139
|
-
- Write the updated content to `.tracerkit/archives/<slug>/prd.md`
|
|
140
|
-
3. Read `.tracerkit/plans/<slug>.md`
|
|
141
|
-
- Append to the end: `\n## Archived\n\nArchived on YYYY-MM-DD.\n`
|
|
142
|
-
- Write the result to `.tracerkit/archives/<slug>/plan.md`
|
|
143
|
-
4. Delete `.tracerkit/prds/<slug>.md` (if it exists)
|
|
144
|
-
5. Delete `.tracerkit/plans/<slug>.md`
|
|
190
|
+
<!-- end:local -->
|
|
191
|
+
<!-- if:github -->
|
|
145
192
|
|
|
146
|
-
|
|
193
|
+
1. PRD issue: add `tk:done`, remove `tk:in-progress`, set metadata `status: done` + `completed` timestamp
|
|
194
|
+
2. Close PRD issue (reason: `completed`)
|
|
195
|
+
3. Close plan issue (reason: `completed`)
|
|
196
|
+
4. If current PR exists, reference it in closing comment on PRD issue
|
|
197
|
+
|
|
198
|
+
<!-- end:github -->
|
|
147
199
|
|
|
148
200
|
### 8. On `in_progress` (no blockers)
|
|
149
201
|
|
|
@@ -159,10 +211,3 @@ List the blockers to fix, then re-run `/tk:check <slug>`.
|
|
|
159
211
|
- The only file writes this skill makes are: checkboxes + verdict block in the plan, and the archive steps on `done`
|
|
160
212
|
- Never modify implementation code — only observe and report
|
|
161
213
|
- If the PRD file is missing but all checks pass, warn and proceed — archive the plan only (skip PRD steps in archive)
|
|
162
|
-
|
|
163
|
-
## Error Handling
|
|
164
|
-
|
|
165
|
-
- Plan not found — list available plans and ask
|
|
166
|
-
- PRD referenced in plan not found — warn and continue with plan checks only
|
|
167
|
-
- `.tracerkit/plans/` missing — tell user to run `/tk:plan` first
|
|
168
|
-
- `.tracerkit/archives/<slug>/` already exists — warn and ask whether to remove it first
|
package/skills/plan/SKILL.md
CHANGED
|
@@ -1,41 +1,76 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Turn a PRD into a multi-phase implementation plan using tracer-bullet vertical slices
|
|
2
|
+
description: Turn a PRD into a multi-phase implementation plan using tracer-bullet vertical slices. Use after /tk:prd.
|
|
3
3
|
argument-hint: '[slug]'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
**Config**: read `.tracerkit/config.json` (default: `local`). Follow matching `<!-- if:local/github -->` blocks. GitHub: use `github.repo` from config or git remote.
|
|
7
|
+
|
|
6
8
|
# PRD to Plan
|
|
7
9
|
|
|
8
|
-
Break a PRD into phased vertical slices (tracer bullets).
|
|
10
|
+
Break a PRD into phased vertical slices (tracer bullets).
|
|
11
|
+
|
|
12
|
+
<!-- if:local -->
|
|
13
|
+
|
|
14
|
+
Output: `.tracerkit/plans/<slug>.md`.
|
|
15
|
+
|
|
16
|
+
<!-- end:local -->
|
|
17
|
+
<!-- if:github -->
|
|
18
|
+
|
|
19
|
+
Output: a GitHub Issue with label `{{github.labels.plan}}`.
|
|
20
|
+
|
|
21
|
+
<!-- end:github -->
|
|
9
22
|
|
|
10
23
|
## Pre-loaded context
|
|
11
24
|
|
|
25
|
+
<!-- if:local -->
|
|
26
|
+
|
|
12
27
|
- Available PRDs: !`ls .tracerkit/prds/ 2>&1`
|
|
28
|
+
<!-- end:local -->
|
|
29
|
+
<!-- if:github -->
|
|
30
|
+
- Available PRDs: list open GitHub Issues with label `{{github.labels.prd}}`
|
|
31
|
+
<!-- end:github -->
|
|
13
32
|
|
|
14
33
|
## Input
|
|
15
34
|
|
|
16
35
|
The argument (if provided) is: $ARGUMENTS
|
|
17
36
|
|
|
18
|
-
Use
|
|
37
|
+
Use argument as `<slug>`. If empty, list available PRDs and ask.
|
|
19
38
|
|
|
20
39
|
## Workflow
|
|
21
40
|
|
|
22
41
|
### 1. Read the PRD
|
|
23
42
|
|
|
24
|
-
|
|
43
|
+
<!-- if:local -->
|
|
44
|
+
|
|
45
|
+
Read `.tracerkit/prds/<slug>.md`. If missing, list PRDs and ask. If `.tracerkit/plans/<slug>.md` exists, ask: overwrite or new name?
|
|
25
46
|
|
|
26
|
-
|
|
47
|
+
<!-- end:local -->
|
|
48
|
+
<!-- if:github -->
|
|
49
|
+
|
|
50
|
+
Find PRD issue: open issue with label `{{github.labels.prd}}`, title matching `[{{github.labels.prd}}] <slug>:`. If missing, list PRDs and ask. If plan issue with label `{{github.labels.plan}}` and matching title exists, ask: update or new name?
|
|
51
|
+
|
|
52
|
+
<!-- end:github -->
|
|
27
53
|
|
|
28
54
|
### 1b. Update PRD status
|
|
29
55
|
|
|
30
|
-
|
|
56
|
+
<!-- if:local -->
|
|
57
|
+
|
|
58
|
+
Set `status: in_progress` in `.tracerkit/prds/<slug>.md` frontmatter. Change only `status`.
|
|
59
|
+
|
|
60
|
+
<!-- end:local -->
|
|
61
|
+
<!-- if:github -->
|
|
31
62
|
|
|
32
|
-
|
|
63
|
+
Update the PRD issue:
|
|
64
|
+
|
|
65
|
+
- Remove `tk:created` label, add `tk:in-progress` label
|
|
66
|
+
- Update the `<!-- tk:metadata -->` comment in the issue body to `status: in_progress`
|
|
67
|
+
<!-- end:github -->
|
|
33
68
|
|
|
34
69
|
### 2. Explore the codebase
|
|
35
70
|
|
|
36
|
-
|
|
71
|
+
Map architecture, patterns, integration points. Skip if codebase context exists from prior step.
|
|
37
72
|
|
|
38
|
-
**Research protocol**: codebase first, then
|
|
73
|
+
**Research protocol**: codebase first, then docs. Unverifiable claims → flag as uncertain, never fabricate.
|
|
39
74
|
|
|
40
75
|
### 3. Identify durable architectural decisions
|
|
41
76
|
|
|
@@ -49,7 +84,7 @@ Before slicing, extract decisions that hold across all phases:
|
|
|
49
84
|
|
|
50
85
|
### 4. Draft vertical slices
|
|
51
86
|
|
|
52
|
-
Each phase
|
|
87
|
+
Each phase: thin vertical slice through all layers (schema → service → API → UI → tests). Demoable alone.
|
|
53
88
|
|
|
54
89
|
**Deriving tasks from the PRD:**
|
|
55
90
|
|
|
@@ -67,9 +102,9 @@ Each phase is a thin **tracer bullet** — a narrow but complete path through ev
|
|
|
67
102
|
|
|
68
103
|
**Phase naming:** use a goal phrase answering "what can we demo when this is done?" (e.g., "Phase 1 — Revenue visible end-to-end"), not a layer name.
|
|
69
104
|
|
|
70
|
-
**Done when:**
|
|
105
|
+
**Done when:** checkbox list of atomic, verifiable conditions (not prose). Test: "Can an agent verify by reading files, running a command, or checking a test?" Agent marks `[x]` during implementation.
|
|
71
106
|
|
|
72
|
-
**
|
|
107
|
+
**Layer-by-layer exception:** if complex schema changes underpin all modules and no story stands alone, build data foundation first, then slice vertically.
|
|
73
108
|
|
|
74
109
|
**Phase count thresholds:**
|
|
75
110
|
|
|
@@ -87,23 +122,43 @@ Assign an agent tag to tasks where appropriate:
|
|
|
87
122
|
|
|
88
123
|
### 5. Quiz the user
|
|
89
124
|
|
|
90
|
-
Present
|
|
91
|
-
|
|
92
|
-
- **Title**: short goal phrase
|
|
93
|
-
- **User stories covered**: which PRD stories this addresses
|
|
94
|
-
- **Done when**: the testable condition
|
|
95
|
-
|
|
96
|
-
Ask: Does the granularity feel right? Should any phases merge or split? Iterate until approved.
|
|
125
|
+
Present breakdown (title, user stories covered, done-when per phase). Ask: granularity right? Merge or split? Iterate until approved.
|
|
97
126
|
|
|
98
127
|
### 6. Save plan
|
|
99
128
|
|
|
100
|
-
|
|
129
|
+
<!-- if:local -->
|
|
130
|
+
|
|
131
|
+
Save to `.tracerkit/plans/<slug>.md` (create dir if missing).
|
|
101
132
|
|
|
102
133
|
```markdown
|
|
103
134
|
# Plan: <Feature Name>
|
|
104
135
|
|
|
105
136
|
> Source PRD: `.tracerkit/prds/<slug>.md`
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
<!-- end:local -->
|
|
140
|
+
<!-- if:github -->
|
|
141
|
+
|
|
142
|
+
Ensure labels exist: `{{github.labels.plan}}`, `tk:in-progress` (create if missing).
|
|
143
|
+
|
|
144
|
+
Create GitHub Issue — title: `[{{github.labels.plan}}] <slug>: Plan: <Feature Title>`, labels: `{{github.labels.plan}}`, `tk:in-progress`.
|
|
145
|
+
|
|
146
|
+
```markdown
|
|
147
|
+
<!-- tk:metadata
|
|
148
|
+
source_prd: #<PRD issue number>
|
|
149
|
+
slug: <slug>
|
|
150
|
+
-->
|
|
151
|
+
|
|
152
|
+
# Plan: <Feature Name>
|
|
106
153
|
|
|
154
|
+
> Source PRD: #<PRD issue number>
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
<!-- end:github -->
|
|
158
|
+
|
|
159
|
+
Use this structure for the plan body:
|
|
160
|
+
|
|
161
|
+
```markdown
|
|
107
162
|
## Architectural Decisions
|
|
108
163
|
|
|
109
164
|
Durable decisions that apply across all phases:
|
|
@@ -138,7 +193,7 @@ Carried forward from PRD verbatim.
|
|
|
138
193
|
Gaps found in the PRD needing resolution. Blank if none.
|
|
139
194
|
```
|
|
140
195
|
|
|
141
|
-
Print
|
|
196
|
+
Print one line per phase: `Phase N — <title> (<condition summary>)`. Then ask: "Run `/tk:check <slug>` when ready?"
|
|
142
197
|
|
|
143
198
|
## Rules
|
|
144
199
|
|
|
@@ -146,11 +201,10 @@ Print saved path and one line per phase: `Phase N — <title> (<condition summar
|
|
|
146
201
|
- Each phase must be demoable end-to-end on its own
|
|
147
202
|
- "Done when" must be a checkbox list of testable conditions, not prose
|
|
148
203
|
- **Safety valve**: if a phase has >5 "Done when" items, stop and split it into smaller phases before continuing
|
|
204
|
+
<!-- if:local -->
|
|
149
205
|
- Never modify the source PRD content — only update frontmatter status fields
|
|
206
|
+
<!-- end:local -->
|
|
207
|
+
<!-- if:github -->
|
|
208
|
+
- Never modify the source PRD content — only update metadata and labels
|
|
209
|
+
<!-- end:github -->
|
|
150
210
|
- Carry PRD's Out of Scope forward verbatim
|
|
151
|
-
|
|
152
|
-
## Error Handling
|
|
153
|
-
|
|
154
|
-
- PRD not found — list available PRDs and ask
|
|
155
|
-
- PRD missing sections — note gaps inline and continue
|
|
156
|
-
- `.tracerkit/plans/` missing — create it
|
package/skills/prd/SKILL.md
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Create a PRD through user interview, codebase exploration, and module design
|
|
2
|
+
description: Create a PRD through user interview, codebase exploration, and module design. Use when starting a new feature or change.
|
|
3
3
|
argument-hint: <idea>
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
+
**Config**: read `.tracerkit/config.json` (default: `local`). Follow matching `<!-- if:local/github -->` blocks. GitHub: use `github.repo` from config or git remote.
|
|
7
|
+
|
|
6
8
|
# PRD Writing
|
|
7
9
|
|
|
8
|
-
Skip steps
|
|
10
|
+
Skip satisfied steps. If argument provided, skip to Step 2.
|
|
9
11
|
|
|
10
12
|
## Pre-loaded context
|
|
11
13
|
|
|
14
|
+
<!-- if:local -->
|
|
15
|
+
|
|
12
16
|
- Existing PRDs: !`ls .tracerkit/prds/ 2>&1`
|
|
17
|
+
<!-- end:local -->
|
|
18
|
+
<!-- if:github -->
|
|
19
|
+
- Existing PRDs: list open GitHub Issues with label `{{github.labels.prd}}`
|
|
20
|
+
<!-- end:github -->
|
|
13
21
|
|
|
14
22
|
## Input
|
|
15
23
|
|
|
16
24
|
The argument is: $ARGUMENTS
|
|
17
25
|
|
|
18
|
-
If
|
|
19
|
-
|
|
20
|
-
If the argument is provided, derive a slug using this exact algorithm:
|
|
26
|
+
If empty, go to Step 1; derive slug after gathering the idea. If provided, derive slug:
|
|
21
27
|
|
|
22
28
|
1. Take only the text before the first `—` or `–` (if present)
|
|
23
29
|
2. Lowercase the text
|
|
@@ -25,9 +31,16 @@ If the argument is provided, derive a slug using this exact algorithm:
|
|
|
25
31
|
4. Take the first 4 remaining words (or fewer if less exist)
|
|
26
32
|
5. Join with hyphens → `<slug>`
|
|
27
33
|
|
|
28
|
-
|
|
34
|
+
<!-- if:local -->
|
|
35
|
+
|
|
36
|
+
Output: `.tracerkit/prds/<slug>.md`. If exists, ask: overwrite or new name?
|
|
37
|
+
|
|
38
|
+
<!-- end:local -->
|
|
39
|
+
<!-- if:github -->
|
|
40
|
+
|
|
41
|
+
Output: GitHub Issue with label `{{github.labels.prd}}`, title `[{{github.labels.prd}}] <slug>: <Feature Title>`. If matching issue exists, ask: update or new slug?
|
|
29
42
|
|
|
30
|
-
|
|
43
|
+
<!-- end:github -->
|
|
31
44
|
|
|
32
45
|
## Workflow
|
|
33
46
|
|
|
@@ -37,129 +50,86 @@ Ask the user for a detailed description of the problem and any solution ideas.
|
|
|
37
50
|
|
|
38
51
|
### 2. Explore codebase
|
|
39
52
|
|
|
40
|
-
|
|
53
|
+
Map current state: data models, services, API routes, frontend, tests. Note exists vs. must build.
|
|
41
54
|
|
|
42
|
-
**Research protocol**: codebase first, then
|
|
55
|
+
**Research protocol**: codebase first, then docs. Unverifiable claims → flag as uncertain, never fabricate.
|
|
43
56
|
|
|
44
57
|
### 3. Interview
|
|
45
58
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
Walk these branches. **Skip rule**: skip a branch when the project type makes it irrelevant (e.g., skip Display/Access/Navigation for CLIs and libraries) AND the user's idea does not mention it.
|
|
49
|
-
|
|
50
|
-
- **Scope & Surface** — Where does this live? New page/view or integrated? Which user roles? _Skip if_: single-surface project (CLI, library) with no new entry points.
|
|
51
|
-
- **Data & Concepts** — Precise definitions for each new concept. What data exists, what's missing? _Never skip_ — every feature has data.
|
|
52
|
-
- **Behavior & Interaction** — How does the user interact? Sorting, filtering, search, time ranges? _Skip if_: feature is purely internal/backend with no user-facing behavior change.
|
|
53
|
-
- **Display & Output** — Numbers, tables, charts, forms? Exportable? URL-driven state? _Skip if_: no UI or formatted output involved.
|
|
54
|
-
- **Access & Privacy** — Who sees what? Role-based restrictions? Sensitive data concerns? _Skip if_: single-user project with no auth layer.
|
|
55
|
-
- **Boundaries** — What is explicitly out of scope? Adjacent features to defer? _Never skip_ — scope control prevents creep.
|
|
56
|
-
- **Integration** — Schema changes? New or extended services? External dependencies? _Skip if_: self-contained change touching no external systems or storage.
|
|
57
|
-
|
|
58
|
-
### 3b. Gray area checkpoint
|
|
59
|
-
|
|
60
|
-
Before continuing, scan the interview for gray areas. Something is a gray area if any of these are true:
|
|
61
|
-
|
|
62
|
-
- **Vague answer**: user said "maybe", "probably", "I think", or gave a one-word answer to a multi-part question
|
|
63
|
-
- **Contradiction**: two answers conflict (e.g., "no auth needed" but "only admins can access")
|
|
64
|
-
- **Unstated assumption**: you filled in a detail the user never confirmed
|
|
65
|
-
- **Ambiguous scope**: a feature boundary is unclear (could be in or out of scope)
|
|
59
|
+
One question at a time. Lead with your recommended answer. Explore code instead of asking when possible. Offer A/B/C for terse answers.
|
|
66
60
|
|
|
67
|
-
|
|
61
|
+
| Branch | Key questions | Skip when |
|
|
62
|
+
| ---------------- | --------------------------------------- | -------------------------------- |
|
|
63
|
+
| Scope & Surface | Where? New page or integrated? Roles? | CLI/library, no new entry points |
|
|
64
|
+
| Data & Concepts | Definitions, existing vs missing data | Never skip |
|
|
65
|
+
| Behavior | Interaction patterns, filtering, search | No user-facing behavior |
|
|
66
|
+
| Display | Numbers, tables, charts, exports | No UI |
|
|
67
|
+
| Access & Privacy | Who sees what? Sensitive data? | Single-user, no auth |
|
|
68
|
+
| Boundaries | Out of scope, deferred features | Never skip |
|
|
69
|
+
| Integration | Schema, services, external deps | Self-contained change |
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
Gray areas found:
|
|
71
|
-
1. <ambiguity> — assumed <X>, confirm?
|
|
72
|
-
2. <ambiguity> — two options: A or B
|
|
73
|
-
```
|
|
71
|
+
### 3b. Gray areas
|
|
74
72
|
|
|
75
|
-
|
|
73
|
+
Surface ambiguities, contradictions, unstated assumptions. Present numbered list. Resolve all before continuing.
|
|
76
74
|
|
|
77
75
|
### 4. Design modules
|
|
78
76
|
|
|
79
|
-
Sketch
|
|
77
|
+
Sketch modules. Favor **deep modules** — simple interface (1-3 entry points) hiding large implementation over shallow modules where interface ≈ implementation.
|
|
80
78
|
|
|
81
|
-
|
|
79
|
+
Shallow signals: many small 1:1 functions, callers compose multiple calls, feature changes require interface changes.
|
|
82
80
|
|
|
83
|
-
Present modules
|
|
81
|
+
Present modules. Confirm which need tests.
|
|
84
82
|
|
|
85
83
|
### 5. Write PRD
|
|
86
84
|
|
|
87
|
-
|
|
85
|
+
<!-- if:local -->
|
|
86
|
+
|
|
87
|
+
Save to `.tracerkit/prds/<slug>.md` (create dir if missing).
|
|
88
88
|
|
|
89
89
|
```markdown
|
|
90
90
|
---
|
|
91
|
-
created: <
|
|
91
|
+
created: <UTC ISO 8601>
|
|
92
92
|
status: created
|
|
93
93
|
---
|
|
94
94
|
|
|
95
95
|
# Feature Name
|
|
96
|
+
```
|
|
96
97
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
The problem from the user's perspective. Focus on pain and impact.
|
|
100
|
-
|
|
101
|
-
## Current State
|
|
102
|
-
|
|
103
|
-
What exists today that this feature changes or builds on. Skip for greenfield.
|
|
98
|
+
<!-- end:local -->
|
|
99
|
+
<!-- if:github -->
|
|
104
100
|
|
|
105
|
-
|
|
106
|
-
- Current behavior the user experiences
|
|
107
|
-
- Known limitations or workarounds
|
|
101
|
+
Ensure labels exist: `{{github.labels.prd}}`, `tk:created` (create if missing).
|
|
108
102
|
|
|
109
|
-
|
|
103
|
+
Create GitHub Issue — title: `[{{github.labels.prd}}] <slug>: <Feature Title>`, labels: `{{github.labels.prd}}`, `tk:created`.
|
|
110
104
|
|
|
111
|
-
|
|
105
|
+
```markdown
|
|
106
|
+
<!-- tk:metadata
|
|
107
|
+
created: <UTC ISO 8601>
|
|
108
|
+
status: created
|
|
109
|
+
-->
|
|
112
110
|
|
|
113
|
-
|
|
111
|
+
# Feature Name
|
|
112
|
+
```
|
|
114
113
|
|
|
115
|
-
|
|
114
|
+
<!-- end:github -->
|
|
116
115
|
|
|
117
|
-
|
|
116
|
+
PRD body structure (same for local file and issue body). Omit empty sections. No file paths or code snippets.
|
|
118
117
|
|
|
118
|
+
```
|
|
119
|
+
## Problem Statement
|
|
120
|
+
## Current State (skip if greenfield)
|
|
121
|
+
## Solution (user experience, not architecture)
|
|
122
|
+
## User Stories (numbered, cover happy + edge + error)
|
|
119
123
|
## Implementation Decisions
|
|
120
|
-
|
|
121
|
-
###
|
|
122
|
-
|
|
123
|
-
- Module name, purpose, and public interface (function signatures with param types)
|
|
124
|
-
- Why each module exists as a separate unit
|
|
125
|
-
|
|
126
|
-
### Architectural Decisions
|
|
127
|
-
|
|
128
|
-
- Key definitions (precise meaning of domain terms)
|
|
129
|
-
- Data flow from storage to display
|
|
130
|
-
- State management approach
|
|
131
|
-
|
|
124
|
+
### New Modules (name, purpose, interface signatures)
|
|
125
|
+
### Architectural Decisions (definitions, data flow, state)
|
|
132
126
|
### Schema Changes
|
|
133
|
-
|
|
134
|
-
- New tables/columns needed, or "None required"
|
|
135
|
-
|
|
136
127
|
### API Contracts
|
|
137
|
-
|
|
138
|
-
- New routes, request/response shapes
|
|
139
|
-
|
|
140
128
|
### Navigation
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
Do NOT include file paths or code snippets — they go stale.
|
|
145
|
-
|
|
146
|
-
Omit any section whose content would be "None required" — only include sections with actual content.
|
|
147
|
-
|
|
148
|
-
## Testing Decisions
|
|
149
|
-
|
|
150
|
-
- What makes a good test (behavior, not implementation)
|
|
151
|
-
- Which modules need tests
|
|
152
|
-
- Key test cases (empty state, boundaries, isolation)
|
|
153
|
-
- Prior art: similar test patterns in the codebase
|
|
154
|
-
|
|
155
|
-
## Out of Scope
|
|
156
|
-
|
|
157
|
-
Explicit list. Be specific — vague exclusions invite scope creep.
|
|
129
|
+
## Testing Decisions (behavior tests, key cases, prior art)
|
|
130
|
+
## Out of Scope (be specific)
|
|
158
131
|
```
|
|
159
132
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
## Error Handling
|
|
133
|
+
---
|
|
163
134
|
|
|
164
|
-
|
|
165
|
-
- Scope larger than expected — surface and re-scope with user before continuing
|
|
135
|
+
Then ask: "Run `/tk:plan <slug>` next?"
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { existsSync as e, mkdirSync as t, readFileSync as n, readdirSync as r, rmSync as i, writeFileSync as a } from "node:fs";
|
|
2
|
-
import { dirname as o, join as s } from "node:path";
|
|
3
|
-
import { createHash as c } from "node:crypto";
|
|
4
|
-
import { fileURLToPath as l } from "node:url";
|
|
5
|
-
//#region src/config.ts
|
|
6
|
-
var u = {
|
|
7
|
-
prds: ".tracerkit/prds",
|
|
8
|
-
plans: ".tracerkit/plans",
|
|
9
|
-
archives: ".tracerkit/archives"
|
|
10
|
-
};
|
|
11
|
-
function d(t) {
|
|
12
|
-
let r = s(t, ".tracerkit", "config.json");
|
|
13
|
-
if (!e(r)) return { paths: { ...u } };
|
|
14
|
-
let i, a;
|
|
15
|
-
try {
|
|
16
|
-
i = n(r, "utf8"), a = JSON.parse(i);
|
|
17
|
-
} catch {
|
|
18
|
-
throw Error("Invalid .tracerkit/config.json — expected valid JSON");
|
|
19
|
-
}
|
|
20
|
-
let o = typeof a.paths == "object" && a.paths !== null ? a.paths : {};
|
|
21
|
-
return { paths: {
|
|
22
|
-
prds: typeof o.prds == "string" ? o.prds : u.prds,
|
|
23
|
-
plans: typeof o.plans == "string" ? o.plans : u.plans,
|
|
24
|
-
archives: typeof o.archives == "string" ? o.archives : u.archives
|
|
25
|
-
} };
|
|
26
|
-
}
|
|
27
|
-
var f = [
|
|
28
|
-
"tk:brief",
|
|
29
|
-
"tk:prd",
|
|
30
|
-
"tk:plan",
|
|
31
|
-
"tk:check"
|
|
32
|
-
], p = ["tk:verify"], m = {
|
|
33
|
-
force: "--force",
|
|
34
|
-
help: "--help",
|
|
35
|
-
version: "--version"
|
|
36
|
-
}, h = [
|
|
37
|
-
"brief",
|
|
38
|
-
"progress",
|
|
39
|
-
"archive"
|
|
40
|
-
], g = [
|
|
41
|
-
{
|
|
42
|
-
name: "init",
|
|
43
|
-
args: "[path]",
|
|
44
|
-
desc: "Install skills to ~/.claude/skills/ (or [path] if given)"
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
name: "update",
|
|
48
|
-
args: "[path]",
|
|
49
|
-
desc: "Refresh unchanged files from latest version, skip modified"
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: "uninstall",
|
|
53
|
-
args: "[path]",
|
|
54
|
-
desc: "Remove TracerKit skill directories, keep .tracerkit/ artifacts"
|
|
55
|
-
}
|
|
56
|
-
], _ = s(o(l(import.meta.url)), "..", "skills");
|
|
57
|
-
function v(e, t = "") {
|
|
58
|
-
let n = r(e, { withFileTypes: !0 }), i = [];
|
|
59
|
-
for (let r of n) {
|
|
60
|
-
let n = t ? `${t}/${r.name}` : r.name;
|
|
61
|
-
r.isDirectory() ? i.push(...v(s(e, r.name), n)) : i.push(n);
|
|
62
|
-
}
|
|
63
|
-
return i.sort();
|
|
64
|
-
}
|
|
65
|
-
function y(e) {
|
|
66
|
-
return `.claude/skills/tk:${e}`;
|
|
67
|
-
}
|
|
68
|
-
function b(e) {
|
|
69
|
-
return e.slice(18);
|
|
70
|
-
}
|
|
71
|
-
function x(e, t) {
|
|
72
|
-
let n = e;
|
|
73
|
-
return t.paths.prds !== u.prds && (n = n.replaceAll(u.prds, t.paths.prds)), t.paths.plans !== u.plans && (n = n.replaceAll(u.plans, t.paths.plans)), t.paths.archives !== u.archives && (n = n.replaceAll(u.archives, t.paths.archives)), n;
|
|
74
|
-
}
|
|
75
|
-
function S(e, r, i) {
|
|
76
|
-
let c = i ?? v(_).map(y);
|
|
77
|
-
for (let i of c) {
|
|
78
|
-
let c = s(_, b(i)), l = s(e, i);
|
|
79
|
-
t(o(l), { recursive: !0 }), a(l, x(n(c, "utf8"), r));
|
|
80
|
-
}
|
|
81
|
-
return { copied: c };
|
|
82
|
-
}
|
|
83
|
-
function C(e) {
|
|
84
|
-
return c("sha256").update(e).digest("hex");
|
|
85
|
-
}
|
|
86
|
-
function w(t, r) {
|
|
87
|
-
let i = v(_).map(y), a = [], o = [], c = [];
|
|
88
|
-
for (let l of i) {
|
|
89
|
-
let i = s(t, l);
|
|
90
|
-
if (!e(i)) c.push(l);
|
|
91
|
-
else {
|
|
92
|
-
let e = x(n(s(_, b(l)), "utf8"), r);
|
|
93
|
-
C(Buffer.from(e)) === C(n(i)) ? a.push(l) : o.push(l);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return {
|
|
97
|
-
unchanged: a,
|
|
98
|
-
modified: o,
|
|
99
|
-
missing: c
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
//#endregion
|
|
103
|
-
//#region src/commands/update.ts
|
|
104
|
-
function T(t, n) {
|
|
105
|
-
if (!f.some((n) => e(s(t, ".claude", "skills", n)))) throw Error("TracerKit not initialized — run `tracerkit init` first");
|
|
106
|
-
let r = d(t), { unchanged: a, modified: o, missing: c } = w(t, r), l = [];
|
|
107
|
-
for (let n of p) {
|
|
108
|
-
let r = s(t, ".claude", "skills", n);
|
|
109
|
-
e(r) && (i(r, {
|
|
110
|
-
recursive: !0,
|
|
111
|
-
force: !0
|
|
112
|
-
}), l.push(`✗ .claude/skills/${n}/ removed (deprecated)`));
|
|
113
|
-
}
|
|
114
|
-
let u = n?.force ?? !1, m = [
|
|
115
|
-
...a,
|
|
116
|
-
...c,
|
|
117
|
-
...u ? o : []
|
|
118
|
-
];
|
|
119
|
-
if (m.length > 0) {
|
|
120
|
-
S(t, r, m);
|
|
121
|
-
for (let e of a) l.push(`✓ ${e}`);
|
|
122
|
-
for (let e of c) l.push(`✓ ${e} (added)`);
|
|
123
|
-
if (u) for (let e of o) l.push(`✓ ${e} (replaced)`);
|
|
124
|
-
}
|
|
125
|
-
if (!u && o.length > 0) {
|
|
126
|
-
for (let e of o) l.push(`⚠ ${e} (skipped — modified)`);
|
|
127
|
-
l.push("", "Run `tracerkit update --force` to replace modified files with latest versions.");
|
|
128
|
-
}
|
|
129
|
-
return l;
|
|
130
|
-
}
|
|
131
|
-
//#endregion
|
|
132
|
-
//#region src/commands/init.ts
|
|
133
|
-
function E(t) {
|
|
134
|
-
if (f.some((n) => e(s(t, ".claude", "skills", n)))) return T(t);
|
|
135
|
-
let { copied: n } = S(t, d(t));
|
|
136
|
-
return n.map((e) => `✓ ${e}`);
|
|
137
|
-
}
|
|
138
|
-
//#endregion
|
|
139
|
-
//#region src/commands/uninstall.ts
|
|
140
|
-
function D(t) {
|
|
141
|
-
if (!f.some((n) => e(s(t, ".claude", "skills", n)))) throw Error("TracerKit not initialized — nothing to uninstall");
|
|
142
|
-
let n = [];
|
|
143
|
-
for (let r of f) {
|
|
144
|
-
let a = s(t, ".claude", "skills", r);
|
|
145
|
-
e(a) && (i(a, {
|
|
146
|
-
recursive: !0,
|
|
147
|
-
force: !0
|
|
148
|
-
}), n.push(`✗ .claude/skills/${r}/ removed`));
|
|
149
|
-
}
|
|
150
|
-
return n;
|
|
151
|
-
}
|
|
152
|
-
//#endregion
|
|
153
|
-
export { h as a, f as c, g as i, E as n, p as o, T as r, m as s, D as t };
|