musubi-sdd 5.1.0 → 5.6.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/README.ja.md +106 -48
- package/README.md +110 -32
- package/bin/musubi-analyze.js +74 -67
- package/bin/musubi-browser.js +27 -26
- package/bin/musubi-change.js +48 -47
- package/bin/musubi-checkpoint.js +10 -7
- package/bin/musubi-convert.js +25 -25
- package/bin/musubi-costs.js +27 -10
- package/bin/musubi-gui.js +52 -46
- package/bin/musubi-init.js +1952 -10
- package/bin/musubi-orchestrate.js +327 -239
- package/bin/musubi-remember.js +69 -56
- package/bin/musubi-resolve.js +53 -45
- package/bin/musubi-trace.js +51 -22
- package/bin/musubi-validate.js +39 -30
- package/bin/musubi-workflow.js +33 -34
- package/bin/musubi.js +39 -2
- package/package.json +1 -1
- package/src/agents/agent-loop.js +94 -95
- package/src/agents/agentic/code-generator.js +119 -109
- package/src/agents/agentic/code-reviewer.js +105 -108
- package/src/agents/agentic/index.js +4 -4
- package/src/agents/browser/action-executor.js +13 -13
- package/src/agents/browser/ai-comparator.js +11 -10
- package/src/agents/browser/context-manager.js +6 -6
- package/src/agents/browser/index.js +5 -5
- package/src/agents/browser/nl-parser.js +31 -46
- package/src/agents/browser/screenshot.js +2 -2
- package/src/agents/browser/test-generator.js +6 -4
- package/src/agents/function-tool.js +71 -65
- package/src/agents/index.js +7 -7
- package/src/agents/schema-generator.js +98 -94
- package/src/analyzers/ast-extractor.js +158 -146
- package/src/analyzers/codegraph-auto-update.js +858 -0
- package/src/analyzers/complexity-analyzer.js +536 -0
- package/src/analyzers/context-optimizer.js +241 -126
- package/src/analyzers/impact-analyzer.js +1 -1
- package/src/analyzers/large-project-analyzer.js +766 -0
- package/src/analyzers/repository-map.js +77 -81
- package/src/analyzers/security-analyzer.js +19 -11
- package/src/analyzers/stuck-detector.js +19 -17
- package/src/converters/index.js +78 -57
- package/src/converters/ir/types.js +12 -12
- package/src/converters/parsers/musubi-parser.js +134 -126
- package/src/converters/parsers/openapi-parser.js +70 -53
- package/src/converters/parsers/speckit-parser.js +239 -175
- package/src/converters/writers/musubi-writer.js +123 -118
- package/src/converters/writers/speckit-writer.js +124 -113
- package/src/generators/rust-migration-generator.js +512 -0
- package/src/gui/public/index.html +1365 -1211
- package/src/gui/server.js +41 -40
- package/src/gui/services/file-watcher.js +23 -8
- package/src/gui/services/project-scanner.js +26 -20
- package/src/gui/services/replanning-service.js +27 -23
- package/src/gui/services/traceability-service.js +8 -8
- package/src/gui/services/workflow-service.js +14 -7
- package/src/index.js +151 -0
- package/src/integrations/cicd.js +90 -104
- package/src/integrations/codegraph-mcp.js +643 -0
- package/src/integrations/documentation.js +142 -103
- package/src/integrations/examples.js +95 -80
- package/src/integrations/github-client.js +17 -17
- package/src/integrations/index.js +5 -5
- package/src/integrations/mcp/index.js +21 -21
- package/src/integrations/mcp/mcp-context-provider.js +76 -78
- package/src/integrations/mcp/mcp-discovery.js +74 -72
- package/src/integrations/mcp/mcp-tool-registry.js +99 -94
- package/src/integrations/mcp-connector.js +70 -66
- package/src/integrations/platforms.js +50 -49
- package/src/integrations/tool-discovery.js +37 -31
- package/src/llm-providers/anthropic-provider.js +11 -11
- package/src/llm-providers/base-provider.js +16 -18
- package/src/llm-providers/copilot-provider.js +22 -19
- package/src/llm-providers/index.js +26 -25
- package/src/llm-providers/ollama-provider.js +11 -11
- package/src/llm-providers/openai-provider.js +12 -12
- package/src/managers/agent-memory.js +36 -24
- package/src/managers/checkpoint-manager.js +4 -8
- package/src/managers/delta-spec.js +19 -19
- package/src/managers/index.js +13 -4
- package/src/managers/memory-condenser.js +35 -45
- package/src/managers/repo-skill-manager.js +57 -31
- package/src/managers/skill-loader.js +25 -22
- package/src/managers/skill-tools.js +36 -72
- package/src/managers/workflow.js +30 -22
- package/src/monitoring/cost-tracker.js +48 -46
- package/src/monitoring/incident-manager.js +116 -106
- package/src/monitoring/index.js +144 -134
- package/src/monitoring/observability.js +75 -62
- package/src/monitoring/quality-dashboard.js +45 -41
- package/src/monitoring/release-manager.js +63 -53
- package/src/orchestration/agent-skill-binding.js +39 -47
- package/src/orchestration/error-handler.js +65 -107
- package/src/orchestration/guardrails/base-guardrail.js +26 -24
- package/src/orchestration/guardrails/guardrail-rules.js +50 -64
- package/src/orchestration/guardrails/index.js +5 -5
- package/src/orchestration/guardrails/input-guardrail.js +58 -45
- package/src/orchestration/guardrails/output-guardrail.js +104 -81
- package/src/orchestration/guardrails/safety-check.js +79 -79
- package/src/orchestration/index.js +38 -55
- package/src/orchestration/mcp-tool-adapters.js +96 -99
- package/src/orchestration/orchestration-engine.js +21 -21
- package/src/orchestration/pattern-registry.js +60 -45
- package/src/orchestration/patterns/auto.js +34 -47
- package/src/orchestration/patterns/group-chat.js +59 -65
- package/src/orchestration/patterns/handoff.js +67 -65
- package/src/orchestration/patterns/human-in-loop.js +51 -72
- package/src/orchestration/patterns/nested.js +25 -40
- package/src/orchestration/patterns/sequential.js +35 -34
- package/src/orchestration/patterns/swarm.js +63 -56
- package/src/orchestration/patterns/triage.js +150 -109
- package/src/orchestration/reasoning/index.js +9 -9
- package/src/orchestration/reasoning/planning-engine.js +143 -140
- package/src/orchestration/reasoning/reasoning-engine.js +206 -144
- package/src/orchestration/reasoning/self-correction.js +121 -128
- package/src/orchestration/replanning/adaptive-goal-modifier.js +107 -112
- package/src/orchestration/replanning/alternative-generator.js +37 -42
- package/src/orchestration/replanning/config.js +63 -59
- package/src/orchestration/replanning/goal-progress-tracker.js +98 -100
- package/src/orchestration/replanning/index.js +24 -20
- package/src/orchestration/replanning/plan-evaluator.js +49 -50
- package/src/orchestration/replanning/plan-monitor.js +32 -28
- package/src/orchestration/replanning/proactive-path-optimizer.js +175 -178
- package/src/orchestration/replanning/replan-history.js +33 -26
- package/src/orchestration/replanning/replanning-engine.js +106 -108
- package/src/orchestration/skill-executor.js +107 -109
- package/src/orchestration/skill-registry.js +85 -89
- package/src/orchestration/workflow-examples.js +228 -231
- package/src/orchestration/workflow-executor.js +65 -68
- package/src/orchestration/workflow-orchestrator.js +72 -73
- package/src/phase4-integration.js +47 -40
- package/src/phase5-integration.js +89 -30
- package/src/reporters/coverage-report.js +82 -30
- package/src/reporters/hierarchical-reporter.js +498 -0
- package/src/reporters/traceability-matrix-report.js +29 -20
- package/src/resolvers/issue-resolver.js +43 -31
- package/src/steering/advanced-validation.js +133 -124
- package/src/steering/auto-updater.js +60 -73
- package/src/steering/index.js +6 -6
- package/src/steering/quality-metrics.js +41 -35
- package/src/steering/steering-auto-update.js +83 -86
- package/src/steering/steering-validator.js +98 -106
- package/src/steering/template-constraints.js +53 -54
- package/src/templates/agents/claude-code/CLAUDE.md +32 -32
- package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +13 -5
- package/src/templates/agents/claude-code/skills/ai-ml-engineer/mlops-guide.md +23 -23
- package/src/templates/agents/claude-code/skills/ai-ml-engineer/model-card-template.md +60 -41
- package/src/templates/agents/claude-code/skills/api-designer/api-patterns.md +27 -19
- package/src/templates/agents/claude-code/skills/api-designer/openapi-template.md +11 -7
- package/src/templates/agents/claude-code/skills/bug-hunter/SKILL.md +4 -3
- package/src/templates/agents/claude-code/skills/bug-hunter/root-cause-analysis.md +37 -15
- package/src/templates/agents/claude-code/skills/change-impact-analyzer/dependency-graph-patterns.md +36 -42
- package/src/templates/agents/claude-code/skills/change-impact-analyzer/impact-analysis-template.md +69 -60
- package/src/templates/agents/claude-code/skills/cloud-architect/aws-patterns.md +31 -38
- package/src/templates/agents/claude-code/skills/cloud-architect/azure-patterns.md +28 -23
- package/src/templates/agents/claude-code/skills/code-reviewer/SKILL.md +61 -0
- package/src/templates/agents/claude-code/skills/code-reviewer/best-practices.md +27 -0
- package/src/templates/agents/claude-code/skills/code-reviewer/review-checklist.md +29 -10
- package/src/templates/agents/claude-code/skills/code-reviewer/review-standards.md +29 -24
- package/src/templates/agents/claude-code/skills/constitution-enforcer/SKILL.md +8 -6
- package/src/templates/agents/claude-code/skills/constitution-enforcer/constitutional-articles.md +62 -26
- package/src/templates/agents/claude-code/skills/constitution-enforcer/phase-minus-one-gates.md +35 -16
- package/src/templates/agents/claude-code/skills/database-administrator/backup-recovery.md +27 -17
- package/src/templates/agents/claude-code/skills/database-administrator/tuning-guide.md +25 -20
- package/src/templates/agents/claude-code/skills/database-schema-designer/schema-patterns.md +39 -22
- package/src/templates/agents/claude-code/skills/devops-engineer/ci-cd-templates.md +25 -22
- package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +24 -21
- package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +148 -63
- package/src/templates/agents/claude-code/skills/orchestrator/patterns.md +35 -16
- package/src/templates/agents/claude-code/skills/orchestrator/selection-matrix.md +69 -64
- package/src/templates/agents/claude-code/skills/performance-engineer/optimization-playbook.md +47 -47
- package/src/templates/agents/claude-code/skills/performance-optimizer/SKILL.md +69 -0
- package/src/templates/agents/claude-code/skills/performance-optimizer/benchmark-template.md +63 -45
- package/src/templates/agents/claude-code/skills/performance-optimizer/optimization-patterns.md +33 -35
- package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +7 -6
- package/src/templates/agents/claude-code/skills/project-manager/agile-ceremonies.md +47 -28
- package/src/templates/agents/claude-code/skills/project-manager/project-templates.md +94 -78
- package/src/templates/agents/claude-code/skills/quality-assurance/SKILL.md +20 -17
- package/src/templates/agents/claude-code/skills/quality-assurance/qa-plan-template.md +63 -49
- package/src/templates/agents/claude-code/skills/release-coordinator/SKILL.md +5 -5
- package/src/templates/agents/claude-code/skills/release-coordinator/feature-flag-guide.md +30 -26
- package/src/templates/agents/claude-code/skills/release-coordinator/release-plan-template.md +67 -35
- package/src/templates/agents/claude-code/skills/requirements-analyst/ears-format.md +54 -42
- package/src/templates/agents/claude-code/skills/requirements-analyst/validation-rules.md +36 -33
- package/src/templates/agents/claude-code/skills/security-auditor/SKILL.md +77 -19
- package/src/templates/agents/claude-code/skills/security-auditor/audit-checklists.md +24 -24
- package/src/templates/agents/claude-code/skills/security-auditor/owasp-top-10.md +61 -20
- package/src/templates/agents/claude-code/skills/security-auditor/vulnerability-patterns.md +43 -11
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/incident-response-template.md +55 -25
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/observability-patterns.md +78 -68
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/slo-sli-guide.md +73 -53
- package/src/templates/agents/claude-code/skills/software-developer/solid-principles.md +83 -37
- package/src/templates/agents/claude-code/skills/software-developer/test-first-workflow.md +38 -31
- package/src/templates/agents/claude-code/skills/steering/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/steering/auto-update-rules.md +31 -0
- package/src/templates/agents/claude-code/skills/system-architect/adr-template.md +25 -7
- package/src/templates/agents/claude-code/skills/system-architect/c4-model-guide.md +74 -61
- package/src/templates/agents/claude-code/skills/technical-writer/doc-templates/documentation-templates.md +70 -52
- package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +2 -0
- package/src/templates/agents/claude-code/skills/test-engineer/ears-test-mapping.md +75 -71
- package/src/templates/agents/claude-code/skills/test-engineer/test-types.md +85 -63
- package/src/templates/agents/claude-code/skills/traceability-auditor/coverage-matrix-template.md +39 -36
- package/src/templates/agents/claude-code/skills/traceability-auditor/gap-detection-rules.md +22 -17
- package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +1 -0
- package/src/templates/agents/claude-code/skills/ui-ux-designer/accessibility-guidelines.md +49 -75
- package/src/templates/agents/claude-code/skills/ui-ux-designer/design-system-components.md +71 -59
- package/src/templates/agents/codex/AGENTS.md +74 -42
- package/src/templates/agents/cursor/AGENTS.md +74 -42
- package/src/templates/agents/gemini-cli/GEMINI.md +74 -42
- package/src/templates/agents/github-copilot/AGENTS.md +83 -51
- package/src/templates/agents/qwen-code/QWEN.md +74 -42
- package/src/templates/agents/windsurf/AGENTS.md +74 -42
- package/src/templates/architectures/README.md +41 -0
- package/src/templates/architectures/clean-architecture/README.md +113 -0
- package/src/templates/architectures/event-driven/README.md +162 -0
- package/src/templates/architectures/hexagonal/README.md +130 -0
- package/src/templates/index.js +6 -1
- package/src/templates/locale-manager.js +16 -16
- package/src/templates/shared/delta-spec-template.md +20 -13
- package/src/templates/shared/github-actions/musubi-issue-resolver.yml +5 -5
- package/src/templates/shared/github-actions/musubi-security-check.yml +3 -3
- package/src/templates/shared/github-actions/musubi-validate.yml +4 -4
- package/src/templates/shared/steering/structure.md +95 -0
- package/src/templates/skills/browser-agent.md +21 -16
- package/src/templates/skills/web-gui.md +8 -0
- package/src/templates/template-constraints.js +50 -53
- package/src/validators/advanced-validation.js +30 -36
- package/src/validators/constitutional-validator.js +77 -73
- package/src/validators/critic-system.js +49 -59
- package/src/validators/delta-format.js +59 -55
- package/src/validators/traceability-validator.js +7 -11
|
@@ -25,24 +25,19 @@ const {
|
|
|
25
25
|
createOrchestrationEngine,
|
|
26
26
|
PatternType,
|
|
27
27
|
ExecutionStatus,
|
|
28
|
-
Priority,
|
|
29
|
-
HandoffPattern,
|
|
30
28
|
HandoffFilters,
|
|
31
29
|
HandoffConfig,
|
|
32
|
-
handoff,
|
|
33
|
-
TriagePattern,
|
|
34
30
|
TriageCategory,
|
|
35
31
|
TriageStrategy,
|
|
36
|
-
AgentCapability
|
|
32
|
+
AgentCapability,
|
|
37
33
|
} = require('../src/orchestration');
|
|
38
34
|
|
|
39
35
|
const {
|
|
40
36
|
ReplanningEngine,
|
|
41
37
|
GoalProgressTracker,
|
|
42
|
-
Goal,
|
|
43
38
|
AdaptiveGoalModifier,
|
|
44
39
|
ProactivePathOptimizer,
|
|
45
|
-
ModificationReason
|
|
40
|
+
ModificationReason,
|
|
46
41
|
} = require('../src/orchestration/replanning');
|
|
47
42
|
|
|
48
43
|
const program = new Command();
|
|
@@ -58,27 +53,27 @@ program
|
|
|
58
53
|
async function loadSkills(projectPath) {
|
|
59
54
|
const skills = new Map();
|
|
60
55
|
const skillsPath = path.join(projectPath, 'src', 'templates', 'skills');
|
|
61
|
-
|
|
56
|
+
|
|
62
57
|
if (await fs.pathExists(skillsPath)) {
|
|
63
58
|
const skillDirs = await fs.readdir(skillsPath);
|
|
64
|
-
|
|
59
|
+
|
|
65
60
|
for (const skillDir of skillDirs) {
|
|
66
61
|
const skillPath = path.join(skillsPath, skillDir);
|
|
67
62
|
const stat = await fs.stat(skillPath);
|
|
68
|
-
|
|
63
|
+
|
|
69
64
|
if (stat.isDirectory()) {
|
|
70
65
|
const metaPath = path.join(skillPath, 'skill.json');
|
|
71
|
-
|
|
66
|
+
|
|
72
67
|
if (await fs.pathExists(metaPath)) {
|
|
73
68
|
try {
|
|
74
69
|
const meta = await fs.readJson(metaPath);
|
|
75
70
|
skills.set(skillDir, {
|
|
76
71
|
name: skillDir,
|
|
77
72
|
...meta,
|
|
78
|
-
execute: async
|
|
73
|
+
execute: async input => {
|
|
79
74
|
// Placeholder for actual skill execution
|
|
80
75
|
return { skill: skillDir, input, executed: true };
|
|
81
|
-
}
|
|
76
|
+
},
|
|
82
77
|
});
|
|
83
78
|
} catch (e) {
|
|
84
79
|
// Skip invalid skills
|
|
@@ -89,39 +84,67 @@ async function loadSkills(projectPath) {
|
|
|
89
84
|
name: skillDir,
|
|
90
85
|
description: `${skillDir} skill`,
|
|
91
86
|
keywords: [skillDir],
|
|
92
|
-
execute: async
|
|
87
|
+
execute: async input => {
|
|
93
88
|
return { skill: skillDir, input, executed: true };
|
|
94
|
-
}
|
|
89
|
+
},
|
|
95
90
|
});
|
|
96
91
|
}
|
|
97
92
|
}
|
|
98
93
|
}
|
|
99
94
|
}
|
|
100
|
-
|
|
95
|
+
|
|
101
96
|
// Add built-in mock skills for demonstration
|
|
102
97
|
if (skills.size === 0) {
|
|
103
98
|
const mockSkills = [
|
|
104
|
-
{
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
99
|
+
{
|
|
100
|
+
name: 'requirements-analyst',
|
|
101
|
+
keywords: ['requirement', 'ears', 'specification'],
|
|
102
|
+
categories: ['requirements'],
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'system-architect',
|
|
106
|
+
keywords: ['architecture', 'design', 'c4'],
|
|
107
|
+
categories: ['design'],
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: 'task-decomposer',
|
|
111
|
+
keywords: ['task', 'breakdown', 'decompose'],
|
|
112
|
+
categories: ['implementation'],
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'code-generator',
|
|
116
|
+
keywords: ['code', 'implement', 'generate'],
|
|
117
|
+
categories: ['implementation'],
|
|
118
|
+
},
|
|
108
119
|
{ name: 'test-engineer', keywords: ['test', 'testing', 'qa'], categories: ['testing'] },
|
|
109
|
-
{
|
|
110
|
-
|
|
111
|
-
|
|
120
|
+
{
|
|
121
|
+
name: 'documentation-writer',
|
|
122
|
+
keywords: ['document', 'readme', 'guide'],
|
|
123
|
+
categories: ['documentation'],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'security-analyst',
|
|
127
|
+
keywords: ['security', 'vulnerability', 'audit'],
|
|
128
|
+
categories: ['security'],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'performance-engineer',
|
|
132
|
+
keywords: ['performance', 'optimize', 'benchmark'],
|
|
133
|
+
categories: ['performance'],
|
|
134
|
+
},
|
|
112
135
|
];
|
|
113
|
-
|
|
136
|
+
|
|
114
137
|
for (const skill of mockSkills) {
|
|
115
138
|
skills.set(skill.name, {
|
|
116
139
|
...skill,
|
|
117
140
|
description: `${skill.name} skill`,
|
|
118
|
-
execute: async
|
|
141
|
+
execute: async input => {
|
|
119
142
|
return { skill: skill.name, input, executed: true, mock: true };
|
|
120
|
-
}
|
|
143
|
+
},
|
|
121
144
|
});
|
|
122
145
|
}
|
|
123
146
|
}
|
|
124
|
-
|
|
147
|
+
|
|
125
148
|
return skills;
|
|
126
149
|
}
|
|
127
150
|
|
|
@@ -131,15 +154,15 @@ async function loadSkills(projectPath) {
|
|
|
131
154
|
async function createEngine(projectPath) {
|
|
132
155
|
const engine = createOrchestrationEngine({
|
|
133
156
|
maxConcurrent: 5,
|
|
134
|
-
timeout: 300000
|
|
157
|
+
timeout: 300000,
|
|
135
158
|
});
|
|
136
|
-
|
|
159
|
+
|
|
137
160
|
// Load and register skills
|
|
138
161
|
const skills = await loadSkills(projectPath);
|
|
139
162
|
for (const [name, skill] of skills) {
|
|
140
163
|
engine.registerSkill(name, skill);
|
|
141
164
|
}
|
|
142
|
-
|
|
165
|
+
|
|
143
166
|
return engine;
|
|
144
167
|
}
|
|
145
168
|
|
|
@@ -150,20 +173,18 @@ function formatResult(result, format = 'text') {
|
|
|
150
173
|
if (format === 'json') {
|
|
151
174
|
return JSON.stringify(result, null, 2);
|
|
152
175
|
}
|
|
153
|
-
|
|
176
|
+
|
|
154
177
|
let output = '';
|
|
155
|
-
|
|
178
|
+
|
|
156
179
|
if (result.selectedSkill) {
|
|
157
180
|
output += chalk.bold(`Selected Skill: `) + chalk.cyan(result.selectedSkill) + '\n';
|
|
158
181
|
output += chalk.bold(`Confidence: `) + formatConfidence(result.confidence) + '\n';
|
|
159
182
|
}
|
|
160
|
-
|
|
183
|
+
|
|
161
184
|
if (result.results) {
|
|
162
185
|
output += chalk.bold('\nExecution Results:\n');
|
|
163
186
|
for (const r of result.results) {
|
|
164
|
-
const status = r.status === ExecutionStatus.COMPLETED
|
|
165
|
-
? chalk.green('✓')
|
|
166
|
-
: chalk.red('✗');
|
|
187
|
+
const status = r.status === ExecutionStatus.COMPLETED ? chalk.green('✓') : chalk.red('✗');
|
|
167
188
|
const step = r.step ? `Step ${r.step}: ` : '';
|
|
168
189
|
output += ` ${status} ${step}${chalk.cyan(r.skill)}`;
|
|
169
190
|
if (r.error) {
|
|
@@ -172,7 +193,7 @@ function formatResult(result, format = 'text') {
|
|
|
172
193
|
output += '\n';
|
|
173
194
|
}
|
|
174
195
|
}
|
|
175
|
-
|
|
196
|
+
|
|
176
197
|
if (result.summary) {
|
|
177
198
|
output += chalk.bold('\nSummary:\n');
|
|
178
199
|
output += ` Total Steps: ${result.summary.totalSteps}\n`;
|
|
@@ -180,7 +201,7 @@ function formatResult(result, format = 'text') {
|
|
|
180
201
|
output += ` Failed: ${chalk.red(result.summary.failed)}\n`;
|
|
181
202
|
output += ` Success Rate: ${result.summary.successRate}\n`;
|
|
182
203
|
}
|
|
183
|
-
|
|
204
|
+
|
|
184
205
|
return output;
|
|
185
206
|
}
|
|
186
207
|
|
|
@@ -204,19 +225,19 @@ program
|
|
|
204
225
|
.action(async (pattern, options) => {
|
|
205
226
|
try {
|
|
206
227
|
console.log(chalk.bold(`\n🎭 Running ${pattern} pattern\n`));
|
|
207
|
-
|
|
228
|
+
|
|
208
229
|
const engine = await createEngine(process.cwd());
|
|
209
|
-
|
|
230
|
+
|
|
210
231
|
const input = options.input ? JSON.parse(options.input) : {};
|
|
211
232
|
if (options.skills) {
|
|
212
233
|
input.skills = options.skills;
|
|
213
234
|
}
|
|
214
|
-
|
|
235
|
+
|
|
215
236
|
const context = await engine.execute(pattern, {
|
|
216
237
|
task: options.task || `Execute ${pattern} pattern`,
|
|
217
|
-
input
|
|
238
|
+
input,
|
|
218
239
|
});
|
|
219
|
-
|
|
240
|
+
|
|
220
241
|
if (context.status === ExecutionStatus.COMPLETED) {
|
|
221
242
|
console.log(chalk.green('✓ Pattern execution completed\n'));
|
|
222
243
|
console.log(formatResult(context.output, options.format));
|
|
@@ -224,7 +245,6 @@ program
|
|
|
224
245
|
console.log(chalk.red(`✗ Pattern execution failed: ${context.error}\n`));
|
|
225
246
|
process.exit(1);
|
|
226
247
|
}
|
|
227
|
-
|
|
228
248
|
} catch (error) {
|
|
229
249
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
230
250
|
if (process.env.DEBUG) {
|
|
@@ -245,12 +265,12 @@ program
|
|
|
245
265
|
try {
|
|
246
266
|
console.log(chalk.bold('\n🤖 Auto Pattern - Intelligent Skill Selection\n'));
|
|
247
267
|
console.log(chalk.dim(`Task: ${task}\n`));
|
|
248
|
-
|
|
268
|
+
|
|
249
269
|
const engine = await createEngine(process.cwd());
|
|
250
|
-
|
|
270
|
+
|
|
251
271
|
const input = options.input ? JSON.parse(options.input) : {};
|
|
252
272
|
input.task = task;
|
|
253
|
-
|
|
273
|
+
|
|
254
274
|
// Update auto pattern config if multi mode
|
|
255
275
|
if (options.multi) {
|
|
256
276
|
const autoPattern = engine.getPattern(PatternType.AUTO);
|
|
@@ -258,12 +278,12 @@ program
|
|
|
258
278
|
autoPattern.options.multiMatch = true;
|
|
259
279
|
}
|
|
260
280
|
}
|
|
261
|
-
|
|
281
|
+
|
|
262
282
|
const context = await engine.execute(PatternType.AUTO, {
|
|
263
283
|
task,
|
|
264
|
-
input
|
|
284
|
+
input,
|
|
265
285
|
});
|
|
266
|
-
|
|
286
|
+
|
|
267
287
|
if (context.status === ExecutionStatus.COMPLETED) {
|
|
268
288
|
console.log(chalk.green('✓ Auto execution completed\n'));
|
|
269
289
|
console.log(formatResult(context.output, options.format));
|
|
@@ -271,7 +291,6 @@ program
|
|
|
271
291
|
console.log(chalk.red(`✗ Auto execution failed: ${context.error}\n`));
|
|
272
292
|
process.exit(1);
|
|
273
293
|
}
|
|
274
|
-
|
|
275
294
|
} catch (error) {
|
|
276
295
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
277
296
|
if (process.env.DEBUG) {
|
|
@@ -289,23 +308,23 @@ program
|
|
|
289
308
|
.option('-i, --input <json>', 'Initial input data as JSON')
|
|
290
309
|
.option('-f, --format <type>', 'Output format (text|json)', 'text')
|
|
291
310
|
.option('--continue-on-error', 'Continue execution on error')
|
|
292
|
-
.action(async
|
|
311
|
+
.action(async options => {
|
|
293
312
|
try {
|
|
294
313
|
console.log(chalk.bold('\n🔗 Sequential Pattern - Step-by-Step Execution\n'));
|
|
295
314
|
console.log(chalk.dim(`Skills: ${options.skills.join(' → ')}\n`));
|
|
296
|
-
|
|
315
|
+
|
|
297
316
|
const engine = await createEngine(process.cwd());
|
|
298
|
-
|
|
317
|
+
|
|
299
318
|
const initialInput = options.input ? JSON.parse(options.input) : {};
|
|
300
|
-
|
|
319
|
+
|
|
301
320
|
const context = await engine.execute(PatternType.SEQUENTIAL, {
|
|
302
321
|
task: `Sequential execution of ${options.skills.length} skills`,
|
|
303
322
|
input: {
|
|
304
323
|
skills: options.skills,
|
|
305
|
-
initialInput
|
|
306
|
-
}
|
|
324
|
+
initialInput,
|
|
325
|
+
},
|
|
307
326
|
});
|
|
308
|
-
|
|
327
|
+
|
|
309
328
|
if (context.status === ExecutionStatus.COMPLETED) {
|
|
310
329
|
console.log(chalk.green('\n✓ Sequential execution completed\n'));
|
|
311
330
|
console.log(formatResult(context.output, options.format));
|
|
@@ -313,7 +332,6 @@ program
|
|
|
313
332
|
console.log(chalk.red(`\n✗ Sequential execution failed: ${context.error}\n`));
|
|
314
333
|
process.exit(1);
|
|
315
334
|
}
|
|
316
|
-
|
|
317
335
|
} catch (error) {
|
|
318
336
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
319
337
|
if (process.env.DEBUG) {
|
|
@@ -328,19 +346,19 @@ program
|
|
|
328
346
|
.command('list-patterns')
|
|
329
347
|
.description('List available orchestration patterns')
|
|
330
348
|
.option('-f, --format <type>', 'Output format (text|json)', 'text')
|
|
331
|
-
.action(async
|
|
349
|
+
.action(async options => {
|
|
332
350
|
try {
|
|
333
351
|
console.log(chalk.bold('\n🎭 Available Orchestration Patterns\n'));
|
|
334
|
-
|
|
352
|
+
|
|
335
353
|
const engine = await createEngine(process.cwd());
|
|
336
354
|
const patterns = engine.listPatterns();
|
|
337
|
-
|
|
355
|
+
|
|
338
356
|
if (options.format === 'json') {
|
|
339
357
|
const patternData = patterns.map(name => {
|
|
340
358
|
const pattern = engine.getPattern(name);
|
|
341
359
|
return {
|
|
342
360
|
name,
|
|
343
|
-
metadata: pattern.metadata || { name }
|
|
361
|
+
metadata: pattern.metadata || { name },
|
|
344
362
|
};
|
|
345
363
|
});
|
|
346
364
|
console.log(JSON.stringify(patternData, null, 2));
|
|
@@ -358,9 +376,8 @@ program
|
|
|
358
376
|
}
|
|
359
377
|
}
|
|
360
378
|
}
|
|
361
|
-
|
|
379
|
+
|
|
362
380
|
console.log('');
|
|
363
|
-
|
|
364
381
|
} catch (error) {
|
|
365
382
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
366
383
|
process.exit(1);
|
|
@@ -373,25 +390,23 @@ program
|
|
|
373
390
|
.description('List available skills')
|
|
374
391
|
.option('-f, --format <type>', 'Output format (text|json)', 'text')
|
|
375
392
|
.option('--category <category>', 'Filter by category')
|
|
376
|
-
.action(async
|
|
393
|
+
.action(async options => {
|
|
377
394
|
try {
|
|
378
395
|
console.log(chalk.bold('\n🛠️ Available Skills\n'));
|
|
379
|
-
|
|
396
|
+
|
|
380
397
|
const engine = await createEngine(process.cwd());
|
|
381
398
|
const skillNames = engine.listSkills();
|
|
382
|
-
|
|
399
|
+
|
|
383
400
|
let skills = skillNames.map(name => {
|
|
384
401
|
const skill = engine.getSkill(name);
|
|
385
402
|
return { name, ...skill };
|
|
386
403
|
});
|
|
387
|
-
|
|
404
|
+
|
|
388
405
|
// Filter by category
|
|
389
406
|
if (options.category) {
|
|
390
|
-
skills = skills.filter(s =>
|
|
391
|
-
s.categories && s.categories.includes(options.category)
|
|
392
|
-
);
|
|
407
|
+
skills = skills.filter(s => s.categories && s.categories.includes(options.category));
|
|
393
408
|
}
|
|
394
|
-
|
|
409
|
+
|
|
395
410
|
if (options.format === 'json') {
|
|
396
411
|
console.log(JSON.stringify(skills, null, 2));
|
|
397
412
|
} else {
|
|
@@ -405,7 +420,7 @@ program
|
|
|
405
420
|
if (!byCategory[cat]) byCategory[cat] = [];
|
|
406
421
|
byCategory[cat].push(skill);
|
|
407
422
|
}
|
|
408
|
-
|
|
423
|
+
|
|
409
424
|
for (const [category, catSkills] of Object.entries(byCategory)) {
|
|
410
425
|
console.log(chalk.bold(` ${category}:`));
|
|
411
426
|
for (const skill of catSkills) {
|
|
@@ -418,7 +433,6 @@ program
|
|
|
418
433
|
}
|
|
419
434
|
}
|
|
420
435
|
}
|
|
421
|
-
|
|
422
436
|
} catch (error) {
|
|
423
437
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
424
438
|
process.exit(1);
|
|
@@ -444,21 +458,21 @@ program
|
|
|
444
458
|
.action(async (action, options) => {
|
|
445
459
|
try {
|
|
446
460
|
const tracker = new GoalProgressTracker();
|
|
447
|
-
|
|
461
|
+
|
|
448
462
|
switch (action) {
|
|
449
463
|
case 'add': {
|
|
450
464
|
if (!options.name) {
|
|
451
465
|
console.error(chalk.red('Error: --name is required'));
|
|
452
466
|
process.exit(1);
|
|
453
467
|
}
|
|
454
|
-
|
|
468
|
+
|
|
455
469
|
const goal = tracker.registerGoal({
|
|
456
470
|
name: options.name,
|
|
457
471
|
description: options.description || '',
|
|
458
472
|
priority: parseInt(options.priority, 10),
|
|
459
|
-
deadline: options.deadline ? new Date(options.deadline).getTime() : null
|
|
473
|
+
deadline: options.deadline ? new Date(options.deadline).getTime() : null,
|
|
460
474
|
});
|
|
461
|
-
|
|
475
|
+
|
|
462
476
|
console.log(chalk.green(`\n✓ Goal created: ${goal.id}\n`));
|
|
463
477
|
console.log(chalk.cyan(` Name: ${goal.name}`));
|
|
464
478
|
console.log(chalk.dim(` Priority: ${goal.priority}`));
|
|
@@ -468,56 +482,73 @@ program
|
|
|
468
482
|
console.log('');
|
|
469
483
|
break;
|
|
470
484
|
}
|
|
471
|
-
|
|
485
|
+
|
|
472
486
|
case 'list': {
|
|
473
487
|
console.log(chalk.bold('\n🎯 Registered Goals\n'));
|
|
474
|
-
|
|
488
|
+
|
|
475
489
|
const goals = Array.from(tracker.goals.values());
|
|
476
|
-
|
|
490
|
+
|
|
477
491
|
if (options.format === 'json') {
|
|
478
|
-
console.log(
|
|
492
|
+
console.log(
|
|
493
|
+
JSON.stringify(
|
|
494
|
+
goals.map(g => g.toJSON()),
|
|
495
|
+
null,
|
|
496
|
+
2
|
|
497
|
+
)
|
|
498
|
+
);
|
|
479
499
|
} else if (goals.length === 0) {
|
|
480
500
|
console.log(chalk.yellow(' No goals registered'));
|
|
481
501
|
} else {
|
|
482
502
|
for (const goal of goals) {
|
|
483
|
-
const statusIcon = goal.isComplete()
|
|
484
|
-
|
|
485
|
-
|
|
503
|
+
const statusIcon = goal.isComplete()
|
|
504
|
+
? chalk.green('✓')
|
|
505
|
+
: goal.status === 'in-progress'
|
|
506
|
+
? chalk.yellow('◐')
|
|
507
|
+
: chalk.dim('○');
|
|
486
508
|
console.log(` ${statusIcon} ${chalk.cyan(goal.name)} (${goal.id})`);
|
|
487
|
-
console.log(
|
|
509
|
+
console.log(
|
|
510
|
+
chalk.dim(
|
|
511
|
+
` Progress: ${(goal.progress * 100).toFixed(0)}% | Priority: ${goal.priority} | Status: ${goal.status}`
|
|
512
|
+
)
|
|
513
|
+
);
|
|
488
514
|
}
|
|
489
515
|
}
|
|
490
516
|
console.log('');
|
|
491
517
|
break;
|
|
492
518
|
}
|
|
493
|
-
|
|
519
|
+
|
|
494
520
|
case 'update': {
|
|
495
521
|
if (!options.id) {
|
|
496
522
|
console.error(chalk.red('Error: --id is required'));
|
|
497
523
|
process.exit(1);
|
|
498
524
|
}
|
|
499
|
-
|
|
525
|
+
|
|
500
526
|
if (options.progress) {
|
|
501
527
|
tracker.updateProgress(options.id, parseFloat(options.progress));
|
|
502
|
-
console.log(
|
|
528
|
+
console.log(
|
|
529
|
+
chalk.green(
|
|
530
|
+
`\n✓ Goal ${options.id} updated to ${(parseFloat(options.progress) * 100).toFixed(0)}% progress\n`
|
|
531
|
+
)
|
|
532
|
+
);
|
|
503
533
|
}
|
|
504
534
|
break;
|
|
505
535
|
}
|
|
506
|
-
|
|
536
|
+
|
|
507
537
|
case 'status': {
|
|
508
538
|
console.log(chalk.bold('\n📊 Goal Tracking Status\n'));
|
|
509
539
|
console.log(` Goals: ${tracker.goals.size}`);
|
|
510
|
-
console.log(
|
|
540
|
+
console.log(
|
|
541
|
+
` Tracking: ${tracker.isTracking ? chalk.green('Active') : chalk.dim('Inactive')}`
|
|
542
|
+
);
|
|
511
543
|
console.log('');
|
|
512
544
|
break;
|
|
513
545
|
}
|
|
514
|
-
|
|
546
|
+
|
|
515
547
|
default:
|
|
516
548
|
console.error(chalk.red(`Unknown action: ${action}`));
|
|
517
549
|
console.log('Available actions: list, add, update, remove, status');
|
|
518
550
|
process.exit(1);
|
|
519
551
|
}
|
|
520
|
-
|
|
521
552
|
} catch (error) {
|
|
522
553
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
523
554
|
process.exit(1);
|
|
@@ -534,10 +565,10 @@ program
|
|
|
534
565
|
.option('--context <json>', 'Execution context as JSON')
|
|
535
566
|
.option('-o, --output <file>', 'Output file for alternatives')
|
|
536
567
|
.option('--format <type>', 'Output format (text|json)', 'text')
|
|
537
|
-
.action(async
|
|
568
|
+
.action(async options => {
|
|
538
569
|
try {
|
|
539
570
|
console.log(chalk.bold('\n🔄 Replanning Analysis\n'));
|
|
540
|
-
|
|
571
|
+
|
|
541
572
|
let plan;
|
|
542
573
|
if (options.planFile) {
|
|
543
574
|
plan = await fs.readJson(options.planFile);
|
|
@@ -550,40 +581,50 @@ program
|
|
|
550
581
|
name: 'Demo Plan',
|
|
551
582
|
tasks: [
|
|
552
583
|
{ id: 't1', skill: 'requirements-analyst', name: 'Analyze Requirements' },
|
|
553
|
-
{
|
|
554
|
-
|
|
555
|
-
|
|
584
|
+
{
|
|
585
|
+
id: 't2',
|
|
586
|
+
skill: 'system-architect',
|
|
587
|
+
name: 'Design Architecture',
|
|
588
|
+
dependencies: ['t1'],
|
|
589
|
+
},
|
|
590
|
+
{ id: 't3', skill: 'code-generator', name: 'Generate Code', dependencies: ['t2'] },
|
|
591
|
+
],
|
|
556
592
|
};
|
|
557
|
-
console.log(
|
|
593
|
+
console.log(
|
|
594
|
+
chalk.dim(' Using demo plan (provide --plan or --plan-file for custom plan)\n')
|
|
595
|
+
);
|
|
558
596
|
}
|
|
559
|
-
|
|
597
|
+
|
|
560
598
|
const engine = new ReplanningEngine();
|
|
561
599
|
const normalized = engine.normalizePlan(plan);
|
|
562
|
-
|
|
600
|
+
|
|
563
601
|
console.log(chalk.bold(' Plan:'));
|
|
564
602
|
console.log(` ID: ${chalk.cyan(normalized.id)}`);
|
|
565
603
|
console.log(` Tasks: ${normalized.tasks.length}`);
|
|
566
|
-
|
|
604
|
+
|
|
567
605
|
// Show tasks
|
|
568
606
|
for (const task of normalized.tasks) {
|
|
569
607
|
console.log(chalk.dim(` - ${task.name || task.skill} (${task.id})`));
|
|
570
608
|
}
|
|
571
|
-
|
|
609
|
+
|
|
572
610
|
console.log(chalk.bold('\n Analysis:'));
|
|
573
611
|
console.log(` Trigger: ${chalk.yellow(options.trigger)}`);
|
|
574
612
|
console.log(` Status: ${chalk.green('Ready for replanning')}`);
|
|
575
|
-
|
|
613
|
+
|
|
576
614
|
if (options.output) {
|
|
577
|
-
await fs.writeJson(
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
615
|
+
await fs.writeJson(
|
|
616
|
+
options.output,
|
|
617
|
+
{
|
|
618
|
+
plan: normalized,
|
|
619
|
+
trigger: options.trigger,
|
|
620
|
+
timestamp: new Date().toISOString(),
|
|
621
|
+
},
|
|
622
|
+
{ spaces: 2 }
|
|
623
|
+
);
|
|
582
624
|
console.log(chalk.dim(`\n Output written to: ${options.output}`));
|
|
583
625
|
}
|
|
584
|
-
|
|
626
|
+
|
|
585
627
|
console.log('');
|
|
586
|
-
|
|
587
628
|
} catch (error) {
|
|
588
629
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
589
630
|
process.exit(1);
|
|
@@ -598,12 +639,12 @@ program
|
|
|
598
639
|
.option('--reason <reason>', 'Modification reason (time|resource|dependency|priority)', 'time')
|
|
599
640
|
.option('--approve', 'Auto-approve modification')
|
|
600
641
|
.option('-f, --format <type>', 'Output format (text|json)', 'text')
|
|
601
|
-
.action(async
|
|
642
|
+
.action(async options => {
|
|
602
643
|
try {
|
|
603
644
|
console.log(chalk.bold('\n🔧 Adaptive Goal Modification\n'));
|
|
604
|
-
|
|
645
|
+
|
|
605
646
|
const modifier = new AdaptiveGoalModifier({ requireApproval: !options.approve });
|
|
606
|
-
|
|
647
|
+
|
|
607
648
|
// Demo goal if no ID provided
|
|
608
649
|
const goal = modifier.registerGoal({
|
|
609
650
|
id: options.id || 'demo-goal',
|
|
@@ -612,48 +653,51 @@ program
|
|
|
612
653
|
targetDate: new Date(Date.now() + 86400000 * 7).toISOString(),
|
|
613
654
|
deliverables: [
|
|
614
655
|
{ id: 'd1', name: 'Core Feature', priority: 'critical' },
|
|
615
|
-
{ id: 'd2', name: 'Documentation', priority: 'normal' }
|
|
616
|
-
]
|
|
656
|
+
{ id: 'd2', name: 'Documentation', priority: 'normal' },
|
|
657
|
+
],
|
|
617
658
|
});
|
|
618
|
-
|
|
659
|
+
|
|
619
660
|
const reasonMap = {
|
|
620
661
|
time: ModificationReason.TIME_CONSTRAINT,
|
|
621
662
|
resource: ModificationReason.RESOURCE_CONSTRAINT,
|
|
622
663
|
dependency: ModificationReason.DEPENDENCY_FAILURE,
|
|
623
|
-
priority: ModificationReason.PRIORITY_SHIFT
|
|
664
|
+
priority: ModificationReason.PRIORITY_SHIFT,
|
|
624
665
|
};
|
|
625
|
-
|
|
666
|
+
|
|
626
667
|
const trigger = { reason: reasonMap[options.reason] || ModificationReason.TIME_CONSTRAINT };
|
|
627
|
-
|
|
668
|
+
|
|
628
669
|
console.log(chalk.bold(' Goal:'));
|
|
629
670
|
console.log(` ID: ${chalk.cyan(goal.id)}`);
|
|
630
671
|
console.log(` Name: ${goal.name}`);
|
|
631
672
|
console.log(` Priority: ${goal.priority}`);
|
|
632
|
-
|
|
673
|
+
|
|
633
674
|
console.log(chalk.bold('\n Trigger:'));
|
|
634
675
|
console.log(` Reason: ${chalk.yellow(trigger.reason)}`);
|
|
635
|
-
|
|
676
|
+
|
|
636
677
|
const result = await modifier.triggerModification(goal.id, trigger);
|
|
637
|
-
|
|
678
|
+
|
|
638
679
|
console.log(chalk.bold('\n Result:'));
|
|
639
|
-
console.log(
|
|
640
|
-
|
|
680
|
+
console.log(
|
|
681
|
+
` Status: ${result.status === 'applied' ? chalk.green(result.status) : chalk.yellow(result.status)}`
|
|
682
|
+
);
|
|
683
|
+
|
|
641
684
|
if (result.modification) {
|
|
642
685
|
console.log(` Strategy: ${chalk.cyan(result.modification.strategy.type)}`);
|
|
643
686
|
console.log(` Confidence: ${(result.modification.confidence * 100).toFixed(0)}%`);
|
|
644
|
-
|
|
687
|
+
|
|
645
688
|
if (result.modification.impact) {
|
|
646
|
-
console.log(
|
|
689
|
+
console.log(
|
|
690
|
+
` Impact Score: ${(result.modification.impact.totalScore * 100).toFixed(0)}%`
|
|
691
|
+
);
|
|
647
692
|
console.log(` Risk Level: ${result.modification.impact.riskLevel}`);
|
|
648
693
|
}
|
|
649
694
|
}
|
|
650
|
-
|
|
695
|
+
|
|
651
696
|
if (options.format === 'json') {
|
|
652
697
|
console.log('\n' + JSON.stringify(result, null, 2));
|
|
653
698
|
}
|
|
654
|
-
|
|
699
|
+
|
|
655
700
|
console.log('');
|
|
656
|
-
|
|
657
701
|
} catch (error) {
|
|
658
702
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
659
703
|
process.exit(1);
|
|
@@ -667,12 +711,12 @@ program
|
|
|
667
711
|
.option('-p, --path <json>', 'Execution path as JSON')
|
|
668
712
|
.option('-f, --path-file <file>', 'Path file')
|
|
669
713
|
.option('--format <type>', 'Output format (text|json)', 'text')
|
|
670
|
-
.action(async
|
|
714
|
+
.action(async options => {
|
|
671
715
|
try {
|
|
672
716
|
console.log(chalk.bold('\n⚡ Path Optimization Analysis\n'));
|
|
673
|
-
|
|
717
|
+
|
|
674
718
|
const optimizer = new ProactivePathOptimizer(null);
|
|
675
|
-
|
|
719
|
+
|
|
676
720
|
let pathData;
|
|
677
721
|
if (options.pathFile) {
|
|
678
722
|
pathData = await fs.readJson(options.pathFile);
|
|
@@ -685,40 +729,48 @@ program
|
|
|
685
729
|
{ id: 't1', name: 'Task 1', estimatedDuration: 10000, dependencies: [] },
|
|
686
730
|
{ id: 't2', name: 'Task 2', estimatedDuration: 5000, dependencies: [] },
|
|
687
731
|
{ id: 't3', name: 'Task 3', estimatedDuration: 8000, dependencies: ['t1'] },
|
|
688
|
-
{ id: 't4', name: 'Task 4', estimatedDuration: 3000, dependencies: ['t2', 't3'] }
|
|
689
|
-
]
|
|
732
|
+
{ id: 't4', name: 'Task 4', estimatedDuration: 3000, dependencies: ['t2', 't3'] },
|
|
733
|
+
],
|
|
690
734
|
};
|
|
691
735
|
console.log(chalk.dim(' Using demo path (provide --path or --path-file for custom)\n'));
|
|
692
736
|
}
|
|
693
|
-
|
|
737
|
+
|
|
694
738
|
const metrics = optimizer.calculatePathMetrics(pathData);
|
|
695
|
-
|
|
739
|
+
|
|
696
740
|
console.log(chalk.bold(' Path Metrics:'));
|
|
697
741
|
console.log(` Estimated Time: ${chalk.cyan(metrics.estimatedTime + 'ms')}`);
|
|
698
|
-
console.log(
|
|
742
|
+
console.log(
|
|
743
|
+
` Parallelization Factor: ${(metrics.parallelizationFactor * 100).toFixed(0)}%`
|
|
744
|
+
);
|
|
699
745
|
console.log(` Risk Score: ${metrics.riskScore.toFixed(2)}`);
|
|
700
746
|
console.log(` Overall Score: ${metrics.getScore().toFixed(2)}`);
|
|
701
|
-
|
|
747
|
+
|
|
702
748
|
console.log(chalk.bold('\n Tasks:'));
|
|
703
749
|
for (const task of pathData.pending) {
|
|
704
750
|
const deps = task.dependencies?.length ? ` -> [${task.dependencies.join(', ')}]` : '';
|
|
705
751
|
console.log(chalk.dim(` - ${task.name} (${task.id})${deps}`));
|
|
706
752
|
}
|
|
707
|
-
|
|
753
|
+
|
|
708
754
|
if (options.format === 'json') {
|
|
709
|
-
console.log(
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
755
|
+
console.log(
|
|
756
|
+
'\n' +
|
|
757
|
+
JSON.stringify(
|
|
758
|
+
{
|
|
759
|
+
path: pathData,
|
|
760
|
+
metrics: {
|
|
761
|
+
estimatedTime: metrics.estimatedTime,
|
|
762
|
+
parallelizationFactor: metrics.parallelizationFactor,
|
|
763
|
+
riskScore: metrics.riskScore,
|
|
764
|
+
score: metrics.getScore(),
|
|
765
|
+
},
|
|
766
|
+
},
|
|
767
|
+
null,
|
|
768
|
+
2
|
|
769
|
+
)
|
|
770
|
+
);
|
|
718
771
|
}
|
|
719
|
-
|
|
772
|
+
|
|
720
773
|
console.log('');
|
|
721
|
-
|
|
722
774
|
} catch (error) {
|
|
723
775
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
724
776
|
process.exit(1);
|
|
@@ -730,13 +782,13 @@ program
|
|
|
730
782
|
.command('status')
|
|
731
783
|
.description('Show orchestration engine status')
|
|
732
784
|
.option('-f, --format <type>', 'Output format (text|json)', 'text')
|
|
733
|
-
.action(async
|
|
785
|
+
.action(async options => {
|
|
734
786
|
try {
|
|
735
787
|
console.log(chalk.bold('\n📊 Orchestration Status\n'));
|
|
736
|
-
|
|
788
|
+
|
|
737
789
|
const engine = await createEngine(process.cwd());
|
|
738
790
|
const status = engine.getStatus();
|
|
739
|
-
|
|
791
|
+
|
|
740
792
|
if (options.format === 'json') {
|
|
741
793
|
console.log(JSON.stringify(status, null, 2));
|
|
742
794
|
} else {
|
|
@@ -745,13 +797,13 @@ program
|
|
|
745
797
|
for (const p of status.patterns) {
|
|
746
798
|
console.log(` - ${p}`);
|
|
747
799
|
}
|
|
748
|
-
|
|
800
|
+
|
|
749
801
|
console.log(chalk.bold('\nSkills:'));
|
|
750
802
|
console.log(` Registered: ${chalk.cyan(status.skills.length)}`);
|
|
751
|
-
|
|
803
|
+
|
|
752
804
|
console.log(chalk.bold('\nActive Executions:'));
|
|
753
805
|
console.log(` Count: ${chalk.cyan(status.activeExecutions)}`);
|
|
754
|
-
|
|
806
|
+
|
|
755
807
|
if (status.contexts.length > 0) {
|
|
756
808
|
console.log(chalk.bold('\nActive Contexts:'));
|
|
757
809
|
for (const ctx of status.contexts) {
|
|
@@ -759,9 +811,8 @@ program
|
|
|
759
811
|
}
|
|
760
812
|
}
|
|
761
813
|
}
|
|
762
|
-
|
|
814
|
+
|
|
763
815
|
console.log('');
|
|
764
|
-
|
|
765
816
|
} catch (error) {
|
|
766
817
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
767
818
|
process.exit(1);
|
|
@@ -776,22 +827,26 @@ program
|
|
|
776
827
|
.requiredOption('--from <agent>', 'Source agent')
|
|
777
828
|
.requiredOption('--to <agent>', 'Target agent')
|
|
778
829
|
.option('-s, --skills <skills...>', 'Available skills/agents')
|
|
779
|
-
.option(
|
|
830
|
+
.option(
|
|
831
|
+
'--filter <filter>',
|
|
832
|
+
'Input filter (removeAllTools|userMessagesOnly|lastN|summarize|keepAll)',
|
|
833
|
+
'keepAll'
|
|
834
|
+
)
|
|
780
835
|
.option('--filter-n <n>', 'N value for lastN filter', '10')
|
|
781
836
|
.option('--reason <reason>', 'Handoff reason')
|
|
782
837
|
.option('--priority <priority>', 'Priority (low|normal|high|urgent)', 'normal')
|
|
783
838
|
.option('-i, --input <json>', 'Additional input data as JSON')
|
|
784
839
|
.option('-f, --format <type>', 'Output format (text|json)', 'text')
|
|
785
|
-
.action(async
|
|
840
|
+
.action(async options => {
|
|
786
841
|
try {
|
|
787
842
|
console.log(chalk.bold('\n🤝 Handoff Pattern - Agent Delegation\n'));
|
|
788
843
|
console.log(chalk.dim(`From: ${options.from} → To: ${options.to}\n`));
|
|
789
|
-
|
|
844
|
+
|
|
790
845
|
const engine = await createEngine(process.cwd());
|
|
791
|
-
|
|
846
|
+
|
|
792
847
|
// Parse additional input
|
|
793
848
|
const additionalInput = options.input ? JSON.parse(options.input) : {};
|
|
794
|
-
|
|
849
|
+
|
|
795
850
|
// Select input filter
|
|
796
851
|
let inputFilter;
|
|
797
852
|
switch (options.filter) {
|
|
@@ -810,68 +865,74 @@ program
|
|
|
810
865
|
default:
|
|
811
866
|
inputFilter = HandoffFilters.keepAll;
|
|
812
867
|
}
|
|
813
|
-
|
|
868
|
+
|
|
814
869
|
// Create handoff configuration
|
|
815
870
|
const handoffConfig = new HandoffConfig({
|
|
816
871
|
targetAgent: options.to,
|
|
817
872
|
reason: options.reason || `Handoff from ${options.from} to ${options.to}`,
|
|
818
873
|
inputFilter,
|
|
819
|
-
priority: options.priority
|
|
874
|
+
priority: options.priority,
|
|
820
875
|
});
|
|
821
|
-
|
|
876
|
+
|
|
822
877
|
// Prepare agent definitions
|
|
823
878
|
const agents = [];
|
|
824
879
|
const skills = options.skills || [options.from, options.to];
|
|
825
|
-
|
|
880
|
+
|
|
826
881
|
for (const skillName of skills) {
|
|
827
882
|
const skill = engine.getSkill(skillName);
|
|
828
883
|
if (skill) {
|
|
829
884
|
agents.push({
|
|
830
885
|
name: skillName,
|
|
831
886
|
...skill,
|
|
832
|
-
handoffs: skillName === options.from ? [handoffConfig] : []
|
|
887
|
+
handoffs: skillName === options.from ? [handoffConfig] : [],
|
|
833
888
|
});
|
|
834
889
|
} else {
|
|
835
890
|
// Create placeholder agent
|
|
836
891
|
agents.push({
|
|
837
892
|
name: skillName,
|
|
838
893
|
description: `${skillName} agent`,
|
|
839
|
-
execute: async
|
|
840
|
-
handoffs: skillName === options.from ? [handoffConfig] : []
|
|
894
|
+
execute: async input => ({ agent: skillName, input, executed: true }),
|
|
895
|
+
handoffs: skillName === options.from ? [handoffConfig] : [],
|
|
841
896
|
});
|
|
842
897
|
}
|
|
843
898
|
}
|
|
844
|
-
|
|
899
|
+
|
|
845
900
|
// Create target agents array as required by HandoffPattern
|
|
846
901
|
const targetAgent = agents.find(a => a.name === options.to) || {
|
|
847
902
|
name: options.to,
|
|
848
903
|
description: `${options.to} agent`,
|
|
849
|
-
execute: async
|
|
904
|
+
execute: async input => ({ agent: options.to, input, executed: true }),
|
|
850
905
|
};
|
|
851
|
-
|
|
906
|
+
|
|
852
907
|
const context = await engine.execute(PatternType.HANDOFF, {
|
|
853
908
|
task: options.task,
|
|
854
909
|
input: {
|
|
855
910
|
sourceAgent: options.from,
|
|
856
|
-
targetAgents: [targetAgent],
|
|
911
|
+
targetAgents: [targetAgent], // Array of target agents
|
|
857
912
|
agents,
|
|
858
913
|
reason: options.reason || `Handoff from ${options.from} to ${options.to}`,
|
|
859
|
-
...additionalInput
|
|
860
|
-
}
|
|
914
|
+
...additionalInput,
|
|
915
|
+
},
|
|
861
916
|
});
|
|
862
|
-
|
|
917
|
+
|
|
863
918
|
if (options.format === 'json') {
|
|
864
|
-
console.log(
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
919
|
+
console.log(
|
|
920
|
+
JSON.stringify(
|
|
921
|
+
{
|
|
922
|
+
success: context.status === ExecutionStatus.COMPLETED,
|
|
923
|
+
sourceAgent: options.from,
|
|
924
|
+
targetAgent: options.to,
|
|
925
|
+
result: context.output,
|
|
926
|
+
handoffChain: context.output?.handoffChain || [],
|
|
927
|
+
},
|
|
928
|
+
null,
|
|
929
|
+
2
|
|
930
|
+
)
|
|
931
|
+
);
|
|
871
932
|
} else {
|
|
872
933
|
if (context.status === ExecutionStatus.COMPLETED) {
|
|
873
934
|
console.log(chalk.green('✓ Handoff completed successfully\n'));
|
|
874
|
-
|
|
935
|
+
|
|
875
936
|
if (context.output?.handoffChain) {
|
|
876
937
|
console.log(chalk.bold('Handoff Chain:'));
|
|
877
938
|
for (const hop of context.output.handoffChain) {
|
|
@@ -882,7 +943,7 @@ program
|
|
|
882
943
|
}
|
|
883
944
|
console.log('');
|
|
884
945
|
}
|
|
885
|
-
|
|
946
|
+
|
|
886
947
|
console.log(chalk.bold('Result:'));
|
|
887
948
|
console.log(` Final Agent: ${chalk.cyan(context.output?.finalAgent || options.to)}`);
|
|
888
949
|
console.log(` Status: ${chalk.green('Completed')}`);
|
|
@@ -891,9 +952,8 @@ program
|
|
|
891
952
|
process.exit(1);
|
|
892
953
|
}
|
|
893
954
|
}
|
|
894
|
-
|
|
955
|
+
|
|
895
956
|
console.log('');
|
|
896
|
-
|
|
897
957
|
} catch (error) {
|
|
898
958
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
899
959
|
if (process.env.DEBUG) {
|
|
@@ -909,61 +969,85 @@ program
|
|
|
909
969
|
.description('Execute triage pattern - Classify and route request to appropriate agent')
|
|
910
970
|
.requiredOption('-m, --message <message>', 'Message/request to classify')
|
|
911
971
|
.option('-s, --skills <skills...>', 'Available agents/skills to route to')
|
|
912
|
-
.option(
|
|
972
|
+
.option(
|
|
973
|
+
'--strategy <strategy>',
|
|
974
|
+
'Classification strategy (keyword|intent|capability|hybrid|llm)',
|
|
975
|
+
'hybrid'
|
|
976
|
+
)
|
|
913
977
|
.option('--default-category <category>', 'Default category if no match', 'general')
|
|
914
978
|
.option('--threshold <threshold>', 'Confidence threshold (0-1)', '0.3')
|
|
915
979
|
.option('--auto-handoff', 'Automatically handoff to selected agent', false)
|
|
916
980
|
.option('-f, --format <type>', 'Output format (text|json)', 'text')
|
|
917
|
-
.action(async
|
|
981
|
+
.action(async options => {
|
|
918
982
|
try {
|
|
919
983
|
console.log(chalk.bold('\n🎯 Triage Pattern - Request Classification\n'));
|
|
920
|
-
console.log(
|
|
921
|
-
|
|
984
|
+
console.log(
|
|
985
|
+
chalk.dim(
|
|
986
|
+
`Message: "${options.message.substring(0, 50)}${options.message.length > 50 ? '...' : ''}"\n`
|
|
987
|
+
)
|
|
988
|
+
);
|
|
989
|
+
|
|
922
990
|
const engine = await createEngine(process.cwd());
|
|
923
|
-
|
|
991
|
+
|
|
924
992
|
// Map strategy string to enum
|
|
925
993
|
const strategyMap = {
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
994
|
+
keyword: TriageStrategy.KEYWORD,
|
|
995
|
+
intent: TriageStrategy.INTENT,
|
|
996
|
+
capability: TriageStrategy.CAPABILITY,
|
|
997
|
+
hybrid: TriageStrategy.HYBRID,
|
|
998
|
+
llm: TriageStrategy.LLM,
|
|
931
999
|
};
|
|
932
|
-
|
|
1000
|
+
|
|
933
1001
|
const strategy = strategyMap[options.strategy] || TriageStrategy.HYBRID;
|
|
934
|
-
|
|
1002
|
+
|
|
935
1003
|
// Prepare agents with capabilities
|
|
936
1004
|
const agents = [];
|
|
937
1005
|
const defaultSkills = [
|
|
938
|
-
{
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1006
|
+
{
|
|
1007
|
+
name: 'billing-agent',
|
|
1008
|
+
categories: [TriageCategory.BILLING, TriageCategory.REFUND],
|
|
1009
|
+
keywords: ['invoice', 'payment', 'refund', 'charge', 'billing'],
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
name: 'support-agent',
|
|
1013
|
+
categories: [TriageCategory.SUPPORT],
|
|
1014
|
+
keywords: ['help', 'issue', 'problem', 'broken', 'support'],
|
|
1015
|
+
},
|
|
1016
|
+
{
|
|
1017
|
+
name: 'sales-agent',
|
|
1018
|
+
categories: [TriageCategory.SALES],
|
|
1019
|
+
keywords: ['buy', 'purchase', 'pricing', 'discount', 'order'],
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
name: 'technical-agent',
|
|
1023
|
+
categories: [TriageCategory.TECHNICAL],
|
|
1024
|
+
keywords: ['api', 'bug', 'error', 'integration', 'code'],
|
|
1025
|
+
},
|
|
1026
|
+
{ name: 'general-agent', categories: [TriageCategory.GENERAL], keywords: [] },
|
|
943
1027
|
];
|
|
944
|
-
|
|
1028
|
+
|
|
945
1029
|
const skillNames = options.skills || defaultSkills.map(s => s.name);
|
|
946
|
-
|
|
1030
|
+
|
|
947
1031
|
for (const skillName of skillNames) {
|
|
948
1032
|
const skill = engine.getSkill(skillName);
|
|
949
1033
|
const defaultSkill = defaultSkills.find(s => s.name === skillName);
|
|
950
|
-
|
|
1034
|
+
|
|
951
1035
|
const agentObj = {
|
|
952
1036
|
name: skillName,
|
|
953
1037
|
description: skill?.description || `${skillName} agent`,
|
|
954
|
-
execute: skill?.execute || (async
|
|
1038
|
+
execute: skill?.execute || (async input => ({ agent: skillName, input, executed: true })),
|
|
955
1039
|
};
|
|
956
|
-
|
|
1040
|
+
|
|
957
1041
|
agentObj.capability = new AgentCapability({
|
|
958
|
-
agent: agentObj,
|
|
1042
|
+
agent: agentObj, // Reference to agent object
|
|
959
1043
|
categories: defaultSkill?.categories || [TriageCategory.GENERAL],
|
|
960
1044
|
keywords: defaultSkill?.keywords || [],
|
|
961
|
-
priority: 1
|
|
1045
|
+
priority: 1,
|
|
962
1046
|
});
|
|
963
|
-
|
|
1047
|
+
|
|
964
1048
|
agents.push(agentObj);
|
|
965
1049
|
}
|
|
966
|
-
|
|
1050
|
+
|
|
967
1051
|
const context = await engine.execute(PatternType.TRIAGE, {
|
|
968
1052
|
task: 'Classify and route request',
|
|
969
1053
|
input: {
|
|
@@ -972,24 +1056,30 @@ program
|
|
|
972
1056
|
strategy,
|
|
973
1057
|
defaultCategory: options.defaultCategory.toUpperCase(),
|
|
974
1058
|
confidenceThreshold: parseFloat(options.threshold),
|
|
975
|
-
enableHandoff: options.autoHandoff
|
|
976
|
-
}
|
|
1059
|
+
enableHandoff: options.autoHandoff, // Only handoff if explicitly requested
|
|
1060
|
+
},
|
|
977
1061
|
});
|
|
978
|
-
|
|
1062
|
+
|
|
979
1063
|
if (options.format === 'json') {
|
|
980
|
-
console.log(
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1064
|
+
console.log(
|
|
1065
|
+
JSON.stringify(
|
|
1066
|
+
{
|
|
1067
|
+
success: context.status === ExecutionStatus.COMPLETED,
|
|
1068
|
+
classification: context.output?.classification,
|
|
1069
|
+
selectedAgent: context.output?.selectedAgent,
|
|
1070
|
+
confidence: context.output?.confidence,
|
|
1071
|
+
category: context.output?.category,
|
|
1072
|
+
},
|
|
1073
|
+
null,
|
|
1074
|
+
2
|
|
1075
|
+
)
|
|
1076
|
+
);
|
|
987
1077
|
} else {
|
|
988
1078
|
if (context.status === ExecutionStatus.COMPLETED) {
|
|
989
1079
|
console.log(chalk.green('✓ Triage completed\n'));
|
|
990
|
-
|
|
1080
|
+
|
|
991
1081
|
const classification = context.output?.classification || context.output;
|
|
992
|
-
|
|
1082
|
+
|
|
993
1083
|
console.log(chalk.bold('Classification Result:'));
|
|
994
1084
|
console.log(` Category: ${chalk.cyan(classification?.category || 'N/A')}`);
|
|
995
1085
|
console.log(` Confidence: ${formatConfidence(classification?.confidence || 0)}`);
|
|
@@ -997,13 +1087,13 @@ program
|
|
|
997
1087
|
console.log(` Reasoning: ${chalk.dim(classification.reasoning)}`);
|
|
998
1088
|
}
|
|
999
1089
|
console.log('');
|
|
1000
|
-
|
|
1090
|
+
|
|
1001
1091
|
if (context.output?.selectedAgent) {
|
|
1002
1092
|
console.log(chalk.bold('Routing Decision:'));
|
|
1003
1093
|
console.log(` Selected Agent: ${chalk.yellow(context.output.selectedAgent)}`);
|
|
1004
1094
|
console.log(` Agent Score: ${(context.output.agentScore || 0).toFixed(2)}`);
|
|
1005
1095
|
}
|
|
1006
|
-
|
|
1096
|
+
|
|
1007
1097
|
if (options.autoHandoff && context.output?.handoffResult) {
|
|
1008
1098
|
console.log('');
|
|
1009
1099
|
console.log(chalk.bold('Handoff Result:'));
|
|
@@ -1014,9 +1104,8 @@ program
|
|
|
1014
1104
|
process.exit(1);
|
|
1015
1105
|
}
|
|
1016
1106
|
}
|
|
1017
|
-
|
|
1107
|
+
|
|
1018
1108
|
console.log('');
|
|
1019
|
-
|
|
1020
1109
|
} catch (error) {
|
|
1021
1110
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
1022
1111
|
if (process.env.DEBUG) {
|
|
@@ -1031,16 +1120,16 @@ program
|
|
|
1031
1120
|
.command('triage-categories')
|
|
1032
1121
|
.description('List available triage categories')
|
|
1033
1122
|
.option('-f, --format <type>', 'Output format (text|json)', 'text')
|
|
1034
|
-
.action(async
|
|
1123
|
+
.action(async options => {
|
|
1035
1124
|
try {
|
|
1036
1125
|
console.log(chalk.bold('\n📋 Triage Categories\n'));
|
|
1037
|
-
|
|
1126
|
+
|
|
1038
1127
|
const categories = Object.entries(TriageCategory).map(([key, value]) => ({
|
|
1039
1128
|
key,
|
|
1040
1129
|
value,
|
|
1041
|
-
description: getCategoryDescription(value)
|
|
1130
|
+
description: getCategoryDescription(value),
|
|
1042
1131
|
}));
|
|
1043
|
-
|
|
1132
|
+
|
|
1044
1133
|
if (options.format === 'json') {
|
|
1045
1134
|
console.log(JSON.stringify(categories, null, 2));
|
|
1046
1135
|
} else {
|
|
@@ -1048,9 +1137,8 @@ program
|
|
|
1048
1137
|
console.log(` ${chalk.cyan(cat.value.padEnd(12))} - ${cat.description}`);
|
|
1049
1138
|
}
|
|
1050
1139
|
}
|
|
1051
|
-
|
|
1140
|
+
|
|
1052
1141
|
console.log('');
|
|
1053
|
-
|
|
1054
1142
|
} catch (error) {
|
|
1055
1143
|
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
1056
1144
|
process.exit(1);
|
|
@@ -1069,7 +1157,7 @@ function getCategoryDescription(category) {
|
|
|
1069
1157
|
[TriageCategory.REFUND]: 'Refund requests and processing',
|
|
1070
1158
|
[TriageCategory.GENERAL]: 'General inquiries, catch-all category',
|
|
1071
1159
|
[TriageCategory.ESCALATION]: 'Escalated issues requiring supervisor attention',
|
|
1072
|
-
[TriageCategory.UNKNOWN]: 'Unclassified requests'
|
|
1160
|
+
[TriageCategory.UNKNOWN]: 'Unclassified requests',
|
|
1073
1161
|
};
|
|
1074
1162
|
return descriptions[category] || 'Unknown category';
|
|
1075
1163
|
}
|