tracerkit 1.7.0 → 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/README.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
Replace ad-hoc AI prompts with a repeatable spec-driven workflow: from idea to verified, archived code.
|
|
12
12
|
|
|
13
|
-
Named after the tracer-bullet technique from _The Pragmatic Programmer_
|
|
13
|
+
Named after the tracer-bullet technique from _The Pragmatic Programmer_: **Tracer** + **Kit**.
|
|
14
14
|
|
|
15
15
|
**Zero runtime dependencies.** Pure Markdown skills, no build step.
|
|
16
16
|
|
|
@@ -18,7 +18,7 @@ Named after the tracer-bullet technique from _The Pragmatic Programmer_ — **Tr
|
|
|
18
18
|
|
|
19
19
|
## Why TracerKit?
|
|
20
20
|
|
|
21
|
-
Without specs, every AI session starts from scratch
|
|
21
|
+
Without specs, every AI session starts from scratch. Vague prompts, duplicated context, no way to confirm "done." Most planning tools produce flat task lists where nothing works until everything is done.
|
|
22
22
|
|
|
23
23
|
TracerKit takes a different approach: **tracer-bullet vertical slices**. Each phase cuts through every layer (schema → service → API → UI → tests), so every phase is demoable on its own. Integration problems surface early, context stays focused, and AI assistants get small, well-scoped phases instead of sprawling layers.
|
|
24
24
|
|
|
@@ -36,17 +36,19 @@ Skills are installed globally to `~/.claude/skills/`, available in every project
|
|
|
36
36
|
|
|
37
37
|
```
|
|
38
38
|
You: /tk:prd add dark mode support
|
|
39
|
-
AI:
|
|
39
|
+
AI: Written .tracerkit/prds/dark-mode-support.md
|
|
40
|
+
Run `/tk:plan dark-mode-support` next?
|
|
40
41
|
|
|
41
42
|
You: /tk:plan dark-mode-support
|
|
42
|
-
AI:
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
AI: Phase 1 — Theme visible end-to-end
|
|
44
|
+
Phase 2 — User can toggle and persist preference
|
|
45
|
+
Written .tracerkit/plans/dark-mode-support.md
|
|
46
|
+
Run `/tk:check dark-mode-support` when ready?
|
|
45
47
|
|
|
46
|
-
You: # implement each phase...
|
|
48
|
+
You: # open the plan, implement each phase, write tests...
|
|
47
49
|
|
|
48
50
|
You: /tk:check dark-mode-support
|
|
49
|
-
AI:
|
|
51
|
+
AI: Status: done | Total: 5/5
|
|
50
52
|
Archived to .tracerkit/archives/dark-mode-support/
|
|
51
53
|
```
|
|
52
54
|
|
|
@@ -97,7 +99,7 @@ Without arguments, shows a feature dashboard with status and progress before ask
|
|
|
97
99
|
| [CLI Reference](docs/cli-reference.md) | Browse all CLI commands and flags |
|
|
98
100
|
| [Configuration](docs/configuration.md) | Configure custom artifact paths via `config.json` |
|
|
99
101
|
| [Metadata Lifecycle](docs/metadata-lifecycle.md) | Understand YAML frontmatter states and transitions |
|
|
100
|
-
| [
|
|
102
|
+
| [Comparison](docs/comparison.md) | Compare TracerKit to Spec Kit, Kiro, and OpenSpec |
|
|
101
103
|
|
|
102
104
|
## Contributing
|
|
103
105
|
|
package/dist/bin.js
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { existsSync as
|
|
4
|
-
import { dirname as
|
|
5
|
-
import { fileURLToPath as
|
|
6
|
-
import { homedir as
|
|
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
|
|
9
|
-
function
|
|
8
|
+
var _ = /^---\n([\s\S]*?)\n---(?:\n|$)/;
|
|
9
|
+
function v(e) {
|
|
10
10
|
return e.replace(/\r\n/g, "\n");
|
|
11
11
|
}
|
|
12
|
-
function
|
|
13
|
-
let
|
|
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
|
|
23
|
-
let
|
|
24
|
-
if (!
|
|
25
|
-
if (
|
|
26
|
-
|
|
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(),
|
|
40
|
+
let e = (/* @__PURE__ */ new Date()).toISOString(), r = [];
|
|
29
41
|
if (m) {
|
|
30
|
-
let t =
|
|
31
|
-
t =
|
|
32
|
-
} else
|
|
33
|
-
let
|
|
34
|
-
return
|
|
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
|
|
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
|
|
45
|
-
function
|
|
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(
|
|
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 && (
|
|
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
|
|
64
|
-
let
|
|
65
|
-
if (!
|
|
66
|
-
let { phases:
|
|
67
|
-
if (
|
|
68
|
-
let
|
|
69
|
-
for (let e of
|
|
70
|
-
return
|
|
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:
|
|
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
|
-
|
|
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
|
|
151
|
+
function P(e, t = g()) {
|
|
92
152
|
let n = e.find((e) => !e.startsWith("-"));
|
|
93
|
-
return n ?
|
|
153
|
+
return n ? m(n) : t;
|
|
94
154
|
}
|
|
95
|
-
function
|
|
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
|
-
...
|
|
160
|
+
...N
|
|
101
161
|
];
|
|
102
162
|
let r = e[n], i = e.filter((e, t) => t !== n);
|
|
103
163
|
try {
|
|
104
|
-
return t(
|
|
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
|
|
110
|
-
if (
|
|
111
|
-
if (
|
|
112
|
-
let
|
|
113
|
-
switch (
|
|
114
|
-
case "
|
|
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
|
|
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
|
|
120
|
-
case "progress": return
|
|
121
|
-
case "archive": return
|
|
122
|
-
default: return
|
|
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
|
|
128
|
-
for (let e of
|
|
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,
|
|
2
|
-
export { t as
|
|
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 =
|
|
32
|
-
|
|
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(...
|
|
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
|
|
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
|
|
44
|
-
let c = i ?? h
|
|
75
|
+
function v(e, r, i) {
|
|
76
|
+
let c = i ?? g(h);
|
|
45
77
|
for (let i of c) {
|
|
46
|
-
let c = s(
|
|
47
|
-
t(o(l), { recursive: !0 }), a(l,
|
|
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
|
|
83
|
+
function y(e) {
|
|
52
84
|
return c("sha256").update(e).digest("hex");
|
|
53
85
|
}
|
|
54
|
-
function
|
|
55
|
-
let i = h
|
|
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 =
|
|
61
|
-
|
|
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
|
|
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 } =
|
|
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
|
|
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 } =
|
|
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
|
-
|
|
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
|
|
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 {
|
|
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
|
@@ -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 **
|
|
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
|