clavix 4.11.2 → 5.0.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 (162) hide show
  1. package/README.md +72 -60
  2. package/dist/cli/commands/update.js +9 -10
  3. package/dist/templates/agents/agents.md +14 -8
  4. package/dist/templates/agents/copilot-instructions.md +1 -1
  5. package/dist/templates/instructions/core/verification.md +2 -2
  6. package/dist/templates/slash-commands/_canonical/archive.md +83 -121
  7. package/dist/templates/slash-commands/_canonical/execute.md +32 -42
  8. package/dist/templates/slash-commands/_canonical/implement.md +32 -44
  9. package/dist/templates/slash-commands/_canonical/improve.md +14 -53
  10. package/dist/templates/slash-commands/_canonical/start.md +1 -1
  11. package/dist/templates/slash-commands/_canonical/summarize.md +8 -8
  12. package/dist/templates/slash-commands/_components/agent-protocols/cli-reference.md +84 -180
  13. package/dist/templates/slash-commands/_components/agent-protocols/error-handling.md +2 -2
  14. package/dist/templates/slash-commands/_components/agent-protocols/file-formats.md +41 -59
  15. package/dist/templates/slash-commands/_components/agent-protocols/state-assertion.md +1 -1
  16. package/dist/templates/slash-commands/_components/references/intent-types.md +1 -1
  17. package/dist/templates/slash-commands/_components/sections/file-saving-protocol.md +20 -27
  18. package/dist/templates/slash-commands/_components/sections/pattern-visibility.md +29 -46
  19. package/dist/templates/slash-commands/_components/troubleshooting/file-not-saved.md +4 -5
  20. package/dist/types/config.d.ts +57 -0
  21. package/dist/utils/legacy-command-cleanup.js +31 -4
  22. package/package.json +5 -4
  23. package/dist/cli/commands/analyze.d.ts +0 -17
  24. package/dist/cli/commands/analyze.js +0 -133
  25. package/dist/cli/commands/archive.d.ts +0 -36
  26. package/dist/cli/commands/archive.js +0 -266
  27. package/dist/cli/commands/deep.d.ts +0 -17
  28. package/dist/cli/commands/deep.js +0 -170
  29. package/dist/cli/commands/execute.d.ts +0 -15
  30. package/dist/cli/commands/execute.js +0 -168
  31. package/dist/cli/commands/fast.d.ts +0 -18
  32. package/dist/cli/commands/fast.js +0 -219
  33. package/dist/cli/commands/implement.d.ts +0 -24
  34. package/dist/cli/commands/implement.js +0 -289
  35. package/dist/cli/commands/improve.d.ts +0 -32
  36. package/dist/cli/commands/improve.js +0 -250
  37. package/dist/cli/commands/list.d.ts +0 -17
  38. package/dist/cli/commands/list.js +0 -217
  39. package/dist/cli/commands/plan.d.ts +0 -21
  40. package/dist/cli/commands/plan.js +0 -297
  41. package/dist/cli/commands/prd.d.ts +0 -24
  42. package/dist/cli/commands/prd.js +0 -321
  43. package/dist/cli/commands/prompts/clear.d.ts +0 -16
  44. package/dist/cli/commands/prompts/clear.js +0 -222
  45. package/dist/cli/commands/prompts/list.d.ts +0 -8
  46. package/dist/cli/commands/prompts/list.js +0 -88
  47. package/dist/cli/commands/show.d.ts +0 -21
  48. package/dist/cli/commands/show.js +0 -191
  49. package/dist/cli/commands/start.d.ts +0 -40
  50. package/dist/cli/commands/start.js +0 -210
  51. package/dist/cli/commands/summarize.d.ts +0 -17
  52. package/dist/cli/commands/summarize.js +0 -196
  53. package/dist/cli/commands/task-complete.d.ts +0 -27
  54. package/dist/cli/commands/task-complete.js +0 -269
  55. package/dist/cli/commands/verify.d.ts +0 -28
  56. package/dist/cli/commands/verify.js +0 -349
  57. package/dist/core/archive-manager.d.ts +0 -100
  58. package/dist/core/archive-manager.js +0 -302
  59. package/dist/core/basic-checklist-generator.d.ts +0 -35
  60. package/dist/core/basic-checklist-generator.js +0 -344
  61. package/dist/core/checklist-parser.d.ts +0 -48
  62. package/dist/core/checklist-parser.js +0 -238
  63. package/dist/core/config-manager.d.ts +0 -149
  64. package/dist/core/config-manager.js +0 -230
  65. package/dist/core/conversation-analyzer.d.ts +0 -86
  66. package/dist/core/conversation-analyzer.js +0 -387
  67. package/dist/core/conversation-quality-tracker.d.ts +0 -81
  68. package/dist/core/conversation-quality-tracker.js +0 -195
  69. package/dist/core/git-manager.d.ts +0 -126
  70. package/dist/core/git-manager.js +0 -282
  71. package/dist/core/intelligence/confidence-calculator.d.ts +0 -93
  72. package/dist/core/intelligence/confidence-calculator.js +0 -124
  73. package/dist/core/intelligence/index.d.ts +0 -11
  74. package/dist/core/intelligence/index.js +0 -15
  75. package/dist/core/intelligence/intent-detector.d.ts +0 -54
  76. package/dist/core/intelligence/intent-detector.js +0 -723
  77. package/dist/core/intelligence/pattern-library.d.ts +0 -104
  78. package/dist/core/intelligence/pattern-library.js +0 -339
  79. package/dist/core/intelligence/patterns/actionability-enhancer.d.ts +0 -27
  80. package/dist/core/intelligence/patterns/actionability-enhancer.js +0 -192
  81. package/dist/core/intelligence/patterns/alternative-phrasing-generator.d.ts +0 -29
  82. package/dist/core/intelligence/patterns/alternative-phrasing-generator.js +0 -239
  83. package/dist/core/intelligence/patterns/ambiguity-detector.d.ts +0 -22
  84. package/dist/core/intelligence/patterns/ambiguity-detector.js +0 -196
  85. package/dist/core/intelligence/patterns/assumption-explicitizer.d.ts +0 -30
  86. package/dist/core/intelligence/patterns/assumption-explicitizer.js +0 -296
  87. package/dist/core/intelligence/patterns/base-pattern.d.ts +0 -192
  88. package/dist/core/intelligence/patterns/base-pattern.js +0 -103
  89. package/dist/core/intelligence/patterns/completeness-validator.d.ts +0 -27
  90. package/dist/core/intelligence/patterns/completeness-validator.js +0 -221
  91. package/dist/core/intelligence/patterns/conciseness-filter.d.ts +0 -20
  92. package/dist/core/intelligence/patterns/conciseness-filter.js +0 -92
  93. package/dist/core/intelligence/patterns/context-precision.d.ts +0 -32
  94. package/dist/core/intelligence/patterns/context-precision.js +0 -389
  95. package/dist/core/intelligence/patterns/conversation-summarizer.d.ts +0 -30
  96. package/dist/core/intelligence/patterns/conversation-summarizer.js +0 -277
  97. package/dist/core/intelligence/patterns/dependency-identifier.d.ts +0 -23
  98. package/dist/core/intelligence/patterns/dependency-identifier.js +0 -166
  99. package/dist/core/intelligence/patterns/domain-context-enricher.d.ts +0 -21
  100. package/dist/core/intelligence/patterns/domain-context-enricher.js +0 -198
  101. package/dist/core/intelligence/patterns/edge-case-identifier.d.ts +0 -30
  102. package/dist/core/intelligence/patterns/edge-case-identifier.js +0 -269
  103. package/dist/core/intelligence/patterns/error-tolerance-enhancer.d.ts +0 -22
  104. package/dist/core/intelligence/patterns/error-tolerance-enhancer.js +0 -179
  105. package/dist/core/intelligence/patterns/implicit-requirement-extractor.d.ts +0 -24
  106. package/dist/core/intelligence/patterns/implicit-requirement-extractor.js +0 -259
  107. package/dist/core/intelligence/patterns/objective-clarifier.d.ts +0 -22
  108. package/dist/core/intelligence/patterns/objective-clarifier.js +0 -126
  109. package/dist/core/intelligence/patterns/output-format-enforcer.d.ts +0 -22
  110. package/dist/core/intelligence/patterns/output-format-enforcer.js +0 -151
  111. package/dist/core/intelligence/patterns/prd-structure-enforcer.d.ts +0 -23
  112. package/dist/core/intelligence/patterns/prd-structure-enforcer.js +0 -183
  113. package/dist/core/intelligence/patterns/prerequisite-identifier.d.ts +0 -23
  114. package/dist/core/intelligence/patterns/prerequisite-identifier.js +0 -221
  115. package/dist/core/intelligence/patterns/requirement-prioritizer.d.ts +0 -24
  116. package/dist/core/intelligence/patterns/requirement-prioritizer.js +0 -134
  117. package/dist/core/intelligence/patterns/scope-definer.d.ts +0 -26
  118. package/dist/core/intelligence/patterns/scope-definer.js +0 -236
  119. package/dist/core/intelligence/patterns/step-decomposer.d.ts +0 -31
  120. package/dist/core/intelligence/patterns/step-decomposer.js +0 -242
  121. package/dist/core/intelligence/patterns/structure-organizer.d.ts +0 -31
  122. package/dist/core/intelligence/patterns/structure-organizer.js +0 -218
  123. package/dist/core/intelligence/patterns/success-criteria-enforcer.d.ts +0 -22
  124. package/dist/core/intelligence/patterns/success-criteria-enforcer.js +0 -165
  125. package/dist/core/intelligence/patterns/success-metrics-enforcer.d.ts +0 -24
  126. package/dist/core/intelligence/patterns/success-metrics-enforcer.js +0 -165
  127. package/dist/core/intelligence/patterns/technical-context-enricher.d.ts +0 -25
  128. package/dist/core/intelligence/patterns/technical-context-enricher.js +0 -165
  129. package/dist/core/intelligence/patterns/topic-coherence-analyzer.d.ts +0 -26
  130. package/dist/core/intelligence/patterns/topic-coherence-analyzer.js +0 -300
  131. package/dist/core/intelligence/patterns/user-persona-enricher.d.ts +0 -24
  132. package/dist/core/intelligence/patterns/user-persona-enricher.js +0 -141
  133. package/dist/core/intelligence/patterns/validation-checklist-creator.d.ts +0 -31
  134. package/dist/core/intelligence/patterns/validation-checklist-creator.js +0 -242
  135. package/dist/core/intelligence/quality-assessor.d.ts +0 -51
  136. package/dist/core/intelligence/quality-assessor.js +0 -505
  137. package/dist/core/intelligence/types.d.ts +0 -111
  138. package/dist/core/intelligence/types.js +0 -3
  139. package/dist/core/intelligence/universal-optimizer.d.ts +0 -84
  140. package/dist/core/intelligence/universal-optimizer.js +0 -371
  141. package/dist/core/prd-generator.d.ts +0 -76
  142. package/dist/core/prd-generator.js +0 -173
  143. package/dist/core/prompt-manager.d.ts +0 -110
  144. package/dist/core/prompt-manager.js +0 -274
  145. package/dist/core/prompt-optimizer.d.ts +0 -268
  146. package/dist/core/prompt-optimizer.js +0 -959
  147. package/dist/core/question-engine.d.ts +0 -167
  148. package/dist/core/question-engine.js +0 -356
  149. package/dist/core/session-manager.d.ts +0 -139
  150. package/dist/core/session-manager.js +0 -365
  151. package/dist/core/task-manager.d.ts +0 -211
  152. package/dist/core/task-manager.js +0 -981
  153. package/dist/core/verification-hooks.d.ts +0 -67
  154. package/dist/core/verification-hooks.js +0 -309
  155. package/dist/core/verification-manager.d.ts +0 -107
  156. package/dist/core/verification-manager.js +0 -415
  157. package/dist/index 2.js +0 -13
  158. package/dist/index.d 2.ts +0 -4
  159. package/dist/types/session.d.ts +0 -78
  160. package/dist/types/session.js +0 -8
  161. package/dist/types/verification.d.ts +0 -205
  162. package/dist/types/verification.js +0 -9
@@ -1,981 +0,0 @@
1
- /**
2
- * TaskManager - Manages PRD-based task generation and execution
3
- *
4
- * This class handles:
5
- * - Analyzing PRD documents
6
- * - Generating optimized task breakdowns
7
- * - Reading/writing tasks.md with checkbox format
8
- * - Tracking task completion state
9
- * - Managing session resume capability
10
- */
11
- import fs from 'fs-extra';
12
- import * as path from 'path';
13
- import { FileSystem } from '../utils/file-system.js';
14
- const SOURCE_FILE_MAP = {
15
- full: ['full-prd.md', 'PRD.md', 'prd.md', 'Full-PRD.md', 'FULL_PRD.md', 'FULL-PRD.md'],
16
- quick: ['quick-prd.md', 'QUICK_PRD.md'],
17
- mini: ['mini-prd.md'],
18
- prompt: ['optimized-prompt.md'],
19
- };
20
- const SOURCE_ORDER_AUTO = [
21
- 'full',
22
- 'quick',
23
- 'mini',
24
- 'prompt',
25
- ];
26
- const ALL_KNOWN_PRD_FILES = Array.from(new Set(Object.values(SOURCE_FILE_MAP).flat()));
27
- /**
28
- * TaskManager class
29
- *
30
- * Generates and manages implementation tasks from PRD documents
31
- */
32
- export class TaskManager {
33
- constructor() {
34
- // TaskManager uses Clavix Intelligence for optimization
35
- }
36
- /**
37
- * Generate tasks.md from PRD
38
- *
39
- * @param prdPath - Path to the PRD directory
40
- * @param options - Generation options
41
- * @returns Task generation result
42
- */
43
- async generateTasksFromPrd(prdPath, options = {}) {
44
- // Read the full PRD
45
- const { path: fullPrdPath, sourceType } = await this.resolvePrdFile(prdPath, options.source ?? 'auto');
46
- const prdContent = await fs.readFile(fullPrdPath, 'utf-8');
47
- // Analyze PRD and generate tasks
48
- const phases = await this.analyzePrdAndGenerateTasks(prdContent, options);
49
- // Write tasks.md
50
- const outputPath = path.join(prdPath, 'tasks.md');
51
- await this.writeTasksFile(outputPath, phases, prdContent);
52
- return {
53
- phases,
54
- totalTasks: phases.reduce((sum, phase) => sum + phase.tasks.length, 0),
55
- outputPath,
56
- sourcePath: fullPrdPath,
57
- sourceType,
58
- };
59
- }
60
- /**
61
- * Find the PRD file in a directory
62
- */
63
- async resolvePrdFile(prdPath, preferredSource) {
64
- const order = preferredSource === 'auto' ? SOURCE_ORDER_AUTO : [preferredSource];
65
- for (const source of order) {
66
- const filenames = SOURCE_FILE_MAP[source];
67
- for (const filename of filenames) {
68
- const filepath = path.join(prdPath, filename);
69
- if (await fs.pathExists(filepath)) {
70
- return { path: filepath, sourceType: source };
71
- }
72
- }
73
- }
74
- if (preferredSource !== 'auto') {
75
- throw new Error(`No PRD artifacts found for source "${preferredSource}" in ${prdPath}`);
76
- }
77
- throw new Error(`No PRD artifacts found in ${prdPath}`);
78
- }
79
- /**
80
- * Analyze PRD content and generate task breakdown
81
- */
82
- async analyzePrdAndGenerateTasks(prdContent, options) {
83
- const phases = [];
84
- // Parse PRD sections
85
- const sections = this.parsePrdSections(prdContent);
86
- const coreSection = this.getSectionByAliases(sections, [
87
- 'requirements',
88
- 'corefeatures',
89
- 'features',
90
- 'keyrequirements',
91
- ]);
92
- if (coreSection) {
93
- phases.push(...this.generatePhasesFromCoreFeatures(coreSection, options));
94
- }
95
- if (phases.length === 0 && sections.requirements) {
96
- phases.push(...this.generateTasksFromRequirements(sections.requirements, sections));
97
- }
98
- const technicalSection = this.getSectionByAliases(sections, [
99
- 'technicalrequirements',
100
- 'technicalconstraints',
101
- ]);
102
- if (technicalSection) {
103
- this.injectTechnicalConstraintsTask(phases, technicalSection, options);
104
- }
105
- const successSection = this.getSectionByAliases(sections, [
106
- 'successcriteria',
107
- 'acceptancecriteria',
108
- ]);
109
- if (successSection) {
110
- this.appendSuccessCriteriaPhase(phases, successSection, options);
111
- }
112
- if (phases.length === 0) {
113
- phases.push(this.generateDefaultPhases(prdContent));
114
- }
115
- // Ensure all tasks follow CLEAR principles (applied AFTER all tasks are added)
116
- phases.forEach((phase) => {
117
- phase.tasks = phase.tasks.map((task) => ({
118
- ...task,
119
- description: this.optimizeTaskDescription(task.description),
120
- }));
121
- });
122
- return phases;
123
- }
124
- getSectionByAliases(sections, aliases) {
125
- for (const alias of aliases) {
126
- if (sections[alias]) {
127
- return sections[alias];
128
- }
129
- }
130
- return null;
131
- }
132
- /**
133
- * Generate phases from core features with intelligent grouping
134
- * CRITICAL FIX: Group related features instead of 1 phase per bullet
135
- */
136
- generatePhasesFromCoreFeatures(coreContent, _options) {
137
- const bullets = this.extractListItems(coreContent);
138
- if (bullets.length === 0) {
139
- return [];
140
- }
141
- // GRANULARITY CONTROL: Warn if too many bullets
142
- if (bullets.length > 50) {
143
- console.warn(`Warning: PRD contains ${bullets.length} top-level features. Consider grouping related items.`);
144
- }
145
- // Group features by type/category instead of creating separate phases
146
- const groupedFeatures = this.groupFeaturesByCategory(bullets);
147
- const phases = [];
148
- let phaseNumber = 1;
149
- for (const [category, features] of Object.entries(groupedFeatures)) {
150
- const phaseName = `Phase ${phaseNumber}: ${category}`;
151
- const tasks = [];
152
- // Generate tasks for each feature in this group
153
- features.forEach((feature) => {
154
- const taskDescriptions = this.buildFeatureTaskDescriptions(feature);
155
- taskDescriptions.forEach((description) => {
156
- tasks.push({
157
- id: `${this.sanitizeId(phaseName)}-${tasks.length + 1}`,
158
- description,
159
- phase: phaseName,
160
- completed: false,
161
- prdReference: feature,
162
- });
163
- });
164
- });
165
- // GRANULARITY CONTROL: Skip empty phases
166
- if (tasks.length > 0) {
167
- phases.push({
168
- name: phaseName,
169
- tasks,
170
- });
171
- phaseNumber++;
172
- }
173
- }
174
- // GRANULARITY CONTROL: Cap total tasks (merge if exceeding)
175
- const totalTasks = phases.reduce((sum, p) => sum + p.tasks.length, 0);
176
- if (totalTasks > 50) {
177
- console.warn(`Warning: Generated ${totalTasks} tasks. Consider merging related tasks or simplifying PRD.`);
178
- }
179
- return phases;
180
- }
181
- /**
182
- * Group features by category for logical phase organization
183
- * Replaces "1 bullet = 1 phase" with intelligent grouping
184
- */
185
- groupFeaturesByCategory(features) {
186
- const groups = {
187
- 'Configuration & Setup': [],
188
- 'Core Implementation': [],
189
- 'Testing & Validation': [],
190
- Documentation: [],
191
- 'Integration & Release': [],
192
- };
193
- features.forEach((feature) => {
194
- // Config/setup phase
195
- if (/\b(config|configuration|setup|install|package|tsconfig|dependencies|environment)\b/i.test(feature)) {
196
- groups['Configuration & Setup'].push(feature);
197
- }
198
- // Testing phase
199
- else if (/\b(test|testing|coverage|validation|verify|qa)\b/i.test(feature)) {
200
- groups['Testing & Validation'].push(feature);
201
- }
202
- // Documentation phase
203
- else if (/\b(document|documentation|readme|changelog|guide|comment)\b/i.test(feature)) {
204
- groups['Documentation'].push(feature);
205
- }
206
- // Integration/release phase
207
- else if (/\b(integrate|integration|release|deploy|publish|build|distribution)\b/i.test(feature)) {
208
- groups['Integration & Release'].push(feature);
209
- }
210
- // Default to core implementation
211
- else {
212
- groups['Core Implementation'].push(feature);
213
- }
214
- });
215
- // Remove empty groups
216
- return Object.fromEntries(Object.entries(groups).filter(([_, features]) => features.length > 0));
217
- }
218
- /**
219
- * Extract top-level list items only (ignore nested bullets)
220
- * This prevents sub-bullets from being treated as separate tasks
221
- */
222
- extractListItems(sectionContent) {
223
- const items = [];
224
- const lines = sectionContent.split('\n');
225
- let inCodeBlock = false;
226
- for (const line of lines) {
227
- // Track code blocks to ignore bullets inside them
228
- if (line.trim().startsWith('```')) {
229
- inCodeBlock = !inCodeBlock;
230
- continue;
231
- }
232
- if (inCodeBlock) {
233
- continue;
234
- }
235
- // Match ONLY top-level bullets (no indentation at all)
236
- // Nested bullets (2+ spaces) are implementation details, not separate tasks
237
- const topLevelMatch = line.match(/^(?:[-*]|\d+[.)])\s+(.+)$/);
238
- if (topLevelMatch) {
239
- const value = topLevelMatch[1].trim();
240
- // Skip items that look like code examples, file paths, or implementation details
241
- if (value &&
242
- !this.looksLikeCodeOrPath(value) &&
243
- !this.looksLikeImplementationDetail(value)) {
244
- items.push(value.replace(/\s+/g, ' ').replace(/\.$/, ''));
245
- }
246
- }
247
- }
248
- return items;
249
- }
250
- /**
251
- * Detect if a line looks like code or a file path (not a task)
252
- */
253
- looksLikeCodeOrPath(text) {
254
- // File paths with extensions
255
- if (/\.(ts|js|json|md|tsx|jsx|mjs|cjs)/.test(text)) {
256
- return true;
257
- }
258
- // Code-like patterns
259
- if (text.includes('import ') || text.includes('export ') || text.includes('require(')) {
260
- return true;
261
- }
262
- // JSON-like
263
- if (text.trim().startsWith('{') || text.trim().startsWith('[')) {
264
- return true;
265
- }
266
- // Very short technical commands
267
- if (text.length < 15 && /^[a-z-]+:[a-z-]+$/i.test(text)) {
268
- return true;
269
- }
270
- return false;
271
- }
272
- /**
273
- * Detect if text is an implementation detail rather than a feature
274
- * Implementation details are constraints, requirements, or sub-steps
275
- */
276
- looksLikeImplementationDetail(text) {
277
- const lower = text.toLowerCase();
278
- // Constraints and requirements (not tasks)
279
- if (lower.includes(' must ') || lower.includes(' should ') || lower.includes(' required')) {
280
- return true;
281
- }
282
- // Very short items (< 25 chars) are likely details, not features
283
- if (text.length < 25 && !lower.startsWith('implement') && !lower.startsWith('create')) {
284
- return true;
285
- }
286
- // Specific technical constraints
287
- if (/^(password|email|session|token|rate|https)/i.test(text)) {
288
- return true;
289
- }
290
- return false;
291
- }
292
- /**
293
- * Build context-aware task descriptions based on feature type and complexity
294
- * Replaces the old 5-task boilerplate with intelligent task generation
295
- */
296
- buildFeatureTaskDescriptions(feature) {
297
- const formattedFeature = this.formatInlineText(feature);
298
- // Detect task type
299
- const isConfig = /\b(config|configuration|setup|install|update.*json|tsconfig|package\.json)\b/i.test(feature);
300
- const isTest = /\b(test|testing|coverage|validation|verify)\b/i.test(feature);
301
- const isDocumentation = /\b(document|documentation|readme|changelog|guide)\b/i.test(feature);
302
- const isConversion = /\b(convert|migrate|refactor|replace|update.*code)\b/i.test(feature);
303
- // Simple tasks (config, documentation) - Single task only
304
- if (isConfig) {
305
- return [this.convertBehaviorToTask(feature)];
306
- }
307
- if (isDocumentation) {
308
- return [this.convertBehaviorToTask(feature)];
309
- }
310
- // Testing tasks - Implementation + validation
311
- if (isTest) {
312
- return [
313
- this.convertBehaviorToTask(feature),
314
- `Verify ${formattedFeature} passes successfully`,
315
- ];
316
- }
317
- // Conversion/migration tasks - Convert + test
318
- if (isConversion) {
319
- return [this.convertBehaviorToTask(feature), `Test ${formattedFeature} works correctly`];
320
- }
321
- // Default for complex features - Implementation + testing only
322
- // (No more "integrate into end-to-end" boilerplate)
323
- return [this.convertBehaviorToTask(feature), `Add tests covering ${formattedFeature}`];
324
- }
325
- formatInlineText(text) {
326
- if (!text) {
327
- return text;
328
- }
329
- const trimmed = text.replace(/\.$/, '').trim();
330
- if (!trimmed) {
331
- return trimmed;
332
- }
333
- return trimmed.charAt(0).toLowerCase() + trimmed.slice(1);
334
- }
335
- toTitleCase(text) {
336
- const cleaned = text.replace(/\.$/, '').trim();
337
- if (!cleaned) {
338
- return 'Feature';
339
- }
340
- return cleaned
341
- .split(' ')
342
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
343
- .join(' ')
344
- .substring(0, 60);
345
- }
346
- injectTechnicalConstraintsTask(phases, technicalContent, _options) {
347
- const constraints = this.extractListItems(technicalContent);
348
- if (constraints.length === 0) {
349
- return;
350
- }
351
- const summary = constraints.slice(0, 3).join('; ');
352
- const description = `Ensure technical constraints are satisfied: ${summary}`;
353
- if (phases.length === 0) {
354
- phases.push({
355
- name: 'Phase 1: Technical Foundations',
356
- tasks: [
357
- {
358
- id: 'technical-1',
359
- description,
360
- phase: 'Phase 1: Technical Foundations',
361
- completed: false,
362
- prdReference: 'Technical Constraints',
363
- },
364
- ],
365
- });
366
- return;
367
- }
368
- const firstPhase = phases[0];
369
- firstPhase.tasks.unshift({
370
- id: `${this.sanitizeId(firstPhase.name)}-constraints`,
371
- description,
372
- phase: firstPhase.name,
373
- completed: false,
374
- prdReference: 'Technical Constraints',
375
- });
376
- }
377
- appendSuccessCriteriaPhase(phases, successContent, _options) {
378
- const criteria = this.extractListItems(successContent);
379
- if (criteria.length === 0) {
380
- return;
381
- }
382
- const selected = criteria.slice(0, 2);
383
- const phaseName = 'Phase QA: Validation & Success';
384
- const tasks = selected.map((criterion, index) => ({
385
- id: `${this.sanitizeId(phaseName)}-${index + 1}`,
386
- description: `Validate success criterion: ${this.formatInlineText(criterion)}`,
387
- phase: phaseName,
388
- completed: false,
389
- prdReference: 'Success Criteria',
390
- }));
391
- phases.push({
392
- name: phaseName,
393
- tasks,
394
- });
395
- }
396
- /**
397
- * Parse PRD into sections
398
- */
399
- parsePrdSections(prdContent) {
400
- const sections = {};
401
- const lines = prdContent.split('\n');
402
- let currentSection = '';
403
- let currentContent = [];
404
- for (const line of lines) {
405
- // Check for section headers (## or ###)
406
- if (line.match(/^##\s+(.+)/)) {
407
- // Save previous section
408
- if (currentSection) {
409
- sections[this.normalizeSectionName(currentSection)] = currentContent.join('\n').trim();
410
- }
411
- // Start new section
412
- currentSection = line.replace(/^##\s+/, '').trim();
413
- currentContent = [];
414
- }
415
- else {
416
- currentContent.push(line);
417
- }
418
- }
419
- // Save last section
420
- if (currentSection) {
421
- sections[this.normalizeSectionName(currentSection)] = currentContent.join('\n').trim();
422
- }
423
- return sections;
424
- }
425
- /**
426
- * Normalize section name for consistent lookup
427
- */
428
- normalizeSectionName(name) {
429
- return name.toLowerCase().replace(/[^a-z0-9]/g, '');
430
- }
431
- /**
432
- * Generate tasks from requirements section
433
- */
434
- generateTasksFromRequirements(requirementsContent, _allSections) {
435
- const phases = [];
436
- // Extract must-have features section (stop at next ### or ##)
437
- const mustHaveMatch = requirementsContent.match(/### Must-Have Features([\s\S]*?)(?=###|##|$)/);
438
- if (mustHaveMatch) {
439
- const featuresContent = mustHaveMatch[1];
440
- // Split by feature headers (#### Number. Feature Name)
441
- const featureHeaders = [...featuresContent.matchAll(/####\s+(\d+)\.\s+(.+)/g)];
442
- // Collect all top-level behaviors from all features
443
- const allBehaviors = [];
444
- for (let i = 0; i < featureHeaders.length; i++) {
445
- const featureName = featureHeaders[i][2].trim();
446
- // Extract content between this header and the next one
447
- const startIndex = featureHeaders[i].index;
448
- const endIndex = i < featureHeaders.length - 1 ? featureHeaders[i + 1].index : featuresContent.length;
449
- const featureContent = featuresContent.substring(startIndex, endIndex);
450
- // Extract behavior points using hierarchical parsing
451
- const behaviorMatch = featureContent.match(/\*\*Behavior\*\*:([\s\S]*?)(?=\*\*|####|$)/);
452
- if (behaviorMatch) {
453
- const behaviors = behaviorMatch[1];
454
- const bulletPoints = this.extractListItems(behaviors);
455
- // Add all top-level behaviors to the list
456
- allBehaviors.push(...bulletPoints);
457
- }
458
- else {
459
- // If no Behavior section, add the feature name itself
460
- allBehaviors.push(featureName);
461
- }
462
- }
463
- // Now group all behaviors by category (instead of 1 phase per feature)
464
- if (allBehaviors.length > 0) {
465
- const groupedFeatures = this.groupFeaturesByCategory(allBehaviors);
466
- let phaseNumber = 1;
467
- for (const [category, features] of Object.entries(groupedFeatures)) {
468
- const phaseName = `Phase ${phaseNumber}: ${category}`;
469
- const tasks = [];
470
- features.forEach((feature) => {
471
- const taskDescriptions = this.buildFeatureTaskDescriptions(feature);
472
- taskDescriptions.forEach((description) => {
473
- tasks.push({
474
- id: `${this.sanitizeId(phaseName)}-${tasks.length + 1}`,
475
- description,
476
- phase: phaseName,
477
- completed: false,
478
- prdReference: feature,
479
- });
480
- });
481
- });
482
- if (tasks.length > 0) {
483
- phases.push({
484
- name: phaseName,
485
- tasks,
486
- });
487
- phaseNumber++;
488
- }
489
- }
490
- }
491
- }
492
- // If no structured features found, create a general implementation phase
493
- if (phases.length === 0) {
494
- phases.push(this.generateDefaultPhases(requirementsContent));
495
- }
496
- return phases;
497
- }
498
- /**
499
- * Generate a phase from a feature description
500
- */
501
- generatePhaseFromFeature(featureName, featureContent, phasePrefix) {
502
- const tasks = [];
503
- // Extract behavior points using hierarchical parsing
504
- const behaviorMatch = featureContent.match(/\*\*Behavior\*\*:([\s\S]*?)(?=\*\*|####|$)/);
505
- if (behaviorMatch) {
506
- const behaviors = behaviorMatch[1];
507
- // Use the same hierarchical parsing as extractListItems()
508
- const bulletPoints = this.extractListItems(behaviors);
509
- bulletPoints.forEach((description, index) => {
510
- // Skip overly long bullets (likely multi-line descriptions)
511
- if (description.length < 200) {
512
- tasks.push({
513
- id: `${this.sanitizeId(featureName)}-${index + 1}`,
514
- description: this.convertBehaviorToTask(description),
515
- phase: phasePrefix,
516
- completed: false,
517
- prdReference: featureName,
518
- });
519
- }
520
- });
521
- }
522
- // If no behavior points, try to extract from feature description
523
- if (tasks.length === 0) {
524
- tasks.push({
525
- id: this.sanitizeId(featureName),
526
- description: `Implement ${featureName.toLowerCase()}`,
527
- phase: phasePrefix,
528
- completed: false,
529
- prdReference: featureName,
530
- });
531
- }
532
- return {
533
- name: `${phasePrefix}: ${featureName}`,
534
- tasks,
535
- };
536
- }
537
- /**
538
- * Convert behavior description to task description
539
- */
540
- convertBehaviorToTask(behavior) {
541
- // Remove ** bold markers
542
- let task = behavior.replace(/\*\*/g, '');
543
- // If starts with action verb, keep as is
544
- // Otherwise, prepend "Implement"
545
- const actionVerbs = /^(Create|Add|Implement|Build|Generate|Read|Write|Parse|Analyze|Display|Update|Handle|Process|Execute|Mark|Track|Ensure|Validate|Configure)/i;
546
- if (!actionVerbs.test(task)) {
547
- task = `Implement ${task.charAt(0).toLowerCase() + task.slice(1)}`;
548
- }
549
- return task;
550
- }
551
- /**
552
- * Generate default phases when no structure found
553
- */
554
- generateDefaultPhases(_requirementsContent) {
555
- return {
556
- name: 'Phase 1: Implementation',
557
- tasks: [
558
- {
559
- id: 'setup-1',
560
- description: 'Set up project structure and dependencies',
561
- phase: 'Phase 1',
562
- completed: false,
563
- },
564
- {
565
- id: 'implement-1',
566
- description: 'Implement core functionality as described in requirements',
567
- phase: 'Phase 1',
568
- completed: false,
569
- prdReference: 'Requirements',
570
- },
571
- {
572
- id: 'test-1',
573
- description: 'Add tests and validation',
574
- phase: 'Phase 1',
575
- completed: false,
576
- },
577
- ],
578
- };
579
- }
580
- /**
581
- * Sanitize ID for use in task IDs
582
- */
583
- sanitizeId(text) {
584
- return text
585
- .toLowerCase()
586
- .replace(/[^a-z0-9]/g, '-')
587
- .replace(/-+/g, '-')
588
- .replace(/^-|-$/g, '')
589
- .substring(0, 30);
590
- }
591
- /**
592
- * Optimize task description using CLEAR principles
593
- */
594
- optimizeTaskDescription(description) {
595
- // Ensure starts with action verb first
596
- const actionVerbs = /^(Create|Add|Implement|Build|Generate|Read|Write|Parse|Analyze|Display|Update|Handle|Process|Execute|Mark|Track|Ensure|Validate|Configure|Set up|Fix|Refactor|Test)/i;
597
- if (!actionVerbs.test(description)) {
598
- description = `Implement ${description}`;
599
- }
600
- // Ensure conciseness: limit to reasonable length (after adding action verb)
601
- if (description.length > 150) {
602
- description = description.substring(0, 147) + '...';
603
- }
604
- return description;
605
- }
606
- /**
607
- * Write tasks to tasks.md file
608
- */
609
- async writeTasksFile(outputPath, phases, prdContent) {
610
- let content = '# Implementation Tasks\n\n';
611
- // Extract project name from PRD
612
- const projectMatch = prdContent.match(/^#\s+(.+?)$/m);
613
- if (projectMatch) {
614
- content += `**Project**: ${projectMatch[1]}\n\n`;
615
- }
616
- content += `**Generated**: ${new Date().toLocaleString()}\n\n`;
617
- content += '---\n\n';
618
- // Add phases and tasks
619
- for (const phase of phases) {
620
- content += `## ${phase.name}\n\n`;
621
- for (const task of phase.tasks) {
622
- const checkbox = task.completed ? '[x]' : '[ ]';
623
- const reference = task.prdReference ? ` (ref: ${task.prdReference})` : '';
624
- content += `- ${checkbox} ${task.description}${reference}\n`;
625
- content += ` Task ID: ${task.id}\n`;
626
- }
627
- content += '\n';
628
- }
629
- // Add footer
630
- content += '---\n\n';
631
- content += '*Generated by Clavix /clavix:plan*\n';
632
- await FileSystem.writeFileAtomic(outputPath, content);
633
- }
634
- /**
635
- * Read tasks from tasks.md file
636
- */
637
- async readTasksFile(tasksPath) {
638
- if (!(await fs.pathExists(tasksPath))) {
639
- throw new Error(`Tasks file not found: ${tasksPath}`);
640
- }
641
- const content = await fs.readFile(tasksPath, 'utf-8');
642
- return this.parseTasksFile(content);
643
- }
644
- /**
645
- * Parse tasks.md content into TaskPhase objects
646
- */
647
- parseTasksFile(content) {
648
- const phases = [];
649
- const lines = content.split('\n');
650
- let currentPhase = null;
651
- let taskCounter = 0;
652
- for (let i = 0; i < lines.length; i++) {
653
- const line = lines[i];
654
- // Check for phase header (## Phase Name)
655
- const phaseMatch = line.match(/^##\s+(.+)$/);
656
- if (phaseMatch) {
657
- if (currentPhase) {
658
- phases.push(currentPhase);
659
- }
660
- currentPhase = {
661
- name: phaseMatch[1].trim(),
662
- tasks: [],
663
- };
664
- taskCounter = 0;
665
- continue;
666
- }
667
- // Check for task (- [ ] or - [x] Task description)
668
- const taskMatch = line.match(/^-\s+\[([ x])\]\s+(.+?)(?:\s+\(ref:\s+(.+?)\))?$/);
669
- if (taskMatch && currentPhase) {
670
- const completed = taskMatch[1] === 'x';
671
- const description = taskMatch[2].trim();
672
- const reference = taskMatch[3]?.trim();
673
- taskCounter++;
674
- // Check next line for Task ID
675
- let taskId;
676
- const nextLine = i + 1 < lines.length ? lines[i + 1] : null;
677
- const taskIdMatch = nextLine?.trim().match(/^Task ID:\s+(.+)$/);
678
- if (taskIdMatch) {
679
- // Use Task ID from file (preferred)
680
- taskId = taskIdMatch[1].trim();
681
- i++; // Skip the Task ID line in next iteration
682
- }
683
- else {
684
- // Fallback: regenerate ID from phase name (backward compatibility)
685
- taskId = `${this.sanitizeId(currentPhase.name)}-${taskCounter}`;
686
- }
687
- currentPhase.tasks.push({
688
- id: taskId,
689
- description,
690
- phase: currentPhase.name,
691
- completed,
692
- prdReference: reference,
693
- });
694
- }
695
- }
696
- // Add last phase
697
- if (currentPhase) {
698
- phases.push(currentPhase);
699
- }
700
- return phases;
701
- }
702
- /**
703
- * Find the first incomplete task
704
- */
705
- findFirstIncompleteTask(phases) {
706
- for (const phase of phases) {
707
- for (const task of phase.tasks) {
708
- if (!task.completed) {
709
- return task;
710
- }
711
- }
712
- }
713
- return null;
714
- }
715
- /**
716
- * Mark a task as completed in the tasks.md file
717
- */
718
- async markTaskCompleted(tasksPath, taskId) {
719
- const phases = await this.readTasksFile(tasksPath);
720
- // Find and mark the task
721
- let found = false;
722
- for (const phase of phases) {
723
- for (const task of phase.tasks) {
724
- if (task.id === taskId) {
725
- task.completed = true;
726
- found = true;
727
- break;
728
- }
729
- }
730
- if (found)
731
- break;
732
- }
733
- if (!found) {
734
- throw new Error(`Task not found: ${taskId}`);
735
- }
736
- // Read original content to preserve formatting
737
- const content = await fs.readFile(tasksPath, 'utf-8');
738
- // Find the task line and replace [ ] with [x]
739
- // We need to find the exact task by description
740
- const targetTask = phases.flatMap((p) => p.tasks).find((t) => t.id === taskId);
741
- if (!targetTask) {
742
- throw new Error(`Task not found: ${taskId}`);
743
- }
744
- // Replace - [ ] with - [x] for this specific task description
745
- const taskDescPattern = this.escapeRegex(targetTask.description);
746
- const refPattern = targetTask.prdReference
747
- ? `\\s+\\(ref:\\s+${this.escapeRegex(targetTask.prdReference)}\\)`
748
- : '';
749
- const regex = new RegExp(`^(-\\s+\\[)( )(\\]\\s+${taskDescPattern}${refPattern})$`, 'm');
750
- const updatedContent = content.replace(regex, '$1x$3');
751
- await FileSystem.writeFileAtomic(tasksPath, updatedContent);
752
- }
753
- /**
754
- * Escape special regex characters
755
- */
756
- escapeRegex(str) {
757
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
758
- }
759
- /**
760
- * Validate that a task exists in the phases
761
- * @param phases - Array of task phases
762
- * @param taskId - Task ID to validate
763
- * @returns The task if found, null otherwise
764
- */
765
- validateTaskExists(phases, taskId) {
766
- for (const phase of phases) {
767
- const task = phase.tasks.find((t) => t.id === taskId);
768
- if (task) {
769
- return task;
770
- }
771
- }
772
- return null;
773
- }
774
- /**
775
- * Verify that a task was successfully marked as completed in the file
776
- * @param tasksPath - Path to tasks.md file
777
- * @param taskId - Task ID to verify
778
- * @returns true if task is marked completed, false otherwise
779
- */
780
- async verifyTaskMarked(tasksPath, taskId) {
781
- try {
782
- const phases = await this.readTasksFile(tasksPath);
783
- const task = this.validateTaskExists(phases, taskId);
784
- return task ? task.completed : false;
785
- }
786
- catch {
787
- // If we can't read the file, verification failed
788
- return false;
789
- }
790
- }
791
- /**
792
- * Mark a task as completed with validation and error recovery
793
- * Enhanced version with pre/post validation
794
- * @param tasksPath - Path to tasks.md file
795
- * @param taskId - Task ID to mark as completed
796
- * @param options - Optional configuration for error recovery
797
- * @returns Object with success status and any warnings/errors
798
- */
799
- async markTaskCompletedWithValidation(tasksPath, taskId, options = {}) {
800
- const { retryOnFailure = true, createBackup = true } = options;
801
- const warnings = [];
802
- // Pre-validation: Check if file exists
803
- if (!(await fs.pathExists(tasksPath))) {
804
- return {
805
- success: false,
806
- error: `Tasks file not found: ${tasksPath}`,
807
- };
808
- }
809
- // Create backup if requested
810
- let backupPath = null;
811
- if (createBackup) {
812
- backupPath = `${tasksPath}.backup`;
813
- try {
814
- await fs.copyFile(tasksPath, backupPath);
815
- }
816
- catch {
817
- warnings.push('Failed to create backup file');
818
- }
819
- }
820
- try {
821
- // Read and validate task exists
822
- const phases = await this.readTasksFile(tasksPath);
823
- const task = this.validateTaskExists(phases, taskId);
824
- if (!task) {
825
- // Task not found - provide helpful error
826
- const allTaskIds = phases.flatMap((p) => p.tasks.map((t) => t.id));
827
- return {
828
- success: false,
829
- error: `Task ID "${taskId}" not found. Available task IDs:\n${allTaskIds.join('\n')}`,
830
- };
831
- }
832
- // Check if already completed
833
- if (task.completed) {
834
- return {
835
- success: true,
836
- alreadyCompleted: true,
837
- warnings: [`Task "${taskId}" was already marked as completed`],
838
- };
839
- }
840
- // Attempt to mark task completed
841
- await this.markTaskCompleted(tasksPath, taskId);
842
- // Post-validation: Verify the checkbox was actually changed
843
- const verified = await this.verifyTaskMarked(tasksPath, taskId);
844
- if (!verified) {
845
- // Verification failed - attempt recovery if enabled
846
- if (retryOnFailure && backupPath) {
847
- warnings.push('First attempt failed verification, retrying...');
848
- // Restore from backup and try again
849
- await fs.copyFile(backupPath, tasksPath);
850
- await this.markTaskCompleted(tasksPath, taskId);
851
- // Verify again
852
- const secondVerification = await this.verifyTaskMarked(tasksPath, taskId);
853
- if (!secondVerification) {
854
- // Still failed - restore backup and return error
855
- await fs.copyFile(backupPath, tasksPath);
856
- return {
857
- success: false,
858
- error: 'Failed to mark task as completed even after retry. File has been restored from backup.',
859
- warnings,
860
- };
861
- }
862
- warnings.push('Task marked successfully on retry');
863
- }
864
- else {
865
- // No retry - just fail
866
- return {
867
- success: false,
868
- error: 'Task completion verification failed',
869
- warnings,
870
- };
871
- }
872
- }
873
- // Clean up backup on success
874
- if (backupPath && (await fs.pathExists(backupPath))) {
875
- await fs.remove(backupPath);
876
- }
877
- return {
878
- success: true,
879
- warnings: warnings.length > 0 ? warnings : undefined,
880
- };
881
- }
882
- catch (error) {
883
- // Restore from backup if available
884
- if (backupPath && (await fs.pathExists(backupPath))) {
885
- try {
886
- await fs.copyFile(backupPath, tasksPath);
887
- warnings.push('Restored tasks.md from backup due to error');
888
- }
889
- catch {
890
- warnings.push('Failed to restore from backup');
891
- }
892
- }
893
- const errorMessage = error instanceof Error ? error.message : 'Unknown error';
894
- return {
895
- success: false,
896
- error: `Error marking task as completed: ${errorMessage}`,
897
- warnings: warnings.length > 0 ? warnings : undefined,
898
- };
899
- }
900
- }
901
- /**
902
- * Get task completion statistics
903
- */
904
- getTaskStats(phases) {
905
- const allTasks = phases.flatMap((p) => p.tasks);
906
- const total = allTasks.length;
907
- const completed = allTasks.filter((t) => t.completed).length;
908
- const remaining = total - completed;
909
- const percentage = total > 0 ? (completed / total) * 100 : 0;
910
- return { total, completed, remaining, percentage };
911
- }
912
- /**
913
- * Find PRD directory from current working directory
914
- */
915
- async findPrdDirectory(projectName) {
916
- const baseDir = '.clavix/outputs';
917
- if (!(await fs.pathExists(baseDir))) {
918
- throw new Error('No .clavix/outputs directory found. Have you generated a PRD yet?');
919
- }
920
- // If project name specified, look for it
921
- if (projectName) {
922
- const projectPath = path.join(baseDir, projectName);
923
- if (await fs.pathExists(projectPath)) {
924
- return projectPath;
925
- }
926
- throw new Error(`PRD project not found: ${projectName}`);
927
- }
928
- // Otherwise, find most recent PRD directory
929
- const dirs = await fs.readdir(baseDir);
930
- const prdDirs = [];
931
- for (const dir of dirs) {
932
- const fullPath = path.join(baseDir, dir);
933
- const stat = await fs.stat(fullPath);
934
- if (stat.isDirectory()) {
935
- // Check if it has a PRD file
936
- const hasPrd = await this.hasPrdFile(fullPath);
937
- if (hasPrd) {
938
- prdDirs.push({
939
- path: fullPath,
940
- mtime: stat.mtime,
941
- });
942
- }
943
- }
944
- }
945
- if (prdDirs.length === 0) {
946
- throw new Error('No PRD directories found in .clavix/outputs');
947
- }
948
- // Return most recent
949
- prdDirs.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
950
- return prdDirs[0].path;
951
- }
952
- /**
953
- * Check if directory has a PRD file
954
- */
955
- async hasPrdFile(dirPath) {
956
- for (const filename of ALL_KNOWN_PRD_FILES) {
957
- if (await fs.pathExists(path.join(dirPath, filename))) {
958
- return true;
959
- }
960
- }
961
- // Prompt-only projects
962
- if (await fs.pathExists(path.join(dirPath, 'optimized-prompt.md'))) {
963
- return true;
964
- }
965
- return false;
966
- }
967
- async detectAvailableSources(dirPath) {
968
- const available = [];
969
- for (const source of SOURCE_ORDER_AUTO) {
970
- const filenames = SOURCE_FILE_MAP[source];
971
- for (const filename of filenames) {
972
- if (await fs.pathExists(path.join(dirPath, filename))) {
973
- available.push(source);
974
- break;
975
- }
976
- }
977
- }
978
- return available;
979
- }
980
- }
981
- //# sourceMappingURL=task-manager.js.map