@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,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* llm-xiaomi.js ā Xiaomi MiMo LLM provider.
|
|
3
|
+
*
|
|
4
|
+
* Uses the OpenAI-compatible API at https://api.xiaomimimo.com/v1.
|
|
5
|
+
* Supports: mimo-v2-flash, mimo-v2-pro, mimo-v2-omni.
|
|
6
|
+
*
|
|
7
|
+
* Requires XIAOMI_API_KEY environment variable.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import OpenAI from 'openai';
|
|
11
|
+
import { LLMProvider } from './llm-provider.js';
|
|
12
|
+
|
|
13
|
+
const XIAOMI_BASE_URL = 'https://api.xiaomimimo.com/v1';
|
|
14
|
+
|
|
15
|
+
function cleanResponse(text) {
|
|
16
|
+
if (!text) return '';
|
|
17
|
+
// Strip markdown code fences
|
|
18
|
+
return text.replace(/^```(?:json)?\s*\n?/i, '').replace(/\n?```\s*$/i, '').trim();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function extractJSON(text) {
|
|
22
|
+
// Try to find JSON object or array in the response
|
|
23
|
+
const jsonMatch = text.match(/(\{[\s\S]*\}|\[[\s\S]*\])/);
|
|
24
|
+
return jsonMatch ? jsonMatch[1] : text;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class XiaomiProvider extends LLMProvider {
|
|
28
|
+
constructor(model) {
|
|
29
|
+
super('xiaomi', model || 'mimo-v2-flash');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
_createClient() {
|
|
33
|
+
const apiKey = process.env.XIAOMI_API_KEY;
|
|
34
|
+
if (!apiKey) {
|
|
35
|
+
throw new Error('XIAOMI_API_KEY not set. Add it to your .env file. Get a key at https://platform.xiaomimimo.com/#/console/api-keys');
|
|
36
|
+
}
|
|
37
|
+
return new OpenAI({
|
|
38
|
+
apiKey,
|
|
39
|
+
baseURL: XIAOMI_BASE_URL,
|
|
40
|
+
timeout: 10 * 60_000, // 10 min
|
|
41
|
+
maxRetries: 0,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async _callProvider(prompt, maxTokens, systemInstructions) {
|
|
46
|
+
if (!this._client) this._client = this._createClient();
|
|
47
|
+
|
|
48
|
+
const messages = [];
|
|
49
|
+
if (systemInstructions) {
|
|
50
|
+
messages.push({ role: 'system', content: systemInstructions });
|
|
51
|
+
}
|
|
52
|
+
messages.push({ role: 'user', content: prompt });
|
|
53
|
+
|
|
54
|
+
const response = await this._client.chat.completions.create({
|
|
55
|
+
model: this.model,
|
|
56
|
+
messages,
|
|
57
|
+
max_tokens: maxTokens,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const usage = response.usage;
|
|
61
|
+
this._trackTokens(usage, { prompt, agentInstructions: systemInstructions, response: response.choices?.[0]?.message?.content });
|
|
62
|
+
|
|
63
|
+
return response.choices?.[0]?.message?.content || '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async generateJSON(prompt, agentInstructions = null, cachedContext = null) {
|
|
67
|
+
if (!this._client) this._client = this._createClient();
|
|
68
|
+
|
|
69
|
+
const JSON_SYSTEM = 'You are a helpful assistant that always returns valid JSON. Your response must be a valid JSON object or array, nothing else. Do not include any thinking, reasoning, or explanation ā only the JSON.';
|
|
70
|
+
const systemParts = [JSON_SYSTEM];
|
|
71
|
+
if (agentInstructions) systemParts.push(agentInstructions);
|
|
72
|
+
if (cachedContext) systemParts.push(`---\n\n${cachedContext}`);
|
|
73
|
+
|
|
74
|
+
const messages = [
|
|
75
|
+
{ role: 'system', content: systemParts.join('\n\n') },
|
|
76
|
+
{ role: 'user', content: prompt },
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
const _t0 = Date.now();
|
|
80
|
+
const response = await this._withRetry(
|
|
81
|
+
() => this._client.chat.completions.create({
|
|
82
|
+
model: this.model,
|
|
83
|
+
messages,
|
|
84
|
+
response_format: { type: 'json_object' },
|
|
85
|
+
}),
|
|
86
|
+
'JSON generation (Xiaomi)'
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const content = response.choices?.[0]?.message?.content || '';
|
|
90
|
+
this._trackTokens(response.usage, {
|
|
91
|
+
prompt,
|
|
92
|
+
agentInstructions: agentInstructions ?? null,
|
|
93
|
+
response: content,
|
|
94
|
+
elapsed: Date.now() - _t0,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const jsonStr = extractJSON(cleanResponse(content));
|
|
98
|
+
try {
|
|
99
|
+
return JSON.parse(jsonStr);
|
|
100
|
+
} catch (firstError) {
|
|
101
|
+
// Try jsonrepair as fallback
|
|
102
|
+
try {
|
|
103
|
+
const { jsonrepair } = await import('jsonrepair');
|
|
104
|
+
return JSON.parse(jsonrepair(jsonStr));
|
|
105
|
+
} catch {
|
|
106
|
+
throw new Error(`Failed to parse JSON from Xiaomi (${this.model}): ${firstError.message}\n\nResponse:\n${content.slice(0, 500)}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async generateText(prompt, agentInstructions = null, cachedContext = null) {
|
|
112
|
+
if (!this._client) this._client = this._createClient();
|
|
113
|
+
|
|
114
|
+
const systemParts = [];
|
|
115
|
+
if (agentInstructions) systemParts.push(agentInstructions);
|
|
116
|
+
if (cachedContext) systemParts.push(cachedContext);
|
|
117
|
+
|
|
118
|
+
const messages = [];
|
|
119
|
+
if (systemParts.length > 0) {
|
|
120
|
+
messages.push({ role: 'system', content: systemParts.join('\n\n') });
|
|
121
|
+
}
|
|
122
|
+
messages.push({ role: 'user', content: prompt });
|
|
123
|
+
|
|
124
|
+
const _t0 = Date.now();
|
|
125
|
+
const response = await this._withRetry(
|
|
126
|
+
() => this._client.chat.completions.create({
|
|
127
|
+
model: this.model,
|
|
128
|
+
messages,
|
|
129
|
+
}),
|
|
130
|
+
'Text generation (Xiaomi)'
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const content = response.choices?.[0]?.message?.content || '';
|
|
134
|
+
this._trackTokens(response.usage, {
|
|
135
|
+
prompt,
|
|
136
|
+
agentInstructions: agentInstructions ?? null,
|
|
137
|
+
response: content,
|
|
138
|
+
elapsed: Date.now() - _t0,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return content;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message Constants - Reusable message definitions
|
|
3
|
+
*
|
|
4
|
+
* Centralized message strings to prevent duplication and ensure consistency.
|
|
5
|
+
* Use these constants instead of hardcoded strings throughout the codebase.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Common error messages
|
|
10
|
+
*/
|
|
11
|
+
export const MESSAGES = {
|
|
12
|
+
/**
|
|
13
|
+
* Project not initialized error
|
|
14
|
+
*/
|
|
15
|
+
PROJECT_NOT_INITIALIZED: {
|
|
16
|
+
error: 'Project not initialized',
|
|
17
|
+
help: 'Please run /init first to create the project structure.'
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Ceremony headers with titles and documentation URLs
|
|
22
|
+
*/
|
|
23
|
+
CEREMONY_HEADERS: {
|
|
24
|
+
'sponsor-call': { title: 'Sponsor Call Ceremony' },
|
|
25
|
+
'sprint-planning': { title: 'Sprint Planning Ceremony' },
|
|
26
|
+
'seed': { title: 'Seed Ceremony' },
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Helper function to get full "project not initialized" error message
|
|
32
|
+
* @returns {string} Complete error message with help text
|
|
33
|
+
*/
|
|
34
|
+
export function getProjectNotInitializedMessage() {
|
|
35
|
+
return `${MESSAGES.PROJECT_NOT_INITIALIZED.error}\n\n${MESSAGES.PROJECT_NOT_INITIALIZED.help}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Helper function to get ceremony header
|
|
40
|
+
* @param {string} ceremonyName - Name of ceremony ('sponsor-call', 'sprint-planning', 'seed')
|
|
41
|
+
* @returns {object} Object with title and url properties
|
|
42
|
+
*/
|
|
43
|
+
export function getCeremonyHeader(ceremonyName) {
|
|
44
|
+
const header = MESSAGES.CEREMONY_HEADERS[ceremonyName];
|
|
45
|
+
if (!header) {
|
|
46
|
+
throw new Error(`Unknown ceremony: ${ceremonyName}`);
|
|
47
|
+
}
|
|
48
|
+
return header;
|
|
49
|
+
}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MessageManager - Centralized message handling system
|
|
3
|
+
*
|
|
4
|
+
* Singleton that manages all console output through execution contexts.
|
|
5
|
+
* Prevents ghost messages by tracking command lifecycle and cancelling
|
|
6
|
+
* messages from previous runs.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { MessageType } from './message-types.js';
|
|
10
|
+
import { ExecutionContext } from './execution-context.js';
|
|
11
|
+
import { outputBuffer } from './output-buffer.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* MessageManager singleton class
|
|
15
|
+
*/
|
|
16
|
+
class MessageManager {
|
|
17
|
+
constructor() {
|
|
18
|
+
if (MessageManager.instance) {
|
|
19
|
+
return MessageManager.instance;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.currentContext = null;
|
|
23
|
+
this.contextHistory = [];
|
|
24
|
+
this.maxHistorySize = 10;
|
|
25
|
+
this.executingMessageCallback = null;
|
|
26
|
+
this.executingSubstepCallback = null;
|
|
27
|
+
|
|
28
|
+
MessageManager.instance = this;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Set executing message callback (from React state setter)
|
|
33
|
+
* @param {Function} callback - setExecutingMessage function
|
|
34
|
+
*/
|
|
35
|
+
setExecutingMessageCallback(callback) {
|
|
36
|
+
this.executingMessageCallback = callback;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Set executing substep callback (from React state setter)
|
|
41
|
+
* @param {Function} callback - setExecutingSubstep function
|
|
42
|
+
*/
|
|
43
|
+
setExecutingSubstepCallback(callback) {
|
|
44
|
+
this.executingSubstepCallback = callback;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Start a new execution context for a command
|
|
49
|
+
* @param {string} commandName - Name of the command
|
|
50
|
+
* @returns {ExecutionContext} New execution context
|
|
51
|
+
*/
|
|
52
|
+
startExecution(commandName) {
|
|
53
|
+
// Cancel previous context if active
|
|
54
|
+
if (this.currentContext && this.currentContext.isActive()) {
|
|
55
|
+
this.currentContext.cancel();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Create new context
|
|
59
|
+
this.currentContext = new ExecutionContext(commandName);
|
|
60
|
+
|
|
61
|
+
// Add to history
|
|
62
|
+
this.contextHistory.push(this.currentContext);
|
|
63
|
+
if (this.contextHistory.length > this.maxHistorySize) {
|
|
64
|
+
this.contextHistory.shift();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Insert visual separator between commands in the Static output
|
|
68
|
+
outputBuffer.clear();
|
|
69
|
+
|
|
70
|
+
return this.currentContext;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* End the current execution context
|
|
75
|
+
*/
|
|
76
|
+
endExecution() {
|
|
77
|
+
if (this.currentContext) {
|
|
78
|
+
this.currentContext.complete();
|
|
79
|
+
this.currentContext = null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Clear progress indicators
|
|
83
|
+
if (this.executingMessageCallback) {
|
|
84
|
+
this.executingMessageCallback('');
|
|
85
|
+
}
|
|
86
|
+
if (this.executingSubstepCallback) {
|
|
87
|
+
this.executingSubstepCallback('');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Cancel the current execution context
|
|
93
|
+
*/
|
|
94
|
+
cancelExecution() {
|
|
95
|
+
if (this.currentContext) {
|
|
96
|
+
this.currentContext.cancel();
|
|
97
|
+
this.currentContext = null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Clear progress indicators
|
|
101
|
+
if (this.executingMessageCallback) {
|
|
102
|
+
this.executingMessageCallback('');
|
|
103
|
+
}
|
|
104
|
+
if (this.executingSubstepCallback) {
|
|
105
|
+
this.executingSubstepCallback('');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get current execution context
|
|
111
|
+
* @returns {ExecutionContext|null} Current context or null
|
|
112
|
+
*/
|
|
113
|
+
getCurrentContext() {
|
|
114
|
+
return this.currentContext;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Send a message through the current context
|
|
119
|
+
* @param {string} type - MessageType
|
|
120
|
+
* @param {string} content - Message content
|
|
121
|
+
* @param {Object} options - Additional options
|
|
122
|
+
*/
|
|
123
|
+
send(type, content, options = {}) {
|
|
124
|
+
// Validate message type
|
|
125
|
+
if (!Object.values(MessageType).includes(type)) {
|
|
126
|
+
console.error(`Invalid message type: ${type}`);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Ignore if no active context
|
|
131
|
+
if (!this.currentContext || !this.currentContext.isActive()) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Handle different message types
|
|
136
|
+
switch (type) {
|
|
137
|
+
case MessageType.COMMAND_START:
|
|
138
|
+
this._handleCommandStart(content);
|
|
139
|
+
break;
|
|
140
|
+
|
|
141
|
+
case MessageType.CEREMONY_HEADER:
|
|
142
|
+
this._handleCeremonyHeader(content);
|
|
143
|
+
break;
|
|
144
|
+
|
|
145
|
+
case MessageType.PROGRESS:
|
|
146
|
+
this._handleProgress(content);
|
|
147
|
+
break;
|
|
148
|
+
|
|
149
|
+
case MessageType.SUBSTEP:
|
|
150
|
+
this._handleSubstep(content);
|
|
151
|
+
break;
|
|
152
|
+
|
|
153
|
+
case MessageType.USER_OUTPUT:
|
|
154
|
+
this._handleUserOutput(content);
|
|
155
|
+
break;
|
|
156
|
+
|
|
157
|
+
case MessageType.ERROR:
|
|
158
|
+
this._handleError(content);
|
|
159
|
+
break;
|
|
160
|
+
|
|
161
|
+
case MessageType.WARNING:
|
|
162
|
+
this._handleWarning(content);
|
|
163
|
+
break;
|
|
164
|
+
|
|
165
|
+
case MessageType.SUCCESS:
|
|
166
|
+
this._handleSuccess(content);
|
|
167
|
+
break;
|
|
168
|
+
|
|
169
|
+
case MessageType.INFO:
|
|
170
|
+
this._handleInfo(content);
|
|
171
|
+
break;
|
|
172
|
+
|
|
173
|
+
case MessageType.DEBUG:
|
|
174
|
+
this._handleDebug(content, options);
|
|
175
|
+
break;
|
|
176
|
+
|
|
177
|
+
default:
|
|
178
|
+
this._handleUserOutput(content);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Handle COMMAND_START message
|
|
184
|
+
* @private
|
|
185
|
+
*/
|
|
186
|
+
_handleCommandStart(content) {
|
|
187
|
+
// Clear output buffer (already done in startExecution)
|
|
188
|
+
// This is a marker message type, no output needed
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Handle CEREMONY_HEADER message
|
|
193
|
+
* @private
|
|
194
|
+
*/
|
|
195
|
+
_handleCeremonyHeader(content) {
|
|
196
|
+
outputBuffer.append(content);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Handle PROGRESS message
|
|
201
|
+
* @private
|
|
202
|
+
*/
|
|
203
|
+
_handleProgress(content) {
|
|
204
|
+
if (this.currentContext) {
|
|
205
|
+
this.currentContext.setExecutingMessage(content);
|
|
206
|
+
}
|
|
207
|
+
if (this.executingMessageCallback) {
|
|
208
|
+
this.executingMessageCallback(content);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Handle SUBSTEP message
|
|
214
|
+
* @private
|
|
215
|
+
*/
|
|
216
|
+
_handleSubstep(content) {
|
|
217
|
+
if (this.currentContext) {
|
|
218
|
+
this.currentContext.setExecutingSubstep(content);
|
|
219
|
+
}
|
|
220
|
+
if (this.executingSubstepCallback) {
|
|
221
|
+
this.executingSubstepCallback(content);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Handle USER_OUTPUT message
|
|
227
|
+
* @private
|
|
228
|
+
*/
|
|
229
|
+
_handleUserOutput(content) {
|
|
230
|
+
// Empty string means "blank line" ā outputBuffer ignores falsy values,
|
|
231
|
+
// so convert '' to '\n' to produce the intended visual gap.
|
|
232
|
+
outputBuffer.append(content === '' ? '\n' : content);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Handle ERROR message
|
|
237
|
+
* @private
|
|
238
|
+
*/
|
|
239
|
+
_handleError(content) {
|
|
240
|
+
outputBuffer.append(`ERROR: ${content}`, 'ERROR');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Handle WARNING message
|
|
245
|
+
* @private
|
|
246
|
+
*/
|
|
247
|
+
_handleWarning(content) {
|
|
248
|
+
outputBuffer.append(`WARNING: ${content}`, 'WARNING');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Handle SUCCESS message
|
|
253
|
+
* @private
|
|
254
|
+
*/
|
|
255
|
+
_handleSuccess(content) {
|
|
256
|
+
outputBuffer.append(`SUCCESS: ${content}`, 'SUCCESS');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Handle INFO message
|
|
261
|
+
* @private
|
|
262
|
+
*/
|
|
263
|
+
_handleInfo(content) {
|
|
264
|
+
outputBuffer.append(`INFO: ${content}`, 'INFO');
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Handle DEBUG message
|
|
269
|
+
* @private
|
|
270
|
+
*/
|
|
271
|
+
_handleDebug(content, options = {}) {
|
|
272
|
+
// Debug messages only go to console.log, not main output
|
|
273
|
+
const timestamp = new Date().toISOString();
|
|
274
|
+
if (options.data) {
|
|
275
|
+
console.log(`[DEBUG][${timestamp}] ${content}`, JSON.stringify(options.data, null, 2));
|
|
276
|
+
} else {
|
|
277
|
+
console.log(`[DEBUG][${timestamp}] ${content}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Clear progress indicators
|
|
283
|
+
*/
|
|
284
|
+
clearProgress() {
|
|
285
|
+
if (this.currentContext) {
|
|
286
|
+
this.currentContext.clearExecutingMessage();
|
|
287
|
+
this.currentContext.clearExecutingSubstep();
|
|
288
|
+
}
|
|
289
|
+
if (this.executingMessageCallback) {
|
|
290
|
+
this.executingMessageCallback('');
|
|
291
|
+
}
|
|
292
|
+
if (this.executingSubstepCallback) {
|
|
293
|
+
this.executingSubstepCallback('');
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Get execution context history
|
|
299
|
+
* @returns {Array} Array of execution contexts
|
|
300
|
+
*/
|
|
301
|
+
getHistory() {
|
|
302
|
+
return [...this.contextHistory];
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get manager state for debugging
|
|
307
|
+
* @returns {Object} Manager state
|
|
308
|
+
*/
|
|
309
|
+
toJSON() {
|
|
310
|
+
return {
|
|
311
|
+
hasCurrentContext: !!this.currentContext,
|
|
312
|
+
currentContext: this.currentContext ? this.currentContext.toJSON() : null,
|
|
313
|
+
historySize: this.contextHistory.length,
|
|
314
|
+
hasCallbacks: {
|
|
315
|
+
executingMessage: !!this.executingMessageCallback,
|
|
316
|
+
executingSubstep: !!this.executingSubstepCallback
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Reset manager (for testing)
|
|
323
|
+
*/
|
|
324
|
+
reset() {
|
|
325
|
+
if (this.currentContext) {
|
|
326
|
+
this.currentContext.cancel();
|
|
327
|
+
}
|
|
328
|
+
this.currentContext = null;
|
|
329
|
+
this.contextHistory = [];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Export singleton instance
|
|
334
|
+
export const messageManager = new MessageManager();
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message Types for MessageManager
|
|
3
|
+
*
|
|
4
|
+
* Defines different types of messages that can be sent through the messaging system.
|
|
5
|
+
* Each type can be handled differently (formatting, logging, display).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const MessageType = {
|
|
9
|
+
/**
|
|
10
|
+
* Command start - marks the beginning of a command execution
|
|
11
|
+
* Typically clears the output buffer
|
|
12
|
+
*/
|
|
13
|
+
COMMAND_START: 'COMMAND_START',
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Ceremony header - displays ceremony title and documentation URL
|
|
17
|
+
* Format: šÆ Title\nš URL
|
|
18
|
+
*/
|
|
19
|
+
CEREMONY_HEADER: 'CEREMONY_HEADER',
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Progress indicator - shows ongoing operation status
|
|
23
|
+
* These go to executingMessage state, not main output
|
|
24
|
+
*/
|
|
25
|
+
PROGRESS: 'PROGRESS',
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Substep progress - shows detailed progress within an operation
|
|
29
|
+
* These go to executingSubstep state, not main output
|
|
30
|
+
*/
|
|
31
|
+
SUBSTEP: 'SUBSTEP',
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* User output - main content shown to user
|
|
35
|
+
* Standard output messages
|
|
36
|
+
*/
|
|
37
|
+
USER_OUTPUT: 'USER_OUTPUT',
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Error message - displays errors to user
|
|
41
|
+
* Automatically formatted with ERROR: prefix in red bold
|
|
42
|
+
*/
|
|
43
|
+
ERROR: 'ERROR',
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Warning message - displays warnings to user
|
|
47
|
+
* Automatically formatted with WARNING: prefix in yellow bold
|
|
48
|
+
*/
|
|
49
|
+
WARNING: 'WARNING',
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Success message - displays success confirmation
|
|
53
|
+
* Automatically formatted with SUCCESS: prefix in green bold
|
|
54
|
+
*/
|
|
55
|
+
SUCCESS: 'SUCCESS',
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Debug message - development/troubleshooting info
|
|
59
|
+
* Not shown in main output, only console.log
|
|
60
|
+
*/
|
|
61
|
+
DEBUG: 'DEBUG',
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Info message - informational content
|
|
65
|
+
* Automatically formatted with INFO: prefix in cyan
|
|
66
|
+
*/
|
|
67
|
+
INFO: 'INFO'
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check if a message type is valid
|
|
72
|
+
* @param {string} type - Message type to validate
|
|
73
|
+
* @returns {boolean} True if valid
|
|
74
|
+
*/
|
|
75
|
+
export function isValidMessageType(type) {
|
|
76
|
+
return Object.values(MessageType).includes(type);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Get default formatting for a message type
|
|
81
|
+
* @param {string} type - Message type
|
|
82
|
+
* @returns {object} Format configuration with prefix, color, and bold flag
|
|
83
|
+
*/
|
|
84
|
+
export function getMessageFormat(type) {
|
|
85
|
+
const formats = {
|
|
86
|
+
[MessageType.ERROR]: { prefix: 'ERROR: ', color: 'red', bold: true },
|
|
87
|
+
[MessageType.WARNING]: { prefix: 'WARNING: ', color: 'yellow', bold: true },
|
|
88
|
+
[MessageType.SUCCESS]: { prefix: 'SUCCESS: ', color: 'green', bold: true },
|
|
89
|
+
[MessageType.INFO]: { prefix: 'INFO: ', color: 'cyan', bold: false },
|
|
90
|
+
[MessageType.CEREMONY_HEADER]: { prefix: '', color: 'cyan', bold: true },
|
|
91
|
+
[MessageType.USER_OUTPUT]: { prefix: '', color: 'white', bold: false },
|
|
92
|
+
[MessageType.DEBUG]: { prefix: '[DEBUG] ', color: 'gray', bold: false }
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return formats[type] || { prefix: '', color: 'white', bold: false };
|
|
96
|
+
}
|