tracerkit 1.7.1 → 1.8.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/dist/bin.js CHANGED
@@ -1,16 +1,28 @@
1
1
  #!/usr/bin/env node
2
- import { n as e, o as t, r as n, t as r } from "./uninstall-Dri3LFXP.js";
3
- import { existsSync as i, mkdirSync as a, readFileSync as o, rmSync as s, unlinkSync as c, writeFileSync as l } from "node:fs";
4
- import { dirname as u, join as d, resolve as f } from "node:path";
5
- import { fileURLToPath as p } from "node:url";
6
- import { homedir as m } from "node:os";
2
+ import { i as e, n as t, r as n, s as r, t as i } from "./uninstall-DO2YpTSz.js";
3
+ import { existsSync as a, mkdirSync as o, readFileSync as s, readdirSync as c, rmSync as l, unlinkSync as u, writeFileSync as d } from "node:fs";
4
+ import { dirname as f, join as p, resolve as m } from "node:path";
5
+ import { fileURLToPath as h } from "node:url";
6
+ import { homedir as g } from "node:os";
7
7
  //#region src/frontmatter.ts
8
- var h = /^---\n([\s\S]*?)\n---(?:\n|$)/;
9
- function g(e) {
8
+ var _ = /^---\n([\s\S]*?)\n---(?:\n|$)/;
9
+ function v(e) {
10
10
  return e.replace(/\r\n/g, "\n");
11
11
  }
12
- function _(e, t, n) {
13
- let r = g(e), i = r.match(h);
12
+ function y(e) {
13
+ let t = v(e).match(_);
14
+ if (!t) return {};
15
+ let n = {};
16
+ for (let e of t[1].split("\n")) {
17
+ let t = e.indexOf(":");
18
+ if (t === -1) continue;
19
+ let r = e.slice(0, t).trim(), i = e.slice(t + 1).trim();
20
+ r && (n[r] = i);
21
+ }
22
+ return n;
23
+ }
24
+ function b(e, t, n) {
25
+ let r = v(e), i = r.match(_);
14
26
  if (!i) return `---\n${t}: ${n}\n---\n${r}`;
15
27
  let a = i[1].split("\n"), o = RegExp(`^${t}\\s*:`), s = a.findIndex((e) => o.test(e));
16
28
  s === -1 ? a.push(`${t}: ${n}`) : a[s] = `${t}: ${n}`;
@@ -19,21 +31,21 @@ function _(e, t, n) {
19
31
  }
20
32
  //#endregion
21
33
  //#region src/commands/archive.ts
22
- function v(e, n) {
23
- let r = t(e), u = d(e, r.paths.prds, `${n}.md`), f = d(e, r.paths.plans, `${n}.md`), p = d(e, r.paths.archives, n), m = i(u);
24
- if (!i(f)) throw Error(`Plan "${n}" not found at ${f}`);
25
- if (i(p)) throw Error(`Archive "${n}" already exists at ${p}`);
26
- a(p, { recursive: !0 });
34
+ function x(e, t) {
35
+ let n = r(e), i = p(e, n.paths.prds, `${t}.md`), c = p(e, n.paths.plans, `${t}.md`), f = p(e, n.paths.archives, t), m = a(i);
36
+ if (!a(c)) throw Error(`Plan "${t}" not found at ${c}`);
37
+ if (a(f)) throw Error(`Archive "${t}" already exists at ${f}`);
38
+ o(f, { recursive: !0 });
27
39
  try {
28
- let e = (/* @__PURE__ */ new Date()).toISOString(), t = [];
40
+ let e = (/* @__PURE__ */ new Date()).toISOString(), r = [];
29
41
  if (m) {
30
- let t = o(u, "utf8");
31
- t = _(t, "status", "done"), t = _(t, "completed", e), l(d(p, "prd.md"), t);
32
- } else t.push(`Warning: PRD "${n}" missing, archiving plan only`);
33
- let i = o(f, "utf8");
34
- return i += `\n## Archived\n\nArchived on ${e.slice(0, 10)}.\n`, l(d(p, "plan.md"), i), m && c(u), c(f), t.push(`Archived "${n}" to ${r.paths.archives}/${n}/`), m && t.push(` prd.md — status: done, completed: ${e}`), t.push(" plan.md — archived block appended"), t;
42
+ let t = s(i, "utf8");
43
+ t = b(t, "status", "done"), t = b(t, "completed", e), d(p(f, "prd.md"), t);
44
+ } else r.push(`Warning: PRD "${t}" missing, archiving plan only`);
45
+ let a = s(c, "utf8");
46
+ return a += `\n## Archived\n\nArchived on ${e.slice(0, 10)}.\n`, d(p(f, "plan.md"), a), m && u(i), u(c), r.push(`Archived "${t}" to ${n.paths.archives}/${t}/`), m && r.push(` prd.md — status: done, completed: ${e}`), r.push(" plan.md — archived block appended"), r;
35
47
  } catch (e) {
36
- throw s(p, {
48
+ throw l(f, {
37
49
  recursive: !0,
38
50
  force: !0
39
51
  }), e;
@@ -41,11 +53,11 @@ function v(e, n) {
41
53
  }
42
54
  //#endregion
43
55
  //#region src/plan.ts
44
- var y = /^## (Phase \d+\s*.*)$/, b = /^- \[x\] /i, x = /^- \[ \] /;
45
- function S(e) {
56
+ var S = /^## (Phase \d+\s*.*)$/, C = /^- \[x\] /i, w = /^- \[ \] /;
57
+ function T(e) {
46
58
  let t = e.replace(/\r\n/g, "\n").split("\n"), n = [], r = null;
47
59
  for (let e of t) {
48
- let t = e.trimStart(), i = t.match(y);
60
+ let t = e.trimStart(), i = t.match(S);
49
61
  if (t.startsWith("## ")) {
50
62
  i ? (r = {
51
63
  title: i[1].trim(),
@@ -54,32 +66,80 @@ function S(e) {
54
66
  }, n.push(r)) : r = null;
55
67
  continue;
56
68
  }
57
- r && (b.test(t) ? (r.checked++, r.total++) : x.test(t) && r.total++);
69
+ r && (C.test(t) ? (r.checked++, r.total++) : w.test(t) && r.total++);
58
70
  }
59
71
  return { phases: n };
60
72
  }
61
73
  //#endregion
74
+ //#region src/commands/brief.ts
75
+ var E = /^- \[ \] (.+)/;
76
+ function D(e, t) {
77
+ let n = new Date(e);
78
+ if (isNaN(n.getTime())) return "";
79
+ let r = Math.floor((t.getTime() - n.getTime()) / 864e5);
80
+ return r < 0 ? "" : r < 7 ? `${r}d` : r < 30 ? `${Math.floor(r / 7)}w` : `${Math.floor(r / 30)}mo`;
81
+ }
82
+ function O(e) {
83
+ for (let t of e.split("\n")) {
84
+ let e = t.trimStart().match(E);
85
+ if (e) return e[1].replace(/\s*\[.*?\]\s*$/, "").trim();
86
+ }
87
+ return "—";
88
+ }
89
+ function k(e, t = /* @__PURE__ */ new Date()) {
90
+ let n = r(e), i = p(e, n.paths.prds);
91
+ if (!a(i)) return ["No features found — run `/tk:prd` to start one."];
92
+ let o = c(i).filter((e) => e.endsWith(".md")).sort();
93
+ if (o.length === 0) return ["No features found — run `/tk:prd` to start one."];
94
+ let l = p(e, n.paths.plans), u = [];
95
+ for (let e of o) {
96
+ let t = e.replace(/\.md$/, ""), n = y(s(p(i, e), "utf8"));
97
+ if (n.status === "done") continue;
98
+ let r = n.status || "unknown", o = n.created || "", c = o && !isNaN(new Date(o).getTime()) ? o : "", d = "—", f = "—", m = p(l, `${t}.md`);
99
+ if (a(m)) {
100
+ let e = s(m, "utf8"), { phases: t } = T(e), n = t.reduce((e, t) => e + t.checked, 0), r = t.reduce((e, t) => e + t.total, 0);
101
+ r > 0 && (d = `${n}/${r}`), f = O(e);
102
+ }
103
+ u.push({
104
+ slug: t,
105
+ status: r,
106
+ created: c,
107
+ progress: d,
108
+ next: f
109
+ });
110
+ }
111
+ if (u.length === 0) return ["No features found — run `/tk:prd` to start one."];
112
+ u.sort((e, t) => e.created && t.created ? new Date(e.created).getTime() - new Date(t.created).getTime() : e.created ? -1 : t.created ? 1 : 0);
113
+ let d = u.map((e) => {
114
+ let n = e.created ? D(e.created, t) : "";
115
+ return `| ${e.slug} | ${e.status} | ${n} | ${e.progress} | ${e.next} |`;
116
+ }), f = u.filter((e) => e.status === "in_progress"), m = f.length === 1 ? f[0] : f[0] ?? u[0];
117
+ return [
118
+ "| Feature | Status | Age | Progress | Next |",
119
+ "|---------|--------|-----|----------|------|",
120
+ ...d,
121
+ "",
122
+ `**Focus → ${m.slug}**`
123
+ ];
124
+ }
125
+ //#endregion
62
126
  //#region src/commands/progress.ts
63
- function C(e, n) {
64
- let r = d(e, t(e).paths.plans, `${n}.md`);
65
- if (!i(r)) throw Error(`Plan "${n}" not found at ${r}`);
66
- let { phases: a } = S(o(r, "utf8"));
67
- if (a.length === 0) return ["No phases found in plan.", "Total: 0/0"];
68
- let s = [], c = 0, l = 0;
69
- for (let e of a) c += e.checked, l += e.total, s.push(` ${e.title}: ${e.checked}/${e.total}`);
70
- return s.push(""), s.push(`Total: ${c}/${l}`), s;
127
+ function A(e, t) {
128
+ let n = p(e, r(e).paths.plans, `${t}.md`);
129
+ if (!a(n)) throw Error(`Plan "${t}" not found at ${n}`);
130
+ let { phases: i } = T(s(n, "utf8"));
131
+ if (i.length === 0) return ["No phases found in plan.", "Total: 0/0"];
132
+ let o = [], c = 0, l = 0;
133
+ for (let e of i) c += e.checked, l += e.total, o.push(` ${e.title}: ${e.checked}/${e.total}`);
134
+ return o.push(""), o.push(`Total: ${c}/${l}`), o;
71
135
  }
72
136
  //#endregion
73
137
  //#region src/cli.ts
74
- var { version: w } = JSON.parse(o(f(u(p(import.meta.url)), "..", "package.json"), "utf8")), T = [
138
+ var { version: j } = JSON.parse(s(m(f(h(import.meta.url)), "..", "package.json"), "utf8")), M = Math.max(...e.map((e) => `${e.name} ${e.args}`.length)), N = [
75
139
  "Usage: tracerkit <command> [path]",
76
140
  "",
77
141
  "Commands:",
78
- " init [path] Install skills to ~/.claude/skills/ (or [path] if given)",
79
- " update [path] Refresh unchanged files from latest version, skip modified",
80
- " uninstall [path] Remove TracerKit skill directories, keep .tracerkit/ artifacts",
81
- " progress <slug> Show per-phase checkbox progress for a plan",
82
- " archive <slug> Archive a completed feature (PRD + plan)",
142
+ ...e.map((e) => ` ${`${e.name} ${e.args}`.padEnd(M + 2)}${e.desc}`),
83
143
  "",
84
144
  "Options:",
85
145
  " --force Overwrite modified files during update",
@@ -88,42 +148,43 @@ var { version: w } = JSON.parse(o(f(u(p(import.meta.url)), "..", "package.json")
88
148
  "",
89
149
  "All commands default to the home directory when no path is given."
90
150
  ];
91
- function E(e, t = m()) {
151
+ function P(e, t = g()) {
92
152
  let n = e.find((e) => !e.startsWith("-"));
93
- return n ? f(n) : t;
153
+ return n ? m(n) : t;
94
154
  }
95
- function D(e, t) {
155
+ function F(e, t) {
96
156
  let n = e.findIndex((e) => !e.startsWith("-"));
97
157
  if (n === -1) return [
98
158
  "Error: missing <slug> argument",
99
159
  "",
100
- ...T
160
+ ...N
101
161
  ];
102
162
  let r = e[n], i = e.filter((e, t) => t !== n);
103
163
  try {
104
- return t(E(i, process.cwd()), r);
164
+ return t(P(i, process.cwd()), r);
105
165
  } catch (e) {
106
166
  return [`Error: ${e instanceof Error ? e.message : String(e)}`];
107
167
  }
108
168
  }
109
- function O(t) {
110
- if (t.includes("--help") || t.includes("-h")) return T;
111
- if (t.includes("--version") || t.includes("-v")) return [`tracerkit/${w}`];
112
- let i = t[0], a = t.slice(1);
113
- switch (i) {
114
- case "init": return n(E(a));
169
+ function I(e) {
170
+ if (e.includes("--help") || e.includes("-h")) return N;
171
+ if (e.includes("--version") || e.includes("-v")) return [`tracerkit/${j}`];
172
+ let r = e[0], a = e.slice(1);
173
+ switch (r) {
174
+ case "brief": return k(P(a, process.cwd()));
175
+ case "init": return n(P(a));
115
176
  case "update": {
116
- let t = a.includes("--force"), n = e(E(a.filter((e) => e !== "--force")), { force: t });
177
+ let e = a.includes("--force"), n = t(P(a.filter((e) => e !== "--force")), { force: e });
117
178
  return n.push("", "Updated to the latest TracerKit."), n.push("If using Claude Code, restart your session to load changes."), n;
118
179
  }
119
- case "uninstall": return r(E(a));
120
- case "progress": return D(a, C);
121
- case "archive": return D(a, v);
122
- default: return T;
180
+ case "uninstall": return i(P(a));
181
+ case "progress": return F(a, A);
182
+ case "archive": return F(a, x);
183
+ default: return N;
123
184
  }
124
185
  }
125
186
  //#endregion
126
187
  //#region src/bin.ts
127
- var k = O(process.argv.slice(2));
128
- for (let e of k) console.log(e);
188
+ var L = I(process.argv.slice(2));
189
+ for (let e of L) console.log(e);
129
190
  //#endregion
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { a as e, i as t, n, r, t as i } from "./uninstall-Dri3LFXP.js";
2
- export { t as DEPRECATED_SKILLS, e as SKILL_NAMES, r as init, i as uninstall, n as update };
1
+ import { a as e, i as t, n, o as r, r as i, t as a } from "./uninstall-DO2YpTSz.js";
2
+ export { t as COMMANDS, e as DEPRECATED_SKILLS, r as SKILL_NAMES, i as init, a as uninstall, n as update };
@@ -25,40 +25,72 @@ function d(t) {
25
25
  } };
26
26
  }
27
27
  var f = [
28
+ "tk:brief",
28
29
  "tk:prd",
29
30
  "tk:plan",
30
31
  "tk:check"
31
- ], p = ["tk:verify"], m = s(o(l(import.meta.url)), "..", "templates");
32
- function h(e, t = "") {
32
+ ], p = ["tk:verify"], m = [
33
+ {
34
+ name: "init",
35
+ args: "[path]",
36
+ desc: "Install skills to ~/.claude/skills/ (or [path] if given)"
37
+ },
38
+ {
39
+ name: "update",
40
+ args: "[path]",
41
+ desc: "Refresh unchanged files from latest version, skip modified"
42
+ },
43
+ {
44
+ name: "uninstall",
45
+ args: "[path]",
46
+ desc: "Remove TracerKit skill directories, keep .tracerkit/ artifacts"
47
+ },
48
+ {
49
+ name: "brief",
50
+ args: "[path]",
51
+ desc: "Show active features, progress, and suggested focus"
52
+ },
53
+ {
54
+ name: "progress",
55
+ args: "<slug>",
56
+ desc: "Show per-phase checkbox progress for a plan"
57
+ },
58
+ {
59
+ name: "archive",
60
+ args: "<slug>",
61
+ desc: "Archive a completed feature (PRD + plan)"
62
+ }
63
+ ], h = s(o(l(import.meta.url)), "..", "templates");
64
+ function g(e, t = "") {
33
65
  let n = r(e, { withFileTypes: !0 }), i = [];
34
66
  for (let r of n) {
35
67
  let n = t ? `${t}/${r.name}` : r.name;
36
- r.isDirectory() ? i.push(...h(s(e, r.name), n)) : i.push(n);
68
+ r.isDirectory() ? i.push(...g(s(e, r.name), n)) : i.push(n);
37
69
  }
38
70
  return i.sort();
39
71
  }
40
- function g(e, t) {
72
+ function _(e, t) {
41
73
  return e.replaceAll("{{paths.prds}}", t.paths.prds).replaceAll("{{paths.plans}}", t.paths.plans).replaceAll("{{paths.archives}}", t.paths.archives);
42
74
  }
43
- function _(e, r, i) {
44
- let c = i ?? h(m);
75
+ function v(e, r, i) {
76
+ let c = i ?? g(h);
45
77
  for (let i of c) {
46
- let c = s(m, i), l = s(e, i);
47
- t(o(l), { recursive: !0 }), a(l, g(n(c, "utf8"), r));
78
+ let c = s(h, i), l = s(e, i);
79
+ t(o(l), { recursive: !0 }), a(l, _(n(c, "utf8"), r));
48
80
  }
49
81
  return { copied: c };
50
82
  }
51
- function v(e) {
83
+ function y(e) {
52
84
  return c("sha256").update(e).digest("hex");
53
85
  }
54
- function y(t, r) {
55
- let i = h(m), a = [], o = [], c = [];
86
+ function b(t, r) {
87
+ let i = g(h), a = [], o = [], c = [];
56
88
  for (let l of i) {
57
89
  let i = s(t, l);
58
90
  if (!e(i)) c.push(l);
59
91
  else {
60
- let e = g(n(s(m, l), "utf8"), r);
61
- v(Buffer.from(e)) === v(n(i)) ? a.push(l) : o.push(l);
92
+ let e = _(n(s(h, l), "utf8"), r);
93
+ y(Buffer.from(e)) === y(n(i)) ? a.push(l) : o.push(l);
62
94
  }
63
95
  }
64
96
  return {
@@ -69,16 +101,16 @@ function y(t, r) {
69
101
  }
70
102
  //#endregion
71
103
  //#region src/commands/init.ts
72
- function b(t) {
104
+ function x(t) {
73
105
  for (let n of f) if (e(s(t, ".claude", "skills", n))) throw Error(`.claude/skills/${n}/ already exists — aborting`);
74
- let { copied: n } = _(t, d(t));
106
+ let { copied: n } = v(t, d(t));
75
107
  return n.map((e) => `✓ ${e}`);
76
108
  }
77
109
  //#endregion
78
110
  //#region src/commands/update.ts
79
- function x(t, n) {
111
+ function S(t, n) {
80
112
  if (!f.some((n) => e(s(t, ".claude", "skills", n)))) throw Error("TracerKit not initialized — run `tracerkit init` first");
81
- let r = d(t), { unchanged: a, modified: o, missing: c } = y(t, r), l = [];
113
+ let r = d(t), { unchanged: a, modified: o, missing: c } = b(t, r), l = [];
82
114
  for (let n of p) {
83
115
  let r = s(t, ".claude", "skills", n);
84
116
  e(r) && (i(r, {
@@ -92,7 +124,7 @@ function x(t, n) {
92
124
  ...u ? o : []
93
125
  ];
94
126
  if (m.length > 0) {
95
- _(t, r, m);
127
+ v(t, r, m);
96
128
  for (let e of a) l.push(`✓ ${e}`);
97
129
  for (let e of c) l.push(`✓ ${e} (added)`);
98
130
  if (u) for (let e of o) l.push(`✓ ${e} (replaced)`);
@@ -105,7 +137,7 @@ function x(t, n) {
105
137
  }
106
138
  //#endregion
107
139
  //#region src/commands/uninstall.ts
108
- function S(t) {
140
+ function C(t) {
109
141
  if (!f.some((n) => e(s(t, ".claude", "skills", n)))) throw Error("TracerKit not initialized — nothing to uninstall");
110
142
  let n = [];
111
143
  for (let r of f) {
@@ -118,4 +150,4 @@ function S(t) {
118
150
  return n;
119
151
  }
120
152
  //#endregion
121
- export { f as a, p as i, x as n, d as o, b as r, S as t };
153
+ export { p as a, m as i, S as n, f as o, x as r, d as s, C as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tracerkit",
3
- "version": "1.7.1",
3
+ "version": "1.8.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": {
@@ -0,0 +1,40 @@
1
+ ---
2
+ description: Session briefing — shows active features, progress, and suggested focus. Use at the start of a session to orient.
3
+ ---
4
+
5
+ # Session Briefing
6
+
7
+ Get a quick overview of all active features, their progress, and what to focus on next.
8
+
9
+ ## Pre-loaded context
10
+
11
+ - Briefing: !`npx tracerkit brief 2>/dev/null || echo "no {{paths.prds}}/ directory found"`
12
+
13
+ ## Workflow
14
+
15
+ ### 1. Present the briefing
16
+
17
+ Display the output above as-is. The table includes:
18
+
19
+ - **Feature**: slug (PRD filename without `.md`)
20
+ - **Status**: from PRD frontmatter (`created`, `in_progress`) — `unknown` if no frontmatter
21
+ - **Age**: time since `created` date
22
+ - **Progress**: checked/total from plan checkboxes, or `—` if no plan
23
+ - **Next**: first unchecked item from the plan, or `—`
24
+
25
+ Features with `status: done` are excluded (already archived).
26
+
27
+ ### 2. Focus recommendation
28
+
29
+ The **Focus** line at the bottom suggests which feature to work on:
30
+
31
+ - If exactly 1 feature is `in_progress`, it's auto-selected
32
+ - Otherwise, the oldest feature by `created` date is selected
33
+
34
+ ### 3. Offer next steps
35
+
36
+ Ask the user what they'd like to do:
37
+
38
+ - Continue the focused feature (read its plan at `{{paths.plans}}/<slug>.md`)
39
+ - Start a new feature with `/tk:prd`
40
+ - Check progress on a feature with `/tk:check <slug>`
@@ -47,7 +47,7 @@ Before launching a subagent, check whether the primary module file(s) from Phase
47
47
 
48
48
  ### 3b. Launch read-only review
49
49
 
50
- Use a **read-only subagent** (no file writes, no edits) to:
50
+ Use a **general-purpose subagent** (not `code-review` — that agent is for PR reviews). The subagent must be **read-only** (no file writes, no edits). It should:
51
51
 
52
52
  1. Read every section of the plan — architectural decisions, each phase, done-when checkboxes
53
53
  2. For each phase, check every `- [ ]` / `- [x]` item against the codebase