musubi-sdd 3.7.1 → 3.9.0

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 CHANGED
@@ -71,14 +71,14 @@ musubi init --windsurf # Windsurf IDE
71
71
 
72
72
  ---
73
73
 
74
- ## 📊 v2.1.0 の新機能
75
-
76
- - 🔄 **ワークフローエンジン** - ステージ管理とメトリクス収集の新CLI `musubi-workflow`
77
- - 📊 **メトリクス収集** - ステージごとの所要時間、イテレーション回数、フィードバックループを追跡
78
- - 🔬 **Spike/PoCステージ** - 要件定義前の調査・プロトタイピング用ステージ0
79
- - 👀 **コードレビューステージ** - 実装とテストの間のステージ5.5
80
- - 🔄 **振り返りステージ** - 継続的改善のためのステージ9
81
- - **ステージ検証ガイド** - ステージ遷移の検証チェックリスト
74
+ ## 📊 v3.7.1 の新機能
75
+
76
+ - 🌐 **WebSocketリアルタイムGUI** - `musubi-browser`ダッシュボードでライブ更新
77
+ - 📋 **GUIクイックアクション** - 新規要件モーダル、プロジェクト検証、レポートエクスポート
78
+ - 🔄 **GitHub Actions統合** - `musubi-action`でCI/CD検証
79
+ - 🔧 **OpenAPIコンバーター** - OpenAPI 3.x/Swagger 2.xスペックをMUSUBI形式に変換
80
+ - 🌍 **多言語テンプレート** - 7言語対応(EN, JA, ZH, KO, ES, DE, FR)
81
+ - 🤖 **Ollama統合** - 9つのモデルプリセットでローカルLLMをサポート
82
82
 
83
83
  ### 以前のバージョン (v2.0.0)
84
84
 
package/README.md CHANGED
@@ -71,13 +71,14 @@ musubi init --windsurf # Windsurf IDE
71
71
 
72
72
  ---
73
73
 
74
- ## 📊 What's New in v3.6.1
75
-
76
- - 🚀 **ProactivePathOptimizer** - Continuous path optimization even during successful execution
77
- - 🎯 **GoalProgressTracker** - Real-time goal progress monitoring with milestone management
78
- - 🔄 **AdaptiveGoalModifier** - Dynamic goal adjustment based on execution context
79
- - 🛠️ **New CLI Commands** - `replan`, `goal`, `optimize`, `path` subcommands for orchestration
80
- - 📚 **Updated Agent Templates** - All 7 platforms with replanning command documentation
74
+ ## 📊 What's New in v3.7.1
75
+
76
+ - 🌐 **WebSocket Real-time GUI** - Live replanning updates with `musubi-browser` dashboard
77
+ - 📋 **GUI Quick Actions** - Modal dialog for New Requirement, Validate Project, Export Report
78
+ - 🔄 **GitHub Actions Integration** - `musubi-action` for CI/CD with MUSUBI validation
79
+ - 🔧 **OpenAPI Converter** - Convert OpenAPI 3.x/Swagger 2.x specs to MUSUBI format
80
+ - 🌍 **Multi-language Templates** - 7 language support (EN, JA, ZH, KO, ES, DE, FR)
81
+ - 🤖 **Ollama Integration** - Local LLM support with 9 model presets
81
82
 
82
83
  ### Previous (v3.6.0)
83
84
 
@@ -9,6 +9,8 @@
9
9
  * musubi-orchestrate run <pattern> --skills <skills...> # Execute pattern with skills
10
10
  * musubi-orchestrate auto <task> # Auto-select and execute skill
11
11
  * musubi-orchestrate sequential --skills <skills...> # Execute skills sequentially
12
+ * musubi-orchestrate handoff --from <agent> --to <agent> # Delegate to another agent
13
+ * musubi-orchestrate triage --message <msg> # Classify and route request
12
14
  * musubi-orchestrate list-patterns # List available patterns
13
15
  * musubi-orchestrate list-skills # List available skills
14
16
  * musubi-orchestrate status # Show orchestration status
@@ -23,7 +25,15 @@ const {
23
25
  createOrchestrationEngine,
24
26
  PatternType,
25
27
  ExecutionStatus,
26
- Priority
28
+ Priority,
29
+ HandoffPattern,
30
+ HandoffFilters,
31
+ HandoffConfig,
32
+ handoff,
33
+ TriagePattern,
34
+ TriageCategory,
35
+ TriageStrategy,
36
+ AgentCapability
27
37
  } = require('../src/orchestration');
28
38
 
29
39
  const {
@@ -758,6 +768,312 @@ program
758
768
  }
759
769
  });
760
770
 
771
+ // Handoff command - Explicit agent-to-agent delegation
772
+ program
773
+ .command('handoff')
774
+ .description('Execute handoff pattern - Delegate task to another agent')
775
+ .requiredOption('-t, --task <task>', 'Task to handoff')
776
+ .requiredOption('--from <agent>', 'Source agent')
777
+ .requiredOption('--to <agent>', 'Target agent')
778
+ .option('-s, --skills <skills...>', 'Available skills/agents')
779
+ .option('--filter <filter>', 'Input filter (removeAllTools|userMessagesOnly|lastN|summarize|keepAll)', 'keepAll')
780
+ .option('--filter-n <n>', 'N value for lastN filter', '10')
781
+ .option('--reason <reason>', 'Handoff reason')
782
+ .option('--priority <priority>', 'Priority (low|normal|high|urgent)', 'normal')
783
+ .option('-i, --input <json>', 'Additional input data as JSON')
784
+ .option('-f, --format <type>', 'Output format (text|json)', 'text')
785
+ .action(async (options) => {
786
+ try {
787
+ console.log(chalk.bold('\n🤝 Handoff Pattern - Agent Delegation\n'));
788
+ console.log(chalk.dim(`From: ${options.from} → To: ${options.to}\n`));
789
+
790
+ const engine = await createEngine(process.cwd());
791
+
792
+ // Parse additional input
793
+ const additionalInput = options.input ? JSON.parse(options.input) : {};
794
+
795
+ // Select input filter
796
+ let inputFilter;
797
+ switch (options.filter) {
798
+ case 'removeAllTools':
799
+ inputFilter = HandoffFilters.removeAllTools;
800
+ break;
801
+ case 'userMessagesOnly':
802
+ inputFilter = HandoffFilters.userMessagesOnly;
803
+ break;
804
+ case 'lastN':
805
+ inputFilter = HandoffFilters.lastN(parseInt(options.filterN, 10));
806
+ break;
807
+ case 'summarize':
808
+ inputFilter = HandoffFilters.summarize;
809
+ break;
810
+ default:
811
+ inputFilter = HandoffFilters.keepAll;
812
+ }
813
+
814
+ // Create handoff configuration
815
+ const handoffConfig = new HandoffConfig({
816
+ targetAgent: options.to,
817
+ reason: options.reason || `Handoff from ${options.from} to ${options.to}`,
818
+ inputFilter,
819
+ priority: options.priority
820
+ });
821
+
822
+ // Prepare agent definitions
823
+ const agents = [];
824
+ const skills = options.skills || [options.from, options.to];
825
+
826
+ for (const skillName of skills) {
827
+ const skill = engine.getSkill(skillName);
828
+ if (skill) {
829
+ agents.push({
830
+ name: skillName,
831
+ ...skill,
832
+ handoffs: skillName === options.from ? [handoffConfig] : []
833
+ });
834
+ } else {
835
+ // Create placeholder agent
836
+ agents.push({
837
+ name: skillName,
838
+ description: `${skillName} agent`,
839
+ execute: async (input) => ({ agent: skillName, input, executed: true }),
840
+ handoffs: skillName === options.from ? [handoffConfig] : []
841
+ });
842
+ }
843
+ }
844
+
845
+ // Create target agents array as required by HandoffPattern
846
+ const targetAgent = agents.find(a => a.name === options.to) || {
847
+ name: options.to,
848
+ description: `${options.to} agent`,
849
+ execute: async (input) => ({ agent: options.to, input, executed: true })
850
+ };
851
+
852
+ const context = await engine.execute(PatternType.HANDOFF, {
853
+ task: options.task,
854
+ input: {
855
+ sourceAgent: options.from,
856
+ targetAgents: [targetAgent], // Array of target agents
857
+ agents,
858
+ reason: options.reason || `Handoff from ${options.from} to ${options.to}`,
859
+ ...additionalInput
860
+ }
861
+ });
862
+
863
+ if (options.format === 'json') {
864
+ console.log(JSON.stringify({
865
+ success: context.status === ExecutionStatus.COMPLETED,
866
+ sourceAgent: options.from,
867
+ targetAgent: options.to,
868
+ result: context.output,
869
+ handoffChain: context.output?.handoffChain || []
870
+ }, null, 2));
871
+ } else {
872
+ if (context.status === ExecutionStatus.COMPLETED) {
873
+ console.log(chalk.green('✓ Handoff completed successfully\n'));
874
+
875
+ if (context.output?.handoffChain) {
876
+ console.log(chalk.bold('Handoff Chain:'));
877
+ for (const hop of context.output.handoffChain) {
878
+ console.log(` ${chalk.cyan(hop.from)} → ${chalk.yellow(hop.to)}`);
879
+ if (hop.reason) {
880
+ console.log(chalk.dim(` Reason: ${hop.reason}`));
881
+ }
882
+ }
883
+ console.log('');
884
+ }
885
+
886
+ console.log(chalk.bold('Result:'));
887
+ console.log(` Final Agent: ${chalk.cyan(context.output?.finalAgent || options.to)}`);
888
+ console.log(` Status: ${chalk.green('Completed')}`);
889
+ } else {
890
+ console.log(chalk.red(`✗ Handoff failed: ${context.error}\n`));
891
+ process.exit(1);
892
+ }
893
+ }
894
+
895
+ console.log('');
896
+
897
+ } catch (error) {
898
+ console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
899
+ if (process.env.DEBUG) {
900
+ console.error(error.stack);
901
+ }
902
+ process.exit(1);
903
+ }
904
+ });
905
+
906
+ // Triage command - Request classification and routing
907
+ program
908
+ .command('triage')
909
+ .description('Execute triage pattern - Classify and route request to appropriate agent')
910
+ .requiredOption('-m, --message <message>', 'Message/request to classify')
911
+ .option('-s, --skills <skills...>', 'Available agents/skills to route to')
912
+ .option('--strategy <strategy>', 'Classification strategy (keyword|intent|capability|hybrid|llm)', 'hybrid')
913
+ .option('--default-category <category>', 'Default category if no match', 'general')
914
+ .option('--threshold <threshold>', 'Confidence threshold (0-1)', '0.3')
915
+ .option('--auto-handoff', 'Automatically handoff to selected agent', false)
916
+ .option('-f, --format <type>', 'Output format (text|json)', 'text')
917
+ .action(async (options) => {
918
+ try {
919
+ console.log(chalk.bold('\n🎯 Triage Pattern - Request Classification\n'));
920
+ console.log(chalk.dim(`Message: "${options.message.substring(0, 50)}${options.message.length > 50 ? '...' : ''}"\n`));
921
+
922
+ const engine = await createEngine(process.cwd());
923
+
924
+ // Map strategy string to enum
925
+ const strategyMap = {
926
+ 'keyword': TriageStrategy.KEYWORD,
927
+ 'intent': TriageStrategy.INTENT,
928
+ 'capability': TriageStrategy.CAPABILITY,
929
+ 'hybrid': TriageStrategy.HYBRID,
930
+ 'llm': TriageStrategy.LLM
931
+ };
932
+
933
+ const strategy = strategyMap[options.strategy] || TriageStrategy.HYBRID;
934
+
935
+ // Prepare agents with capabilities
936
+ const agents = [];
937
+ const defaultSkills = [
938
+ { name: 'billing-agent', categories: [TriageCategory.BILLING, TriageCategory.REFUND], keywords: ['invoice', 'payment', 'refund', 'charge', 'billing'] },
939
+ { name: 'support-agent', categories: [TriageCategory.SUPPORT], keywords: ['help', 'issue', 'problem', 'broken', 'support'] },
940
+ { name: 'sales-agent', categories: [TriageCategory.SALES], keywords: ['buy', 'purchase', 'pricing', 'discount', 'order'] },
941
+ { name: 'technical-agent', categories: [TriageCategory.TECHNICAL], keywords: ['api', 'bug', 'error', 'integration', 'code'] },
942
+ { name: 'general-agent', categories: [TriageCategory.GENERAL], keywords: [] }
943
+ ];
944
+
945
+ const skillNames = options.skills || defaultSkills.map(s => s.name);
946
+
947
+ for (const skillName of skillNames) {
948
+ const skill = engine.getSkill(skillName);
949
+ const defaultSkill = defaultSkills.find(s => s.name === skillName);
950
+
951
+ const agentObj = {
952
+ name: skillName,
953
+ description: skill?.description || `${skillName} agent`,
954
+ execute: skill?.execute || (async (input) => ({ agent: skillName, input, executed: true }))
955
+ };
956
+
957
+ agentObj.capability = new AgentCapability({
958
+ agent: agentObj, // Reference to agent object
959
+ categories: defaultSkill?.categories || [TriageCategory.GENERAL],
960
+ keywords: defaultSkill?.keywords || [],
961
+ priority: 1
962
+ });
963
+
964
+ agents.push(agentObj);
965
+ }
966
+
967
+ const context = await engine.execute(PatternType.TRIAGE, {
968
+ task: 'Classify and route request',
969
+ input: {
970
+ message: options.message,
971
+ agents,
972
+ strategy,
973
+ defaultCategory: options.defaultCategory.toUpperCase(),
974
+ confidenceThreshold: parseFloat(options.threshold),
975
+ enableHandoff: options.autoHandoff // Only handoff if explicitly requested
976
+ }
977
+ });
978
+
979
+ if (options.format === 'json') {
980
+ console.log(JSON.stringify({
981
+ success: context.status === ExecutionStatus.COMPLETED,
982
+ classification: context.output?.classification,
983
+ selectedAgent: context.output?.selectedAgent,
984
+ confidence: context.output?.confidence,
985
+ category: context.output?.category
986
+ }, null, 2));
987
+ } else {
988
+ if (context.status === ExecutionStatus.COMPLETED) {
989
+ console.log(chalk.green('✓ Triage completed\n'));
990
+
991
+ const classification = context.output?.classification || context.output;
992
+
993
+ console.log(chalk.bold('Classification Result:'));
994
+ console.log(` Category: ${chalk.cyan(classification?.category || 'N/A')}`);
995
+ console.log(` Confidence: ${formatConfidence(classification?.confidence || 0)}`);
996
+ if (classification?.reasoning) {
997
+ console.log(` Reasoning: ${chalk.dim(classification.reasoning)}`);
998
+ }
999
+ console.log('');
1000
+
1001
+ if (context.output?.selectedAgent) {
1002
+ console.log(chalk.bold('Routing Decision:'));
1003
+ console.log(` Selected Agent: ${chalk.yellow(context.output.selectedAgent)}`);
1004
+ console.log(` Agent Score: ${(context.output.agentScore || 0).toFixed(2)}`);
1005
+ }
1006
+
1007
+ if (options.autoHandoff && context.output?.handoffResult) {
1008
+ console.log('');
1009
+ console.log(chalk.bold('Handoff Result:'));
1010
+ console.log(` Status: ${chalk.green('Executed')}`);
1011
+ }
1012
+ } else {
1013
+ console.log(chalk.red(`✗ Triage failed: ${context.error}\n`));
1014
+ process.exit(1);
1015
+ }
1016
+ }
1017
+
1018
+ console.log('');
1019
+
1020
+ } catch (error) {
1021
+ console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
1022
+ if (process.env.DEBUG) {
1023
+ console.error(error.stack);
1024
+ }
1025
+ process.exit(1);
1026
+ }
1027
+ });
1028
+
1029
+ // Triage categories command - List available categories
1030
+ program
1031
+ .command('triage-categories')
1032
+ .description('List available triage categories')
1033
+ .option('-f, --format <type>', 'Output format (text|json)', 'text')
1034
+ .action(async (options) => {
1035
+ try {
1036
+ console.log(chalk.bold('\n📋 Triage Categories\n'));
1037
+
1038
+ const categories = Object.entries(TriageCategory).map(([key, value]) => ({
1039
+ key,
1040
+ value,
1041
+ description: getCategoryDescription(value)
1042
+ }));
1043
+
1044
+ if (options.format === 'json') {
1045
+ console.log(JSON.stringify(categories, null, 2));
1046
+ } else {
1047
+ for (const cat of categories) {
1048
+ console.log(` ${chalk.cyan(cat.value.padEnd(12))} - ${cat.description}`);
1049
+ }
1050
+ }
1051
+
1052
+ console.log('');
1053
+
1054
+ } catch (error) {
1055
+ console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
1056
+ process.exit(1);
1057
+ }
1058
+ });
1059
+
1060
+ /**
1061
+ * Get category description
1062
+ */
1063
+ function getCategoryDescription(category) {
1064
+ const descriptions = {
1065
+ [TriageCategory.BILLING]: 'Billing, invoices, payments, refunds',
1066
+ [TriageCategory.SUPPORT]: 'General support, help requests, issues',
1067
+ [TriageCategory.SALES]: 'Sales inquiries, purchases, pricing',
1068
+ [TriageCategory.TECHNICAL]: 'Technical issues, bugs, API, integrations',
1069
+ [TriageCategory.REFUND]: 'Refund requests and processing',
1070
+ [TriageCategory.GENERAL]: 'General inquiries, catch-all category',
1071
+ [TriageCategory.ESCALATION]: 'Escalated issues requiring supervisor attention',
1072
+ [TriageCategory.UNKNOWN]: 'Unclassified requests'
1073
+ };
1074
+ return descriptions[category] || 'Unknown category';
1075
+ }
1076
+
761
1077
  program.parse(process.argv);
762
1078
 
763
1079
  if (!process.argv.slice(2).length) {
@@ -11,12 +11,20 @@
11
11
  * musubi-validate article <1-9> # Validate specific article
12
12
  * musubi-validate gates # Validate Phase -1 Gates
13
13
  * musubi-validate complexity # Validate complexity limits
14
+ * musubi-validate guardrails # Validate content with guardrails
14
15
  * musubi-validate all # Run all validations
15
16
  */
16
17
 
17
18
  const { Command } = require('commander');
18
19
  const chalk = require('chalk');
19
20
  const ConstitutionValidator = require('../src/validators/constitution');
21
+ const {
22
+ createInputGuardrail,
23
+ createOutputGuardrail,
24
+ createSafetyCheckGuardrail,
25
+ GuardrailChain,
26
+ SafetyLevel
27
+ } = require('../src/orchestration/guardrails');
20
28
 
21
29
  const program = new Command();
22
30
 
@@ -107,6 +115,134 @@ program
107
115
  }
108
116
  });
109
117
 
118
+ // Guardrails validation
119
+ program
120
+ .command('guardrails')
121
+ .description('Validate content with input/output guardrails')
122
+ .argument('[content]', 'Content to validate (or use --file)')
123
+ .option('--file <path>', 'Read content from file')
124
+ .option('-t, --type <type>', 'Guardrail type (input|output|safety)', 'input')
125
+ .option('-l, --level <level>', 'Safety level (basic|standard|strict|paranoid)', 'standard')
126
+ .option('--constitutional', 'Enable constitutional compliance checks')
127
+ .option('--redact', 'Enable redaction for output guardrails')
128
+ .option('-f, --format <type>', 'Output format (console|json)', 'console')
129
+ .action(async (content, options) => {
130
+ try {
131
+ // Get content from argument, file, or stdin
132
+ let inputContent = content;
133
+
134
+ if (options.file) {
135
+ const fs = require('fs');
136
+ const path = require('path');
137
+ const filePath = path.resolve(process.cwd(), options.file);
138
+ inputContent = fs.readFileSync(filePath, 'utf-8');
139
+ } else if (!inputContent && !process.stdin.isTTY) {
140
+ // Read from stdin
141
+ const chunks = [];
142
+ for await (const chunk of process.stdin) {
143
+ chunks.push(chunk);
144
+ }
145
+ inputContent = Buffer.concat(chunks).toString('utf-8');
146
+ }
147
+
148
+ if (!inputContent) {
149
+ console.error(chalk.red('✗ No content provided. Use --file or pipe content.'));
150
+ process.exit(1);
151
+ }
152
+
153
+ let guardrail;
154
+ const guardrailType = options.type.toLowerCase();
155
+
156
+ switch (guardrailType) {
157
+ case 'input':
158
+ guardrail = createInputGuardrail('userInput', {
159
+ sanitize: true
160
+ });
161
+ break;
162
+
163
+ case 'output':
164
+ guardrail = createOutputGuardrail(options.redact ? 'redact' : 'safe');
165
+ break;
166
+
167
+ case 'safety':
168
+ guardrail = createSafetyCheckGuardrail(options.level, {
169
+ enforceConstitution: options.constitutional
170
+ });
171
+ break;
172
+
173
+ default:
174
+ console.error(chalk.red(`✗ Unknown guardrail type: ${guardrailType}`));
175
+ process.exit(1);
176
+ }
177
+
178
+ console.log(chalk.dim(`\n🛡️ Running ${guardrailType} guardrail validation...\n`));
179
+
180
+ const result = await guardrail.run(inputContent);
181
+
182
+ displayGuardrailResults(result, options);
183
+ process.exit(result.passed ? 0 : 1);
184
+ } catch (error) {
185
+ console.error(chalk.red('✗ Guardrail validation error:'), error.message);
186
+ process.exit(1);
187
+ }
188
+ });
189
+
190
+ // Guardrails chain validation
191
+ program
192
+ .command('guardrails-chain')
193
+ .description('Run a chain of guardrails on content')
194
+ .argument('[content]', 'Content to validate')
195
+ .option('--file <path>', 'Read content from file')
196
+ .option('--parallel', 'Run guardrails in parallel')
197
+ .option('--stop-on-failure', 'Stop on first failure')
198
+ .option('-f, --format <type>', 'Output format (console|json)', 'console')
199
+ .action(async (content, options) => {
200
+ try {
201
+ // Get content
202
+ let inputContent = content;
203
+
204
+ if (options.file) {
205
+ const fs = require('fs');
206
+ const path = require('path');
207
+ const filePath = path.resolve(process.cwd(), options.file);
208
+ inputContent = fs.readFileSync(filePath, 'utf-8');
209
+ } else if (!inputContent && !process.stdin.isTTY) {
210
+ const chunks = [];
211
+ for await (const chunk of process.stdin) {
212
+ chunks.push(chunk);
213
+ }
214
+ inputContent = Buffer.concat(chunks).toString('utf-8');
215
+ }
216
+
217
+ if (!inputContent) {
218
+ console.error(chalk.red('✗ No content provided. Use --file or pipe content.'));
219
+ process.exit(1);
220
+ }
221
+
222
+ // Create guardrail chain
223
+ const chain = new GuardrailChain({
224
+ name: 'ValidationChain',
225
+ parallel: options.parallel || false,
226
+ stopOnFirstFailure: options.stopOnFailure || false
227
+ });
228
+
229
+ // Add default guardrails
230
+ chain.add(createInputGuardrail('security'));
231
+ chain.add(createSafetyCheckGuardrail('standard'));
232
+ chain.add(createOutputGuardrail('safe'));
233
+
234
+ console.log(chalk.dim('\n🔗 Running guardrail chain validation...\n'));
235
+
236
+ const result = await chain.run(inputContent);
237
+
238
+ displayChainResults(result, options);
239
+ process.exit(result.passed ? 0 : 1);
240
+ } catch (error) {
241
+ console.error(chalk.red('✗ Guardrail chain error:'), error.message);
242
+ process.exit(1);
243
+ }
244
+ });
245
+
110
246
  // Score validation (numeric score output for CI/CD)
111
247
  program
112
248
  .command('score')
@@ -345,6 +481,104 @@ function displayMarkdown(title, results) {
345
481
  }
346
482
  }
347
483
 
484
+ /**
485
+ * Display guardrail validation results
486
+ */
487
+ function displayGuardrailResults(result, options) {
488
+ if (options.format === 'json') {
489
+ console.log(JSON.stringify(result, null, 2));
490
+ return;
491
+ }
492
+
493
+ console.log(chalk.bold('Guardrail Validation Results'));
494
+ console.log(chalk.bold('━'.repeat(50)));
495
+
496
+ if (result.passed) {
497
+ console.log(chalk.green(`\n✓ PASSED - ${result.guardrailName}\n`));
498
+ } else {
499
+ console.log(chalk.red(`\n✗ FAILED - ${result.guardrailName}\n`));
500
+ }
501
+
502
+ if (result.message) {
503
+ console.log(chalk.dim(`Message: ${result.message}`));
504
+ }
505
+
506
+ console.log(chalk.dim(`Execution time: ${result.executionTimeMs}ms\n`));
507
+
508
+ if (result.violations && result.violations.length > 0) {
509
+ console.log(chalk.bold.red('Violations:'));
510
+ result.violations.forEach(violation => {
511
+ const severityIcon = violation.severity === 'error' ? '✗' :
512
+ violation.severity === 'warning' ? '⚠' : 'ℹ';
513
+ const severityColor = violation.severity === 'error' ? chalk.red :
514
+ violation.severity === 'warning' ? chalk.yellow : chalk.blue;
515
+ console.log(severityColor(` ${severityIcon} [${violation.code}] ${violation.message}`));
516
+ });
517
+ console.log();
518
+ }
519
+
520
+ // Show metadata if redaction was applied
521
+ if (result.metadata?.redactionApplied) {
522
+ console.log(chalk.bold.blue('Redaction Applied:'));
523
+ console.log(chalk.dim(` ${result.metadata.redactionCount} item(s) redacted`));
524
+ if (result.metadata.redactions) {
525
+ result.metadata.redactions.forEach(r => {
526
+ console.log(chalk.dim(` - ${r.type}: ${r.count}`));
527
+ });
528
+ }
529
+ console.log();
530
+ }
531
+
532
+ // Show quality scores if available
533
+ if (result.metadata?.qualityScores) {
534
+ console.log(chalk.bold.blue('Quality Scores:'));
535
+ Object.entries(result.metadata.qualityScores).forEach(([name, score]) => {
536
+ console.log(chalk.dim(` ${name}: ${(score * 100).toFixed(1)}%`));
537
+ });
538
+ console.log();
539
+ }
540
+ }
541
+
542
+ /**
543
+ * Display guardrail chain results
544
+ */
545
+ function displayChainResults(result, options) {
546
+ if (options.format === 'json') {
547
+ console.log(JSON.stringify(result, null, 2));
548
+ return;
549
+ }
550
+
551
+ console.log(chalk.bold('Guardrail Chain Results'));
552
+ console.log(chalk.bold('━'.repeat(50)));
553
+
554
+ if (result.passed) {
555
+ console.log(chalk.green(`\n✓ PASSED - ${result.chainName}\n`));
556
+ } else {
557
+ console.log(chalk.red(`\n✗ FAILED - ${result.chainName}\n`));
558
+ }
559
+
560
+ console.log(chalk.dim(`Guardrails executed: ${result.executedCount}/${result.guardrailCount}`));
561
+ console.log(chalk.dim(`Total time: ${result.executionTimeMs}ms\n`));
562
+
563
+ // Show individual guardrail results
564
+ console.log(chalk.bold('Individual Results:'));
565
+ result.results.forEach((r, index) => {
566
+ const icon = r.passed ? chalk.green('✓') : chalk.red('✗');
567
+ console.log(` ${icon} ${r.guardrailName} (${r.executionTimeMs}ms)`);
568
+ });
569
+ console.log();
570
+
571
+ // Show all violations
572
+ if (result.violations && result.violations.length > 0) {
573
+ console.log(chalk.bold.red('All Violations:'));
574
+ result.violations.forEach(violation => {
575
+ const severityColor = violation.severity === 'error' ? chalk.red : chalk.yellow;
576
+ console.log(severityColor(` • [${violation.code}] ${violation.message}`));
577
+ });
578
+ console.log();
579
+ }
580
+ }
581
+
348
582
  // Parse arguments
349
583
  program.parse(process.argv);
350
584
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musubi-sdd",
3
- "version": "3.7.1",
3
+ "version": "3.9.0",
4
4
  "description": "Ultimate Specification Driven Development Tool with 27 Agents for 7 AI Coding Platforms + MCP Integration (Claude Code, GitHub Copilot, Cursor, Gemini CLI, Windsurf, Codex, Qwen Code)",
5
5
  "main": "src/index.js",
6
6
  "bin": {