@hivehub/rulebook 5.2.0 → 5.3.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.
Files changed (157) hide show
  1. package/.claude/commands/analysis.md +35 -0
  2. package/.claude/commands/rulebook-task-apply.md +7 -25
  3. package/.claude/commands/rulebook-task-archive.md +10 -19
  4. package/.claude/commands/rulebook-task-create.md +1 -1
  5. package/README.md +354 -965
  6. package/dist/cli/commands/analysis.d.ts +8 -0
  7. package/dist/cli/commands/analysis.d.ts.map +1 -0
  8. package/dist/cli/commands/analysis.js +78 -0
  9. package/dist/cli/commands/analysis.js.map +1 -0
  10. package/dist/cli/commands/context-intelligence.d.ts +33 -0
  11. package/dist/cli/commands/context-intelligence.d.ts.map +1 -0
  12. package/dist/cli/commands/context-intelligence.js +181 -0
  13. package/dist/cli/commands/context-intelligence.js.map +1 -0
  14. package/dist/cli/commands/index.d.ts +19 -0
  15. package/dist/cli/commands/index.d.ts.map +1 -0
  16. package/dist/cli/commands/index.js +19 -0
  17. package/dist/cli/commands/index.js.map +1 -0
  18. package/dist/cli/commands/init.d.ts +15 -0
  19. package/dist/cli/commands/init.d.ts.map +1 -0
  20. package/dist/cli/commands/init.js +608 -0
  21. package/dist/cli/commands/init.js.map +1 -0
  22. package/dist/cli/commands/mcp.d.ts +10 -0
  23. package/dist/cli/commands/mcp.d.ts.map +1 -0
  24. package/dist/cli/commands/mcp.js +128 -0
  25. package/dist/cli/commands/mcp.js.map +1 -0
  26. package/dist/cli/commands/memory.d.ts +24 -0
  27. package/dist/cli/commands/memory.d.ts.map +1 -0
  28. package/dist/cli/commands/memory.js +265 -0
  29. package/dist/cli/commands/memory.js.map +1 -0
  30. package/dist/cli/commands/misc.d.ts +33 -0
  31. package/dist/cli/commands/misc.d.ts.map +1 -0
  32. package/dist/cli/commands/misc.js +576 -0
  33. package/dist/cli/commands/misc.js.map +1 -0
  34. package/dist/cli/commands/plans.d.ts +15 -0
  35. package/dist/cli/commands/plans.d.ts.map +1 -0
  36. package/dist/cli/commands/plans.js +266 -0
  37. package/dist/cli/commands/plans.js.map +1 -0
  38. package/dist/cli/commands/ralph.d.ts +45 -0
  39. package/dist/cli/commands/ralph.d.ts.map +1 -0
  40. package/dist/cli/commands/ralph.js +694 -0
  41. package/dist/cli/commands/ralph.js.map +1 -0
  42. package/dist/cli/commands/skills.d.ts +9 -0
  43. package/dist/cli/commands/skills.d.ts.map +1 -0
  44. package/dist/cli/commands/skills.js +249 -0
  45. package/dist/cli/commands/skills.js.map +1 -0
  46. package/dist/cli/commands/task.d.ts +16 -0
  47. package/dist/cli/commands/task.d.ts.map +1 -0
  48. package/dist/cli/commands/task.js +256 -0
  49. package/dist/cli/commands/task.js.map +1 -0
  50. package/dist/cli/commands/update.d.ts +14 -0
  51. package/dist/cli/commands/update.d.ts.map +1 -0
  52. package/dist/cli/commands/update.js +636 -0
  53. package/dist/cli/commands/update.js.map +1 -0
  54. package/dist/cli/commands/workspace.d.ts +6 -0
  55. package/dist/cli/commands/workspace.d.ts.map +1 -0
  56. package/dist/cli/commands/workspace.js +141 -0
  57. package/dist/cli/commands/workspace.js.map +1 -0
  58. package/dist/core/agent-template-engine.js +28 -28
  59. package/dist/core/analysis-manager.d.ts +56 -0
  60. package/dist/core/analysis-manager.d.ts.map +1 -0
  61. package/dist/core/analysis-manager.js +218 -0
  62. package/dist/core/analysis-manager.js.map +1 -0
  63. package/dist/core/claude-md-generator.d.ts +52 -0
  64. package/dist/core/claude-md-generator.d.ts.map +1 -0
  65. package/dist/core/claude-md-generator.js +104 -0
  66. package/dist/core/claude-md-generator.js.map +1 -0
  67. package/dist/core/claude-settings-manager.d.ts +37 -0
  68. package/dist/core/claude-settings-manager.d.ts.map +1 -0
  69. package/dist/core/claude-settings-manager.js +168 -0
  70. package/dist/core/claude-settings-manager.js.map +1 -0
  71. package/dist/core/compact-context-manager.d.ts +34 -0
  72. package/dist/core/compact-context-manager.d.ts.map +1 -0
  73. package/dist/core/compact-context-manager.js +60 -0
  74. package/dist/core/compact-context-manager.js.map +1 -0
  75. package/dist/core/doctor.d.ts +19 -0
  76. package/dist/core/doctor.d.ts.map +1 -0
  77. package/dist/core/doctor.js +163 -0
  78. package/dist/core/doctor.js.map +1 -0
  79. package/dist/core/generator.js +28 -28
  80. package/dist/core/mcp-reference-generator.d.ts +13 -0
  81. package/dist/core/mcp-reference-generator.d.ts.map +1 -0
  82. package/dist/core/mcp-reference-generator.js +66 -0
  83. package/dist/core/mcp-reference-generator.js.map +1 -0
  84. package/dist/core/merger.d.ts +35 -0
  85. package/dist/core/merger.d.ts.map +1 -1
  86. package/dist/core/merger.js +120 -0
  87. package/dist/core/merger.js.map +1 -1
  88. package/dist/core/prd-generator.d.ts.map +1 -1
  89. package/dist/core/prd-generator.js +7 -1
  90. package/dist/core/prd-generator.js.map +1 -1
  91. package/dist/core/ralph-manager.d.ts.map +1 -1
  92. package/dist/core/ralph-manager.js +17 -0
  93. package/dist/core/ralph-manager.js.map +1 -1
  94. package/dist/core/rules-generator.d.ts +73 -0
  95. package/dist/core/rules-generator.d.ts.map +1 -0
  96. package/dist/core/rules-generator.js +201 -0
  97. package/dist/core/rules-generator.js.map +1 -0
  98. package/dist/core/state-writer.d.ts +35 -0
  99. package/dist/core/state-writer.d.ts.map +1 -0
  100. package/dist/core/state-writer.js +81 -0
  101. package/dist/core/state-writer.js.map +1 -0
  102. package/dist/core/task-manager.d.ts +35 -0
  103. package/dist/core/task-manager.d.ts.map +1 -1
  104. package/dist/core/task-manager.js +137 -38
  105. package/dist/core/task-manager.js.map +1 -1
  106. package/dist/core/telemetry.d.ts +29 -0
  107. package/dist/core/telemetry.d.ts.map +1 -0
  108. package/dist/core/telemetry.js +57 -0
  109. package/dist/core/telemetry.js.map +1 -0
  110. package/dist/core/workflow-generator.d.ts.map +1 -1
  111. package/dist/core/workflow-generator.js +2 -177
  112. package/dist/core/workflow-generator.js.map +1 -1
  113. package/dist/index.js +28 -1
  114. package/dist/index.js.map +1 -1
  115. package/dist/mcp/rulebook-server.d.ts.map +1 -1
  116. package/dist/mcp/rulebook-server.js +190 -7
  117. package/dist/mcp/rulebook-server.js.map +1 -1
  118. package/dist/memory/memory-store.js +91 -91
  119. package/dist/types.d.ts +11 -0
  120. package/dist/types.d.ts.map +1 -1
  121. package/dist/utils/gitignore.d.ts +10 -0
  122. package/dist/utils/gitignore.d.ts.map +1 -0
  123. package/dist/utils/gitignore.js +38 -0
  124. package/dist/utils/gitignore.js.map +1 -0
  125. package/package.json +1 -1
  126. package/templates/compact-context/_default.md +23 -0
  127. package/templates/compact-context/cpp.md +26 -0
  128. package/templates/compact-context/go.md +26 -0
  129. package/templates/compact-context/python.md +26 -0
  130. package/templates/compact-context/rust.md +28 -0
  131. package/templates/compact-context/typescript.md +29 -0
  132. package/templates/core/CLAUDE_MD_v2.md +71 -0
  133. package/templates/hooks/check-context-and-handoff.ps1 +50 -0
  134. package/templates/hooks/check-context-and-handoff.sh +69 -0
  135. package/templates/hooks/enforce-mcp-for-tasks.sh +31 -0
  136. package/templates/hooks/enforce-no-deferred.sh +21 -0
  137. package/templates/hooks/enforce-no-shortcuts.sh +31 -0
  138. package/templates/hooks/enforce-team-for-background-agents.ps1 +63 -0
  139. package/templates/hooks/enforce-team-for-background-agents.sh +55 -0
  140. package/templates/hooks/on-compact-reinject.sh +34 -0
  141. package/templates/hooks/resume-from-handoff.ps1 +33 -0
  142. package/templates/hooks/resume-from-handoff.sh +55 -0
  143. package/templates/rules/consult-analysis-before-implementing.md +23 -0
  144. package/templates/rules/cpp.md +46 -0
  145. package/templates/rules/csharp.md +44 -0
  146. package/templates/rules/diagnostic-first.md +39 -0
  147. package/templates/rules/fail-twice-escalate.md +46 -0
  148. package/templates/rules/go.md +40 -0
  149. package/templates/rules/java.md +43 -0
  150. package/templates/rules/javascript.md +39 -0
  151. package/templates/rules/multi-agent-teams.md +75 -0
  152. package/templates/rules/python.md +43 -0
  153. package/templates/rules/respect-handoff-trigger.md +41 -0
  154. package/templates/rules/rust.md +40 -0
  155. package/templates/rules/typescript.md +40 -0
  156. package/templates/skills/dev/analysis/SKILL.md +19 -0
  157. package/templates/skills/dev/handoff/SKILL.md +27 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules-generator.d.ts","sourceRoot":"","sources":["../../src/core/rules-generator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,SAAS,kBAAkB,CAAC;AACzC,eAAO,MAAM,kBAAkB,0CAA0C,CAAC;AAE1E;;;GAGG;AACH,eAAO,MAAM,wBAAwB,wFAS3B,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,eAAe,8IAMlB,CAAC;AAEX,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAkB9E,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4EAA4E;IAC5E,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,qBAAqB,GAAG,MAAM,CAExF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAMlF;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAMvF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,GAC5C,OAAO,CAAC,qBAAqB,CAAC,CA2DhC;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC;IAC7B,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACxB;AAED,wBAAsB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAuBpF"}
@@ -0,0 +1,201 @@
1
+ import path from 'path';
2
+ import { readFile, writeFile, fileExists, ensureDir } from '../utils/file-system.js';
3
+ import { getTemplatesDir } from './generator.js';
4
+ /**
5
+ * v5.3.0 `.claude/rules/` generator.
6
+ *
7
+ * For every detected language that rulebook ships a template for, emit
8
+ * `.claude/rules/<language>.md` with YAML `paths:` frontmatter. These are
9
+ * Anthropic's path-scoped rules (loaded only when Claude reads a file
10
+ * matching the glob), so they cost zero context budget outside of the
11
+ * relevant file types.
12
+ *
13
+ * Files generated by rulebook carry a sentinel comment immediately after
14
+ * the frontmatter. On `rulebook update`, any `.claude/rules/*.md` without
15
+ * the sentinel is treated as user-authored and left alone. This lets users
16
+ * adopt a generated rule file as their own by simply deleting the sentinel
17
+ * comment line.
18
+ */
19
+ export const RULES_DIR = '.claude/rules';
20
+ export const GENERATED_SENTINEL = 'Generated by @hivehub/rulebook v5.3.0';
21
+ /**
22
+ * Languages with shipped rule templates. Adding a new entry here requires a
23
+ * matching `templates/rules/<slug>.md` file.
24
+ */
25
+ export const SUPPORTED_RULE_LANGUAGES = [
26
+ 'typescript',
27
+ 'javascript',
28
+ 'rust',
29
+ 'python',
30
+ 'go',
31
+ 'cpp',
32
+ 'java',
33
+ 'csharp',
34
+ ];
35
+ /**
36
+ * Generic (non-language) rules shipped by rulebook v5.3.0.
37
+ * These are always emitted regardless of detection result.
38
+ */
39
+ export const ALWAYS_ON_RULES = [
40
+ 'multi-agent-teams',
41
+ 'consult-analysis-before-implementing',
42
+ 'respect-handoff-trigger',
43
+ 'diagnostic-first',
44
+ 'fail-twice-escalate',
45
+ ];
46
+ /**
47
+ * Detected languages from the `detector.ts` pipeline may use aliases
48
+ * (e.g. `"c"` — which rulebook treats as C/C++ and routes to the `cpp`
49
+ * template). Map them here.
50
+ */
51
+ const LANGUAGE_ALIASES = {
52
+ c: 'cpp',
53
+ 'c++': 'cpp',
54
+ cs: 'csharp',
55
+ 'c#': 'csharp',
56
+ ts: 'typescript',
57
+ js: 'javascript',
58
+ py: 'python',
59
+ rs: 'rust',
60
+ };
61
+ export function getRulesDir(projectRoot) {
62
+ return path.join(projectRoot, RULES_DIR);
63
+ }
64
+ export function getRulePath(projectRoot, language) {
65
+ return path.join(getRulesDir(projectRoot), `${language}.md`);
66
+ }
67
+ /**
68
+ * Normalize a detected language string to a supported rule template slug.
69
+ * Returns null if rulebook does not ship a template for it.
70
+ */
71
+ export function resolveRuleLanguage(detected) {
72
+ const lower = detected.toLowerCase().trim();
73
+ if (SUPPORTED_RULE_LANGUAGES.includes(lower)) {
74
+ return lower;
75
+ }
76
+ return LANGUAGE_ALIASES[lower] ?? null;
77
+ }
78
+ /**
79
+ * Read a language rule template from the package's `templates/rules/`
80
+ * directory.
81
+ */
82
+ export async function readRuleTemplate(language) {
83
+ const templatePath = path.join(getTemplatesDir(), 'rules', `${language}.md`);
84
+ if (!(await fileExists(templatePath))) {
85
+ throw new Error(`Rule template not found at ${templatePath}`);
86
+ }
87
+ return await readFile(templatePath);
88
+ }
89
+ /**
90
+ * Check whether an existing rule file was generated by rulebook. Returns
91
+ * true only if the canonical sentinel comment is present. User files that
92
+ * the user "adopted" by deleting the sentinel return false and are
93
+ * preserved across updates.
94
+ */
95
+ export function hasGeneratedSentinel(content) {
96
+ return content.includes(GENERATED_SENTINEL);
97
+ }
98
+ /**
99
+ * Generate `.claude/rules/<language>.md` for every detected language that
100
+ * has a shipped template. User-authored rules (no sentinel) are preserved.
101
+ */
102
+ export async function generateRules(projectRoot, detection) {
103
+ const rulesDir = getRulesDir(projectRoot);
104
+ await ensureDir(rulesDir);
105
+ const result = {
106
+ written: [],
107
+ preserved: [],
108
+ unsupported: [],
109
+ };
110
+ // Deduplicate: a project might detect both "typescript" and "javascript";
111
+ // we emit both rules since both apply. But if two detection entries map
112
+ // to the same template slug, only write once.
113
+ const seen = new Set();
114
+ for (const entry of detection.languages) {
115
+ const slug = resolveRuleLanguage(entry.language);
116
+ if (!slug) {
117
+ result.unsupported.push(entry.language);
118
+ continue;
119
+ }
120
+ if (seen.has(slug))
121
+ continue;
122
+ seen.add(slug);
123
+ const targetPath = getRulePath(projectRoot, slug);
124
+ if (await fileExists(targetPath)) {
125
+ const existing = await readFile(targetPath);
126
+ if (!hasGeneratedSentinel(existing)) {
127
+ // User-owned file — never touch it.
128
+ result.preserved.push(targetPath);
129
+ continue;
130
+ }
131
+ }
132
+ const template = await readRuleTemplate(slug);
133
+ await writeFile(targetPath, template);
134
+ result.written.push(targetPath);
135
+ }
136
+ // Always-on rules (not language-scoped) — emit if sentinel-based skip allows
137
+ for (const ruleName of ALWAYS_ON_RULES) {
138
+ const targetPath = path.join(rulesDir, `${ruleName}.md`);
139
+ if (await fileExists(targetPath)) {
140
+ const existing = await readFile(targetPath);
141
+ if (!hasGeneratedSentinel(existing)) {
142
+ result.preserved.push(targetPath);
143
+ continue;
144
+ }
145
+ }
146
+ const templatePath = path.join(getTemplatesDir(), 'rules', `${ruleName}.md`);
147
+ if (await fileExists(templatePath)) {
148
+ const template = await readFile(templatePath);
149
+ await writeFile(targetPath, template);
150
+ result.written.push(targetPath);
151
+ }
152
+ }
153
+ return result;
154
+ }
155
+ export async function listRulesWithSource(projectRoot) {
156
+ const rulesDir = getRulesDir(projectRoot);
157
+ if (!(await fileExists(rulesDir)))
158
+ return [];
159
+ const { promises: fs } = await import('fs');
160
+ const entries = await fs.readdir(rulesDir, { withFileTypes: true });
161
+ const results = [];
162
+ for (const entry of entries) {
163
+ if (!entry.isFile() || !entry.name.endsWith('.md'))
164
+ continue;
165
+ const filePath = path.join(rulesDir, entry.name);
166
+ const content = await readFile(filePath);
167
+ const source = hasGeneratedSentinel(content) ? 'generated' : 'user';
168
+ results.push({
169
+ name: entry.name.replace(/\.md$/, ''),
170
+ path: filePath,
171
+ source,
172
+ paths: extractPathsFrontmatter(content),
173
+ });
174
+ }
175
+ results.sort((a, b) => a.name.localeCompare(b.name));
176
+ return results;
177
+ }
178
+ /**
179
+ * Extract the `paths:` field from a YAML frontmatter block at the top of a
180
+ * markdown file. Returns null if no frontmatter or no `paths:` field.
181
+ * Intentionally simple — handles the exact shape the shipped templates use,
182
+ * not arbitrary YAML. Callers that need full YAML support should use a real
183
+ * parser.
184
+ */
185
+ function extractPathsFrontmatter(content) {
186
+ // Handle both LF and CRLF line endings (Windows writes CRLF)
187
+ const normalized = content.replace(/\r\n/g, '\n');
188
+ const match = normalized.match(/^---\n([\s\S]*?)\n---/);
189
+ if (!match)
190
+ return null;
191
+ const frontmatter = match[1];
192
+ const pathsMatch = frontmatter.match(/^paths:\s*\n((?:\s*-\s+.+\n?)+)/m);
193
+ if (!pathsMatch)
194
+ return null;
195
+ const lines = pathsMatch[1].split('\n').filter((l) => l.trim().startsWith('-'));
196
+ return lines.map((l) => l
197
+ .replace(/^\s*-\s+/, '')
198
+ .replace(/^["']|["']$/g, '')
199
+ .trim());
200
+ }
201
+ //# sourceMappingURL=rules-generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules-generator.js","sourceRoot":"","sources":["../../src/core/rules-generator.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAErF,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD;;;;;;;;;;;;;;GAcG;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,eAAe,CAAC;AACzC,MAAM,CAAC,MAAM,kBAAkB,GAAG,uCAAuC,CAAC;AAE1E;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,YAAY;IACZ,YAAY;IACZ,MAAM;IACN,QAAQ;IACR,IAAI;IACJ,KAAK;IACL,MAAM;IACN,QAAQ;CACA,CAAC;AAEX;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,mBAAmB;IACnB,sCAAsC;IACtC,yBAAyB;IACzB,kBAAkB;IAClB,qBAAqB;CACb,CAAC;AAIX;;;;GAIG;AACH,MAAM,gBAAgB,GAA0C;IAC9D,CAAC,EAAE,KAAK;IACR,KAAK,EAAE,KAAK;IACZ,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,QAAQ;IACd,EAAE,EAAE,YAAY;IAChB,EAAE,EAAE,YAAY;IAChB,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,MAAM;CACX,CAAC;AAWF,MAAM,UAAU,WAAW,CAAC,WAAmB;IAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,WAAmB,EAAE,QAA+B;IAC9E,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,EAAE,GAAG,QAAQ,KAAK,CAAC,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;IAC5C,IAAI,wBAAwB,CAAC,QAAQ,CAAC,KAA8B,CAAC,EAAE,CAAC;QACtE,OAAO,KAA8B,CAAC;IACxC,CAAC;IACD,OAAO,gBAAgB,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,QAA+B;IACpE,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,OAAO,EAAE,GAAG,QAAQ,KAAK,CAAC,CAAC;IAC7E,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,8BAA8B,YAAY,EAAE,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;AACtC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAe;IAClD,OAAO,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,WAAmB,EACnB,SAA6C;IAE7C,MAAM,QAAQ,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IAC1C,MAAM,SAAS,CAAC,QAAQ,CAAC,CAAC;IAE1B,MAAM,MAAM,GAA0B;QACpC,OAAO,EAAE,EAAE;QACX,SAAS,EAAE,EAAE;QACb,WAAW,EAAE,EAAE;KAChB,CAAC;IAEF,0EAA0E;IAC1E,wEAAwE;IACxE,8CAA8C;IAC9C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE9C,KAAK,MAAM,KAAK,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACjD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACxC,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAC7B,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAEf,MAAM,UAAU,GAAG,WAAW,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QAElD,IAAI,MAAM,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YACjC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC5C,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACpC,oCAAoC;gBACpC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAClC,SAAS;YACX,CAAC;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QACtC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAClC,CAAC;IAED,6EAA6E;IAC7E,KAAK,MAAM,QAAQ,IAAI,eAAe,EAAE,CAAC;QACvC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,QAAQ,KAAK,CAAC,CAAC;QACzD,IAAI,MAAM,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YACjC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,CAAC;YAC5C,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACpC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAClC,SAAS;YACX,CAAC;QACH,CAAC;QACD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,OAAO,EAAE,GAAG,QAAQ,KAAK,CAAC,CAAC;QAC7E,IAAI,MAAM,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;YAC9C,MAAM,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YACtC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAaD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,WAAmB;IAC3D,MAAM,QAAQ,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC;IAC1C,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAE7C,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACpE,MAAM,OAAO,GAAiB,EAAE,CAAC;IAEjC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,MAAM,GAAyB,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC;QAC1F,OAAO,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;YACrC,IAAI,EAAE,QAAQ;YACd,MAAM;YACN,KAAK,EAAE,uBAAuB,CAAC,OAAO,CAAC;SACxC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACrD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,uBAAuB,CAAC,OAAe;IAC9C,6DAA6D;IAC7D,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACxD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACzE,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAChF,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACrB,CAAC;SACE,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;SAC3B,IAAI,EAAE,CACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * v5.3.0 F3 — `.rulebook/STATE.md` auto-regeneration.
3
+ *
4
+ * Renders a short (<40 line) machine-written status file imported by
5
+ * CLAUDE.md via `@.rulebook/STATE.md`. Updated on every task state
6
+ * change or Ralph iteration. Users can opt out by adding a
7
+ * `manual: true` YAML frontmatter flag.
8
+ */
9
+ export declare const STATE_FILE = ".rulebook/STATE.md";
10
+ export interface StateSnapshot {
11
+ activeTask?: {
12
+ id: string;
13
+ phase: string;
14
+ progress: string;
15
+ } | null;
16
+ lastRalphIteration?: number | null;
17
+ lastQualityGate?: string | null;
18
+ healthScore?: number | null;
19
+ updatedAt: string;
20
+ }
21
+ export declare function getStatePath(projectRoot: string): string;
22
+ /**
23
+ * Check whether the STATE.md has `manual: true` frontmatter, meaning
24
+ * the user maintains it by hand and rulebook should not touch it.
25
+ */
26
+ export declare function isManuallyMaintained(projectRoot: string): Promise<boolean>;
27
+ /**
28
+ * Write or overwrite `.rulebook/STATE.md` with the given snapshot.
29
+ * No-op if the file has `manual: true` frontmatter.
30
+ */
31
+ export declare function writeState(projectRoot: string, snapshot: StateSnapshot): Promise<{
32
+ path: string;
33
+ written: boolean;
34
+ }>;
35
+ //# sourceMappingURL=state-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-writer.d.ts","sourceRoot":"","sources":["../../src/core/state-writer.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AAEH,eAAO,MAAM,UAAU,uBAAuB,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,UAAU,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpE,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKhF;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,aAAa,GACtB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAU7C"}
@@ -0,0 +1,81 @@
1
+ import path from 'path';
2
+ import { writeFile, fileExists, readFile, ensureDir } from '../utils/file-system.js';
3
+ /**
4
+ * v5.3.0 F3 — `.rulebook/STATE.md` auto-regeneration.
5
+ *
6
+ * Renders a short (<40 line) machine-written status file imported by
7
+ * CLAUDE.md via `@.rulebook/STATE.md`. Updated on every task state
8
+ * change or Ralph iteration. Users can opt out by adding a
9
+ * `manual: true` YAML frontmatter flag.
10
+ */
11
+ export const STATE_FILE = '.rulebook/STATE.md';
12
+ export function getStatePath(projectRoot) {
13
+ return path.join(projectRoot, STATE_FILE);
14
+ }
15
+ /**
16
+ * Check whether the STATE.md has `manual: true` frontmatter, meaning
17
+ * the user maintains it by hand and rulebook should not touch it.
18
+ */
19
+ export async function isManuallyMaintained(projectRoot) {
20
+ const target = getStatePath(projectRoot);
21
+ if (!(await fileExists(target)))
22
+ return false;
23
+ const content = await readFile(target);
24
+ return /^---\n[\s\S]*?manual:\s*true/m.test(content);
25
+ }
26
+ /**
27
+ * Write or overwrite `.rulebook/STATE.md` with the given snapshot.
28
+ * No-op if the file has `manual: true` frontmatter.
29
+ */
30
+ export async function writeState(projectRoot, snapshot) {
31
+ if (await isManuallyMaintained(projectRoot)) {
32
+ return { path: getStatePath(projectRoot), written: false };
33
+ }
34
+ const target = getStatePath(projectRoot);
35
+ await ensureDir(path.dirname(target));
36
+ const content = renderState(snapshot);
37
+ await writeFile(target, content);
38
+ return { path: target, written: true };
39
+ }
40
+ function renderState(s) {
41
+ const lines = [
42
+ '<!-- Auto-generated by rulebook v5.3.0. Do not edit — changes will be overwritten. -->',
43
+ '<!-- Add `manual: true` frontmatter to opt out of auto-generation. -->',
44
+ '',
45
+ '# Project state',
46
+ '',
47
+ `**Last updated**: ${s.updatedAt}`,
48
+ '',
49
+ ];
50
+ if (s.activeTask) {
51
+ lines.push(`## Active task`);
52
+ lines.push('');
53
+ lines.push(`- **ID**: \`${s.activeTask.id}\``);
54
+ lines.push(`- **Phase**: ${s.activeTask.phase}`);
55
+ lines.push(`- **Progress**: ${s.activeTask.progress}`);
56
+ lines.push('');
57
+ }
58
+ else {
59
+ lines.push('## Active task');
60
+ lines.push('');
61
+ lines.push('No active task.');
62
+ lines.push('');
63
+ }
64
+ if (s.lastRalphIteration != null) {
65
+ lines.push(`## Ralph`);
66
+ lines.push('');
67
+ lines.push(`- **Last iteration**: ${s.lastRalphIteration}`);
68
+ if (s.lastQualityGate) {
69
+ lines.push(`- **Quality gate**: ${s.lastQualityGate}`);
70
+ }
71
+ lines.push('');
72
+ }
73
+ if (s.healthScore != null) {
74
+ lines.push(`## Health`);
75
+ lines.push('');
76
+ lines.push(`- **Score**: ${s.healthScore}/100`);
77
+ lines.push('');
78
+ }
79
+ return lines.join('\n');
80
+ }
81
+ //# sourceMappingURL=state-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-writer.js","sourceRoot":"","sources":["../../src/core/state-writer.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,37 @@ export interface TaskValidationResult {
15
15
  errors: string[];
16
16
  warnings: string[];
17
17
  }
18
+ /**
19
+ * v5.3.0 F-NEW-3 — mandatory task tail.
20
+ *
21
+ * Every task MUST end with these three items. `createTask()` appends them
22
+ * automatically; `validateTask()` refuses any task that does not have them
23
+ * all checked; `archiveTask()` propagates that refusal.
24
+ *
25
+ * Idempotent: `renderMandatoryTail` is deterministic and `checkMandatoryTail`
26
+ * is case-insensitive on the leading verb so a user who rewords the items
27
+ * slightly (e.g. "Update docs covering the change") is still recognized.
28
+ */
29
+ export declare const MANDATORY_TAIL_ITEMS: readonly [{
30
+ readonly label: "Update or create documentation covering the implementation";
31
+ readonly match: RegExp;
32
+ }, {
33
+ readonly label: "Write tests covering the new behavior";
34
+ readonly match: RegExp;
35
+ }, {
36
+ readonly label: "Run tests and confirm they pass";
37
+ readonly match: RegExp;
38
+ }];
39
+ export declare function renderMandatoryTail(sectionNumber: number): string;
40
+ export interface TailCheckResult {
41
+ /** True when all three tail items are present in the file. */
42
+ present: boolean;
43
+ /** Labels of items that are entirely missing from the file. */
44
+ missing: string[];
45
+ /** Labels of items that are present but unchecked (`- [ ]`). */
46
+ unchecked: string[];
47
+ }
48
+ export declare function checkMandatoryTail(tasksContent: string): TailCheckResult;
18
49
  export declare class TaskManager {
19
50
  private rulebookPath;
20
51
  private tasksPath;
@@ -75,6 +106,10 @@ export declare class TaskManager {
75
106
  * Update task status
76
107
  */
77
108
  updateTaskStatus(taskId: string, status: RulebookTask['status']): Promise<void>;
109
+ /**
110
+ * v5.3.0 F3: refresh .rulebook/STATE.md after task state changes.
111
+ */
112
+ private refreshState;
78
113
  /**
79
114
  * Get raw task metadata (including blocks/blockedBy/cascadeImpact for v5 blocker tracking)
80
115
  */
@@ -1 +1 @@
1
- {"version":3,"file":"task-manager.d.ts","sourceRoot":"","sources":["../../src/core/task-manager.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,SAAS,CAAC;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAS;gBAEhB,WAAW,EAAE,MAAM,EAAE,WAAW,GAAE,MAAoB;IAMlE;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAYjC;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAInC;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAUlE;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAMlE;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgE/C;;;OAGG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAkEnC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC;IAkCxC;;OAEG;IACG,SAAS,CAAC,eAAe,GAAE,OAAe,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAyC1E;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAE,OAAe,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAsFvF;;OAEG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAkEjE;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,GAAE,OAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAoCjF;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBrF;;OAEG;IACG,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAW9E;;OAEG;IACG,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAiB5D;;OAEG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAchD;AAED,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,WAAW,GAAE,MAAoB,GAChC,WAAW,CAEb"}
1
+ {"version":3,"file":"task-manager.d.ts","sourceRoot":"","sources":["../../src/core/task-manager.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,58 @@ const README_FILE = 'README.md';
12
12
  * Examples: phase0_3.1.0-volumetric-clouds, phase3a_3.24.0-lens-flare
13
13
  */
14
14
  const PHASE_PREFIX_REGEX = /^phase\d+[a-z]?_/;
15
+ /**
16
+ * v5.3.0 F-NEW-3 — mandatory task tail.
17
+ *
18
+ * Every task MUST end with these three items. `createTask()` appends them
19
+ * automatically; `validateTask()` refuses any task that does not have them
20
+ * all checked; `archiveTask()` propagates that refusal.
21
+ *
22
+ * Idempotent: `renderMandatoryTail` is deterministic and `checkMandatoryTail`
23
+ * is case-insensitive on the leading verb so a user who rewords the items
24
+ * slightly (e.g. "Update docs covering the change") is still recognized.
25
+ */
26
+ export const MANDATORY_TAIL_ITEMS = [
27
+ {
28
+ label: 'Update or create documentation covering the implementation',
29
+ match: /^\s*-\s*\[[ x]\]\s.*(update|create).*documentation/i,
30
+ },
31
+ { label: 'Write tests covering the new behavior', match: /^\s*-\s*\[[ x]\]\s.*write.*tests?/i },
32
+ {
33
+ label: 'Run tests and confirm they pass',
34
+ match: /^\s*-\s*\[[ x]\]\s.*run.*tests?.*(pass|confirm)/i,
35
+ },
36
+ ];
37
+ export function renderMandatoryTail(sectionNumber) {
38
+ return [
39
+ `## ${sectionNumber}. Tail (mandatory — enforced by rulebook v5.3.0)`,
40
+ `- [ ] ${sectionNumber}.1 Update or create documentation covering the implementation`,
41
+ `- [ ] ${sectionNumber}.2 Write tests covering the new behavior`,
42
+ `- [ ] ${sectionNumber}.3 Run tests and confirm they pass`,
43
+ '',
44
+ ].join('\n');
45
+ }
46
+ export function checkMandatoryTail(tasksContent) {
47
+ const lines = tasksContent.split('\n');
48
+ const missing = [];
49
+ const unchecked = [];
50
+ for (const item of MANDATORY_TAIL_ITEMS) {
51
+ const matched = lines.find((line) => item.match.test(line));
52
+ if (!matched) {
53
+ missing.push(item.label);
54
+ continue;
55
+ }
56
+ // Checked if the line contains `[x]` or `[X]`; unchecked otherwise.
57
+ if (!/\[x\]/i.test(matched)) {
58
+ unchecked.push(item.label);
59
+ }
60
+ }
61
+ return {
62
+ present: missing.length === 0,
63
+ missing,
64
+ unchecked,
65
+ };
66
+ }
15
67
  export class TaskManager {
16
68
  rulebookPath;
17
69
  tasksPath;
@@ -34,6 +86,8 @@ export class TaskManager {
34
86
  if (!existsSync(this.archivePath)) {
35
87
  mkdirSync(this.archivePath, { recursive: true });
36
88
  }
89
+ // Auto-migrate legacy archive from tasks/archive to .rulebook/archive
90
+ await this.migrateArchive();
37
91
  }
38
92
  /**
39
93
  * Get the path to a task directory
@@ -79,34 +133,30 @@ export class TaskManager {
79
133
  mkdirSync(taskPath, { recursive: true });
80
134
  mkdirSync(join(taskPath, SPECS_DIR), { recursive: true });
81
135
  // Create proposal.md template
82
- const proposalContent = `# Proposal: ${taskId}
83
-
84
- ## Why
85
- [Explain why this change is needed - minimum 20 characters]
86
-
87
- ## What Changes
88
- [Describe what will change]
89
-
90
- ## Impact
91
- - Affected specs: [list]
92
- - Affected code: [list]
93
- - Breaking change: YES/NO
94
- - User benefit: [describe]
136
+ const proposalContent = `# Proposal: ${taskId}
137
+
138
+ ## Why
139
+ [Explain why this change is needed - minimum 20 characters]
140
+
141
+ ## What Changes
142
+ [Describe what will change]
143
+
144
+ ## Impact
145
+ - Affected specs: [list]
146
+ - Affected code: [list]
147
+ - Breaking change: YES/NO
148
+ - User benefit: [describe]
95
149
  `;
96
150
  await writeFileAsync(join(taskPath, 'proposal.md'), proposalContent);
97
- // Create tasks.md template
98
- const tasksContent = `## 1. Implementation
99
- - [ ] 1.1 First task
100
- - [ ] 1.2 Second task
101
-
102
- ## 2. Testing
103
- - [ ] 2.1 Write tests
104
- - [ ] 2.2 Verify coverage
105
-
106
- ## 3. Documentation
107
- - [ ] 3.1 Update README
108
- - [ ] 3.2 Update CHANGELOG
109
- `;
151
+ // Create tasks.md template — the MANDATORY tail items (v5.3.0 F-NEW-3)
152
+ // are appended automatically here AND enforced by validateTask() /
153
+ // archiveTask(): a task cannot be closed unless docs are updated,
154
+ // tests are written, and tests pass.
155
+ const tasksContent = `## 1. Implementation
156
+ - [ ] 1.1 First task
157
+ - [ ] 1.2 Second task
158
+
159
+ ${renderMandatoryTail(2)}`;
110
160
  await writeFileAsync(join(taskPath, 'tasks.md'), tasksContent);
111
161
  // Create .metadata.json with initial status
112
162
  const now = new Date().toISOString();
@@ -116,8 +166,9 @@ export class TaskManager {
116
166
  updatedAt: now,
117
167
  };
118
168
  await writeFileAsync(join(taskPath, '.metadata.json'), JSON.stringify(metadata, null, 2));
119
- // Update tasks README index
169
+ // Update tasks README index + STATE.md
120
170
  await this.updateReadme();
171
+ await this.refreshState();
121
172
  }
122
173
  /**
123
174
  * Generate and update README.md index in the tasks root directory.
@@ -154,17 +205,21 @@ export class TaskManager {
154
205
  };
155
206
  const statusIcon = (status) => {
156
207
  switch (status) {
157
- case 'completed': return '✅';
158
- case 'in-progress': return '🔄';
159
- case 'blocked': return '🚫';
160
- default: return '';
208
+ case 'completed':
209
+ return '';
210
+ case 'in-progress':
211
+ return '🔄';
212
+ case 'blocked':
213
+ return '🚫';
214
+ default:
215
+ return '⬚';
161
216
  }
162
217
  };
163
218
  let readme = `# Tasks Index\n\n`;
164
219
  readme += `> Auto-generated by rulebook. Do not edit manually.\n\n`;
165
220
  const totalTasks = tasks.length;
166
- const completedTasks = tasks.filter(t => t.status === 'completed').length;
167
- const inProgressTasks = tasks.filter(t => t.status === 'in-progress').length;
221
+ const completedTasks = tasks.filter((t) => t.status === 'completed').length;
222
+ const inProgressTasks = tasks.filter((t) => t.status === 'in-progress').length;
168
223
  readme += `**Total**: ${totalTasks} tasks | **Completed**: ${completedTasks} | **In Progress**: ${inProgressTasks} | **Pending**: ${totalTasks - completedTasks - inProgressTasks}\n\n`;
169
224
  for (const [phaseKey, phaseTasks] of phases) {
170
225
  readme += `## ${phaseKey}\n\n`;
@@ -172,9 +227,7 @@ export class TaskManager {
172
227
  readme += `|--------|------|----------|-------------|\n`;
173
228
  for (const task of phaseTasks) {
174
229
  const progress = getProgress(task);
175
- const progressStr = progress.total > 0
176
- ? `${progress.done}/${progress.total}`
177
- : '-';
230
+ const progressStr = progress.total > 0 ? `${progress.done}/${progress.total}` : '-';
178
231
  // Extract short description from task ID (after phase prefix)
179
232
  const desc = task.id.replace(/^phase\d+[a-z]?_/, '').replace(/-/g, ' ');
180
233
  readme += `| ${statusIcon(task.status)} | ${task.id} | ${progressStr} | ${desc} |\n`;
@@ -362,6 +415,16 @@ export class TaskManager {
362
415
  errors.push('Purpose section (## Why) must have at least 20 characters');
363
416
  }
364
417
  }
418
+ // v5.3.0 F-NEW-3: mandatory task tail (docs + tests + verify)
419
+ const tail = task.tasks
420
+ ? checkMandatoryTail(task.tasks)
421
+ : { present: false, missing: MANDATORY_TAIL_ITEMS.map((i) => i.label), unchecked: [] };
422
+ if (!tail.present) {
423
+ errors.push(`Mandatory task tail missing from tasks.md (required in v5.3.0): ${tail.missing.join(', ')}`);
424
+ }
425
+ else if (tail.unchecked.length > 0) {
426
+ errors.push(`Mandatory task tail items are still unchecked: ${tail.unchecked.join(', ')}`);
427
+ }
365
428
  // Validate specs
366
429
  if (!task.specs || Object.keys(task.specs).length === 0) {
367
430
  warnings.push('No spec files found (specs/*/spec.md)');
@@ -430,8 +493,9 @@ export class TaskManager {
430
493
  // Move task to archive using cross-platform filesystem operations
431
494
  const { renameSync } = await import('fs');
432
495
  renameSync(taskPath, archiveTaskPath);
433
- // Update tasks README index
496
+ // Update tasks README index + STATE.md
434
497
  await this.updateReadme();
498
+ await this.refreshState();
435
499
  }
436
500
  /**
437
501
  * Update task status
@@ -452,8 +516,43 @@ export class TaskManager {
452
516
  updatedAt: task.updatedAt,
453
517
  };
454
518
  await writeFileUtil(metadataPath, JSON.stringify(metadata, null, 2));
455
- // Update tasks README index
519
+ // Update tasks README index + STATE.md
456
520
  await this.updateReadme();
521
+ await this.refreshState();
522
+ }
523
+ /**
524
+ * v5.3.0 F3: refresh .rulebook/STATE.md after task state changes.
525
+ */
526
+ async refreshState() {
527
+ try {
528
+ const { writeState } = await import('./state-writer.js');
529
+ const tasks = await this.listTasks(false);
530
+ const active = tasks.find((t) => t.status === 'in-progress') ??
531
+ tasks.find((t) => t.status === 'pending') ??
532
+ null;
533
+ const totalItems = tasks.reduce((n, t) => {
534
+ const m = t.tasks?.match(/- \[x\]/gi);
535
+ const u = t.tasks?.match(/- \[ \]/g);
536
+ return n + (m?.length ?? 0) + (u?.length ?? 0);
537
+ }, 0);
538
+ const checkedItems = tasks.reduce((n, t) => {
539
+ const m = t.tasks?.match(/- \[x\]/gi);
540
+ return n + (m?.length ?? 0);
541
+ }, 0);
542
+ await writeState(join(this.rulebookPath, '..'), {
543
+ activeTask: active
544
+ ? {
545
+ id: active.id,
546
+ phase: active.id.match(/^(phase\d+[a-z]?)_/)?.[1] ?? '?',
547
+ progress: `${checkedItems}/${totalItems} items`,
548
+ }
549
+ : null,
550
+ updatedAt: new Date().toISOString(),
551
+ });
552
+ }
553
+ catch {
554
+ // Non-fatal — STATE.md refresh must never break task operations
555
+ }
457
556
  }
458
557
  /**
459
558
  * Get raw task metadata (including blocks/blockedBy/cascadeImpact for v5 blocker tracking)