flanders 0.0.2 → 0.2.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/lib/ai/AiRunner.d.ts +24 -0
- package/lib/ai/AiRunner.js +100 -0
- package/lib/ai/AiSession.d.ts +32 -0
- package/lib/ai/AiSession.js +143 -0
- package/lib/ai/ClaudeAdapter.d.ts +12 -0
- package/lib/ai/ClaudeAdapter.js +345 -0
- package/lib/ai/CodexAdapter.d.ts +13 -0
- package/lib/ai/CodexAdapter.js +354 -0
- package/lib/ai/ToolAdapter.d.ts +52 -0
- package/lib/ai/ToolAdapter.js +2 -0
- package/lib/cli.js +56 -35
- package/lib/commands/Implement.d.ts +22 -8
- package/lib/commands/Implement.js +296 -171
- package/lib/commands/Install.d.ts +31 -3
- package/lib/commands/Install.js +649 -49
- package/lib/commands/InstallAvailabilityCheck.d.ts +8 -0
- package/lib/commands/InstallAvailabilityCheck.js +45 -0
- package/lib/commands/InstallModelProbe.d.ts +2 -0
- package/lib/commands/InstallModelProbe.js +74 -0
- package/lib/contexts.d.ts +5 -4
- package/lib/index.d.ts +5 -3
- package/lib/{PlanFile.d.ts → plan/PlanFile.d.ts} +8 -1
- package/lib/{PlanFile.js → plan/PlanFile.js} +44 -5
- package/lib/prompts/prompts.d.ts +48 -0
- package/lib/prompts/prompts.js +328 -0
- package/lib/prompts/skills.d.ts +3 -0
- package/lib/prompts/skills.js +463 -0
- package/lib/{Git.d.ts → system/Git.d.ts} +4 -2
- package/lib/{Git.js → system/Git.js} +63 -3
- package/lib/{ScriptRunner.d.ts → system/ScriptRunner.d.ts} +1 -1
- package/lib/system/ShellScriptContext.d.ts +36 -0
- package/lib/system/ShellScriptContext.js +70 -0
- package/lib/{fsUtils.d.ts → system/fsUtils.d.ts} +1 -1
- package/lib/{wait.d.ts → system/wait.d.ts} +1 -1
- package/lib/ui/BottomBlock.d.ts +9 -1
- package/lib/ui/BottomBlock.js +30 -14
- package/lib/ui/PromptHelper.d.ts +12 -0
- package/lib/ui/PromptHelper.js +32 -0
- package/lib/ui/TerminalSizeSource.d.ts +19 -0
- package/lib/ui/TerminalSizeSource.js +77 -0
- package/lib/ui/formatters.d.ts +14 -0
- package/lib/ui/formatters.js +65 -2
- package/lib/workspace/FlandersConfig.d.ts +23 -0
- package/lib/workspace/FlandersConfig.js +90 -0
- package/lib/workspace/SpecDiscovery.d.ts +12 -0
- package/lib/workspace/SpecDiscovery.js +40 -0
- package/lib/{Workspace.d.ts → workspace/Workspace.d.ts} +10 -2
- package/lib/{Workspace.js → workspace/Workspace.js} +52 -6
- package/package.json +3 -2
- package/lib/Claude.d.ts +0 -129
- package/lib/Claude.js +0 -387
- package/lib/ClaudeSession.d.ts +0 -34
- package/lib/ClaudeSession.js +0 -292
- package/lib/prompts.d.ts +0 -18
- package/lib/prompts.js +0 -221
- package/lib/skills.d.ts +0 -3
- package/lib/skills.js +0 -256
- /package/lib/{ScriptRunner.js → system/ScriptRunner.js} +0 -0
- /package/lib/{fsUtils.js → system/fsUtils.js} +0 -0
- /package/lib/{wait.js → system/wait.js} +0 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.workSkillBody = exports.specSkillBody = exports.planSkillBody = void 0;
|
|
4
|
+
const PlanFile_1 = require("../plan/PlanFile");
|
|
5
|
+
const prompts_1 = require("./prompts");
|
|
6
|
+
exports.planSkillBody = `---
|
|
7
|
+
description: Produce a contract-aware work plan inside the project's plans/ folder.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
You are the /flanders-plan skill. Your sole deliverable is exactly one markdown plan file inside the project's plans/ folder. You must not write, modify, or delete any source code or any file outside plans/.
|
|
11
|
+
|
|
12
|
+
## Input resolution
|
|
13
|
+
|
|
14
|
+
The user invokes you as: /flanders-plan [<data>]
|
|
15
|
+
|
|
16
|
+
- If <data> is omitted, take the user's natural-language request from the conversation.
|
|
17
|
+
- If <data> is supplied and resolves to an existing file path, read the file's content and use it as input.
|
|
18
|
+
- If <data> is supplied and does not resolve to an existing file, use the value verbatim as inline input.
|
|
19
|
+
|
|
20
|
+
## Procedure
|
|
21
|
+
|
|
22
|
+
1. Resolve the input from the invocation rule above.
|
|
23
|
+
2. Discover every directory named \`.docs\` across the whole project tree at every depth, excluding every path the project's git ignore rules exclude (for example by enumerating with \`git ls-files --cached --others --exclude-standard\` — which lists tracked files plus untracked-but-not-ignored files — and dropping any candidate that sits under a git-ignored path, for example via \`git check-ignore\`); the files under each \`.docs/contracts\` subfolder form the canonical contracts listing and the files under each \`.docs/rules\` subfolder form the canonical rules listing; the files under each \`.docs/flanders\` subfolder form the behavior-rule listing, treating every file inside a \`.docs/flanders\` folder at any depth as a behavior rule; each file is identified by its namespace — its path relative to the project root, which for nested \`.docs\` folders includes the directories above the \`.docs\` folder, so files sharing a leaf filename in different \`.docs\` folders stay distinct.
|
|
24
|
+
|
|
25
|
+
**Behavior rules.** Before persisting the plan file, read every behavior rule whose \`.docs/flanders\` scope encloses the plan file you are about to write — the project-root \`.docs\` folder and any other \`.docs\` folder whose scope encloses the \`plans/\` target — and honor all of them. Behavior rules govern how you name and organize the plan file you author; an in-scope behavior rule is binding on that work, not advisory, and applies whether or not the request mentions it. This adds no new task-line link obligation: the plan-file format and its contract and rule links are unchanged.
|
|
26
|
+
3. **Clarification phase.** Ask the user clarifying questions only when the question targets one of three things: an implementation choice in the code the tasks will produce that the request does not specify, or a task-scope ambiguity you cannot reasonably infer from the request or from the canonical contracts and rules, or a load-bearing runtime-behavior premise the plan would otherwise have to assert without backing (see the plan content rules below). Any other doubt is resolved silently: pick the most reasonable default and proceed, documenting the choice in the relevant task's description when it is plan-local and load-bearing. Permitted questions are asked sequentially — one question per turn, multiple-choice preferred when the answer space is bounded.
|
|
27
|
+
|
|
28
|
+
When the doubt is about how the code should be implemented, resolve it through one of two outcomes:
|
|
29
|
+
- **Cross-cutting convention** — the answer would apply to all future code of the same kind in the project and belongs in a \`.docs/rules\` folder. Surface the gap to the user and recommend creating the rule via /flanders-spec before the plan is drafted, instead of silently baking the decision into the plan. The user may explicitly elect to treat the decision as plan-local for this run; in that case it follows the plan-local outcome below.
|
|
30
|
+
- **Plan-local implementation choice** — the answer is specific to the requested work and does not generalize. The chosen answer is embedded in the relevant task's description and acceptance criteria, and is never promoted to a rule.
|
|
31
|
+
|
|
32
|
+
The skill itself never writes to any \`.docs/rules\` or \`.docs/contracts\` folder. Rule creation, when the user elects it, happens through /flanders-spec as a separate, user-initiated act.
|
|
33
|
+
4. **Drafting phase.** Once the clarification phase is complete, persist the plan file directly without presenting a layout summary, a section-by-section draft, or any other pre-write approval step. The user reviews the written plan file after the fact.
|
|
34
|
+
5. Persist exactly one markdown file inside the project's plans/ folder. The filename must be descriptive of the plan's subject.
|
|
35
|
+
6. Upon successful completion, print the summary described in the Summary section below. If the plan cannot be made compliant with the Plan content rules, do not declare complete: surface the issue along with the plan file path to the user in chat.
|
|
36
|
+
|
|
37
|
+
## Plan file format
|
|
38
|
+
|
|
39
|
+
The plan file must follow these rules exactly:
|
|
40
|
+
|
|
41
|
+
### Task lines
|
|
42
|
+
|
|
43
|
+
A task is a markdown list item that carries a checkbox and a metrics object at the start of its content. The full shape of a task line is:
|
|
44
|
+
|
|
45
|
+
- [ ]{"it":0,"ot":0,"t":0} 1.1 TITLE
|
|
46
|
+
|
|
47
|
+
with the following pieces, in this exact order and spacing:
|
|
48
|
+
|
|
49
|
+
- A markdown list marker — one of \`-\`, \`*\`, or \`+\` — followed by at least one space. The line may be indented by leading whitespace before the marker. This marker is mandatory: a line that begins with the checkbox but no preceding list marker is not a task line and is not detected as one.
|
|
50
|
+
- A checkbox, in one of two states:
|
|
51
|
+
- \`[ ]\` — open (not yet implemented).
|
|
52
|
+
- \`[x]\` — done (already implemented).
|
|
53
|
+
- Immediately after the closing \`]\`, with no whitespace between them, the metrics object (a strict JSON literal — see Task metrics below).
|
|
54
|
+
- A single space after the closing \`}\`.
|
|
55
|
+
- The task number (see Numbering).
|
|
56
|
+
- A single space.
|
|
57
|
+
- The task title.
|
|
58
|
+
|
|
59
|
+
No malformed variants such as \`[]\`, \`[ x]\`, or \`[X ]\` are permitted. All new tasks are written as open (\`[ ]\`).
|
|
60
|
+
|
|
61
|
+
### Task metrics
|
|
62
|
+
|
|
63
|
+
Every leaf task line carries a metrics object \`{"it":0,"ot":0,"t":0}\` at generation time. This is a strict JSON literal with three integer fields: \`it\` (input tokens), \`ot\` (output tokens), and \`t\` (time in seconds), all set to zero for new tasks. The object is placed immediately after the checkbox with no whitespace between \`]\` and \`{\`, and one space between the closing \`}\` and the task number.
|
|
64
|
+
|
|
65
|
+
### Hierarchy and sub-tasks
|
|
66
|
+
|
|
67
|
+
- A leaf task (no sub-tasks) carries a checkbox.
|
|
68
|
+
- A parent task (has sub-tasks with their own checkboxes) does NOT carry its own checkbox. It appears as a heading or list item with a title and description, but no checkbox.
|
|
69
|
+
|
|
70
|
+
Checkboxes appear only on the smallest atomic units of work, never on a unit that aggregates other checkboxed units.
|
|
71
|
+
|
|
72
|
+
### Numbering
|
|
73
|
+
|
|
74
|
+
Tasks are numbered hierarchically:
|
|
75
|
+
- Top-level tasks: 1, 2, 3, ...
|
|
76
|
+
- Sub-tasks of task 2: 2.1, 2.2, 2.3, ...
|
|
77
|
+
- Deeper levels follow the same dotted convention.
|
|
78
|
+
|
|
79
|
+
The numbering is part of the visible task identifier.
|
|
80
|
+
|
|
81
|
+
### Ordering
|
|
82
|
+
|
|
83
|
+
Tasks are written in the order they must be implemented, accounting for dependencies. A task that depends on another must appear after the task it depends on.
|
|
84
|
+
|
|
85
|
+
### Plan content rules
|
|
86
|
+
|
|
87
|
+
- The persisted plan is free of placeholders, contradictions with existing contracts or rules, ambiguous task wording, missing acceptance criteria on leaf tasks, and missing contract or rule links on leaf tasks.
|
|
88
|
+
- The persisted plan is internally self-consistent: its narrative — context, rationale, and any explanatory prose — does not contradict the obligations, verification approach, or any other statement made in its task bodies, and no task contradicts that narrative. Where the prose describes how something is tested or built, it matches what the tasks prescribe.
|
|
89
|
+
- Every task that creates, modifies, or removes code is grounded in the real state of the code it builds on — the current source, plus the changes any earlier task it depends on prescribes. Before writing the task, establish that state: read the current source for code that already exists, and consult the producing earlier task for code an earlier task in the plan creates or changes. Changing what the code does is the task's purpose and is allowed; what a task may not do is misstate the code it builds on — naming structure or behavior that code does not and will not have, or removing or rewriting code on a mistaken account of what it is for.
|
|
90
|
+
- No task asserts, as settled fact, a runtime- or observable-behavior premise that its approach depends on and that cannot be confirmed by reading the source. Such a premise is either backed — by an existing contract or rule, an existing test, or a preceding task in the plan that establishes it executably — or escalated to the user during the clarification phase. A task does not remove, weaken, or replace existing code on the strength of an unbacked, unescalated runtime-behavior premise.
|
|
91
|
+
- The plan only references contracts and rules that exist in the canonical state captured at invocation.
|
|
92
|
+
- Implementation decisions resolved during the clarification phase and classified as plan-local are embedded in the relevant task's description and acceptance criteria, and are never promoted to a rule.
|
|
93
|
+
- Tasks are ordered top-to-bottom in the order they must be implemented, accounting for dependencies. A task that depends on another must appear after the task it depends on.
|
|
94
|
+
- Write each leaf task with a detailed description and explicit acceptance criteria — the conditions that must be true once the task is implemented for it to be considered complete.
|
|
95
|
+
- Every leaf task carries the initial metrics object \`{"it":0,"ot":0,"t":0}\` literally. Done tasks generated by \`/flanders-plan\` follow the same shape with the same zero values.
|
|
96
|
+
- Choose a granularity that is neither too broad nor too narrow. Tasks must be small enough for a single AI invocation without excessive tokens, but large enough that splitting further would create artificial fragmentation. When in doubt, subdivide.
|
|
97
|
+
- For every leaf task, link the relevant contract file or files by their listed relative path. When the relevant obligation lives in a specific section or line range, reference that section or line range as well.
|
|
98
|
+
- For every leaf task, link the relevant rule file or files by their listed relative path. The planner MUST read every rule file it determines is relevant to the request before drafting the plan; reading the relevant rules is not optional. When a rule's enforcement is bound to a specific scope, reference that scope alongside the file path.
|
|
99
|
+
- Rule selection per task is scope-driven, not topic-driven. Before listing the rule links for a leaf task, walk the rules listing and ask: which rule namespaces are in scope for the work this task actually performs? Use the namespace as the scope hint. Heuristics: a task that modifies or adds tests must link every applicable rule under a \`testing/\` subfolder; a task that creates or modifies anything with timers, listeners, controllers, child processes, or other async lifecycle must link every applicable rule under a \`disposables/\` subfolder; a task that changes terminal UI or live-region output must link every applicable rule under a \`ui/\` subfolder. Walk every namespace whose scope could plausibly apply, and pick every file whose obligation could be triggered by the task. Under-linking is costly: the downstream implementor is FAILed by the adversarial reviewer for any global rule that should have applied but was not applied, so when in doubt, link rather than omit.
|
|
100
|
+
- Tasks are numbered hierarchically (1, 1.1, 1.2, 2, 2.1, ...) per the Plan file format section above.
|
|
101
|
+
- No task may describe work that creates, modifies, deletes, or renames files inside any \`.docs/contracts\` folder, any \`.docs/rules\` folder, or the \`plans/\` folder (the bounded checkbox/metrics update that the implement command holds is not available to tasks).
|
|
102
|
+
- Never produce a plan that violates any contract or rule on the canonical lists.
|
|
103
|
+
|
|
104
|
+
## Post-write verification
|
|
105
|
+
|
|
106
|
+
After writing the plan file, re-read it and verify:
|
|
107
|
+
- The file exists at the expected path inside plans/ and is non-empty.
|
|
108
|
+
- Every task line follows the checkbox shape defined above (every list item carrying a task identifier has a valid \`[ ]\` or \`[x]\` checkbox; no malformed variants).
|
|
109
|
+
- Every leaf task line carries a metrics object literally equal to \`{"it":0,"ot":0,"t":0}\`. The verification re-parses each metrics object with strict JSON, so the check is byte-exact — no extra spaces, no reordered keys, no trailing commas.
|
|
110
|
+
- At least one task line was produced.
|
|
111
|
+
|
|
112
|
+
If any check fails, fix the file and re-verify instead of leaving a malformed plan on disk.
|
|
113
|
+
|
|
114
|
+
## Final validation
|
|
115
|
+
|
|
116
|
+
Before declaring this skill complete, run a final validator over the plan file. The validator is the gate — only declare complete when it returns PASS.
|
|
117
|
+
|
|
118
|
+
What a passing gate certifies: a pass certifies that the file(s) you wrote or updated in this run satisfy the validator's checks and do not contradict the corpus the validator inspected. It does not certify that the entire corpus is mutually consistent independent of this run's files — whole-corpus consistency is not re-verified on every run, and a passing gate is not a proof of it. Report a pass as a statement about this run's own output, never as a statement that the whole spec is globally sound.
|
|
119
|
+
|
|
120
|
+
### Validator host
|
|
121
|
+
|
|
122
|
+
Launch the validator as a fresh subagent via the AI tool's subagent mechanism, in a session that does not share context with this drafting session. The fresh session is load-bearing — it forces the validator to re-derive its judgments from the file on disk rather than from this session's confirmation bias.
|
|
123
|
+
|
|
124
|
+
The subagent mechanism is tool-specific. In Claude Code, the host spawns the validator through the Agent tool. In Codex CLI, the host spawns it through whatever Codex documents as its subagent surface at the time of the run.
|
|
125
|
+
|
|
126
|
+
You may fall back to an inline pass (running the validator in this same session) only when the subagent mechanism is unavailable in the current environment, or when a subagent invocation returns an unrecoverable error (spawn failure, transport error, environment refusal). Inline fallback for ergonomic reasons — the plan looks small, tokens feel tight, you are confident — is forbidden. When you take the inline path, state in chat that you are falling back and name the concrete reason; a silent fallback is a violation. The validator is read-only on the project and does not run git mutations.
|
|
127
|
+
|
|
128
|
+
### Validator inputs
|
|
129
|
+
|
|
130
|
+
Pass the validator:
|
|
131
|
+
- The absolute path to the plan file you just wrote.
|
|
132
|
+
- The canonical contract listing captured in step 2 of the procedure.
|
|
133
|
+
- The canonical rule listing captured in step 2 of the procedure.
|
|
134
|
+
- The number of leaf task lines you generated (a single non-negative integer).
|
|
135
|
+
- The verbatim text of the five check categories below. The host MUST inline these categories in the validator's prompt — it does not just point the validator at the rule file by path, and it does not rely on the validator discovering them by transitive reading of the skill's contract.
|
|
136
|
+
|
|
137
|
+
The validator reads the plan file in full, plus any contract or rule from the listings it judges relevant to forming its verdict.
|
|
138
|
+
|
|
139
|
+
Additionally, the validator reads the on-disk source files the plan's tasks build on — not only the plan text and the specs — and audits each code-touching task against its baseline: the current source, plus the changes earlier tasks in the plan it depends on prescribe. This is what lets categories 4 and 5 catch a task that misstates the code it builds on. Reading source is read-only and does not relax the validator's read-only discipline.
|
|
140
|
+
|
|
141
|
+
### Validator checks
|
|
142
|
+
|
|
143
|
+
Five categories, all mandatory; failure in any one is a FAIL. Each category is audited independently and violations are enumerated exhaustively — encountering a violation in one category does not exempt the validator from completing the remaining four.
|
|
144
|
+
|
|
145
|
+
1. Format and shape. Every task line conforms to the Plan file format section above. Every line the plan presents as a task must match the canonical task-line recognizer regex \`/${PlanFile_1.TASK_LINE.source}/\`, inlined here as part of the verbatim text the host passes to the validator. The validator confirms every task line matches this regex; a line that the plan treats as a task but does not match — in particular a line beginning with \`[ ]{...}\` without the leading list marker — is FAIL, because the \`implement\` command's detector would skip it and treat the plan as having no tasks. Additionally: valid \`[ ]\` or \`[x]\` checkbox (no malformed variants), immediately-following metrics object literally equal to \`{"it":0,"ot":0,"t":0}\` for freshly generated tasks (byte-exact: no extra spaces, no reordered keys, no trailing commas), a single space between the closing \`}\` and the task number, hierarchical task number coherent with document position (1 before 2, 1.1 before 1.2, no malformed numbering), leaf-vs-parent distinction respected (leaves carry checkbox and metrics, parents carry neither), each leaf carries a description and an explicit acceptance-criteria section, plan file inside plans/ and non-empty, at least one task line. Finally, the number of task lines the validator detects via the canonical recognizer regex above equals the leaf-task count the host supplied above exactly; a detected count that differs from the supplied count in either direction — a generated task lost to a recognition failure, or a non-task line counted as a task — is FAIL. The validator enumerates the recognized task lines, reports the detected count, and on inequality names the discrepancy as the expected count versus the detected count.
|
|
146
|
+
|
|
147
|
+
2. Semantic dependency order. Tasks appear top-to-bottom in implementation order. The audit is semantic, not numeric: read each task's description and acceptance criteria and confirm that no task depends on work performed by a task that appears later in the document. A plan whose numbering is well-formed but whose dependencies flow upward is FAIL.
|
|
148
|
+
|
|
149
|
+
3. Spec-folder write boundary. No task (leaf or parent) describes work that creates, modifies, deletes, or renames any file inside any \`.docs/contracts\` folder, any \`.docs/rules\` folder, or the \`plans/\` folder. There is no exception for flipping checkboxes or rewriting metrics: those mutations are performed programmatically by the implement command and are never described by a task.
|
|
150
|
+
|
|
151
|
+
4. Plan content rules. Verify the plan satisfies EACH of the following independently:
|
|
152
|
+
- Free of placeholders. No \`<TBD>\` or analogous task markers, no template-style blanks, no parenthetical "(to be decided)" deferrals.
|
|
153
|
+
- Free of contradictions with existing contracts or rules. No task pins behavior the canonical listings forbid.
|
|
154
|
+
- Internally self-consistent — no contradiction between the plan's narrative and its tasks. The plan's context, rationale, and explanatory prose do not contradict the obligations, verification approach, or any other statement in its task bodies, and no task contradicts that narrative. Where the prose describes how something is tested or built, it matches what the tasks prescribe.
|
|
155
|
+
- Free of ambiguous task wording. Open-ended decisions deferred to the implementer are FAIL. This includes, non-exhaustively, hedge phrases such as: \`(or class)\`, \`(or function)\`, \`(or refactor in place if preferred)\`, \`pick the lower-friction option\`, \`pick the X that minimizes Y\`, \`suggested location\`, \`or — alternatively —\`, \`or — equivalently —\`, \`or equivalent\`, \`at the time of implementation\`, \`if the X exists, do Y; otherwise Z\`, \`either A or B — pick one\`, \`A or B (or some hybrid)\`, \`or, more strongly\`, \`or X if Y\`. An implementation choice that the request did not specify must be either (a) closed to a single concrete value in the task's description and acceptance criteria, or (b) escalated by the skill to the user before the plan was drafted — never left open for the worker to resolve.
|
|
156
|
+
- Every leaf task carries an explicit acceptance-criteria section.
|
|
157
|
+
- Every leaf task carries the relevant contract link(s) by their listed relative path.
|
|
158
|
+
- Every leaf task carries the relevant rule link(s) by their listed relative path. When a rule's enforcement is bound to a specific scope, that scope is referenced alongside the file path.
|
|
159
|
+
- The plan only references contracts and rules that exist in the canonical state captured at invocation.
|
|
160
|
+
- Tasks are numbered hierarchically per the Plan file format section above.
|
|
161
|
+
- Task granularity is sane: a leaf task is not so broad it would need to be split nor so narrow it is artificial.
|
|
162
|
+
- Each code-touching task's claims about the code it builds on are accurate to its baseline — the current on-disk source, plus the changes any earlier task in the plan it depends on prescribes. A task that names a function, type, field, file, or behavior that neither the source nor any earlier task in the plan provides, or that removes or rewrites code on a mistaken account of what it does, is FAIL. Do NOT FAIL a task merely for describing code the current on-disk source lacks when an earlier task in the plan introduces it — confirm instead that the depended-on task is ordered first. Changing the code's behavior is the task's purpose and is not itself a violation — only a false claim about the code the task builds on is.
|
|
163
|
+
- Runtime-behavior premises are backed or escalated. A task whose approach depends on a runtime- or observable-behavior claim not confirmable from the source — and that no contract, rule, existing test, or preceding task in the plan backs, and that was not escalated to the user — is FAIL. This explicitly includes a task that removes, weakens, or replaces existing code on the strength of such an unbacked claim.
|
|
164
|
+
|
|
165
|
+
5. Active application of referenced contracts and rules. For every contract and rule referenced by any task in the plan, verify that the task's description and acceptance criteria actually require or honor the obligations of that reference. A task that lists a contract or rule link without the description or acceptance criteria invoking the obligation is FAIL. Additionally, for every contract or rule in the canonical listings the validator judges should have been linked by a task whose scope makes it applicable, but was not linked, the missing link is FAIL. Apply scope-driven selection: walk every rule namespace whose scope could plausibly apply to the task, and link every file whose obligation could be triggered; under-linking is penalized.
|
|
166
|
+
|
|
167
|
+
Out of scope: verifying that contract and rule paths referenced by tasks resolve to files that physically exist on disk.
|
|
168
|
+
|
|
169
|
+
### Validator output
|
|
170
|
+
|
|
171
|
+
The validator's final response ends with a single verdict line, with no Evidence Report and no other multi-line content after it:
|
|
172
|
+
|
|
173
|
+
- \`PASS\`
|
|
174
|
+
- \`FAIL <enumerated issues>\` — each issue stated clearly enough that the auto-fix step can act on it. Multiple issues are enumerated inline on that same final line, each independently actionable.
|
|
175
|
+
|
|
176
|
+
If the validator wants to show its work, it does so in the body of its response above the verdict line.
|
|
177
|
+
|
|
178
|
+
### On FAIL: bounded triage-then-fix loop
|
|
179
|
+
|
|
180
|
+
When the validator returns FAIL, enter the triage-then-fix loop:
|
|
181
|
+
|
|
182
|
+
1. Triage each issue. For every issue enumerated in the FAIL report, classify it against the clarification-scope criteria of this skill's clarification phase — the same criteria that govern the initial clarification phase above: an implementation choice in the code the tasks will produce that the request does not specify, a task-scope ambiguity the planner cannot reasonably infer from the request or the canonical contracts and rules, or a load-bearing runtime-behavior premise the plan would otherwise have to assert without backing. A validator FAIL never broadens what the skill may ask the user about; an unbacked runtime-behavior premise the validator flags is escalated to the user, never silently rewritten.
|
|
183
|
+
2. For issues whose fix would commit the skill to an answer that, per the clarification phase, the user is the one who must give and that the user did not give in the initial clarification phase of this invocation: re-enter the clarification phase for that specific ambiguity before any rewrite. Re-entered clarification follows the same mechanics — one question per turn, multiple-choice preferred when bounded, no bundling. The re-entered phase is scoped to the specific ambiguity at hand and never re-asks decisions the user has already given in this invocation.
|
|
184
|
+
3. For every other issue — placeholders, missing acceptance criteria, missing contract or rule links on a leaf task, hedge phrasing the planner can resolve by picking a concrete value, task ordering, hierarchical numbering, format-shape violations, and any other fix the skill is authorized to resolve on its own — apply in place without asking.
|
|
185
|
+
4. Rewrite the plan file in place, addressing every enumerated issue.
|
|
186
|
+
5. Re-launch the validator (a new subagent in a fresh session when the subagent host is available) over the rewritten file.
|
|
187
|
+
6. Repeat the cycle. Perform at most FIVE triage-then-fix passes per /flanders-plan invocation. The fifth FAIL ends the loop.
|
|
188
|
+
|
|
189
|
+
When the loop ends with a PASS at any iteration, proceed to the end-of-run summary below.
|
|
190
|
+
|
|
191
|
+
When the loop ends with FAIL after five passes, do not declare complete: surface the last FAIL report and the plan file path to the user in chat, then stop. Do not print the end-of-run summary as if the plan were valid.
|
|
192
|
+
|
|
193
|
+
## Summary
|
|
194
|
+
|
|
195
|
+
After the final validator returns PASS, print a summary in chat containing:
|
|
196
|
+
- The plan file path.
|
|
197
|
+
- The plan file's character size.
|
|
198
|
+
- The plan file's total line count.
|
|
199
|
+
- The total number of detected tasks.
|
|
200
|
+
|
|
201
|
+
## Output language
|
|
202
|
+
|
|
203
|
+
Write the plan file in the same natural language as the input request, unless the user says otherwise.
|
|
204
|
+
|
|
205
|
+
## Interaction language
|
|
206
|
+
|
|
207
|
+
Every message you address to the user during the run — your clarifying questions, the recommendation to create a rule via /flanders-spec, the warnings printed when the project has no contracts or no rules, the end-of-run summary, and any other text you print in chat — is written in the natural language of the user's most recent message in the conversation. When the user switches the language they write in partway through the interaction, every subsequent message you address to the user follows the language of their latest message. This is resolved independently of the Output language above: it governs only what you say to the user in the conversation, never the language of the plan file you write.
|
|
208
|
+
|
|
209
|
+
## Missing contracts or rules
|
|
210
|
+
|
|
211
|
+
If no \`.docs/contracts\` folder contains any file, warn the user in chat and produce a plan that includes whatever contracts the request implicitly requires before any implementation work. If no \`.docs/rules\` folder contains any file, warn the user in chat and proceed without rule references on the resulting tasks.`;
|
|
212
|
+
exports.specSkillBody = `---
|
|
213
|
+
description: Translate a free-form request into one or more spec markdown files inside the project's .docs/contracts and .docs/rules folders.
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
You are the /flanders-spec skill. Your sole deliverable is one or more markdown files inside the project's \`.docs/contracts\` and \`.docs/rules\` folders. You must not write, modify, or delete any source code or any file outside the project's \`.docs/contracts\` and \`.docs/rules\` folders.
|
|
217
|
+
|
|
218
|
+
## Input resolution
|
|
219
|
+
|
|
220
|
+
The user invokes you as: /flanders-spec [<data>]
|
|
221
|
+
|
|
222
|
+
- If <data> is omitted, take the user's natural-language request from the same turn or from subsequent turns of the conversation.
|
|
223
|
+
- If <data> is supplied and resolves to an existing file path, read the file's content and use it as input.
|
|
224
|
+
- If <data> is supplied and does not resolve to an existing file, use the value verbatim as inline input.
|
|
225
|
+
|
|
226
|
+
## What a contract is
|
|
227
|
+
|
|
228
|
+
A contract is a markdown document that describes the public behavior of the directory its \`.docs\` folder scopes — what code outside that directory relies on — stated abstractly, never naming internal symbols, internal data shapes, or paths inside a source directory; at the project-root \`.docs\` folder the boundary is the whole project, so its contracts capture what the end user sees, does, and relies on.
|
|
229
|
+
|
|
230
|
+
Contracts are the public surface of the scope they belong to. Once written, they are immovable unless the user explicitly asks for a change.
|
|
231
|
+
|
|
232
|
+
## What a rule is
|
|
233
|
+
|
|
234
|
+
A rule is a markdown document that captures a single, atomic piece of implementation guidance internal to the directory its \`.docs\` folder scopes — a constraint, convention, or pattern that the directory's code must follow. Each rule file describes exactly one rule.
|
|
235
|
+
|
|
236
|
+
Bundles of related rules (for example, the multiple obligations that make up SOLID, or the dispose pattern) are modeled as a subfolder under the scope's \`.docs/rules\` folder containing one file per atomic rule inside, never as a single multi-rule file.
|
|
237
|
+
|
|
238
|
+
The namespace of a rule is its path relative to the project root. The namespace is what downstream tooling uses to organize, filter, and reference rules.
|
|
239
|
+
|
|
240
|
+
Rules are immovable once written unless the user explicitly asks for a change.
|
|
241
|
+
|
|
242
|
+
## Contract vs rule: how the skill classifies and places
|
|
243
|
+
|
|
244
|
+
For every obligation in the request, the skill decides whether it is a contract or a rule and which \`.docs\` folder it belongs to: public behavior across a scope's boundary is a contract, internal implementation guidance is a rule, and the spec lands in the \`.docs\` folder of the lowest directory that encloses all the code its obligation governs — an obligation governing one directory goes in that directory's \`.docs\` folder, an obligation spanning sibling directories goes in their nearest common ancestor's \`.docs\` folder, and an obligation about project-boundary behavior goes in the project-root \`.docs\` folder. A spec is a contract because code outside its scope depends on it, not because the end user observes it directly; only at the project root do those coincide. A single request may carry both kinds and may span several scopes; the skill writes each spec to its proper \`.docs\` folder in the same invocation. The classification and placement are the skill's own decisions, not questions put to the user — the user reviews and approves them in the drafting phase before anything is persisted.
|
|
245
|
+
|
|
246
|
+
## Procedure
|
|
247
|
+
|
|
248
|
+
1. Resolve the input from the invocation rule above.
|
|
249
|
+
2. Discover every directory named \`.docs\` across the whole project tree at every depth, excluding every path the project's git ignore rules exclude (for example by enumerating with \`git ls-files --cached --others --exclude-standard\` — which lists tracked files plus untracked-but-not-ignored files — and dropping any candidate that sits under a git-ignored path, for example via \`git check-ignore\`); the files under each \`.docs/contracts\` subfolder form the canonical contracts listing and the files under each \`.docs/rules\` subfolder form the canonical rules listing; the files under each \`.docs/flanders\` subfolder form the behavior-rule listing, treating every file inside a \`.docs/flanders\` folder at any depth as a behavior rule; each file is identified by its namespace — its path relative to the project root, which for nested \`.docs\` folders includes the directories above the \`.docs\` folder, so files sharing a leaf filename in different \`.docs\` folders stay distinct. A missing or empty discovery — no \`.docs\` folder, or none containing any file — yields an empty canonical reference set. This is the canonical reference set for the run.
|
|
250
|
+
3. Before drafting anything, read every file in the canonical reference set that is relevant to the request. Reading the relevant existing files is mandatory — a draft begun without having read them is invalid, regardless of your confidence. When in doubt, read rather than omit: a deliverable that contradicts or duplicates an unread file is invalid.
|
|
251
|
+
|
|
252
|
+
**Behavior rules.** Before persisting any file, read every behavior rule whose \`.docs/flanders\` scope encloses each file you are about to write — the \`.docs\` folder you write the file into and every parent \`.docs\` folder up to the project root — and honor all of them. Behavior rules govern how you name, place, and organize the files you author; an in-scope behavior rule is binding on that work, not advisory, and applies whether or not the request mentions it.
|
|
253
|
+
|
|
254
|
+
**Rename sweep.** When the run renames, relocates, or removes a term that can recur across the corpus beyond the files it is editing — a folder name, a path segment, a flag, an identifier, a fixed string, or a namespace convention — establish the full set of files to touch by searching the whole corpus (every contract and every rule) for the old term and inspecting every occurrence the search returns. The search is exhaustive over the corpus; it is not narrowed to the files you already planned to edit. Triage each occurrence individually into exactly one of two dispositions: an occurrence the rename must update, which the run edits; or an occurrence that is an intentional reference the rename leaves alone (for example a cross-reference to an unrelated file, or a deliberately unchanged example). An occurrence is never left unexamined on the grounds that its file looked irrelevant. Coverage is driven by the token, not by a judgment of which files are relevant: the set of files the run edits is the union of the occurrences the sweep shows must be updated, and a file the sweep surfaces that you had not planned to touch is added to the run.
|
|
255
|
+
4. **Clarification phase.** Whenever the request leaves an obligation ambiguous, leaves a UI or logic decision unspecified, leaves a rule or its scope of enforcement unspecified, or admits multiple valid interpretations, ask the user clarifying questions sequentially — one question per turn. Prefer multiple-choice questions when the answer space is bounded. Use open-ended questions only when multiple-choice would force a false dichotomy. When two or three substantially different approaches would all satisfy the request, present those approaches with a short trade-off summary for each and ask the user to pick or redirect, instead of silently choosing one. The clarification phase ends only when you have enough information to draft files that contain no placeholders, no contradictions, and no scope ambiguity.
|
|
256
|
+
5. **Drafting phase.** Before persisting any file:
|
|
257
|
+
- Present the planned file layout — which files will exist, which \`.docs\` folder each falls in, which are contracts and which are rules (the classification and placement made visible), and the key obligations of each file — as a structured summary, and wait for user approval or redirection.
|
|
258
|
+
- Once the layout is approved, persist every resulting file in a single batch without any further per-file or per-section confirmation step.
|
|
259
|
+
- Update related existing files in place when the request affects obligations they already cover, and create new files only for obligations not already covered. Do not duplicate an obligation across files, whether within a folder or across the two folders.
|
|
260
|
+
- Do not write historical, transitional, or migration content into the contracts and rules you produce. A spec file states only the present spec — what the software does now and what the code must do now. Content recording what the spec used to be, what it replaces, what changed in this run, or any transitional framing (for example, "replaces the former X", "previously Y", a changelog of what this run changed) belongs in the commit message or pull-request description, not in a permanent spec file.
|
|
261
|
+
6. After approval, run a self-review pass before finalizing each file: re-read the draft and check for placeholders left behind, contradictions with the canonical reference set, ambiguous wording, and scope that drifted beyond what the user requested. Fix any issue in place; if a fix would change the meaning of content the user approved in the layout summary, surface the issue to the user and ask before applying it.
|
|
262
|
+
7. Organize the resulting files in whichever shape best fits the content:
|
|
263
|
+
- Within a \`.docs/contracts\` folder: a single descriptive file when the scope is small; multiple files when the scope has clearly separable concerns (for example, a logic file and a UI file); subfolders grouping related files when the scope has multiple sections (for example, one folder per major feature).
|
|
264
|
+
- Within a \`.docs/rules\` folder: one file per atomic rule. Subfolders group thematically related rules (for example, a testing/ subfolder for testing rules, a dependencies/ subfolder for dependency-management rules, a solid/ subfolder with one file per SOLID principle). A bundle of related rules MUST be modeled as a subfolder of single-rule files, never as one multi-rule file.
|
|
265
|
+
8. Filenames must be descriptive of their content — the user must be able to tell what each contract file covers, and which single rule each rule file pins, from the name alone.
|
|
266
|
+
9. Before declaring complete, run the final validator over the persisted file(s). The validator is the gate — only declare complete when it returns PASS. The procedure is in the Final validation section below.
|
|
267
|
+
|
|
268
|
+
## Final validation
|
|
269
|
+
|
|
270
|
+
Before declaring this skill complete, run a final validator over the persisted or updated file(s). The validator is the gate — only declare complete when it returns PASS.
|
|
271
|
+
|
|
272
|
+
What a passing gate certifies: a pass certifies that the file(s) you wrote or updated in this run satisfy the validator's checks and do not contradict the corpus the validator inspected. It does not certify that the entire corpus is mutually consistent independent of this run's files — whole-corpus consistency is not re-verified on every run, and a passing gate is not a proof of it. Report a pass as a statement about this run's own output, never as a statement that the whole spec is globally sound.
|
|
273
|
+
|
|
274
|
+
### Validator host
|
|
275
|
+
|
|
276
|
+
Launch the validator as a fresh subagent via the AI tool's subagent mechanism, in a session that does not share context with this drafting session. The fresh session is load-bearing — it forces the validator to re-derive its judgments from the file(s) on disk rather than from this session's confirmation bias.
|
|
277
|
+
|
|
278
|
+
The subagent mechanism is tool-specific. In Claude Code, the host spawns the validator through the Agent tool. In Codex CLI, the host spawns it through whatever Codex documents as its subagent surface at the time of the run.
|
|
279
|
+
|
|
280
|
+
You may fall back to an inline pass (running the validator in this same session) only when the subagent mechanism is unavailable in the current environment, or when a subagent invocation returns an unrecoverable error (spawn failure, transport error, environment refusal). Inline fallback for ergonomic reasons — the artifact looks small, tokens feel tight, you are confident — is forbidden. When you take the inline path, state in chat that you are falling back and name the concrete reason; a silent fallback is a violation. The validator is read-only on the project and does not run git mutations.
|
|
281
|
+
|
|
282
|
+
### Validator inputs
|
|
283
|
+
|
|
284
|
+
Pass the validator:
|
|
285
|
+
- The absolute path(s) to the file(s) you just wrote or updated, partitioned by folder, plus an explicit enumeration of which subset of the canonical listings is under audit in this run.
|
|
286
|
+
- The canonical contracts listing captured in step 2 of the procedure.
|
|
287
|
+
- The canonical rules listing captured in step 2 of the procedure.
|
|
288
|
+
- When this run renamed, relocated, or removed a term that can recur across the corpus (per the Rename sweep obligation in the procedure above), the explicit list of those old term(s). The list is empty when the run changed no such term.
|
|
289
|
+
- The verbatim text of the check categories below. The host MUST inline these categories in the validator's prompt — it does not just point the validator at a file by path, and it does not rely on the validator discovering them by transitive reading.
|
|
290
|
+
|
|
291
|
+
The validator reads the file(s) in full, plus any contract or rule from the listings it judges relevant to forming its verdict.
|
|
292
|
+
|
|
293
|
+
### Validator checks
|
|
294
|
+
|
|
295
|
+
Three categories, all mandatory; failure in any one is a FAIL. Each category is audited independently and violations are enumerated exhaustively. The category set is selected by the folder each file landed in: category A applies to each file that landed in a \`.docs/contracts\` folder; category B applies to each file that landed in a \`.docs/rules\` folder; category C applies to every file written or updated in the run.
|
|
296
|
+
|
|
297
|
+
**A. Contract artifacts (each file written or updated under a \`.docs/contracts\` folder)**
|
|
298
|
+
|
|
299
|
+
A1. Format and shape. Every contract file written or updated lives inside a \`.docs/contracts\` folder, is non-empty, is markdown, has a filename descriptive of its content, and is organized as described in step 7 of the procedure.
|
|
300
|
+
|
|
301
|
+
A2. Content rules. Verify the artifact satisfies EACH of the following independently:
|
|
302
|
+
- Free of placeholders. No \`<TBD>\` or analogous task markers, no template-style blanks, no parenthetical "(to be decided)" deferrals.
|
|
303
|
+
- Free of ambiguous wording. Open-ended phrasing — hedge phrases such as \`may or may not\`, \`left to the implementer\`, \`pick one of\`, \`or equivalent\`, \`at the discretion of the user\`, \`or — alternatively —\`, \`or X if Y\`, or any formulation that leaves an obligation undefined — is FAIL. A contract obligation reads as a single concrete commitment, never as a choice the reader is invited to make.
|
|
304
|
+
- Describes only public behavior across its scope's boundary — what code outside the directory its \`.docs\` folder scopes can rely on, stated abstractly, where for the project-root \`.docs/contracts\` that boundary is the end user. References to implementation details — names of specific classes, functions, libraries, modules, or frameworks; paths under src/, lib/, or any source folder; internal data shapes that consumers across the boundary do not directly observe; private helper or coordinator types; the existence of specific test files or runners; choices of HTTP client, ORM, database engine, build tool, or other tooling consumers do not directly interact with — are out of scope of a contract and are FAIL.
|
|
305
|
+
- Free of historical or migration content. The contract states only the present spec — what the software does now. Content recording what the spec used to be, what it replaces, what changed in this run, or any transitional framing is FAIL.
|
|
306
|
+
- No obligation is duplicated across files. When the request relates to obligations already covered by existing files, those files are updated rather than duplicated.
|
|
307
|
+
|
|
308
|
+
**B. Rule artifacts (each file written or updated under a \`.docs/rules\` folder)**
|
|
309
|
+
|
|
310
|
+
B1. Format and shape. Every rule file written or updated lives inside a \`.docs/rules\` folder, is non-empty, is markdown, captures exactly one atomic rule (a file that pins two or more independent obligations is FAIL — those obligations belong in separate files inside the same subfolder), has a filename descriptive of the single rule it captures, and bundles of related rules are modeled as subfolders containing single-rule files.
|
|
311
|
+
|
|
312
|
+
B2. Content rules. Verify the artifact satisfies EACH of the following independently:
|
|
313
|
+
- Free of placeholders. No \`<TBD>\` or analogous task markers, no template-style blanks, no parenthetical "(to be decided)" deferrals.
|
|
314
|
+
- Scope of enforcement is explicit. The rule has a "Who this applies to" or equivalent section that names exactly which code, agents, surfaces, file patterns, or call sites the rule binds. An open-ended "applies everywhere" without enumeration of the actual surface is FAIL.
|
|
315
|
+
- Free of ambiguous wording. Hedge phrasing that turns the obligation into a choice instead of a commitment — \`may or may not\`, \`pick one of\`, \`or equivalent\`, \`left to the implementer\`, \`at the discretion of\`, \`or — alternatively —\`, \`or X if Y\` — is FAIL.
|
|
316
|
+
- Free of historical or migration content. The rule states only the present spec. Content recording what the rule used to be, what it replaces, what changed in this run, or any transitional framing is FAIL.
|
|
317
|
+
- No rule is duplicated across files. When the request relates to a rule already covered by an existing file, that file is updated rather than a parallel duplicate created.
|
|
318
|
+
|
|
319
|
+
**C. Non-contradiction with the canonical corpus (every file written or updated in this run)**
|
|
320
|
+
|
|
321
|
+
The file(s) written or updated do not contradict any other contract in the project's contracts (the canonical contracts listing, spanning every \`.docs/contracts\` folder) and do not contradict any rule in the project's rules (the canonical rules listing, spanning every \`.docs/rules\` folder). A contradiction is an obligation pinned in two places with incompatible content. Tightening, extending, or qualifying an existing obligation in a way the existing text already allows is not a contradiction.
|
|
322
|
+
|
|
323
|
+
**Renamed-term sweep.** For each old term the host passed (the terms this run renamed, relocated, or removed), the validator searches the whole corpus for that term and inspects every occurrence. An occurrence that is a stale, un-updated instance of the renamed term — a leftover that should have been changed in this run — is FAIL. An occurrence that is an intentional reference the rename correctly leaves alone is not a violation. The validator drives this check from the passed term(s), not from its own judgment of which files are relevant, so that a stale occurrence in a file the validator would not otherwise open is still caught. When the passed list is empty, this check is vacuously satisfied.
|
|
324
|
+
|
|
325
|
+
Out of scope of the validator: verifying that paths referenced by a contract or rule physically resolve on disk.
|
|
326
|
+
|
|
327
|
+
### Validator output
|
|
328
|
+
|
|
329
|
+
The validator's final response ends with a single verdict line, with no Evidence Report and no other multi-line content after it:
|
|
330
|
+
|
|
331
|
+
- \`PASS\`
|
|
332
|
+
- \`FAIL <enumerated issues>\` — each issue stated clearly enough that the auto-fix step can act on it. Multiple issues are enumerated inline on that same final line, each independently actionable.
|
|
333
|
+
|
|
334
|
+
If the validator wants to show its work, it does so in the body of its response above the verdict line.
|
|
335
|
+
|
|
336
|
+
### On FAIL: bounded triage-then-fix loop
|
|
337
|
+
|
|
338
|
+
When the validator returns FAIL, enter the triage-then-fix loop:
|
|
339
|
+
|
|
340
|
+
1. Triage each issue. For every issue enumerated in the FAIL report, classify it against the clarification-scope criteria of this skill's clarification phase — the same criteria that govern the initial clarification phase above: obligation ambiguous, UI or logic decision unspecified, rule or scope of enforcement unspecified, or multiple valid interpretations.
|
|
341
|
+
2. For issues whose fix would commit the skill to an answer that, per the clarification phase, the user is the one who must give and that the user did not give in the initial clarification phase of this invocation: re-enter the clarification phase for that specific ambiguity before any rewrite. Re-entered clarification follows the same mechanics — one question per turn, multiple-choice preferred when bounded, no bundling. The re-entered phase is scoped to the specific ambiguity at hand and never re-asks decisions the user has already given in this invocation.
|
|
342
|
+
3. For every other issue — formatting, naming, descriptive-filename violations, placeholders that do not require a user-level decision, and any other fix the skill is authorized to resolve on its own — apply in place without asking.
|
|
343
|
+
4. Rewrite the affected file(s) in place, addressing every enumerated issue.
|
|
344
|
+
5. Re-launch the validator (a new subagent in a fresh session when the subagent host is available) over the rewritten file(s).
|
|
345
|
+
6. Repeat the cycle. Perform at most FIVE triage-then-fix passes per /flanders-spec invocation. The fifth FAIL ends the loop.
|
|
346
|
+
|
|
347
|
+
When the loop ends with a PASS at any iteration, declare complete.
|
|
348
|
+
|
|
349
|
+
When the loop ends with FAIL after five passes, do not declare complete: surface the last FAIL report and the file path(s) to the user in chat, then stop.
|
|
350
|
+
|
|
351
|
+
## Output language
|
|
352
|
+
|
|
353
|
+
Resolve the natural language to write each spec file in by this priority order:
|
|
354
|
+
|
|
355
|
+
1. When the request explicitly states a language to write in, write in that language.
|
|
356
|
+
2. Otherwise, when at least one spec file already exists in the project, write in the language of those existing spec files, determined by inspecting a single existing spec file — reading more than one is unnecessary, since the corpus is kept in one language.
|
|
357
|
+
3. Otherwise — when the request names no language and no spec file exists yet — write in the language the request itself is written in.
|
|
358
|
+
|
|
359
|
+
Do not translate already-written content; the resolved language governs only the content you author in this run.
|
|
360
|
+
|
|
361
|
+
## Interaction language
|
|
362
|
+
|
|
363
|
+
Every message you address to the user during the run — your clarifying questions, the approach trade-off summaries, the drafting-phase layout summary, and any other text you print in chat — is written in the natural language of the user's most recent message in the conversation. When the user switches the language they write in partway through the interaction, every subsequent message you address to the user follows the language of their latest message. This is resolved independently of the Output language above: it governs only what you say to the user in the conversation, never the language of the spec files you write.
|
|
364
|
+
|
|
365
|
+
## Idempotency and overwrites
|
|
366
|
+
|
|
367
|
+
Existing files in the project's \`.docs/contracts\` and \`.docs/rules\` folders are not protected. Because you receive the current state of both folders and update related files in place, re-running with related input will modify those files rather than create parallel duplicates. Preserving prior versions is the user's responsibility (typically through version control).`;
|
|
368
|
+
exports.workSkillBody = `---
|
|
369
|
+
description: Carry a single self-contained piece of work from request to reviewed completion in the current session, gating the result through one adversarial reviewer subagent, without authoring a plan or running the implement pipeline.
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
You are the /flanders-work skill. You carry a single, self-contained piece of work from the user's request to reviewed completion in one invocation, directly in the session you are running in. You implement the request yourself — editing the project's code and tests — and then gate the result through an adversarial reviewer you run as a subagent, reworking until the review is clean. You do not author a plan and you do not run the implement pipeline.
|
|
373
|
+
|
|
374
|
+
## Input resolution
|
|
375
|
+
|
|
376
|
+
The user invokes you as: /flanders-work [<data>]
|
|
377
|
+
|
|
378
|
+
- If <data> is omitted, take the user's natural-language request from the conversation.
|
|
379
|
+
- If <data> is supplied and resolves to an existing file path, read the file's content and use it as input.
|
|
380
|
+
- If <data> is supplied and does not resolve to an existing file, use the value verbatim as inline input.
|
|
381
|
+
|
|
382
|
+
## Procedure
|
|
383
|
+
|
|
384
|
+
1. Resolve the input from the invocation rule above. This request is the spec under review for the rest of the run.
|
|
385
|
+
2. **Work.** Implement the request directly in this session (see Performing the work below).
|
|
386
|
+
3. **Build and test gate.** Determine the project's build and test commands and run them as ordered gates, reworking until both pass, before any review runs (see Build and test below).
|
|
387
|
+
4. **Review.** Validate the result through a single adversarial reviewer you run as a subagent (see The reviewer and The review loop below).
|
|
388
|
+
5. **Iterate.** While the reviewer reports violations, rework the implementation to address them, re-run the build and test gate before the next review round, and review again, with no fixed upper bound.
|
|
389
|
+
6. **Finish.** When a review reports no violations, finalize without committing, without writing a plan, and without writing any configuration (see Finalization below).
|
|
390
|
+
|
|
391
|
+
## Performing the work
|
|
392
|
+
|
|
393
|
+
Implement the request directly in this session: edit the project's code and update or extend its tests so the new behavior is covered. Honor every contract, rule, and behavior rule in the project's spec corpus whose scope your changes touch — discovered across the project's \`.docs\` folders (the files under each \`.docs/contracts\` folder are contracts, the files under each \`.docs/rules\` folder are rules, and the files under each \`.docs/flanders\` folder are behavior rules) — whether or not the request names them. A contract or rule whose scope your changes touch is binding even when the request never mentions it; a behavior rule whose \`.docs/flanders\` scope encloses a file you author or change governs how you name, place, and organize that file. Treat the corpus as part of your specification, not optional reading.
|
|
394
|
+
|
|
395
|
+
## Build and test
|
|
396
|
+
|
|
397
|
+
Once the work is done, gate it through the project's build and test before any review runs. Determine the build and test commands yourself by inspecting the project — recognizing what kind of project it is and the build and test commands it exposes (for example, the \`build\` and \`test\` scripts a Node.js project exposes, or the compiler or build-system invocation a C++ project uses). Determine them by reading the project itself: do not ask the user, and do not consult any configuration file. The build command and the test command are determined independently — either one may be determinable while the other is not. A command you cannot confidently determine leaves that gate skipped, and you invent no fallback command in its place.
|
|
398
|
+
|
|
399
|
+
Run the two gates in order: the build command first, then the test command. Run each in the foreground, keeping your turn active until that command finishes — never start either in the background, never detach it, and never end your turn while it is still running. Capture each command's output. A command that completes with a non-zero exit status is a failing gate: rework the implementation using that command's captured output, then run the gate again. Repeat until the build and test gates have both passed.
|
|
400
|
+
|
|
401
|
+
Proceed to the review only once the build and test gates have both passed; a gate whose command you could not determine is skipped, and a skipped gate counts as passed.
|
|
402
|
+
|
|
403
|
+
## Spec-folder write boundary
|
|
404
|
+
|
|
405
|
+
Neither the work you perform nor the reviewer subagent creates, modifies, deletes, or renames any file inside any \`.docs/contracts\` folder, any \`.docs/rules\` folder, or the \`plans/\` folder. Those folders are the project's source of truth, governed by their own dedicated skills; consult them freely but never write to them.
|
|
406
|
+
|
|
407
|
+
## The reviewer
|
|
408
|
+
|
|
409
|
+
Once the work is done, validate it through exactly one adversarial reviewer that you run as a subagent of this same session, using the host AI tool's own subagent mechanism, in a fresh subagent session that does not share context with the work you just performed. The fresh session is load-bearing: it forces the reviewer to re-derive its judgment from the working tree rather than from the reasoning you used while doing the work.
|
|
410
|
+
|
|
411
|
+
The subagent mechanism is tool-specific. In Claude Code, you spawn the reviewer through the Agent tool. In Codex CLI, you spawn it through whatever Codex documents as its subagent surface at the time of the run.
|
|
412
|
+
|
|
413
|
+
You run a single reviewer per review round — never a list of reviewers and never several reviewers concurrently. The reviewer's tool, model, and effort are the host session's. You do not read or consult any \`.flanders/\` configuration to choose the reviewer — not its worker or reviewer tool, model, or effort, and not any reviewer list; /flanders-work relies only on having been installed and consumes no configuration.
|
|
414
|
+
|
|
415
|
+
### Inline fallback
|
|
416
|
+
|
|
417
|
+
You may fall back to running the review inline in this same session only when the host AI tool exposes no subagent mechanism, or when a subagent invocation returns an unrecoverable error (spawn failure, transport error, environment refusal). When you take the inline path, state in chat that you are falling back and name the concrete reason; a silent fallback is a violation. Inline fallback for ergonomic reasons — the change looks small, tokens feel tight, you are confident — is forbidden.
|
|
418
|
+
|
|
419
|
+
### The reviewer's prompt
|
|
420
|
+
|
|
421
|
+
Assemble the reviewer subagent's prompt as four parts, in order:
|
|
422
|
+
|
|
423
|
+
1. A framing that states the spec under review is the user's request that you implemented, quoting or summarizing that request so the reviewer can measure the work against it.
|
|
424
|
+
2. The available contracts, the available rules, and the available behavior rules as lists of their namespaces (each namespace is a path relative to the project root), discovered across the project's \`.docs\` folders, placed above the methodology so the methodology's references to "the global lists above" and "the behavior-rule list above" resolve.
|
|
425
|
+
3. The absolute path of the error-log file you provisioned for this round — this is the "error-log file" the methodology refers to — with the instruction to record its verdict there.
|
|
426
|
+
4. The methodology below, verbatim.
|
|
427
|
+
|
|
428
|
+
The methodology is self-contained: keep it that way and add no citation to any file inside the project's spec folders.
|
|
429
|
+
|
|
430
|
+
${prompts_1.reviewerMethodologyCore}
|
|
431
|
+
|
|
432
|
+
The reviewer is inspection-only. Its prompt also states the boundaries every Flanders subagent honors:
|
|
433
|
+
|
|
434
|
+
- **Git.** It runs only read-only git commands (\`git status\`, \`git diff\`, \`git log\`, \`git show\`, \`git blame\`, \`git ls-files\`) to inspect the change set, and never a command that mutates repository state — no \`git add\`, \`git commit\`, \`git stash\`, \`git reset\`, \`git restore\`, \`git checkout\`, \`git branch\`, \`git tag\`, \`git rebase\`, \`git merge\`, \`git cherry-pick\`, no edits under \`.git/\`, and no remote operations.
|
|
435
|
+
- **Foreground.** It runs every command it executes in the foreground and keeps its turn active until that command finishes; it never starts a command in the background, never detaches one, and never ends its turn while a spawned command is still running.
|
|
436
|
+
|
|
437
|
+
## The review loop
|
|
438
|
+
|
|
439
|
+
Drive the work-then-review cycle entirely from a temporary error-log file — the reviewer's verdict file. A review round is reached only after the build and test gate has passed, so every round runs against changes that already build and pass tests. For each review round:
|
|
440
|
+
|
|
441
|
+
1. **Provision the verdict file as absent.** Before launching the reviewer, ensure the temporary error-log file does not exist, deleting it if a previous round left one, so the reviewer recreating it is observable. Pass the reviewer the path to that file.
|
|
442
|
+
2. **Launch the reviewer and wait for it to complete.** Spawn the reviewer as described above and wait until it finishes.
|
|
443
|
+
3. **Branch on the file once the reviewer has completed:**
|
|
444
|
+
- **Absent** — the reviewer did not produce the file it was required to produce, so it did not run to a verdict. Relaunch the reviewer for the same round, repeating with no maximum count until the file exists. An absent file is never read as a pass.
|
|
445
|
+
- **Present and empty** — the reviewer ran to a verdict and found no violation. Accept the work; the loop ends and you finalize.
|
|
446
|
+
- **Present and non-empty** — the reviewer ran to a verdict and recorded violations. Rework the implementation to address every recorded violation, re-run the build and test gate (which must pass before the review runs again), then start a new review round from step 1 against a freshly-provisioned absent file.
|
|
447
|
+
4. **No fixed upper bound.** The cycle repeats until a round ends with a present empty file. There is no iteration cap; the user interrupts the session to stop it.
|
|
448
|
+
|
|
449
|
+
Read the verdict only from the file's presence and content, never from the reviewer's streamed output or its exit code.
|
|
450
|
+
|
|
451
|
+
## Finalization
|
|
452
|
+
|
|
453
|
+
When a review round ends with a present empty verdict file, the work is complete and you stop there:
|
|
454
|
+
|
|
455
|
+
- **No commit.** Run no \`git add\`, \`git commit\`, or any other git command that mutates repository state. The implemented changes are left in the working tree as an uncommitted change set for the user to review, amend, commit, or discard.
|
|
456
|
+
- **No plan.** Create, modify, delete, or rename nothing in the \`plans/\` folder. /flanders-work has no plan file: its spec is the user's request, so there is no checkbox to flip and no metrics to record.
|
|
457
|
+
- **No configuration.** Write nothing to \`.flanders/\`. The skill consumes no configuration and produces none.
|
|
458
|
+
|
|
459
|
+
Then report completion to the user in chat.
|
|
460
|
+
|
|
461
|
+
## Interaction language
|
|
462
|
+
|
|
463
|
+
Every message you address to the user during the run — your progress messages, the summary you print when the work is done or when you surface a review that keeps failing, and any other text you print in chat — is written in the natural language of the user's most recent message in the conversation. When the user switches the language they write in partway through the interaction, every subsequent message you address to the user follows the language of their latest message. This is resolved independently of the code you write: it governs only what you say to the user in the conversation, never the language or content of the code you produce.`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OutputContext, ScriptContext, TimeContext } from "
|
|
1
|
+
import type { OutputContext, ScriptContext, TimeContext } from "../contexts";
|
|
2
2
|
type GitResult = {
|
|
3
3
|
code: number;
|
|
4
4
|
stdout: string;
|
|
@@ -8,5 +8,7 @@ export declare function isGitAvailable(script: ScriptContext, _time: TimeContext
|
|
|
8
8
|
export declare function isInsideWorkTree(script: ScriptContext, _time: TimeContext, cwd: string): Promise<boolean>;
|
|
9
9
|
export declare function addAll(script: ScriptContext, _time: TimeContext, output: OutputContext, cwd: string): Promise<GitResult>;
|
|
10
10
|
export declare function commit(script: ScriptContext, _time: TimeContext, output: OutputContext, cwd: string, message: string): Promise<GitResult>;
|
|
11
|
-
export declare function
|
|
11
|
+
export declare function countUnstagedChangesExcept(script: ScriptContext, _time: TimeContext, cwd: string, excludePath: string): Promise<number>;
|
|
12
|
+
export declare function listNonIgnoredFiles(script: ScriptContext, _time: TimeContext, cwd: string): Promise<string[]>;
|
|
13
|
+
export declare function listIgnoredPaths(script: ScriptContext, _time: TimeContext, cwd: string, paths: readonly string[]): Promise<Set<string>>;
|
|
12
14
|
export {};
|
|
@@ -37,7 +37,9 @@ exports.isGitAvailable = isGitAvailable;
|
|
|
37
37
|
exports.isInsideWorkTree = isInsideWorkTree;
|
|
38
38
|
exports.addAll = addAll;
|
|
39
39
|
exports.commit = commit;
|
|
40
|
-
exports.
|
|
40
|
+
exports.countUnstagedChangesExcept = countUnstagedChangesExcept;
|
|
41
|
+
exports.listNonIgnoredFiles = listNonIgnoredFiles;
|
|
42
|
+
exports.listIgnoredPaths = listIgnoredPaths;
|
|
41
43
|
const path = __importStar(require("path"));
|
|
42
44
|
function isGitAvailable(script, _time) {
|
|
43
45
|
return new Promise(resolve => {
|
|
@@ -91,7 +93,7 @@ function _streamingGit(script, output, args, cwd) {
|
|
|
91
93
|
});
|
|
92
94
|
});
|
|
93
95
|
}
|
|
94
|
-
function
|
|
96
|
+
function countUnstagedChangesExcept(script, _time, cwd, excludePath) {
|
|
95
97
|
return new Promise((resolve, reject) => {
|
|
96
98
|
var _a, _b;
|
|
97
99
|
const proc = script.spawn("git", ["status", "--porcelain=v1", "--untracked-files=all"], { stdio: "pipe", cwd });
|
|
@@ -120,7 +122,7 @@ function countPendingChangesExcept(script, _time, cwd, excludePath) {
|
|
|
120
122
|
entryPath = rawPath;
|
|
121
123
|
}
|
|
122
124
|
const normalizedEntry = path.normalize(path.resolve(cwd, entryPath));
|
|
123
|
-
if (normalizedEntry !== normalizedExclude) {
|
|
125
|
+
if (line[1] !== " " && normalizedEntry !== normalizedExclude) {
|
|
124
126
|
count++;
|
|
125
127
|
}
|
|
126
128
|
}
|
|
@@ -128,3 +130,61 @@ function countPendingChangesExcept(script, _time, cwd, excludePath) {
|
|
|
128
130
|
});
|
|
129
131
|
});
|
|
130
132
|
}
|
|
133
|
+
function listNonIgnoredFiles(script, _time, cwd) {
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
var _a, _b;
|
|
136
|
+
const proc = script.spawn("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], { stdio: "pipe", cwd });
|
|
137
|
+
const stdoutChunks = [];
|
|
138
|
+
const stderrChunks = [];
|
|
139
|
+
(_a = proc.stdout) === null || _a === void 0 ? void 0 : _a.on("data", chunk => { stdoutChunks.push(String(chunk)); });
|
|
140
|
+
(_b = proc.stderr) === null || _b === void 0 ? void 0 : _b.on("data", chunk => { stderrChunks.push(String(chunk)); });
|
|
141
|
+
proc.on("error", e => reject(e instanceof Error ? e : new Error(String(e))));
|
|
142
|
+
proc.on("exit", code => {
|
|
143
|
+
if (code !== 0) {
|
|
144
|
+
return reject(new Error(stderrChunks.join("")));
|
|
145
|
+
}
|
|
146
|
+
const seen = new Set();
|
|
147
|
+
const result = [];
|
|
148
|
+
for (const entry of stdoutChunks.join("").split("\0")) {
|
|
149
|
+
if (entry.length === 0)
|
|
150
|
+
continue;
|
|
151
|
+
if (seen.has(entry))
|
|
152
|
+
continue;
|
|
153
|
+
seen.add(entry);
|
|
154
|
+
result.push(entry);
|
|
155
|
+
}
|
|
156
|
+
resolve(result);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function listIgnoredPaths(script, _time, cwd, paths) {
|
|
161
|
+
if (paths.length === 0) {
|
|
162
|
+
return Promise.resolve(new Set());
|
|
163
|
+
}
|
|
164
|
+
return new Promise((resolve, reject) => {
|
|
165
|
+
var _a, _b, _c, _d;
|
|
166
|
+
const proc = script.spawn("git", ["check-ignore", "-z", "--stdin"], { stdio: "pipe", cwd });
|
|
167
|
+
const stdoutChunks = [];
|
|
168
|
+
const stderrChunks = [];
|
|
169
|
+
(_a = proc.stdout) === null || _a === void 0 ? void 0 : _a.on("data", chunk => { stdoutChunks.push(String(chunk)); });
|
|
170
|
+
(_b = proc.stderr) === null || _b === void 0 ? void 0 : _b.on("data", chunk => { stderrChunks.push(String(chunk)); });
|
|
171
|
+
proc.on("error", e => reject(e instanceof Error ? e : new Error(String(e))));
|
|
172
|
+
proc.on("exit", code => {
|
|
173
|
+
if (code === 1) {
|
|
174
|
+
return resolve(new Set());
|
|
175
|
+
}
|
|
176
|
+
if (code !== 0) {
|
|
177
|
+
return reject(new Error(stderrChunks.join("")));
|
|
178
|
+
}
|
|
179
|
+
const ignored = new Set();
|
|
180
|
+
for (const entry of stdoutChunks.join("").split("\0")) {
|
|
181
|
+
if (entry.length === 0)
|
|
182
|
+
continue;
|
|
183
|
+
ignored.add(entry);
|
|
184
|
+
}
|
|
185
|
+
resolve(ignored);
|
|
186
|
+
});
|
|
187
|
+
(_c = proc.stdin) === null || _c === void 0 ? void 0 : _c.write(paths.join("\0") + "\0");
|
|
188
|
+
(_d = proc.stdin) === null || _d === void 0 ? void 0 : _d.end();
|
|
189
|
+
});
|
|
190
|
+
}
|