tracerkit 1.13.1 → 1.15.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 CHANGED
@@ -103,6 +103,14 @@ tracerkit config github.repo org/repo # set target repo
103
103
 
104
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
105
 
106
+ To migrate existing artifacts between backends:
107
+
108
+ ```bash
109
+ tracerkit migrate-storage # local→github or github→local (auto-detected)
110
+ ```
111
+
112
+ 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. For archived features migrating to GitHub, merged PRs matching the slug are linked automatically.
113
+
106
114
  </details>
107
115
 
108
116
  ## Skills
@@ -137,13 +145,13 @@ Without arguments, shows a feature dashboard with status and progress before ask
137
145
 
138
146
  ## Docs
139
147
 
140
- | Document | Description |
141
- | ------------------------------------------------ | -------------------------------------------------- |
142
- | [Examples](docs/examples.md) | Walk through end-to-end usage scenarios |
143
- | [CLI Reference](docs/cli-reference.md) | Commands: init, update, config, uninstall |
144
- | [Configuration](docs/configuration.md) | Storage backends, GitHub options, custom paths |
145
- | [Metadata Lifecycle](docs/metadata-lifecycle.md) | Understand YAML frontmatter states and transitions |
146
- | [Comparison](docs/comparison.md) | Compare TracerKit to Spec Kit, Kiro, and OpenSpec |
148
+ | Document | Description |
149
+ | ------------------------------------------------ | ---------------------------------------------------------- |
150
+ | [Examples](docs/examples.md) | Walk through end-to-end usage scenarios |
151
+ | [CLI Reference](docs/cli-reference.md) | Commands: init, update, config, migrate-storage, uninstall |
152
+ | [Configuration](docs/configuration.md) | Storage backends, GitHub options, custom paths |
153
+ | [Metadata Lifecycle](docs/metadata-lifecycle.md) | Understand YAML frontmatter states and transitions |
154
+ | [Comparison](docs/comparison.md) | Compare TracerKit to Spec Kit, Kiro, and OpenSpec |
147
155
 
148
156
  ## Contributing
149
157
 
package/dist/bin.js CHANGED
@@ -1,34 +1,34 @@
1
1
  #!/usr/bin/env node
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";
2
+ import { a as e, f as t, i as n, l as r, n as i, o as a, p as o, r as s, s as c, t as l, u } from "./uninstall-k_ZGY-Mk.js";
3
+ import { existsSync as d, readFileSync as f, statSync as p } from "node:fs";
4
+ import { dirname as m, join as h, resolve as g } from "node:path";
5
+ import { fileURLToPath as _ } from "node:url";
6
+ import { homedir as v } from "node:os";
7
7
  //#region src/commands/config.ts
8
- function v(e, t) {
8
+ function y(e, t) {
9
9
  let [n, r] = t;
10
- return n ? r ? x(e, n, r) : b(e, n) : y(e);
10
+ return n ? r ? S(e, n, r) : x(e, n) : b(e);
11
11
  }
12
- function y(e) {
13
- let t = n(e), r = t.storage === "local" ? {
14
- storage: t.storage,
15
- paths: t.paths
16
- } : t;
12
+ function b(e) {
13
+ let n = t(e), r = n.storage === "local" ? {
14
+ storage: n.storage,
15
+ paths: n.paths
16
+ } : n;
17
17
  return JSON.stringify(r, null, 2).split("\n");
18
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)];
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
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;
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
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"));
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
30
  }
31
- function C(e, t) {
31
+ function w(e, t) {
32
32
  let n = t.split("."), r = e;
33
33
  for (let e of n) {
34
34
  if (typeof r != "object" || !r) return;
@@ -36,7 +36,7 @@ function C(e, t) {
36
36
  }
37
37
  return r;
38
38
  }
39
- function w(e, t) {
39
+ function T(e, t) {
40
40
  let n = e.split("."), r = {}, i = r;
41
41
  for (let e = 0; e < n.length - 1; e++) {
42
42
  let t = {};
@@ -46,11 +46,11 @@ function w(e, t) {
46
46
  }
47
47
  //#endregion
48
48
  //#region src/cli.ts
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 = [
49
+ var { version: E } = JSON.parse(f(g(m(_(import.meta.url)), "..", "package.json"), "utf8")), D = Math.max(...a.map((e) => `${e.name} ${e.args}`.length)), O = [
50
50
  "Usage: tracerkit <command> [path]",
51
51
  "",
52
52
  "Commands:",
53
- ...e.map((e) => ` ${`${e.name} ${e.args}`.padEnd(E + 2)}${e.desc}`),
53
+ ...a.map((e) => ` ${`${e.name} ${e.args}`.padEnd(D + 2)}${e.desc}`),
54
54
  "",
55
55
  "Options:",
56
56
  " --force Overwrite modified files during update",
@@ -60,39 +60,40 @@ var { version: T } = JSON.parse(d(h(p(g(import.meta.url)), "..", "package.json")
60
60
  "init/update/uninstall default to the home directory when no path is given.",
61
61
  "config defaults to the current working directory."
62
62
  ];
63
- function O(e) {
63
+ function k(e) {
64
64
  if (!e) return !1;
65
65
  try {
66
- return f(h(e)).isDirectory();
66
+ return p(g(e)).isDirectory();
67
67
  } catch {
68
68
  return !1;
69
69
  }
70
70
  }
71
- function k(e, t = _()) {
71
+ function A(e, t = v()) {
72
72
  let n = e.find((e) => !e.startsWith("-"));
73
- return n ? h(n) : t;
73
+ return n ? g(n) : t;
74
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));
75
+ function j(e) {
76
+ if (e.includes(r.help) || e.includes("-h")) return O;
77
+ if (e.includes(r.version) || e.includes("-v")) return [`tracerkit/${E}`];
78
+ let t = e[0], a = e.slice(1);
79
+ if (c.includes(t)) return [`"${t}" has been removed — skills handle this now.`, "Run `tracerkit update` to get the latest skills."];
80
+ switch (t) {
81
+ case "init": return s(A(a));
82
82
  case "update": {
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;
83
+ let e = a.includes(r.force), t = n(A(a.filter((e) => e !== r.force)), { force: e });
84
+ return t.push("", "Updated to the latest TracerKit."), t.push("If using Claude Code, restart your session to load changes."), t;
85
85
  }
86
86
  case "config": {
87
- let e = O(r[0]);
88
- return v(e ? h(r[0]) : process.cwd(), e ? r.slice(1) : r);
87
+ let e = k(a[0]);
88
+ return y(e ? g(a[0]) : process.cwd(), e ? a.slice(1) : a);
89
89
  }
90
- case "uninstall": return l(k(r));
91
- default: return D;
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;
92
93
  }
93
94
  }
94
95
  //#endregion
95
96
  //#region src/bin.ts
96
- var j = A(process.argv.slice(2));
97
- for (let e of j) console.log(e);
97
+ var M = j(process.argv.slice(2));
98
+ for (let e of M) console.log(e);
98
99
  //#endregion
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
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 };
1
+ import { c as e, i as t, n, o as r, r as i, t as a, u as o } from "./uninstall-k_ZGY-Mk.js";
2
+ export { r as COMMANDS, e as DEPRECATED_SKILLS, o as SKILL_NAMES, i as init, n as migrateStorage, a as uninstall, t as update };
@@ -0,0 +1,518 @@
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
+ archives: ".tracerkit/archives"
11
+ }, g = { labels: {
12
+ prd: "tk:prd",
13
+ plan: "tk:plan"
14
+ } };
15
+ function _(t) {
16
+ let r = c(t, ".tracerkit", "config.json");
17
+ if (!e(r)) return {
18
+ storage: f,
19
+ paths: { ...h },
20
+ github: { ...g }
21
+ };
22
+ let i;
23
+ try {
24
+ i = JSON.parse(n(r, "utf8"));
25
+ } catch {
26
+ throw Error("Invalid .tracerkit/config.json — expected valid JSON");
27
+ }
28
+ return {
29
+ storage: ee(i.storage),
30
+ paths: v(i.paths),
31
+ github: y(i.github)
32
+ };
33
+ }
34
+ function ee(e) {
35
+ return typeof e == "string" && m.includes(e) ? e : f;
36
+ }
37
+ function v(e) {
38
+ let t = S(e) ? e : {};
39
+ return {
40
+ prds: typeof t.prds == "string" ? t.prds : h.prds,
41
+ plans: typeof t.plans == "string" ? t.plans : h.plans,
42
+ archives: typeof t.archives == "string" ? t.archives : h.archives
43
+ };
44
+ }
45
+ function y(e) {
46
+ let t = S(e) ? e : {}, n = S(t.labels) ? t.labels : {};
47
+ return {
48
+ ...typeof t.repo == "string" ? { repo: t.repo } : {},
49
+ labels: {
50
+ prd: typeof n.prd == "string" ? n.prd : g.labels.prd,
51
+ plan: typeof n.plan == "string" ? n.plan : g.labels.plan
52
+ }
53
+ };
54
+ }
55
+ function b(r, i) {
56
+ let o = c(r, ".tracerkit", "config.json");
57
+ t(s(o), { recursive: !0 });
58
+ let l = {};
59
+ if (e(o)) try {
60
+ l = JSON.parse(n(o, "utf8"));
61
+ } catch {
62
+ l = {};
63
+ }
64
+ let u = x(l, i);
65
+ a(o, JSON.stringify(u, null, 2) + "\n");
66
+ }
67
+ function x(e, t) {
68
+ let n = { ...e };
69
+ for (let e of Object.keys(t)) S(n[e]) && S(t[e]) ? n[e] = x(n[e], t[e]) : n[e] = t[e];
70
+ return n;
71
+ }
72
+ function S(e) {
73
+ return typeof e == "object" && !!e && !Array.isArray(e);
74
+ }
75
+ var C = [
76
+ "tk:brief",
77
+ "tk:prd",
78
+ "tk:plan",
79
+ "tk:check"
80
+ ], w = ["tk:verify"], T = {
81
+ force: "--force",
82
+ help: "--help",
83
+ version: "--version"
84
+ }, E = [
85
+ "brief",
86
+ "progress",
87
+ "archive"
88
+ ], D = [
89
+ {
90
+ name: "init",
91
+ args: "[path]",
92
+ desc: "Install skills to ~/.claude/skills/ (or [path] if given)"
93
+ },
94
+ {
95
+ name: "update",
96
+ args: "[path]",
97
+ desc: "Refresh unchanged files from latest version, skip modified"
98
+ },
99
+ {
100
+ name: "config",
101
+ args: "[path] [key] [value]",
102
+ desc: "Get or set TracerKit configuration"
103
+ },
104
+ {
105
+ name: "uninstall",
106
+ args: "[path]",
107
+ desc: "Remove TracerKit skill directories, keep .tracerkit/ artifacts"
108
+ },
109
+ {
110
+ name: "migrate-storage",
111
+ args: "[path]",
112
+ desc: "Migrate artifacts between local and GitHub storage backends"
113
+ }
114
+ ], O = c(s(u(import.meta.url)), "..", "skills");
115
+ function k(e, t = "") {
116
+ let n = r(e, { withFileTypes: !0 }), i = [];
117
+ for (let r of n) {
118
+ let n = t ? `${t}/${r.name}` : r.name;
119
+ r.isDirectory() ? i.push(...k(c(e, r.name), n)) : i.push(n);
120
+ }
121
+ return i.sort();
122
+ }
123
+ function A(e) {
124
+ return `.claude/skills/tk:${e}`;
125
+ }
126
+ function j(e) {
127
+ return e.slice(18);
128
+ }
129
+ function M(e, t) {
130
+ let n = e;
131
+ 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.paths.archives !== h.archives && (n = n.replaceAll(h.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;
132
+ }
133
+ function N(e, r, i) {
134
+ let o = i ?? k(O).map(A);
135
+ for (let i of o) {
136
+ let o = c(O, j(i)), l = c(e, i);
137
+ t(s(l), { recursive: !0 }), a(l, M(n(o, "utf8"), r));
138
+ }
139
+ return { copied: o };
140
+ }
141
+ function P(e) {
142
+ return l("sha256").update(e).digest("hex");
143
+ }
144
+ function F(t, r) {
145
+ let i = k(O).map(A), a = [], o = [], s = [];
146
+ for (let l of i) {
147
+ let i = c(t, l);
148
+ if (!e(i)) s.push(l);
149
+ else {
150
+ let e = M(n(c(O, j(l)), "utf8"), r);
151
+ P(Buffer.from(e)) === P(n(i)) ? a.push(l) : o.push(l);
152
+ }
153
+ }
154
+ return {
155
+ unchanged: a,
156
+ modified: o,
157
+ missing: s
158
+ };
159
+ }
160
+ //#endregion
161
+ //#region src/commands/update.ts
162
+ function I(t, n) {
163
+ if (!C.some((n) => e(c(t, ".claude", "skills", n)))) throw Error("TracerKit not initialized — run `tracerkit init` first");
164
+ let r = _(t), { unchanged: a, modified: o, missing: s } = F(t, r), l = [];
165
+ for (let n of w) {
166
+ let r = c(t, ".claude", "skills", n);
167
+ e(r) && (i(r, {
168
+ recursive: !0,
169
+ force: !0
170
+ }), l.push(`✗ .claude/skills/${n}/ removed (deprecated)`));
171
+ }
172
+ let u = n?.force ?? !1, d = [
173
+ ...a,
174
+ ...s,
175
+ ...u ? o : []
176
+ ];
177
+ if (d.length > 0) {
178
+ N(t, r, d);
179
+ for (let e of a) l.push(`✓ ${e}`);
180
+ for (let e of s) l.push(`✓ ${e} (added)`);
181
+ if (u) for (let e of o) l.push(`✓ ${e} (replaced)`);
182
+ }
183
+ if (!u && o.length > 0) {
184
+ for (let e of o) l.push(`⚠ ${e} (skipped — modified)`);
185
+ l.push("", "Run `tracerkit update --force` to replace modified files with latest versions.");
186
+ }
187
+ return l;
188
+ }
189
+ //#endregion
190
+ //#region src/commands/init.ts
191
+ function te(t) {
192
+ if (C.some((n) => e(c(t, ".claude", "skills", n)))) return I(t, { force: !1 });
193
+ let { copied: n } = N(t, _(t));
194
+ return n.map((e) => `✓ ${e}`);
195
+ }
196
+ //#endregion
197
+ //#region src/commands/migrate-storage.ts
198
+ var L = {
199
+ created: "tk:created",
200
+ in_progress: "tk:in-progress",
201
+ done: "tk:done"
202
+ }, R = {
203
+ "tk:created": "created",
204
+ "tk:in-progress": "in_progress",
205
+ "tk:done": "done"
206
+ };
207
+ function z(e) {
208
+ let t = e.match(/^---\n([\s\S]*?)---\n([\s\S]*)$/);
209
+ if (!t) return {
210
+ metadata: {},
211
+ body: e
212
+ };
213
+ let n = {};
214
+ for (let e of t[1].split("\n")) {
215
+ let t = e.indexOf(":");
216
+ if (t === -1) continue;
217
+ let r = e.slice(0, t).trim(), i = e.slice(t + 1).trim();
218
+ r && (n[r] = i);
219
+ }
220
+ return {
221
+ metadata: n,
222
+ body: t[2]
223
+ };
224
+ }
225
+ function B(e) {
226
+ let t = Object.entries(e);
227
+ return t.length === 0 ? "<!-- tk:metadata\n-->" : `<!-- tk:metadata\n${t.map(([e, t]) => `${e}: ${t}`).join("\n")}\n-->`;
228
+ }
229
+ function V(e) {
230
+ return L[e] ?? "tk:created";
231
+ }
232
+ function H(e) {
233
+ let t = e.match(/<!--\s*tk:metadata\n([\s\S]*?)-->\n*([\s\S]*)$/);
234
+ if (!t) return {
235
+ metadata: {},
236
+ body: e
237
+ };
238
+ let n = {};
239
+ for (let e of t[1].split("\n")) {
240
+ let t = e.indexOf(":");
241
+ if (t === -1) continue;
242
+ let r = e.slice(0, t).trim(), i = e.slice(t + 1).trim();
243
+ r && (n[r] = i);
244
+ }
245
+ return {
246
+ metadata: n,
247
+ body: t[2]
248
+ };
249
+ }
250
+ function U(e) {
251
+ let t = Object.entries(e);
252
+ return t.length === 0 ? "---\n---\n" : `---\n${t.map(([e, t]) => `${e}: ${t}`).join("\n")}\n---\n`;
253
+ }
254
+ function W(e) {
255
+ let t = e.match(/\[[^\]]+\]\s+([^:]+):/);
256
+ return t ? t[1].trim() : null;
257
+ }
258
+ function G(e) {
259
+ let t = e.match(/^#\s+(.+)$/m);
260
+ return t ? t[1].trim() : "Untitled";
261
+ }
262
+ function K(t, i) {
263
+ let a = [], s = c(t, i.paths.prds);
264
+ if (e(s)) for (let e of r(s)) {
265
+ if (!e.endsWith(".md")) continue;
266
+ let t = o(e, ".md"), { metadata: r, body: i } = z(n(c(s, e), "utf8"));
267
+ a.push({
268
+ slug: t,
269
+ type: "prd",
270
+ metadata: r,
271
+ body: i,
272
+ title: G(i),
273
+ archived: !1
274
+ });
275
+ }
276
+ let l = c(t, i.paths.plans);
277
+ if (e(l)) for (let e of r(l)) {
278
+ if (!e.endsWith(".md")) continue;
279
+ let t = o(e, ".md"), r = n(c(l, e), "utf8");
280
+ a.push({
281
+ slug: t,
282
+ type: "plan",
283
+ metadata: {},
284
+ body: r,
285
+ title: G(r),
286
+ archived: !1
287
+ });
288
+ }
289
+ let u = c(t, i.paths.archives);
290
+ if (e(u)) for (let t of r(u, { withFileTypes: !0 })) {
291
+ if (!t.isDirectory()) continue;
292
+ let r = t.name, i = c(u, r, "prd.md"), o = c(u, r, "plan.md");
293
+ if (e(i)) {
294
+ let { metadata: e, body: t } = z(n(i, "utf8"));
295
+ a.push({
296
+ slug: r,
297
+ type: "prd",
298
+ metadata: {
299
+ ...e,
300
+ status: "done"
301
+ },
302
+ body: t,
303
+ title: G(t),
304
+ archived: !0
305
+ });
306
+ }
307
+ if (e(o)) {
308
+ let e = n(o, "utf8");
309
+ a.push({
310
+ slug: r,
311
+ type: "plan",
312
+ metadata: { status: "done" },
313
+ body: e,
314
+ title: G(e),
315
+ archived: !0
316
+ });
317
+ }
318
+ }
319
+ return a;
320
+ }
321
+ function q(e) {
322
+ return d(`gh ${e.map((e) => `'${e.replace(/'/g, "'\\''")}'`).join(" ")}`, {
323
+ encoding: "utf8",
324
+ stdio: [
325
+ "pipe",
326
+ "pipe",
327
+ "pipe"
328
+ ]
329
+ }).trim();
330
+ }
331
+ function J(e, t) {
332
+ return e.github.repo ? e.github.repo : t([
333
+ "repo",
334
+ "view",
335
+ "--json",
336
+ "nameWithOwner",
337
+ "-q",
338
+ ".nameWithOwner"
339
+ ]);
340
+ }
341
+ function Y(e, t, n) {
342
+ let r = n([
343
+ "issue",
344
+ "list",
345
+ "--repo",
346
+ e,
347
+ "--label",
348
+ t,
349
+ "--state",
350
+ "all",
351
+ "--json",
352
+ "number,title,body,labels,state",
353
+ "--limit",
354
+ "1000"
355
+ ]);
356
+ return JSON.parse(r || "[]");
357
+ }
358
+ function X(e, t) {
359
+ return t.some((t) => W(t.title) === e);
360
+ }
361
+ function Z(e, t, n) {
362
+ for (let r of t) n([
363
+ "label",
364
+ "create",
365
+ r,
366
+ "--repo",
367
+ e,
368
+ "--force"
369
+ ]);
370
+ }
371
+ function ne(e, t, n) {
372
+ let r = [
373
+ "issue",
374
+ "create",
375
+ "--repo",
376
+ e,
377
+ "--title",
378
+ t.title,
379
+ "--body",
380
+ t.body
381
+ ];
382
+ for (let e of t.labels) r.push("--label", e);
383
+ let i = n(r).match(/\/(\d+)\s*$/);
384
+ return i ? parseInt(i[1], 10) : 0;
385
+ }
386
+ function re(e, t, n) {
387
+ n([
388
+ "issue",
389
+ "close",
390
+ String(t),
391
+ "--repo",
392
+ e
393
+ ]);
394
+ }
395
+ function ie(e, t, n) {
396
+ let r = n([
397
+ "pr",
398
+ "list",
399
+ "--repo",
400
+ e,
401
+ "--search",
402
+ t,
403
+ "--state",
404
+ "merged",
405
+ "--json",
406
+ "number,title",
407
+ "--limit",
408
+ "5"
409
+ ]);
410
+ return JSON.parse(r || "[]");
411
+ }
412
+ function ae(e, t, n, r) {
413
+ r([
414
+ "issue",
415
+ "comment",
416
+ String(t),
417
+ "--repo",
418
+ e,
419
+ "--body",
420
+ n
421
+ ]);
422
+ }
423
+ function oe(e) {
424
+ for (let t of e) {
425
+ let e = R[t];
426
+ if (e) return e;
427
+ }
428
+ return "created";
429
+ }
430
+ function Q(e, n) {
431
+ t(s(e), { recursive: !0 }), a(e, n);
432
+ }
433
+ function $(e, t) {
434
+ let n = t?.runGh ?? q, r = _(e);
435
+ return r.storage === "local" ? se(e, r, n) : le(e, r, n);
436
+ }
437
+ function se(e, t, n) {
438
+ let r = [], i = J(t, n), a = K(e, t);
439
+ if (a.length === 0) return b(e, { storage: p }), r.push("No artifacts found — nothing to migrate."), r.push(`✓ Storage switched to "${p}".`), r;
440
+ let o = t.github.labels?.prd ?? "tk:prd", s = t.github.labels?.plan ?? "tk:plan";
441
+ Z(i, [
442
+ o,
443
+ s,
444
+ ...[...new Set(a.map((e) => V(e.metadata.status ?? "created")))]
445
+ ], n);
446
+ let c = Y(i, o, n), l = Y(i, s, n);
447
+ for (let e of a) {
448
+ let t = e.type === "prd" ? o : s, a = e.type === "prd" ? c : l;
449
+ if (X(e.slug, a)) {
450
+ r.push(`⚠ skip ${e.type} "${e.slug}" — already exists on GitHub`);
451
+ continue;
452
+ }
453
+ let u = V(e.metadata.status ?? "created"), d = `${B(e.metadata)}\n\n${e.body.replace(/^\n/, "")}`, f = ne(i, {
454
+ title: `[${t}] ${e.slug}: ${e.title}`,
455
+ body: d,
456
+ labels: [t, u]
457
+ }, n);
458
+ e.archived ? (re(i, f, n), ce(i, e.slug, f, n), r.push(`✓ ${e.type} "${e.slug}" → issue #${f} (closed)`)) : r.push(`✓ ${e.type} "${e.slug}" → issue #${f}`);
459
+ }
460
+ return b(e, { storage: p }), r.push(`✓ Storage switched to "${p}".`), r;
461
+ }
462
+ function ce(e, t, n, r) {
463
+ let i = ie(e, t, r);
464
+ i.length !== 0 && ae(e, n, `Linked PR: ${i.map((e) => `#${e.number}`).join(", ")}`, r);
465
+ }
466
+ function le(t, n, r) {
467
+ let i = [], a = J(n, r), o = n.github.labels?.prd ?? "tk:prd", s = n.github.labels?.plan ?? "tk:plan", l = Y(a, o, r), u = Y(a, s, r), d = [...l.map((e) => ({
468
+ ...e,
469
+ type: "prd"
470
+ })), ...u.map((e) => ({
471
+ ...e,
472
+ type: "plan"
473
+ }))];
474
+ if (d.length === 0) return b(t, { storage: f }), i.push("No GitHub issues found — nothing to migrate."), i.push(`✓ Storage switched to "${f}".`), i;
475
+ for (let r of d) {
476
+ let a = W(r.title);
477
+ if (!a) continue;
478
+ let o = r.labels.map((e) => e.name), s = o.includes("tk:done") && r.state === "CLOSED", { metadata: l, body: u } = H(r.body ?? ""), d = l.status ?? oe(o);
479
+ if (s) {
480
+ let o = c(t, n.paths.archives, a), s = c(o, "prd.md"), f = c(o, "plan.md"), p = r.type === "prd" ? s : f;
481
+ if (e(p)) {
482
+ i.push(`⚠ skip ${r.type} "${a}" — local file already exists`);
483
+ continue;
484
+ }
485
+ r.type === "prd" ? Q(p, `${U({
486
+ ...l,
487
+ status: d
488
+ })}\n${u}`) : Q(p, u), i.push(`✓ ${r.type} "${a}" → ${p} (archived)`);
489
+ } else {
490
+ let o = c(t, r.type === "prd" ? n.paths.prds : n.paths.plans, `${a}.md`);
491
+ if (e(o)) {
492
+ i.push(`⚠ skip ${r.type} "${a}" — local file already exists`);
493
+ continue;
494
+ }
495
+ r.type === "prd" ? Q(o, `${U({
496
+ ...l,
497
+ status: d
498
+ })}\n${u}`) : Q(o, u), i.push(`✓ ${r.type} "${a}" → ${o}`);
499
+ }
500
+ }
501
+ return b(t, { storage: f }), i.push(`✓ Storage switched to "${f}".`), i;
502
+ }
503
+ //#endregion
504
+ //#region src/commands/uninstall.ts
505
+ function ue(t) {
506
+ if (!C.some((n) => e(c(t, ".claude", "skills", n)))) throw Error("TracerKit not initialized — nothing to uninstall");
507
+ let n = [];
508
+ for (let r of C) {
509
+ let a = c(t, ".claude", "skills", r);
510
+ e(a) && (i(a, {
511
+ recursive: !0,
512
+ force: !0
513
+ }), n.push(`✗ .claude/skills/${r}/ removed`));
514
+ }
515
+ return n;
516
+ }
517
+ //#endregion
518
+ 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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracerkit",
3
- "version": "1.13.1",
3
+ "version": "1.15.0",
4
4
  "description": "Spec-driven workflow for Claude Code: replace ad-hoc prompts with PRD → plan → verify.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -1,205 +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 = "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 };