tracerkit 1.19.0 → 1.19.2
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 +17 -39
- package/dist/bin.js +23 -78
- package/dist/index.js +2 -2
- package/dist/uninstall-4Qp75twq.js +130 -0
- package/package.json +1 -1
- package/skills/brief/SKILL.md +4 -42
- package/skills/build/SKILL.md +3 -31
- package/skills/check/SKILL.md +2 -72
- package/skills/plan/SKILL.md +21 -82
- package/skills/prd/SKILL.md +8 -40
- package/dist/uninstall-CNGQ7MEU.js +0 -468
package/README.md
CHANGED
|
@@ -142,50 +142,28 @@ AI: | Feature | Status | Age | Progress | Next
|
|
|
142
142
|
|
|
143
143
|
See [Examples](docs/examples.md) for full walkthroughs.
|
|
144
144
|
|
|
145
|
-
<details>
|
|
146
|
-
<summary>GitHub Issues as storage backend</summary>
|
|
147
|
-
|
|
148
|
-
Same skills, same workflow. Storage is configured per-project:
|
|
149
|
-
|
|
150
|
-
```bash
|
|
151
|
-
tracerkit config storage github # set current project to use GitHub
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
PRDs and plans become GitHub Issues with `tk:prd` and `tk:plan` labels. On `/tk:check` pass, issues are closed with `completed` reason and any related PRs are linked automatically. Each project can use a different backend; local is the default. See [Configuration](docs/configuration.md) for details.
|
|
155
|
-
|
|
156
|
-
To migrate existing artifacts between backends:
|
|
157
|
-
|
|
158
|
-
```bash
|
|
159
|
-
tracerkit migrate-storage # local→github or github→local (auto-detected)
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
Direction is inferred from the current `storage` config. All artifacts are migrated, existing duplicates are skipped, and the config is flipped to the target backend. Source artifacts are left intact as backup.
|
|
163
|
-
|
|
164
|
-
</details>
|
|
165
|
-
|
|
166
145
|
## Skills
|
|
167
146
|
|
|
168
|
-
| Skill | What it does | Output
|
|
169
|
-
| ------------------ | ------------------------------------------------------ |
|
|
170
|
-
| `/tk:prd <idea>` | Interview → codebase scan → structured PRD | `.tracerkit/prds/<slug>.md`
|
|
171
|
-
| `/tk:plan <slug>` | PRD → phased vertical slices, each demoable on its own | `.tracerkit/plans/<slug>.md`
|
|
172
|
-
| `/tk:build <slug>` | Implement next incomplete phase, run feedback loops | Code changes + checked items in plan
|
|
173
|
-
| `/tk:brief` | Feature dashboard with progress and suggested focus | Terminal only, no files
|
|
174
|
-
| `/tk:check [slug]` | Verify done-when checkboxes against codebase and tests | Verdict block in plan
|
|
147
|
+
| Skill | What it does | Output |
|
|
148
|
+
| ------------------ | ------------------------------------------------------ | --------------------------------------------------- |
|
|
149
|
+
| `/tk:prd <idea>` | Interview → codebase scan → structured PRD | `.tracerkit/prds/<slug>.md` |
|
|
150
|
+
| `/tk:plan <slug>` | PRD → phased vertical slices, each demoable on its own | `.tracerkit/plans/<slug>.md` |
|
|
151
|
+
| `/tk:build <slug>` | Implement next incomplete phase, run feedback loops | Code changes + checked items in plan |
|
|
152
|
+
| `/tk:brief` | Feature dashboard with progress and suggested focus | Terminal only, no files |
|
|
153
|
+
| `/tk:check [slug]` | Verify done-when checkboxes against codebase and tests | Verdict block in plan, status updated when all pass |
|
|
175
154
|
|
|
176
155
|
## Docs
|
|
177
156
|
|
|
178
|
-
| Document | Description
|
|
179
|
-
| ------------------------------------------------ |
|
|
180
|
-
| [Examples](docs/examples.md) | Walk through end-to-end usage scenarios
|
|
181
|
-
| [CLI Reference](docs/cli-reference.md) | Commands: init, update,
|
|
182
|
-
| [
|
|
183
|
-
| [
|
|
184
|
-
| [
|
|
185
|
-
| [
|
|
186
|
-
| [
|
|
187
|
-
| [
|
|
188
|
-
| [OpenCode Setup](docs/opencode-setup.md) | Use TracerKit skills in OpenCode |
|
|
157
|
+
| Document | Description |
|
|
158
|
+
| ------------------------------------------------ | -------------------------------------------------- |
|
|
159
|
+
| [Examples](docs/examples.md) | Walk through end-to-end usage scenarios |
|
|
160
|
+
| [CLI Reference](docs/cli-reference.md) | Commands: init, update, uninstall |
|
|
161
|
+
| [Metadata Lifecycle](docs/metadata-lifecycle.md) | Understand YAML frontmatter states and transitions |
|
|
162
|
+
| [Comparison](docs/comparison.md) | Compare TracerKit to Spec Kit, Kiro, and OpenSpec |
|
|
163
|
+
| [Cursor Setup](docs/cursor-setup.md) | Use TracerKit skills in Cursor |
|
|
164
|
+
| [Gemini CLI Setup](docs/gemini-cli-setup.md) | Use TracerKit skills in Gemini CLI |
|
|
165
|
+
| [Copilot Setup](docs/copilot-setup.md) | Use TracerKit skills in GitHub Copilot |
|
|
166
|
+
| [OpenCode Setup](docs/opencode-setup.md) | Use TracerKit skills in OpenCode |
|
|
189
167
|
|
|
190
168
|
## Contributing
|
|
191
169
|
|
package/dist/bin.js
CHANGED
|
@@ -1,99 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as e,
|
|
3
|
-
import {
|
|
4
|
-
import { dirname as
|
|
5
|
-
import { fileURLToPath as
|
|
6
|
-
import { homedir as
|
|
7
|
-
//#region src/commands/config.ts
|
|
8
|
-
function y(e, t) {
|
|
9
|
-
let [n, r] = t;
|
|
10
|
-
return n ? r ? S(e, n, r) : x(e, n) : b(e);
|
|
11
|
-
}
|
|
12
|
-
function b(e) {
|
|
13
|
-
let n = t(e), r = n.storage === "local" ? {
|
|
14
|
-
storage: n.storage,
|
|
15
|
-
paths: n.paths
|
|
16
|
-
} : n;
|
|
17
|
-
return JSON.stringify(r, null, 2).split("\n");
|
|
18
|
-
}
|
|
19
|
-
function x(e, n) {
|
|
20
|
-
let r = w(t(e), n);
|
|
21
|
-
return r === void 0 ? [`Unknown key: ${n}`] : typeof r == "object" ? [JSON.stringify(r, null, 2)] : [String(r)];
|
|
22
|
-
}
|
|
23
|
-
function S(e, t, n) {
|
|
24
|
-
o(e, T(t, n));
|
|
25
|
-
let r = [`✓ Set ${t} = ${n}`];
|
|
26
|
-
return C(e, r), r;
|
|
27
|
-
}
|
|
28
|
-
function C(n, r) {
|
|
29
|
-
u.some((e) => d(h(n, ".claude", "skills", e))) && (e(n, t(n)), r.push("✓ Skills re-rendered"));
|
|
30
|
-
}
|
|
31
|
-
function w(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 T(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
|
|
2
|
+
import { a as e, i as t, n, r, s as i, t as a } from "./uninstall-4Qp75twq.js";
|
|
3
|
+
import { readFileSync as o } from "node:fs";
|
|
4
|
+
import { dirname as s, resolve as c } from "node:path";
|
|
5
|
+
import { fileURLToPath as l } from "node:url";
|
|
6
|
+
import { homedir as u } from "node:os";
|
|
48
7
|
//#region src/cli.ts
|
|
49
|
-
var { version:
|
|
8
|
+
var { version: d } = JSON.parse(o(c(s(l(import.meta.url)), "..", "package.json"), "utf8")), f = Math.max(...t.map((e) => `${e.name} ${e.args}`.length)), p = [
|
|
50
9
|
"Usage: tracerkit <command> [path]",
|
|
51
10
|
"",
|
|
52
11
|
"Commands:",
|
|
53
|
-
...
|
|
12
|
+
...t.map((e) => ` ${`${e.name} ${e.args}`.padEnd(f + 2)}${e.desc}`),
|
|
54
13
|
"",
|
|
55
14
|
"Options:",
|
|
56
15
|
" --force Overwrite modified files during update",
|
|
57
16
|
" --help, -h Show this help message",
|
|
58
17
|
" --version, -v Print version",
|
|
59
18
|
"",
|
|
60
|
-
"
|
|
61
|
-
"config defaults to the current working directory."
|
|
19
|
+
"Defaults to the home directory when no path is given."
|
|
62
20
|
];
|
|
63
|
-
function
|
|
64
|
-
if (!e) return !1;
|
|
65
|
-
try {
|
|
66
|
-
return p(g(e)).isDirectory();
|
|
67
|
-
} catch {
|
|
68
|
-
return !1;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
function A(e, t = v()) {
|
|
21
|
+
function m(e, t = u()) {
|
|
72
22
|
let n = e.find((e) => !e.startsWith("-"));
|
|
73
|
-
return n ?
|
|
74
|
-
}
|
|
75
|
-
function
|
|
76
|
-
if (
|
|
77
|
-
if (
|
|
78
|
-
let
|
|
79
|
-
if (
|
|
80
|
-
switch (
|
|
81
|
-
case "init": return
|
|
23
|
+
return n ? c(n) : t;
|
|
24
|
+
}
|
|
25
|
+
function h(t) {
|
|
26
|
+
if (t.includes(i.help) || t.includes("-h")) return p;
|
|
27
|
+
if (t.includes(i.version) || t.includes("-v")) return [`tracerkit/${d}`];
|
|
28
|
+
let o = t[0], s = t.slice(1);
|
|
29
|
+
if (e.includes(o)) return [`"${o}" has been removed — skills handle this now.`, "Run `tracerkit update` to get the latest skills."];
|
|
30
|
+
switch (o) {
|
|
31
|
+
case "init": return n(m(s));
|
|
82
32
|
case "update": {
|
|
83
|
-
let e =
|
|
33
|
+
let e = s.includes(i.force), t = r(m(s.filter((e) => e !== i.force)), { force: e });
|
|
84
34
|
return t.push("", "Updated to the latest TracerKit."), t.push("If using Claude Code, restart your session to load changes."), t;
|
|
85
35
|
}
|
|
86
|
-
case "
|
|
87
|
-
|
|
88
|
-
return y(e ? g(a[0]) : process.cwd(), e ? a.slice(1) : a);
|
|
89
|
-
}
|
|
90
|
-
case "uninstall": return l(A(a));
|
|
91
|
-
case "migrate-storage": return i(k(a[0]) ? g(a[0]) : process.cwd());
|
|
92
|
-
default: return O;
|
|
36
|
+
case "uninstall": return a(m(s));
|
|
37
|
+
default: return p;
|
|
93
38
|
}
|
|
94
39
|
}
|
|
95
40
|
//#endregion
|
|
96
41
|
//#region src/bin.ts
|
|
97
|
-
var
|
|
98
|
-
for (let e of
|
|
42
|
+
var g = h(process.argv.slice(2));
|
|
43
|
+
for (let e of g) console.log(e);
|
|
99
44
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { c as e, i as t, n, o as r, r as i, t as a
|
|
2
|
-
export {
|
|
1
|
+
import { c as e, i as t, n, o as r, r as i, t as a } from "./uninstall-4Qp75twq.js";
|
|
2
|
+
export { t as COMMANDS, r as DEPRECATED_SKILLS, e as SKILL_NAMES, n as init, a as uninstall, i as update };
|
|
@@ -0,0 +1,130 @@
|
|
|
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
|
+
var u = [
|
|
6
|
+
"tk:brief",
|
|
7
|
+
"tk:prd",
|
|
8
|
+
"tk:plan",
|
|
9
|
+
"tk:build",
|
|
10
|
+
"tk:check"
|
|
11
|
+
], d = ["tk:verify"], f = {
|
|
12
|
+
force: "--force",
|
|
13
|
+
help: "--help",
|
|
14
|
+
version: "--version"
|
|
15
|
+
}, p = [
|
|
16
|
+
"brief",
|
|
17
|
+
"progress",
|
|
18
|
+
"archive",
|
|
19
|
+
"config",
|
|
20
|
+
"migrate-storage"
|
|
21
|
+
], m = [
|
|
22
|
+
{
|
|
23
|
+
name: "init",
|
|
24
|
+
args: "[path]",
|
|
25
|
+
desc: "Install skills to ~/.claude/skills/ (or [path] if given)"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "update",
|
|
29
|
+
args: "[path]",
|
|
30
|
+
desc: "Refresh unchanged files from latest version, skip modified"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: "uninstall",
|
|
34
|
+
args: "[path]",
|
|
35
|
+
desc: "Remove TracerKit skill directories, keep .tracerkit/ artifacts"
|
|
36
|
+
}
|
|
37
|
+
], h = s(o(l(import.meta.url)), "..", "skills");
|
|
38
|
+
function g(e, t = "") {
|
|
39
|
+
let n = r(e, { withFileTypes: !0 }), i = [];
|
|
40
|
+
for (let r of n) {
|
|
41
|
+
let n = t ? `${t}/${r.name}` : r.name;
|
|
42
|
+
r.isDirectory() ? i.push(...g(s(e, r.name), n)) : i.push(n);
|
|
43
|
+
}
|
|
44
|
+
return i.sort();
|
|
45
|
+
}
|
|
46
|
+
function _(e) {
|
|
47
|
+
return `.claude/skills/tk:${e}`;
|
|
48
|
+
}
|
|
49
|
+
function v(e) {
|
|
50
|
+
return e.slice(18);
|
|
51
|
+
}
|
|
52
|
+
function y(e, r) {
|
|
53
|
+
let i = r ?? g(h).map(_);
|
|
54
|
+
for (let r of i) {
|
|
55
|
+
let i = s(h, v(r)), c = s(e, r);
|
|
56
|
+
t(o(c), { recursive: !0 }), a(c, n(i, "utf8"));
|
|
57
|
+
}
|
|
58
|
+
return { copied: i };
|
|
59
|
+
}
|
|
60
|
+
function b(e) {
|
|
61
|
+
return c("sha256").update(e).digest("hex");
|
|
62
|
+
}
|
|
63
|
+
function x(t) {
|
|
64
|
+
let r = g(h).map(_), i = [], a = [], o = [];
|
|
65
|
+
for (let c of r) {
|
|
66
|
+
let r = s(t, c);
|
|
67
|
+
if (!e(r)) o.push(c);
|
|
68
|
+
else {
|
|
69
|
+
let e = n(s(h, v(c)), "utf8");
|
|
70
|
+
b(Buffer.from(e)) === b(n(r)) ? i.push(c) : a.push(c);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
unchanged: i,
|
|
75
|
+
modified: a,
|
|
76
|
+
missing: o
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/commands/update.ts
|
|
81
|
+
function S(t, n) {
|
|
82
|
+
if (!u.some((n) => e(s(t, ".claude", "skills", n)))) throw Error("TracerKit not initialized — run `tracerkit init` first");
|
|
83
|
+
let { unchanged: r, modified: a, missing: o } = x(t), c = [];
|
|
84
|
+
for (let n of d) {
|
|
85
|
+
let r = s(t, ".claude", "skills", n);
|
|
86
|
+
e(r) && (i(r, {
|
|
87
|
+
recursive: !0,
|
|
88
|
+
force: !0
|
|
89
|
+
}), c.push(`✗ .claude/skills/${n}/ removed (deprecated)`));
|
|
90
|
+
}
|
|
91
|
+
let l = n?.force ?? !1, f = [
|
|
92
|
+
...r,
|
|
93
|
+
...o,
|
|
94
|
+
...l ? a : []
|
|
95
|
+
];
|
|
96
|
+
if (f.length > 0) {
|
|
97
|
+
y(t, f);
|
|
98
|
+
for (let e of r) c.push(`✓ ${e}`);
|
|
99
|
+
for (let e of o) c.push(`✓ ${e} (added)`);
|
|
100
|
+
if (l) for (let e of a) c.push(`✓ ${e} (replaced)`);
|
|
101
|
+
}
|
|
102
|
+
if (!l && a.length > 0) {
|
|
103
|
+
for (let e of a) c.push(`⚠ ${e} (skipped — modified)`);
|
|
104
|
+
c.push("", "Run `tracerkit update --force` to replace modified files with latest versions.");
|
|
105
|
+
}
|
|
106
|
+
return c;
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region src/commands/init.ts
|
|
110
|
+
function C(t) {
|
|
111
|
+
if (u.some((n) => e(s(t, ".claude", "skills", n)))) return S(t, { force: !1 });
|
|
112
|
+
let { copied: n } = y(t);
|
|
113
|
+
return n.map((e) => `✓ ${e}`);
|
|
114
|
+
}
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/commands/uninstall.ts
|
|
117
|
+
function w(t) {
|
|
118
|
+
if (!u.some((n) => e(s(t, ".claude", "skills", n)))) throw Error("TracerKit not initialized — nothing to uninstall");
|
|
119
|
+
let n = [];
|
|
120
|
+
for (let r of u) {
|
|
121
|
+
let a = s(t, ".claude", "skills", r);
|
|
122
|
+
e(a) && (i(a, {
|
|
123
|
+
recursive: !0,
|
|
124
|
+
force: !0
|
|
125
|
+
}), n.push(`✗ .claude/skills/${r}/ removed`));
|
|
126
|
+
}
|
|
127
|
+
return n;
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
export { p as a, u as c, m as i, C as n, d as o, S as r, f as s, w as t };
|
package/package.json
CHANGED
package/skills/brief/SKILL.md
CHANGED
|
@@ -2,59 +2,26 @@
|
|
|
2
2
|
description: Session briefing — shows active features, progress, and suggested focus. Use at the start of a session, when asking what to work on, or when checking active features.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
**Config**: read `.tracerkit/config.json` (default: `local`). Follow matching `<!-- if:local/github -->` blocks.
|
|
6
|
-
|
|
7
5
|
# Session Briefing
|
|
8
6
|
|
|
9
7
|
Overview of active features, progress, and suggested focus.
|
|
10
8
|
|
|
11
9
|
## Pre-loaded context
|
|
12
10
|
|
|
13
|
-
<!-- if:local -->
|
|
14
|
-
|
|
15
11
|
- Available PRDs: !`ls .tracerkit/prds/*.md 2>/dev/null || echo "(none)"`
|
|
16
|
-
<!-- end:local -->
|
|
17
|
-
<!-- if:github -->
|
|
18
|
-
- Available PRDs: list open GitHub Issues with label `{{github.labels.prd}}`
|
|
19
|
-
<!-- end:github -->
|
|
20
12
|
|
|
21
13
|
## Algorithm
|
|
22
14
|
|
|
23
15
|
### 1. Discover features
|
|
24
16
|
|
|
25
|
-
<!-- if:local -->
|
|
26
|
-
|
|
27
17
|
For each `.md` file in `.tracerkit/prds/`: parse frontmatter, extract `status` and `created`. Skip `status: done`. Slug = filename without `.md`.
|
|
28
18
|
|
|
29
|
-
<!-- end:local -->
|
|
30
|
-
<!-- if:github -->
|
|
31
|
-
|
|
32
|
-
List open GitHub Issues with label `{{github.labels.prd}}`:
|
|
33
|
-
|
|
34
|
-
1. For each issue, parse the `<!-- tk:metadata -->` comment in the body
|
|
35
|
-
2. Extract `status` and `created` fields from the metadata
|
|
36
|
-
3. Also check labels: `tk:created`, `tk:in-progress`
|
|
37
|
-
4. Skip issues with `tk:done` label
|
|
38
|
-
5. The slug is extracted from the title: `[{{github.labels.prd}}] <slug>: ...`
|
|
39
|
-
<!-- end:github -->
|
|
40
|
-
|
|
41
19
|
### 2. Count progress from plans
|
|
42
20
|
|
|
43
|
-
<!-- if:local -->
|
|
44
|
-
|
|
45
21
|
For each slug with a plan at `.tracerkit/plans/<slug>.md`:
|
|
46
22
|
|
|
47
23
|
Count `- [x]` and `- [ ]` lines under each `## Phase N` heading. Sum → `checked/total`. First unchecked item → "Next" (strip trailing `[tag]`). No plan → `—`.
|
|
48
24
|
|
|
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 -->
|
|
57
|
-
|
|
58
25
|
### 3. Build the table
|
|
59
26
|
|
|
60
27
|
Sort by `created` ascending (no-date last). Age from `created`:
|
|
@@ -89,13 +56,8 @@ Append below the table:
|
|
|
89
56
|
|
|
90
57
|
### 5. Offer next steps
|
|
91
58
|
|
|
92
|
-
|
|
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.
|
|
59
|
+
Present options and wait for the user's choice:
|
|
100
60
|
|
|
101
|
-
|
|
61
|
+
1. Continue focused feature — read plan at `.tracerkit/plans/<slug>.md` (Recommended)
|
|
62
|
+
2. Start new feature — `/tk:prd`
|
|
63
|
+
3. Check progress — `/tk:check <slug>`
|
package/skills/build/SKILL.md
CHANGED
|
@@ -3,8 +3,6 @@ description: Implement one phase of a plan. Reads plan, finds next incomplete ph
|
|
|
3
3
|
argument-hint: '[slug]'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
**Config**: read `.tracerkit/config.json` (default: `local`). Follow matching `<!-- if:local/github -->` blocks.
|
|
7
|
-
|
|
8
6
|
# Build Phase
|
|
9
7
|
|
|
10
8
|
Implement the next incomplete phase of a plan — one phase per invocation.
|
|
@@ -15,19 +13,13 @@ Implement the next incomplete phase of a plan — one phase per invocation.
|
|
|
15
13
|
|
|
16
14
|
## Pre-loaded context
|
|
17
15
|
|
|
18
|
-
<!-- if:local -->
|
|
19
|
-
|
|
20
16
|
- Available plans: !`ls .tracerkit/plans/*.md 2>/dev/null || echo "(none)"`
|
|
21
|
-
<!-- end:local -->
|
|
22
|
-
<!-- if:github -->
|
|
23
|
-
- Available plans: list open GitHub Issues with label `{{github.labels.plan}}`
|
|
24
|
-
<!-- end:github -->
|
|
25
17
|
|
|
26
18
|
## Input
|
|
27
19
|
|
|
28
20
|
The argument (if provided) is: $ARGUMENTS
|
|
29
21
|
|
|
30
|
-
Use argument as `<slug>`. If empty, list
|
|
22
|
+
Use argument as `<slug>`. If empty, list plans as numbered options and wait for the user's choice.
|
|
31
23
|
|
|
32
24
|
Accepts slug or `@file` reference:
|
|
33
25
|
|
|
@@ -42,16 +34,7 @@ If argument starts with `@`, treat it as a file path — read that file directly
|
|
|
42
34
|
|
|
43
35
|
### 1. Load the plan
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Read `.tracerkit/plans/<slug>.md`. If missing, list plans and ask the user to select one.
|
|
48
|
-
|
|
49
|
-
<!-- end:local -->
|
|
50
|
-
<!-- if:github -->
|
|
51
|
-
|
|
52
|
-
Find plan issue: open issue with label `{{github.labels.plan}}`, title matching `[{{github.labels.plan}}] <slug>:`. If missing, list plans and ask the user to select one.
|
|
53
|
-
|
|
54
|
-
<!-- end:github -->
|
|
37
|
+
Read `.tracerkit/plans/<slug>.md`. If missing, list plans as numbered options and wait for the user's choice.
|
|
55
38
|
|
|
56
39
|
### 2. Find the next incomplete phase
|
|
57
40
|
|
|
@@ -134,18 +117,7 @@ Wait for the user's response before continuing.
|
|
|
134
117
|
|
|
135
118
|
### 7. Mark checkboxes
|
|
136
119
|
|
|
137
|
-
After all feedback loops pass,
|
|
138
|
-
|
|
139
|
-
<!-- if:local -->
|
|
140
|
-
|
|
141
|
-
For each completed item, change `- [ ]` → `- [x]` in `.tracerkit/plans/<slug>.md`.
|
|
142
|
-
|
|
143
|
-
<!-- end:local -->
|
|
144
|
-
<!-- if:github -->
|
|
145
|
-
|
|
146
|
-
For each completed item, change `- [ ]` → `- [x]` in the plan issue body using `gh issue edit`.
|
|
147
|
-
|
|
148
|
-
<!-- end:github -->
|
|
120
|
+
After all feedback loops pass, for each completed item change `- [ ]` → `- [x]` in `.tracerkit/plans/<slug>.md`.
|
|
149
121
|
|
|
150
122
|
### 8. Offer commit
|
|
151
123
|
|
package/skills/check/SKILL.md
CHANGED
|
@@ -3,8 +3,6 @@ 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.
|
|
7
|
-
|
|
8
6
|
# Check Implementation
|
|
9
7
|
|
|
10
8
|
Check implementation against a plan. Update checks, stamp findings, transition status, and mark complete when done.
|
|
@@ -13,13 +11,7 @@ Check implementation against a plan. Update checks, stamp findings, transition s
|
|
|
13
11
|
|
|
14
12
|
## Pre-loaded context
|
|
15
13
|
|
|
16
|
-
<!-- if:local -->
|
|
17
|
-
|
|
18
14
|
- Available plans: !`ls .tracerkit/plans/*.md 2>/dev/null || echo "(none)"`
|
|
19
|
-
<!-- end:local -->
|
|
20
|
-
<!-- if:github -->
|
|
21
|
-
- Available plans: list open GitHub Issues with label `{{github.labels.plan}}`
|
|
22
|
-
<!-- end:github -->
|
|
23
15
|
|
|
24
16
|
## Input
|
|
25
17
|
|
|
@@ -35,24 +27,13 @@ If no argument is provided, build a summary table before asking which one to che
|
|
|
35
27
|
| <slug> | ... | 3/7 |
|
|
36
28
|
```
|
|
37
29
|
|
|
38
|
-
<!-- if:local -->
|
|
39
|
-
|
|
40
30
|
For each `.md` file in `.tracerkit/prds/`:
|
|
41
31
|
|
|
42
32
|
1. Read the file, parse YAML frontmatter (block between `---` fences)
|
|
43
33
|
2. Extract `status` — use `unknown` if missing
|
|
44
34
|
3. If `.tracerkit/plans/<slug>.md` exists, count progress (see Progress Algorithm below). Show `—` if no plan.
|
|
45
|
-
<!-- end:local -->
|
|
46
|
-
<!-- if:github -->
|
|
47
|
-
|
|
48
|
-
List open GitHub Issues with label `{{github.labels.prd}}`:
|
|
49
35
|
|
|
50
|
-
|
|
51
|
-
5. Find matching plan issue with label `{{github.labels.plan}}` and same slug in title
|
|
52
|
-
6. If plan issue exists, count progress from checkboxes in its body (see Progress Algorithm below). Show `—` if no plan.
|
|
53
|
-
<!-- end:github -->
|
|
54
|
-
|
|
55
|
-
Present each feature as an option and let the user pick which to verify.
|
|
36
|
+
Present each feature as a numbered option and wait for the user's choice.
|
|
56
37
|
|
|
57
38
|
## Progress Algorithm
|
|
58
39
|
|
|
@@ -62,30 +43,12 @@ Count `- [x]` and `- [ ]` lines under each `## Phase N` heading. Per-phase: `Pha
|
|
|
62
43
|
|
|
63
44
|
### 1. Load the plan
|
|
64
45
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Read `.tracerkit/plans/<slug>.md`. If missing, list plans and ask the user to select one.
|
|
68
|
-
|
|
69
|
-
<!-- end:local -->
|
|
70
|
-
<!-- if:github -->
|
|
71
|
-
|
|
72
|
-
Find plan issue: open issue with label `{{github.labels.plan}}`, title matching `[{{github.labels.plan}}] <slug>:`. If missing, list plans and ask the user to select one.
|
|
73
|
-
|
|
74
|
-
<!-- end:github -->
|
|
46
|
+
Read `.tracerkit/plans/<slug>.md`. If missing, list plans as numbered options and wait for the user's choice.
|
|
75
47
|
|
|
76
48
|
### 2. Load the PRD
|
|
77
49
|
|
|
78
|
-
<!-- if:local -->
|
|
79
|
-
|
|
80
50
|
Read source PRD referenced in plan header (`> Source PRD: ...`).
|
|
81
51
|
|
|
82
|
-
<!-- end:local -->
|
|
83
|
-
<!-- if:github -->
|
|
84
|
-
|
|
85
|
-
Read source PRD issue referenced in plan body (`> Source PRD: #<number>`).
|
|
86
|
-
|
|
87
|
-
<!-- end:github -->
|
|
88
|
-
|
|
89
52
|
### 3. Fast-path: check if implementation exists
|
|
90
53
|
|
|
91
54
|
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.
|
|
@@ -108,17 +71,8 @@ Collect findings into two categories:
|
|
|
108
71
|
|
|
109
72
|
### 3c. Update checkboxes
|
|
110
73
|
|
|
111
|
-
<!-- if:local -->
|
|
112
|
-
|
|
113
74
|
Using the subagent's report, update each checkbox in `.tracerkit/plans/<slug>.md` to `[x]` or `[ ]`.
|
|
114
75
|
|
|
115
|
-
<!-- end:local -->
|
|
116
|
-
<!-- if:github -->
|
|
117
|
-
|
|
118
|
-
Using the subagent's report, update each checkbox in the plan issue body to `[x]` or `[ ]` by editing the issue.
|
|
119
|
-
|
|
120
|
-
<!-- end:github -->
|
|
121
|
-
|
|
122
76
|
### 4. Determine outcome
|
|
123
77
|
|
|
124
78
|
Based on checks and findings, decide the status transition:
|
|
@@ -153,17 +107,8 @@ Total: checked/total
|
|
|
153
107
|
|
|
154
108
|
### 6. Stamp the plan
|
|
155
109
|
|
|
156
|
-
<!-- if:local -->
|
|
157
|
-
|
|
158
110
|
Append a verdict block at the bottom of `.tracerkit/plans/<slug>.md`:
|
|
159
111
|
|
|
160
|
-
<!-- end:local -->
|
|
161
|
-
<!-- if:github -->
|
|
162
|
-
|
|
163
|
-
Append a verdict block at the bottom of the plan issue body by editing the issue:
|
|
164
|
-
|
|
165
|
-
<!-- end:github -->
|
|
166
|
-
|
|
167
112
|
```markdown
|
|
168
113
|
---
|
|
169
114
|
|
|
@@ -181,24 +126,9 @@ If a previous verdict block exists, replace it with the new one.
|
|
|
181
126
|
|
|
182
127
|
If all checks pass and zero BLOCKERS:
|
|
183
128
|
|
|
184
|
-
<!-- if:local -->
|
|
185
|
-
|
|
186
129
|
1. Update PRD frontmatter: `status: done`, add `completed: <UTC ISO 8601>`
|
|
187
130
|
2. Update plan frontmatter: `status: done`, add `completed: <UTC ISO 8601>`
|
|
188
131
|
|
|
189
|
-
<!-- end:local -->
|
|
190
|
-
<!-- if:github -->
|
|
191
|
-
|
|
192
|
-
1. If on a feature branch with commits ahead of default:
|
|
193
|
-
a. Push branch if not pushed
|
|
194
|
-
b. Open PR with `Closes #<prd-number>, Closes #<plan-number>` (or update existing PR body)
|
|
195
|
-
2. Search merged PRs matching slug: `gh pr list --search <slug> --state merged` — add comment on PRD issue linking found PRs
|
|
196
|
-
3. PRD issue: add `tk:done`, remove `tk:in-progress`, update metadata `status: done` + `completed` timestamp
|
|
197
|
-
4. Close PRD issue (reason: `completed`)
|
|
198
|
-
5. Close plan issue (reason: `completed`)
|
|
199
|
-
|
|
200
|
-
<!-- end:github -->
|
|
201
|
-
|
|
202
132
|
### 8. On `in_progress` (no blockers)
|
|
203
133
|
|
|
204
134
|
Show progress summary (checked/total per phase), list the next unchecked items to implement. Keep going.
|
package/skills/plan/SKILL.md
CHANGED
|
@@ -3,71 +3,39 @@ description: Turn a PRD into a multi-phase implementation plan using tracer-bull
|
|
|
3
3
|
argument-hint: '[slug]'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
**Config**: read `.tracerkit/config.json` (default: `local`). Follow matching `<!-- if:local/github -->` blocks.
|
|
7
|
-
|
|
8
6
|
# PRD to Plan
|
|
9
7
|
|
|
10
8
|
Break a PRD into phased vertical slices (tracer bullets).
|
|
11
9
|
|
|
12
10
|
**Interactive prompts**: present options as a numbered list and wait for the user's choice.
|
|
13
11
|
|
|
14
|
-
<!-- if:local -->
|
|
15
|
-
|
|
16
12
|
Output: `.tracerkit/plans/<slug>.md`.
|
|
17
13
|
|
|
18
|
-
<!-- end:local -->
|
|
19
|
-
<!-- if:github -->
|
|
20
|
-
|
|
21
|
-
Output: a GitHub Issue with label `{{github.labels.plan}}`.
|
|
22
|
-
|
|
23
|
-
<!-- end:github -->
|
|
24
|
-
|
|
25
14
|
## Pre-loaded context
|
|
26
15
|
|
|
27
|
-
<!-- if:local -->
|
|
28
|
-
|
|
29
16
|
- Available PRDs: !`ls .tracerkit/prds/*.md 2>/dev/null || echo "(none)"`
|
|
30
|
-
<!-- end:local -->
|
|
31
|
-
<!-- if:github -->
|
|
32
|
-
- Available PRDs: list open GitHub Issues with label `{{github.labels.prd}}`
|
|
33
|
-
<!-- end:github -->
|
|
34
17
|
|
|
35
18
|
## Input
|
|
36
19
|
|
|
37
20
|
The argument (if provided) is: $ARGUMENTS
|
|
38
21
|
|
|
39
|
-
Use argument as `<slug>`. If empty, list
|
|
22
|
+
Use argument as `<slug>`. If empty, list PRDs as numbered options and wait for the user's choice.
|
|
40
23
|
|
|
41
24
|
## Workflow
|
|
42
25
|
|
|
43
26
|
### 1. Read the PRD
|
|
44
27
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Read `.tracerkit/prds/<slug>.md`. If missing, list PRDs and ask the user to select one. If `.tracerkit/plans/<slug>.md` exists, ask: "Overwrite existing" / "Pick a new name".
|
|
28
|
+
Read `.tracerkit/prds/<slug>.md`. If missing, list PRDs as numbered options and wait for the user's choice.
|
|
48
29
|
|
|
49
|
-
|
|
50
|
-
<!-- if:github -->
|
|
30
|
+
If `.tracerkit/plans/<slug>.md` exists, present options and wait:
|
|
51
31
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
<!-- end:github -->
|
|
32
|
+
1. Overwrite existing (Recommended)
|
|
33
|
+
2. Pick a new name
|
|
55
34
|
|
|
56
35
|
### 1b. Update PRD status
|
|
57
36
|
|
|
58
|
-
<!-- if:local -->
|
|
59
|
-
|
|
60
37
|
Set `status: in_progress` in `.tracerkit/prds/<slug>.md` frontmatter. Change only `status`.
|
|
61
38
|
|
|
62
|
-
<!-- end:local -->
|
|
63
|
-
<!-- if:github -->
|
|
64
|
-
|
|
65
|
-
Update the PRD issue:
|
|
66
|
-
|
|
67
|
-
- Remove `tk:created` label, add `tk:in-progress` label
|
|
68
|
-
- Update the `<!-- tk:metadata -->` comment in the issue body to `status: in_progress`
|
|
69
|
-
<!-- end:github -->
|
|
70
|
-
|
|
71
39
|
### 2. Explore the codebase
|
|
72
40
|
|
|
73
41
|
Map architecture, patterns, integration points. Skip if codebase context exists from prior step.
|
|
@@ -112,7 +80,9 @@ Each phase: thin vertical slice through all layers (schema → service → API
|
|
|
112
80
|
|
|
113
81
|
- 1 module touched → 2–3 phases max
|
|
114
82
|
- 2–3 modules touched → 3–5 phases max
|
|
115
|
-
- 4+ modules or 6+ phases → stop and
|
|
83
|
+
- 4+ modules or 6+ phases → stop and present options:
|
|
84
|
+
1. Split the PRD (Recommended)
|
|
85
|
+
2. Continue anyway
|
|
116
86
|
|
|
117
87
|
Count "modules touched" by scanning the PRD's New Modules and Schema Changes sections.
|
|
118
88
|
|
|
@@ -124,11 +94,15 @@ Assign an agent tag to tasks where appropriate:
|
|
|
124
94
|
|
|
125
95
|
### 5. Quiz the user
|
|
126
96
|
|
|
127
|
-
Present breakdown (title, user stories covered, done-when per phase).
|
|
97
|
+
Present breakdown (title, user stories covered, done-when per phase). Present options and wait:
|
|
128
98
|
|
|
129
|
-
|
|
99
|
+
1. Looks good, proceed (Recommended)
|
|
100
|
+
2. Merge some phases
|
|
101
|
+
3. Split a phase
|
|
130
102
|
|
|
131
|
-
|
|
103
|
+
Iterate until approved.
|
|
104
|
+
|
|
105
|
+
### 6. Save plan
|
|
132
106
|
|
|
133
107
|
Save to `.tracerkit/plans/<slug>.md` (create dir if missing).
|
|
134
108
|
|
|
@@ -144,41 +118,7 @@ status: in_progress
|
|
|
144
118
|
> Source PRD: `.tracerkit/prds/<slug>.md`
|
|
145
119
|
```
|
|
146
120
|
|
|
147
|
-
Then update PRD frontmatter: add `plan: .tracerkit/plans/<slug>.md` field.
|
|
148
|
-
|
|
149
|
-
<!-- end:local -->
|
|
150
|
-
<!-- if:github -->
|
|
151
|
-
|
|
152
|
-
Ensure labels exist: `gh label create {{github.labels.plan}} --force`, `gh label create tk:in-progress --force`.
|
|
153
|
-
|
|
154
|
-
Create GitHub Issue — title: `[{{github.labels.plan}}] <slug>: Plan: <Feature Title>`, labels: `{{github.labels.plan}}`, `tk:in-progress`.
|
|
155
|
-
|
|
156
|
-
```markdown
|
|
157
|
-
<!-- tk:metadata
|
|
158
|
-
source_prd: #<PRD issue number>
|
|
159
|
-
slug: <slug>
|
|
160
|
-
status: in_progress
|
|
161
|
-
-->
|
|
162
|
-
|
|
163
|
-
# Plan: <Feature Name>
|
|
164
|
-
|
|
165
|
-
> Source PRD: #<PRD issue number>
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
<!-- end:github -->
|
|
169
|
-
|
|
170
|
-
### 6b. Backlink PRD
|
|
171
|
-
|
|
172
|
-
<!-- if:local -->
|
|
173
|
-
|
|
174
|
-
Already linked via PRD frontmatter `plan:` field (set in step 6).
|
|
175
|
-
|
|
176
|
-
<!-- end:local -->
|
|
177
|
-
<!-- if:github -->
|
|
178
|
-
|
|
179
|
-
Add comment on PRD issue: "Plan: #<plan-issue-number>" (creates cross-reference).
|
|
180
|
-
|
|
181
|
-
<!-- end:github -->
|
|
121
|
+
Then update PRD frontmatter: add `plan: .tracerkit/plans/<slug>.md` field (creates backlink).
|
|
182
122
|
|
|
183
123
|
Use this structure for the plan body:
|
|
184
124
|
|
|
@@ -217,7 +157,11 @@ Carried forward from PRD verbatim.
|
|
|
217
157
|
Gaps found in the PRD needing resolution. Blank if none.
|
|
218
158
|
```
|
|
219
159
|
|
|
220
|
-
Print one line per phase: `Phase N — <title> (<condition summary>)`.
|
|
160
|
+
Print one line per phase: `Phase N — <title> (<condition summary>)`. Present options and wait:
|
|
161
|
+
|
|
162
|
+
1. Run `/tk:build <slug>` (Recommended)
|
|
163
|
+
2. Run `/tk:check <slug>`
|
|
164
|
+
3. Done for now
|
|
221
165
|
|
|
222
166
|
## Execution guidance
|
|
223
167
|
|
|
@@ -229,10 +173,5 @@ To implement this plan phase by phase, run `/tk:build <slug>`. It handles branch
|
|
|
229
173
|
- Each phase must be demoable end-to-end on its own
|
|
230
174
|
- "Done when" must be a checkbox list of testable conditions, not prose
|
|
231
175
|
- **Safety valve**: if a phase has >5 "Done when" items, stop and split it into smaller phases before continuing
|
|
232
|
-
<!-- if:local -->
|
|
233
176
|
- Never modify the source PRD content — only update frontmatter status fields
|
|
234
|
-
<!-- end:local -->
|
|
235
|
-
<!-- if:github -->
|
|
236
|
-
- Never modify the source PRD content — only update metadata and labels
|
|
237
|
-
<!-- end:github -->
|
|
238
177
|
- Carry PRD's Out of Scope forward verbatim
|
package/skills/prd/SKILL.md
CHANGED
|
@@ -3,8 +3,6 @@ description: Create a PRD through user interview, codebase exploration, and modu
|
|
|
3
3
|
argument-hint: <idea>
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
**Config**: read `.tracerkit/config.json` (default: `local`). Follow matching `<!-- if:local/github -->` blocks.
|
|
7
|
-
|
|
8
6
|
# PRD Writing
|
|
9
7
|
|
|
10
8
|
Skip satisfied steps. If argument provided, skip to Step 2.
|
|
@@ -13,13 +11,7 @@ Skip satisfied steps. If argument provided, skip to Step 2.
|
|
|
13
11
|
|
|
14
12
|
## Pre-loaded context
|
|
15
13
|
|
|
16
|
-
<!-- if:local -->
|
|
17
|
-
|
|
18
14
|
- Existing PRDs: !`ls .tracerkit/prds/*.md 2>/dev/null || echo "(none)"`
|
|
19
|
-
<!-- end:local -->
|
|
20
|
-
<!-- if:github -->
|
|
21
|
-
- Existing PRDs: list open GitHub Issues with label `{{github.labels.prd}}`
|
|
22
|
-
<!-- end:github -->
|
|
23
15
|
|
|
24
16
|
## Input
|
|
25
17
|
|
|
@@ -34,16 +26,10 @@ If empty, go to Step 1; derive slug after gathering the idea. If provided, deriv
|
|
|
34
26
|
5. Take the first 4 remaining words (or fewer if less exist)
|
|
35
27
|
6. Join with hyphens → `<slug>`
|
|
36
28
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
Output: `.tracerkit/prds/<slug>.md`. If exists, ask: "Overwrite existing" / "Pick a new name".
|
|
40
|
-
|
|
41
|
-
<!-- end:local -->
|
|
42
|
-
<!-- if:github -->
|
|
43
|
-
|
|
44
|
-
Output: GitHub Issue with label `{{github.labels.prd}}`, title `[{{github.labels.prd}}] <slug>: <Feature Title>`. If matching issue exists, ask: "Update existing issue" / "Use a new slug".
|
|
29
|
+
Output: `.tracerkit/prds/<slug>.md`. If file exists, present options and wait:
|
|
45
30
|
|
|
46
|
-
|
|
31
|
+
1. Overwrite existing (Recommended)
|
|
32
|
+
2. Pick a new name
|
|
47
33
|
|
|
48
34
|
## Workflow
|
|
49
35
|
|
|
@@ -85,8 +71,6 @@ Present modules. Confirm which need tests.
|
|
|
85
71
|
|
|
86
72
|
### 5. Write PRD
|
|
87
73
|
|
|
88
|
-
<!-- if:local -->
|
|
89
|
-
|
|
90
74
|
Save to `.tracerkit/prds/<slug>.md` (create dir if missing).
|
|
91
75
|
|
|
92
76
|
```markdown
|
|
@@ -98,26 +82,7 @@ status: created
|
|
|
98
82
|
# Feature Name
|
|
99
83
|
```
|
|
100
84
|
|
|
101
|
-
|
|
102
|
-
<!-- if:github -->
|
|
103
|
-
|
|
104
|
-
Ensure labels exist: `gh label create {{github.labels.prd}} --force`, `gh label create tk:created --force`.
|
|
105
|
-
|
|
106
|
-
Create GitHub Issue — title: `[{{github.labels.prd}}] <slug>: <Feature Title>`, labels: `{{github.labels.prd}}`, `tk:created`.
|
|
107
|
-
|
|
108
|
-
```markdown
|
|
109
|
-
<!-- tk:metadata
|
|
110
|
-
slug: <slug>
|
|
111
|
-
created: <UTC ISO 8601>
|
|
112
|
-
status: created
|
|
113
|
-
-->
|
|
114
|
-
|
|
115
|
-
# Feature Name
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
<!-- end:github -->
|
|
119
|
-
|
|
120
|
-
PRD body structure (same for local file and issue body). Omit empty sections. No file paths or code snippets.
|
|
85
|
+
PRD body structure. Omit empty sections. No file paths or code snippets.
|
|
121
86
|
|
|
122
87
|
```
|
|
123
88
|
## Problem Statement
|
|
@@ -136,4 +101,7 @@ PRD body structure (same for local file and issue body). Omit empty sections. No
|
|
|
136
101
|
|
|
137
102
|
---
|
|
138
103
|
|
|
139
|
-
|
|
104
|
+
Present options and wait for the user's choice:
|
|
105
|
+
|
|
106
|
+
1. Run `/tk:plan <slug>` (Recommended)
|
|
107
|
+
2. Done for now
|
|
@@ -1,468 +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 { basename as o, dirname as s, join as c } from "node:path";
|
|
3
|
-
import { createHash as l } from "node:crypto";
|
|
4
|
-
import { fileURLToPath as u } from "node:url";
|
|
5
|
-
import { execSync as d } from "node:child_process";
|
|
6
|
-
//#region src/config.ts
|
|
7
|
-
var f = "local", p = "github", m = [f, p], h = {
|
|
8
|
-
prds: ".tracerkit/prds",
|
|
9
|
-
plans: ".tracerkit/plans"
|
|
10
|
-
}, g = { labels: {
|
|
11
|
-
prd: "tk:prd",
|
|
12
|
-
plan: "tk:plan"
|
|
13
|
-
} };
|
|
14
|
-
function _(t) {
|
|
15
|
-
let r = c(t, ".tracerkit", "config.json");
|
|
16
|
-
if (!e(r)) return {
|
|
17
|
-
storage: f,
|
|
18
|
-
paths: { ...h },
|
|
19
|
-
github: { ...g }
|
|
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: ee(i.storage),
|
|
29
|
-
paths: v(i.paths),
|
|
30
|
-
github: y(i.github)
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
function ee(e) {
|
|
34
|
-
return typeof e == "string" && m.includes(e) ? e : f;
|
|
35
|
-
}
|
|
36
|
-
function v(e) {
|
|
37
|
-
let t = S(e) ? e : {};
|
|
38
|
-
return {
|
|
39
|
-
prds: typeof t.prds == "string" ? t.prds : h.prds,
|
|
40
|
-
plans: typeof t.plans == "string" ? t.plans : h.plans
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
function y(e) {
|
|
44
|
-
let t = S(e) ? e : {}, n = S(t.labels) ? t.labels : {};
|
|
45
|
-
return { labels: {
|
|
46
|
-
prd: typeof n.prd == "string" ? n.prd : g.labels.prd,
|
|
47
|
-
plan: typeof n.plan == "string" ? n.plan : g.labels.plan
|
|
48
|
-
} };
|
|
49
|
-
}
|
|
50
|
-
function b(r, i) {
|
|
51
|
-
let o = c(r, ".tracerkit", "config.json");
|
|
52
|
-
t(s(o), { recursive: !0 });
|
|
53
|
-
let l = {};
|
|
54
|
-
if (e(o)) try {
|
|
55
|
-
l = JSON.parse(n(o, "utf8"));
|
|
56
|
-
} catch {
|
|
57
|
-
l = {};
|
|
58
|
-
}
|
|
59
|
-
let u = x(l, i);
|
|
60
|
-
a(o, JSON.stringify(u, null, 2) + "\n");
|
|
61
|
-
}
|
|
62
|
-
function x(e, t) {
|
|
63
|
-
let n = { ...e };
|
|
64
|
-
for (let e of Object.keys(t)) S(n[e]) && S(t[e]) ? n[e] = x(n[e], t[e]) : n[e] = t[e];
|
|
65
|
-
return n;
|
|
66
|
-
}
|
|
67
|
-
function S(e) {
|
|
68
|
-
return typeof e == "object" && !!e && !Array.isArray(e);
|
|
69
|
-
}
|
|
70
|
-
var C = [
|
|
71
|
-
"tk:brief",
|
|
72
|
-
"tk:prd",
|
|
73
|
-
"tk:plan",
|
|
74
|
-
"tk:build",
|
|
75
|
-
"tk:check"
|
|
76
|
-
], w = ["tk:verify"], T = {
|
|
77
|
-
force: "--force",
|
|
78
|
-
help: "--help",
|
|
79
|
-
version: "--version"
|
|
80
|
-
}, E = [
|
|
81
|
-
"brief",
|
|
82
|
-
"progress",
|
|
83
|
-
"archive"
|
|
84
|
-
], D = [
|
|
85
|
-
{
|
|
86
|
-
name: "init",
|
|
87
|
-
args: "[path]",
|
|
88
|
-
desc: "Install skills to ~/.claude/skills/ (or [path] if given)"
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
name: "update",
|
|
92
|
-
args: "[path]",
|
|
93
|
-
desc: "Refresh unchanged files from latest version, skip modified"
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: "config",
|
|
97
|
-
args: "[path] [key] [value]",
|
|
98
|
-
desc: "Get or set TracerKit configuration"
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
name: "uninstall",
|
|
102
|
-
args: "[path]",
|
|
103
|
-
desc: "Remove TracerKit skill directories, keep .tracerkit/ artifacts"
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
name: "migrate-storage",
|
|
107
|
-
args: "[path]",
|
|
108
|
-
desc: "Migrate artifacts between local and GitHub storage backends"
|
|
109
|
-
}
|
|
110
|
-
], O = c(s(u(import.meta.url)), "..", "skills");
|
|
111
|
-
function k(e, t = "") {
|
|
112
|
-
let n = r(e, { withFileTypes: !0 }), i = [];
|
|
113
|
-
for (let r of n) {
|
|
114
|
-
let n = t ? `${t}/${r.name}` : r.name;
|
|
115
|
-
r.isDirectory() ? i.push(...k(c(e, r.name), n)) : i.push(n);
|
|
116
|
-
}
|
|
117
|
-
return i.sort();
|
|
118
|
-
}
|
|
119
|
-
function A(e) {
|
|
120
|
-
return `.claude/skills/tk:${e}`;
|
|
121
|
-
}
|
|
122
|
-
function j(e) {
|
|
123
|
-
return e.slice(18);
|
|
124
|
-
}
|
|
125
|
-
function M(e, t) {
|
|
126
|
-
let n = e;
|
|
127
|
-
return t.paths.prds !== h.prds && (n = n.replaceAll(h.prds, t.paths.prds)), t.paths.plans !== h.plans && (n = n.replaceAll(h.plans, t.paths.plans)), 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;
|
|
128
|
-
}
|
|
129
|
-
function N(e, r, i) {
|
|
130
|
-
let o = i ?? k(O).map(A);
|
|
131
|
-
for (let i of o) {
|
|
132
|
-
let o = c(O, j(i)), l = c(e, i);
|
|
133
|
-
t(s(l), { recursive: !0 }), a(l, M(n(o, "utf8"), r));
|
|
134
|
-
}
|
|
135
|
-
return { copied: o };
|
|
136
|
-
}
|
|
137
|
-
function P(e) {
|
|
138
|
-
return l("sha256").update(e).digest("hex");
|
|
139
|
-
}
|
|
140
|
-
function F(t, r) {
|
|
141
|
-
let i = k(O).map(A), a = [], o = [], s = [];
|
|
142
|
-
for (let l of i) {
|
|
143
|
-
let i = c(t, l);
|
|
144
|
-
if (!e(i)) s.push(l);
|
|
145
|
-
else {
|
|
146
|
-
let e = M(n(c(O, j(l)), "utf8"), r);
|
|
147
|
-
P(Buffer.from(e)) === P(n(i)) ? a.push(l) : o.push(l);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return {
|
|
151
|
-
unchanged: a,
|
|
152
|
-
modified: o,
|
|
153
|
-
missing: s
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
//#endregion
|
|
157
|
-
//#region src/commands/update.ts
|
|
158
|
-
function I(t, n) {
|
|
159
|
-
if (!C.some((n) => e(c(t, ".claude", "skills", n)))) throw Error("TracerKit not initialized — run `tracerkit init` first");
|
|
160
|
-
let r = _(t), { unchanged: a, modified: o, missing: s } = F(t, r), l = [];
|
|
161
|
-
for (let n of w) {
|
|
162
|
-
let r = c(t, ".claude", "skills", n);
|
|
163
|
-
e(r) && (i(r, {
|
|
164
|
-
recursive: !0,
|
|
165
|
-
force: !0
|
|
166
|
-
}), l.push(`✗ .claude/skills/${n}/ removed (deprecated)`));
|
|
167
|
-
}
|
|
168
|
-
let u = n?.force ?? !1, d = [
|
|
169
|
-
...a,
|
|
170
|
-
...s,
|
|
171
|
-
...u ? o : []
|
|
172
|
-
];
|
|
173
|
-
if (d.length > 0) {
|
|
174
|
-
N(t, r, d);
|
|
175
|
-
for (let e of a) l.push(`✓ ${e}`);
|
|
176
|
-
for (let e of s) l.push(`✓ ${e} (added)`);
|
|
177
|
-
if (u) for (let e of o) l.push(`✓ ${e} (replaced)`);
|
|
178
|
-
}
|
|
179
|
-
if (!u && o.length > 0) {
|
|
180
|
-
for (let e of o) l.push(`⚠ ${e} (skipped — modified)`);
|
|
181
|
-
l.push("", "Run `tracerkit update --force` to replace modified files with latest versions.");
|
|
182
|
-
}
|
|
183
|
-
return l;
|
|
184
|
-
}
|
|
185
|
-
//#endregion
|
|
186
|
-
//#region src/commands/init.ts
|
|
187
|
-
function te(t) {
|
|
188
|
-
if (C.some((n) => e(c(t, ".claude", "skills", n)))) return I(t, { force: !1 });
|
|
189
|
-
let { copied: n } = N(t, _(t));
|
|
190
|
-
return n.map((e) => `✓ ${e}`);
|
|
191
|
-
}
|
|
192
|
-
//#endregion
|
|
193
|
-
//#region src/commands/migrate-storage.ts
|
|
194
|
-
var L = {
|
|
195
|
-
created: "tk:created",
|
|
196
|
-
in_progress: "tk:in-progress",
|
|
197
|
-
done: "tk:done"
|
|
198
|
-
}, R = {
|
|
199
|
-
"tk:created": "created",
|
|
200
|
-
"tk:in-progress": "in_progress",
|
|
201
|
-
"tk:done": "done"
|
|
202
|
-
};
|
|
203
|
-
function z(e) {
|
|
204
|
-
let t = e.match(/^---\n([\s\S]*?)---\n([\s\S]*)$/);
|
|
205
|
-
if (!t) return {
|
|
206
|
-
metadata: {},
|
|
207
|
-
body: e
|
|
208
|
-
};
|
|
209
|
-
let n = {};
|
|
210
|
-
for (let e of t[1].split("\n")) {
|
|
211
|
-
let t = e.indexOf(":");
|
|
212
|
-
if (t === -1) continue;
|
|
213
|
-
let r = e.slice(0, t).trim(), i = e.slice(t + 1).trim();
|
|
214
|
-
r && (n[r] = i);
|
|
215
|
-
}
|
|
216
|
-
return {
|
|
217
|
-
metadata: n,
|
|
218
|
-
body: t[2]
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
function B(e) {
|
|
222
|
-
let t = Object.entries(e);
|
|
223
|
-
return t.length === 0 ? "<!-- tk:metadata\n-->" : `<!-- tk:metadata\n${t.map(([e, t]) => `${e}: ${t}`).join("\n")}\n-->`;
|
|
224
|
-
}
|
|
225
|
-
function V(e) {
|
|
226
|
-
return L[e] ?? "tk:created";
|
|
227
|
-
}
|
|
228
|
-
function H(e) {
|
|
229
|
-
let t = e.match(/<!--\s*tk:metadata\n([\s\S]*?)-->\n*([\s\S]*)$/);
|
|
230
|
-
if (!t) return {
|
|
231
|
-
metadata: {},
|
|
232
|
-
body: e
|
|
233
|
-
};
|
|
234
|
-
let n = {};
|
|
235
|
-
for (let e of t[1].split("\n")) {
|
|
236
|
-
let t = e.indexOf(":");
|
|
237
|
-
if (t === -1) continue;
|
|
238
|
-
let r = e.slice(0, t).trim(), i = e.slice(t + 1).trim();
|
|
239
|
-
r && (n[r] = i);
|
|
240
|
-
}
|
|
241
|
-
return {
|
|
242
|
-
metadata: n,
|
|
243
|
-
body: t[2]
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
function U(e) {
|
|
247
|
-
let t = Object.entries(e);
|
|
248
|
-
return t.length === 0 ? "---\n---\n" : `---\n${t.map(([e, t]) => `${e}: ${t}`).join("\n")}\n---\n`;
|
|
249
|
-
}
|
|
250
|
-
function W(e) {
|
|
251
|
-
let t = e.match(/\[[^\]]+\]\s+([^:]+):/);
|
|
252
|
-
return t ? t[1].trim() : null;
|
|
253
|
-
}
|
|
254
|
-
function G(e) {
|
|
255
|
-
let t = e.match(/^#\s+(.+)$/m);
|
|
256
|
-
return t ? t[1].trim() : "Untitled";
|
|
257
|
-
}
|
|
258
|
-
function K(t, i) {
|
|
259
|
-
let a = [], s = c(t, i.paths.prds);
|
|
260
|
-
if (e(s)) for (let e of r(s)) {
|
|
261
|
-
if (!e.endsWith(".md")) continue;
|
|
262
|
-
let t = o(e, ".md"), { metadata: r, body: i } = z(n(c(s, e), "utf8"));
|
|
263
|
-
a.push({
|
|
264
|
-
slug: t,
|
|
265
|
-
type: "prd",
|
|
266
|
-
metadata: r,
|
|
267
|
-
body: i,
|
|
268
|
-
title: G(i)
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
let l = c(t, i.paths.plans);
|
|
272
|
-
if (e(l)) for (let e of r(l)) {
|
|
273
|
-
if (!e.endsWith(".md")) continue;
|
|
274
|
-
let t = o(e, ".md"), r = n(c(l, e), "utf8"), { metadata: i, body: s } = z(r);
|
|
275
|
-
a.push({
|
|
276
|
-
slug: t,
|
|
277
|
-
type: "plan",
|
|
278
|
-
metadata: i,
|
|
279
|
-
body: s,
|
|
280
|
-
title: G(s || r)
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
return a;
|
|
284
|
-
}
|
|
285
|
-
function q(e) {
|
|
286
|
-
let t = e;
|
|
287
|
-
if (t.code === "ENOENT") return /* @__PURE__ */ Error("gh CLI not found — install it: https://cli.github.com");
|
|
288
|
-
let n = (t.stderr ?? t.message ?? "").toLowerCase();
|
|
289
|
-
return n.includes("not logged in") || n.includes("authentication") ? /* @__PURE__ */ Error("Not authenticated with GitHub. Run: gh auth login") : n.includes("rate limit") || n.includes("403") ? /* @__PURE__ */ Error("GitHub rate limit exceeded. Wait and retry.") : n.includes("not found") || n.includes("404") ? /* @__PURE__ */ Error("Repository not found. Check your git remote configuration.") : e instanceof Error ? e : Error(String(e));
|
|
290
|
-
}
|
|
291
|
-
function J(e) {
|
|
292
|
-
try {
|
|
293
|
-
return d(`gh ${e.map((e) => `'${e.replace(/'/g, "'\\''")}'`).join(" ")}`, {
|
|
294
|
-
encoding: "utf8",
|
|
295
|
-
stdio: [
|
|
296
|
-
"pipe",
|
|
297
|
-
"pipe",
|
|
298
|
-
"pipe"
|
|
299
|
-
]
|
|
300
|
-
}).trim();
|
|
301
|
-
} catch (e) {
|
|
302
|
-
throw q(e);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
function Y(e, t) {
|
|
306
|
-
let n = t([
|
|
307
|
-
"issue",
|
|
308
|
-
"list",
|
|
309
|
-
"--label",
|
|
310
|
-
e,
|
|
311
|
-
"--state",
|
|
312
|
-
"all",
|
|
313
|
-
"--json",
|
|
314
|
-
"number,title,body,labels,state",
|
|
315
|
-
"--limit",
|
|
316
|
-
"1000"
|
|
317
|
-
]);
|
|
318
|
-
return JSON.parse(n || "[]");
|
|
319
|
-
}
|
|
320
|
-
function X(e, t) {
|
|
321
|
-
return t.some((t) => {
|
|
322
|
-
if (t.body) {
|
|
323
|
-
let { metadata: n } = H(t.body);
|
|
324
|
-
if (n.slug) return n.slug === e;
|
|
325
|
-
}
|
|
326
|
-
return W(t.title) === e;
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
function Z(e, t) {
|
|
330
|
-
for (let n of e) t([
|
|
331
|
-
"label",
|
|
332
|
-
"create",
|
|
333
|
-
n,
|
|
334
|
-
"--force"
|
|
335
|
-
]);
|
|
336
|
-
}
|
|
337
|
-
function ne(e, t) {
|
|
338
|
-
let n = [
|
|
339
|
-
"issue",
|
|
340
|
-
"create",
|
|
341
|
-
"--title",
|
|
342
|
-
e.title,
|
|
343
|
-
"--body",
|
|
344
|
-
e.body
|
|
345
|
-
];
|
|
346
|
-
for (let t of e.labels) n.push("--label", t);
|
|
347
|
-
let r = t(n).match(/\/(\d+)\s*$/);
|
|
348
|
-
return r ? parseInt(r[1], 10) : 0;
|
|
349
|
-
}
|
|
350
|
-
function re(e, t) {
|
|
351
|
-
t([
|
|
352
|
-
"issue",
|
|
353
|
-
"close",
|
|
354
|
-
String(e)
|
|
355
|
-
]);
|
|
356
|
-
}
|
|
357
|
-
function ie(e, t) {
|
|
358
|
-
let n = t([
|
|
359
|
-
"pr",
|
|
360
|
-
"list",
|
|
361
|
-
"--search",
|
|
362
|
-
e,
|
|
363
|
-
"--state",
|
|
364
|
-
"merged",
|
|
365
|
-
"--json",
|
|
366
|
-
"number,title",
|
|
367
|
-
"--limit",
|
|
368
|
-
"5"
|
|
369
|
-
]);
|
|
370
|
-
return JSON.parse(n || "[]");
|
|
371
|
-
}
|
|
372
|
-
function ae(e, t, n) {
|
|
373
|
-
n([
|
|
374
|
-
"issue",
|
|
375
|
-
"comment",
|
|
376
|
-
String(e),
|
|
377
|
-
"--body",
|
|
378
|
-
t
|
|
379
|
-
]);
|
|
380
|
-
}
|
|
381
|
-
function oe(e) {
|
|
382
|
-
for (let t of e) {
|
|
383
|
-
let e = R[t];
|
|
384
|
-
if (e) return e;
|
|
385
|
-
}
|
|
386
|
-
return "created";
|
|
387
|
-
}
|
|
388
|
-
function Q(e, n) {
|
|
389
|
-
t(s(e), { recursive: !0 }), a(e, n);
|
|
390
|
-
}
|
|
391
|
-
function $(e, t) {
|
|
392
|
-
let n = t?.runGh ?? J, r = _(e);
|
|
393
|
-
return r.storage === "local" ? se(e, r, n) : le(e, r, n);
|
|
394
|
-
}
|
|
395
|
-
function se(e, t, n) {
|
|
396
|
-
let r = [], i = K(e, t);
|
|
397
|
-
if (i.length === 0) return b(e, { storage: p }), r.push("No artifacts found — nothing to migrate."), r.push(`✓ Storage switched to "${p}".`), r;
|
|
398
|
-
let a = t.github.labels?.prd ?? "tk:prd", o = t.github.labels?.plan ?? "tk:plan";
|
|
399
|
-
Z([
|
|
400
|
-
a,
|
|
401
|
-
o,
|
|
402
|
-
...[...new Set(i.map((e) => V(e.metadata.status ?? "created")))]
|
|
403
|
-
], n);
|
|
404
|
-
let s = Y(a, n), c = Y(o, n);
|
|
405
|
-
for (let e of i) {
|
|
406
|
-
let t = e.type === "prd" ? a : o, i = e.type === "prd" ? s : c;
|
|
407
|
-
if (X(e.slug, i)) {
|
|
408
|
-
r.push(`⚠ skip ${e.type} "${e.slug}" — already exists on GitHub`);
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
let l = e.metadata.status ?? "created", u = V(l), d = `${B(e.metadata)}\n\n${e.body.replace(/^\n/, "")}`, f = ne({
|
|
412
|
-
title: `[${t}] ${e.slug}: ${e.title}`,
|
|
413
|
-
body: d,
|
|
414
|
-
labels: [t, u]
|
|
415
|
-
}, n);
|
|
416
|
-
l === "done" ? (re(f, n), ce(e.slug, f, n), r.push(`✓ ${e.type} "${e.slug}" → issue #${f} (closed)`)) : r.push(`✓ ${e.type} "${e.slug}" → issue #${f}`);
|
|
417
|
-
}
|
|
418
|
-
return b(e, { storage: p }), r.push(`✓ Storage switched to "${p}".`), r;
|
|
419
|
-
}
|
|
420
|
-
function ce(e, t, n) {
|
|
421
|
-
let r = ie(e, n);
|
|
422
|
-
r.length !== 0 && ae(t, `Linked PR: ${r.map((e) => `#${e.number}`).join(", ")}`, n);
|
|
423
|
-
}
|
|
424
|
-
function le(t, n, r) {
|
|
425
|
-
let i = [], a = n.github.labels?.prd ?? "tk:prd", o = n.github.labels?.plan ?? "tk:plan", s = Y(a, r), l = Y(o, r), u = [...s.map((e) => ({
|
|
426
|
-
...e,
|
|
427
|
-
type: "prd"
|
|
428
|
-
})), ...l.map((e) => ({
|
|
429
|
-
...e,
|
|
430
|
-
type: "plan"
|
|
431
|
-
}))];
|
|
432
|
-
if (u.length === 0) return b(t, { storage: f }), i.push("No GitHub issues found — nothing to migrate."), i.push(`✓ Storage switched to "${f}".`), i;
|
|
433
|
-
for (let r of u) {
|
|
434
|
-
let { metadata: a, body: o } = H(r.body ?? ""), s = a.slug ?? W(r.title);
|
|
435
|
-
if (!s) continue;
|
|
436
|
-
let l = r.labels.map((e) => e.name), u = a.status ?? oe(l), d = c(t, r.type === "prd" ? n.paths.prds : n.paths.plans, `${s}.md`);
|
|
437
|
-
if (e(d)) {
|
|
438
|
-
i.push(`⚠ skip ${r.type} "${s}" — local file already exists`);
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
if (r.type === "prd") Q(d, `${U({
|
|
442
|
-
...a,
|
|
443
|
-
status: u
|
|
444
|
-
})}\n${o}`);
|
|
445
|
-
else {
|
|
446
|
-
let e = {};
|
|
447
|
-
a.source_prd && (e.source_prd = a.source_prd), (a.slug || s) && (e.slug = a.slug ?? s), u && (e.status = u), a.completed && (e.completed = a.completed), Q(d, `${U(e)}\n${o}`);
|
|
448
|
-
}
|
|
449
|
-
i.push(`✓ ${r.type} "${s}" → ${d}`);
|
|
450
|
-
}
|
|
451
|
-
return b(t, { storage: f }), i.push(`✓ Storage switched to "${f}".`), i;
|
|
452
|
-
}
|
|
453
|
-
//#endregion
|
|
454
|
-
//#region src/commands/uninstall.ts
|
|
455
|
-
function ue(t) {
|
|
456
|
-
if (!C.some((n) => e(c(t, ".claude", "skills", n)))) throw Error("TracerKit not initialized — nothing to uninstall");
|
|
457
|
-
let n = [];
|
|
458
|
-
for (let r of C) {
|
|
459
|
-
let a = c(t, ".claude", "skills", r);
|
|
460
|
-
e(a) && (i(a, {
|
|
461
|
-
recursive: !0,
|
|
462
|
-
force: !0
|
|
463
|
-
}), n.push(`✗ .claude/skills/${r}/ removed`));
|
|
464
|
-
}
|
|
465
|
-
return n;
|
|
466
|
-
}
|
|
467
|
-
//#endregion
|
|
468
|
-
export { N as a, w as c, f as d, _ as f, I as i, T as l, $ as n, D as o, b as p, te as r, E as s, ue as t, C as u };
|