avtc-pi-featyard 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +194 -0
- package/agents/fy-design-reviewer.md +77 -0
- package/agents/fy-feature-verifier.md +24 -0
- package/agents/fy-general-reviewer.md +81 -0
- package/agents/fy-guidelines-reviewer.md +46 -0
- package/agents/fy-implementer.md +20 -0
- package/agents/fy-performance-reviewer.md +46 -0
- package/agents/fy-plan-reviewer.md +74 -0
- package/agents/fy-plan-verifier.md +44 -0
- package/agents/fy-quality-reviewer.md +50 -0
- package/agents/fy-requirements-reviewer.md +46 -0
- package/agents/fy-researcher.md +6 -0
- package/agents/fy-security-reviewer.md +46 -0
- package/agents/fy-task-verifier.md +42 -0
- package/agents/fy-testing-reviewer.md +60 -0
- package/assets/images/kanban-board.png +0 -0
- package/assets/images/settings-dropdown.png +0 -0
- package/assets/images/settings.png +0 -0
- package/assets/images/widget.png +0 -0
- package/docs/CONFIGURATION.md +266 -0
- package/index.ts +8 -0
- package/package.json +85 -0
- package/skills/fy-design/SKILL.md +108 -0
- package/skills/fy-design-review/SKILL.md +45 -0
- package/skills/fy-finish/SKILL.md +28 -0
- package/skills/fy-implement/SKILL.md +36 -0
- package/skills/fy-plan/SKILL.md +77 -0
- package/skills/fy-plan-review/SKILL.md +45 -0
- package/skills/fy-research/SKILL.md +38 -0
- package/skills/fy-review/SKILL.md +88 -0
- package/skills/fy-verify/SKILL.md +23 -0
- package/src/commands/auto-agent-commands.ts +748 -0
- package/src/commands/kanban-commands.ts +278 -0
- package/src/commands/workflow-commands.ts +531 -0
- package/src/compaction/compact-handler.ts +371 -0
- package/src/compaction/compact-message.ts +52 -0
- package/src/compaction/compact-trigger.ts +121 -0
- package/src/compaction/safe-editor-write.ts +18 -0
- package/src/events/agent/agent-end.ts +19 -0
- package/src/events/agent/agent-settled.ts +20 -0
- package/src/events/agent/agent-start.ts +18 -0
- package/src/events/agent/turn-end.ts +20 -0
- package/src/events/agent/turn-start.ts +19 -0
- package/src/events/event-deps.ts +33 -0
- package/src/events/index.ts +91 -0
- package/src/events/input/input.ts +143 -0
- package/src/events/session/context.ts +68 -0
- package/src/events/session/model-select.ts +20 -0
- package/src/events/session/session-compact.ts +20 -0
- package/src/events/session/session-shutdown.ts +26 -0
- package/src/events/session/session-start.ts +45 -0
- package/src/events/session/session-tree.ts +17 -0
- package/src/events/tool/tool-call.ts +58 -0
- package/src/events/tool/tool-result.ts +54 -0
- package/src/git/git-queries.ts +154 -0
- package/src/git/resolve-base-branch.ts +173 -0
- package/src/git/worktrees/worktree-helpers.ts +142 -0
- package/src/git/worktrees/worktree-interception.ts +66 -0
- package/src/git/worktrees/worktree-lifecycle.ts +380 -0
- package/src/guardrails/completion-gating.ts +63 -0
- package/src/guardrails/file-classifier.ts +461 -0
- package/src/guardrails/force-add-guard.ts +312 -0
- package/src/guardrails/guardrail-tracker.ts +73 -0
- package/src/guardrails/guardrail-violations.ts +47 -0
- package/src/guardrails/guardrails-engine.ts +663 -0
- package/src/guardrails/session-guardrails.ts +75 -0
- package/src/guardrails/shell-decompose.ts +285 -0
- package/src/guardrails/tdd-enforcement.ts +122 -0
- package/src/guardrails/test-output.ts +272 -0
- package/src/index.ts +598 -0
- package/src/integrations/parallel-work-guardrail-integration.ts +29 -0
- package/src/integrations/subagent-integration.ts +166 -0
- package/src/integrations/todo-integration.ts +58 -0
- package/src/kanban/auto-agent/auto-agent-cleanup.ts +37 -0
- package/src/kanban/auto-agent/auto-agent-events.ts +142 -0
- package/src/kanban/auto-agent/auto-agent-grace-period.ts +83 -0
- package/src/kanban/auto-agent/auto-agent-lifecycle.ts +497 -0
- package/src/kanban/auto-agent/auto-agent-notify.ts +43 -0
- package/src/kanban/auto-agent/auto-agent-pause.ts +44 -0
- package/src/kanban/auto-agent/auto-agent-state-machine.ts +629 -0
- package/src/kanban/data/kanban-database.ts +851 -0
- package/src/kanban/data/kanban-db-schema.ts +137 -0
- package/src/kanban/data/kanban-detect-project.ts +43 -0
- package/src/kanban/data/kanban-move-feature.ts +43 -0
- package/src/kanban/data/kanban-types.ts +64 -0
- package/src/kanban/ensure-feature.ts +226 -0
- package/src/kanban/kanban-board-ui/app.js +1125 -0
- package/src/kanban/kanban-board-ui/csv-parser.d.ts +5 -0
- package/src/kanban/kanban-board-ui/csv-parser.js +80 -0
- package/src/kanban/kanban-board-ui/index.html +127 -0
- package/src/kanban/kanban-board-ui/style.css +820 -0
- package/src/kanban/kanban-bridge.ts +216 -0
- package/src/kanban/kanban-context.ts +43 -0
- package/src/kanban/kanban-events.ts +34 -0
- package/src/kanban/kanban-generate-title.ts +259 -0
- package/src/kanban/kanban-generate-topic.ts +170 -0
- package/src/kanban/kanban-operations.ts +170 -0
- package/src/kanban/kanban-server.ts +931 -0
- package/src/kanban/kanban-turn-handlers.ts +55 -0
- package/src/kanban/model-capture.ts +34 -0
- package/src/log.ts +47 -0
- package/src/phases/env-sync.ts +64 -0
- package/src/phases/execution-mode.ts +79 -0
- package/src/phases/implement-to-verify.ts +31 -0
- package/src/phases/phase-progression.ts +249 -0
- package/src/phases/phase-transitions.ts +226 -0
- package/src/phases/workflow-router.ts +98 -0
- package/src/prompts/finish-instructions.ts +143 -0
- package/src/prompts/skill-block-builder.ts +73 -0
- package/src/prompts/skill-expansion.ts +262 -0
- package/src/prompts/task-gate-skill.ts +83 -0
- package/src/prompts/template-engine.ts +433 -0
- package/src/prompts/text-blocks.ts +279 -0
- package/src/review/review-context.ts +56 -0
- package/src/review/review-counter.ts +66 -0
- package/src/review/review-empty-loop-tracking.ts +52 -0
- package/src/review/review-loops.ts +217 -0
- package/src/review/review-report.ts +101 -0
- package/src/settings/model-overrides.ts +269 -0
- package/src/settings/settings-schema.ts +458 -0
- package/src/settings/settings-types.ts +81 -0
- package/src/settings/settings-ui.ts +222 -0
- package/src/shared/errors.ts +24 -0
- package/src/shared/global.d.ts +22 -0
- package/src/shared/session-entries.ts +24 -0
- package/src/shared/types.ts +195 -0
- package/src/shared/workflow-refs.ts +253 -0
- package/src/shared/workflow-types.ts +180 -0
- package/src/snippets/vendored/subscribe-to-dialog-coordinator.ts +98 -0
- package/src/snippets/vendored/subscribe-to-notifications.ts +150 -0
- package/src/snippets/vendored/subscribe-to-parallel-work-guardrail.ts +35 -0
- package/src/snippets/vendored/subscribe-to-subagent.ts +67 -0
- package/src/snippets/vendored/subscribe-to-todo.ts +63 -0
- package/src/state/archive-artifacts.ts +602 -0
- package/src/state/artifact-junction.ts +462 -0
- package/src/state/artifact-paths.ts +230 -0
- package/src/state/feature-activation.ts +239 -0
- package/src/state/feature-management.ts +325 -0
- package/src/state/feature-record-store.ts +39 -0
- package/src/state/feature-session.ts +263 -0
- package/src/state/feature-state.ts +439 -0
- package/src/state/post-turn-dispatch.ts +106 -0
- package/src/state/session-lifecycle.ts +346 -0
- package/src/state/state-persistence.ts +122 -0
- package/src/state/worth-notes.ts +89 -0
- package/src/tools/add-to-backlog.ts +117 -0
- package/src/tools/phase-ready.ts +742 -0
- package/src/tools/task-ready-advance.ts +235 -0
- package/src/tools/text-result.ts +15 -0
- package/src/ui/featyard-widget.ts +250 -0
- package/src/ui/manage-features-dialog.ts +246 -0
- package/src/ui/select-dialog.ts +27 -0
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 avtc <tarasenkov@gmail.com>
|
|
3
|
+
|
|
4
|
+
// Static text blocks injected by the template engine's PI_FY_* placeholders.
|
|
5
|
+
// Pure data (array-joined strings) with no runtime deps; the engine imports these.
|
|
6
|
+
// Split out of template-substitution so the prompt content is browsable apart from
|
|
7
|
+
// the substitution mechanics.
|
|
8
|
+
|
|
9
|
+
/** General reviewer dispatch template — single generalist reviewer. */
|
|
10
|
+
export const GENERAL_DISPATCH_TEMPLATE = [
|
|
11
|
+
"Dispatch the fy-general-reviewer as a subagent:",
|
|
12
|
+
"",
|
|
13
|
+
"```ts",
|
|
14
|
+
"subagent({",
|
|
15
|
+
' tasks: [{ agent: "fy-general-reviewer", task: `<filled template>` }],',
|
|
16
|
+
"})",
|
|
17
|
+
"```",
|
|
18
|
+
"",
|
|
19
|
+
"**Template for the reviewer task:**",
|
|
20
|
+
"",
|
|
21
|
+
"```",
|
|
22
|
+
"Review the following changed files for all quality aspects (SOLID, KISS, DRY, security, performance, testing, guidelines, requirements compliance):",
|
|
23
|
+
"**Changed files:**",
|
|
24
|
+
"<list of changed files with git diff summary>",
|
|
25
|
+
"",
|
|
26
|
+
"**Diff base:** <base SHA> — the commit before feature work began.",
|
|
27
|
+
"**HEAD:** <head SHA>",
|
|
28
|
+
"",
|
|
29
|
+
"Report findings using the standard issue format:",
|
|
30
|
+
"- ID: R<loopNumber>-<index>",
|
|
31
|
+
"- Severity: Critical | Important | Minor",
|
|
32
|
+
"- Category: <category>",
|
|
33
|
+
"- File: path/to/file:startLine-endLine (optional)",
|
|
34
|
+
"- Description: What is wrong and why",
|
|
35
|
+
"- Suggested fix: How to fix (optional)",
|
|
36
|
+
"",
|
|
37
|
+
'If no NEW issues found, report "No new issues found."',
|
|
38
|
+
"```",
|
|
39
|
+
].join("\n");
|
|
40
|
+
|
|
41
|
+
/** Comprehensive reviewer dispatch template — all 6 specialized reviewers. */
|
|
42
|
+
export const COMPREHENSIVE_DISPATCH_TEMPLATE = [
|
|
43
|
+
"Dispatch ALL 6 specialized reviewers as parallel subagents. Do NOT skip any reviewer — each has a unique perspective that catches issues the others miss:",
|
|
44
|
+
"",
|
|
45
|
+
"- `fy-quality-reviewer` — SOLID, KISS, DRY, maintainability, naming",
|
|
46
|
+
"- `fy-testing-reviewer` — Test coverage, edge cases, mock quality",
|
|
47
|
+
"- `fy-security-reviewer` — Injection, auth, secrets, data exposure, input validation",
|
|
48
|
+
"- `fy-performance-reviewer` — Data processing, algorithms, loops, queries, caching",
|
|
49
|
+
"- `fy-guidelines-reviewer` — Project conventions, linting rules, architecture patterns, file organization",
|
|
50
|
+
"- `fy-requirements-reviewer` — Spec compliance: verify implementation matches plan/spec document",
|
|
51
|
+
"",
|
|
52
|
+
"```ts",
|
|
53
|
+
"subagent({",
|
|
54
|
+
" tasks: [",
|
|
55
|
+
' { agent: "fy-quality-reviewer", task: "<filled template>" },',
|
|
56
|
+
' { agent: "fy-testing-reviewer", task: "<filled template>" },',
|
|
57
|
+
' { agent: "fy-security-reviewer", task: "<filled template>" },',
|
|
58
|
+
' { agent: "fy-performance-reviewer", task: "<filled template>" },',
|
|
59
|
+
' { agent: "fy-guidelines-reviewer", task: "<filled template>" },',
|
|
60
|
+
' { agent: "fy-requirements-reviewer", task: "<filled template>" },',
|
|
61
|
+
" ],",
|
|
62
|
+
"})",
|
|
63
|
+
"```",
|
|
64
|
+
"",
|
|
65
|
+
"**Template for each reviewer task:**",
|
|
66
|
+
"",
|
|
67
|
+
"```",
|
|
68
|
+
"Review the following changed files for <specialty>:",
|
|
69
|
+
"**Changed files:**",
|
|
70
|
+
"<list of changed files with git diff summary>",
|
|
71
|
+
"",
|
|
72
|
+
"**Diff base:** <base SHA> — the commit before feature work began.",
|
|
73
|
+
"**HEAD:** <head SHA>",
|
|
74
|
+
"",
|
|
75
|
+
"Review each file and report only NEW findings using the issue format:",
|
|
76
|
+
"- ID: R<loopNumber>-<index>",
|
|
77
|
+
"- Severity: Critical | Important | Minor",
|
|
78
|
+
"- Category: <specialty-category>",
|
|
79
|
+
"- File: path/to/file:startLine-endLine (optional)",
|
|
80
|
+
"- Description: What is wrong and why",
|
|
81
|
+
"- Suggested fix: How to fix (optional)",
|
|
82
|
+
"",
|
|
83
|
+
'If no NEW issues found in your specialty area, report "No new issues found."',
|
|
84
|
+
"```",
|
|
85
|
+
].join("\n");
|
|
86
|
+
|
|
87
|
+
/** Deferred-as-finding rule — treat a `⏭️ deferred` verifier outcome as a finding, not an escape. */
|
|
88
|
+
export const DEFERRED_IS_A_FINDING =
|
|
89
|
+
"`⏭️ deferred` is a finding, not an escape — treat it like `❌ missing`: the work is not done, implement it.";
|
|
90
|
+
|
|
91
|
+
/** Verify phase templates — full verifier spawn instruction blocks. */
|
|
92
|
+
export const VERIFY_PHASE_TEMPLATES: Record<"verify" | "plan", string> = {
|
|
93
|
+
verify: [
|
|
94
|
+
'1. **Spawn fy-feature-verifier subagent** — `subagent({ agent: "fy-feature-verifier", task: "..." })`.',
|
|
95
|
+
"Always run this verification — NEVER skip, even if you think there is nothing new to verify.",
|
|
96
|
+
"2. **If issues found:** Use `todo_add` with `parentId` set to the currently active todo item to create sub-items for all issues (single call, multiple items). Fix them one at a time. After all fixes, commit. Then re-init the todo list (`todo_init` with `overwrite: true`) with a fresh cycle: [spawn verifier, fix issues, run build/lint/tests, call phase_ready], and return to step 1. Max iterations: {{PI_FY_VERIFY_ITERATIONS}}. After max attempts, escalate to user.",
|
|
97
|
+
DEFERRED_IS_A_FINDING,
|
|
98
|
+
"3. **Fallback:** If fy-feature-verifier subagent fails (timeout, crash, error), retry once. If still fails, report failure to user.",
|
|
99
|
+
].join("\n"),
|
|
100
|
+
plan: [
|
|
101
|
+
"1. Load the design document.",
|
|
102
|
+
'2. Spawn `fy-plan-verifier` subagent — `subagent({ agent: "fy-plan-verifier", task: "..." })`.',
|
|
103
|
+
"Always run this verification — NEVER skip, even if you think there is nothing new to verify.",
|
|
104
|
+
"3. If fy-plan-verifier finds issues: fix the plan, then re-dispatch fy-plan-verifier to confirm fixes. Max iterations: {{PI_FY_VERIFY_ITERATIONS}}. After max attempts, escalate to user.",
|
|
105
|
+
DEFERRED_IS_A_FINDING,
|
|
106
|
+
"4. If all ✅: proceed to `phase_ready`",
|
|
107
|
+
"5. **Fallback:** If fy-plan-verifier subagent fails (timeout, crash, error), retry once. If still fails, fall back to the manual per-point checklist: re-init the todo list (`todo_init` with `overwrite: true`) with one item per design point, verify each has a corresponding plan task.",
|
|
108
|
+
].join("\n"),
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Architecture principles — injected by {{PI_FY_ARCHITECTURE_PRINCIPLES}}.
|
|
113
|
+
* Shared across design / plan / fy-implement / fy-implementer (single source of truth).
|
|
114
|
+
* Role-neutral wording so it fits every phase.
|
|
115
|
+
*/
|
|
116
|
+
export const ARCHITECTURE_PRINCIPLES = [
|
|
117
|
+
"- Apply SOLID, Clean Architecture, best practices. Favor separation of concerns and narrow interfaces.",
|
|
118
|
+
"- Favor composition over inheritance — build from small, composable units.",
|
|
119
|
+
"- Prefer explicit contracts (interfaces, defined boundaries) over implicit coupling (callbacks, shared mutable state, hidden assumptions).",
|
|
120
|
+
'- Name the contract at every boundary crossing: inputs, outputs, invariants, error semantics. "A calls B" without the contract is hand-waving; state it explicitly.',
|
|
121
|
+
"- Map the coupling: for each dependency, what must each side know — minimize it. The minimal contract that satisfies the real need wins.",
|
|
122
|
+
"- Single source of truth: each piece of state has one authoritative location; derive the rest, never duplicate.",
|
|
123
|
+
"- Stateless where possible: minimize mutable and shared state; prefer pure transformation — the principle, not a mandated mechanism.",
|
|
124
|
+
"- Point dependencies toward stable abstractions, never in cycles; high-level policy does not depend on low-level details (dependency inversion).",
|
|
125
|
+
"- Make invalid states unrepresentable: use types and constraints so illegal states cannot be constructed; fail fast on the rest.",
|
|
126
|
+
"- Treat error handling as a first-class concern: define failure semantics at boundaries, fail fast, never swallow errors — errors are values.",
|
|
127
|
+
"- Minimal public surface: expose the smallest useful API; do not build speculative generality (YAGNI).",
|
|
128
|
+
"- Design for testability: units are independently testable via dependency injection and clear seams, with no hidden globals.",
|
|
129
|
+
].join("\n");
|
|
130
|
+
|
|
131
|
+
/** Coverage-first review process — mechanical skeleton shared across the seven code reviewers. */
|
|
132
|
+
export const COVERAGE_REVIEW_PROCESS = [
|
|
133
|
+
"Review exhaustively, not by free-form scanning: build a complete checklist of the diff, then work every item in order. This is what surfaces issues that ad-hoc review misses loop after loop. The report file is an ISSUES list - track coverage through the todo list, not the report.",
|
|
134
|
+
"",
|
|
135
|
+
"1. **Scope.** Base commit = the commit before this work began (from your task prompt; if `(not available)`, find it with `git log`). It is fixed — review the full cumulative diff from base to HEAD. Run `git diff --name-only <base>..HEAD` and walk the changed files in diff order so nothing is skipped.",
|
|
136
|
+
'2. **Build the checklist (your first todo item).** `todo_init` one item: "Build coverage checklist". Work it: read each changed source file and enumerate its reviewable units (functions/methods, classes, branches, error paths, public interfaces); for each changed test file, enumerate what it covers. For every Coverage Area below (including the final "Other"), `todo_add` an area item, then `todo_add` leaf items under it (`parentId`) — concrete `file:method`, line ranges, or data flows. The bullet points under an area are the checks to run on each leaf — they are NOT items. Finish by `todo_add`-ing a last item "Re-validate findings for false positives", then `todo_complete` the build item. Every leaf\'s `details` MUST hold the exact file/method refs and what to check — only what is written to the todo list or report file survives context compaction, not what is held in mind.',
|
|
137
|
+
"3. **Work every item in order, one at a time.** For each leaf: actively hunt for an issue in your domain. The moment you find a potential issue, append it to the report file IMMEDIATELY — do not batch to the end, because the checklist can be large and a compaction may occur mid-way. Then `todo_complete` the leaf. Completing items is your coverage record; the report holds only real issues.",
|
|
138
|
+
"4. **Re-validate (last item).** For each finding in the report, re-read the surrounding code and confirm it is real and within your role; then check it against the known-issues file and drop it if it duplicates an already-dismissed entry. Remove false positives. If the report is large, decompose this item into one sub-item per finding.",
|
|
139
|
+
].join("\n");
|
|
140
|
+
|
|
141
|
+
/** Coverage-first review process for documents — the doc analog of COVERAGE_REVIEW_PROCESS. */
|
|
142
|
+
export const DOC_COVERAGE_PROCESS = [
|
|
143
|
+
"Review exhaustively, not by free-form scanning: enumerate the document into checkable items, then work every one in order. This surfaces issues that ad-hoc review misses loop after loop. The report file is an ISSUES list - track coverage through the todo list, not the report.",
|
|
144
|
+
"",
|
|
145
|
+
"1. **Read.** Read the document(s) named in your task, plus any docs you reference only for intent.",
|
|
146
|
+
'2. **Build the checklist (your first todo item).** `todo_init` one item: "Build coverage checklist". Work it: read the document and enumerate its reviewable units. For every Coverage Area below (including the final "Other"), `todo_add` an area item, then `todo_add` leaf items under it (`parentId`) — concrete `section`/`file`/`task` refs. The bullet points under an area are the checks to run on each leaf — they are NOT items. Finish by `todo_add`-ing a last item "Re-validate findings for false positives", then `todo_complete` the build item. Every leaf\'s `details` MUST hold the exact refs and what to check — only what is in the todo list or report file survives context compaction, not what is held in mind.',
|
|
147
|
+
"3. **Work every item in order, one at a time.** For each leaf: actively hunt for an issue in your domain. The moment you find one, append it to the report file IMMEDIATELY — do not batch to the end, because the checklist can be large and a compaction may occur mid-way. Then `todo_complete` the leaf. Completing items is your coverage record; the report holds only real issues.",
|
|
148
|
+
"4. **Re-validate (last item).** For each finding, re-read the surrounding document/code and confirm it is real and within your role; then check it against the known-issues file and drop it if it duplicates an already-dismissed entry. Remove false positives. If the report is large, decompose this item into one sub-item per finding.",
|
|
149
|
+
].join("\n");
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Additional areas of attention — injected by {{PI_FY_ADDITIONAL_AREAS_OF_ATTENTION}}.
|
|
153
|
+
* Concerns to be mindful of while writing tasks (plan) or code (fy-implementer), drawn from
|
|
154
|
+
* the specialized code reviewers. Heading-less body; each consumer owns its own heading.
|
|
155
|
+
* Deduped against ARCHITECTURE_PRINCIPLES (SOLID/error-handling/coupling/contracts live there).
|
|
156
|
+
*/
|
|
157
|
+
export const ADDITIONAL_AREAS_OF_ATTENTION = [
|
|
158
|
+
"**Requirements**",
|
|
159
|
+
"- Spec compliance — every requirement implemented; no scope creep; no divergent behavior.",
|
|
160
|
+
"",
|
|
161
|
+
"**Security**",
|
|
162
|
+
"- Input validation at every trust boundary — reject early; defend against injection.",
|
|
163
|
+
"- Authentication and authorization on every privileged action — fail closed.",
|
|
164
|
+
"- Secrets, credentials, and PII kept out of code, logs, error messages, and config.",
|
|
165
|
+
"- Data exposure, CSRF, dependency vulnerabilities, and configuration security.",
|
|
166
|
+
"",
|
|
167
|
+
"**Performance**",
|
|
168
|
+
"- Algorithmic complexity, N+1 queries, allocations, and I/O in hot paths; right data structure; caching where it pays off.",
|
|
169
|
+
"- Actual scale respected — a loop over ten items is not an issue.",
|
|
170
|
+
"- Resources closed (files, handles, connections); no leaks; bounded concurrency.",
|
|
171
|
+
"",
|
|
172
|
+
"**Testing**",
|
|
173
|
+
"- Critical path, every error and boundary path, edge cases, and negative cases covered.",
|
|
174
|
+
"- Real assertions, not weak tautologies; mocking only what crosses the boundary.",
|
|
175
|
+
"- Unit/integration balance; realistic test data.",
|
|
176
|
+
"",
|
|
177
|
+
"**Quality**",
|
|
178
|
+
"- KISS and DRY — shared logic extracted once.",
|
|
179
|
+
"- Intent-revealing names; small single-purpose units (high cohesion); readable code.",
|
|
180
|
+
"",
|
|
181
|
+
"**Conventions**",
|
|
182
|
+
"- Project lint/format, naming, import/export, file-organization, and architecture-pattern conventions matched exactly.",
|
|
183
|
+
"",
|
|
184
|
+
"**Production readiness**",
|
|
185
|
+
"- Backwards compatibility preserved, migrated, or broken deliberately — know what relies on current behavior.",
|
|
186
|
+
"- Every caller and every reader/writer of changed data found before the change.",
|
|
187
|
+
"- Shared state or background work: ordering guarantees and impossible races nailed.",
|
|
188
|
+
"- Documentation and config aligned with the changes.",
|
|
189
|
+
].join("\n");
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Implementer guidance — injected by {{PI_FY_IMPLEMENTER_GUIDANCE}}.
|
|
193
|
+
* Shared by BOTH the fy-implementer agent (agents/implementer.md) and fy-implement's
|
|
194
|
+
* current-session mode. Inner placeholders (ARCHITECTURE_PRINCIPLES, ADDITIONAL_AREAS_OF_ATTENTION)
|
|
195
|
+
* are INLINED here so the block substitutes once (no nested/double substitution).
|
|
196
|
+
*/
|
|
197
|
+
export const IMPLEMENTER_GUIDANCE = [
|
|
198
|
+
"## Cycle",
|
|
199
|
+
"",
|
|
200
|
+
"For each behavior a task requires:",
|
|
201
|
+
"",
|
|
202
|
+
"1. **Red.** Write a test for one behavior. Run it. Confirm it fails for the *right* reason — assertion failure, not a compile or import error. If it fails for the wrong reason, fix that first.",
|
|
203
|
+
"2. **Green.** Write the minimum code to pass it. Run it. Confirm green.",
|
|
204
|
+
"3. **Refactor.** Clean up only after green. Re-run after each change; revert if it goes red.",
|
|
205
|
+
"",
|
|
206
|
+
"## Discipline",
|
|
207
|
+
"",
|
|
208
|
+
"- **Do NOT skip the failing run.** A new test that passes before you've implemented tests nothing — find out why it's already green before writing any code.",
|
|
209
|
+
"- **Do NOT write code before its test.** If you already did, delete it, write the test, watch it fail, then reimplement to pass.",
|
|
210
|
+
"- **One test, one behavior.** Don't stack tests against one monolithic change. Cycle per behavior.",
|
|
211
|
+
"- **Do NOT commit red.** Build clean and all tests green at every commit.",
|
|
212
|
+
"- **Do NOT rationalize an untested path** as obvious or covered elsewhere — if it's behavior, it has a test.",
|
|
213
|
+
"",
|
|
214
|
+
"## Architecture",
|
|
215
|
+
ARCHITECTURE_PRINCIPLES,
|
|
216
|
+
"",
|
|
217
|
+
"## Reuse & dedup",
|
|
218
|
+
"",
|
|
219
|
+
"Reuse existing code when it already solves the problem and follows best practices — search before you write, and do not duplicate. As you work, refactor in progress to deduplicate code you write: extract the shared piece once and point every caller at it. Judge existing code before building on it — do not perpetuate a wrong design.",
|
|
220
|
+
"",
|
|
221
|
+
"## Refactoring scope",
|
|
222
|
+
"",
|
|
223
|
+
"Refactor whatever the task needs for a clean, correct implementation — decide autonomously; do not ask the user for routine improvements. High-level designs and tasks often leave details to your judgment; make the code right.",
|
|
224
|
+
"",
|
|
225
|
+
"Do not refactor code unrelated to the task. When you notice an out-of-scope code smell, a bug you cannot fix, or anything strange, report it in your final report as a **worth-note** (what and where) for the caller to record — do not fix it.",
|
|
226
|
+
"",
|
|
227
|
+
"## Bugs",
|
|
228
|
+
"",
|
|
229
|
+
"When fixing a bug or a verifier finding, write a failing test that reproduces it first, then fix. The test stays — it guards the regression.",
|
|
230
|
+
"",
|
|
231
|
+
"When you notice a real bug along the way — outside the literal task — investigate until 100% certain the logic is wrong, then fix it with a failing test that reproduces it (the test stays). Track each fix as additional `todo` items, and report every extra bug fixed: what was wrong and how you fixed it.",
|
|
232
|
+
"",
|
|
233
|
+
"## Commits",
|
|
234
|
+
"",
|
|
235
|
+
"Commit at green points: after a red→green cycle completes, or after a refactor that stayed green. Never commit broken builds, skipped tests, or work-in-progress.",
|
|
236
|
+
"",
|
|
237
|
+
"## Completion Rules",
|
|
238
|
+
"",
|
|
239
|
+
"You MUST implement EVERYTHING the task specifies. Do NOT stop partial.",
|
|
240
|
+
"",
|
|
241
|
+
'- **Do NOT stop early** because you think "enough was done" or "the rest is trivial" — implement everything.',
|
|
242
|
+
"- **Do NOT skip steps** from the task description — each step was planned for a reason.",
|
|
243
|
+
'- **Do NOT defer** planned work to "follow-up" or "future work" — this task IS the follow-up.',
|
|
244
|
+
"- **Size up the scope.** If the task is large or under-specified — mis-planning, or high-level planning that omits details — deconstruct it into sub-steps with the `todo` tool and work them sequentially. Make each sub-step independently completable.",
|
|
245
|
+
"",
|
|
246
|
+
"## Very large tasks",
|
|
247
|
+
"",
|
|
248
|
+
"If a task is too large to finish in one session AND it decomposes into granular, independently-verifiable parts, decompose it and dispatch one nested `fy-implementer` subagent at a time per part, then review each subagent's work for correctness before moving on. Verify sequentially — do not dispatch in parallel.",
|
|
249
|
+
"",
|
|
250
|
+
"## Blockers",
|
|
251
|
+
"",
|
|
252
|
+
"Detect blockers and stop. When you cannot proceed cleanly — an unresolved ambiguity, a missing dependency, broken upstream code, or a decision out of your lane — do not guess or improvise around it. Stop and report to the user: what blocks you, what you tried, and what unblocks you. Do not silently narrow scope.",
|
|
253
|
+
"",
|
|
254
|
+
"## Additional areas of attention",
|
|
255
|
+
"",
|
|
256
|
+
ADDITIONAL_AREAS_OF_ATTENTION,
|
|
257
|
+
].join("\n");
|
|
258
|
+
|
|
259
|
+
/** Researcher delegation section — injected by {{PI_FY_RESEARCHER_DELEGATION}} when nestedResearchers is "on". */
|
|
260
|
+
export const RESEARCHER_DELEGATION_SECTION = [
|
|
261
|
+
"## Delegation",
|
|
262
|
+
"",
|
|
263
|
+
"When the investigation covers multiple independent sub-areas (multiple modules, long call chains, broad subsystems), delegate each to a nested `fy-researcher` subagent in parallel after mapping the landscape yourself.",
|
|
264
|
+
"",
|
|
265
|
+
"- **Delegate a part, not the whole task** — split your investigation into narrower scopes, not a repeat of it",
|
|
266
|
+
"- **Map before delegating** — do the initial `find`/`grep` yourself; delegate only after you know what sub-areas exist",
|
|
267
|
+
"- **Don't delegate single lookups** — do them yourself",
|
|
268
|
+
"- **Hierarchical output files** — append `.N` to your file stem: `feature-agent-1.md` → `feature-agent-1.1.md`",
|
|
269
|
+
"- **Synthesize, don't concatenate** — connect findings across nested reports, note relationships and gaps",
|
|
270
|
+
"",
|
|
271
|
+
"```ts",
|
|
272
|
+
"subagent({",
|
|
273
|
+
" tasks: [",
|
|
274
|
+
' { agent: "fy-researcher", task: "<narrow sub-task>. Write to: <your-path>-agent-X.1.md" },',
|
|
275
|
+
' { agent: "fy-researcher", task: "<narrow sub-task>. Write to: <your-path>-agent-X.2.md" },',
|
|
276
|
+
" ],",
|
|
277
|
+
"})",
|
|
278
|
+
"```",
|
|
279
|
+
].join("\n");
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 avtc <tarasenkov@gmail.com>
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Review iteration context helpers.
|
|
6
|
+
*
|
|
7
|
+
* Small shared utilities consumed by the placeholder substitution pipeline
|
|
8
|
+
* and phase dispatch: review-method strings, the display loop-number
|
|
9
|
+
* transform, phase→review-skill-name mapping, and the review-phase guard.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { Phase } from "../phases/phase-progression.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Check if a task is a commit task (not a review fix task).
|
|
16
|
+
*/
|
|
17
|
+
export function isCommitTask(task: { name: string | null; result: string | null }): boolean {
|
|
18
|
+
return task.result === "committed" || /Commit:/.test(task.name ?? "");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Convert a raw "iterations started" review loop count (1-indexed: 1 = first
|
|
23
|
+
* iteration has started) into the 0-indexed "iteration being reviewed" display
|
|
24
|
+
* number. Clamped at 0 to avoid negatives. Centralized so the off-by-one is
|
|
25
|
+
* defined once across phase-ready and the substitution pipeline.
|
|
26
|
+
*/
|
|
27
|
+
export function toDisplayLoopNumber(rawCount: number): number {
|
|
28
|
+
return Math.max(0, rawCount - 1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** True for the two phases that drive design/plan review loops. Undefined-safe. */
|
|
32
|
+
export function isReviewPhase(phase: Phase | undefined): phase is "design" | "plan" {
|
|
33
|
+
return phase === "design" || phase === "plan";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function reviewSkillName(phase: "design" | "plan"): "fy-design-review" | "fy-plan-review" {
|
|
37
|
+
return phase === "design" ? "fy-design-review" : "fy-plan-review";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Compute the review method string for design/plan review iteration skills.
|
|
42
|
+
*/
|
|
43
|
+
export function computeReviewMethod(isDesign: boolean, settings: { planReviewMode: string }): string {
|
|
44
|
+
const mode = settings.planReviewMode;
|
|
45
|
+
if (mode === "parallel-subagents") {
|
|
46
|
+
if (isDesign) {
|
|
47
|
+
return "Dispatch reviewer: `subagent({ agent: 'fy-design-reviewer', task: 'Review the design document for design mistakes. Read the design doc and relevant project files, then output findings following the fy-design-review skill format.' })`";
|
|
48
|
+
} else {
|
|
49
|
+
return "Dispatch reviewer: `subagent({ agent: 'fy-plan-reviewer', task: 'Review the implementation plan against the design document for gaps, inconsistencies, and mistakes. Read both documents and relevant project files, then output findings.' })`";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (isDesign) {
|
|
53
|
+
return "Read the full assembled design document carefully. Review for logical consistency (contradictions, missing transitions, circular deps), architectural soundness (abstraction boundaries, reinvented solutions, coupling), design clarity (vague specs, missing interfaces), feasibility & scope (too large, unrelated concerns), and common-sense (over/under-engineering). Output findings with severity and suggested resolutions, plus a scope assessment.";
|
|
54
|
+
}
|
|
55
|
+
return "Read the design document and implementation plan. Review the plan against the design for: inconsistencies, gaps, missing pieces, incorrect steps, unclear instructions.";
|
|
56
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 avtc <tarasenkov@gmail.com>
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Review iteration counter — single source of truth for incrementing the
|
|
6
|
+
* design/plan review loop counter and persisting feature state.
|
|
7
|
+
*
|
|
8
|
+
* Used by both the code-driven path (phase_ready handler) and the manual
|
|
9
|
+
* invocation path (events/input/, when the user types /skill:fy-design-review or
|
|
10
|
+
* /skill:fy-plan-review). Compaction recovery does NOT use this — it resumes the
|
|
11
|
+
* current iteration rather than starting a new one.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { log } from "../log.js";
|
|
15
|
+
import type { FeatureSession } from "../state/feature-session.js";
|
|
16
|
+
import { DEFAULT_DIR, type FeatureState, saveFeatureState } from "../state/feature-state.js";
|
|
17
|
+
|
|
18
|
+
/** Marks the review loop as active when persisting a new iteration counter. */
|
|
19
|
+
const REVIEW_ACTIVE = true;
|
|
20
|
+
|
|
21
|
+
/** Map a review phase to its phase-data object. Single source of truth for the
|
|
22
|
+
* phase→object mapping (used by startReviewIteration). */
|
|
23
|
+
export function reviewPhaseData(phase: "design" | "plan"): "design" | "plan" {
|
|
24
|
+
return phase;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Back-compat alias retained for callers that still reference the field selector. */
|
|
28
|
+
export function reviewLoopStateField(phase: "design" | "plan"): "design" | "plan" {
|
|
29
|
+
return phase;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Start a new review iteration: increment the phase's review loop counter, mark
|
|
34
|
+
* the review as active, and persist feature state.
|
|
35
|
+
*
|
|
36
|
+
* @param featureState - Pre-loaded state to mutate (e.g. one that already carries
|
|
37
|
+
* unsaved mutations such as recorded review history). Pass `null`
|
|
38
|
+
* (NO_FEATURE_STATE_OVERRIDE) to load fresh from disk. Either way it is saved
|
|
39
|
+
* after incrementing.
|
|
40
|
+
*
|
|
41
|
+
* @returns the saved FeatureState (with the incremented counter), or `null` if no
|
|
42
|
+
* feature state exists (counter unchanged). Callers reuse the returned state for
|
|
43
|
+
* downstream env/widget sync to avoid a redundant disk read.
|
|
44
|
+
*/
|
|
45
|
+
export function startReviewIteration(
|
|
46
|
+
handler: FeatureSession,
|
|
47
|
+
slug: string,
|
|
48
|
+
phase: "design" | "plan",
|
|
49
|
+
featureState: FeatureState | null,
|
|
50
|
+
): FeatureState | null {
|
|
51
|
+
const state = featureState ?? handler.getActiveFeatureState();
|
|
52
|
+
if (!state) {
|
|
53
|
+
log.warn(`startReviewIteration: no feature state for '${slug}' — counter unchanged`);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const obj = phase === "design" ? state.design : state.plan;
|
|
58
|
+
const current = obj.reviewLoopCount ?? 0;
|
|
59
|
+
obj.reviewLoopCount = current + 1;
|
|
60
|
+
obj.reviewActive = REVIEW_ACTIVE;
|
|
61
|
+
handler.setReviewActiveFlag(phase, REVIEW_ACTIVE);
|
|
62
|
+
saveFeatureState(state, DEFAULT_DIR);
|
|
63
|
+
|
|
64
|
+
log.info(`[workflow] startReviewIteration: phase=${phase} slug=${slug} counter ${current}→${current + 1}`);
|
|
65
|
+
return state;
|
|
66
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// SPDX-FileCopyrightText: 2026 avtc <tarasenkov@gmail.com>
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Reviewer empty-loop tracking.
|
|
6
|
+
*
|
|
7
|
+
* Tracks how many consecutive empty (no-change) loops each reviewer has produced
|
|
8
|
+
* per feature slug. Used to decide when to skip a reviewer that keeps producing
|
|
9
|
+
* empty results (configurable threshold).
|
|
10
|
+
*
|
|
11
|
+
* State is in-memory only — not persisted to disk.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export class EmptyLoopTracker {
|
|
15
|
+
private readonly loops = new Map<string, Map<string, number>>();
|
|
16
|
+
|
|
17
|
+
private getEmptyLoops(slug: string): Map<string, number> {
|
|
18
|
+
if (!this.loops.has(slug)) this.loops.set(slug, new Map());
|
|
19
|
+
return this.loops.get(slug) as Map<string, number>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
incrementEmptyLoop(slug: string, reviewerName: string): void {
|
|
23
|
+
const map = this.getEmptyLoops(slug);
|
|
24
|
+
map.set(reviewerName, (map.get(reviewerName) ?? 0) + 1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
resetEmptyLoop(slug: string, reviewerName: string): void {
|
|
28
|
+
this.getEmptyLoops(slug).delete(reviewerName);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getEmptyLoopsForSlug(slug: string): Record<string, number> {
|
|
32
|
+
return Object.fromEntries(this.getEmptyLoops(slug));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getReviewerEmptyLoops(): Record<string, Record<string, number>> {
|
|
36
|
+
const result: Record<string, Record<string, number>> = {};
|
|
37
|
+
for (const [slug, map] of this.loops) {
|
|
38
|
+
result[slug] = Object.fromEntries(map);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
isReviewerSkipped(slug: string, reviewerName: string, threshold: number): boolean {
|
|
44
|
+
if (threshold === 0) return false;
|
|
45
|
+
const count = this.getEmptyLoops(slug).get(reviewerName) ?? 0;
|
|
46
|
+
return count >= threshold;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
resetAllEmptyLoops(): void {
|
|
50
|
+
this.loops.clear();
|
|
51
|
+
}
|
|
52
|
+
}
|