@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,150 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { useCeremonyStore } from '../../../store/ceremonyStore';
|
|
3
|
+
import { AskArchPopup } from '../AskArchPopup';
|
|
4
|
+
|
|
5
|
+
const COST_TIER_COLOR = {
|
|
6
|
+
'Free': 'text-green-600',
|
|
7
|
+
'$': 'text-green-600',
|
|
8
|
+
'$$': 'text-amber-500',
|
|
9
|
+
'$$$': 'text-orange-500',
|
|
10
|
+
'$$$$': 'text-red-500',
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function ArchCard({ arch, selected, onSelect }) {
|
|
14
|
+
return (
|
|
15
|
+
<button
|
|
16
|
+
onClick={() => onSelect(arch)}
|
|
17
|
+
className={`text-left rounded-xl border-2 p-4 w-full transition-all ${
|
|
18
|
+
selected
|
|
19
|
+
? 'border-indigo-500 bg-indigo-50'
|
|
20
|
+
: 'border-slate-200 hover:border-slate-300 bg-white'
|
|
21
|
+
}`}
|
|
22
|
+
>
|
|
23
|
+
<div className="flex items-start justify-between gap-2 mb-2">
|
|
24
|
+
<span className="font-semibold text-slate-900 text-sm">{arch.name}</span>
|
|
25
|
+
{arch.requiresCloudProvider ? (
|
|
26
|
+
<span className="text-xs bg-purple-100 text-purple-700 px-2 py-0.5 rounded-full whitespace-nowrap">
|
|
27
|
+
Cloud
|
|
28
|
+
</span>
|
|
29
|
+
) : (
|
|
30
|
+
<span className="text-xs bg-green-100 text-green-700 px-2 py-0.5 rounded-full whitespace-nowrap">
|
|
31
|
+
Local
|
|
32
|
+
</span>
|
|
33
|
+
)}
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<p className="text-xs text-slate-600 mb-2 line-clamp-3">{arch.description}</p>
|
|
37
|
+
|
|
38
|
+
{arch.bestFor && (
|
|
39
|
+
<p className="text-xs text-slate-500 italic">Best for: {arch.bestFor}</p>
|
|
40
|
+
)}
|
|
41
|
+
|
|
42
|
+
{arch.costTier && (
|
|
43
|
+
<p className={`mt-2 text-sm font-semibold ${COST_TIER_COLOR[arch.costTier] ?? 'text-slate-500'}`}>
|
|
44
|
+
{arch.costTier}
|
|
45
|
+
</p>
|
|
46
|
+
)}
|
|
47
|
+
|
|
48
|
+
{arch.migrationPath && (
|
|
49
|
+
<div className="mt-2 text-xs text-slate-400">
|
|
50
|
+
Migration to cloud: {arch.migrationPath.estimatedMigrationEffort} (
|
|
51
|
+
{arch.migrationPath.migrationComplexity})
|
|
52
|
+
</div>
|
|
53
|
+
)}
|
|
54
|
+
</button>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function ArchitectureStep({ onNext, onBack, analyzing, onOpenSettings }) {
|
|
59
|
+
const { archOptions, selectedArch, setSelectedArch, setArchOptions } = useCeremonyStore();
|
|
60
|
+
const [showAskArch, setShowAskArch] = useState(false);
|
|
61
|
+
|
|
62
|
+
if (!archOptions || archOptions.length === 0) {
|
|
63
|
+
return (
|
|
64
|
+
<div className="flex items-center justify-center py-16">
|
|
65
|
+
<div className="text-center text-slate-400">
|
|
66
|
+
<p>Loading architecture recommendations...</p>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<div className="space-y-6">
|
|
74
|
+
<div>
|
|
75
|
+
<div className="flex items-center justify-between">
|
|
76
|
+
<h2 className="text-xl font-semibold text-slate-900">Architecture Selection</h2>
|
|
77
|
+
<button
|
|
78
|
+
type="button"
|
|
79
|
+
onClick={() => setShowAskArch(true)}
|
|
80
|
+
disabled={analyzing}
|
|
81
|
+
className="text-xs text-blue-600 hover:text-blue-700 flex items-center gap-1 transition-colors disabled:opacity-40"
|
|
82
|
+
>
|
|
83
|
+
✨ Ask a Model
|
|
84
|
+
</button>
|
|
85
|
+
</div>
|
|
86
|
+
<p className="text-sm text-slate-500 mt-1">
|
|
87
|
+
Choose the deployment architecture that fits your project best.
|
|
88
|
+
</p>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
92
|
+
{archOptions.map((arch, i) => (
|
|
93
|
+
<ArchCard
|
|
94
|
+
key={i}
|
|
95
|
+
arch={arch}
|
|
96
|
+
selected={selectedArch === arch}
|
|
97
|
+
onSelect={setSelectedArch}
|
|
98
|
+
/>
|
|
99
|
+
))}
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
<div className="flex items-center justify-between pt-2">
|
|
103
|
+
<button
|
|
104
|
+
type="button"
|
|
105
|
+
onClick={onBack}
|
|
106
|
+
disabled={analyzing}
|
|
107
|
+
className="text-sm text-slate-400 hover:text-slate-600 disabled:opacity-40 transition-colors"
|
|
108
|
+
>
|
|
109
|
+
← Back
|
|
110
|
+
</button>
|
|
111
|
+
<div className="flex items-center gap-3">
|
|
112
|
+
<button
|
|
113
|
+
type="button"
|
|
114
|
+
onClick={onNext}
|
|
115
|
+
disabled={analyzing}
|
|
116
|
+
className="text-sm text-slate-400 hover:text-slate-600 disabled:opacity-40 transition-colors"
|
|
117
|
+
>
|
|
118
|
+
Skip — let the model decide
|
|
119
|
+
</button>
|
|
120
|
+
<button
|
|
121
|
+
onClick={onNext}
|
|
122
|
+
disabled={!selectedArch || analyzing}
|
|
123
|
+
className="px-5 py-2 bg-slate-900 text-white text-sm font-medium rounded-lg disabled:opacity-40 hover:bg-slate-700 transition-colors flex items-center gap-2"
|
|
124
|
+
>
|
|
125
|
+
{analyzing ? (
|
|
126
|
+
<>
|
|
127
|
+
<span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
|
128
|
+
Preparing requirements…
|
|
129
|
+
</>
|
|
130
|
+
) : (
|
|
131
|
+
'Continue'
|
|
132
|
+
)}
|
|
133
|
+
</button>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
{showAskArch && (
|
|
138
|
+
<AskArchPopup
|
|
139
|
+
onUse={(arch) => {
|
|
140
|
+
setArchOptions([...archOptions, arch]);
|
|
141
|
+
setSelectedArch(arch);
|
|
142
|
+
setShowAskArch(false);
|
|
143
|
+
}}
|
|
144
|
+
onClose={() => setShowAskArch(false)}
|
|
145
|
+
onOpenSettings={onOpenSettings}
|
|
146
|
+
/>
|
|
147
|
+
)}
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { useCeremonyStore } from '../../../store/ceremonyStore';
|
|
2
|
+
|
|
3
|
+
function IssueTag({ severity }) {
|
|
4
|
+
const cls =
|
|
5
|
+
severity === 'critical' ? 'bg-red-100 text-red-700' :
|
|
6
|
+
severity === 'major' ? 'bg-amber-100 text-amber-700' :
|
|
7
|
+
'bg-slate-100 text-slate-500';
|
|
8
|
+
return (
|
|
9
|
+
<span className={`flex-shrink-0 px-1.5 py-0.5 rounded text-[10px] font-semibold uppercase ${cls}`}>
|
|
10
|
+
{severity}
|
|
11
|
+
</span>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function FileTag({ path }) {
|
|
16
|
+
const short = path?.split('/').slice(-2).join('/') || path;
|
|
17
|
+
return (
|
|
18
|
+
<span className="inline-flex items-center gap-1 bg-green-50 border border-green-200 text-green-700 text-xs px-2 py-1 rounded-md font-mono">
|
|
19
|
+
<span>📄</span>
|
|
20
|
+
{short}
|
|
21
|
+
</span>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function Stat({ label, value }) {
|
|
26
|
+
return (
|
|
27
|
+
<div className="bg-slate-50 rounded-lg border border-slate-200 p-3 text-center">
|
|
28
|
+
<div className="text-lg font-bold text-slate-900">{value}</div>
|
|
29
|
+
<div className="text-xs text-slate-500">{label}</div>
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function CompleteStep({ onClose }) {
|
|
35
|
+
const { ceremonyResult } = useCeremonyStore();
|
|
36
|
+
const r = ceremonyResult || {};
|
|
37
|
+
|
|
38
|
+
const tokenInput = r.tokenUsage?.input || r.tokenUsage?.inputTokens || 0;
|
|
39
|
+
const tokenOutput = r.tokenUsage?.output || r.tokenUsage?.outputTokens || 0;
|
|
40
|
+
const tokenTotal = r.tokenUsage?.total || r.tokenUsage?.totalTokens || tokenInput + tokenOutput;
|
|
41
|
+
const files = r.outputPath && r.contextPath
|
|
42
|
+
? [r.outputPath, r.contextPath]
|
|
43
|
+
: r.filesGenerated || [];
|
|
44
|
+
|
|
45
|
+
// Only show real validation issues; never show the example preview
|
|
46
|
+
const rawIssues = Array.isArray(r.validationIssues) ? r.validationIssues : [];
|
|
47
|
+
|
|
48
|
+
// Group duplicate rule applications by ruleId
|
|
49
|
+
const issueMap = {};
|
|
50
|
+
for (const issue of rawIssues) {
|
|
51
|
+
const key = issue.ruleId || issue.name;
|
|
52
|
+
if (!issueMap[key]) {
|
|
53
|
+
issueMap[key] = { ...issue, count: 1, iterations: issue.iteration ? [issue.iteration] : [] };
|
|
54
|
+
} else {
|
|
55
|
+
issueMap[key].count++;
|
|
56
|
+
if (issue.iteration && !issueMap[key].iterations.includes(issue.iteration)) {
|
|
57
|
+
issueMap[key].iterations.push(issue.iteration);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const issues = Object.values(issueMap);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className="space-y-6">
|
|
65
|
+
<div className="text-center">
|
|
66
|
+
<h2 className="text-xl font-semibold text-slate-900">Documentation Generated!</h2>
|
|
67
|
+
<p className="text-sm text-slate-500 mt-1">
|
|
68
|
+
Your project documentation is ready. The kanban board will refresh automatically.
|
|
69
|
+
</p>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
{files.length > 0 && (
|
|
73
|
+
<div>
|
|
74
|
+
<p className="text-xs font-medium text-slate-500 mb-2">Files created</p>
|
|
75
|
+
<div className="flex flex-wrap gap-2">
|
|
76
|
+
{files.map((f, i) => (
|
|
77
|
+
<FileTag key={i} path={f} />
|
|
78
|
+
))}
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
)}
|
|
82
|
+
|
|
83
|
+
<div className="grid grid-cols-2 gap-3">
|
|
84
|
+
<Stat label="Input tokens" value={tokenInput.toLocaleString()} />
|
|
85
|
+
<Stat label="Output tokens" value={tokenOutput.toLocaleString()} />
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
{r.model && (
|
|
89
|
+
<p className="text-xs text-center text-slate-400">
|
|
90
|
+
Model: <span className="font-mono">{r.model}</span>
|
|
91
|
+
</p>
|
|
92
|
+
)}
|
|
93
|
+
|
|
94
|
+
{issues.length > 0 && (
|
|
95
|
+
<div>
|
|
96
|
+
<p className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2">
|
|
97
|
+
Quality fixes applied
|
|
98
|
+
</p>
|
|
99
|
+
<div className="space-y-1.5">
|
|
100
|
+
{issues.map((issue, i) => (
|
|
101
|
+
<div key={i} className="bg-amber-50 border border-amber-100 rounded-md px-3 py-2 text-xs">
|
|
102
|
+
<div className="flex items-center gap-2">
|
|
103
|
+
<IssueTag severity={issue.severity} />
|
|
104
|
+
<span className="text-slate-400 flex-shrink-0">{issue.stage}</span>
|
|
105
|
+
<span className="text-slate-700 font-medium">{issue.name}</span>
|
|
106
|
+
{issue.count > 1 && (
|
|
107
|
+
<span className="ml-auto flex-shrink-0 bg-amber-200 text-amber-800 text-[10px] font-bold px-1.5 py-0.5 rounded-full">
|
|
108
|
+
×{issue.count}
|
|
109
|
+
</span>
|
|
110
|
+
)}
|
|
111
|
+
{issue.iterations?.length > 0 && (
|
|
112
|
+
<span className="flex-shrink-0 text-slate-400">
|
|
113
|
+
iter {issue.iterations.sort((a, b) => a - b).join(', ')}
|
|
114
|
+
</span>
|
|
115
|
+
)}
|
|
116
|
+
</div>
|
|
117
|
+
{issue.description && (
|
|
118
|
+
<p className="mt-1 text-slate-500 pl-1">{issue.description}</p>
|
|
119
|
+
)}
|
|
120
|
+
</div>
|
|
121
|
+
))}
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
125
|
+
|
|
126
|
+
<div className="flex justify-center pt-2">
|
|
127
|
+
<button
|
|
128
|
+
onClick={onClose}
|
|
129
|
+
className="px-6 py-2 bg-slate-900 text-white text-sm font-medium rounded-lg hover:bg-slate-700 transition-colors"
|
|
130
|
+
>
|
|
131
|
+
Close
|
|
132
|
+
</button>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { Info, BarChart2 } from 'lucide-react';
|
|
2
|
+
import { useCeremonyStore } from '../../../store/ceremonyStore';
|
|
3
|
+
|
|
4
|
+
/** Convert a monetary string like "$150-280/month" into a relative $ tier. */
|
|
5
|
+
function costTier(monthly) {
|
|
6
|
+
if (!monthly) return null;
|
|
7
|
+
const str = String(monthly).toLowerCase();
|
|
8
|
+
const nums = (str.match(/\d+/g) || []).map(Number).filter((n) => n > 0);
|
|
9
|
+
const avg = nums.length ? nums.reduce((a, b) => a + b, 0) / nums.length : 0;
|
|
10
|
+
if (avg === 0) return { symbols: 'Free', color: 'text-green-600' };
|
|
11
|
+
if (avg <= 50) return { symbols: '$', color: 'text-green-600' };
|
|
12
|
+
if (avg <= 150) return { symbols: '$$', color: 'text-amber-500' };
|
|
13
|
+
if (avg <= 350) return { symbols: '$$$', color: 'text-orange-500' };
|
|
14
|
+
return { symbols: '$$$$', color: 'text-red-500' };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function DbOptionCard({ option, type, selected, onSelect }) {
|
|
18
|
+
const label = type === 'sql' ? 'SQL' : 'NoSQL';
|
|
19
|
+
const color = type === 'sql' ? 'blue' : 'emerald';
|
|
20
|
+
|
|
21
|
+
const colorMap = {
|
|
22
|
+
blue: {
|
|
23
|
+
badge: 'bg-blue-100 text-blue-700',
|
|
24
|
+
border: 'border-blue-500',
|
|
25
|
+
bg: 'bg-blue-50',
|
|
26
|
+
btn: 'ring-blue-500',
|
|
27
|
+
},
|
|
28
|
+
emerald: {
|
|
29
|
+
badge: 'bg-emerald-100 text-emerald-700',
|
|
30
|
+
border: 'border-emerald-500',
|
|
31
|
+
bg: 'bg-emerald-50',
|
|
32
|
+
btn: 'ring-emerald-500',
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
const c = colorMap[color];
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<button
|
|
39
|
+
onClick={() => onSelect(type)}
|
|
40
|
+
className={`text-left rounded-xl border-2 p-5 w-full transition-all ${
|
|
41
|
+
selected ? `${c.border} ${c.bg}` : 'border-slate-200 hover:border-slate-300 bg-white'
|
|
42
|
+
}`}
|
|
43
|
+
>
|
|
44
|
+
<div className="flex items-center gap-2 mb-3">
|
|
45
|
+
<span className={`text-xs font-semibold px-2 py-0.5 rounded-full ${c.badge}`}>{label}</span>
|
|
46
|
+
<span className="font-semibold text-slate-900">{option.database}</span>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
{option.specificVersion && (
|
|
50
|
+
<p className="text-xs text-slate-500 mb-2">Version: {option.specificVersion}</p>
|
|
51
|
+
)}
|
|
52
|
+
|
|
53
|
+
{option.estimatedCosts && (() => {
|
|
54
|
+
const tier = costTier(option.estimatedCosts.monthly);
|
|
55
|
+
return tier ? (
|
|
56
|
+
<p className={`text-sm font-semibold mb-2 ${tier.color}`}>{tier.symbols}</p>
|
|
57
|
+
) : null;
|
|
58
|
+
})()}
|
|
59
|
+
|
|
60
|
+
{option.keyStrengths && option.keyStrengths.length > 0 && (
|
|
61
|
+
<div className="mt-2">
|
|
62
|
+
<p className="text-xs font-medium text-slate-500 mb-1">Strengths</p>
|
|
63
|
+
<ul className="space-y-0.5">
|
|
64
|
+
{option.keyStrengths.slice(0, 3).map((s, i) => (
|
|
65
|
+
<li key={i} className="text-xs text-slate-600 flex items-start gap-1.5">
|
|
66
|
+
<span className="mt-1 w-1 h-1 rounded-full bg-slate-400 flex-shrink-0" />
|
|
67
|
+
{s}
|
|
68
|
+
</li>
|
|
69
|
+
))}
|
|
70
|
+
</ul>
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
73
|
+
|
|
74
|
+
{option.tradeoffs && (
|
|
75
|
+
<p className="text-xs text-slate-400 mt-2 italic">{option.tradeoffs}</p>
|
|
76
|
+
)}
|
|
77
|
+
</button>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function DatabaseStep({ onNext, onBack, analyzing }) {
|
|
82
|
+
const { dbResult, dbChoice, setDbChoice } = useCeremonyStore();
|
|
83
|
+
|
|
84
|
+
if (!dbResult) {
|
|
85
|
+
return (
|
|
86
|
+
<div className="flex items-center justify-center py-16">
|
|
87
|
+
<div className="text-center text-slate-400">
|
|
88
|
+
<p>Loading database analysis...</p>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const { comparison, rationale } = dbResult;
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className="space-y-6">
|
|
98
|
+
<div>
|
|
99
|
+
<h2 className="text-xl font-semibold text-slate-900">Database Choice</h2>
|
|
100
|
+
<p className="text-sm text-slate-500 mt-1">
|
|
101
|
+
Choose between SQL and NoSQL based on your project's data needs.
|
|
102
|
+
</p>
|
|
103
|
+
{rationale && (
|
|
104
|
+
<p className="text-sm text-slate-600 mt-2 bg-slate-50 border border-slate-200 rounded-lg px-3 py-2">
|
|
105
|
+
{rationale}
|
|
106
|
+
</p>
|
|
107
|
+
)}
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
{comparison && (
|
|
111
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
112
|
+
<DbOptionCard
|
|
113
|
+
option={comparison.sqlOption}
|
|
114
|
+
type="sql"
|
|
115
|
+
selected={dbChoice === 'sql'}
|
|
116
|
+
onSelect={setDbChoice}
|
|
117
|
+
/>
|
|
118
|
+
<DbOptionCard
|
|
119
|
+
option={comparison.nosqlOption}
|
|
120
|
+
type="nosql"
|
|
121
|
+
selected={dbChoice === 'nosql'}
|
|
122
|
+
onSelect={setDbChoice}
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
)}
|
|
126
|
+
|
|
127
|
+
{dbResult.keyMetrics && (
|
|
128
|
+
<div className="rounded-xl border border-slate-200 bg-slate-50 overflow-hidden">
|
|
129
|
+
<div className="flex items-center gap-1.5 px-4 py-2 border-b border-slate-200 bg-white">
|
|
130
|
+
<BarChart2 className="w-3.5 h-3.5 text-slate-400" />
|
|
131
|
+
<span className="text-xs font-semibold text-slate-500">Project metrics analysed</span>
|
|
132
|
+
</div>
|
|
133
|
+
<div className="flex divide-x divide-slate-200">
|
|
134
|
+
{dbResult.keyMetrics.estimatedReadWriteRatio && (
|
|
135
|
+
<div className="flex-1 px-4 py-3">
|
|
136
|
+
<p className="text-[10px] font-medium text-slate-400 uppercase tracking-wide mb-1">Read / Write ratio</p>
|
|
137
|
+
<p className="text-sm font-semibold text-slate-700">{dbResult.keyMetrics.estimatedReadWriteRatio}</p>
|
|
138
|
+
</div>
|
|
139
|
+
)}
|
|
140
|
+
{dbResult.keyMetrics.expectedThroughput && (
|
|
141
|
+
<div className="flex-1 px-4 py-3">
|
|
142
|
+
<p className="text-[10px] font-medium text-slate-400 uppercase tracking-wide mb-1">Expected throughput</p>
|
|
143
|
+
<p className="text-sm font-semibold text-slate-700">{dbResult.keyMetrics.expectedThroughput}</p>
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
146
|
+
{dbResult.keyMetrics.dataComplexity && (
|
|
147
|
+
<div className="flex-1 px-4 py-3">
|
|
148
|
+
<p className="text-[10px] font-medium text-slate-400 uppercase tracking-wide mb-1">Data complexity</p>
|
|
149
|
+
<p className="text-sm font-semibold text-slate-700">{dbResult.keyMetrics.dataComplexity}</p>
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
)}
|
|
155
|
+
|
|
156
|
+
<div className="flex items-start gap-2 px-1 py-2 text-xs text-slate-400">
|
|
157
|
+
<Info className="w-3.5 h-3.5 flex-shrink-0 mt-0.5 text-slate-300" />
|
|
158
|
+
<p>
|
|
159
|
+
The SQL and NoSQL candidates above are AI-suggested based on your mission and scope —
|
|
160
|
+
specific technology (e.g. PostgreSQL vs MySQL, MongoDB vs DynamoDB) and configuration
|
|
161
|
+
will be refined during architecture analysis. Skipping lets the model choose the best fit autonomously,
|
|
162
|
+
which may include a vector database, a time-series store, or no database at all.
|
|
163
|
+
</p>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<div className="flex items-center justify-between pt-2">
|
|
167
|
+
<button
|
|
168
|
+
type="button"
|
|
169
|
+
onClick={onBack}
|
|
170
|
+
disabled={analyzing}
|
|
171
|
+
className="text-sm text-slate-400 hover:text-slate-600 disabled:opacity-40 transition-colors"
|
|
172
|
+
>
|
|
173
|
+
← Back
|
|
174
|
+
</button>
|
|
175
|
+
<div className="flex items-center gap-3">
|
|
176
|
+
<button
|
|
177
|
+
type="button"
|
|
178
|
+
onClick={onNext}
|
|
179
|
+
disabled={analyzing}
|
|
180
|
+
className="text-sm text-slate-400 hover:text-slate-600 disabled:opacity-40 transition-colors"
|
|
181
|
+
>
|
|
182
|
+
Skip — let the model decide
|
|
183
|
+
</button>
|
|
184
|
+
<button
|
|
185
|
+
onClick={onNext}
|
|
186
|
+
disabled={!dbChoice || analyzing}
|
|
187
|
+
className="px-5 py-2 bg-slate-900 text-white text-sm font-medium rounded-lg disabled:opacity-40 hover:bg-slate-700 transition-colors flex items-center gap-2"
|
|
188
|
+
>
|
|
189
|
+
{analyzing ? (
|
|
190
|
+
<>
|
|
191
|
+
<span className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
|
192
|
+
Analyzing...
|
|
193
|
+
</>
|
|
194
|
+
) : (
|
|
195
|
+
'Continue'
|
|
196
|
+
)}
|
|
197
|
+
</button>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Info } from 'lucide-react';
|
|
2
|
+
import { useCeremonyStore } from '../../../store/ceremonyStore';
|
|
3
|
+
|
|
4
|
+
const strategies = [
|
|
5
|
+
{
|
|
6
|
+
id: 'local-mvp',
|
|
7
|
+
icon: '💻',
|
|
8
|
+
title: 'Local MVP First',
|
|
9
|
+
subtitle: 'Start on your machine, migrate later',
|
|
10
|
+
bullets: [
|
|
11
|
+
'Zero cloud costs during development',
|
|
12
|
+
'Fast iteration — no deployment delays',
|
|
13
|
+
'SQLite or local PostgreSQL/MongoDB',
|
|
14
|
+
'Docker Compose for production parity',
|
|
15
|
+
],
|
|
16
|
+
color: 'blue',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: 'cloud',
|
|
20
|
+
icon: '☁️',
|
|
21
|
+
title: 'Cloud Deployment',
|
|
22
|
+
subtitle: 'Production-ready from day one',
|
|
23
|
+
bullets: [
|
|
24
|
+
'Managed infrastructure (AWS / Azure / GCP)',
|
|
25
|
+
'Auto-scaling and high availability',
|
|
26
|
+
'Managed databases with backups',
|
|
27
|
+
'CI/CD pipeline recommendations',
|
|
28
|
+
'Monthly cost estimates included',
|
|
29
|
+
],
|
|
30
|
+
color: 'purple',
|
|
31
|
+
},
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
export function DeploymentStep({ onNext }) {
|
|
35
|
+
const { strategy, setStrategy } = useCeremonyStore();
|
|
36
|
+
|
|
37
|
+
const colorMap = {
|
|
38
|
+
blue: {
|
|
39
|
+
border: 'border-blue-500',
|
|
40
|
+
bg: 'bg-blue-50',
|
|
41
|
+
icon: 'text-blue-600',
|
|
42
|
+
bullet: 'bg-blue-500',
|
|
43
|
+
btn: 'bg-blue-600 hover:bg-blue-700',
|
|
44
|
+
},
|
|
45
|
+
purple: {
|
|
46
|
+
border: 'border-purple-500',
|
|
47
|
+
bg: 'bg-purple-50',
|
|
48
|
+
icon: 'text-purple-600',
|
|
49
|
+
bullet: 'bg-purple-500',
|
|
50
|
+
btn: 'bg-purple-600 hover:bg-purple-700',
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handleSelect = (id) => {
|
|
55
|
+
setStrategy(id);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div className="space-y-6">
|
|
60
|
+
<div>
|
|
61
|
+
<h2 className="text-xl font-semibold text-slate-900">Deployment Strategy</h2>
|
|
62
|
+
<p className="text-sm text-slate-500 mt-1">
|
|
63
|
+
Choose how you want to deploy your project. This shapes architecture and technology recommendations.
|
|
64
|
+
</p>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
68
|
+
{strategies.map((s) => {
|
|
69
|
+
const c = colorMap[s.color];
|
|
70
|
+
const selected = strategy === s.id;
|
|
71
|
+
return (
|
|
72
|
+
<button
|
|
73
|
+
key={s.id}
|
|
74
|
+
onClick={() => handleSelect(s.id)}
|
|
75
|
+
className={`text-left rounded-xl border-2 p-5 transition-all ${
|
|
76
|
+
selected ? `${c.border} ${c.bg}` : 'border-slate-200 hover:border-slate-300 bg-white'
|
|
77
|
+
}`}
|
|
78
|
+
>
|
|
79
|
+
<div className={`text-3xl mb-3 ${c.icon}`}>{s.icon}</div>
|
|
80
|
+
<div className="font-semibold text-slate-900 text-base">{s.title}</div>
|
|
81
|
+
<div className="text-xs text-slate-500 mb-3">{s.subtitle}</div>
|
|
82
|
+
<ul className="space-y-1">
|
|
83
|
+
{s.bullets.map((b, i) => (
|
|
84
|
+
<li key={i} className="flex items-start gap-2 text-xs text-slate-600">
|
|
85
|
+
<span className={`mt-1.5 w-1.5 h-1.5 rounded-full flex-shrink-0 ${c.bullet}`} />
|
|
86
|
+
{b}
|
|
87
|
+
</li>
|
|
88
|
+
))}
|
|
89
|
+
</ul>
|
|
90
|
+
</button>
|
|
91
|
+
);
|
|
92
|
+
})}
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div className="flex items-start gap-2 px-1 py-2 text-xs text-slate-400">
|
|
96
|
+
<Info className="w-3.5 h-3.5 flex-shrink-0 mt-0.5 text-slate-300" />
|
|
97
|
+
<p>
|
|
98
|
+
Technologies listed (e.g. SQLite, MongoDB) are illustrative examples.
|
|
99
|
+
The ceremony tailors all recommendations to your actual requirements — your project may end up
|
|
100
|
+
using a vector database, a search engine, no database at all, or something else entirely.
|
|
101
|
+
</p>
|
|
102
|
+
</div>
|
|
103
|
+
|
|
104
|
+
<div className="flex items-center justify-end gap-3 pt-2">
|
|
105
|
+
<button
|
|
106
|
+
type="button"
|
|
107
|
+
onClick={onNext}
|
|
108
|
+
className="text-sm text-slate-400 hover:text-slate-600 transition-colors"
|
|
109
|
+
>
|
|
110
|
+
Skip — let the model decide
|
|
111
|
+
</button>
|
|
112
|
+
<button
|
|
113
|
+
type="button"
|
|
114
|
+
onClick={onNext}
|
|
115
|
+
disabled={!strategy}
|
|
116
|
+
className="px-5 py-2 bg-slate-900 text-white text-sm font-medium rounded-lg disabled:opacity-40 hover:bg-slate-700 transition-colors"
|
|
117
|
+
>
|
|
118
|
+
Continue
|
|
119
|
+
</button>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
}
|