@agile-vibe-coding/avc 0.1.1 → 0.3.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.
- package/cli/agent-loader.js +21 -0
- package/cli/agents/agent-selector.md +152 -0
- package/cli/agents/architecture-recommender.md +418 -0
- package/cli/agents/code-implementer.md +117 -0
- package/cli/agents/code-validator.md +80 -0
- package/cli/agents/context-reviewer-epic.md +101 -0
- package/cli/agents/context-reviewer-story.md +92 -0
- package/cli/agents/context-writer-epic.md +145 -0
- package/cli/agents/context-writer-story.md +111 -0
- package/cli/agents/database-deep-dive.md +470 -0
- package/cli/agents/database-recommender.md +634 -0
- package/cli/agents/doc-distributor.md +176 -0
- package/cli/agents/doc-writer-epic.md +42 -0
- package/cli/agents/doc-writer-story.md +43 -0
- package/cli/agents/documentation-updater.md +203 -0
- package/cli/agents/duplicate-detector.md +110 -0
- package/cli/agents/epic-story-decomposer.md +559 -0
- package/cli/agents/feature-context-generator.md +91 -0
- package/cli/agents/gap-checker-epic.md +52 -0
- package/cli/agents/impact-checker-story.md +51 -0
- package/cli/agents/migration-guide-generator.md +305 -0
- package/cli/agents/mission-scope-generator.md +143 -0
- package/cli/agents/mission-scope-validator.md +146 -0
- package/cli/agents/project-context-extractor.md +122 -0
- package/cli/agents/project-documentation-creator.json +226 -0
- package/cli/agents/project-documentation-creator.md +595 -0
- package/cli/agents/question-prefiller.md +269 -0
- package/cli/agents/refiner-epic.md +39 -0
- package/cli/agents/refiner-story.md +42 -0
- package/cli/agents/scaffolding-generator.md +99 -0
- package/cli/agents/seed-validator.md +71 -0
- package/cli/agents/story-doc-enricher.md +133 -0
- package/cli/agents/story-scope-reviewer.md +147 -0
- package/cli/agents/story-splitter.md +83 -0
- package/cli/agents/suggestion-business-analyst.md +88 -0
- package/cli/agents/suggestion-deployment-architect.md +263 -0
- package/cli/agents/suggestion-product-manager.md +129 -0
- package/cli/agents/suggestion-security-specialist.md +156 -0
- package/cli/agents/suggestion-technical-architect.md +269 -0
- package/cli/agents/suggestion-ux-researcher.md +93 -0
- package/cli/agents/task-subtask-decomposer.md +188 -0
- package/cli/agents/validator-documentation.json +183 -0
- package/cli/agents/validator-documentation.md +455 -0
- package/cli/agents/validator-selector.md +211 -0
- package/cli/ansi-colors.js +21 -0
- package/cli/api-reference-tool.js +368 -0
- package/cli/build-docs.js +29 -8
- package/cli/ceremony-history.js +369 -0
- package/cli/checks/catalog.json +76 -0
- package/cli/checks/code/quality.json +26 -0
- package/cli/checks/code/testing.json +14 -0
- package/cli/checks/code/traceability.json +26 -0
- package/cli/checks/cross-refs/epic.json +171 -0
- package/cli/checks/cross-refs/story.json +149 -0
- package/cli/checks/epic/api.json +114 -0
- package/cli/checks/epic/backend.json +126 -0
- package/cli/checks/epic/cloud.json +126 -0
- package/cli/checks/epic/data.json +102 -0
- package/cli/checks/epic/database.json +114 -0
- package/cli/checks/epic/developer.json +182 -0
- package/cli/checks/epic/devops.json +174 -0
- package/cli/checks/epic/frontend.json +162 -0
- package/cli/checks/epic/mobile.json +102 -0
- package/cli/checks/epic/qa.json +90 -0
- package/cli/checks/epic/security.json +184 -0
- package/cli/checks/epic/solution-architect.json +192 -0
- package/cli/checks/epic/test-architect.json +90 -0
- package/cli/checks/epic/ui.json +102 -0
- package/cli/checks/epic/ux.json +90 -0
- package/cli/checks/fixes/epic-fix-template.md +10 -0
- package/cli/checks/fixes/story-fix-template.md +10 -0
- package/cli/checks/story/api.json +186 -0
- package/cli/checks/story/backend.json +102 -0
- package/cli/checks/story/cloud.json +102 -0
- package/cli/checks/story/data.json +210 -0
- package/cli/checks/story/database.json +102 -0
- package/cli/checks/story/developer.json +168 -0
- package/cli/checks/story/devops.json +102 -0
- package/cli/checks/story/frontend.json +174 -0
- package/cli/checks/story/mobile.json +102 -0
- package/cli/checks/story/qa.json +210 -0
- package/cli/checks/story/security.json +198 -0
- package/cli/checks/story/solution-architect.json +230 -0
- package/cli/checks/story/test-architect.json +210 -0
- package/cli/checks/story/ui.json +102 -0
- package/cli/checks/story/ux.json +102 -0
- package/cli/coding-order.js +401 -0
- package/cli/command-logger.js +49 -12
- package/cli/components/static-output.js +63 -0
- package/cli/console-output-manager.js +94 -0
- package/cli/dependency-checker.js +72 -0
- package/cli/docs-sync.js +306 -0
- package/cli/epic-story-validator.js +659 -0
- package/cli/evaluation-prompts.js +1008 -0
- package/cli/execution-context.js +195 -0
- package/cli/generate-summary-table.js +340 -0
- package/cli/init-model-config.js +704 -0
- package/cli/init.js +1737 -278
- package/cli/kanban-server-manager.js +227 -0
- package/cli/llm-claude.js +150 -1
- package/cli/llm-gemini.js +109 -0
- package/cli/llm-local.js +493 -0
- package/cli/llm-mock.js +233 -0
- package/cli/llm-openai.js +454 -0
- package/cli/llm-provider.js +379 -3
- package/cli/llm-token-limits.js +211 -0
- package/cli/llm-verifier.js +662 -0
- package/cli/llm-xiaomi.js +143 -0
- package/cli/message-constants.js +49 -0
- package/cli/message-manager.js +334 -0
- package/cli/message-types.js +96 -0
- package/cli/messaging-api.js +291 -0
- package/cli/micro-check-fixer.js +335 -0
- package/cli/micro-check-runner.js +449 -0
- package/cli/micro-check-scorer.js +148 -0
- package/cli/micro-check-validator.js +538 -0
- package/cli/model-pricing.js +192 -0
- package/cli/model-query-engine.js +468 -0
- package/cli/model-recommendation-analyzer.js +495 -0
- package/cli/model-selector.js +270 -0
- package/cli/output-buffer.js +107 -0
- package/cli/process-manager.js +73 -2
- package/cli/prompt-logger.js +57 -0
- package/cli/repl-ink.js +4625 -1094
- package/cli/repl-old.js +3 -4
- package/cli/seed-processor.js +962 -0
- package/cli/sprint-planning-processor.js +4162 -0
- package/cli/template-processor.js +2149 -105
- package/cli/templates/project.md +25 -8
- package/cli/templates/vitepress-config.mts.template +5 -4
- package/cli/token-tracker.js +547 -0
- package/cli/tools/generate-story-validators.js +317 -0
- package/cli/tools/generate-validators.js +669 -0
- package/cli/update-checker.js +19 -17
- package/cli/update-notifier.js +4 -4
- package/cli/validation-router.js +667 -0
- package/cli/verification-tracker.js +563 -0
- package/cli/worktree-runner.js +654 -0
- package/kanban/README.md +386 -0
- package/kanban/client/README.md +205 -0
- package/kanban/client/components.json +20 -0
- package/kanban/client/dist/assets/index-D_KC5EQT.css +1 -0
- package/kanban/client/dist/assets/index-DjY5zqW7.js +351 -0
- package/kanban/client/dist/index.html +16 -0
- package/kanban/client/dist/vite.svg +1 -0
- package/kanban/client/index.html +15 -0
- package/kanban/client/package-lock.json +9442 -0
- package/kanban/client/package.json +44 -0
- package/kanban/client/postcss.config.js +6 -0
- package/kanban/client/public/vite.svg +1 -0
- package/kanban/client/src/App.jsx +651 -0
- package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
- package/kanban/client/src/components/ceremony/AskArchPopup.jsx +420 -0
- package/kanban/client/src/components/ceremony/AskModelPopup.jsx +629 -0
- package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +1133 -0
- package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
- package/kanban/client/src/components/ceremony/ProviderSwitcherButton.jsx +290 -0
- package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +686 -0
- package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +838 -0
- package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
- package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +136 -0
- package/kanban/client/src/components/ceremony/steps/DatabaseStep.jsx +202 -0
- package/kanban/client/src/components/ceremony/steps/DeploymentStep.jsx +123 -0
- package/kanban/client/src/components/ceremony/steps/MissionStep.jsx +106 -0
- package/kanban/client/src/components/ceremony/steps/ReviewAnswersStep.jsx +329 -0
- package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +249 -0
- package/kanban/client/src/components/kanban/CardDetailModal.jsx +646 -0
- package/kanban/client/src/components/kanban/EpicSection.jsx +146 -0
- package/kanban/client/src/components/kanban/FilterToolbar.jsx +222 -0
- package/kanban/client/src/components/kanban/GroupingSelector.jsx +63 -0
- package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
- package/kanban/client/src/components/kanban/KanbanCard.jsx +147 -0
- package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
- package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +784 -0
- package/kanban/client/src/components/kanban/RunButton.jsx +162 -0
- package/kanban/client/src/components/kanban/SeedButton.jsx +176 -0
- package/kanban/client/src/components/layout/LoadingScreen.jsx +82 -0
- package/kanban/client/src/components/process/ProcessMonitorBar.jsx +80 -0
- package/kanban/client/src/components/settings/AgentEditorPopup.jsx +171 -0
- package/kanban/client/src/components/settings/AgentsTab.jsx +381 -0
- package/kanban/client/src/components/settings/ApiKeysTab.jsx +142 -0
- package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +105 -0
- package/kanban/client/src/components/settings/CheckEditorPopup.jsx +507 -0
- package/kanban/client/src/components/settings/CostThresholdsTab.jsx +95 -0
- package/kanban/client/src/components/settings/ModelPricingTab.jsx +269 -0
- package/kanban/client/src/components/settings/OpenAIAuthSection.jsx +412 -0
- package/kanban/client/src/components/settings/ServersTab.jsx +121 -0
- package/kanban/client/src/components/settings/SettingsModal.jsx +84 -0
- package/kanban/client/src/components/stats/CostModal.jsx +384 -0
- package/kanban/client/src/components/ui/badge.jsx +27 -0
- package/kanban/client/src/components/ui/dialog.jsx +121 -0
- package/kanban/client/src/components/ui/tabs.jsx +85 -0
- package/kanban/client/src/hooks/__tests__/useGrouping.test.js +232 -0
- package/kanban/client/src/hooks/useGrouping.js +177 -0
- package/kanban/client/src/hooks/useWebSocket.js +120 -0
- package/kanban/client/src/lib/__tests__/api.test.js +196 -0
- package/kanban/client/src/lib/__tests__/status-grouping.test.js +94 -0
- package/kanban/client/src/lib/api.js +515 -0
- package/kanban/client/src/lib/status-grouping.js +154 -0
- package/kanban/client/src/lib/utils.js +11 -0
- package/kanban/client/src/main.jsx +10 -0
- package/kanban/client/src/store/__tests__/kanbanStore.test.js +164 -0
- package/kanban/client/src/store/ceremonyStore.js +172 -0
- package/kanban/client/src/store/filterStore.js +201 -0
- package/kanban/client/src/store/kanbanStore.js +123 -0
- package/kanban/client/src/store/processStore.js +65 -0
- package/kanban/client/src/store/sprintPlanningStore.js +33 -0
- package/kanban/client/src/styles/globals.css +59 -0
- package/kanban/client/tailwind.config.js +77 -0
- package/kanban/client/vite.config.js +28 -0
- package/kanban/client/vitest.config.js +28 -0
- package/kanban/dev-start.sh +47 -0
- package/kanban/package.json +12 -0
- package/kanban/server/index.js +537 -0
- package/kanban/server/routes/ceremony.js +454 -0
- package/kanban/server/routes/costs.js +163 -0
- package/kanban/server/routes/openai-oauth.js +366 -0
- package/kanban/server/routes/processes.js +50 -0
- package/kanban/server/routes/settings.js +736 -0
- package/kanban/server/routes/websocket.js +281 -0
- package/kanban/server/routes/work-items.js +487 -0
- package/kanban/server/services/CeremonyService.js +1441 -0
- package/kanban/server/services/FileSystemScanner.js +95 -0
- package/kanban/server/services/FileWatcher.js +144 -0
- package/kanban/server/services/HierarchyBuilder.js +196 -0
- package/kanban/server/services/ProcessRegistry.js +122 -0
- package/kanban/server/services/TaskRunnerService.js +261 -0
- package/kanban/server/services/WorkItemReader.js +123 -0
- package/kanban/server/services/WorkItemRefineService.js +510 -0
- package/kanban/server/start.js +49 -0
- package/kanban/server/utils/kanban-logger.js +132 -0
- package/kanban/server/utils/markdown.js +91 -0
- package/kanban/server/utils/status-grouping.js +107 -0
- package/kanban/server/workers/run-task-worker.js +121 -0
- package/kanban/server/workers/seed-worker.js +94 -0
- package/kanban/server/workers/sponsor-call-worker.js +92 -0
- package/kanban/server/workers/sprint-planning-worker.js +212 -0
- package/package.json +19 -7
- package/cli/agents/documentation.md +0 -302
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* LLM-based verification engine
|
|
10
|
+
*
|
|
11
|
+
* CONFIGURATION-DRIVEN VALIDATION:
|
|
12
|
+
* - All validation logic defined in JSON rule files
|
|
13
|
+
* - Fast-path optimizations configurable per rule (not hardcoded)
|
|
14
|
+
* - Hardcoded helpers exist (JSON parsing, regex) but triggered by JSON config
|
|
15
|
+
* - Each rule checks ONE thing and fixes ONE thing (atomic)
|
|
16
|
+
*
|
|
17
|
+
* Fast-Path Types Available:
|
|
18
|
+
* - 'json-parse': Validate JSON syntax programmatically
|
|
19
|
+
* - 'json-fields': Check required fields programmatically
|
|
20
|
+
* - 'none': Always use LLM (no fast-path)
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* const verifier = new LLMVerifier(llmProvider, 'project-documentation-creator');
|
|
24
|
+
* const result = await verifier.verify(content, progressCallback);
|
|
25
|
+
*/
|
|
26
|
+
export class LLMVerifier {
|
|
27
|
+
constructor(llmProvider, agentName, tracker = null) {
|
|
28
|
+
this.llmProvider = llmProvider;
|
|
29
|
+
this.agentName = agentName;
|
|
30
|
+
this.tracker = tracker; // Optional verification tracker
|
|
31
|
+
this.rules = this.loadRules();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Fast-path: Programmatically unwrap JSON from markdown code fence
|
|
36
|
+
* @param {string} content - Content that may be wrapped
|
|
37
|
+
* @returns {object} { isWrapped: boolean, unwrapped: string }
|
|
38
|
+
*/
|
|
39
|
+
unwrapJsonCodeFence(content) {
|
|
40
|
+
const trimmed = content.trim();
|
|
41
|
+
|
|
42
|
+
// Pattern 1: ```json\n...\n```
|
|
43
|
+
const pattern1 = /^```json\s*\n([\s\S]*)\n```$/;
|
|
44
|
+
const match1 = trimmed.match(pattern1);
|
|
45
|
+
if (match1) {
|
|
46
|
+
return { isWrapped: true, unwrapped: match1[1].trim() };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Pattern 2: ```\n...\n``` (generic code fence)
|
|
50
|
+
const pattern2 = /^```\s*\n([\s\S]*)\n```$/;
|
|
51
|
+
const match2 = trimmed.match(pattern2);
|
|
52
|
+
if (match2) {
|
|
53
|
+
// Check if content looks like JSON
|
|
54
|
+
const unwrapped = match2[1].trim();
|
|
55
|
+
if (unwrapped.startsWith('{') || unwrapped.startsWith('[')) {
|
|
56
|
+
return { isWrapped: true, unwrapped };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { isWrapped: false, unwrapped: content };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Fast-path: Check if content is valid JSON
|
|
65
|
+
* @param {string} content - Content to check
|
|
66
|
+
* @returns {object} { canFastPath: boolean, violated: boolean, reason: string }
|
|
67
|
+
*/
|
|
68
|
+
fastPathValidJson(content) {
|
|
69
|
+
// Check for markdown code fence
|
|
70
|
+
const { isWrapped, unwrapped } = this.unwrapJsonCodeFence(content);
|
|
71
|
+
|
|
72
|
+
if (isWrapped) {
|
|
73
|
+
return { canFastPath: true, violated: true, reason: 'markdown-fence' };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Try parsing JSON
|
|
77
|
+
try {
|
|
78
|
+
JSON.parse(unwrapped);
|
|
79
|
+
return { canFastPath: true, violated: false };
|
|
80
|
+
} catch (e) {
|
|
81
|
+
// Parse error - might be fixable by LLM
|
|
82
|
+
return { canFastPath: false, violated: true, reason: e.message };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Fast-path: Check if required fields present
|
|
88
|
+
* @param {string} content - JSON content
|
|
89
|
+
* @param {array} requiredFields - Field names to check
|
|
90
|
+
* @returns {object} { canFastPath: boolean, violated: boolean, missingFields: array }
|
|
91
|
+
*/
|
|
92
|
+
fastPathRequiredFields(content, requiredFields) {
|
|
93
|
+
try {
|
|
94
|
+
// First unwrap if needed
|
|
95
|
+
const { unwrapped } = this.unwrapJsonCodeFence(content);
|
|
96
|
+
const obj = JSON.parse(unwrapped);
|
|
97
|
+
const missing = requiredFields.filter(field => !(field in obj));
|
|
98
|
+
|
|
99
|
+
if (missing.length === 0) {
|
|
100
|
+
return { canFastPath: true, violated: false };
|
|
101
|
+
} else {
|
|
102
|
+
return { canFastPath: true, violated: true, missingFields: missing };
|
|
103
|
+
}
|
|
104
|
+
} catch (e) {
|
|
105
|
+
// Can't parse - let LLM handle
|
|
106
|
+
return { canFastPath: false };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Fast-path: Check if a numeric field is within range
|
|
112
|
+
*/
|
|
113
|
+
fastPathScoreRange(content, field, min, max) {
|
|
114
|
+
try {
|
|
115
|
+
const { unwrapped } = this.unwrapJsonCodeFence(content);
|
|
116
|
+
const obj = JSON.parse(unwrapped);
|
|
117
|
+
const val = obj[field];
|
|
118
|
+
if (typeof val === 'number' && Number.isInteger(val) && val >= min && val <= max) {
|
|
119
|
+
return { canFastPath: true, violated: false };
|
|
120
|
+
}
|
|
121
|
+
return { canFastPath: true, violated: true, reason: `${field}=${val} (expected integer ${min}-${max})` };
|
|
122
|
+
} catch {
|
|
123
|
+
return { canFastPath: false };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Fast-path: Check if a field value is one of allowed enum values
|
|
129
|
+
*/
|
|
130
|
+
fastPathEnum(content, field, allowedValues) {
|
|
131
|
+
try {
|
|
132
|
+
const { unwrapped } = this.unwrapJsonCodeFence(content);
|
|
133
|
+
const obj = JSON.parse(unwrapped);
|
|
134
|
+
if (allowedValues.includes(obj[field])) {
|
|
135
|
+
return { canFastPath: true, violated: false };
|
|
136
|
+
}
|
|
137
|
+
return { canFastPath: true, violated: true, reason: `${field}="${obj[field]}" not in [${allowedValues.join(',')}]` };
|
|
138
|
+
} catch {
|
|
139
|
+
return { canFastPath: false };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Fast-path: Check enum values inside nested objects within arrays
|
|
145
|
+
*/
|
|
146
|
+
fastPathEnumDeep(content, field, allowedValues, searchArrays) {
|
|
147
|
+
try {
|
|
148
|
+
const { unwrapped } = this.unwrapJsonCodeFence(content);
|
|
149
|
+
const obj = JSON.parse(unwrapped);
|
|
150
|
+
const invalid = [];
|
|
151
|
+
for (const arrName of searchArrays) {
|
|
152
|
+
if (Array.isArray(obj[arrName])) {
|
|
153
|
+
for (const item of obj[arrName]) {
|
|
154
|
+
if (item[field] && !allowedValues.includes(item[field])) {
|
|
155
|
+
invalid.push(`${arrName}.${field}="${item[field]}"`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (invalid.length === 0) return { canFastPath: true, violated: false };
|
|
161
|
+
return { canFastPath: true, violated: true, reason: invalid.join(', ') };
|
|
162
|
+
} catch {
|
|
163
|
+
return { canFastPath: false };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Fast-path: Check if specified fields are arrays (not strings/objects/null)
|
|
169
|
+
*/
|
|
170
|
+
fastPathArrayFields(content, arrayFields) {
|
|
171
|
+
try {
|
|
172
|
+
const { unwrapped } = this.unwrapJsonCodeFence(content);
|
|
173
|
+
const obj = JSON.parse(unwrapped);
|
|
174
|
+
const nonArrays = arrayFields.filter(f => f in obj && !Array.isArray(obj[f]));
|
|
175
|
+
if (nonArrays.length === 0) {
|
|
176
|
+
return { canFastPath: true, violated: false };
|
|
177
|
+
}
|
|
178
|
+
return { canFastPath: true, violated: true, reason: `non-array fields: ${nonArrays.join(', ')}` };
|
|
179
|
+
} catch {
|
|
180
|
+
return { canFastPath: false };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Execute fast-path optimization if configured
|
|
186
|
+
* @param {string} content - Content to check
|
|
187
|
+
* @param {Object} rule - Verification rule with fastPath config
|
|
188
|
+
* @returns {Promise<Object>} { canFastPath: boolean, violated: boolean, reason: string }
|
|
189
|
+
*/
|
|
190
|
+
async executeFastPath(content, rule) {
|
|
191
|
+
if (!rule.fastPath?.enabled) {
|
|
192
|
+
return { canFastPath: false };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const type = rule.fastPath.type;
|
|
196
|
+
|
|
197
|
+
switch (type) {
|
|
198
|
+
case 'json-parse':
|
|
199
|
+
// JSON parsing fast-path
|
|
200
|
+
return this.fastPathValidJson(content);
|
|
201
|
+
|
|
202
|
+
case 'json-fields':
|
|
203
|
+
// Required fields fast-path
|
|
204
|
+
const fields = rule.fastPath.requiredFields || [];
|
|
205
|
+
return this.fastPathRequiredFields(content, fields);
|
|
206
|
+
|
|
207
|
+
case 'json-arrays':
|
|
208
|
+
// Array fields fast-path — check that specified fields are arrays
|
|
209
|
+
return this.fastPathArrayFields(content, rule.fastPath.arrayFields || []);
|
|
210
|
+
|
|
211
|
+
case 'json-score-range':
|
|
212
|
+
return this.fastPathScoreRange(content, rule.fastPath.field, rule.fastPath.min, rule.fastPath.max);
|
|
213
|
+
|
|
214
|
+
case 'json-enum':
|
|
215
|
+
return this.fastPathEnum(content, rule.fastPath.field, rule.fastPath.allowedValues || []);
|
|
216
|
+
|
|
217
|
+
case 'json-enum-deep':
|
|
218
|
+
return this.fastPathEnumDeep(content, rule.fastPath.field, rule.fastPath.allowedValues || [], rule.fastPath.searchArrays || []);
|
|
219
|
+
|
|
220
|
+
case 'none':
|
|
221
|
+
default:
|
|
222
|
+
// No fast-path, use LLM
|
|
223
|
+
return { canFastPath: false };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Load verification rules from JSON file
|
|
229
|
+
* @returns {Array} Enabled verification rules
|
|
230
|
+
*/
|
|
231
|
+
loadRules() {
|
|
232
|
+
const rulesPath = path.join(__dirname, 'agents', `${this.agentName}.json`);
|
|
233
|
+
|
|
234
|
+
if (!fs.existsSync(rulesPath)) {
|
|
235
|
+
console.warn(`Warning: No verification rules found for agent: ${this.agentName}`);
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
const data = JSON.parse(fs.readFileSync(rulesPath, 'utf8'));
|
|
241
|
+
|
|
242
|
+
// Filter to enabled rules only
|
|
243
|
+
const enabledRules = data.verifications.filter(r => r.enabled !== false);
|
|
244
|
+
|
|
245
|
+
return enabledRules;
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error(`Error loading verification rules from ${rulesPath}:`, error.message);
|
|
248
|
+
return [];
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Check if rule is violated
|
|
254
|
+
* @param {string} content - Content to check
|
|
255
|
+
* @param {Object} rule - Verification rule
|
|
256
|
+
* @returns {Promise<boolean>} True if rule is violated (needs fixing)
|
|
257
|
+
*/
|
|
258
|
+
async checkRule(content, rule) {
|
|
259
|
+
try {
|
|
260
|
+
if (this.tracker) {
|
|
261
|
+
this.tracker.startRuleCheck(rule);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Try fast-path if configured in rule
|
|
265
|
+
if (rule.fastPath?.enabled) {
|
|
266
|
+
const fastPathResult = await this.executeFastPath(content, rule);
|
|
267
|
+
if (fastPathResult.canFastPath) {
|
|
268
|
+
console.log(`[DEBUG] Fast-path used for ${rule.id}: ${fastPathResult.violated ? 'VIOLATED' : 'PASSED'}${fastPathResult.reason ? ` (${fastPathResult.reason})` : ''}${fastPathResult.missingFields ? ` (missing: ${fastPathResult.missingFields.join(', ')})` : ''}`);
|
|
269
|
+
if (this.tracker) {
|
|
270
|
+
this.tracker.endRuleCheck(fastPathResult.violated ? 'YES' : 'NO');
|
|
271
|
+
}
|
|
272
|
+
return fastPathResult.violated;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Fallback to LLM check
|
|
277
|
+
console.log(`[DEBUG] Fast-path not available for ${rule.id}, using LLM`);
|
|
278
|
+
const prompt = rule.check.prompt.replace('{content}', content);
|
|
279
|
+
const maxTokens = rule.check.maxTokens || 10;
|
|
280
|
+
|
|
281
|
+
const response = await this.llmProvider.generate(prompt, maxTokens);
|
|
282
|
+
const answer = response.trim().toUpperCase();
|
|
283
|
+
|
|
284
|
+
// Check if response matches expected pattern (YES means violation found)
|
|
285
|
+
let result = false;
|
|
286
|
+
if (rule.check.expectedResponse === 'YES|NO') {
|
|
287
|
+
result = answer === 'YES';
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (this.tracker) {
|
|
291
|
+
this.tracker.endRuleCheck(answer);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
console.log(`[DEBUG] checkRule - Rule: ${rule.id}, Result: ${answer}`);
|
|
295
|
+
return result;
|
|
296
|
+
} catch (error) {
|
|
297
|
+
console.error(`Error checking rule ${rule.id}:`, error.message);
|
|
298
|
+
if (this.tracker) {
|
|
299
|
+
this.tracker.endRuleCheck('ERROR');
|
|
300
|
+
this.tracker.completeRule();
|
|
301
|
+
}
|
|
302
|
+
return false; // Skip this rule on error
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Fix content according to rule
|
|
308
|
+
* @param {string} content - Content to fix
|
|
309
|
+
* @param {Object} rule - Verification rule
|
|
310
|
+
* @returns {Promise<string>} Fixed content
|
|
311
|
+
*/
|
|
312
|
+
async fixContent(content, rule) {
|
|
313
|
+
try {
|
|
314
|
+
if (this.tracker) {
|
|
315
|
+
this.tracker.startRuleFix(content.length);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Fast-path fix: unwrap JSON code fences
|
|
319
|
+
if (rule.fastPath?.enabled && rule.fastPath.type === 'json-parse') {
|
|
320
|
+
const { isWrapped, unwrapped } = this.unwrapJsonCodeFence(content);
|
|
321
|
+
if (isWrapped) {
|
|
322
|
+
console.log('[DEBUG] Fast-path: Unwrapping JSON code fence (no LLM call)');
|
|
323
|
+
if (this.tracker) {
|
|
324
|
+
this.tracker.endRuleFix(unwrapped.length);
|
|
325
|
+
}
|
|
326
|
+
return unwrapped;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Fast-path fix: clamp score to valid range
|
|
331
|
+
if (rule.fastPath?.enabled && rule.fastPath.type === 'json-score-range') {
|
|
332
|
+
try {
|
|
333
|
+
const { unwrapped } = this.unwrapJsonCodeFence(content);
|
|
334
|
+
const obj = JSON.parse(unwrapped);
|
|
335
|
+
const { field, min, max } = rule.fastPath;
|
|
336
|
+
let val = obj[field];
|
|
337
|
+
if (typeof val !== 'number' || isNaN(val)) val = 50;
|
|
338
|
+
obj[field] = Math.round(Math.min(max, Math.max(min, val)));
|
|
339
|
+
const result = JSON.stringify(obj, null, 2);
|
|
340
|
+
console.log(`[DEBUG] Fast-path fix for ${rule.id}: clamped ${field} to ${obj[field]} (no LLM call)`);
|
|
341
|
+
if (this.tracker) this.tracker.endRuleFix(result.length);
|
|
342
|
+
return result;
|
|
343
|
+
} catch { /* fall through */ }
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Fast-path fix: correct enum value based on score
|
|
347
|
+
if (rule.fastPath?.enabled && rule.fastPath.type === 'json-enum' && rule.fastPath.field === 'validationStatus') {
|
|
348
|
+
try {
|
|
349
|
+
const { unwrapped } = this.unwrapJsonCodeFence(content);
|
|
350
|
+
const obj = JSON.parse(unwrapped);
|
|
351
|
+
const score = obj.overallScore;
|
|
352
|
+
if (typeof score === 'number') {
|
|
353
|
+
obj.validationStatus = score >= 90 ? 'excellent' : score >= 75 ? 'acceptable' : 'needs-improvement';
|
|
354
|
+
} else {
|
|
355
|
+
obj.validationStatus = 'needs-improvement';
|
|
356
|
+
}
|
|
357
|
+
const result = JSON.stringify(obj, null, 2);
|
|
358
|
+
console.log(`[DEBUG] Fast-path fix for ${rule.id}: set validationStatus="${obj.validationStatus}" (no LLM call)`);
|
|
359
|
+
if (this.tracker) this.tracker.endRuleFix(result.length);
|
|
360
|
+
return result;
|
|
361
|
+
} catch { /* fall through */ }
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Fast-path fix: correct severity enum values in nested arrays
|
|
365
|
+
if (rule.fastPath?.enabled && rule.fastPath.type === 'json-enum-deep') {
|
|
366
|
+
try {
|
|
367
|
+
const { unwrapped } = this.unwrapJsonCodeFence(content);
|
|
368
|
+
const obj = JSON.parse(unwrapped);
|
|
369
|
+
const { field, allowedValues, searchArrays } = rule.fastPath;
|
|
370
|
+
const severityMap = { high: 'critical', medium: 'major', low: 'minor' };
|
|
371
|
+
for (const arrName of searchArrays) {
|
|
372
|
+
if (Array.isArray(obj[arrName])) {
|
|
373
|
+
for (const item of obj[arrName]) {
|
|
374
|
+
if (item[field] && !allowedValues.includes(item[field])) {
|
|
375
|
+
item[field] = severityMap[item[field]] || 'major';
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const result = JSON.stringify(obj, null, 2);
|
|
381
|
+
console.log(`[DEBUG] Fast-path fix for ${rule.id}: corrected ${field} values (no LLM call)`);
|
|
382
|
+
if (this.tracker) this.tracker.endRuleFix(result.length);
|
|
383
|
+
return result;
|
|
384
|
+
} catch { /* fall through */ }
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Fast-path fix: convert non-array fields to arrays
|
|
388
|
+
if (rule.fastPath?.enabled && rule.fastPath.type === 'json-arrays') {
|
|
389
|
+
try {
|
|
390
|
+
const { unwrapped } = this.unwrapJsonCodeFence(content);
|
|
391
|
+
const obj = JSON.parse(unwrapped);
|
|
392
|
+
const arrayFields = rule.fastPath.arrayFields || [];
|
|
393
|
+
let fixed = false;
|
|
394
|
+
for (const field of arrayFields) {
|
|
395
|
+
if (field in obj && !Array.isArray(obj[field])) {
|
|
396
|
+
const val = obj[field];
|
|
397
|
+
if (val === null || val === undefined) {
|
|
398
|
+
obj[field] = [];
|
|
399
|
+
} else if (typeof val === 'string') {
|
|
400
|
+
obj[field] = val ? [val] : [];
|
|
401
|
+
} else if (typeof val === 'object') {
|
|
402
|
+
obj[field] = [val];
|
|
403
|
+
} else {
|
|
404
|
+
obj[field] = [];
|
|
405
|
+
}
|
|
406
|
+
fixed = true;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
if (fixed) {
|
|
410
|
+
const result = JSON.stringify(obj, null, 2);
|
|
411
|
+
console.log(`[DEBUG] Fast-path fix for ${rule.id}: converted non-array fields to arrays (no LLM call)`);
|
|
412
|
+
if (this.tracker) {
|
|
413
|
+
this.tracker.endRuleFix(result.length);
|
|
414
|
+
}
|
|
415
|
+
return result;
|
|
416
|
+
}
|
|
417
|
+
} catch { /* fall through to LLM */ }
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Fast-path fix: add missing required fields with defaults from fix prompt
|
|
421
|
+
if (rule.fastPath?.enabled && rule.fastPath.type === 'json-fields') {
|
|
422
|
+
try {
|
|
423
|
+
const { unwrapped } = this.unwrapJsonCodeFence(content);
|
|
424
|
+
const obj = JSON.parse(unwrapped);
|
|
425
|
+
const missing = (rule.fastPath.requiredFields || []).filter(f => !(f in obj));
|
|
426
|
+
if (missing.length > 0) {
|
|
427
|
+
// Parse defaults from fix prompt — use sensible fallbacks
|
|
428
|
+
const defaults = {
|
|
429
|
+
validationStatus: 'needs-improvement',
|
|
430
|
+
overallScore: 50,
|
|
431
|
+
structuralIssues: [],
|
|
432
|
+
contentIssues: [],
|
|
433
|
+
applicationFlowGaps: [],
|
|
434
|
+
strengths: ['Document structure present'],
|
|
435
|
+
improvementPriorities: [],
|
|
436
|
+
readyForPublication: false,
|
|
437
|
+
};
|
|
438
|
+
for (const field of missing) {
|
|
439
|
+
obj[field] = defaults[field] !== undefined ? defaults[field] : null;
|
|
440
|
+
}
|
|
441
|
+
const fixed = JSON.stringify(obj, null, 2);
|
|
442
|
+
console.log(`[DEBUG] Fast-path fix for ${rule.id}: added missing fields [${missing.join(', ')}] (no LLM call)`);
|
|
443
|
+
if (this.tracker) {
|
|
444
|
+
this.tracker.endRuleFix(fixed.length);
|
|
445
|
+
}
|
|
446
|
+
return fixed;
|
|
447
|
+
}
|
|
448
|
+
} catch { /* JSON parse failed — fall through to LLM */ }
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Fallback to LLM fix
|
|
452
|
+
const prompt = rule.fix.prompt.replace('{content}', content);
|
|
453
|
+
const maxTokens = rule.fix.maxTokens || 4096;
|
|
454
|
+
|
|
455
|
+
console.log(`[DEBUG] fixContent - Rule: ${rule.id}, Fixing content (length: ${content.length})`);
|
|
456
|
+
const fixed = await this.llmProvider.generate(prompt, maxTokens);
|
|
457
|
+
console.log(`[DEBUG] fixContent - Rule: ${rule.id}, LLM returned ${fixed.length} chars`);
|
|
458
|
+
console.log(`[DEBUG] fixContent - Rule: ${rule.id}, Raw output preview:`, fixed.substring(0, 300));
|
|
459
|
+
|
|
460
|
+
const trimmed = fixed.trim();
|
|
461
|
+
console.log(`[DEBUG] fixContent - Rule: ${rule.id}, After trim: ${trimmed.length} chars`);
|
|
462
|
+
|
|
463
|
+
if (this.tracker) {
|
|
464
|
+
this.tracker.endRuleFix(trimmed.length);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return trimmed;
|
|
468
|
+
} catch (error) {
|
|
469
|
+
console.error(`Error fixing with rule ${rule.id}:`, error.message);
|
|
470
|
+
return content; // Return original content on error
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Verify and fix content using all enabled rules
|
|
476
|
+
* @param {string} content - Content to verify
|
|
477
|
+
* @param {Function} progressCallback - Optional callback (mainMsg, substep)
|
|
478
|
+
* @returns {Promise<Object>} { content, rulesApplied }
|
|
479
|
+
*/
|
|
480
|
+
async verify(content, progressCallback = null) {
|
|
481
|
+
// Check cache first
|
|
482
|
+
if (this.tracker && this.tracker.verificationCache) {
|
|
483
|
+
const contentHash = this.tracker.hashContent(content);
|
|
484
|
+
const cacheKey = `${this.agentName}-${contentHash}`;
|
|
485
|
+
|
|
486
|
+
if (this.tracker.verificationCache.has(cacheKey)) {
|
|
487
|
+
console.log(`[DEBUG] Cache HIT: Reusing verification for ${this.agentName} (hash: ${contentHash})`);
|
|
488
|
+
return this.tracker.verificationCache.get(cacheKey);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
console.log(`[DEBUG] Cache MISS: Running verification for ${this.agentName} (hash: ${contentHash})`);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (this.tracker) {
|
|
495
|
+
this.tracker.startSession(this.agentName, content);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
console.log(`[DEBUG] verify - Starting verification with ${this.rules.length} rules`);
|
|
499
|
+
console.log(`[DEBUG] verify - Input content length: ${content.length}`);
|
|
500
|
+
console.log(`[DEBUG] verify - Input content preview:`, content.substring(0, 300));
|
|
501
|
+
|
|
502
|
+
let current = content;
|
|
503
|
+
const applied = [];
|
|
504
|
+
|
|
505
|
+
// If no rules loaded, return original content
|
|
506
|
+
if (this.rules.length === 0) {
|
|
507
|
+
return { content: current, rulesApplied: [] };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// PHASE 1: Check all rules in parallel
|
|
511
|
+
console.log(`[DEBUG] verify - Phase 1: Checking ${this.rules.length} rules in parallel`);
|
|
512
|
+
if (progressCallback) {
|
|
513
|
+
progressCallback(null, `Checking ${this.rules.length} rules...`);
|
|
514
|
+
await new Promise(resolve => setTimeout(resolve, 20));
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const checkPromises = this.rules.map(async (rule) => {
|
|
518
|
+
// Check if rule should be skipped based on profiling
|
|
519
|
+
if (this.tracker && this.tracker.shouldSkipRule(this.agentName, rule.id)) {
|
|
520
|
+
return { rule, violated: false, error: null, skipped: true };
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
try {
|
|
524
|
+
const violated = await this.checkRule(current, rule);
|
|
525
|
+
|
|
526
|
+
// Update rule profile
|
|
527
|
+
if (this.tracker) {
|
|
528
|
+
this.tracker.updateRuleProfile(this.agentName, rule.id, violated);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
return { rule, violated, error: null, skipped: false };
|
|
532
|
+
} catch (error) {
|
|
533
|
+
console.error(`Error checking rule ${rule.id}:`, error.message);
|
|
534
|
+
return { rule, violated: false, error: error.message, skipped: false };
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
const checkResults = await Promise.all(checkPromises);
|
|
539
|
+
|
|
540
|
+
// PHASE 2: Fix violations sequentially
|
|
541
|
+
console.log(`[DEBUG] verify - Phase 2: Fixing violations sequentially`);
|
|
542
|
+
const violatedRules = checkResults.filter(r => r.violated && !r.error);
|
|
543
|
+
console.log(`[DEBUG] verify - Found ${violatedRules.length} violations:`, violatedRules.map(r => r.rule.id));
|
|
544
|
+
|
|
545
|
+
for (const { rule, violated } of violatedRules) {
|
|
546
|
+
// SAFEGUARD: Double-check that rule was actually violated
|
|
547
|
+
if (!violated) {
|
|
548
|
+
console.warn(`[WARN] verify - Skipping fix for ${rule.id} - violated flag is false (defensive check)`);
|
|
549
|
+
if (this.tracker) {
|
|
550
|
+
this.tracker.completeRule();
|
|
551
|
+
}
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Report progress: fixing
|
|
556
|
+
if (progressCallback) {
|
|
557
|
+
progressCallback(null, `Fixing: ${rule.name}...`);
|
|
558
|
+
await new Promise(resolve => setTimeout(resolve, 20));
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
try {
|
|
562
|
+
const beforeLength = current.length;
|
|
563
|
+
|
|
564
|
+
// Apply fix
|
|
565
|
+
const fixed = await this.fixContent(current, rule);
|
|
566
|
+
|
|
567
|
+
// Only update if fix actually changed content
|
|
568
|
+
if (fixed !== current) {
|
|
569
|
+
const afterLength = fixed.length;
|
|
570
|
+
const changePercent = Math.abs((afterLength - beforeLength) / beforeLength * 100);
|
|
571
|
+
const changeChars = afterLength - beforeLength;
|
|
572
|
+
|
|
573
|
+
// SAFEGUARD: Warn on aggressive content changes
|
|
574
|
+
if (changePercent > 30) {
|
|
575
|
+
console.warn(`[WARN] verify - Rule ${rule.id} caused ${changePercent.toFixed(1)}% content change (${changeChars > 0 ? '+' : ''}${changeChars} chars)`);
|
|
576
|
+
console.warn(`[WARN] verify - Before: ${beforeLength} chars, After: ${afterLength} chars`);
|
|
577
|
+
console.warn(`[WARN] verify - This may indicate an overly aggressive fix. Review rule prompt.`);
|
|
578
|
+
} else {
|
|
579
|
+
console.log(`[DEBUG] verify - Rule ${rule.id} changed content by ${changePercent.toFixed(1)}% (${changeChars > 0 ? '+' : ''}${changeChars} chars)`);
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
current = fixed;
|
|
583
|
+
applied.push({
|
|
584
|
+
id: rule.id,
|
|
585
|
+
name: rule.name,
|
|
586
|
+
severity: rule.severity,
|
|
587
|
+
description: rule.description
|
|
588
|
+
});
|
|
589
|
+
} else {
|
|
590
|
+
console.log(`[DEBUG] verify - Rule ${rule.id} fix did not change content (no-op fix)`);
|
|
591
|
+
}
|
|
592
|
+
} catch (error) {
|
|
593
|
+
console.error(`Error fixing rule ${rule.id}:`, error.message);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (this.tracker) {
|
|
597
|
+
this.tracker.completeRule();
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Complete tracking for rules that didn't violate
|
|
602
|
+
const passedRules = checkResults.filter(r => !r.violated || r.error);
|
|
603
|
+
for (const { rule } of passedRules) {
|
|
604
|
+
if (this.tracker) {
|
|
605
|
+
this.tracker.completeRule();
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
console.log(`[DEBUG] verify - Completed with ${applied.length} rules applied:`, applied.map(r => r.id));
|
|
610
|
+
console.log(`[DEBUG] verify - Final content length: ${current.length}`);
|
|
611
|
+
console.log(`[DEBUG] verify - Final content preview:`, current.substring(0, 300));
|
|
612
|
+
|
|
613
|
+
if (this.tracker) {
|
|
614
|
+
this.tracker.endSession(current, applied);
|
|
615
|
+
this.tracker.logSessionSummary();
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const result = {
|
|
619
|
+
content: current,
|
|
620
|
+
rulesApplied: applied,
|
|
621
|
+
noViolations: applied.length === 0, // Track if this was a perfect verification
|
|
622
|
+
timestamp: Date.now()
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
// Store in cache
|
|
626
|
+
if (this.tracker && this.tracker.verificationCache) {
|
|
627
|
+
const contentHash = this.tracker.hashContent(content);
|
|
628
|
+
const cacheKey = `${this.agentName}-${contentHash}`;
|
|
629
|
+
this.tracker.verificationCache.set(cacheKey, result);
|
|
630
|
+
|
|
631
|
+
if (applied.length === 0) {
|
|
632
|
+
console.log(`[DEBUG] Cached PERFECT verification result for ${this.agentName} (hash: ${contentHash}) - no violations found`);
|
|
633
|
+
} else {
|
|
634
|
+
console.log(`[DEBUG] Cached verification result for ${this.agentName} (hash: ${contentHash}) - ${applied.length} fixes applied`);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
return result;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* Get list of all rules for this agent
|
|
643
|
+
* @returns {Array} Rule metadata (id, name, severity, description)
|
|
644
|
+
*/
|
|
645
|
+
getRules() {
|
|
646
|
+
return this.rules.map(r => ({
|
|
647
|
+
id: r.id,
|
|
648
|
+
name: r.name,
|
|
649
|
+
severity: r.severity,
|
|
650
|
+
description: r.description,
|
|
651
|
+
enabled: r.enabled !== false
|
|
652
|
+
}));
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Get count of enabled rules
|
|
657
|
+
* @returns {number} Number of enabled rules
|
|
658
|
+
*/
|
|
659
|
+
getRuleCount() {
|
|
660
|
+
return this.rules.length;
|
|
661
|
+
}
|
|
662
|
+
}
|