zames_pro 2.77.1 → 2.78.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.
- package/CHANGELOG.md +19 -0
- package/dist/context.js +15 -0
- package/dist/diagnostics.js +10 -4
- package/dist/fsutil.js +18 -6
- package/dist/hooks.js +12 -1
- package/dist/permissions.js +6 -2
- package/dist/statusline.js +13 -1
- package/package.json +2 -1
- package/skills/brainstorming/SKILL.md +37 -0
- package/skills/systematic-debugging/SKILL.md +56 -0
- package/skills/test-driven-development/SKILL.md +34 -0
- package/skills/verification-before-completion/SKILL.md +40 -0
- package/skills/writing-plans/SKILL.md +40 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.78.1] - 2026-10-18
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **permissions:** match a command rule against every plausible command key
|
|
15
|
+
- **hooks:** cap a PreToolUse denial so it cannot flood the tool result
|
|
16
|
+
- **diagnostics:** treat a zero-error watch banner as clean
|
|
17
|
+
- **statusline:** cap the hook output so a chatty command cannot flood the row
|
|
18
|
+
|
|
19
|
+
## [2.78.0] - 2026-10-18
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **skills:** ship the methodology skills inside the package
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **fsutil:** remove the temp file when an atomic write fails
|
|
28
|
+
|
|
10
29
|
## [2.77.1] - 2026-10-17
|
|
11
30
|
|
|
12
31
|
### Fixed
|
package/dist/context.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'fs/promises';
|
|
|
2
2
|
import { statSync } from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import os from 'os';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
5
6
|
// Built-in skills, always available unless a project/global skill with the
|
|
6
7
|
// same name overrides them (real files win). They give the agent proven,
|
|
7
8
|
// reusable workflows in the spirit of Claude Code / Codex. English only (the
|
|
@@ -362,6 +363,20 @@ export async function loadSkills(workdir) {
|
|
|
362
363
|
dir: path.join(os.homedir(), '.claude', 'skills'),
|
|
363
364
|
source: 'global',
|
|
364
365
|
});
|
|
366
|
+
// The skills SHIPPED with the package. In the installed layout they sit next
|
|
367
|
+
// to `dist/`; in a development checkout that path resolves INSIDE the repo
|
|
368
|
+
// (`src/../skills`), where the same skills also exist as files. The built-in
|
|
369
|
+
// copies (BUILTIN_SKILLS, appended below with an inline body) are the
|
|
370
|
+
// canonical ones — adding the files here would override them and break
|
|
371
|
+
// `/skill-name` expansion and the offline docs. So only add the directory when
|
|
372
|
+
// the module runs from the built `dist/` layout.
|
|
373
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
374
|
+
if (!moduleDir.endsWith(path.sep + 'src')) {
|
|
375
|
+
roots.push({
|
|
376
|
+
dir: path.join(moduleDir, '..', 'skills'),
|
|
377
|
+
source: 'global',
|
|
378
|
+
});
|
|
379
|
+
}
|
|
365
380
|
for (const root of roots) {
|
|
366
381
|
const files = await findSkillFiles(root.dir);
|
|
367
382
|
for (const f of files) {
|
package/dist/diagnostics.js
CHANGED
|
@@ -23,11 +23,17 @@ export function summarizeDiagnostics(raw, maxChars) {
|
|
|
23
23
|
const text = String(raw || '').trim();
|
|
24
24
|
if (!text)
|
|
25
25
|
return null;
|
|
26
|
-
// tsc prints nothing on success
|
|
27
|
-
//
|
|
28
|
-
|
|
26
|
+
// tsc prints nothing on success, but with `--watch` it prints a banner
|
|
27
|
+
// ("Found 0 errors. Watching for file changes.") that is longer than the
|
|
28
|
+
// old 40-char cutoff — so every edit in watch mode appended a pointless
|
|
29
|
+
// diagnostics block to the tool result. Treat a ZERO-count banner as clean
|
|
30
|
+
// however long it is; a non-zero count still falls through to the lines.
|
|
31
|
+
if (/^(?:found\s+)?0\s+errors?\b/im.test(text))
|
|
32
|
+
return null;
|
|
33
|
+
if (/^no errors?\.?$/im.test(text))
|
|
34
|
+
return null;
|
|
35
|
+
if (/^0 problems?\b/im.test(text))
|
|
29
36
|
return null;
|
|
30
|
-
}
|
|
31
37
|
// Keep only error-looking lines when we can recognise them, so a long build
|
|
32
38
|
// log collapses to the actual problems. Fall back to the whole output.
|
|
33
39
|
const lines = text.split(/\r?\n/);
|
package/dist/fsutil.js
CHANGED
|
@@ -20,15 +20,27 @@ export function writeFileAtomic(file, data) {
|
|
|
20
20
|
// fsync the file BEFORE rename: without it a power loss can leave the
|
|
21
21
|
// renamed name pointing at an empty/partial inode (rename itself is atomic,
|
|
22
22
|
// but the data blocks may not have reached the disk yet).
|
|
23
|
-
const fd = fs.openSync(tmp, 'w', 0o600);
|
|
24
23
|
try {
|
|
25
|
-
fs.
|
|
26
|
-
|
|
24
|
+
const fd = fs.openSync(tmp, 'w', 0o600);
|
|
25
|
+
try {
|
|
26
|
+
fs.writeFileSync(fd, data);
|
|
27
|
+
fs.fsyncSync(fd);
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
fs.closeSync(fd);
|
|
31
|
+
}
|
|
32
|
+
fs.renameSync(tmp, file);
|
|
27
33
|
}
|
|
28
|
-
|
|
29
|
-
|
|
34
|
+
catch (e) {
|
|
35
|
+
// A failed write/fsync/rename (full disk, EIO, a directory in the way)
|
|
36
|
+
// must not leave the temp file behind: every failure used to accumulate a
|
|
37
|
+
// stray `<file>.tmp-<pid>` next to the real file.
|
|
38
|
+
try {
|
|
39
|
+
fs.unlinkSync(tmp);
|
|
40
|
+
}
|
|
41
|
+
catch { }
|
|
42
|
+
throw e;
|
|
30
43
|
}
|
|
31
|
-
fs.renameSync(tmp, file);
|
|
32
44
|
// fsync the DIRECTORY so the rename entry itself is durable.
|
|
33
45
|
try {
|
|
34
46
|
const dirFd = fs.openSync(path.dirname(file), 'r');
|
package/dist/hooks.js
CHANGED
|
@@ -12,6 +12,9 @@ const LIFECYCLE_EVENTS = [
|
|
|
12
12
|
// tool call must not be held hostage by it.
|
|
13
13
|
const HOOK_TIMEOUT_MS = 10_000;
|
|
14
14
|
const HOOK_MAX_BUFFER = 1024 * 1024;
|
|
15
|
+
// A PreToolUse denial becomes the tool result the model reads, so it is capped
|
|
16
|
+
// like any other tool output.
|
|
17
|
+
const HOOK_DENIAL_CAP = 4000;
|
|
15
18
|
export function hooksConfigPath(workdir) {
|
|
16
19
|
return path.join(workdir, '.zames', 'hooks.json');
|
|
17
20
|
}
|
|
@@ -142,7 +145,15 @@ export async function runPreToolUse(hooks, tool, args, workdir) {
|
|
|
142
145
|
const r = await runHook(entry, 'PreToolUse', tool, args, workdir);
|
|
143
146
|
if (r.code !== 0) {
|
|
144
147
|
const reason = (r.stderr || r.stdout).trim();
|
|
145
|
-
|
|
148
|
+
// The denial text becomes a TOOL RESULT the model reads: a hook that
|
|
149
|
+
// dumps a log must not flood the context with one call.
|
|
150
|
+
const capped = reason.slice(0, HOOK_DENIAL_CAP);
|
|
151
|
+
const suffix = reason.length > HOOK_DENIAL_CAP
|
|
152
|
+
? '\n… [denial truncated: ' +
|
|
153
|
+
(reason.length - HOOK_DENIAL_CAP) +
|
|
154
|
+
' chars omitted]'
|
|
155
|
+
: '';
|
|
156
|
+
return capped + suffix || `hook exited with code ${r.code}`;
|
|
146
157
|
}
|
|
147
158
|
}
|
|
148
159
|
return null;
|
package/dist/permissions.js
CHANGED
|
@@ -63,6 +63,10 @@ function safeRegex(source) {
|
|
|
63
63
|
// A file path may arrive in any of these keys depending on the tool.
|
|
64
64
|
// We collect them all and match the path regex against ANY of them.
|
|
65
65
|
const PATH_KEYS = ['path', 'file_path', 'notebook_path', 'from', 'to'];
|
|
66
|
+
// The command may be named differently by an MCP tool (its JSON Schema picks
|
|
67
|
+
// the field name) or invented by the model. Checking every plausible key keeps
|
|
68
|
+
// a `command` rule from being bypassed just because the field is called `cmd`.
|
|
69
|
+
const COMMAND_KEYS = ['command', 'cmd', 'shell_command', 'script', 'argv'];
|
|
66
70
|
// Best-effort check whether a string looks like a filesystem path. Used for
|
|
67
71
|
// tools whose arguments are not covered by PATH_KEYS (MCP tools take arbitrary
|
|
68
72
|
// arg names from their JSON Schema), so a `path` rule is not silently
|
|
@@ -106,7 +110,7 @@ export function decidePermission(policy, tool, args) {
|
|
|
106
110
|
if (!policy)
|
|
107
111
|
return { action: 'allow', reason: 'no policy' };
|
|
108
112
|
const a = (args ?? {});
|
|
109
|
-
const
|
|
113
|
+
const commands = COMMAND_KEYS.filter((k) => typeof a[k] === 'string').map((k) => a[k]);
|
|
110
114
|
const paths = collectPathCandidates(tool, args);
|
|
111
115
|
for (const r of policy.rules) {
|
|
112
116
|
if (r.tool) {
|
|
@@ -116,7 +120,7 @@ export function decidePermission(policy, tool, args) {
|
|
|
116
120
|
}
|
|
117
121
|
if (r.command) {
|
|
118
122
|
const re = safeRegex(r.command);
|
|
119
|
-
if (!re || !
|
|
123
|
+
if (!re || !commands.some((c) => re.test(c)))
|
|
120
124
|
continue;
|
|
121
125
|
}
|
|
122
126
|
if (r.path) {
|
package/dist/statusline.js
CHANGED
|
@@ -26,6 +26,10 @@ export function runStatusLineCommand(workdir, command, info, timeoutMs = 4000) {
|
|
|
26
26
|
const first = String(out).split(/\r?\n/)[0].trim();
|
|
27
27
|
resolve(first);
|
|
28
28
|
};
|
|
29
|
+
// Only the FIRST LINE is shown, so there is no reason to keep collecting a
|
|
30
|
+
// big blob: a chatty command (a JSON dump, `ls -R`) would grow `out`
|
|
31
|
+
// unboundedly and the status row then repainted megabytes. Stop appending
|
|
32
|
+
// once a short line is buffered (the rest of that line does not matter).
|
|
29
33
|
const timer = setTimeout(() => {
|
|
30
34
|
try {
|
|
31
35
|
child.kill();
|
|
@@ -33,8 +37,16 @@ export function runStatusLineCommand(workdir, command, info, timeoutMs = 4000) {
|
|
|
33
37
|
catch { }
|
|
34
38
|
finish();
|
|
35
39
|
}, timeoutMs);
|
|
40
|
+
// A status line is a short badge: cap what is buffered so a chatty
|
|
41
|
+
// command cannot grow it (and the rendered row) without bound.
|
|
42
|
+
const MAX_OUT = 400;
|
|
36
43
|
child.stdout?.on('data', (c) => {
|
|
37
|
-
out
|
|
44
|
+
if (out.length >= MAX_OUT)
|
|
45
|
+
return;
|
|
46
|
+
// A chunk can be huge (one 64 KB read); clip it to the remaining space
|
|
47
|
+
// so the buffer never exceeds MAX_OUT. An unbounded `out` made the
|
|
48
|
+
// status row repaint megabytes on every tick.
|
|
49
|
+
out += c.toString().slice(0, MAX_OUT - out.length);
|
|
38
50
|
});
|
|
39
51
|
child.on('error', () => {
|
|
40
52
|
clearTimeout(timer);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zames_pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.78.1",
|
|
4
4
|
"description": "Terminal coding agent that drives a real browser to chat with DeepSeek, Qwen and other web LLMs — no API key required. Reads and edits files, runs commands, commits to git, like Claude Code / Codex CLI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"dist",
|
|
11
|
+
"skills",
|
|
11
12
|
"scripts/postinstall.mjs",
|
|
12
13
|
"README.md",
|
|
13
14
|
"CHANGELOG.md",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: brainstorming
|
|
3
|
+
description: Use when the operator's request is a GOAL, not a spec — "add X", "make it better", "integrate Y" — and it is unclear what "done" means. Draws out the intent and plays it back before building.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Brainstorming
|
|
7
|
+
|
|
8
|
+
When a request is a goal ("add superpowers", "make scrolling feel right"),
|
|
9
|
+
building immediately produces the wrong thing fast. First make the intent
|
|
10
|
+
concrete.
|
|
11
|
+
|
|
12
|
+
## What to pin down
|
|
13
|
+
|
|
14
|
+
- **The real outcome.** What should be TRUE after this that is not true now?
|
|
15
|
+
Not the feature name — the result the operator wants to see.
|
|
16
|
+
- **The acceptance check.** How will the operator know it works? "I open X
|
|
17
|
+
and Y happens." If neither of you can state this, the task is not ready.
|
|
18
|
+
- **Scope edges.** What is explicitly NOT part of this (the operator often
|
|
19
|
+
has an opinion, e.g. "no button", "don't touch the existing code").
|
|
20
|
+
- **The alternatives you considered** and why you chose one — a decision is
|
|
21
|
+
cheaper to correct before it is built.
|
|
22
|
+
|
|
23
|
+
## The zames constraint (IMPORTANT)
|
|
24
|
+
|
|
25
|
+
zames has NO conversational turn: the operator sees only your final `respond`
|
|
26
|
+
and your tool calls, and a task is expected to make PROGRESS, not to ask
|
|
27
|
+
questions and wait. So do the brainstorming yourself and return it as a
|
|
28
|
+
SHORT, concrete proposal in ONE `respond`, instead of an interview:
|
|
29
|
+
|
|
30
|
+
- state the goal as you understood it;
|
|
31
|
+
- state your assumptions and the choices you made by default;
|
|
32
|
+
- state what you will NOT do;
|
|
33
|
+
- then either (a) proceed if the request was clearly actionable, or (b) stop
|
|
34
|
+
and ask the ONE decision you genuinely cannot make alone.
|
|
35
|
+
|
|
36
|
+
For a request that is already precise ("read file X", "rename Y"), skip this
|
|
37
|
+
entirely — brainstorming a one-line task wastes the operator's time.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: systematic-debugging
|
|
3
|
+
description: Use when something is broken and the cause is not obvious — a failing test, a wrong result, a hang, a crash. Four phases: reproduce, isolate, hypothesize, fix. Prevents guessing.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Systematic debugging
|
|
7
|
+
|
|
8
|
+
Do NOT start by editing code. Work through the four phases in order; each
|
|
9
|
+
phase must produce an artifact you can point at.
|
|
10
|
+
|
|
11
|
+
## Phase 1 — Reproduce (no fix yet)
|
|
12
|
+
|
|
13
|
+
Get a command that FAILS the same way the operator describes it.
|
|
14
|
+
|
|
15
|
+
- Run it. Copy the exact error/output into your reasoning.
|
|
16
|
+
- If it passes, you do not have the bug yet: narrow the input until it fails,
|
|
17
|
+
or ask which steps differ.
|
|
18
|
+
- A bug you cannot reproduce is a bug you cannot verify you fixed.
|
|
19
|
+
|
|
20
|
+
## Phase 2 — Isolate (bisect, do not read blindly)
|
|
21
|
+
|
|
22
|
+
Shrink the failure to the smallest unit that still fails.
|
|
23
|
+
|
|
24
|
+
- Comment out / stub / binary-search the input space; each step doubles the
|
|
25
|
+
information you have.
|
|
26
|
+
- Read the code that the failing path ACTUALLY executes — not the code you
|
|
27
|
+
remember writing.
|
|
28
|
+
- Check the obvious causes FIRST: wrong file, stale build, cached state,
|
|
29
|
+
different config, an earlier error swallowed by a catch.
|
|
30
|
+
|
|
31
|
+
## Phase 3 — Hypothesize (one at a time)
|
|
32
|
+
|
|
33
|
+
State the hypothesis in one sentence and predict what you should see if it is
|
|
34
|
+
right. Then test exactly that.
|
|
35
|
+
|
|
36
|
+
- One change at a time; two changes at once prove nothing.
|
|
37
|
+
- Prefer an observation (a log, a probe, a test assertion) over an edit.
|
|
38
|
+
- If the prediction is wrong, the hypothesis is wrong — discard it, do not
|
|
39
|
+
"adjust" it to fit.
|
|
40
|
+
|
|
41
|
+
## Phase 4 — Fix and verify
|
|
42
|
+
|
|
43
|
+
- Fix the CAUSE, not the symptom. A guard added to hide a wrong value leaves
|
|
44
|
+
the wrong value in place.
|
|
45
|
+
- Add a regression test that fails WITHOUT the fix and passes WITH it.
|
|
46
|
+
- Re-run the Phase 1 reproduction verbatim.
|
|
47
|
+
- Look for the same bug elsewhere (the same wrong assumption is usually not
|
|
48
|
+
in one place only).
|
|
49
|
+
|
|
50
|
+
## Anti-patterns
|
|
51
|
+
|
|
52
|
+
- Editing before reproducing.
|
|
53
|
+
- "It works now" without knowing what changed.
|
|
54
|
+
- Adding a try/catch or a null-check to silence an error you do not understand.
|
|
55
|
+
- Trusting a green test you wrote against the FIXED code without ever seeing
|
|
56
|
+
it red.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: test-driven-development
|
|
3
|
+
description: Use when writing or changing behaviour that can be tested — a bug fix, a new function, a parser, a formatter. Write the failing test first, watch it fail, then make it pass. Prevents tests that only pass because they were written against the finished code.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Test-driven development
|
|
7
|
+
|
|
8
|
+
RED → GREEN → REFACTOR. The order is the point, not a formality.
|
|
9
|
+
|
|
10
|
+
## The cycle
|
|
11
|
+
|
|
12
|
+
1. **RED** — write the smallest test that expresses the new behaviour and
|
|
13
|
+
RUN it. It must FAIL, and fail for the RIGHT reason ("not implemented",
|
|
14
|
+
not a typo or a missing import). A test you never saw fail proves nothing.
|
|
15
|
+
2. **GREEN** — write the MINIMUM code that makes it pass. Do not add the
|
|
16
|
+
next feature "while you are there"; that code is untested.
|
|
17
|
+
3. **REFACTOR** — clean up with the test green, and re-run it.
|
|
18
|
+
|
|
19
|
+
## What makes a test good
|
|
20
|
+
|
|
21
|
+
- It asserts BEHAVIOUR (input → output), not implementation detail.
|
|
22
|
+
- It fails when the behaviour is wrong — verify this by breaking the code
|
|
23
|
+
on purpose once, if you are unsure.
|
|
24
|
+
- It is deterministic: no sleeps, no network, no dependence on wall-clock
|
|
25
|
+
time or on the order other tests ran in.
|
|
26
|
+
- One reason to fail, with a message that says what broke.
|
|
27
|
+
|
|
28
|
+
## Rules in this project
|
|
29
|
+
|
|
30
|
+
- A bug fix ALWAYS gets a regression test that reproduces the original bug.
|
|
31
|
+
- When you fix a test to make it pass, ask whether the TEST was wrong or the
|
|
32
|
+
CODE was wrong; do not just move the assertion.
|
|
33
|
+
- If the code was written before its test, delete and rewrite it driven by
|
|
34
|
+
the test — otherwise the test is shaped around the implementation.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verification-before-completion
|
|
3
|
+
description: Use BEFORE declaring a task done or reporting success — a feature, a bug fix, a file you generated. Forces you to run the verification you promised instead of trusting that the code looks right.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Verification before completion
|
|
7
|
+
|
|
8
|
+
"It should work" is not done. Before you report success, produce EVIDENCE
|
|
9
|
+
from the real artifact — not from your intent.
|
|
10
|
+
|
|
11
|
+
## The checklist
|
|
12
|
+
|
|
13
|
+
1. **Re-read the original request.** Every requirement, including the small
|
|
14
|
+
ones in a parenthetical. List them; tick each one against something you
|
|
15
|
+
can show.
|
|
16
|
+
2. **Run the thing.** For code: build/typecheck/tests. For a script: run it.
|
|
17
|
+
For a generated file: open/parse it and confirm it is complete, not
|
|
18
|
+
truncated. For a UI: the real screen, not the diff.
|
|
19
|
+
3. **Check the WHOLE file.** A generated file (HTML/JSON/config) is a common
|
|
20
|
+
silent failure: the head is there, the body/tail is missing or a
|
|
21
|
+
placeholder. Confirm the closing structure exists and the file runs.
|
|
22
|
+
4. **Re-read your own diff** as a reviewer: leftover debug output, dead code,
|
|
23
|
+
a commented-out block, a wrong path, a test that asserts nothing.
|
|
24
|
+
5. **State the evidence** in the final report — the exact command and its
|
|
25
|
+
result — so the operator can reproduce it. "Tests pass" without the
|
|
26
|
+
command is not evidence.
|
|
27
|
+
|
|
28
|
+
## The two failure modes this prevents
|
|
29
|
+
|
|
30
|
+
- **Partial delivery**: a game file with a scene but no game loop; a handler
|
|
31
|
+
that handles one case; a migration that covers one table. The operator runs
|
|
32
|
+
it, it is broken, and trust drops.
|
|
33
|
+
- **Phantom fix**: the code changed, but the original reproduction was never
|
|
34
|
+
re-run, so nothing proves the bug is gone.
|
|
35
|
+
|
|
36
|
+
## Rule
|
|
37
|
+
|
|
38
|
+
If you cannot run the verification (no test, no runnable entry point), SAY SO
|
|
39
|
+
explicitly and describe exactly what you did instead. Never report "done"
|
|
40
|
+
while implying a verification you did not perform.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: writing-plans
|
|
3
|
+
description: Use when a task is large enough to need more than one or two tool calls — a multi-file change, a new feature, a refactor. Breaks the work into small ordered steps with exact paths and a verification for each.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Writing plans
|
|
7
|
+
|
|
8
|
+
A plan is a sequence of steps a careful engineer with NO project context
|
|
9
|
+
could follow. If a step needs knowledge you have but did not write down, the
|
|
10
|
+
plan is incomplete.
|
|
11
|
+
|
|
12
|
+
## Shape of a good plan
|
|
13
|
+
|
|
14
|
+
Each step is 2–5 minutes of work and names:
|
|
15
|
+
|
|
16
|
+
- the EXACT file path(s) it touches;
|
|
17
|
+
- what changes (the concrete edit, not "update the parser");
|
|
18
|
+
- how to verify it (the command, the test name, the expected output).
|
|
19
|
+
|
|
20
|
+
Order matters: put the step that DE-RISKS the task first (a spike, a failing
|
|
21
|
+
test, a proof that the API works). Do not build three layers on an unverified
|
|
22
|
+
assumption.
|
|
23
|
+
|
|
24
|
+
## Rules
|
|
25
|
+
|
|
26
|
+
- One concern per step. "Add the flag AND refactor the caller" is two steps.
|
|
27
|
+
- Prefer a step that FAILS LOUDLY over a step that silently half-works.
|
|
28
|
+
- Every behavioural change gets a test step (see test-driven-development);
|
|
29
|
+
write the failing test BEFORE the implementation step that satisfies it.
|
|
30
|
+
- If you discover mid-plan that a step was wrong, STOP and revise the plan —
|
|
31
|
+
do not improvise the rest of it.
|
|
32
|
+
- Keep the plan short enough to hold in your head; a 30-step plan is a sign
|
|
33
|
+
the task should be split.
|
|
34
|
+
|
|
35
|
+
## In zames
|
|
36
|
+
|
|
37
|
+
Because there is no separate plan file unless the operator asks for one, the
|
|
38
|
+
plan lives in your reasoning and, for a long task, in a TodoWrite list — one
|
|
39
|
+
entry per step, status updated as you go. Do not narrate the plan to the
|
|
40
|
+
operator between tool calls; the todo list IS the visible plan.
|