@agile-vibe-coding/avc 0.1.0 → 0.2.3
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/README.md +2 -0
- package/cli/agent-loader.js +21 -0
- package/cli/agents/agent-selector.md +129 -0
- package/cli/agents/architecture-recommender.md +418 -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/documentation-updater.md +203 -0
- package/cli/agents/epic-story-decomposer.md +280 -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 +79 -0
- package/cli/agents/mission-scope-validator.md +112 -0
- package/cli/agents/project-context-extractor.md +107 -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/solver-epic-api.json +15 -0
- package/cli/agents/solver-epic-api.md +39 -0
- package/cli/agents/solver-epic-backend.json +15 -0
- package/cli/agents/solver-epic-backend.md +39 -0
- package/cli/agents/solver-epic-cloud.json +15 -0
- package/cli/agents/solver-epic-cloud.md +39 -0
- package/cli/agents/solver-epic-data.json +15 -0
- package/cli/agents/solver-epic-data.md +39 -0
- package/cli/agents/solver-epic-database.json +15 -0
- package/cli/agents/solver-epic-database.md +39 -0
- package/cli/agents/solver-epic-developer.json +15 -0
- package/cli/agents/solver-epic-developer.md +39 -0
- package/cli/agents/solver-epic-devops.json +15 -0
- package/cli/agents/solver-epic-devops.md +39 -0
- package/cli/agents/solver-epic-frontend.json +15 -0
- package/cli/agents/solver-epic-frontend.md +39 -0
- package/cli/agents/solver-epic-mobile.json +15 -0
- package/cli/agents/solver-epic-mobile.md +39 -0
- package/cli/agents/solver-epic-qa.json +15 -0
- package/cli/agents/solver-epic-qa.md +39 -0
- package/cli/agents/solver-epic-security.json +15 -0
- package/cli/agents/solver-epic-security.md +39 -0
- package/cli/agents/solver-epic-solution-architect.json +15 -0
- package/cli/agents/solver-epic-solution-architect.md +39 -0
- package/cli/agents/solver-epic-test-architect.json +15 -0
- package/cli/agents/solver-epic-test-architect.md +39 -0
- package/cli/agents/solver-epic-ui.json +15 -0
- package/cli/agents/solver-epic-ui.md +39 -0
- package/cli/agents/solver-epic-ux.json +15 -0
- package/cli/agents/solver-epic-ux.md +39 -0
- package/cli/agents/solver-story-api.json +15 -0
- package/cli/agents/solver-story-api.md +39 -0
- package/cli/agents/solver-story-backend.json +15 -0
- package/cli/agents/solver-story-backend.md +39 -0
- package/cli/agents/solver-story-cloud.json +15 -0
- package/cli/agents/solver-story-cloud.md +39 -0
- package/cli/agents/solver-story-data.json +15 -0
- package/cli/agents/solver-story-data.md +39 -0
- package/cli/agents/solver-story-database.json +15 -0
- package/cli/agents/solver-story-database.md +39 -0
- package/cli/agents/solver-story-developer.json +15 -0
- package/cli/agents/solver-story-developer.md +39 -0
- package/cli/agents/solver-story-devops.json +15 -0
- package/cli/agents/solver-story-devops.md +39 -0
- package/cli/agents/solver-story-frontend.json +15 -0
- package/cli/agents/solver-story-frontend.md +39 -0
- package/cli/agents/solver-story-mobile.json +15 -0
- package/cli/agents/solver-story-mobile.md +39 -0
- package/cli/agents/solver-story-qa.json +15 -0
- package/cli/agents/solver-story-qa.md +39 -0
- package/cli/agents/solver-story-security.json +15 -0
- package/cli/agents/solver-story-security.md +39 -0
- package/cli/agents/solver-story-solution-architect.json +15 -0
- package/cli/agents/solver-story-solution-architect.md +39 -0
- package/cli/agents/solver-story-test-architect.json +15 -0
- package/cli/agents/solver-story-test-architect.md +39 -0
- package/cli/agents/solver-story-ui.json +15 -0
- package/cli/agents/solver-story-ui.md +39 -0
- package/cli/agents/solver-story-ux.json +15 -0
- package/cli/agents/solver-story-ux.md +39 -0
- package/cli/agents/story-doc-enricher.md +133 -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 +152 -0
- package/cli/agents/validator-documentation.md +453 -0
- package/cli/agents/validator-epic-api.json +93 -0
- package/cli/agents/validator-epic-api.md +137 -0
- package/cli/agents/validator-epic-backend.json +93 -0
- package/cli/agents/validator-epic-backend.md +130 -0
- package/cli/agents/validator-epic-cloud.json +93 -0
- package/cli/agents/validator-epic-cloud.md +137 -0
- package/cli/agents/validator-epic-data.json +93 -0
- package/cli/agents/validator-epic-data.md +130 -0
- package/cli/agents/validator-epic-database.json +93 -0
- package/cli/agents/validator-epic-database.md +137 -0
- package/cli/agents/validator-epic-developer.json +74 -0
- package/cli/agents/validator-epic-developer.md +153 -0
- package/cli/agents/validator-epic-devops.json +74 -0
- package/cli/agents/validator-epic-devops.md +153 -0
- package/cli/agents/validator-epic-frontend.json +74 -0
- package/cli/agents/validator-epic-frontend.md +153 -0
- package/cli/agents/validator-epic-mobile.json +93 -0
- package/cli/agents/validator-epic-mobile.md +130 -0
- package/cli/agents/validator-epic-qa.json +93 -0
- package/cli/agents/validator-epic-qa.md +130 -0
- package/cli/agents/validator-epic-security.json +74 -0
- package/cli/agents/validator-epic-security.md +154 -0
- package/cli/agents/validator-epic-solution-architect.json +74 -0
- package/cli/agents/validator-epic-solution-architect.md +156 -0
- package/cli/agents/validator-epic-test-architect.json +93 -0
- package/cli/agents/validator-epic-test-architect.md +130 -0
- package/cli/agents/validator-epic-ui.json +93 -0
- package/cli/agents/validator-epic-ui.md +130 -0
- package/cli/agents/validator-epic-ux.json +93 -0
- package/cli/agents/validator-epic-ux.md +130 -0
- package/cli/agents/validator-selector.md +211 -0
- package/cli/agents/validator-story-api.json +104 -0
- package/cli/agents/validator-story-api.md +152 -0
- package/cli/agents/validator-story-backend.json +104 -0
- package/cli/agents/validator-story-backend.md +152 -0
- package/cli/agents/validator-story-cloud.json +104 -0
- package/cli/agents/validator-story-cloud.md +152 -0
- package/cli/agents/validator-story-data.json +104 -0
- package/cli/agents/validator-story-data.md +152 -0
- package/cli/agents/validator-story-database.json +104 -0
- package/cli/agents/validator-story-database.md +152 -0
- package/cli/agents/validator-story-developer.json +104 -0
- package/cli/agents/validator-story-developer.md +152 -0
- package/cli/agents/validator-story-devops.json +104 -0
- package/cli/agents/validator-story-devops.md +152 -0
- package/cli/agents/validator-story-frontend.json +104 -0
- package/cli/agents/validator-story-frontend.md +152 -0
- package/cli/agents/validator-story-mobile.json +104 -0
- package/cli/agents/validator-story-mobile.md +152 -0
- package/cli/agents/validator-story-qa.json +104 -0
- package/cli/agents/validator-story-qa.md +152 -0
- package/cli/agents/validator-story-security.json +104 -0
- package/cli/agents/validator-story-security.md +152 -0
- package/cli/agents/validator-story-solution-architect.json +104 -0
- package/cli/agents/validator-story-solution-architect.md +152 -0
- package/cli/agents/validator-story-test-architect.json +104 -0
- package/cli/agents/validator-story-test-architect.md +152 -0
- package/cli/agents/validator-story-ui.json +104 -0
- package/cli/agents/validator-story-ui.md +152 -0
- package/cli/agents/validator-story-ux.json +104 -0
- package/cli/agents/validator-story-ux.md +152 -0
- package/cli/ansi-colors.js +21 -0
- package/cli/build-docs.js +298 -0
- package/cli/ceremony-history.js +369 -0
- package/cli/command-logger.js +245 -0
- package/cli/components/static-output.js +63 -0
- package/cli/console-output-manager.js +94 -0
- package/cli/docs-sync.js +306 -0
- package/cli/epic-story-validator.js +1174 -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/index.js +3 -25
- package/cli/init-model-config.js +697 -0
- package/cli/init.js +1765 -100
- package/cli/kanban-server-manager.js +228 -0
- package/cli/llm-claude.js +109 -0
- package/cli/llm-gemini.js +115 -0
- package/cli/llm-mock.js +233 -0
- package/cli/llm-openai.js +233 -0
- package/cli/llm-provider.js +300 -0
- package/cli/llm-token-limits.js +102 -0
- package/cli/llm-verifier.js +454 -0
- package/cli/logger.js +32 -5
- package/cli/message-constants.js +58 -0
- package/cli/message-manager.js +334 -0
- package/cli/message-types.js +96 -0
- package/cli/messaging-api.js +297 -0
- package/cli/model-pricing.js +169 -0
- package/cli/model-query-engine.js +468 -0
- package/cli/model-recommendation-analyzer.js +495 -0
- package/cli/model-selector.js +269 -0
- package/cli/output-buffer.js +107 -0
- package/cli/process-manager.js +332 -0
- package/cli/repl-ink.js +5840 -504
- package/cli/repl-old.js +4 -4
- package/cli/seed-processor.js +792 -0
- package/cli/sprint-planning-processor.js +1813 -0
- package/cli/template-processor.js +2306 -108
- package/cli/templates/project.md +25 -8
- package/cli/templates/vitepress-config.mts.template +34 -0
- package/cli/token-tracker.js +520 -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 +605 -0
- package/cli/verification-tracker.js +563 -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-CiD8PS2e.js +306 -0
- package/kanban/client/dist/assets/index-nLh0m82Q.css +1 -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 +622 -0
- package/kanban/client/src/components/ProjectFileEditorPopup.jsx +117 -0
- package/kanban/client/src/components/ceremony/AskArchPopup.jsx +416 -0
- package/kanban/client/src/components/ceremony/AskModelPopup.jsx +616 -0
- package/kanban/client/src/components/ceremony/CeremonyWorkflowModal.jsx +946 -0
- package/kanban/client/src/components/ceremony/EpicStorySelectionModal.jsx +254 -0
- package/kanban/client/src/components/ceremony/SponsorCallModal.jsx +619 -0
- package/kanban/client/src/components/ceremony/SprintPlanningModal.jsx +704 -0
- package/kanban/client/src/components/ceremony/steps/ArchitectureStep.jsx +150 -0
- package/kanban/client/src/components/ceremony/steps/CompleteStep.jsx +154 -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 +125 -0
- package/kanban/client/src/components/ceremony/steps/RunningStep.jsx +228 -0
- package/kanban/client/src/components/kanban/CardDetailModal.jsx +559 -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 +57 -0
- package/kanban/client/src/components/kanban/KanbanBoard.jsx +211 -0
- package/kanban/client/src/components/kanban/KanbanCard.jsx +138 -0
- package/kanban/client/src/components/kanban/KanbanColumn.jsx +90 -0
- package/kanban/client/src/components/kanban/RefineWorkItemPopup.jsx +789 -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 +353 -0
- package/kanban/client/src/components/settings/ApiKeysTab.jsx +113 -0
- package/kanban/client/src/components/settings/CeremonyModelsTab.jsx +98 -0
- package/kanban/client/src/components/settings/CostThresholdsTab.jsx +94 -0
- package/kanban/client/src/components/settings/ModelPricingTab.jsx +204 -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 +353 -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 +118 -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 +401 -0
- package/kanban/client/src/lib/status-grouping.js +144 -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 +115 -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 +516 -0
- package/kanban/server/routes/ceremony.js +305 -0
- package/kanban/server/routes/costs.js +157 -0
- package/kanban/server/routes/processes.js +50 -0
- package/kanban/server/routes/settings.js +303 -0
- package/kanban/server/routes/websocket.js +276 -0
- package/kanban/server/routes/work-items.js +347 -0
- package/kanban/server/services/CeremonyService.js +1190 -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/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/sponsor-call-worker.js +84 -0
- package/kanban/server/workers/sprint-planning-worker.js +130 -0
- package/package.json +34 -7
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import net from 'net';
|
|
6
|
+
import http from 'http';
|
|
7
|
+
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Kanban Server Manager
|
|
12
|
+
* Manages lifecycle of the AVC Kanban Board server
|
|
13
|
+
*/
|
|
14
|
+
export class KanbanServerManager {
|
|
15
|
+
constructor(projectRoot = process.cwd()) {
|
|
16
|
+
this.projectRoot = projectRoot;
|
|
17
|
+
this.avcDir = path.join(projectRoot, '.avc');
|
|
18
|
+
this.avcProjectPath = path.join(this.avcDir, 'project');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check if .avc/project directory exists
|
|
23
|
+
*/
|
|
24
|
+
hasWorkItems() {
|
|
25
|
+
return fs.existsSync(this.avcProjectPath);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get kanban server port from avc.json config
|
|
30
|
+
* Returns default port 4174 if not configured
|
|
31
|
+
*/
|
|
32
|
+
getPort() {
|
|
33
|
+
const configPath = path.join(this.avcDir, 'avc.json');
|
|
34
|
+
|
|
35
|
+
if (!fs.existsSync(configPath)) {
|
|
36
|
+
return 4174; // Default port
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
41
|
+
return config.settings?.kanban?.port || 4174;
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.warn(`Could not read port from avc.json: ${error.message}`);
|
|
44
|
+
return 4174;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if a port is in use
|
|
50
|
+
* @param {number} port - Port number to check
|
|
51
|
+
* @returns {Promise<boolean>} - True if port is in use
|
|
52
|
+
*/
|
|
53
|
+
async isPortInUse(port) {
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
const server = net.createServer();
|
|
56
|
+
|
|
57
|
+
server.once('error', (err) => {
|
|
58
|
+
if (err.code === 'EADDRINUSE') {
|
|
59
|
+
resolve(true); // Port is in use
|
|
60
|
+
} else {
|
|
61
|
+
resolve(false);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
server.once('listening', () => {
|
|
66
|
+
server.close();
|
|
67
|
+
resolve(false); // Port is available
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
server.listen(port, '127.0.0.1');
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if the server on this port is the AVC Kanban Board
|
|
76
|
+
* Makes HTTP request to /api/health and checks for AVC kanban response
|
|
77
|
+
* @param {number} port - Port number to check
|
|
78
|
+
* @returns {Promise<boolean>} - True if it's confirmed to be AVC kanban server
|
|
79
|
+
*/
|
|
80
|
+
async isKanbanServer(port) {
|
|
81
|
+
return new Promise((resolve) => {
|
|
82
|
+
const req = http.get(`http://localhost:${port}/api/health`, {
|
|
83
|
+
timeout: 2000,
|
|
84
|
+
}, (res) => {
|
|
85
|
+
let data = '';
|
|
86
|
+
|
|
87
|
+
res.on('data', (chunk) => {
|
|
88
|
+
data += chunk;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
res.on('end', () => {
|
|
92
|
+
try {
|
|
93
|
+
const json = JSON.parse(data);
|
|
94
|
+
// Check if it's our AVC kanban server
|
|
95
|
+
const isKanban = json.projectRoot === this.projectRoot;
|
|
96
|
+
resolve(isKanban);
|
|
97
|
+
} catch {
|
|
98
|
+
resolve(false);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
req.on('error', () => {
|
|
104
|
+
resolve(false); // Can't connect or verify
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
req.on('timeout', () => {
|
|
108
|
+
req.destroy();
|
|
109
|
+
resolve(false);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Find which process is using a port
|
|
116
|
+
* Works cross-platform (Linux, macOS, Windows)
|
|
117
|
+
* @param {number} port - Port number to check
|
|
118
|
+
* @returns {Promise<{pid: number, command: string} | null>} - Process info or null if not found
|
|
119
|
+
*/
|
|
120
|
+
async findProcessUsingPort(port) {
|
|
121
|
+
try {
|
|
122
|
+
let command;
|
|
123
|
+
let parseOutput;
|
|
124
|
+
|
|
125
|
+
if (process.platform === 'win32') {
|
|
126
|
+
// Windows: netstat -ano | findstr :PORT
|
|
127
|
+
command = `netstat -ano | findstr :${port}`;
|
|
128
|
+
parseOutput = (output) => {
|
|
129
|
+
const lines = output.split('\n');
|
|
130
|
+
for (const line of lines) {
|
|
131
|
+
if (
|
|
132
|
+
line.includes(`0.0.0.0:${port}`) ||
|
|
133
|
+
line.includes(`127.0.0.1:${port}`) ||
|
|
134
|
+
line.includes(`[::]:${port}`)
|
|
135
|
+
) {
|
|
136
|
+
const parts = line.trim().split(/\s+/);
|
|
137
|
+
const pid = parseInt(parts[parts.length - 1]);
|
|
138
|
+
if (pid && !isNaN(pid)) {
|
|
139
|
+
return { pid, command: 'Unknown' };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
};
|
|
145
|
+
} else {
|
|
146
|
+
// Linux/macOS: lsof -i :PORT
|
|
147
|
+
command = `lsof -i :${port} -t -sTCP:LISTEN`;
|
|
148
|
+
parseOutput = (output) => {
|
|
149
|
+
const pid = parseInt(output.trim());
|
|
150
|
+
if (pid && !isNaN(pid)) {
|
|
151
|
+
// Try to get process name
|
|
152
|
+
try {
|
|
153
|
+
const { execSync } = require('child_process');
|
|
154
|
+
const psOutput = execSync(`ps -p ${pid} -o comm=`, { encoding: 'utf8' });
|
|
155
|
+
return { pid, command: psOutput.trim() };
|
|
156
|
+
} catch {
|
|
157
|
+
return { pid, command: 'Unknown' };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const { stdout } = await execAsync(command);
|
|
165
|
+
return parseOutput(stdout);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
// Command failed (no process found) or permission error
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Kill a process by PID
|
|
174
|
+
* @param {number} pid - Process ID to kill
|
|
175
|
+
* @returns {Promise<boolean>} - True if kill succeeded
|
|
176
|
+
*/
|
|
177
|
+
async killProcess(pid) {
|
|
178
|
+
try {
|
|
179
|
+
if (process.platform === 'win32') {
|
|
180
|
+
await execAsync(`taskkill /F /PID ${pid}`);
|
|
181
|
+
} else {
|
|
182
|
+
await execAsync(`kill -9 ${pid}`);
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.error(`Failed to kill process ${pid}:`, error.message);
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get the frontend dev server URL (Vite)
|
|
193
|
+
* @returns {string} Frontend URL
|
|
194
|
+
*/
|
|
195
|
+
getFrontendUrl() {
|
|
196
|
+
const config = this.getConfig();
|
|
197
|
+
const frontendPort = config.settings?.kanban?.frontendPort || 5173;
|
|
198
|
+
return `http://localhost:${frontendPort}`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get the backend API server URL
|
|
203
|
+
* @returns {string} Backend URL
|
|
204
|
+
*/
|
|
205
|
+
getBackendUrl() {
|
|
206
|
+
const port = this.getPort();
|
|
207
|
+
return `http://localhost:${port}`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Get AVC configuration
|
|
212
|
+
* @returns {object} Configuration object
|
|
213
|
+
*/
|
|
214
|
+
getConfig() {
|
|
215
|
+
const configPath = path.join(this.avcDir, 'avc.json');
|
|
216
|
+
|
|
217
|
+
if (!fs.existsSync(configPath)) {
|
|
218
|
+
return { settings: {} };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
try {
|
|
222
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.warn(`Could not read avc.json: ${error.message}`);
|
|
225
|
+
return { settings: {} };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import { jsonrepair } from 'jsonrepair';
|
|
3
|
+
import { LLMProvider } from './llm-provider.js';
|
|
4
|
+
import { getMaxTokensForModel } from './llm-token-limits.js';
|
|
5
|
+
|
|
6
|
+
export class ClaudeProvider extends LLMProvider {
|
|
7
|
+
constructor(model) { super('claude', model); }
|
|
8
|
+
|
|
9
|
+
_createClient() {
|
|
10
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
11
|
+
if (!apiKey) throw new Error('ANTHROPIC_API_KEY not set. Add it to your .env file.');
|
|
12
|
+
// 5-minute timeout per request; SDK retries disabled so our retryWithBackoff
|
|
13
|
+
// handles all retries with full logging visibility.
|
|
14
|
+
return new Anthropic({ apiKey, timeout: 5 * 60 * 1000, maxRetries: 0 });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async _callProvider(prompt, maxTokens, systemInstructions) {
|
|
18
|
+
const params = {
|
|
19
|
+
model: this.model,
|
|
20
|
+
max_tokens: maxTokens,
|
|
21
|
+
messages: [{ role: 'user', content: prompt }]
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
if (systemInstructions) {
|
|
25
|
+
params.system = systemInstructions;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const response = await this._client.messages.create(params);
|
|
29
|
+
this._trackTokens(response.usage);
|
|
30
|
+
return response.content[0].text;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async generateJSON(prompt, agentInstructions = null) {
|
|
34
|
+
if (!this._client) {
|
|
35
|
+
this._client = this._createClient();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const fullPrompt = agentInstructions ? `${agentInstructions}\n\n${prompt}` : prompt;
|
|
39
|
+
|
|
40
|
+
// Use model-specific maximum tokens
|
|
41
|
+
const maxTokens = getMaxTokensForModel(this.model);
|
|
42
|
+
|
|
43
|
+
const response = await this._withRetry(
|
|
44
|
+
() => this._client.messages.create({
|
|
45
|
+
model: this.model,
|
|
46
|
+
max_tokens: maxTokens,
|
|
47
|
+
messages: [{
|
|
48
|
+
role: 'user',
|
|
49
|
+
content: fullPrompt
|
|
50
|
+
}],
|
|
51
|
+
system: 'You are a helpful assistant that always returns valid JSON. Your response must be a valid JSON object or array, nothing else.'
|
|
52
|
+
}),
|
|
53
|
+
'JSON generation (Claude)'
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
this._trackTokens(response.usage);
|
|
57
|
+
const content = response.content[0].text;
|
|
58
|
+
|
|
59
|
+
// Extract JSON from response (handle markdown code blocks)
|
|
60
|
+
// Strip markdown code fences if present (more robust)
|
|
61
|
+
let jsonStr = content.trim();
|
|
62
|
+
if (jsonStr.startsWith('```')) {
|
|
63
|
+
// Remove opening fence (```json or ```)
|
|
64
|
+
jsonStr = jsonStr.replace(/^```(?:json)?\s*\n?/, '');
|
|
65
|
+
// Remove closing fence
|
|
66
|
+
jsonStr = jsonStr.replace(/\n?\s*```\s*$/, '');
|
|
67
|
+
jsonStr = jsonStr.trim();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(jsonStr);
|
|
72
|
+
} catch (firstError) {
|
|
73
|
+
// Only attempt repair when the content looks like JSON (starts with { or [)
|
|
74
|
+
// — avoids silently accepting completely non-JSON responses
|
|
75
|
+
if (jsonStr.startsWith('{') || jsonStr.startsWith('[')) {
|
|
76
|
+
try {
|
|
77
|
+
return JSON.parse(jsonrepair(jsonStr));
|
|
78
|
+
} catch { /* fall through to throw */ }
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`Failed to parse JSON response: ${firstError.message}\n\nResponse was:\n${content}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async generateText(prompt, agentInstructions = null) {
|
|
85
|
+
if (!this._client) {
|
|
86
|
+
this._client = this._createClient();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const fullPrompt = agentInstructions ? `${agentInstructions}\n\n${prompt}` : prompt;
|
|
90
|
+
|
|
91
|
+
// Use model-specific maximum tokens
|
|
92
|
+
const maxTokens = getMaxTokensForModel(this.model);
|
|
93
|
+
|
|
94
|
+
const response = await this._withRetry(
|
|
95
|
+
() => this._client.messages.create({
|
|
96
|
+
model: this.model,
|
|
97
|
+
max_tokens: maxTokens,
|
|
98
|
+
messages: [{
|
|
99
|
+
role: 'user',
|
|
100
|
+
content: fullPrompt
|
|
101
|
+
}]
|
|
102
|
+
}),
|
|
103
|
+
'Text generation (Claude)'
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
this._trackTokens(response.usage);
|
|
107
|
+
return response.content[0].text;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { GoogleGenAI } from '@google/genai';
|
|
2
|
+
import { jsonrepair } from 'jsonrepair';
|
|
3
|
+
import { LLMProvider } from './llm-provider.js';
|
|
4
|
+
import { getMaxTokensForModel } from './llm-token-limits.js';
|
|
5
|
+
|
|
6
|
+
export class GeminiProvider extends LLMProvider {
|
|
7
|
+
constructor(model = 'gemini-2.5-flash') { super('gemini', model); }
|
|
8
|
+
|
|
9
|
+
_createClient() {
|
|
10
|
+
const apiKey = process.env.GEMINI_API_KEY;
|
|
11
|
+
if (!apiKey) throw new Error('GEMINI_API_KEY not set. Add it to your .env file.');
|
|
12
|
+
return new GoogleGenAI({ apiKey });
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async _callProvider(prompt, maxTokens, systemInstructions) {
|
|
16
|
+
const params = {
|
|
17
|
+
model: this.model,
|
|
18
|
+
contents: prompt,
|
|
19
|
+
generationConfig: { maxOutputTokens: maxTokens }
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
if (systemInstructions) {
|
|
23
|
+
params.systemInstruction = systemInstructions;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const response = await this._client.models.generateContent(params);
|
|
27
|
+
if (!response.text) {
|
|
28
|
+
throw new Error('Gemini returned no text (possible safety filter block).');
|
|
29
|
+
}
|
|
30
|
+
this._trackTokens(response.usageMetadata);
|
|
31
|
+
return response.text;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async generateJSON(prompt, agentInstructions = null) {
|
|
35
|
+
if (!this._client) {
|
|
36
|
+
this._client = this._createClient();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const fullPrompt = agentInstructions ? `${agentInstructions}\n\n${prompt}` : prompt;
|
|
40
|
+
|
|
41
|
+
// Use model-specific maximum tokens
|
|
42
|
+
const maxTokens = getMaxTokensForModel(this.model);
|
|
43
|
+
|
|
44
|
+
const params = {
|
|
45
|
+
model: this.model,
|
|
46
|
+
contents: fullPrompt,
|
|
47
|
+
generationConfig: {
|
|
48
|
+
responseMimeType: 'application/json', // Gemini's native JSON mode
|
|
49
|
+
maxOutputTokens: maxTokens
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const response = await this._withRetry(
|
|
54
|
+
() => this._client.models.generateContent(params),
|
|
55
|
+
'JSON generation (Gemini)'
|
|
56
|
+
);
|
|
57
|
+
if (!response.text) {
|
|
58
|
+
throw new Error('Gemini returned no text (possible safety filter block).');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this._trackTokens(response.usageMetadata);
|
|
62
|
+
const content = response.text;
|
|
63
|
+
|
|
64
|
+
// Strip markdown code fences if present (more robust)
|
|
65
|
+
let jsonStr = content.trim();
|
|
66
|
+
if (jsonStr.startsWith('```')) {
|
|
67
|
+
// Remove opening fence (```json or ```)
|
|
68
|
+
jsonStr = jsonStr.replace(/^```(?:json)?\s*\n?/, '');
|
|
69
|
+
// Remove closing fence
|
|
70
|
+
jsonStr = jsonStr.replace(/\n?\s*```\s*$/, '');
|
|
71
|
+
jsonStr = jsonStr.trim();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(jsonStr);
|
|
76
|
+
} catch (firstError) {
|
|
77
|
+
if (jsonStr.startsWith('{') || jsonStr.startsWith('[')) {
|
|
78
|
+
try {
|
|
79
|
+
return JSON.parse(jsonrepair(jsonStr));
|
|
80
|
+
} catch { /* fall through to throw */ }
|
|
81
|
+
}
|
|
82
|
+
throw new Error(`Failed to parse JSON response: ${firstError.message}\n\nResponse was:\n${content}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async generateText(prompt, agentInstructions = null) {
|
|
87
|
+
if (!this._client) {
|
|
88
|
+
this._client = this._createClient();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const fullPrompt = agentInstructions ? `${agentInstructions}\n\n${prompt}` : prompt;
|
|
92
|
+
|
|
93
|
+
// Use model-specific maximum tokens
|
|
94
|
+
const maxTokens = getMaxTokensForModel(this.model);
|
|
95
|
+
|
|
96
|
+
const params = {
|
|
97
|
+
model: this.model,
|
|
98
|
+
contents: fullPrompt,
|
|
99
|
+
generationConfig: {
|
|
100
|
+
maxOutputTokens: maxTokens
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const response = await this._withRetry(
|
|
105
|
+
() => this._client.models.generateContent(params),
|
|
106
|
+
'Text generation (Gemini)'
|
|
107
|
+
);
|
|
108
|
+
if (!response.text) {
|
|
109
|
+
throw new Error('Gemini returned no text (possible safety filter block).');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this._trackTokens(response.usageMetadata);
|
|
113
|
+
return response.text;
|
|
114
|
+
}
|
|
115
|
+
}
|
package/cli/llm-mock.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MockLLMProvider — instant canned responses for E2E testing.
|
|
3
|
+
* Activated when AVC_LLM_MOCK=1 is set in the environment.
|
|
4
|
+
*
|
|
5
|
+
* Detects what kind of response to return by inspecting the prompt text.
|
|
6
|
+
*/
|
|
7
|
+
export class MockLLMProvider {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.providerName = 'mock';
|
|
10
|
+
this.model = 'mock-model';
|
|
11
|
+
this.tokenUsage = { inputTokens: 0, outputTokens: 0, totalCalls: 0 };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async validateApiKey() {
|
|
15
|
+
return { valid: true };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
getTokenUsage() {
|
|
19
|
+
return {
|
|
20
|
+
inputTokens: this.tokenUsage.inputTokens,
|
|
21
|
+
outputTokens: this.tokenUsage.outputTokens,
|
|
22
|
+
totalTokens: this.tokenUsage.inputTokens + this.tokenUsage.outputTokens,
|
|
23
|
+
totalCalls: this.tokenUsage.totalCalls,
|
|
24
|
+
estimatedCost: 0,
|
|
25
|
+
provider: 'mock',
|
|
26
|
+
model: 'mock-model'
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
_track(prompt = '') {
|
|
31
|
+
this.tokenUsage.inputTokens += Math.ceil(prompt.length / 4);
|
|
32
|
+
this.tokenUsage.outputTokens += 50;
|
|
33
|
+
this.tokenUsage.totalCalls++;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** generateJSON — detect call type from agent instructions (most reliable discriminator) */
|
|
37
|
+
async generateJSON(prompt, agentInstructions = null) {
|
|
38
|
+
this._track(prompt);
|
|
39
|
+
|
|
40
|
+
const p = (prompt || '').toLowerCase();
|
|
41
|
+
// Use agent instructions filename/content as the primary discriminator — it's
|
|
42
|
+
// more reliable than prompt text which can contain overlapping keywords.
|
|
43
|
+
const agent = (agentInstructions || '').toLowerCase();
|
|
44
|
+
|
|
45
|
+
// Validation calls (validator-documentation.md / validator-context.md)
|
|
46
|
+
// validator-documentation.md contains "validationStatus" and "overallScore" as output fields
|
|
47
|
+
if (agent.includes('validationstatus') || agent.includes('overallscore') ||
|
|
48
|
+
p.includes('validate the following')) {
|
|
49
|
+
return {
|
|
50
|
+
validationStatus: 'acceptable',
|
|
51
|
+
overallScore: 90,
|
|
52
|
+
issues: [],
|
|
53
|
+
contentIssues: [],
|
|
54
|
+
structuralIssues: [],
|
|
55
|
+
applicationFlowGaps: [],
|
|
56
|
+
strengths: ['Well-structured document (mock validation)'],
|
|
57
|
+
improvementPriorities: [],
|
|
58
|
+
readyForPublication: true,
|
|
59
|
+
readyForUse: true
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Database recommendation (database-recommender.md)
|
|
64
|
+
// database-recommender.md uniquely contains "hasDatabaseNeeds" as an output field
|
|
65
|
+
if (agent.includes('hasdatabaseneeds') || p.includes('determine if it needs a database')) {
|
|
66
|
+
return {
|
|
67
|
+
hasDatabaseNeeds: true,
|
|
68
|
+
comparison: {
|
|
69
|
+
sqlOption: {
|
|
70
|
+
database: 'PostgreSQL',
|
|
71
|
+
specificVersion: 'PostgreSQL 16',
|
|
72
|
+
bestFor: 'Relational data with ACID guarantees',
|
|
73
|
+
strengths: ['Strong consistency', 'Rich query language', 'Mature ecosystem'],
|
|
74
|
+
weaknesses: ['Schema migrations required'],
|
|
75
|
+
estimatedCosts: { monthly: '$0 (local Docker)' }
|
|
76
|
+
},
|
|
77
|
+
nosqlOption: {
|
|
78
|
+
database: 'MongoDB',
|
|
79
|
+
specificVersion: 'MongoDB 7',
|
|
80
|
+
bestFor: 'Flexible document storage',
|
|
81
|
+
strengths: ['Schema flexibility', 'Easy horizontal scaling'],
|
|
82
|
+
weaknesses: ['Eventual consistency by default'],
|
|
83
|
+
estimatedCosts: { monthly: '$0 (local Docker)' }
|
|
84
|
+
},
|
|
85
|
+
keyMetrics: {
|
|
86
|
+
estimatedReadWriteRatio: '70/30',
|
|
87
|
+
expectedThroughput: 'Low-medium (< 1000 req/s)',
|
|
88
|
+
dataComplexity: 'Medium — relational entities with joins'
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
recommendation: 'sql',
|
|
92
|
+
confidence: 85,
|
|
93
|
+
reasoning: 'Mock: task management apps benefit from relational integrity'
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Architecture recommendations (architecture-recommender.md)
|
|
98
|
+
// architecture-recommender.md uniquely contains "requiresCloudProvider" as an output field
|
|
99
|
+
if (agent.includes('requirescloudprovider') || p.includes('recommend 3-5') || p.includes('deployment architectures')) {
|
|
100
|
+
return {
|
|
101
|
+
architectures: [
|
|
102
|
+
{
|
|
103
|
+
name: 'Local Hybrid Stack',
|
|
104
|
+
description: 'Express.js/FastAPI backend on localhost with PostgreSQL in Docker',
|
|
105
|
+
requiresCloudProvider: false,
|
|
106
|
+
bestFor: 'Experienced developers who want fast debugging with database isolation',
|
|
107
|
+
migrationPath: {
|
|
108
|
+
targetCloud: 'AWS ECS / Azure Container Apps / GCP Cloud Run',
|
|
109
|
+
steps: [
|
|
110
|
+
'Containerize backend with Docker',
|
|
111
|
+
'Push images to ECR/ACR/GCR',
|
|
112
|
+
'Deploy to container orchestration service'
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'Full Docker Compose',
|
|
118
|
+
description: 'All services in Docker Compose — database, backend, and frontend',
|
|
119
|
+
requiresCloudProvider: false,
|
|
120
|
+
bestFor: 'Teams who want identical environments across all machines',
|
|
121
|
+
migrationPath: {
|
|
122
|
+
targetCloud: 'AWS ECS / GCP Cloud Run',
|
|
123
|
+
steps: ['Convert docker-compose.yml to ECS task definitions', 'Set up managed database']
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Question prefilling (question-prefiller.md)
|
|
131
|
+
// question-prefiller.md uniquely contains "TARGET_USERS" as an output field
|
|
132
|
+
if (agent.includes('target_users') || p.includes('target_users')) {
|
|
133
|
+
return {
|
|
134
|
+
TARGET_USERS: 'Developers and project teams managing software development tasks',
|
|
135
|
+
DEPLOYMENT_TARGET: 'Local development environment using Docker Compose; ready to migrate to AWS ECS or Azure Container Apps for production',
|
|
136
|
+
TECHNICAL_CONSIDERATIONS: 'Node.js/Express.js or FastAPI backend, React 18 + Vite frontend, PostgreSQL 16 in Docker for local development with production migration path',
|
|
137
|
+
SECURITY_AND_COMPLIANCE_REQUIREMENTS: 'JWT authentication with refresh tokens, bcrypt password hashing, HTTPS in production, standard OWASP security practices'
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Context generation (project-context-generator.md)
|
|
142
|
+
// project-context-generator.md uniquely contains "contextMarkdown" as an output field
|
|
143
|
+
if (agent.includes('contextmarkdown') || agent.includes('context generator')) {
|
|
144
|
+
const mockContext = `# Project Context
|
|
145
|
+
|
|
146
|
+
**Mission:** Build a test task manager app
|
|
147
|
+
**Architecture:** Local Hybrid Stack
|
|
148
|
+
**Database:** PostgreSQL 16
|
|
149
|
+
**Tech Stack:** Node.js, Express.js, React 18, Vite
|
|
150
|
+
**Deployment:** Local Docker Compose → AWS ECS
|
|
151
|
+
`;
|
|
152
|
+
return {
|
|
153
|
+
contextMarkdown: mockContext,
|
|
154
|
+
tokenCount: Math.ceil(mockContext.length / 4),
|
|
155
|
+
withinBudget: true
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Generic fallback
|
|
160
|
+
return { result: 'Mock JSON response', success: true };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** generate — return a mock sponsor-call document */
|
|
164
|
+
async generate(prompt, maxTokens = 256, systemInstructions = null) {
|
|
165
|
+
this._track(prompt);
|
|
166
|
+
|
|
167
|
+
const p = (prompt || '').toLowerCase();
|
|
168
|
+
|
|
169
|
+
// Document generation
|
|
170
|
+
if (p.includes('sponsor') || p.includes('project brief') || p.includes('enhance')) {
|
|
171
|
+
return `# Sponsor Call — Test Task Manager
|
|
172
|
+
|
|
173
|
+
## Mission Statement
|
|
174
|
+
Build a test task manager app to help teams manage development tasks efficiently.
|
|
175
|
+
|
|
176
|
+
## Initial Scope & Key Features
|
|
177
|
+
MVP with task creation and basic authentication.
|
|
178
|
+
|
|
179
|
+
## Target Users
|
|
180
|
+
Developers and project teams managing software development tasks.
|
|
181
|
+
|
|
182
|
+
## Deployment Target
|
|
183
|
+
Local development environment with Docker Compose. Ready to migrate to AWS ECS when needed.
|
|
184
|
+
|
|
185
|
+
## Technical Considerations
|
|
186
|
+
Node.js/Express.js backend, React 18 + Vite frontend, PostgreSQL 16 in Docker.
|
|
187
|
+
|
|
188
|
+
## Security & Compliance
|
|
189
|
+
JWT authentication with refresh tokens, bcrypt password hashing, HTTPS in production.
|
|
190
|
+
|
|
191
|
+
## Architecture
|
|
192
|
+
Local Hybrid Stack: backend on localhost, database in Docker for isolation.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
*Generated by AVC mock provider for E2E testing*
|
|
196
|
+
`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Improvement pass (iterative validation improve step)
|
|
200
|
+
if (p.includes('improve') || p.includes('enhancement')) {
|
|
201
|
+
return `# Sponsor Call — Test Task Manager (Improved)
|
|
202
|
+
|
|
203
|
+
## Mission Statement
|
|
204
|
+
Build a comprehensive test task manager app for development teams.
|
|
205
|
+
|
|
206
|
+
## Initial Scope & Key Features
|
|
207
|
+
MVP with task creation, assignment, and basic JWT authentication.
|
|
208
|
+
|
|
209
|
+
## Target Users
|
|
210
|
+
Software development teams and individual developers.
|
|
211
|
+
|
|
212
|
+
## Deployment Target
|
|
213
|
+
Local development with Docker Compose, production on AWS ECS.
|
|
214
|
+
|
|
215
|
+
## Technical Considerations
|
|
216
|
+
Express.js/Node.js backend, React 18 frontend, PostgreSQL 16 in Docker container.
|
|
217
|
+
|
|
218
|
+
## Security & Compliance
|
|
219
|
+
JWT + bcrypt authentication, OWASP security practices.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
*Improved by AVC mock provider for E2E testing*
|
|
223
|
+
`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return 'Mock text response from AVC E2E mock provider.';
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** generateText — alias for generate (used by migration guide generator) */
|
|
230
|
+
async generateText(prompt, agentInstructions = null) {
|
|
231
|
+
return this.generate(prompt, 4096, agentInstructions);
|
|
232
|
+
}
|
|
233
|
+
}
|