@broject/retro 0.0.0 → 0.2.3
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 +30 -1
- package/dist/index.d.ts +71 -0
- package/dist/index.js +323 -0
- package/package.json +40 -8
package/README.md
CHANGED
|
@@ -1,3 +1,32 @@
|
|
|
1
1
|
# @broject/retro
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The retrospect engine behind `bro retrospect` — parses TOML retro plans
|
|
4
|
+
(a root cause + prevention actions) and fans each action out to its sink:
|
|
5
|
+
beads, skills, rules, or docs. Powers `bro wtf`.
|
|
6
|
+
|
|
7
|
+
> You probably want the CLI instead: `bro wtf "it happened again"`.
|
|
8
|
+
> Install this only when wiring self-correction into your own loop.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm i @broject/retro
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Requires Node ≥ 22 and `bd`. ESM only.
|
|
17
|
+
|
|
18
|
+
## Surface
|
|
19
|
+
|
|
20
|
+
- `parsePlan` / `parsePlanDoc` / `PLAN_SCHEMA` — retro plan parsing +
|
|
21
|
+
validation
|
|
22
|
+
- `ACTION_SINKS` — where prevention actions land: `backlog`, `memory`,
|
|
23
|
+
`agentic-documents`, `upstream-issue`, `workaround`
|
|
24
|
+
- `RETRO_SCOPES` — action visibility: `universal`, `project`, `user`,
|
|
25
|
+
`agent`, `session`
|
|
26
|
+
- Re-exports `bd` primitives from `@broject/core`
|
|
27
|
+
|
|
28
|
+
## Links
|
|
29
|
+
|
|
30
|
+
- Docs: https://broject.dev/docs/commands/retrospect
|
|
31
|
+
- Source: https://github.com/ThePlenkov/bro/tree/main/packages/retro
|
|
32
|
+
- CLI: https://www.npmjs.com/package/@broject/bro
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { bd, bdJson, checkBeads, refKind } from "@broject/core";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
interface BeadRow {
|
|
5
|
+
id: string;
|
|
6
|
+
title: string;
|
|
7
|
+
status: string;
|
|
8
|
+
labels?: string[];
|
|
9
|
+
updated_at?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Scope decides where a finding must be persisted to actually prevent
|
|
12
|
+
* recurrence — mirrors the retrospect skill's scope table. */
|
|
13
|
+
declare const RETRO_SCOPES: readonly ["universal", "project", "user", "agent", "session"];
|
|
14
|
+
type RetroScope = (typeof RETRO_SCOPES)[number];
|
|
15
|
+
/** Where a follow-up action lands. Every action becomes a prevention bead;
|
|
16
|
+
* the sink label tells the executor which resource to write to. */
|
|
17
|
+
declare const ACTION_SINKS: readonly ["backlog", "memory", "agentic-documents", "upstream-issue", "workaround"];
|
|
18
|
+
type ActionSink = (typeof ACTION_SINKS)[number];
|
|
19
|
+
interface RetroAction {
|
|
20
|
+
title: string;
|
|
21
|
+
sink: ActionSink;
|
|
22
|
+
scope?: RetroScope;
|
|
23
|
+
detail?: string;
|
|
24
|
+
}
|
|
25
|
+
interface RetroPlan {
|
|
26
|
+
what: string;
|
|
27
|
+
why: string;
|
|
28
|
+
scope: RetroScope;
|
|
29
|
+
/** Open wtf bead this retro answers — closed on record. */
|
|
30
|
+
wtf?: string;
|
|
31
|
+
evidence: string[];
|
|
32
|
+
actions: RetroAction[];
|
|
33
|
+
}
|
|
34
|
+
interface RecordResult {
|
|
35
|
+
retroId: string;
|
|
36
|
+
actionIds: string[];
|
|
37
|
+
closedWtf?: string;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/plan.d.ts
|
|
41
|
+
/** The `kind` value a retro plan must carry when it has one — lets
|
|
42
|
+
* `bro run <file>` route the plan to this plugin. */
|
|
43
|
+
declare const PLAN_KIND = "retrospect";
|
|
44
|
+
/** Validate an already-parsed plan document — the plugin planSchema.
|
|
45
|
+
* Throws one error listing every problem — the agent fixes the file
|
|
46
|
+
* once instead of iterating on single failures. */
|
|
47
|
+
declare function parsePlanDoc(doc: unknown, source?: string): RetroPlan;
|
|
48
|
+
/** Parse + validate a plan file's TOML text. */
|
|
49
|
+
declare function parsePlan(text: string, source?: string): RetroPlan;
|
|
50
|
+
/** The commented template `bro retrospect schema` prints — keeps the schema
|
|
51
|
+
* out of skill files so the CLI stays the single source of truth. */
|
|
52
|
+
declare const PLAN_SCHEMA = "# retrospection plan \u2014 written by the agent, executed by `bro retrospect record`\n# or routed by kind via `bro run retro.toml`\n#\n# bro retrospect record retro.toml\n\nkind = \"retrospect\" # plan envelope \u2014 `bro run` routes on it\n\n[retro]\nwhat = \"\" # required \u2014 what went wrong, one line\nwhy = \"\" # required \u2014 root cause, not the symptom\nscope = \"project\" # universal | project | user | agent | session\nwtf = \"\" # optional \u2014 open wtf bead this retro answers (closed on record)\nevidence = [] # optional \u2014 shas, PR urls, bead ids \u2192 bd provenance\n\n# Every action becomes a 'prevention' bead linked discovered-from the retro.\n# sink decides where the executor lands it:\n# backlog tracked follow-up work\n# memory persist to user or project memory (scope decides)\n# agentic-documents AGENTS.md / rules / skill update\n# upstream-issue file an issue in the external repo\n# workaround code-level fix or guardrail to implement now\n#\n# recurrence: a root cause that recurred despite a prose rule needs >=1\n# workaround|backlog action \u2014 wider prose is more of what already failed\n#\n# [[actions]]\n# title = \"\"\n# sink = \"backlog\"\n# scope = \"project\" # optional \u2014 overrides retro.scope for this action\n# detail = \"\" # optional \u2014 body of the follow-up bead\n";
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/retro.d.ts
|
|
55
|
+
declare function listWtf(): BeadRow[];
|
|
56
|
+
declare function openWtf(): BeadRow[];
|
|
57
|
+
declare function listRetros(): BeadRow[];
|
|
58
|
+
declare function openPreventions(): BeadRow[];
|
|
59
|
+
/**
|
|
60
|
+
* Capture a wtf moment: the user's complaint verbatim + a context snapshot.
|
|
61
|
+
* The quote is evidence — never paraphrased.
|
|
62
|
+
*/
|
|
63
|
+
declare function captureWtf(complaint: string): BeadRow;
|
|
64
|
+
/**
|
|
65
|
+
* Record a validated plan: the retro bead carries the analysis memo and is
|
|
66
|
+
* closed immediately — it is a record, not work. Each action lands as an
|
|
67
|
+
* open prevention bead the executor routes by its `sink:` label.
|
|
68
|
+
*/
|
|
69
|
+
declare function recordRetro(plan: RetroPlan): RecordResult;
|
|
70
|
+
//#endregion
|
|
71
|
+
export { ACTION_SINKS, type ActionSink, type BeadRow, PLAN_KIND, PLAN_SCHEMA, RETRO_SCOPES, type RecordResult, type RetroAction, type RetroPlan, type RetroScope, bd, bdJson, captureWtf, checkBeads, listRetros, listWtf, openPreventions, openWtf, parsePlan, parsePlanDoc, recordRetro, refKind };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { bd, bd as bd$1, bdJson, checkBeads, evidenceKind, refKind, refKind as refKind$1, taskStore } from "@broject/core";
|
|
2
|
+
import { parse } from "smol-toml";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
|
|
5
|
+
//#region src/types.ts
|
|
6
|
+
/** Scope decides where a finding must be persisted to actually prevent
|
|
7
|
+
* recurrence — mirrors the retrospect skill's scope table. */
|
|
8
|
+
const RETRO_SCOPES = [
|
|
9
|
+
"universal",
|
|
10
|
+
"project",
|
|
11
|
+
"user",
|
|
12
|
+
"agent",
|
|
13
|
+
"session"
|
|
14
|
+
];
|
|
15
|
+
/** Where a follow-up action lands. Every action becomes a prevention bead;
|
|
16
|
+
* the sink label tells the executor which resource to write to. */
|
|
17
|
+
const ACTION_SINKS = [
|
|
18
|
+
"backlog",
|
|
19
|
+
"memory",
|
|
20
|
+
"agentic-documents",
|
|
21
|
+
"upstream-issue",
|
|
22
|
+
"workaround"
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/plan.ts
|
|
27
|
+
const RETRO_KEYS = new Set([
|
|
28
|
+
"what",
|
|
29
|
+
"why",
|
|
30
|
+
"scope",
|
|
31
|
+
"wtf",
|
|
32
|
+
"evidence"
|
|
33
|
+
]);
|
|
34
|
+
const ACTION_KEYS = new Set([
|
|
35
|
+
"title",
|
|
36
|
+
"sink",
|
|
37
|
+
"scope",
|
|
38
|
+
"detail"
|
|
39
|
+
]);
|
|
40
|
+
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
41
|
+
const nonEmpty = (v) => typeof v === "string" && v.trim() !== "";
|
|
42
|
+
function scope(raw, where, errors) {
|
|
43
|
+
if (raw === void 0) return;
|
|
44
|
+
if (typeof raw !== "string" || !RETRO_SCOPES.includes(raw)) {
|
|
45
|
+
errors.push(`${where}: scope must be one of ${RETRO_SCOPES.join("|")}`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
return raw;
|
|
49
|
+
}
|
|
50
|
+
function parseAction(raw, i, errors) {
|
|
51
|
+
const where = `actions[${i}]`;
|
|
52
|
+
if (!isRecord(raw)) {
|
|
53
|
+
errors.push(`${where}: must be a table`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
for (const key of Object.keys(raw)) if (!ACTION_KEYS.has(key)) errors.push(`${where}: unknown key "${key}"`);
|
|
57
|
+
if (!nonEmpty(raw.title)) errors.push(`${where}: title is required`);
|
|
58
|
+
if (!nonEmpty(raw.sink) || !ACTION_SINKS.includes(raw.sink)) errors.push(`${where}: sink must be one of ${ACTION_SINKS.join("|")}`);
|
|
59
|
+
if (raw.detail !== void 0 && typeof raw.detail !== "string") errors.push(`${where}: detail must be a string`);
|
|
60
|
+
const actionScope = scope(raw.scope, where, errors);
|
|
61
|
+
if (!nonEmpty(raw.title) || !nonEmpty(raw.sink)) return;
|
|
62
|
+
return {
|
|
63
|
+
title: raw.title.trim(),
|
|
64
|
+
sink: raw.sink,
|
|
65
|
+
scope: actionScope,
|
|
66
|
+
detail: typeof raw.detail === "string" ? raw.detail : void 0
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function parseRetroSection(retro, errors) {
|
|
70
|
+
for (const key of Object.keys(retro)) if (!RETRO_KEYS.has(key)) errors.push(`retro: unknown key "${key}"`);
|
|
71
|
+
if (!nonEmpty(retro.what)) errors.push("retro: what is required — what went wrong");
|
|
72
|
+
if (!nonEmpty(retro.why)) errors.push("retro: why is required — the root cause");
|
|
73
|
+
const retroScope = scope(retro.scope, "retro", errors) ?? "project";
|
|
74
|
+
if (retro.wtf !== void 0 && typeof retro.wtf !== "string") errors.push("retro: wtf must be a bead id");
|
|
75
|
+
let evidence = [];
|
|
76
|
+
if (retro.evidence !== void 0) if (!Array.isArray(retro.evidence) || !retro.evidence.every(nonEmpty)) errors.push("retro: evidence must be a list of refs (sha, PR url, bead id)");
|
|
77
|
+
else evidence = retro.evidence.map((e) => e.trim());
|
|
78
|
+
return {
|
|
79
|
+
what: typeof retro.what === "string" ? retro.what.trim() : "",
|
|
80
|
+
why: typeof retro.why === "string" ? retro.why.trim() : "",
|
|
81
|
+
scope: retroScope,
|
|
82
|
+
wtf: nonEmpty(retro.wtf) ? retro.wtf.trim() : void 0,
|
|
83
|
+
evidence
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function parseActions(rawActions, errors) {
|
|
87
|
+
if (rawActions === void 0) return [];
|
|
88
|
+
if (!Array.isArray(rawActions)) {
|
|
89
|
+
errors.push("actions: must be an array of tables ([[actions]])");
|
|
90
|
+
return [];
|
|
91
|
+
}
|
|
92
|
+
const actions = [];
|
|
93
|
+
rawActions.forEach((raw, i) => {
|
|
94
|
+
const action = parseAction(raw, i, errors);
|
|
95
|
+
if (action) actions.push(action);
|
|
96
|
+
});
|
|
97
|
+
return actions;
|
|
98
|
+
}
|
|
99
|
+
/** The `kind` value a retro plan must carry when it has one — lets
|
|
100
|
+
* `bro run <file>` route the plan to this plugin. */
|
|
101
|
+
const PLAN_KIND = "retrospect";
|
|
102
|
+
/** Validate an already-parsed plan document — the plugin planSchema.
|
|
103
|
+
* Throws one error listing every problem — the agent fixes the file
|
|
104
|
+
* once instead of iterating on single failures. */
|
|
105
|
+
function parsePlanDoc(doc, source = "plan") {
|
|
106
|
+
const errors = [];
|
|
107
|
+
if (isRecord(doc)) {
|
|
108
|
+
for (const key of Object.keys(doc)) if (key !== "retro" && key !== "actions" && key !== "kind") errors.push(`unknown top-level key "${key}"`);
|
|
109
|
+
if (doc.kind !== void 0 && doc.kind !== PLAN_KIND) errors.push(`kind: expected "${PLAN_KIND}", got ${JSON.stringify(doc.kind)}`);
|
|
110
|
+
}
|
|
111
|
+
const retro = isRecord(doc) ? doc.retro : void 0;
|
|
112
|
+
if (!isRecord(retro)) throw new Error(`${source}: [retro] table is required`);
|
|
113
|
+
const section = parseRetroSection(retro, errors);
|
|
114
|
+
const actions = parseActions(isRecord(doc) ? doc.actions : void 0, errors);
|
|
115
|
+
if (errors.length > 0) {
|
|
116
|
+
const details = errors.map((e) => ` - ${e}`).join("\n");
|
|
117
|
+
throw new Error(`${source}:\n${details}`);
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
...section,
|
|
121
|
+
actions
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/** Parse + validate a plan file's TOML text. */
|
|
125
|
+
function parsePlan(text, source = "plan") {
|
|
126
|
+
let doc;
|
|
127
|
+
try {
|
|
128
|
+
doc = parse(text);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
throw new Error(`${source}: invalid TOML — ${err instanceof Error ? err.message : String(err)}`);
|
|
131
|
+
}
|
|
132
|
+
return parsePlanDoc(doc, source);
|
|
133
|
+
}
|
|
134
|
+
/** The commented template `bro retrospect schema` prints — keeps the schema
|
|
135
|
+
* out of skill files so the CLI stays the single source of truth. */
|
|
136
|
+
const PLAN_SCHEMA = `# retrospection plan — written by the agent, executed by \`bro retrospect record\`
|
|
137
|
+
# or routed by kind via \`bro run retro.toml\`
|
|
138
|
+
#
|
|
139
|
+
# bro retrospect record retro.toml
|
|
140
|
+
|
|
141
|
+
kind = "retrospect" # plan envelope — \`bro run\` routes on it
|
|
142
|
+
|
|
143
|
+
[retro]
|
|
144
|
+
what = "" # required — what went wrong, one line
|
|
145
|
+
why = "" # required — root cause, not the symptom
|
|
146
|
+
scope = "project" # universal | project | user | agent | session
|
|
147
|
+
wtf = "" # optional — open wtf bead this retro answers (closed on record)
|
|
148
|
+
evidence = [] # optional — shas, PR urls, bead ids → bd provenance
|
|
149
|
+
|
|
150
|
+
# Every action becomes a 'prevention' bead linked discovered-from the retro.
|
|
151
|
+
# sink decides where the executor lands it:
|
|
152
|
+
# backlog tracked follow-up work
|
|
153
|
+
# memory persist to user or project memory (scope decides)
|
|
154
|
+
# agentic-documents AGENTS.md / rules / skill update
|
|
155
|
+
# upstream-issue file an issue in the external repo
|
|
156
|
+
# workaround code-level fix or guardrail to implement now
|
|
157
|
+
#
|
|
158
|
+
# recurrence: a root cause that recurred despite a prose rule needs >=1
|
|
159
|
+
# workaround|backlog action — wider prose is more of what already failed
|
|
160
|
+
#
|
|
161
|
+
# [[actions]]
|
|
162
|
+
# title = ""
|
|
163
|
+
# sink = "backlog"
|
|
164
|
+
# scope = "project" # optional — overrides retro.scope for this action
|
|
165
|
+
# detail = "" # optional — body of the follow-up bead
|
|
166
|
+
`;
|
|
167
|
+
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/retro.ts
|
|
170
|
+
const WTF_LABEL = "wtf";
|
|
171
|
+
const RETRO_LABEL = "retro";
|
|
172
|
+
const PREVENTION_LABEL = "prevention";
|
|
173
|
+
const isOpen = (row) => row.status !== "closed" && row.status !== "done";
|
|
174
|
+
function listByLabel(label) {
|
|
175
|
+
return taskStore().list({
|
|
176
|
+
labels: [label],
|
|
177
|
+
all: true,
|
|
178
|
+
limit: 0
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
function listWtf() {
|
|
182
|
+
return listByLabel(WTF_LABEL);
|
|
183
|
+
}
|
|
184
|
+
function openWtf() {
|
|
185
|
+
return listWtf().filter(isOpen);
|
|
186
|
+
}
|
|
187
|
+
function listRetros() {
|
|
188
|
+
return listByLabel(RETRO_LABEL);
|
|
189
|
+
}
|
|
190
|
+
function openPreventions() {
|
|
191
|
+
return listByLabel(PREVENTION_LABEL).filter(isOpen);
|
|
192
|
+
}
|
|
193
|
+
function shorten(text) {
|
|
194
|
+
return text.length > 72 ? `${text.slice(0, 71)}…` : text;
|
|
195
|
+
}
|
|
196
|
+
/** Best-effort provenance for the capture — tolerate non-git directories. */
|
|
197
|
+
function gitSnapshot() {
|
|
198
|
+
try {
|
|
199
|
+
return [`git: ${execFileSync("git", [
|
|
200
|
+
"rev-parse",
|
|
201
|
+
"--abbrev-ref",
|
|
202
|
+
"HEAD"
|
|
203
|
+
], { encoding: "utf8" }).trim()}@${execFileSync("git", [
|
|
204
|
+
"rev-parse",
|
|
205
|
+
"--short",
|
|
206
|
+
"HEAD"
|
|
207
|
+
], { encoding: "utf8" }).trim()}`];
|
|
208
|
+
} catch {
|
|
209
|
+
return [];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Capture a wtf moment: the user's complaint verbatim + a context snapshot.
|
|
214
|
+
* The quote is evidence — never paraphrased.
|
|
215
|
+
*/
|
|
216
|
+
function captureWtf(complaint) {
|
|
217
|
+
if (!complaint.trim()) throw new Error("wtf capture requires the complaint — quote the user verbatim");
|
|
218
|
+
const description = [
|
|
219
|
+
`> ${complaint}`,
|
|
220
|
+
"",
|
|
221
|
+
`captured: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
222
|
+
`cwd: ${process.cwd()}`,
|
|
223
|
+
...gitSnapshot()
|
|
224
|
+
].join("\n");
|
|
225
|
+
const title = `wtf: ${shorten(complaint.trim().split("\n")[0] ?? "")}`;
|
|
226
|
+
return taskStore().create({
|
|
227
|
+
title,
|
|
228
|
+
labels: [WTF_LABEL],
|
|
229
|
+
description
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
function requireOpenWtf(id) {
|
|
233
|
+
const row = taskStore().get(id);
|
|
234
|
+
if (!row || !(row.labels?.includes(WTF_LABEL) ?? false) || !isOpen(row)) throw new Error(`wtf ${id} is not an open wtf bead`);
|
|
235
|
+
return row;
|
|
236
|
+
}
|
|
237
|
+
/** No explicit wtf? The single open wtf bead is the obvious answer;
|
|
238
|
+
* ambiguity is the caller's problem to resolve. */
|
|
239
|
+
function resolveSingleWtf() {
|
|
240
|
+
const open = openWtf();
|
|
241
|
+
if (open.length > 1) throw new Error(`${open.length} open wtf beads — set retro.wtf in the plan or pass --wtf to pick one`);
|
|
242
|
+
return open[0];
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Record a validated plan: the retro bead carries the analysis memo and is
|
|
246
|
+
* closed immediately — it is a record, not work. Each action lands as an
|
|
247
|
+
* open prevention bead the executor routes by its `sink:` label.
|
|
248
|
+
*/
|
|
249
|
+
function recordRetro(plan) {
|
|
250
|
+
const wtf = plan.wtf ? requireOpenWtf(plan.wtf) : resolveSingleWtf();
|
|
251
|
+
const memo = [
|
|
252
|
+
"## What",
|
|
253
|
+
"",
|
|
254
|
+
plan.what,
|
|
255
|
+
"",
|
|
256
|
+
"## Why",
|
|
257
|
+
"",
|
|
258
|
+
plan.why,
|
|
259
|
+
"",
|
|
260
|
+
`scope: ${plan.scope}`
|
|
261
|
+
].join("\n");
|
|
262
|
+
const title = `retro: ${shorten(plan.what.split("\n")[0] ?? "")}`;
|
|
263
|
+
const retro = taskStore().create({
|
|
264
|
+
title,
|
|
265
|
+
labels: [RETRO_LABEL],
|
|
266
|
+
description: memo,
|
|
267
|
+
...wtf ? {
|
|
268
|
+
deps: [`discovered-from:${wtf.id}`],
|
|
269
|
+
noInheritLabels: true
|
|
270
|
+
} : {}
|
|
271
|
+
});
|
|
272
|
+
const created = [retro.id];
|
|
273
|
+
const actionIds = [];
|
|
274
|
+
try {
|
|
275
|
+
for (const action of plan.actions) {
|
|
276
|
+
const detail = [
|
|
277
|
+
action.detail ?? "",
|
|
278
|
+
"",
|
|
279
|
+
`sink: ${action.sink}`,
|
|
280
|
+
`scope: ${action.scope ?? plan.scope}`,
|
|
281
|
+
`retro: ${retro.id}`
|
|
282
|
+
].join("\n").trim();
|
|
283
|
+
const row = taskStore().create({
|
|
284
|
+
title: action.title,
|
|
285
|
+
labels: [PREVENTION_LABEL, `sink:${action.sink}`],
|
|
286
|
+
noInheritLabels: true,
|
|
287
|
+
deps: [`discovered-from:${retro.id}`],
|
|
288
|
+
description: detail
|
|
289
|
+
});
|
|
290
|
+
actionIds.push(row.id);
|
|
291
|
+
created.push(row.id);
|
|
292
|
+
}
|
|
293
|
+
for (const ref of plan.evidence) bd$1([
|
|
294
|
+
"provenance",
|
|
295
|
+
"record",
|
|
296
|
+
"--issue",
|
|
297
|
+
retro.id,
|
|
298
|
+
"--kind",
|
|
299
|
+
evidenceKind(ref),
|
|
300
|
+
"--source",
|
|
301
|
+
"bro retrospect record",
|
|
302
|
+
"--ref",
|
|
303
|
+
ref,
|
|
304
|
+
"--ref-kind",
|
|
305
|
+
refKind$1(ref)
|
|
306
|
+
]);
|
|
307
|
+
taskStore().close(retro.id, "retrospect recorded");
|
|
308
|
+
if (wtf) taskStore().close(wtf.id, `answered by retro ${retro.id}`);
|
|
309
|
+
} catch (err) {
|
|
310
|
+
for (const id of created) try {
|
|
311
|
+
taskStore().remove(id);
|
|
312
|
+
} catch {}
|
|
313
|
+
throw err;
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
retroId: retro.id,
|
|
317
|
+
actionIds,
|
|
318
|
+
closedWtf: wtf?.id
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
//#endregion
|
|
323
|
+
export { ACTION_SINKS, PLAN_KIND, PLAN_SCHEMA, RETRO_SCOPES, bd, bdJson, captureWtf, checkBeads, listRetros, listWtf, openPreventions, openWtf, parsePlan, parsePlanDoc, recordRetro, refKind };
|
package/package.json
CHANGED
|
@@ -1,16 +1,48 @@
|
|
|
1
1
|
{
|
|
2
|
-
"description": "Placeholder for @broject/retro published by @nx-devkit/prepare-for-release.",
|
|
3
|
-
"license": "MIT",
|
|
4
2
|
"name": "@broject/retro",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
"version": "0.2.3",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"exports": {
|
|
6
|
+
".": {
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"default": "./dist/index.js"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "tsx --test src/**/*.test.ts",
|
|
13
|
+
"typecheck": "tsc --noEmit"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"smol-toml": "1.7.1",
|
|
17
|
+
"@broject/core": "^0.2.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"tsdown": "^0.15.0",
|
|
21
|
+
"tsx": "^4.20.0",
|
|
22
|
+
"typescript": "^5.9.0"
|
|
8
23
|
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=22"
|
|
29
|
+
},
|
|
30
|
+
"license": "MIT",
|
|
9
31
|
"repository": {
|
|
10
32
|
"type": "git",
|
|
11
33
|
"url": "git+https://github.com/ThePlenkov/bro.git",
|
|
12
34
|
"directory": "packages/retro"
|
|
13
35
|
},
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
}
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"description": "Retrospect engine for bro — TOML retro plans fan prevention actions out to beads, skills, rules",
|
|
40
|
+
"keywords": [
|
|
41
|
+
"retrospective",
|
|
42
|
+
"self-correction",
|
|
43
|
+
"beads",
|
|
44
|
+
"agent",
|
|
45
|
+
"bro"
|
|
46
|
+
],
|
|
47
|
+
"homepage": "https://broject.dev/docs"
|
|
48
|
+
}
|