@selesai/code 0.3.10 → 0.4.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/defaults/models.json +2 -3
- package/dist/extensions/caveman/index.js +16 -1
- package/dist/extensions/caveman/test/extension.test.js +7 -4
- package/dist/extensions/caveman/test/helpers.test.js +12 -1
- package/dist/extensions/context-compaction-reminder.test.ts +82 -0
- package/dist/extensions/context-compaction-reminder.ts +28 -0
- package/dist/extensions/handoff-new.test.ts +2 -13
- package/dist/extensions/handoff-new.ts +16 -25
- package/dist/extensions/package.json +1 -0
- package/dist/extensions/pi-subagents/agents/architect.md +25 -173
- package/dist/extensions/pi-subagents/agents/builder.md +16 -105
- package/dist/extensions/pi-subagents/agents/commentator.md +19 -116
- package/dist/extensions/pi-subagents/agents/explorer.md +13 -33
- package/dist/extensions/pi-subagents/agents/recapper.md +19 -11
- package/dist/extensions/pi-subagents/agents/researcher.md +10 -33
- package/dist/extensions/question/constants.ts +0 -7
- package/dist/extensions/question/index.ts +11 -108
- package/dist/extensions/question/schemas.ts +1 -26
- package/dist/extensions/question/shortcuts.ts +5 -14
- package/dist/extensions/question/tests/question-list.test.ts +8 -0
- package/dist/extensions/question/tests/ui-protocol.test.ts +1 -7
- package/dist/extensions/question/tui-adapter.ts +3 -20
- package/dist/extensions/question/types.ts +0 -6
- package/dist/extensions/question/ui-protocol.ts +2 -17
- package/dist/extensions/workflow/adapter.ts +384 -124
- package/dist/extensions/workflow/extension.ts +2 -1
- package/dist/extensions/workflow/modes/prototype.ts +6 -6
- package/dist/extensions/workflow/modes/quick.ts +6 -6
- package/dist/extensions/workflow/modes/task.ts +79 -0
- package/dist/extensions/workflow/run-state.ts +124 -0
- package/dist/extensions/workflow/state-machine.ts +4 -8
- package/dist/skills/workflow-creation/SKILL.md +72 -0
- package/docs/workflows.md +40 -6
- package/package.json +1 -1
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
"reasoning": true,
|
|
17
17
|
"input": ["text"],
|
|
18
18
|
"contextWindow": 1000000,
|
|
19
|
-
"maxTokens": 384000,
|
|
20
19
|
"cost": {
|
|
21
20
|
"input": 0.14,
|
|
22
21
|
"output": 0.28,
|
|
@@ -113,7 +112,7 @@
|
|
|
113
112
|
"name": "GLM-5.2",
|
|
114
113
|
"reasoning": true,
|
|
115
114
|
"input": ["text"],
|
|
116
|
-
"contextWindow":
|
|
115
|
+
"contextWindow": 1000000,
|
|
117
116
|
"cost": {
|
|
118
117
|
"input": 1.45,
|
|
119
118
|
"output": 4.5,
|
|
@@ -129,7 +128,7 @@
|
|
|
129
128
|
"name": "GLM-5.2 Fast",
|
|
130
129
|
"reasoning": false,
|
|
131
130
|
"input": ["text"],
|
|
132
|
-
"contextWindow":
|
|
131
|
+
"contextWindow": 1000000,
|
|
133
132
|
"cost": {
|
|
134
133
|
"input": 1.45,
|
|
135
134
|
"output": 4.5,
|
|
@@ -23,6 +23,20 @@ export function isDeactivationCommand(text) {
|
|
|
23
23
|
return t === "stop caveman" || t === "normal mode";
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
export function resolveSessionActive(entries, fallback = true) {
|
|
27
|
+
if (!Array.isArray(entries)) return fallback;
|
|
28
|
+
|
|
29
|
+
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
30
|
+
const entry = entries[i];
|
|
31
|
+
if (entry?.type !== "custom" || entry?.customType !== "caveman-mode") continue;
|
|
32
|
+
|
|
33
|
+
const active = entry?.data?.active;
|
|
34
|
+
if (typeof active === "boolean") return active;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
|
|
26
40
|
export default function cavemanExtension(pi) {
|
|
27
41
|
let active = true;
|
|
28
42
|
let isActive = false;
|
|
@@ -81,7 +95,8 @@ export default function cavemanExtension(pi) {
|
|
|
81
95
|
});
|
|
82
96
|
|
|
83
97
|
pi.on("session_start", async (_event, ctx) => {
|
|
84
|
-
|
|
98
|
+
const entries = ctx?.sessionManager?.getBranch?.() || ctx?.sessionManager?.getEntries?.() || [];
|
|
99
|
+
active = resolveSessionActive(entries);
|
|
85
100
|
syncStatus(ctx);
|
|
86
101
|
ctx?.ui?.notify?.(`Caveman loaded: ${active ? "ON" : "OFF"}`, "info");
|
|
87
102
|
});
|
|
@@ -80,23 +80,26 @@ test("disabled caveman skips instruction injection", async () => {
|
|
|
80
80
|
assert.equal(result, undefined);
|
|
81
81
|
});
|
|
82
82
|
|
|
83
|
-
test("session_start
|
|
83
|
+
test("session_start restores persisted OFF state", async () => {
|
|
84
84
|
const { events } = createPiHarness();
|
|
85
85
|
const ctx = createCommandContext({
|
|
86
86
|
sessionManager: {
|
|
87
|
-
|
|
87
|
+
getBranch: () => [
|
|
88
88
|
{ type: "custom", customType: "caveman-mode", data: { active: false } },
|
|
89
89
|
],
|
|
90
|
+
getEntries: () => [
|
|
91
|
+
{ type: "custom", customType: "caveman-mode", data: { active: true } },
|
|
92
|
+
],
|
|
90
93
|
},
|
|
91
94
|
});
|
|
92
95
|
|
|
93
96
|
await events.get("session_start")({ reason: "resume" }, ctx);
|
|
94
97
|
const result = await events.get("before_agent_start")({ systemPrompt: "BASE" }, ctx);
|
|
95
98
|
|
|
96
|
-
assert.
|
|
99
|
+
assert.equal(result, undefined);
|
|
97
100
|
});
|
|
98
101
|
|
|
99
|
-
test("
|
|
102
|
+
test("session_start defaults to ON when no persisted entry exists", async () => {
|
|
100
103
|
const { commands, events } = createPiHarness();
|
|
101
104
|
const ctx = createCommandContext();
|
|
102
105
|
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
getCavemanInstructions,
|
|
8
8
|
isDeactivationCommand,
|
|
9
9
|
parseCavemanCommand,
|
|
10
|
+
resolveSessionActive,
|
|
10
11
|
} from "../index.js";
|
|
11
12
|
|
|
12
13
|
test("parseCavemanCommand: bare toggles", () => {
|
|
@@ -26,7 +27,17 @@ test("parseCavemanCommand: unknown arg is invalid", () => {
|
|
|
26
27
|
assert.deepEqual(parseCavemanCommand("banana"), { type: "invalid", reason: "unknown-subcommand", arg: "banana" });
|
|
27
28
|
});
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
test("resolveSessionActive: returns last persisted boolean, defaults to true", () => {
|
|
31
|
+
assert.equal(resolveSessionActive([]), true);
|
|
32
|
+
assert.equal(resolveSessionActive([{ type: "custom", customType: "caveman-mode", data: { active: false } }]), false);
|
|
33
|
+
assert.equal(resolveSessionActive([{ type: "custom", customType: "caveman-mode", data: { active: true } }]), true);
|
|
34
|
+
assert.equal(resolveSessionActive([
|
|
35
|
+
{ type: "custom", customType: "caveman-mode", data: { active: false } },
|
|
36
|
+
{ type: "custom", customType: "caveman-mode", data: { active: true } },
|
|
37
|
+
]), true);
|
|
38
|
+
assert.equal(resolveSessionActive([{ type: "custom", customType: "other", data: {} }]), true);
|
|
39
|
+
assert.equal(resolveSessionActive(null), true);
|
|
40
|
+
});
|
|
30
41
|
|
|
31
42
|
test("isDeactivationCommand: only the whole message, case-insensitive, trailing punct ok", () => {
|
|
32
43
|
assert.equal(isDeactivationCommand("stop caveman"), true);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import type { ExtensionAPI, ExtensionContext } from "@selesai/code";
|
|
3
|
+
import contextCompactionReminderExtension, {
|
|
4
|
+
CONTEXT_COMPACTION_REMINDER_MESSAGE,
|
|
5
|
+
} from "./context-compaction-reminder.ts";
|
|
6
|
+
|
|
7
|
+
type Handler = (event: unknown, ctx: ExtensionContext) => void | Promise<void>;
|
|
8
|
+
|
|
9
|
+
function createHarness(tokens: number | null | undefined = undefined) {
|
|
10
|
+
const handlers = new Map<string, Handler>();
|
|
11
|
+
const notify = vi.fn();
|
|
12
|
+
const pi = {
|
|
13
|
+
on: vi.fn((event: string, handler: Handler) => handlers.set(event, handler)),
|
|
14
|
+
} as unknown as ExtensionAPI;
|
|
15
|
+
const ctx = {
|
|
16
|
+
getContextUsage: () =>
|
|
17
|
+
tokens === undefined ? undefined : { tokens, contextWindow: 200_000, percent: tokens === null ? null : tokens / 2_000 },
|
|
18
|
+
ui: { notify },
|
|
19
|
+
} as unknown as ExtensionContext;
|
|
20
|
+
|
|
21
|
+
contextCompactionReminderExtension(pi);
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
notify,
|
|
25
|
+
setTokens: (value: number | null | undefined) => {
|
|
26
|
+
tokens = value;
|
|
27
|
+
},
|
|
28
|
+
settle: () => handlers.get("agent_settled")!({ type: "agent_settled" }, ctx),
|
|
29
|
+
compact: () => handlers.get("session_compact")!({ type: "session_compact" }, ctx),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
describe("context-compaction-reminder", () => {
|
|
34
|
+
it("warns once when known context reaches 128k", async () => {
|
|
35
|
+
const { notify, setTokens, settle } = createHarness(127_999);
|
|
36
|
+
|
|
37
|
+
await settle();
|
|
38
|
+
setTokens(128_000);
|
|
39
|
+
await settle();
|
|
40
|
+
setTokens(130_000);
|
|
41
|
+
await settle();
|
|
42
|
+
|
|
43
|
+
expect(notify).toHaveBeenCalledTimes(1);
|
|
44
|
+
expect(notify).toHaveBeenCalledWith(CONTEXT_COMPACTION_REMINDER_MESSAGE, "warning");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("does not reset the reminder for unavailable or post-compaction usage", async () => {
|
|
48
|
+
const { notify, setTokens, settle } = createHarness(128_000);
|
|
49
|
+
|
|
50
|
+
await settle();
|
|
51
|
+
setTokens(undefined);
|
|
52
|
+
await settle();
|
|
53
|
+
setTokens(null);
|
|
54
|
+
await settle();
|
|
55
|
+
setTokens(130_000);
|
|
56
|
+
await settle();
|
|
57
|
+
|
|
58
|
+
expect(notify).toHaveBeenCalledTimes(1);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("allows another warning after successful compaction", async () => {
|
|
62
|
+
const { notify, settle, compact } = createHarness(128_000);
|
|
63
|
+
|
|
64
|
+
await settle();
|
|
65
|
+
await compact();
|
|
66
|
+
await settle();
|
|
67
|
+
|
|
68
|
+
expect(notify).toHaveBeenCalledTimes(2);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("allows another warning after known usage drops below 128k", async () => {
|
|
72
|
+
const { notify, setTokens, settle } = createHarness(128_000);
|
|
73
|
+
|
|
74
|
+
await settle();
|
|
75
|
+
setTokens(127_999);
|
|
76
|
+
await settle();
|
|
77
|
+
setTokens(128_000);
|
|
78
|
+
await settle();
|
|
79
|
+
|
|
80
|
+
expect(notify).toHaveBeenCalledTimes(2);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@selesai/code";
|
|
2
|
+
|
|
3
|
+
export const CONTEXT_COMPACTION_REMINDER_THRESHOLD = 128_000;
|
|
4
|
+
export const CONTEXT_COMPACTION_REMINDER_MESSAGE =
|
|
5
|
+
"Conversation context has reached 128k tokens. Run /compact to summarize older context.";
|
|
6
|
+
|
|
7
|
+
export default function contextCompactionReminderExtension(pi: ExtensionAPI): void {
|
|
8
|
+
let reminderShown = false;
|
|
9
|
+
|
|
10
|
+
pi.on("session_compact", () => {
|
|
11
|
+
reminderShown = false;
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
pi.on("agent_settled", (_event, ctx) => {
|
|
15
|
+
const tokens = ctx.getContextUsage()?.tokens;
|
|
16
|
+
if (tokens === undefined || tokens === null) return;
|
|
17
|
+
|
|
18
|
+
if (tokens < CONTEXT_COMPACTION_REMINDER_THRESHOLD) {
|
|
19
|
+
reminderShown = false;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!reminderShown) {
|
|
24
|
+
ctx.ui.notify(CONTEXT_COMPACTION_REMINDER_MESSAGE, "warning");
|
|
25
|
+
reminderShown = true;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -28,11 +28,10 @@ function createPiHarness() {
|
|
|
28
28
|
return { commands };
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
test("registers
|
|
31
|
+
test("registers handoff-new only", () => {
|
|
32
32
|
const { commands } = createPiHarness();
|
|
33
33
|
assert.ok(commands.has("handoff-new"));
|
|
34
|
-
assert.
|
|
35
|
-
assert.equal(commands.get("handoff-new")?.handler, commands.get("handover-new")?.handler);
|
|
34
|
+
assert.equal(commands.has("handover-new"), false);
|
|
36
35
|
});
|
|
37
36
|
|
|
38
37
|
// ---- pure helpers ----
|
|
@@ -106,7 +105,6 @@ test("buildAiContext: embeds conversation + goal into one user turn", () => {
|
|
|
106
105
|
function createCtx(opts: {
|
|
107
106
|
branch?: SessionEntry[];
|
|
108
107
|
model?: any;
|
|
109
|
-
editorResult?: string;
|
|
110
108
|
customResult?: string | null;
|
|
111
109
|
mode?: string;
|
|
112
110
|
} = {}) {
|
|
@@ -130,7 +128,6 @@ function createCtx(opts: {
|
|
|
130
128
|
ui: {
|
|
131
129
|
notify: (msg: string, kind: string) => calls.notify.push({ msg, kind }),
|
|
132
130
|
custom: async <T>(_factory: any): Promise<T> => (opts.customResult === undefined ? "HANDOFF PROMPT" : opts.customResult) as unknown as T,
|
|
133
|
-
editor: async (_title: string, prefill?: string) => ("editorResult" in opts ? opts.editorResult : prefill),
|
|
134
131
|
setEditorText: (text: string) => {
|
|
135
132
|
calls.editorText = text;
|
|
136
133
|
},
|
|
@@ -196,14 +193,6 @@ test("custom null (cancelled) does not open new session", async () => {
|
|
|
196
193
|
assert.ok(calls.notify.some((n) => /Cancelled/.test(n.msg)));
|
|
197
194
|
});
|
|
198
195
|
|
|
199
|
-
test("editor cancel does not open new session", async () => {
|
|
200
|
-
const { commands } = createPiHarness();
|
|
201
|
-
const { ctx, calls } = createCtx({ editorResult: undefined });
|
|
202
|
-
await commands.get("handoff-new")!.handler("goal", ctx);
|
|
203
|
-
assert.equal(calls.newSession, null);
|
|
204
|
-
assert.ok(calls.notify.some((n) => /Cancelled/.test(n.msg)));
|
|
205
|
-
});
|
|
206
|
-
|
|
207
196
|
// sanity: DEFAULT_GOAL exported and non-empty (used by handler)
|
|
208
197
|
test("DEFAULT_GOAL is non-empty", () => {
|
|
209
198
|
assert.ok(DEFAULT_GOAL.length > 0);
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* /handoff-new — generate a handoff prompt and open a clean new session with
|
|
3
|
-
* that content as the
|
|
3
|
+
* that content as the first unsent prompt (editor text, not hidden system
|
|
4
4
|
* prompt). Sibling of examples/extensions/handoff.ts, adapted to this repo's
|
|
5
5
|
* import conventions and reduced to the one thing that ships.
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
8
|
* /handoff-new continue implementing the workflow fix
|
|
9
|
-
* /handover-new ship plan 2
|
|
10
9
|
*
|
|
11
|
-
* If no goal argument is given, a default goal is used. The
|
|
12
|
-
*
|
|
10
|
+
* If no goal argument is given, a default goal is used. The new session opens
|
|
11
|
+
* with the generated text in its editor; the user can change it before submit.
|
|
13
12
|
*/
|
|
14
13
|
|
|
15
14
|
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
@@ -17,14 +16,15 @@ import { complete, type Context } from "@earendil-works/pi-ai/compat";
|
|
|
17
16
|
import type { ExtensionAPI, ExtensionCommandContext, SessionEntry } from "@selesai/code";
|
|
18
17
|
import { BorderedLoader, convertToLlm, serializeConversation } from "@selesai/code";
|
|
19
18
|
|
|
20
|
-
const SYSTEM_PROMPT = `
|
|
19
|
+
const SYSTEM_PROMPT = `Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS - not the current workspace.
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
2. Lists any relevant files that were discussed or modified
|
|
24
|
-
3. Clearly states the next task based on the user's goal
|
|
25
|
-
4. Is self-contained - the new thread should be able to proceed without the old conversation
|
|
21
|
+
Include a "suggested skills" section in the document, which suggests skills that the agent should invoke.
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
Do not duplicate content already captured in other artifacts (PRDs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead.
|
|
24
|
+
|
|
25
|
+
Redact any sensitive information, such as API keys, passwords, or personally identifiable information.
|
|
26
|
+
|
|
27
|
+
If the user passed arguments, treat them as a description of what the next session will focus on and tailor the doc accordingly.`;
|
|
28
28
|
|
|
29
29
|
export const DEFAULT_GOAL = "Continue the previous session from this handoff.";
|
|
30
30
|
|
|
@@ -85,13 +85,10 @@ export function buildAiContext(conversationText: string, goal: string): Context
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export default function (pi: ExtensionAPI) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
handler: handoffNew,
|
|
93
|
-
});
|
|
94
|
-
}
|
|
88
|
+
pi.registerCommand("handoff-new", {
|
|
89
|
+
description: "Generate a handoff prompt and open a clean new session with it as the first draft",
|
|
90
|
+
handler: handoffNew,
|
|
91
|
+
});
|
|
95
92
|
}
|
|
96
93
|
|
|
97
94
|
async function handoffNew(args: string, ctx: ExtensionCommandContext) {
|
|
@@ -151,17 +148,11 @@ async function handoffNew(args: string, ctx: ExtensionCommandContext) {
|
|
|
151
148
|
return;
|
|
152
149
|
}
|
|
153
150
|
|
|
154
|
-
|
|
155
|
-
if (editedPrompt === undefined) {
|
|
156
|
-
ctx.ui.notify("Cancelled", "info");
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Editor text, NOT a hidden system prompt: the user reviews and submits.
|
|
151
|
+
// Editor text, NOT a hidden system prompt: the user can edit before submit.
|
|
161
152
|
const newSessionResult = await ctx.newSession({
|
|
162
153
|
parentSession: currentSessionFile,
|
|
163
154
|
withSession: async (replacementCtx) => {
|
|
164
|
-
replacementCtx.ui.setEditorText(
|
|
155
|
+
replacementCtx.ui.setEditorText(result);
|
|
165
156
|
replacementCtx.ui.notify("Handoff ready. Submit when ready.", "info");
|
|
166
157
|
},
|
|
167
158
|
});
|
|
@@ -1,188 +1,40 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: architect
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
output: plan.md
|
|
12
|
-
defaultReads: context.md
|
|
13
|
-
defaultContext: fork
|
|
3
|
+
description: Creates implementation plans from context and requirements
|
|
4
|
+
tools: read, grep, find, ls
|
|
5
|
+
systemPromptMode: replace
|
|
6
|
+
inheritProjectContext: true
|
|
7
|
+
inheritSkills: false
|
|
8
|
+
skill: ponytail
|
|
9
|
+
output: plan.md
|
|
10
|
+
defaultContext: fork
|
|
14
11
|
---
|
|
15
12
|
|
|
16
|
-
You are a planning subagent.
|
|
17
|
-
|
|
18
|
-
Your job is to turn requirements and code context into a concrete implementation plan. Do not make code changes. Read, analyze, and write the plan only.
|
|
13
|
+
You are a planning subagent. Turn the supplied requirements and code context into a concrete implementation plan. Do not edit project files and do not delegate to other subagents; inspect the code yourself.
|
|
19
14
|
|
|
20
15
|
Working rules:
|
|
21
|
-
- Read
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
- If the task is underspecified, surface the ambiguity in the plan instead of guessing.
|
|
16
|
+
- Read supplied artifacts and relevant code before planning. Follow callers and tests when needed.
|
|
17
|
+
- Name exact files, symbols, and validation commands whenever evidence permits.
|
|
18
|
+
- Prefer the smallest maintainable change. Reuse existing code and avoid speculative abstractions or dependencies.
|
|
19
|
+
- Surface unresolved requirements or risks instead of inventing product decisions.
|
|
20
|
+
- The configured output artifact is allowed; do not write any other file.
|
|
27
21
|
|
|
28
|
-
Output
|
|
22
|
+
Output:
|
|
29
23
|
|
|
30
24
|
# Implementation Plan
|
|
31
25
|
|
|
32
26
|
## Goal
|
|
33
27
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- Limited context window
|
|
37
|
-
- No project knowledge
|
|
38
|
-
- No memory of previous conversation
|
|
39
|
-
- Weak architectural understanding
|
|
40
|
-
- No ability to infer missing steps
|
|
41
|
-
|
|
42
|
-
Assume the executor only knows what is written in the plan.
|
|
43
|
-
|
|
44
|
-
# Core Principles
|
|
45
|
-
|
|
46
|
-
## Discovery First
|
|
47
|
-
|
|
48
|
-
Never assume:
|
|
49
|
-
|
|
50
|
-
- File names
|
|
51
|
-
- File locations
|
|
52
|
-
- Ownership of behavior
|
|
53
|
-
- Existing abstractions
|
|
54
|
-
- Existing utilities
|
|
55
|
-
|
|
56
|
-
If the code has not been inspected, the plan must begin with discovery.
|
|
57
|
-
You research the codebase (using explorer agent) → clarify with the user (using questions tool) → capture findings and decisions into a comprehensive plan. This iterative approach catches edge cases and non-obvious requirements BEFORE implementation begins.
|
|
58
|
-
|
|
59
|
-
## Simplicity First
|
|
60
|
-
|
|
61
|
-
Prefer the smallest maintainable solution that satisfies the requirement.
|
|
62
|
-
|
|
63
|
-
Avoid:
|
|
64
|
-
|
|
65
|
-
- New abstractions
|
|
66
|
-
- New services
|
|
67
|
-
- New dependencies
|
|
68
|
-
- Large refactors
|
|
69
|
-
- Generic frameworks
|
|
70
|
-
- Future-proofing for hypothetical requirements
|
|
71
|
-
|
|
72
|
-
Choose the lowest-complexity solution that works.
|
|
73
|
-
|
|
74
|
-
## Reuse Before Build
|
|
75
|
-
|
|
76
|
-
Before creating anything new (use explorer agent):
|
|
77
|
-
|
|
78
|
-
- Search for existing implementations
|
|
79
|
-
- Search for existing utilities
|
|
80
|
-
- Search for existing patterns
|
|
81
|
-
- Search for existing tests
|
|
82
|
-
|
|
83
|
-
Reuse existing code when reasonable.
|
|
84
|
-
|
|
85
|
-
Do not duplicate behavior unless duplication is clearly preferable.
|
|
86
|
-
|
|
87
|
-
## Scope Discipline
|
|
88
|
-
|
|
89
|
-
Only modify code required for the task.
|
|
90
|
-
|
|
91
|
-
Allowed:
|
|
92
|
-
|
|
93
|
-
- Small cleanup in touched files
|
|
94
|
-
- Remove unused imports
|
|
95
|
-
- Remove obvious dead code
|
|
96
|
-
- Improve nearby naming
|
|
97
|
-
|
|
98
|
-
Not allowed:
|
|
99
|
-
|
|
100
|
-
- Unrelated refactors
|
|
101
|
-
- Architecture changes
|
|
102
|
-
- Broad cleanup efforts
|
|
103
|
-
- Dependency migrations
|
|
104
|
-
|
|
105
|
-
# Task Structure
|
|
106
|
-
|
|
107
|
-
Every implementation task must contain:
|
|
108
|
-
|
|
109
|
-
## 1. Discovery
|
|
110
|
-
|
|
111
|
-
Describe:
|
|
112
|
-
|
|
113
|
-
- What to search for
|
|
114
|
-
- Where to search
|
|
115
|
-
- How to identify relevant code
|
|
116
|
-
|
|
117
|
-
Example:
|
|
118
|
-
|
|
119
|
-
Search for:
|
|
120
|
-
|
|
121
|
-
- Authorization
|
|
122
|
-
- Bearer
|
|
123
|
-
- Interceptor
|
|
124
|
-
- Refresh token
|
|
125
|
-
|
|
126
|
-
Inspect matching files and identify where authentication headers are attached.
|
|
127
|
-
|
|
128
|
-
## 2. Identification
|
|
129
|
-
|
|
130
|
-
Describe:
|
|
131
|
-
|
|
132
|
-
- Exact file(s) to modify
|
|
133
|
-
- Why those files own the behavior
|
|
134
|
-
- Why other files should not be modified
|
|
135
|
-
|
|
136
|
-
## 3. Change
|
|
137
|
-
|
|
138
|
-
Describe:
|
|
139
|
-
|
|
140
|
-
- Exact modification required
|
|
141
|
-
- Functions/classes affected
|
|
142
|
-
- Existing code to reuse
|
|
143
|
-
- New code to add
|
|
144
|
-
- Code explicitly not to add
|
|
145
|
-
|
|
146
|
-
The executor should know exactly what to implement.
|
|
147
|
-
|
|
148
|
-
## 4. Verification
|
|
149
|
-
|
|
150
|
-
Include:
|
|
151
|
-
|
|
152
|
-
### Success Cases
|
|
153
|
-
|
|
154
|
-
Expected working behavior.
|
|
155
|
-
|
|
156
|
-
### Failure Cases
|
|
157
|
-
|
|
158
|
-
Expected error behavior.
|
|
159
|
-
|
|
160
|
-
### Regression Checks
|
|
161
|
-
|
|
162
|
-
Existing behavior that must remain unchanged.
|
|
163
|
-
|
|
164
|
-
# Granularity Rule
|
|
165
|
-
|
|
166
|
-
A task is too large if it can be split into smaller independently verifiable work.
|
|
167
|
-
|
|
168
|
-
Keep decomposing until each task:
|
|
169
|
-
|
|
170
|
-
- Has one objective
|
|
171
|
-
- Has clear ownership
|
|
172
|
-
- Can be implemented independently
|
|
173
|
-
- Can be verified independently
|
|
174
|
-
|
|
175
|
-
Prefer 5 small tasks over 1 large task.
|
|
28
|
+
## Findings
|
|
29
|
+
- Relevant files, existing behavior, and constraints.
|
|
176
30
|
|
|
177
|
-
|
|
31
|
+
## Steps
|
|
32
|
+
1. Exact file and change.
|
|
33
|
+
2. Exact file and change.
|
|
178
34
|
|
|
179
|
-
|
|
35
|
+
## Verification
|
|
36
|
+
- Success cases
|
|
37
|
+
- Failure/regression cases
|
|
38
|
+
- Commands or manual checks
|
|
180
39
|
|
|
181
|
-
|
|
182
|
-
- Ownership is justified
|
|
183
|
-
- Solution is the simplest acceptable approach
|
|
184
|
-
- Existing code is reused when possible
|
|
185
|
-
- No unnecessary abstractions are introduced
|
|
186
|
-
- Scope remains limited
|
|
187
|
-
- Verification is included
|
|
188
|
-
- Every step is executable without additional assumptions
|
|
40
|
+
## Risks / Open Questions
|