@hobin/developer 0.1.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/LICENSE +21 -0
- package/README.md +231 -0
- package/extensions/developer.ts +700 -0
- package/extensions/skills.ts +89 -0
- package/extensions/state.ts +283 -0
- package/extensions/tool-policy.ts +65 -0
- package/extensions/tui.ts +391 -0
- package/package.json +43 -0
- package/skills/abstraction-review/SKILL.md +87 -0
- package/skills/abstraction-review/references/field-card.md +209 -0
- package/skills/abstraction-review/references/recipe-cards.md +255 -0
- package/skills/abstraction-review/references/repair-table.md +98 -0
- package/skills/abstraction-review/references/worked-examples.md +306 -0
- package/skills/adversarial-eval/SKILL.md +87 -0
- package/skills/model/SKILL.md +76 -0
- package/skills/model/references/problem-modeling.md +262 -0
- package/skills/naming-judgment/SKILL.md +77 -0
- package/skills/naming-judgment/references/elements-of-clojure-naming.md +74 -0
- package/skills/schedule/SKILL.md +71 -0
- package/skills/signal/SKILL.md +75 -0
- package/skills/sketch/SKILL.md +70 -0
- package/skills/specify/SKILL.md +67 -0
- package/skills/verify/SKILL.md +71 -0
- package/skills/visualize/SKILL.md +65 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DynamicBorder,
|
|
3
|
+
type ExtensionCommandContext,
|
|
4
|
+
type Theme,
|
|
5
|
+
type ThemeColor,
|
|
6
|
+
} from "@earendil-works/pi-coding-agent";
|
|
7
|
+
import {
|
|
8
|
+
Container,
|
|
9
|
+
matchesKey,
|
|
10
|
+
type SelectItem,
|
|
11
|
+
SelectList,
|
|
12
|
+
Text,
|
|
13
|
+
truncateToWidth,
|
|
14
|
+
visibleWidth,
|
|
15
|
+
wrapTextWithAnsi,
|
|
16
|
+
} from "@earendil-works/pi-tui";
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
protocolState,
|
|
20
|
+
type DeveloperMode,
|
|
21
|
+
type DeveloperState,
|
|
22
|
+
type PendingQuestion,
|
|
23
|
+
type ProtocolState,
|
|
24
|
+
} from "./state.ts";
|
|
25
|
+
|
|
26
|
+
export type DeveloperAction = "status" | "questions" | DeveloperMode;
|
|
27
|
+
|
|
28
|
+
function modeName(mode: DeveloperMode): string {
|
|
29
|
+
if (mode === "on") return "adaptive";
|
|
30
|
+
return mode;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function protocolColor(value: ProtocolState): ThemeColor {
|
|
34
|
+
if (value === "blocked") return "error";
|
|
35
|
+
if (value === "needs-evidence") return "warning";
|
|
36
|
+
if (value === "needs-judgment") return "accent";
|
|
37
|
+
return "dim";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function modeColor(mode: DeveloperMode): ThemeColor {
|
|
41
|
+
if (mode === "strict") return "warning";
|
|
42
|
+
if (mode === "on") return "accent";
|
|
43
|
+
return "dim";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function renderDeveloperFooter(state: DeveloperState, theme: Theme): string {
|
|
47
|
+
const currentProtocol = protocolState(state);
|
|
48
|
+
const target = state.activeRoute?.owner ?? "none";
|
|
49
|
+
return (
|
|
50
|
+
theme.fg("accent", "developer") +
|
|
51
|
+
theme.fg("dim", " · ") +
|
|
52
|
+
theme.fg(modeColor(state.mode), modeName(state.mode)) +
|
|
53
|
+
theme.fg("dim", " · ") +
|
|
54
|
+
theme.fg(protocolColor(currentProtocol), currentProtocol) +
|
|
55
|
+
theme.fg("dim", ` · ${target}`)
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function developerActionItems(state: DeveloperState): SelectItem[] {
|
|
60
|
+
const currentProtocol = protocolState(state);
|
|
61
|
+
const items: SelectItem[] = [
|
|
62
|
+
{
|
|
63
|
+
value: "status",
|
|
64
|
+
label: "Inspect status",
|
|
65
|
+
description: `${modeName(state.mode)} · ${currentProtocol} · ${state.pendingQuestions.length} open`,
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
if (state.pendingQuestions.length > 0) {
|
|
69
|
+
items.push({
|
|
70
|
+
value: "questions",
|
|
71
|
+
label: "Revisit an open question",
|
|
72
|
+
description: `Choose from ${state.pendingQuestions.length} unresolved question(s) and prepare the editor`,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
items.push(
|
|
76
|
+
{
|
|
77
|
+
value: "on",
|
|
78
|
+
label: state.mode === "on" ? "Adaptive mode (active)" : "Adaptive mode",
|
|
79
|
+
description: "Route judgments adaptively while preserving the current active tool set",
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
value: "strict",
|
|
83
|
+
label: state.mode === "strict" ? "Strict mode (active)" : "Strict mode",
|
|
84
|
+
description: "Require a direct route before Pi built-in mutation tools become active",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
value: "off",
|
|
88
|
+
label: state.mode === "off" ? "Off (active)" : "Turn off",
|
|
89
|
+
description: "Clear Developer protocol state and remove its persistent UI",
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
return items;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function pendingQuestionItems(questions: PendingQuestion[]): SelectItem[] {
|
|
96
|
+
return questions.map((question) => ({
|
|
97
|
+
value: question.id,
|
|
98
|
+
label: question.question,
|
|
99
|
+
description: `${question.status} · ${question.id}`,
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
interface SelectDialogOptions {
|
|
104
|
+
title: string;
|
|
105
|
+
subtitle: string;
|
|
106
|
+
items: SelectItem[];
|
|
107
|
+
width: number;
|
|
108
|
+
maxVisible: number;
|
|
109
|
+
maxPrimaryColumnWidth?: number;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function showSelectDialog(
|
|
113
|
+
ctx: ExtensionCommandContext,
|
|
114
|
+
options: SelectDialogOptions,
|
|
115
|
+
): Promise<string | undefined> {
|
|
116
|
+
const result = await ctx.ui.custom<string | null>(
|
|
117
|
+
(tui, theme, _keybindings, done) => {
|
|
118
|
+
const container = new Container();
|
|
119
|
+
const title = new Text("", 1, 0);
|
|
120
|
+
const subtitle = new Text("", 1, 0);
|
|
121
|
+
const hint = new Text("", 1, 0);
|
|
122
|
+
const updateText = () => {
|
|
123
|
+
title.setText(theme.fg("accent", theme.bold(options.title)));
|
|
124
|
+
subtitle.setText(theme.fg("muted", options.subtitle));
|
|
125
|
+
hint.setText(theme.fg("dim", "↑↓ navigate · enter select · esc cancel"));
|
|
126
|
+
};
|
|
127
|
+
updateText();
|
|
128
|
+
|
|
129
|
+
const list = new SelectList(
|
|
130
|
+
options.items,
|
|
131
|
+
Math.min(options.items.length, options.maxVisible),
|
|
132
|
+
{
|
|
133
|
+
selectedPrefix: (text) => theme.fg("accent", text),
|
|
134
|
+
selectedText: (text) => theme.fg("accent", text),
|
|
135
|
+
description: (text) => theme.fg("muted", text),
|
|
136
|
+
scrollInfo: (text) => theme.fg("dim", text),
|
|
137
|
+
noMatch: (text) => theme.fg("warning", text),
|
|
138
|
+
},
|
|
139
|
+
options.maxPrimaryColumnWidth
|
|
140
|
+
? { minPrimaryColumnWidth: 24, maxPrimaryColumnWidth: options.maxPrimaryColumnWidth }
|
|
141
|
+
: undefined,
|
|
142
|
+
);
|
|
143
|
+
list.onSelect = (item) => done(item.value);
|
|
144
|
+
list.onCancel = () => done(null);
|
|
145
|
+
|
|
146
|
+
container.addChild(new DynamicBorder((text) => theme.fg("borderAccent", text)));
|
|
147
|
+
container.addChild(title);
|
|
148
|
+
container.addChild(subtitle);
|
|
149
|
+
container.addChild(list);
|
|
150
|
+
container.addChild(hint);
|
|
151
|
+
container.addChild(new DynamicBorder((text) => theme.fg("borderAccent", text)));
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
render(width: number) {
|
|
155
|
+
return container.render(width);
|
|
156
|
+
},
|
|
157
|
+
invalidate() {
|
|
158
|
+
updateText();
|
|
159
|
+
container.invalidate();
|
|
160
|
+
},
|
|
161
|
+
handleInput(data: string) {
|
|
162
|
+
list.handleInput(data);
|
|
163
|
+
tui.requestRender();
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
overlay: true,
|
|
169
|
+
overlayOptions: {
|
|
170
|
+
anchor: "center",
|
|
171
|
+
width: options.width,
|
|
172
|
+
maxHeight: Math.min(options.maxVisible + 7, 20),
|
|
173
|
+
margin: 1,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
);
|
|
177
|
+
return result ?? undefined;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export async function showDeveloperActionSelector(
|
|
181
|
+
ctx: ExtensionCommandContext,
|
|
182
|
+
state: DeveloperState,
|
|
183
|
+
): Promise<DeveloperAction | undefined> {
|
|
184
|
+
const result = await showSelectDialog(ctx, {
|
|
185
|
+
title: "Developer control",
|
|
186
|
+
subtitle: `Current · ${modeName(state.mode)} · ${protocolState(state)}`,
|
|
187
|
+
items: developerActionItems(state),
|
|
188
|
+
width: 78,
|
|
189
|
+
maxVisible: 6,
|
|
190
|
+
});
|
|
191
|
+
if (result === "status" || result === "questions" || result === "on" || result === "strict" || result === "off") {
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export async function showPendingQuestionSelector(
|
|
198
|
+
ctx: ExtensionCommandContext,
|
|
199
|
+
questions: PendingQuestion[],
|
|
200
|
+
): Promise<string | undefined> {
|
|
201
|
+
if (questions.length === 0) return undefined;
|
|
202
|
+
return showSelectDialog(ctx, {
|
|
203
|
+
title: "Revisit an open Developer question",
|
|
204
|
+
subtitle: "Selection prepares an editable prompt; protocol state changes only after the route is judged",
|
|
205
|
+
items: pendingQuestionItems(questions),
|
|
206
|
+
width: 96,
|
|
207
|
+
maxVisible: 10,
|
|
208
|
+
maxPrimaryColumnWidth: 52,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export class DeveloperWidget {
|
|
213
|
+
private readonly state: DeveloperState;
|
|
214
|
+
private readonly theme: Theme;
|
|
215
|
+
|
|
216
|
+
constructor(state: DeveloperState, theme: Theme) {
|
|
217
|
+
this.state = state;
|
|
218
|
+
this.theme = theme;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
render(width: number): string[] {
|
|
222
|
+
const lines: string[] = [];
|
|
223
|
+
if (this.state.activeRoute) {
|
|
224
|
+
lines.push(
|
|
225
|
+
truncateToWidth(
|
|
226
|
+
`${this.theme.fg("accent", "◆ route")} ${this.theme.fg("muted", "·")} ${this.theme.fg("accent", this.state.activeRoute.owner)} ${this.theme.fg("muted", this.state.activeRoute.question)}`,
|
|
227
|
+
width,
|
|
228
|
+
"…",
|
|
229
|
+
),
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
for (const question of this.state.pendingQuestions.slice(0, 3)) {
|
|
233
|
+
lines.push(
|
|
234
|
+
truncateToWidth(
|
|
235
|
+
`${this.theme.fg(question.status === "blocked" ? "error" : "warning", "? open")} ${this.theme.fg("dim", "·")} ${this.theme.fg("muted", question.question)}`,
|
|
236
|
+
width,
|
|
237
|
+
"…",
|
|
238
|
+
),
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
if (this.state.pendingQuestions.length > 3) {
|
|
242
|
+
lines.push(this.theme.fg("dim", ` +${this.state.pendingQuestions.length - 3} more open questions`));
|
|
243
|
+
}
|
|
244
|
+
return lines;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
invalidate(): void {}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export interface DeveloperStatusView {
|
|
251
|
+
state: DeveloperState;
|
|
252
|
+
activeTools: string[];
|
|
253
|
+
availableSkills: string[];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export class DeveloperStatusPanel {
|
|
257
|
+
private cachedWidth?: number;
|
|
258
|
+
private cachedLines?: string[];
|
|
259
|
+
private readonly view: DeveloperStatusView;
|
|
260
|
+
private readonly theme: Theme;
|
|
261
|
+
private readonly onClose: () => void;
|
|
262
|
+
|
|
263
|
+
constructor(view: DeveloperStatusView, theme: Theme, onClose: () => void) {
|
|
264
|
+
this.view = view;
|
|
265
|
+
this.theme = theme;
|
|
266
|
+
this.onClose = onClose;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
handleInput(data: string): void {
|
|
270
|
+
if (matchesKey(data, "escape") || matchesKey(data, "enter") || matchesKey(data, "ctrl+c")) {
|
|
271
|
+
this.onClose();
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
render(width: number): string[] {
|
|
276
|
+
if (this.cachedLines && this.cachedWidth === width) return this.cachedLines;
|
|
277
|
+
|
|
278
|
+
const panelWidth = Math.max(20, width);
|
|
279
|
+
const innerWidth = Math.max(18, panelWidth - 2);
|
|
280
|
+
const rows: string[] = [];
|
|
281
|
+
const border = (text: string) => this.theme.fg("borderMuted", text);
|
|
282
|
+
const row = (content = "") => `${border("│")}${truncateToWidth(content, innerWidth, "…", true)}${border("│")}`;
|
|
283
|
+
const addWrapped = (label: string, value: string, color: ThemeColor = "muted", maxLines = 2) => {
|
|
284
|
+
const prefix = ` ${this.theme.fg("dim", `${label} ·`)} `;
|
|
285
|
+
const wrapped = wrapTextWithAnsi(prefix + this.theme.fg(color, value), innerWidth).slice(0, maxLines);
|
|
286
|
+
for (const line of wrapped) rows.push(row(line));
|
|
287
|
+
};
|
|
288
|
+
const section = (title: string) => rows.push(row(` ${this.theme.fg("accent", this.theme.bold(title))}`));
|
|
289
|
+
|
|
290
|
+
rows.push(border(`╭${"─".repeat(innerWidth)}╮`));
|
|
291
|
+
rows.push(row(` ${this.theme.fg("accent", this.theme.bold("Developer status"))}`));
|
|
292
|
+
rows.push(row());
|
|
293
|
+
|
|
294
|
+
const state = this.view.state;
|
|
295
|
+
const currentProtocol = protocolState(state);
|
|
296
|
+
rows.push(
|
|
297
|
+
row(
|
|
298
|
+
` mode ${this.theme.fg(modeColor(state.mode), modeName(state.mode))}` +
|
|
299
|
+
this.theme.fg("dim", " · ") +
|
|
300
|
+
`protocol ${this.theme.fg(protocolColor(currentProtocol), currentProtocol)}` +
|
|
301
|
+
this.theme.fg("dim", " · ") +
|
|
302
|
+
`target ${this.theme.fg("muted", state.activeRoute?.owner ?? "none")}`,
|
|
303
|
+
),
|
|
304
|
+
);
|
|
305
|
+
rows.push(row());
|
|
306
|
+
|
|
307
|
+
section("Active route");
|
|
308
|
+
if (state.activeRoute) {
|
|
309
|
+
addWrapped("id", state.activeRoute.routeId, "dim", 1);
|
|
310
|
+
addWrapped("question", state.activeRoute.question, "text");
|
|
311
|
+
addWrapped("reason", state.activeRoute.reason);
|
|
312
|
+
addWrapped("skill", state.activeRoute.methodLocation ?? "direct action", "dim", 1);
|
|
313
|
+
addWrapped("known evidence", String(state.activeRoute.knownEvidence.length), "muted", 1);
|
|
314
|
+
} else {
|
|
315
|
+
addWrapped("state", "No route is currently waiting for judgment.", "dim", 1);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
rows.push(row());
|
|
319
|
+
section(`Open questions · ${state.pendingQuestions.length}`);
|
|
320
|
+
if (state.pendingQuestions.length === 0) {
|
|
321
|
+
addWrapped("state", "No unresolved Developer questions.", "dim", 1);
|
|
322
|
+
} else {
|
|
323
|
+
for (const question of state.pendingQuestions.slice(0, 4)) {
|
|
324
|
+
addWrapped(
|
|
325
|
+
question.status === "blocked" ? "blocked" : "needs evidence",
|
|
326
|
+
question.question,
|
|
327
|
+
question.status === "blocked" ? "error" : "warning",
|
|
328
|
+
1,
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
if (state.pendingQuestions.length > 4) {
|
|
332
|
+
addWrapped("more", String(state.pendingQuestions.length - 4), "dim", 1);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
rows.push(row());
|
|
337
|
+
section("Last judgment");
|
|
338
|
+
if (state.lastJudgment) {
|
|
339
|
+
addWrapped("status", state.lastJudgment.status, protocolColor(
|
|
340
|
+
state.lastJudgment.status === "blocked"
|
|
341
|
+
? "blocked"
|
|
342
|
+
: state.lastJudgment.status === "needs-evidence"
|
|
343
|
+
? "needs-evidence"
|
|
344
|
+
: "idle",
|
|
345
|
+
), 1);
|
|
346
|
+
addWrapped("result", state.lastJudgment.result, "muted");
|
|
347
|
+
addWrapped(
|
|
348
|
+
"evidence",
|
|
349
|
+
`${state.lastJudgment.basis.length} basis · ${state.lastJudgment.artifacts.length} artifacts`,
|
|
350
|
+
"dim",
|
|
351
|
+
1,
|
|
352
|
+
);
|
|
353
|
+
} else {
|
|
354
|
+
addWrapped("state", "No judgment has been recorded on this branch.", "dim", 1);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
rows.push(row());
|
|
358
|
+
addWrapped(
|
|
359
|
+
"resources",
|
|
360
|
+
`${this.view.availableSkills.length} skills · ${this.view.activeTools.length} active tools`,
|
|
361
|
+
"dim",
|
|
362
|
+
1,
|
|
363
|
+
);
|
|
364
|
+
rows.push(row());
|
|
365
|
+
rows.push(row(` ${this.theme.fg("dim", "enter/esc close · /develop questions revisits open work")}`));
|
|
366
|
+
rows.push(border(`╰${"─".repeat(innerWidth)}╯`));
|
|
367
|
+
|
|
368
|
+
this.cachedWidth = width;
|
|
369
|
+
this.cachedLines = rows;
|
|
370
|
+
return rows;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
invalidate(): void {
|
|
374
|
+
this.cachedWidth = undefined;
|
|
375
|
+
this.cachedLines = undefined;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export async function showDeveloperStatus(
|
|
380
|
+
ctx: ExtensionCommandContext,
|
|
381
|
+
view: DeveloperStatusView,
|
|
382
|
+
): Promise<void> {
|
|
383
|
+
await ctx.ui.custom<void>((_tui, theme, _keybindings, done) =>
|
|
384
|
+
new DeveloperStatusPanel(view, theme, () => done()));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export function prepareQuestionPrompt(ctx: ExtensionCommandContext, question: PendingQuestion): void {
|
|
388
|
+
const prompt = `Revisit Developer question ${question.id}: ${question.question}`;
|
|
389
|
+
const current = ctx.ui.getEditorText();
|
|
390
|
+
ctx.ui.setEditorText(current.trim() ? `${current.trimEnd()}\n\n${prompt}` : prompt);
|
|
391
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hobin/developer",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Adaptive product-development reasoning and evidence for Pi.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"keywords": ["pi-package", "pi", "developer", "skills"],
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "dev-hobin",
|
|
10
|
+
"url": "https://github.com/dev-hobin"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/dev-hobin/agent.git",
|
|
15
|
+
"directory": "packages/developer"
|
|
16
|
+
},
|
|
17
|
+
"homepage": "https://github.com/dev-hobin/agent/tree/main/packages/developer#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/dev-hobin/agent/issues"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"files": ["extensions", "skills", "README.md", "LICENSE"],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=22.19.0"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"check": "node scripts/check-package.mjs && node --test tests/*.test.ts",
|
|
30
|
+
"eval": "node scripts/eval-rpc.mjs",
|
|
31
|
+
"eval:json": "node scripts/eval-json.mjs"
|
|
32
|
+
},
|
|
33
|
+
"pi": {
|
|
34
|
+
"extensions": ["./extensions/developer.ts"],
|
|
35
|
+
"skills": ["./skills"]
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"@earendil-works/pi-ai": "*",
|
|
39
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
40
|
+
"@earendil-works/pi-tui": "*",
|
|
41
|
+
"typebox": "*"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: abstraction-review
|
|
3
|
+
description: "Judge whether a concrete wished interface, helper, API, workflow rule, boundary, or structural candidate is stable enough to keep, revise, split, reject, or defer. Use when a concrete candidate and the pressure behind it can be inspected, including when its contract or evidence may be incomplete."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Abstraction Review
|
|
7
|
+
|
|
8
|
+
Decide whether a concrete abstraction candidate is safe to rely on.
|
|
9
|
+
|
|
10
|
+
## Core Question
|
|
11
|
+
|
|
12
|
+
Is this candidate stable enough to keep, or should it be revised, split,
|
|
13
|
+
rejected, or deferred?
|
|
14
|
+
|
|
15
|
+
## Inputs
|
|
16
|
+
|
|
17
|
+
- Concrete candidate and the pressure it should remove
|
|
18
|
+
- Caller language, representative cases, and expected contract
|
|
19
|
+
- Hidden detail and failure mode
|
|
20
|
+
- Requirement, model, design, code, diff, or tests when available
|
|
21
|
+
|
|
22
|
+
## Output
|
|
23
|
+
|
|
24
|
+
Lead with the caller's concrete failure mode; keep review labels secondary.
|
|
25
|
+
Produce review timing, layer, contract, hidden detail, output artifact, stop
|
|
26
|
+
check, evidence and gaps, and one decision: `keep`, `revise-surface`,
|
|
27
|
+
`revise-model`, `split`, `reject`, or `defer`. When used inside a larger task,
|
|
28
|
+
return:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
Status: resolved | needs-evidence | not-applicable | blocked
|
|
32
|
+
Result: review decision and its concrete failure mode
|
|
33
|
+
Basis: candidate source, callers, cases, code, tests, and assumptions
|
|
34
|
+
Open questions: missing contract or evidence, or none
|
|
35
|
+
Artifacts: boundary, invariant, table, graph, trace, or repair rule
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Return only this skill's judgment for the question at hand; leave subsequent
|
|
39
|
+
routing to the caller.
|
|
40
|
+
|
|
41
|
+
## Completion
|
|
42
|
+
|
|
43
|
+
Finish when the candidate can satisfy this sentence or is rejected/deferred for
|
|
44
|
+
failing it:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
Given <input artifact>, derive <output artifact> by <rule>, then check
|
|
48
|
+
<observable stop>. If it fails, repair the named layer or contract.
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Revisit when new callers, cases, implementation evidence, or failures change
|
|
52
|
+
the contract or stop check.
|
|
53
|
+
|
|
54
|
+
## Method
|
|
55
|
+
|
|
56
|
+
1. Identify the candidate, its source, and confidence.
|
|
57
|
+
2. State the pressure it is supposed to remove. Bias toward `reject` or `defer`
|
|
58
|
+
when no pressure is visible.
|
|
59
|
+
3. Review pre-implementation intent or post-implementation evidence explicitly.
|
|
60
|
+
4. Choose the tested layer: Language, Unit, Law, Boundary, Engine, Time, or Run.
|
|
61
|
+
5. State the contract in caller language and the detail callers may ignore.
|
|
62
|
+
6. Name the reusable output artifact left by the review.
|
|
63
|
+
7. Define an observable stop check using cases, properties, tests, diffs,
|
|
64
|
+
behavior, traces, or command output.
|
|
65
|
+
8. Decide without converting missing evidence into polished approval.
|
|
66
|
+
|
|
67
|
+
## Missing Evidence
|
|
68
|
+
|
|
69
|
+
Return `needs-evidence` when a caller, representative case, or stop check can
|
|
70
|
+
stabilize the judgment. Return `not-applicable` when there is no concrete
|
|
71
|
+
candidate yet. Return `blocked` when product meaning needed for the contract is
|
|
72
|
+
human-owned.
|
|
73
|
+
|
|
74
|
+
## Boundary
|
|
75
|
+
|
|
76
|
+
Do not discover structural movement, create the original design surface, decide
|
|
77
|
+
timing, implement the change, or perform final completion review.
|
|
78
|
+
|
|
79
|
+
## References
|
|
80
|
+
|
|
81
|
+
- Read [the field card](references/field-card.md) for substantial or auditable
|
|
82
|
+
reviews.
|
|
83
|
+
- Read [the recipe cards](references/recipe-cards.md) when the candidate needs a construction or
|
|
84
|
+
repair rule.
|
|
85
|
+
- Read [the repair table](references/repair-table.md) when a stop check fails.
|
|
86
|
+
- Read [the worked examples](references/worked-examples.md) for concrete
|
|
87
|
+
calibration.
|