work-kit-cli 0.4.0 → 0.4.1
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.
|
@@ -51,49 +51,39 @@ function parseStateMd(stateMd: string): RawEntry[] {
|
|
|
51
51
|
const out: RawEntry[] = [];
|
|
52
52
|
if (!stateMd) return out;
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
// Only `## Observations` is auto-harvested. `## Decisions` and `## Deviations`
|
|
55
|
+
// are agent scratch space during normal phase work — they routinely contain
|
|
56
|
+
// test plans, acceptance-criteria checklists, and self-review dumps. Auto-
|
|
57
|
+
// routing them floods workflow.md with noise. Agents opt into harvesting by
|
|
58
|
+
// writing typed bullets (`- [lesson|convention|risk|workflow] text`) under
|
|
59
|
+
// `## Observations`.
|
|
60
|
+
let inObservations = false;
|
|
55
61
|
|
|
56
62
|
for (const rawLine of stateMd.split("\n")) {
|
|
57
63
|
const trimmed = rawLine.trim();
|
|
58
64
|
|
|
59
65
|
if (trimmed.startsWith("## ")) {
|
|
60
|
-
|
|
61
|
-
if (header === "observations") section = "observations";
|
|
62
|
-
else if (header === "decisions") section = "decisions";
|
|
63
|
-
else if (header === "deviations") section = "deviations";
|
|
64
|
-
else section = null;
|
|
66
|
+
inObservations = trimmed.slice(3).trim().toLowerCase() === "observations";
|
|
65
67
|
continue;
|
|
66
68
|
}
|
|
67
69
|
|
|
68
|
-
if (
|
|
70
|
+
if (!inObservations) continue;
|
|
69
71
|
if (!trimmed.startsWith("-") || trimmed.startsWith("<!--")) continue;
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const text = m[3].trim();
|
|
78
|
-
if (text.length === 0) continue;
|
|
79
|
-
const entry: RawEntry = { type: tag, text, source: "auto-state-md" };
|
|
80
|
-
if (phaseStep) {
|
|
81
|
-
const [p, s] = phaseStep.split("/");
|
|
82
|
-
entry.phase = p;
|
|
83
|
-
entry.step = s;
|
|
84
|
-
}
|
|
85
|
-
out.push(entry);
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const text = trimmed.replace(/^-\s*/, "").trim();
|
|
73
|
+
const m = trimmed.match(OBSERVATION_RE);
|
|
74
|
+
if (!m) continue;
|
|
75
|
+
const tag = m[1].toLowerCase();
|
|
76
|
+
if (!isKnowledgeType(tag)) continue;
|
|
77
|
+
const phaseStep = m[2];
|
|
78
|
+
const text = m[3].trim();
|
|
90
79
|
if (text.length === 0) continue;
|
|
91
|
-
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
80
|
+
const entry: RawEntry = { type: tag, text, source: "auto-state-md" };
|
|
81
|
+
if (phaseStep) {
|
|
82
|
+
const [p, s] = phaseStep.split("/");
|
|
83
|
+
entry.phase = p;
|
|
84
|
+
entry.step = s;
|
|
96
85
|
}
|
|
86
|
+
out.push(entry);
|
|
97
87
|
}
|
|
98
88
|
|
|
99
89
|
return out;
|
|
@@ -176,6 +176,33 @@ describe("learnCommand", () => {
|
|
|
176
176
|
assert.ok(workflowMd.includes("e2e step needs server"));
|
|
177
177
|
});
|
|
178
178
|
|
|
179
|
+
it("ignores bullets under ## Decisions and ## Deviations", () => {
|
|
180
|
+
const tmp = makeTmpDir();
|
|
181
|
+
tmpDirs.push(tmp);
|
|
182
|
+
setupSession(tmp);
|
|
183
|
+
|
|
184
|
+
// Simulate an agent dumping test-plan noise into Decisions/Deviations.
|
|
185
|
+
// None of these should be harvested — only ## Observations is auto-routed.
|
|
186
|
+
const stateMdPath = path.join(tmp, ".work-kit", "state.md");
|
|
187
|
+
const original = fs.readFileSync(stateMdPath, "utf-8");
|
|
188
|
+
const injected = original
|
|
189
|
+
.replace(
|
|
190
|
+
"## Decisions\n<!-- Append here whenever you choose between real alternatives -->",
|
|
191
|
+
"## Decisions\n<!-- Append here whenever you choose between real alternatives -->\n- Picked Zod over Yup\n- Use Firestore onSnapshot\n- **Expected:** Badge renders X / Y"
|
|
192
|
+
)
|
|
193
|
+
.replace(
|
|
194
|
+
"## Deviations\n<!-- Append here whenever implementation diverges from the Blueprint -->",
|
|
195
|
+
"## Deviations\n<!-- Append here whenever implementation diverges from the Blueprint -->\n- Navigate to / and verify cards render\n- Check badge shows 0 / 0\n- **Flow 3:** Pass"
|
|
196
|
+
);
|
|
197
|
+
fs.writeFileSync(stateMdPath, injected);
|
|
198
|
+
|
|
199
|
+
const r = extractCommand({ worktreeRoot: tmp });
|
|
200
|
+
assert.equal(r.action, "extracted");
|
|
201
|
+
assert.equal(r.written, 0, "no bullets from Decisions/Deviations should be harvested");
|
|
202
|
+
assert.equal(r.byType.convention, 0);
|
|
203
|
+
assert.equal(r.byType.workflow, 0);
|
|
204
|
+
});
|
|
205
|
+
|
|
179
206
|
it("extract is idempotent (re-run produces only duplicates)", () => {
|
|
180
207
|
const tmp = makeTmpDir();
|
|
181
208
|
tmpDirs.push(tmp);
|
|
@@ -243,7 +243,23 @@ function installPlaywrightPackage(pm: PackageManager, projectDir: string): boole
|
|
|
243
243
|
pm === "yarn" ? ["add", "-D", "@playwright/test"] :
|
|
244
244
|
["install", "-D", "@playwright/test"];
|
|
245
245
|
console.error(` ${dim(`$ ${pm} ${args.join(" ")}`)}`);
|
|
246
|
-
|
|
246
|
+
if (runStreamed(pm, args, projectDir)) return true;
|
|
247
|
+
|
|
248
|
+
// The most common npm failure here is ERESOLVE — the user's project has
|
|
249
|
+
// a pre-existing peer-dep conflict that npm refuses to resolve. Retry with
|
|
250
|
+
// --legacy-peer-deps so Playwright still installs; the user's underlying
|
|
251
|
+
// conflict is left for them to fix separately.
|
|
252
|
+
if (pm === "npm") {
|
|
253
|
+
console.error(` ${yellow("!")} npm install failed (likely peer-dep conflict). Retrying with --legacy-peer-deps...`);
|
|
254
|
+
const fallbackArgs = [...args, "--legacy-peer-deps"];
|
|
255
|
+
console.error(` ${dim(`$ ${pm} ${fallbackArgs.join(" ")}`)}`);
|
|
256
|
+
if (runStreamed(pm, fallbackArgs, projectDir)) {
|
|
257
|
+
console.error(` ${dim("Note: installed with --legacy-peer-deps. Your project still has the original peer-dep conflict — fix it separately when convenient.")}`);
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return false;
|
|
247
263
|
}
|
|
248
264
|
|
|
249
265
|
function installPlaywrightBrowsers(projectDir: string): boolean {
|
package/package.json
CHANGED
|
@@ -26,7 +26,7 @@ The summary you write goes into `.work-kit/summary.md`; the CLI archives it into
|
|
|
26
26
|
4. **Run** `work-kit complete wrap-up/summary --outcome done`
|
|
27
27
|
|
|
28
28
|
### Step 2: knowledge
|
|
29
|
-
5. **Run `work-kit extract`** —
|
|
29
|
+
5. **Run `work-kit extract`** — routes typed `## Observations` bullets and tracker loopbacks/skipped/failed steps into `.work-kit-knowledge/` files. `## Decisions` and `## Deviations` are not auto-harvested (they're scratch space).
|
|
30
30
|
6. **Review the summary you just wrote** for subjective additions the parser would miss. For each, call `work-kit learn --type <lesson|convention|risk|workflow> --text "..."`.
|
|
31
31
|
7. **Run** `work-kit complete wrap-up/knowledge --outcome done`
|
|
32
32
|
|
|
@@ -15,12 +15,12 @@ After `wrap-up/summary`. By now you've just re-read the full `state.md` and dist
|
|
|
15
15
|
work-kit extract
|
|
16
16
|
```
|
|
17
17
|
This parses `.work-kit/state.md` and `.work-kit/tracker.json` and routes entries to `.work-kit-knowledge/{lessons,conventions,risks,workflow}.md`. It pulls from:
|
|
18
|
-
- `## Observations` typed bullets (`- [lesson|convention|risk|workflow] text`)
|
|
19
|
-
- `## Decisions` → conventions
|
|
20
|
-
- `## Deviations` → workflow feedback
|
|
18
|
+
- `## Observations` typed bullets (`- [lesson|convention|risk|workflow] text`) — the only section that is auto-harvested
|
|
21
19
|
- `tracker.json.loopbacks[]` → workflow feedback
|
|
22
20
|
- Skipped/failed steps → workflow feedback
|
|
23
21
|
|
|
22
|
+
`## Decisions` and `## Deviations` are **not** auto-harvested — they're agent scratch space and routinely contain test plans and review notes. If something in there is worth preserving, restate it as a typed bullet under `## Observations` (or call `work-kit learn` directly in step 2).
|
|
23
|
+
|
|
24
24
|
The output JSON tells you how many entries were `written` vs `duplicates`. Re-running is idempotent.
|
|
25
25
|
|
|
26
26
|
2. **Read your `.work-kit/summary.md`** (the one you just wrote). For each non-obvious thing in it that the parser would NOT have captured automatically, call `work-kit learn`:
|