clavix 4.7.0 → 4.8.1

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 (45) hide show
  1. package/dist/cli/commands/execute.js +29 -9
  2. package/dist/cli/commands/verify.d.ts +28 -0
  3. package/dist/cli/commands/verify.js +347 -0
  4. package/dist/core/adapters/amp-adapter.d.ts +3 -0
  5. package/dist/core/adapters/amp-adapter.js +1 -0
  6. package/dist/core/adapters/cline-adapter.d.ts +3 -0
  7. package/dist/core/adapters/cline-adapter.js +1 -0
  8. package/dist/core/adapters/codebuddy-adapter.d.ts +3 -0
  9. package/dist/core/adapters/codebuddy-adapter.js +1 -0
  10. package/dist/core/adapters/codex-adapter.d.ts +3 -0
  11. package/dist/core/adapters/codex-adapter.js +1 -0
  12. package/dist/core/adapters/cursor-adapter.d.ts +3 -0
  13. package/dist/core/adapters/cursor-adapter.js +1 -0
  14. package/dist/core/adapters/droid-adapter.d.ts +3 -0
  15. package/dist/core/adapters/droid-adapter.js +1 -0
  16. package/dist/core/adapters/instructions-generator.js +9 -2
  17. package/dist/core/adapters/kilocode-adapter.d.ts +3 -0
  18. package/dist/core/adapters/kilocode-adapter.js +1 -0
  19. package/dist/core/adapters/opencode-adapter.d.ts +3 -0
  20. package/dist/core/adapters/opencode-adapter.js +1 -0
  21. package/dist/core/adapters/roocode-adapter.d.ts +3 -0
  22. package/dist/core/adapters/roocode-adapter.js +1 -0
  23. package/dist/core/adapters/windsurf-adapter.d.ts +3 -0
  24. package/dist/core/adapters/windsurf-adapter.js +1 -0
  25. package/dist/core/basic-checklist-generator.d.ts +35 -0
  26. package/dist/core/basic-checklist-generator.js +344 -0
  27. package/dist/core/checklist-parser.d.ts +48 -0
  28. package/dist/core/checklist-parser.js +238 -0
  29. package/dist/core/command-transformer.d.ts +55 -0
  30. package/dist/core/command-transformer.js +65 -0
  31. package/dist/core/prompt-manager.d.ts +7 -0
  32. package/dist/core/prompt-manager.js +47 -22
  33. package/dist/core/verification-hooks.d.ts +67 -0
  34. package/dist/core/verification-hooks.js +309 -0
  35. package/dist/core/verification-manager.d.ts +106 -0
  36. package/dist/core/verification-manager.js +422 -0
  37. package/dist/templates/slash-commands/_canonical/execute.md +72 -1
  38. package/dist/templates/slash-commands/_canonical/verify.md +292 -0
  39. package/dist/templates/slash-commands/_components/agent-protocols/verification-methods.md +184 -0
  40. package/dist/types/agent.d.ts +4 -0
  41. package/dist/types/verification.d.ts +204 -0
  42. package/dist/types/verification.js +8 -0
  43. package/dist/utils/template-loader.d.ts +1 -1
  44. package/dist/utils/template-loader.js +5 -1
  45. package/package.json +1 -1
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Clavix v4.8: Checklist Parser
3
+ *
4
+ * Parses validation checklists, edge cases, and risk sections from
5
+ * deep mode prompt output files.
6
+ */
7
+ /**
8
+ * Regex patterns for parsing checklist sections
9
+ */
10
+ const PATTERNS = {
11
+ // Section headers (support both ## and ### headers)
12
+ validationSection: /#{2,3}\s*Validation\s+Checklist[\s\S]*?(?=#{2,3}|$)/i,
13
+ edgeCaseSection: /#{2,3}\s*Edge\s+Cases?\s+to\s+Consider[\s\S]*?(?=#{2,3}|$)/i,
14
+ riskSection: /#{2,3}\s*What\s+Could\s+Go\s+Wrong[\s\S]*?(?=#{2,3}|$)/i,
15
+ // Item patterns
16
+ checklistItem: /[☐□○●◯]\s*(.+)$/gm, // Various checkbox characters
17
+ categoryHeader: /\*\*(.+?):\*\*/g,
18
+ edgeCaseItem: /[•\-*]\s*\*\*(.+?)\*\*:?\s*(.+?)$/gm,
19
+ riskItem: /[•\-*]\s*\*\*(.+?)\*\*:?\s*(.+?)$/gm,
20
+ bulletItem: /[•\-*]\s+(.+?)$/gm,
21
+ };
22
+ /**
23
+ * Keywords for determining verification type
24
+ */
25
+ const VERIFICATION_TYPE_KEYWORDS = {
26
+ automated: [
27
+ 'compiles',
28
+ 'builds',
29
+ 'tests pass',
30
+ 'all tests',
31
+ 'test coverage',
32
+ 'lint',
33
+ 'typecheck',
34
+ 'no errors',
35
+ 'exit code',
36
+ 'npm test',
37
+ 'npm run',
38
+ 'build succeeds',
39
+ 'build passes',
40
+ ],
41
+ semiAutomated: [
42
+ 'renders',
43
+ 'displays',
44
+ 'console errors',
45
+ 'console warnings',
46
+ 'no warnings',
47
+ 'ui renders',
48
+ 'responsive',
49
+ 'screen sizes',
50
+ 'visual',
51
+ 'layout',
52
+ ],
53
+ // Everything else defaults to manual
54
+ };
55
+ /**
56
+ * Parse checklist items from prompt content
57
+ */
58
+ export class ChecklistParser {
59
+ /**
60
+ * Parse all checklist sections from prompt content
61
+ */
62
+ parse(content) {
63
+ const validationItems = this.parseValidationSection(content);
64
+ const edgeCases = this.parseEdgeCaseSection(content);
65
+ const risks = this.parseRiskSection(content);
66
+ const totalItems = validationItems.length + edgeCases.length + risks.length;
67
+ return {
68
+ validationItems,
69
+ edgeCases,
70
+ risks,
71
+ hasChecklist: totalItems > 0,
72
+ totalItems,
73
+ };
74
+ }
75
+ /**
76
+ * Parse the Validation Checklist section
77
+ */
78
+ parseValidationSection(content) {
79
+ const sectionMatch = content.match(PATTERNS.validationSection);
80
+ if (!sectionMatch)
81
+ return [];
82
+ const section = sectionMatch[0];
83
+ const items = [];
84
+ let currentGroup;
85
+ let itemIndex = 0;
86
+ // Split by lines and process
87
+ const lines = section.split('\n');
88
+ for (const line of lines) {
89
+ // Check for category header
90
+ const categoryMatch = line.match(/\*\*(.+?):\*\*/);
91
+ if (categoryMatch) {
92
+ currentGroup = categoryMatch[1].trim();
93
+ continue;
94
+ }
95
+ // Check for checklist item (Unicode checkbox or markdown checkbox)
96
+ const itemMatch = line.match(/(?:[☐□○●◯]|-\s*\[\s*\])\s*(.+)$/);
97
+ if (itemMatch) {
98
+ const itemContent = itemMatch[1].trim();
99
+ const verificationType = this.detectVerificationType(itemContent);
100
+ items.push({
101
+ id: `validation-${++itemIndex}`,
102
+ category: 'validation',
103
+ content: itemContent,
104
+ group: currentGroup,
105
+ verificationType,
106
+ });
107
+ }
108
+ }
109
+ return items;
110
+ }
111
+ /**
112
+ * Parse the Edge Cases to Consider section
113
+ */
114
+ parseEdgeCaseSection(content) {
115
+ const sectionMatch = content.match(PATTERNS.edgeCaseSection);
116
+ if (!sectionMatch)
117
+ return [];
118
+ const section = sectionMatch[0];
119
+ const items = [];
120
+ let itemIndex = 0;
121
+ // Match edge case items with scenario and consideration
122
+ const regex = /[•\-*]\s*\*\*(.+?)\*\*:?\s*(.+?)$/gm;
123
+ let match;
124
+ while ((match = regex.exec(section)) !== null) {
125
+ const scenario = match[1].trim();
126
+ const consideration = match[2].trim();
127
+ const itemContent = `${scenario}: ${consideration}`;
128
+ items.push({
129
+ id: `edge-case-${++itemIndex}`,
130
+ category: 'edge-case',
131
+ content: itemContent,
132
+ group: scenario,
133
+ verificationType: 'manual', // Edge cases are typically manual
134
+ });
135
+ }
136
+ // Also try to match simple bullet items without bold scenario
137
+ if (items.length === 0) {
138
+ const simpleRegex = /[•\-*]\s+([^*\n]+)$/gm;
139
+ while ((match = simpleRegex.exec(section)) !== null) {
140
+ const itemContent = match[1].trim();
141
+ if (itemContent && !itemContent.startsWith('#')) {
142
+ items.push({
143
+ id: `edge-case-${++itemIndex}`,
144
+ category: 'edge-case',
145
+ content: itemContent,
146
+ verificationType: 'manual',
147
+ });
148
+ }
149
+ }
150
+ }
151
+ return items;
152
+ }
153
+ /**
154
+ * Parse the What Could Go Wrong section
155
+ */
156
+ parseRiskSection(content) {
157
+ const sectionMatch = content.match(PATTERNS.riskSection);
158
+ if (!sectionMatch)
159
+ return [];
160
+ const section = sectionMatch[0];
161
+ const items = [];
162
+ let itemIndex = 0;
163
+ // Match risk items with bold titles
164
+ const regex = /[•\-*]\s*\*\*(.+?)\*\*:?\s*(.*)$/gm;
165
+ let match;
166
+ while ((match = regex.exec(section)) !== null) {
167
+ const risk = match[1].trim();
168
+ const details = match[2]?.trim() || '';
169
+ const itemContent = details ? `${risk}: ${details}` : risk;
170
+ items.push({
171
+ id: `risk-${++itemIndex}`,
172
+ category: 'risk',
173
+ content: itemContent,
174
+ group: risk,
175
+ verificationType: 'manual', // Risks are typically manual verification
176
+ });
177
+ }
178
+ // Also try to match simple bullet items
179
+ if (items.length === 0) {
180
+ const simpleRegex = /[•\-*]\s+([^*\n]+)$/gm;
181
+ while ((match = simpleRegex.exec(section)) !== null) {
182
+ const itemContent = match[1].trim();
183
+ if (itemContent && !itemContent.startsWith('#')) {
184
+ items.push({
185
+ id: `risk-${++itemIndex}`,
186
+ category: 'risk',
187
+ content: itemContent,
188
+ verificationType: 'manual',
189
+ });
190
+ }
191
+ }
192
+ }
193
+ return items;
194
+ }
195
+ /**
196
+ * Detect verification type based on item content
197
+ */
198
+ detectVerificationType(content) {
199
+ const lowerContent = content.toLowerCase();
200
+ // Check for automated keywords
201
+ for (const keyword of VERIFICATION_TYPE_KEYWORDS.automated) {
202
+ if (lowerContent.includes(keyword)) {
203
+ return 'automated';
204
+ }
205
+ }
206
+ // Check for semi-automated keywords
207
+ for (const keyword of VERIFICATION_TYPE_KEYWORDS.semiAutomated) {
208
+ if (lowerContent.includes(keyword)) {
209
+ return 'semi-automated';
210
+ }
211
+ }
212
+ // Default to manual
213
+ return 'manual';
214
+ }
215
+ /**
216
+ * Get a summary of the parsed checklist
217
+ */
218
+ getSummary(checklist) {
219
+ const allItems = [...checklist.validationItems, ...checklist.edgeCases, ...checklist.risks];
220
+ return {
221
+ validation: checklist.validationItems.length,
222
+ edgeCases: checklist.edgeCases.length,
223
+ risks: checklist.risks.length,
224
+ automated: allItems.filter((i) => i.verificationType === 'automated').length,
225
+ semiAutomated: allItems.filter((i) => i.verificationType === 'semi-automated').length,
226
+ manual: allItems.filter((i) => i.verificationType === 'manual').length,
227
+ };
228
+ }
229
+ /**
230
+ * Check if content has any checklist sections
231
+ */
232
+ hasChecklist(content) {
233
+ return (PATTERNS.validationSection.test(content) ||
234
+ PATTERNS.edgeCaseSection.test(content) ||
235
+ PATTERNS.riskSection.test(content));
236
+ }
237
+ }
238
+ //# sourceMappingURL=checklist-parser.js.map
@@ -0,0 +1,55 @@
1
+ import { IntegrationFeatures } from '../types/agent.js';
2
+ /**
3
+ * CommandTransformer - Transforms slash command references in template content
4
+ *
5
+ * Handles conversion between command formats:
6
+ * - Colon format: /clavix:fast (Claude Code style - uses subdirectories)
7
+ * - Hyphen format: /clavix-fast (Cursor, Droid style - flat files)
8
+ *
9
+ * Preserves CLI commands (clavix prompts list) unchanged - only transforms
10
+ * slash commands that start with /clavix:
11
+ *
12
+ * @since v4.8.1
13
+ */
14
+ export declare class CommandTransformer {
15
+ /**
16
+ * Matches /clavix:commandname pattern
17
+ * Supports hyphenated commands like task-complete
18
+ * Does NOT match CLI usage (no leading slash)
19
+ */
20
+ private static readonly SLASH_COMMAND_PATTERN;
21
+ /** Default command format (canonical/Claude Code style) */
22
+ private static readonly DEFAULT_SEPARATOR;
23
+ /**
24
+ * Transform slash command references in content based on adapter's command format
25
+ *
26
+ * @param content - Template content with canonical /clavix:command references
27
+ * @param features - Adapter's integration features (may include commandFormat)
28
+ * @returns Transformed content with correct command format
29
+ *
30
+ * @example
31
+ * // For Cursor/Droid (hyphen format):
32
+ * transform('/clavix:fast', { commandFormat: { separator: '-' } })
33
+ * // Returns: '/clavix-fast'
34
+ *
35
+ * @example
36
+ * // For Claude Code (colon format, default):
37
+ * transform('/clavix:fast', { commandFormat: { separator: ':' } })
38
+ * // Returns: '/clavix:fast' (unchanged)
39
+ */
40
+ static transform(content: string, features?: IntegrationFeatures): string;
41
+ /**
42
+ * Get the formatted command name for a specific adapter
43
+ * Useful for generating documentation or references
44
+ *
45
+ * @param commandName - Base command name (e.g., 'fast', 'execute', 'task-complete')
46
+ * @param features - Adapter's integration features
47
+ * @returns Formatted slash command (e.g., '/clavix:fast' or '/clavix-fast')
48
+ *
49
+ * @example
50
+ * formatCommand('execute', { commandFormat: { separator: '-' } })
51
+ * // Returns: '/clavix-execute'
52
+ */
53
+ static formatCommand(commandName: string, features?: IntegrationFeatures): string;
54
+ }
55
+ //# sourceMappingURL=command-transformer.d.ts.map
@@ -0,0 +1,65 @@
1
+ /**
2
+ * CommandTransformer - Transforms slash command references in template content
3
+ *
4
+ * Handles conversion between command formats:
5
+ * - Colon format: /clavix:fast (Claude Code style - uses subdirectories)
6
+ * - Hyphen format: /clavix-fast (Cursor, Droid style - flat files)
7
+ *
8
+ * Preserves CLI commands (clavix prompts list) unchanged - only transforms
9
+ * slash commands that start with /clavix:
10
+ *
11
+ * @since v4.8.1
12
+ */
13
+ export class CommandTransformer {
14
+ /**
15
+ * Matches /clavix:commandname pattern
16
+ * Supports hyphenated commands like task-complete
17
+ * Does NOT match CLI usage (no leading slash)
18
+ */
19
+ static SLASH_COMMAND_PATTERN = /\/clavix:(\w+(?:-\w+)*)/g;
20
+ /** Default command format (canonical/Claude Code style) */
21
+ static DEFAULT_SEPARATOR = ':';
22
+ /**
23
+ * Transform slash command references in content based on adapter's command format
24
+ *
25
+ * @param content - Template content with canonical /clavix:command references
26
+ * @param features - Adapter's integration features (may include commandFormat)
27
+ * @returns Transformed content with correct command format
28
+ *
29
+ * @example
30
+ * // For Cursor/Droid (hyphen format):
31
+ * transform('/clavix:fast', { commandFormat: { separator: '-' } })
32
+ * // Returns: '/clavix-fast'
33
+ *
34
+ * @example
35
+ * // For Claude Code (colon format, default):
36
+ * transform('/clavix:fast', { commandFormat: { separator: ':' } })
37
+ * // Returns: '/clavix:fast' (unchanged)
38
+ */
39
+ static transform(content, features) {
40
+ const separator = features?.commandFormat?.separator ?? this.DEFAULT_SEPARATOR;
41
+ // If using canonical format (colon), no transformation needed
42
+ if (separator === ':') {
43
+ return content;
44
+ }
45
+ // Transform /clavix:command to /clavix-command (or other separator)
46
+ return content.replace(this.SLASH_COMMAND_PATTERN, `/clavix${separator}$1`);
47
+ }
48
+ /**
49
+ * Get the formatted command name for a specific adapter
50
+ * Useful for generating documentation or references
51
+ *
52
+ * @param commandName - Base command name (e.g., 'fast', 'execute', 'task-complete')
53
+ * @param features - Adapter's integration features
54
+ * @returns Formatted slash command (e.g., '/clavix:fast' or '/clavix-fast')
55
+ *
56
+ * @example
57
+ * formatCommand('execute', { commandFormat: { separator: '-' } })
58
+ * // Returns: '/clavix-execute'
59
+ */
60
+ static formatCommand(commandName, features) {
61
+ const separator = features?.commandFormat?.separator ?? this.DEFAULT_SEPARATOR;
62
+ return `/clavix${separator}${commandName}`;
63
+ }
64
+ }
65
+ //# sourceMappingURL=command-transformer.js.map
@@ -11,6 +11,9 @@ export interface PromptMetadata {
11
11
  executedAt: string | null;
12
12
  ageInDays?: number;
13
13
  linkedProject?: string;
14
+ verificationRequired: boolean;
15
+ verified: boolean;
16
+ verifiedAt: string | null;
14
17
  }
15
18
  export interface PromptsIndex {
16
19
  version: string;
@@ -67,6 +70,10 @@ export declare class PromptManager {
67
70
  * Mark prompt as executed
68
71
  */
69
72
  markExecuted(id: string): Promise<void>;
73
+ /**
74
+ * Mark prompt as verified (v4.8)
75
+ */
76
+ markVerified(id: string): Promise<void>;
70
77
  /**
71
78
  * Delete prompts by filter
72
79
  */
@@ -56,6 +56,10 @@ export class PromptManager {
56
56
  executed: false,
57
57
  executedAt: null,
58
58
  linkedProject,
59
+ // Verification tracking (v4.8)
60
+ verificationRequired: true,
61
+ verified: false,
62
+ verifiedAt: null,
59
63
  };
60
64
  // Create file with frontmatter
61
65
  const frontmatter = [
@@ -66,9 +70,13 @@ export class PromptManager {
66
70
  `executed: ${metadata.executed}`,
67
71
  `originalPrompt: ${originalPrompt}`,
68
72
  linkedProject ? `linkedProject: ${linkedProject}` : '',
73
+ `verificationRequired: ${metadata.verificationRequired}`,
74
+ `verified: ${metadata.verified}`,
69
75
  '---',
70
76
  '',
71
- ].filter(Boolean).join('\n');
77
+ ]
78
+ .filter(Boolean)
79
+ .join('\n');
72
80
  const fileContent = frontmatter + content;
73
81
  await fs.writeFile(filePath, fileContent, 'utf-8');
74
82
  // Update index
@@ -80,12 +88,12 @@ export class PromptManager {
80
88
  */
81
89
  async loadPrompt(id) {
82
90
  const index = await this.loadIndex();
83
- const metadata = index.prompts.find(p => p.id === id);
91
+ const metadata = index.prompts.find((p) => p.id === id);
84
92
  if (!metadata) {
85
93
  return null;
86
94
  }
87
95
  const filePath = path.join(this.promptsDir, metadata.source, metadata.filename);
88
- if (!await fs.pathExists(filePath)) {
96
+ if (!(await fs.pathExists(filePath))) {
89
97
  return null;
90
98
  }
91
99
  const content = await fs.readFile(filePath, 'utf-8');
@@ -105,24 +113,24 @@ export class PromptManager {
105
113
  // Ensure index exists when filtering by source (for corruption recovery tests)
106
114
  if (filters?.source) {
107
115
  const indexPath = this.getIndexPath(filters.source);
108
- if (!await fs.pathExists(indexPath)) {
116
+ if (!(await fs.pathExists(indexPath))) {
109
117
  await this.saveIndex({ version: '1.0', prompts: [] }, filters.source);
110
118
  }
111
119
  }
112
120
  // Apply filters
113
121
  if (filters) {
114
122
  if (filters.executed !== undefined) {
115
- prompts = prompts.filter(p => p.executed === filters.executed);
123
+ prompts = prompts.filter((p) => p.executed === filters.executed);
116
124
  }
117
125
  if (filters.stale) {
118
- prompts = prompts.filter(p => this.getPromptAge(p) > 30);
126
+ prompts = prompts.filter((p) => this.getPromptAge(p) > 30);
119
127
  }
120
128
  if (filters.old) {
121
- prompts = prompts.filter(p => this.getPromptAge(p) > 7);
129
+ prompts = prompts.filter((p) => this.getPromptAge(p) > 7);
122
130
  }
123
131
  }
124
132
  // Add age calculation
125
- prompts = prompts.map(p => ({
133
+ prompts = prompts.map((p) => ({
126
134
  ...p,
127
135
  createdAt: new Date(p.timestamp),
128
136
  ageInDays: this.getPromptAge(p),
@@ -139,19 +147,38 @@ export class PromptManager {
139
147
  async markExecuted(id) {
140
148
  // Load all indexes to find the prompt
141
149
  const allPrompts = await this.listPrompts();
142
- const prompt = allPrompts.find(p => p.id === id);
150
+ const prompt = allPrompts.find((p) => p.id === id);
143
151
  if (!prompt) {
144
152
  throw new Error(`Prompt not found: ${id}`);
145
153
  }
146
154
  // Load source-specific index
147
155
  const index = await this.loadIndex(prompt.source);
148
- const indexPrompt = index.prompts.find(p => p.id === id);
156
+ const indexPrompt = index.prompts.find((p) => p.id === id);
149
157
  if (indexPrompt) {
150
158
  indexPrompt.executed = true;
151
159
  indexPrompt.executedAt = new Date().toISOString();
152
160
  await this.saveIndex(index, prompt.source);
153
161
  }
154
162
  }
163
+ /**
164
+ * Mark prompt as verified (v4.8)
165
+ */
166
+ async markVerified(id) {
167
+ // Load all indexes to find the prompt
168
+ const allPrompts = await this.listPrompts();
169
+ const prompt = allPrompts.find((p) => p.id === id);
170
+ if (!prompt) {
171
+ throw new Error(`Prompt not found: ${id}`);
172
+ }
173
+ // Load source-specific index
174
+ const index = await this.loadIndex(prompt.source);
175
+ const indexPrompt = index.prompts.find((p) => p.id === id);
176
+ if (indexPrompt) {
177
+ indexPrompt.verified = true;
178
+ indexPrompt.verifiedAt = new Date().toISOString();
179
+ await this.saveIndex(index, prompt.source);
180
+ }
181
+ }
155
182
  /**
156
183
  * Delete prompts by filter
157
184
  */
@@ -174,7 +201,7 @@ export class PromptManager {
174
201
  // Update each source index
175
202
  for (const [source, deletedIds] of bySource.entries()) {
176
203
  const index = await this.loadIndex(source);
177
- index.prompts = index.prompts.filter(p => !deletedIds.has(p.id));
204
+ index.prompts = index.prompts.filter((p) => !deletedIds.has(p.id));
178
205
  await this.saveIndex(index, source);
179
206
  }
180
207
  return deleteCount;
@@ -201,15 +228,13 @@ export class PromptManager {
201
228
  const allPrompts = await this.listPrompts();
202
229
  const stats = {
203
230
  totalPrompts: allPrompts.length,
204
- fastPrompts: allPrompts.filter(p => p.source === 'fast').length,
205
- deepPrompts: allPrompts.filter(p => p.source === 'deep').length,
206
- executedPrompts: allPrompts.filter(p => p.executed).length,
207
- pendingPrompts: allPrompts.filter(p => !p.executed).length,
208
- stalePrompts: allPrompts.filter(p => (p.ageInDays || 0) > 30).length,
209
- oldPrompts: allPrompts.filter(p => (p.ageInDays || 0) > 7).length,
210
- oldestPromptAge: allPrompts.length > 0
211
- ? Math.max(...allPrompts.map(p => p.ageInDays || 0))
212
- : 0,
231
+ fastPrompts: allPrompts.filter((p) => p.source === 'fast').length,
232
+ deepPrompts: allPrompts.filter((p) => p.source === 'deep').length,
233
+ executedPrompts: allPrompts.filter((p) => p.executed).length,
234
+ pendingPrompts: allPrompts.filter((p) => !p.executed).length,
235
+ stalePrompts: allPrompts.filter((p) => (p.ageInDays || 0) > 30).length,
236
+ oldPrompts: allPrompts.filter((p) => (p.ageInDays || 0) > 7).length,
237
+ oldestPromptAge: allPrompts.length > 0 ? Math.max(...allPrompts.map((p) => p.ageInDays || 0)) : 0,
213
238
  };
214
239
  return stats;
215
240
  }
@@ -230,7 +255,7 @@ export class PromptManager {
230
255
  }
231
256
  // Load specific source index
232
257
  const indexPath = this.getIndexPath(source);
233
- if (!await fs.pathExists(indexPath)) {
258
+ if (!(await fs.pathExists(indexPath))) {
234
259
  return {
235
260
  version: '1.0',
236
261
  prompts: [],
@@ -267,7 +292,7 @@ export class PromptManager {
267
292
  async addToIndex(metadata) {
268
293
  const index = await this.loadIndex(metadata.source);
269
294
  // Remove any existing entry with same ID (shouldn't happen, but be safe)
270
- index.prompts = index.prompts.filter(p => p.id !== metadata.id);
295
+ index.prompts = index.prompts.filter((p) => p.id !== metadata.id);
271
296
  // Add new entry
272
297
  index.prompts.push(metadata);
273
298
  await this.saveIndex(index, metadata.source);
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Clavix v4.8: Verification Hooks
3
+ *
4
+ * Detects and executes CLI hooks for automated verification of checklist items.
5
+ * Supports npm, yarn, and pnpm package managers.
6
+ */
7
+ import { VerificationHook, HookResult, HookType, DetectedHooks } from '../types/verification.js';
8
+ /**
9
+ * Verification Hooks Manager
10
+ */
11
+ export declare class VerificationHooks {
12
+ private readonly cwd;
13
+ private detectedHooks;
14
+ constructor(cwd?: string);
15
+ /**
16
+ * Detect available hooks in the project
17
+ */
18
+ detectHooks(): Promise<DetectedHooks>;
19
+ /**
20
+ * Detect package manager used in the project
21
+ */
22
+ private detectPackageManager;
23
+ /**
24
+ * Create a hook definition
25
+ */
26
+ private createHook;
27
+ /**
28
+ * Run a specific hook
29
+ */
30
+ runHook(hook: VerificationHook): Promise<HookResult>;
31
+ /**
32
+ * Run all detected hooks
33
+ */
34
+ runAllHooks(): Promise<HookResult[]>;
35
+ /**
36
+ * Run a hook by type
37
+ */
38
+ runHookByType(type: HookType): Promise<HookResult | null>;
39
+ /**
40
+ * Execute a command and capture output
41
+ */
42
+ private executeCommand;
43
+ /**
44
+ * Determine if the hook succeeded
45
+ */
46
+ private determineSuccess;
47
+ /**
48
+ * Determine confidence level in the result
49
+ */
50
+ private determineConfidence;
51
+ /**
52
+ * Get hook by type
53
+ */
54
+ getHook(type: HookType): Promise<VerificationHook | null>;
55
+ /**
56
+ * Check if a hook type is available
57
+ */
58
+ hasHook(type: HookType): Promise<boolean>;
59
+ /**
60
+ * Get summary of available hooks
61
+ */
62
+ getHookSummary(): Promise<{
63
+ available: HookType[];
64
+ unavailable: HookType[];
65
+ }>;
66
+ }
67
+ //# sourceMappingURL=verification-hooks.d.ts.map