clavix 4.7.0 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/execute.js +29 -9
- package/dist/cli/commands/verify.d.ts +28 -0
- package/dist/cli/commands/verify.js +347 -0
- package/dist/core/basic-checklist-generator.d.ts +35 -0
- package/dist/core/basic-checklist-generator.js +344 -0
- package/dist/core/checklist-parser.d.ts +48 -0
- package/dist/core/checklist-parser.js +238 -0
- package/dist/core/prompt-manager.d.ts +7 -0
- package/dist/core/prompt-manager.js +47 -22
- package/dist/core/verification-hooks.d.ts +67 -0
- package/dist/core/verification-hooks.js +309 -0
- package/dist/core/verification-manager.d.ts +106 -0
- package/dist/core/verification-manager.js +422 -0
- package/dist/templates/slash-commands/_canonical/execute.md +72 -1
- package/dist/templates/slash-commands/_canonical/verify.md +292 -0
- package/dist/templates/slash-commands/_components/agent-protocols/verification-methods.md +184 -0
- package/dist/types/verification.d.ts +204 -0
- package/dist/types/verification.js +8 -0
- package/package.json +1 -1
|
@@ -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
|
-
]
|
|
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
|
|
@@ -0,0 +1,309 @@
|
|
|
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 { spawn } from 'child_process';
|
|
8
|
+
import fs from 'fs-extra';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
/**
|
|
11
|
+
* Default timeout for hooks (60 seconds)
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_TIMEOUT = 60000;
|
|
14
|
+
/**
|
|
15
|
+
* Built-in hook definitions
|
|
16
|
+
*/
|
|
17
|
+
const HOOK_DEFINITIONS = {
|
|
18
|
+
test: {
|
|
19
|
+
displayName: 'Tests',
|
|
20
|
+
commands: ['npm test', 'npm run test', 'yarn test', 'pnpm test'],
|
|
21
|
+
successPatterns: [
|
|
22
|
+
/(\d+)\s+(passing|passed)/i,
|
|
23
|
+
/tests?\s+passed/i,
|
|
24
|
+
/all\s+tests?\s+passed/i,
|
|
25
|
+
/0\s+failed/i,
|
|
26
|
+
/test\s+suites?:\s+\d+\s+passed/i,
|
|
27
|
+
],
|
|
28
|
+
failurePatterns: [/(\d+)\s+failed/i, /test\s+failed/i, /FAIL/i, /error/i],
|
|
29
|
+
},
|
|
30
|
+
build: {
|
|
31
|
+
displayName: 'Build',
|
|
32
|
+
commands: ['npm run build', 'yarn build', 'pnpm build', 'tsc'],
|
|
33
|
+
successPatterns: [/successfully/i, /done/i, /built/i, /compiled/i],
|
|
34
|
+
failurePatterns: [/error/i, /failed/i, /TS\d{4}:/],
|
|
35
|
+
},
|
|
36
|
+
lint: {
|
|
37
|
+
displayName: 'Lint',
|
|
38
|
+
commands: ['npm run lint', 'yarn lint', 'pnpm lint', 'eslint .'],
|
|
39
|
+
successPatterns: [/0\s+errors?/i, /no\s+errors?/i, /all\s+files?\s+pass/i],
|
|
40
|
+
failurePatterns: [/(\d+)\s+errors?/i, /error/i],
|
|
41
|
+
},
|
|
42
|
+
typecheck: {
|
|
43
|
+
displayName: 'Type Check',
|
|
44
|
+
commands: ['tsc --noEmit', 'npm run typecheck', 'yarn typecheck'],
|
|
45
|
+
successPatterns: [/^$/], // Empty output means success for tsc
|
|
46
|
+
failurePatterns: [/error\s+TS\d{4}/i, /Type\s+error/i],
|
|
47
|
+
},
|
|
48
|
+
custom: {
|
|
49
|
+
displayName: 'Custom',
|
|
50
|
+
commands: [],
|
|
51
|
+
successPatterns: [],
|
|
52
|
+
failurePatterns: [],
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Verification Hooks Manager
|
|
57
|
+
*/
|
|
58
|
+
export class VerificationHooks {
|
|
59
|
+
cwd;
|
|
60
|
+
detectedHooks = null;
|
|
61
|
+
constructor(cwd) {
|
|
62
|
+
this.cwd = cwd || process.cwd();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Detect available hooks in the project
|
|
66
|
+
*/
|
|
67
|
+
async detectHooks() {
|
|
68
|
+
if (this.detectedHooks) {
|
|
69
|
+
return this.detectedHooks;
|
|
70
|
+
}
|
|
71
|
+
const packageJsonPath = path.join(this.cwd, 'package.json');
|
|
72
|
+
const hasPackageJson = await fs.pathExists(packageJsonPath);
|
|
73
|
+
const packageManager = await this.detectPackageManager();
|
|
74
|
+
const hooks = [];
|
|
75
|
+
if (hasPackageJson) {
|
|
76
|
+
try {
|
|
77
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
78
|
+
const scripts = packageJson.scripts || {};
|
|
79
|
+
// Check for test script
|
|
80
|
+
if (scripts.test) {
|
|
81
|
+
hooks.push(this.createHook('test', `${packageManager} test`, packageManager));
|
|
82
|
+
}
|
|
83
|
+
// Check for build script
|
|
84
|
+
if (scripts.build) {
|
|
85
|
+
hooks.push(this.createHook('build', `${packageManager} run build`, packageManager));
|
|
86
|
+
}
|
|
87
|
+
// Check for lint script
|
|
88
|
+
if (scripts.lint) {
|
|
89
|
+
hooks.push(this.createHook('lint', `${packageManager} run lint`, packageManager));
|
|
90
|
+
}
|
|
91
|
+
// Check for typecheck script
|
|
92
|
+
if (scripts.typecheck || scripts['type-check']) {
|
|
93
|
+
const cmd = scripts.typecheck ? 'typecheck' : 'type-check';
|
|
94
|
+
hooks.push(this.createHook('typecheck', `${packageManager} run ${cmd}`, packageManager));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Check for TypeScript
|
|
98
|
+
const hasTsConfig = await fs.pathExists(path.join(this.cwd, 'tsconfig.json'));
|
|
99
|
+
if (hasTsConfig) {
|
|
100
|
+
hooks.push(this.createHook('typecheck', 'npx tsc --noEmit', packageManager));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// Ignore errors reading package.json
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
this.detectedHooks = {
|
|
109
|
+
hooks,
|
|
110
|
+
packageManager,
|
|
111
|
+
hasPackageJson,
|
|
112
|
+
};
|
|
113
|
+
return this.detectedHooks;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Detect package manager used in the project
|
|
117
|
+
*/
|
|
118
|
+
async detectPackageManager() {
|
|
119
|
+
// Check for lock files
|
|
120
|
+
if (await fs.pathExists(path.join(this.cwd, 'pnpm-lock.yaml'))) {
|
|
121
|
+
return 'pnpm';
|
|
122
|
+
}
|
|
123
|
+
if (await fs.pathExists(path.join(this.cwd, 'yarn.lock'))) {
|
|
124
|
+
return 'yarn';
|
|
125
|
+
}
|
|
126
|
+
if (await fs.pathExists(path.join(this.cwd, 'package-lock.json'))) {
|
|
127
|
+
return 'npm';
|
|
128
|
+
}
|
|
129
|
+
return 'unknown';
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Create a hook definition
|
|
133
|
+
*/
|
|
134
|
+
createHook(type, command, _packageManager) {
|
|
135
|
+
const def = HOOK_DEFINITIONS[type];
|
|
136
|
+
return {
|
|
137
|
+
name: type,
|
|
138
|
+
displayName: def.displayName,
|
|
139
|
+
command,
|
|
140
|
+
successPattern: def.successPatterns[0],
|
|
141
|
+
failurePattern: def.failurePatterns[0],
|
|
142
|
+
timeout: DEFAULT_TIMEOUT,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Run a specific hook
|
|
147
|
+
*/
|
|
148
|
+
async runHook(hook) {
|
|
149
|
+
const startTime = Date.now();
|
|
150
|
+
try {
|
|
151
|
+
const { exitCode, output } = await this.executeCommand(hook.command, hook.timeout);
|
|
152
|
+
const executionTimeMs = Date.now() - startTime;
|
|
153
|
+
// Determine success based on exit code and patterns
|
|
154
|
+
const success = this.determineSuccess(hook, exitCode, output);
|
|
155
|
+
const confidence = this.determineConfidence(hook, exitCode, output, success);
|
|
156
|
+
return {
|
|
157
|
+
hook,
|
|
158
|
+
success,
|
|
159
|
+
exitCode,
|
|
160
|
+
output,
|
|
161
|
+
confidence,
|
|
162
|
+
executionTimeMs,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
const executionTimeMs = Date.now() - startTime;
|
|
167
|
+
return {
|
|
168
|
+
hook,
|
|
169
|
+
success: false,
|
|
170
|
+
exitCode: -1,
|
|
171
|
+
output: '',
|
|
172
|
+
confidence: 'low',
|
|
173
|
+
executionTimeMs,
|
|
174
|
+
error: error instanceof Error ? error.message : String(error),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Run all detected hooks
|
|
180
|
+
*/
|
|
181
|
+
async runAllHooks() {
|
|
182
|
+
const detected = await this.detectHooks();
|
|
183
|
+
const results = [];
|
|
184
|
+
for (const hook of detected.hooks) {
|
|
185
|
+
const result = await this.runHook(hook);
|
|
186
|
+
results.push(result);
|
|
187
|
+
}
|
|
188
|
+
return results;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Run a hook by type
|
|
192
|
+
*/
|
|
193
|
+
async runHookByType(type) {
|
|
194
|
+
const detected = await this.detectHooks();
|
|
195
|
+
const hook = detected.hooks.find((h) => h.name === type);
|
|
196
|
+
if (!hook) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
return this.runHook(hook);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Execute a command and capture output
|
|
203
|
+
*/
|
|
204
|
+
executeCommand(command, timeout) {
|
|
205
|
+
return new Promise((resolve, reject) => {
|
|
206
|
+
const [cmd, ...args] = command.split(' ');
|
|
207
|
+
let output = '';
|
|
208
|
+
const proc = spawn(cmd, args, {
|
|
209
|
+
cwd: this.cwd,
|
|
210
|
+
shell: true,
|
|
211
|
+
env: { ...process.env, CI: 'true', FORCE_COLOR: '0' },
|
|
212
|
+
});
|
|
213
|
+
const timeoutId = setTimeout(() => {
|
|
214
|
+
proc.kill('SIGTERM');
|
|
215
|
+
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
216
|
+
}, timeout);
|
|
217
|
+
proc.stdout?.on('data', (data) => {
|
|
218
|
+
output += data.toString();
|
|
219
|
+
});
|
|
220
|
+
proc.stderr?.on('data', (data) => {
|
|
221
|
+
output += data.toString();
|
|
222
|
+
});
|
|
223
|
+
proc.on('close', (code) => {
|
|
224
|
+
clearTimeout(timeoutId);
|
|
225
|
+
resolve({
|
|
226
|
+
exitCode: code ?? 0,
|
|
227
|
+
output: output.trim(),
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
proc.on('error', (error) => {
|
|
231
|
+
clearTimeout(timeoutId);
|
|
232
|
+
reject(error);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Determine if the hook succeeded
|
|
238
|
+
*/
|
|
239
|
+
determineSuccess(hook, exitCode, output) {
|
|
240
|
+
// Exit code 0 is primary success indicator
|
|
241
|
+
if (exitCode !== 0) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
// Check for explicit failure patterns
|
|
245
|
+
const def = HOOK_DEFINITIONS[hook.name];
|
|
246
|
+
for (const pattern of def.failurePatterns) {
|
|
247
|
+
if (pattern.test(output)) {
|
|
248
|
+
// Check if it's a "0 errors" type match
|
|
249
|
+
const match = output.match(pattern);
|
|
250
|
+
if (match && match[1] === '0') {
|
|
251
|
+
continue; // "0 errors" is success
|
|
252
|
+
}
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Determine confidence level in the result
|
|
260
|
+
*/
|
|
261
|
+
determineConfidence(hook, exitCode, output, success) {
|
|
262
|
+
const def = HOOK_DEFINITIONS[hook.name];
|
|
263
|
+
// Clear exit code with matching success patterns = high confidence
|
|
264
|
+
if (exitCode === 0 && success) {
|
|
265
|
+
for (const pattern of def.successPatterns) {
|
|
266
|
+
if (pattern.test(output)) {
|
|
267
|
+
return 'high';
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
// Exit 0 but no explicit success pattern
|
|
271
|
+
return 'medium';
|
|
272
|
+
}
|
|
273
|
+
// Clear failure indicators
|
|
274
|
+
if (exitCode !== 0 || !success) {
|
|
275
|
+
for (const pattern of def.failurePatterns) {
|
|
276
|
+
if (pattern.test(output)) {
|
|
277
|
+
return 'high';
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return 'medium';
|
|
281
|
+
}
|
|
282
|
+
return 'low';
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Get hook by type
|
|
286
|
+
*/
|
|
287
|
+
async getHook(type) {
|
|
288
|
+
const detected = await this.detectHooks();
|
|
289
|
+
return detected.hooks.find((h) => h.name === type) || null;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Check if a hook type is available
|
|
293
|
+
*/
|
|
294
|
+
async hasHook(type) {
|
|
295
|
+
const detected = await this.detectHooks();
|
|
296
|
+
return detected.hooks.some((h) => h.name === type);
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Get summary of available hooks
|
|
300
|
+
*/
|
|
301
|
+
async getHookSummary() {
|
|
302
|
+
const detected = await this.detectHooks();
|
|
303
|
+
const allTypes = ['test', 'build', 'lint', 'typecheck'];
|
|
304
|
+
const available = detected.hooks.map((h) => h.name);
|
|
305
|
+
const unavailable = allTypes.filter((t) => !available.includes(t));
|
|
306
|
+
return { available, unavailable };
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=verification-hooks.js.map
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clavix v4.8: Verification Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages verification state, execution flow, and persistence.
|
|
5
|
+
* Coordinates between checklist parsing, hook execution, and result storage.
|
|
6
|
+
*/
|
|
7
|
+
import { VerificationReport, VerificationResult, VerificationSummary, ChecklistItem, ReportStatus, VerificationStatus, VerificationConfidence, VerificationMethod } from '../types/verification.js';
|
|
8
|
+
/**
|
|
9
|
+
* Verification Manager
|
|
10
|
+
*/
|
|
11
|
+
export declare class VerificationManager {
|
|
12
|
+
private readonly promptManager;
|
|
13
|
+
private readonly checklistParser;
|
|
14
|
+
private readonly verificationHooks;
|
|
15
|
+
private readonly outputDir;
|
|
16
|
+
constructor(baseDir?: string);
|
|
17
|
+
/**
|
|
18
|
+
* Initialize verification for a prompt
|
|
19
|
+
*/
|
|
20
|
+
initializeVerification(promptId: string): Promise<VerificationReport>;
|
|
21
|
+
/**
|
|
22
|
+
* Get verification report path for a prompt
|
|
23
|
+
*/
|
|
24
|
+
getReportPath(promptId: string, source: 'fast' | 'deep'): string;
|
|
25
|
+
/**
|
|
26
|
+
* Load verification report
|
|
27
|
+
*/
|
|
28
|
+
loadReport(promptId: string): Promise<VerificationReport | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Save verification report
|
|
31
|
+
*/
|
|
32
|
+
saveReport(report: VerificationReport): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Mark a single item as verified
|
|
35
|
+
*/
|
|
36
|
+
markItemVerified(promptId: string, itemId: string, status: VerificationStatus, options?: {
|
|
37
|
+
evidence?: string;
|
|
38
|
+
reason?: string;
|
|
39
|
+
confidence?: VerificationConfidence;
|
|
40
|
+
method?: VerificationMethod;
|
|
41
|
+
}): Promise<VerificationReport>;
|
|
42
|
+
/**
|
|
43
|
+
* Run automated verification for a prompt
|
|
44
|
+
*/
|
|
45
|
+
runAutomatedVerification(promptId: string): Promise<VerificationReport>;
|
|
46
|
+
/**
|
|
47
|
+
* Match checklist item content to hook type
|
|
48
|
+
*/
|
|
49
|
+
private matchItemToHook;
|
|
50
|
+
/**
|
|
51
|
+
* Truncate output for storage
|
|
52
|
+
*/
|
|
53
|
+
private truncateOutput;
|
|
54
|
+
/**
|
|
55
|
+
* Calculate summary from results
|
|
56
|
+
*/
|
|
57
|
+
calculateSummary(results: VerificationResult[]): VerificationSummary;
|
|
58
|
+
/**
|
|
59
|
+
* Calculate overall report status
|
|
60
|
+
*/
|
|
61
|
+
private calculateReportStatus;
|
|
62
|
+
/**
|
|
63
|
+
* Get pending items from report
|
|
64
|
+
*/
|
|
65
|
+
getPendingItems(report: VerificationReport): ChecklistItem[];
|
|
66
|
+
/**
|
|
67
|
+
* Get failed items from report
|
|
68
|
+
*/
|
|
69
|
+
getFailedItems(report: VerificationReport): Array<{
|
|
70
|
+
item: ChecklistItem;
|
|
71
|
+
result: VerificationResult;
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* Check if verification is complete
|
|
75
|
+
*/
|
|
76
|
+
isComplete(report: VerificationReport): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Check if verification requires attention (has failures)
|
|
79
|
+
*/
|
|
80
|
+
requiresAttention(report: VerificationReport): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Delete verification report
|
|
83
|
+
*/
|
|
84
|
+
deleteReport(promptId: string): Promise<boolean>;
|
|
85
|
+
/**
|
|
86
|
+
* Get all verification reports
|
|
87
|
+
*/
|
|
88
|
+
listReports(): Promise<VerificationReport[]>;
|
|
89
|
+
/**
|
|
90
|
+
* Get verification status for a prompt
|
|
91
|
+
*/
|
|
92
|
+
getVerificationStatus(promptId: string): Promise<{
|
|
93
|
+
hasReport: boolean;
|
|
94
|
+
status: ReportStatus | null;
|
|
95
|
+
summary: VerificationSummary | null;
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* Format verification report for display
|
|
99
|
+
*/
|
|
100
|
+
formatReportForDisplay(report: VerificationReport): string;
|
|
101
|
+
/**
|
|
102
|
+
* Get status icon for display
|
|
103
|
+
*/
|
|
104
|
+
private getStatusIcon;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=verification-manager.d.ts.map
|