@hivehub/rulebook 5.2.1 → 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 +135 -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":"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"}
@@ -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;IAcjC;;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;
@@ -81,34 +133,30 @@ export class TaskManager {
81
133
  mkdirSync(taskPath, { recursive: true });
82
134
  mkdirSync(join(taskPath, SPECS_DIR), { recursive: true });
83
135
  // Create proposal.md template
84
- const proposalContent = `# Proposal: ${taskId}
85
-
86
- ## Why
87
- [Explain why this change is needed - minimum 20 characters]
88
-
89
- ## What Changes
90
- [Describe what will change]
91
-
92
- ## Impact
93
- - Affected specs: [list]
94
- - Affected code: [list]
95
- - Breaking change: YES/NO
96
- - 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]
97
149
  `;
98
150
  await writeFileAsync(join(taskPath, 'proposal.md'), proposalContent);
99
- // Create tasks.md template
100
- const tasksContent = `## 1. Implementation
101
- - [ ] 1.1 First task
102
- - [ ] 1.2 Second task
103
-
104
- ## 2. Testing
105
- - [ ] 2.1 Write tests
106
- - [ ] 2.2 Verify coverage
107
-
108
- ## 3. Documentation
109
- - [ ] 3.1 Update README
110
- - [ ] 3.2 Update CHANGELOG
111
- `;
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)}`;
112
160
  await writeFileAsync(join(taskPath, 'tasks.md'), tasksContent);
113
161
  // Create .metadata.json with initial status
114
162
  const now = new Date().toISOString();
@@ -118,8 +166,9 @@ export class TaskManager {
118
166
  updatedAt: now,
119
167
  };
120
168
  await writeFileAsync(join(taskPath, '.metadata.json'), JSON.stringify(metadata, null, 2));
121
- // Update tasks README index
169
+ // Update tasks README index + STATE.md
122
170
  await this.updateReadme();
171
+ await this.refreshState();
123
172
  }
124
173
  /**
125
174
  * Generate and update README.md index in the tasks root directory.
@@ -156,17 +205,21 @@ export class TaskManager {
156
205
  };
157
206
  const statusIcon = (status) => {
158
207
  switch (status) {
159
- case 'completed': return '✅';
160
- case 'in-progress': return '🔄';
161
- case 'blocked': return '🚫';
162
- default: return '';
208
+ case 'completed':
209
+ return '';
210
+ case 'in-progress':
211
+ return '🔄';
212
+ case 'blocked':
213
+ return '🚫';
214
+ default:
215
+ return '⬚';
163
216
  }
164
217
  };
165
218
  let readme = `# Tasks Index\n\n`;
166
219
  readme += `> Auto-generated by rulebook. Do not edit manually.\n\n`;
167
220
  const totalTasks = tasks.length;
168
- const completedTasks = tasks.filter(t => t.status === 'completed').length;
169
- 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;
170
223
  readme += `**Total**: ${totalTasks} tasks | **Completed**: ${completedTasks} | **In Progress**: ${inProgressTasks} | **Pending**: ${totalTasks - completedTasks - inProgressTasks}\n\n`;
171
224
  for (const [phaseKey, phaseTasks] of phases) {
172
225
  readme += `## ${phaseKey}\n\n`;
@@ -174,9 +227,7 @@ export class TaskManager {
174
227
  readme += `|--------|------|----------|-------------|\n`;
175
228
  for (const task of phaseTasks) {
176
229
  const progress = getProgress(task);
177
- const progressStr = progress.total > 0
178
- ? `${progress.done}/${progress.total}`
179
- : '-';
230
+ const progressStr = progress.total > 0 ? `${progress.done}/${progress.total}` : '-';
180
231
  // Extract short description from task ID (after phase prefix)
181
232
  const desc = task.id.replace(/^phase\d+[a-z]?_/, '').replace(/-/g, ' ');
182
233
  readme += `| ${statusIcon(task.status)} | ${task.id} | ${progressStr} | ${desc} |\n`;
@@ -364,6 +415,16 @@ export class TaskManager {
364
415
  errors.push('Purpose section (## Why) must have at least 20 characters');
365
416
  }
366
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
+ }
367
428
  // Validate specs
368
429
  if (!task.specs || Object.keys(task.specs).length === 0) {
369
430
  warnings.push('No spec files found (specs/*/spec.md)');
@@ -432,8 +493,9 @@ export class TaskManager {
432
493
  // Move task to archive using cross-platform filesystem operations
433
494
  const { renameSync } = await import('fs');
434
495
  renameSync(taskPath, archiveTaskPath);
435
- // Update tasks README index
496
+ // Update tasks README index + STATE.md
436
497
  await this.updateReadme();
498
+ await this.refreshState();
437
499
  }
438
500
  /**
439
501
  * Update task status
@@ -454,8 +516,43 @@ export class TaskManager {
454
516
  updatedAt: task.updatedAt,
455
517
  };
456
518
  await writeFileUtil(metadataPath, JSON.stringify(metadata, null, 2));
457
- // Update tasks README index
519
+ // Update tasks README index + STATE.md
458
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
+ }
459
556
  }
460
557
  /**
461
558
  * Get raw task metadata (including blocks/blockedBy/cascadeImpact for v5 blocker tracking)