orchestr8 2.5.0 → 2.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.
Files changed (54) hide show
  1. package/.blueprint/agents/AGENT_BA_CASS.md +42 -19
  2. package/.blueprint/agents/AGENT_DEVELOPER_CODEY.md +42 -38
  3. package/.blueprint/agents/AGENT_SPECIFICATION_ALEX.md +45 -0
  4. package/.blueprint/agents/AGENT_TESTER_NIGEL.md +42 -21
  5. package/.blueprint/features/feature_adaptive-retry/FEATURE_SPEC.md +239 -0
  6. package/.blueprint/features/feature_adaptive-retry/IMPLEMENTATION_PLAN.md +48 -0
  7. package/.blueprint/features/feature_adaptive-retry/story-prompt-modification.md +85 -0
  8. package/.blueprint/features/feature_adaptive-retry/story-retry-config.md +89 -0
  9. package/.blueprint/features/feature_adaptive-retry/story-should-retry.md +98 -0
  10. package/.blueprint/features/feature_adaptive-retry/story-strategy-recommendation.md +85 -0
  11. package/.blueprint/features/feature_agent-guardrails/FEATURE_SPEC.md +328 -0
  12. package/.blueprint/features/feature_agent-guardrails/IMPLEMENTATION_PLAN.md +90 -0
  13. package/.blueprint/features/feature_agent-guardrails/story-citation-requirements.md +50 -0
  14. package/.blueprint/features/feature_agent-guardrails/story-confidentiality.md +50 -0
  15. package/.blueprint/features/feature_agent-guardrails/story-escalation-protocol.md +55 -0
  16. package/.blueprint/features/feature_agent-guardrails/story-source-restrictions.md +50 -0
  17. package/.blueprint/features/feature_feedback-loop/FEATURE_SPEC.md +347 -0
  18. package/.blueprint/features/feature_feedback-loop/IMPLEMENTATION_PLAN.md +71 -0
  19. package/.blueprint/features/feature_feedback-loop/story-feedback-collection.md +63 -0
  20. package/.blueprint/features/feature_feedback-loop/story-feedback-config.md +61 -0
  21. package/.blueprint/features/feature_feedback-loop/story-feedback-insights.md +63 -0
  22. package/.blueprint/features/feature_feedback-loop/story-quality-gates.md +57 -0
  23. package/.blueprint/features/feature_pipeline-history/FEATURE_SPEC.md +239 -0
  24. package/.blueprint/features/feature_pipeline-history/IMPLEMENTATION_PLAN.md +71 -0
  25. package/.blueprint/features/feature_pipeline-history/story-clear-history.md +73 -0
  26. package/.blueprint/features/feature_pipeline-history/story-display-history.md +75 -0
  27. package/.blueprint/features/feature_pipeline-history/story-record-execution.md +76 -0
  28. package/.blueprint/features/feature_pipeline-history/story-show-statistics.md +85 -0
  29. package/.blueprint/features/feature_pipeline-insights/FEATURE_SPEC.md +288 -0
  30. package/.blueprint/features/feature_pipeline-insights/IMPLEMENTATION_PLAN.md +65 -0
  31. package/.blueprint/features/feature_pipeline-insights/story-anomaly-detection.md +71 -0
  32. package/.blueprint/features/feature_pipeline-insights/story-bottleneck-analysis.md +75 -0
  33. package/.blueprint/features/feature_pipeline-insights/story-failure-patterns.md +75 -0
  34. package/.blueprint/features/feature_pipeline-insights/story-json-output.md +75 -0
  35. package/.blueprint/features/feature_pipeline-insights/story-trend-analysis.md +78 -0
  36. package/.blueprint/features/feature_validate-command/FEATURE_SPEC.md +209 -0
  37. package/.blueprint/features/feature_validate-command/IMPLEMENTATION_PLAN.md +59 -0
  38. package/.blueprint/features/feature_validate-command/story-failure-output.md +61 -0
  39. package/.blueprint/features/feature_validate-command/story-node-version-check.md +52 -0
  40. package/.blueprint/features/feature_validate-command/story-run-validation.md +59 -0
  41. package/.blueprint/features/feature_validate-command/story-success-output.md +50 -0
  42. package/.blueprint/system_specification/SYSTEM_SPEC.md +248 -0
  43. package/README.md +182 -38
  44. package/SKILL.md +333 -23
  45. package/bin/cli.js +128 -20
  46. package/package.json +2 -2
  47. package/src/feedback.js +171 -0
  48. package/src/history.js +306 -0
  49. package/src/index.js +57 -2
  50. package/src/init.js +2 -6
  51. package/src/insights.js +504 -0
  52. package/src/retry.js +274 -0
  53. package/src/validate.js +172 -0
  54. package/src/skills.js +0 -93
package/src/retry.js ADDED
@@ -0,0 +1,274 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { readHistoryFile } = require('./history');
4
+
5
+ const CONFIG_FILE = '.claude/retry-config.json';
6
+
7
+ /**
8
+ * Returns the default retry configuration.
9
+ * Per FEATURE_SPEC.md defaults.
10
+ */
11
+ function getDefaultConfig() {
12
+ return {
13
+ maxRetries: 3,
14
+ windowSize: 10,
15
+ highFailureThreshold: 0.2,
16
+ strategies: {
17
+ alex: ['simplify-prompt', 'add-context'],
18
+ cass: ['reduce-stories', 'simplify-prompt'],
19
+ nigel: ['simplify-tests', 'add-context'],
20
+ 'codey-plan': ['add-context', 'simplify-prompt'],
21
+ 'codey-implement': ['incremental', 'rollback']
22
+ }
23
+ };
24
+ }
25
+
26
+ /**
27
+ * Ensures the .claude directory exists.
28
+ */
29
+ function ensureConfigDir() {
30
+ const dir = path.dirname(CONFIG_FILE);
31
+ if (!fs.existsSync(dir)) {
32
+ fs.mkdirSync(dir, { recursive: true });
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Reads the retry config from file.
38
+ * Returns defaults if file is missing or corrupted.
39
+ */
40
+ function readConfig() {
41
+ ensureConfigDir();
42
+ if (!fs.existsSync(CONFIG_FILE)) {
43
+ return getDefaultConfig();
44
+ }
45
+ try {
46
+ const content = fs.readFileSync(CONFIG_FILE, 'utf8');
47
+ return JSON.parse(content);
48
+ } catch (err) {
49
+ // Graceful degradation: return defaults on parse error
50
+ return getDefaultConfig();
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Writes the retry config to file.
56
+ * Creates .claude directory if needed.
57
+ */
58
+ function writeConfig(config) {
59
+ ensureConfigDir();
60
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
61
+ }
62
+
63
+ /**
64
+ * Resets config to defaults by writing default config to file.
65
+ */
66
+ function resetConfig() {
67
+ writeConfig(getDefaultConfig());
68
+ }
69
+
70
+ /**
71
+ * Calculates failure rate for a stage over a sliding window.
72
+ * @param {string} stage - The pipeline stage name
73
+ * @param {Array} history - Array of history entries
74
+ * @param {number} windowSize - Number of recent entries to consider
75
+ * @returns {number} Failure rate between 0 and 1
76
+ */
77
+ function calculateFailureRate(stage, history, windowSize = 10) {
78
+ // Handle corrupted history gracefully
79
+ if (!Array.isArray(history) || history.length === 0) {
80
+ return 0;
81
+ }
82
+ const recent = history.slice(-windowSize);
83
+ if (recent.length === 0) return 0;
84
+ const failedAtStage = recent.filter(
85
+ e => e.status === 'failed' && e.failedStage === stage
86
+ ).length;
87
+ return failedAtStage / recent.length;
88
+ }
89
+
90
+ /**
91
+ * Recommends a retry strategy based on attempt count and failure rate.
92
+ * @param {string} stage - The pipeline stage name
93
+ * @param {number} attemptCount - Current attempt number (1-based)
94
+ * @param {number} failureRate - Calculated failure rate (0-1)
95
+ * @param {object} config - Retry configuration object
96
+ * @returns {string} Strategy name or 'abort-recommended'
97
+ */
98
+ function recommendStrategy(stage, attemptCount, failureRate, config) {
99
+ if (attemptCount > config.maxRetries) {
100
+ return 'abort-recommended';
101
+ }
102
+ if (failureRate > config.highFailureThreshold) {
103
+ const strategies = config.strategies[stage] || [];
104
+ const idx = Math.min(attemptCount - 1, strategies.length - 1);
105
+ return strategies[idx] || 'retry';
106
+ }
107
+ return 'retry';
108
+ }
109
+
110
+ /**
111
+ * Applies a strategy by modifying the prompt.
112
+ * @param {string} strategy - The strategy name
113
+ * @param {string} originalPrompt - The original prompt text
114
+ * @returns {string} Modified prompt or original if no modification needed
115
+ */
116
+ function applyStrategy(strategy, originalPrompt) {
117
+ const modifications = {
118
+ 'retry': null,
119
+ 'simplify-prompt': 'Focus on core requirements only. Skip edge cases and optional sections.',
120
+ 'reduce-stories': 'Write only the 2-3 most critical user stories. Defer others to follow-up.',
121
+ 'simplify-tests': 'Write only happy-path tests for each AC. Skip edge cases.',
122
+ 'add-context': '[Context from previous stage prepended]',
123
+ 'incremental': 'Implement one test at a time. Stop and report after each.',
124
+ 'rollback': 'git checkout -- .'
125
+ };
126
+ const mod = modifications[strategy];
127
+ return mod ? `${originalPrompt}\n\n${mod}` : originalPrompt;
128
+ }
129
+
130
+ /**
131
+ * Orchestrator entry point: determines if/how to retry a failed stage.
132
+ * @param {string} stage - The pipeline stage that failed
133
+ * @param {string} featureSlug - The feature being implemented
134
+ * @param {Array|null} history - History entries (pass null to read from file)
135
+ * @param {object|null} config - Config object (pass null to read from file)
136
+ * @param {number} attemptCount - Current attempt number (1-based)
137
+ * @returns {object} Recommendation object with strategy and metadata
138
+ */
139
+ function shouldRetry(stage, featureSlug, history, config, attemptCount) {
140
+ // Read history from file if not provided
141
+ let historyData = history;
142
+ if (historyData === null || historyData === undefined) {
143
+ historyData = readHistoryFile();
144
+ // Handle corrupted history
145
+ if (historyData && historyData.error === 'corrupted') {
146
+ historyData = [];
147
+ }
148
+ }
149
+
150
+ // Read config from file if not provided
151
+ const configData = config || readConfig();
152
+
153
+ const failureRate = calculateFailureRate(
154
+ stage,
155
+ historyData,
156
+ configData.windowSize
157
+ );
158
+
159
+ const strategy = recommendStrategy(
160
+ stage,
161
+ attemptCount,
162
+ failureRate,
163
+ configData
164
+ );
165
+
166
+ return {
167
+ stage,
168
+ featureSlug,
169
+ attemptCount,
170
+ failureRate,
171
+ strategy,
172
+ shouldRetry: strategy !== 'abort-recommended',
173
+ maxRetries: configData.maxRetries
174
+ };
175
+ }
176
+
177
+ /**
178
+ * Displays the current retry configuration.
179
+ */
180
+ function displayConfig() {
181
+ const config = readConfig();
182
+ console.log('\nRetry Configuration\n');
183
+ console.log(` Max retries: ${config.maxRetries}`);
184
+ console.log(` Window size: ${config.windowSize}`);
185
+ console.log(` High failure threshold: ${config.highFailureThreshold}`);
186
+ console.log('\n Stage Strategies:');
187
+ for (const [stage, strategies] of Object.entries(config.strategies)) {
188
+ console.log(` ${stage.padEnd(16)}: ${strategies.join(' -> ')}`);
189
+ }
190
+ console.log('');
191
+ }
192
+
193
+ /**
194
+ * Sets a config value by key.
195
+ * @param {string} key - Config key (maxRetries, windowSize, highFailureThreshold)
196
+ * @param {string} value - New value (will be parsed appropriately)
197
+ */
198
+ function setConfigValue(key, value) {
199
+ const config = readConfig();
200
+ const numericKeys = ['maxRetries', 'windowSize', 'highFailureThreshold'];
201
+
202
+ if (!numericKeys.includes(key)) {
203
+ throw new Error(
204
+ `Unknown config key: ${key}. Valid keys: ${numericKeys.join(', ')}`
205
+ );
206
+ }
207
+
208
+ const numValue = parseFloat(value);
209
+ if (isNaN(numValue)) {
210
+ throw new Error(`Invalid value for ${key}: ${value}. Must be a number.`);
211
+ }
212
+
213
+ // Validate specific constraints
214
+ if (key === 'maxRetries' && (numValue < 0 || !Number.isInteger(numValue))) {
215
+ throw new Error('maxRetries must be a non-negative integer.');
216
+ }
217
+ if (key === 'windowSize' && (numValue < 1 || !Number.isInteger(numValue))) {
218
+ throw new Error('windowSize must be a positive integer.');
219
+ }
220
+ if (key === 'highFailureThreshold' && (numValue < 0 || numValue > 1)) {
221
+ throw new Error('highFailureThreshold must be between 0 and 1.');
222
+ }
223
+
224
+ config[key] = numValue;
225
+ writeConfig(config);
226
+ console.log(`Set ${key} = ${numValue}`);
227
+ }
228
+
229
+ /**
230
+ * Maps feedback issues to retry strategies using issue mappings config.
231
+ * Per FEATURE_SPEC.md:Rule 3.
232
+ * @param {Array} issues - Array of issue codes from feedback
233
+ * @param {object} config - Config with issueMappings (from feedback config)
234
+ * @returns {Array} Array of recommended strategies
235
+ */
236
+ function mapIssuesToStrategies(issues, config) {
237
+ if (!issues || !Array.isArray(issues) || issues.length === 0) {
238
+ return ['retry'];
239
+ }
240
+
241
+ const mappings = config?.issueMappings || {
242
+ 'missing-error-handling': 'add-context',
243
+ 'unclear-scope': 'simplify-prompt',
244
+ 'too-complex': 'simplify-prompt',
245
+ 'too-many-stories': 'reduce-stories',
246
+ 'untestable-criteria': 'simplify-tests',
247
+ 'missing-edge-cases': 'add-context'
248
+ };
249
+
250
+ const strategies = [];
251
+ for (const issue of issues) {
252
+ const strategy = mappings[issue];
253
+ if (strategy && !strategies.includes(strategy)) {
254
+ strategies.push(strategy);
255
+ }
256
+ }
257
+
258
+ return strategies.length > 0 ? strategies : ['retry'];
259
+ }
260
+
261
+ module.exports = {
262
+ CONFIG_FILE,
263
+ getDefaultConfig,
264
+ readConfig,
265
+ writeConfig,
266
+ resetConfig,
267
+ calculateFailureRate,
268
+ recommendStrategy,
269
+ applyStrategy,
270
+ shouldRetry,
271
+ displayConfig,
272
+ setConfigValue,
273
+ mapIssuesToStrategies
274
+ };
@@ -0,0 +1,172 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ const REQUIRED_DIRS = ['.blueprint', '.business_context', '.claude/commands'];
5
+ const AGENT_FILES = [
6
+ 'AGENT_SPECIFICATION_ALEX.md',
7
+ 'AGENT_BA_CASS.md',
8
+ 'AGENT_TESTER_NIGEL.md',
9
+ 'AGENT_DEVELOPER_CODEY.md'
10
+ ];
11
+ const SYSTEM_SPEC_PATH = '.blueprint/system_specification/SYSTEM_SPEC.md';
12
+ const SKILL_PATH = '.claude/commands/implement-feature.md';
13
+ const MIN_NODE_VERSION = 18;
14
+
15
+ function checkDirectories() {
16
+ const missing = REQUIRED_DIRS.filter(dir => !fs.existsSync(dir));
17
+ const passed = missing.length === 0;
18
+ return {
19
+ name: 'Required directories',
20
+ passed,
21
+ message: passed
22
+ ? 'All required directories exist'
23
+ : `Missing directories: ${missing.join(', ')}`,
24
+ fix: passed ? null : 'Run `orchestr8 init` to initialize project'
25
+ };
26
+ }
27
+
28
+ function checkSystemSpec() {
29
+ const exists = fs.existsSync(SYSTEM_SPEC_PATH);
30
+ return {
31
+ name: 'System specification',
32
+ passed: exists,
33
+ message: exists
34
+ ? 'System specification exists'
35
+ : `Missing: ${SYSTEM_SPEC_PATH}`,
36
+ fix: exists ? null : 'Run `orchestr8 init` to create system specification'
37
+ };
38
+ }
39
+
40
+ function checkAgentSpecs() {
41
+ const agentsDir = '.blueprint/agents';
42
+ if (!fs.existsSync(agentsDir)) {
43
+ return {
44
+ name: 'agent specifications',
45
+ passed: false,
46
+ message: 'Missing: .blueprint/agents directory',
47
+ fix: 'Run `orchestr8 init` to create agent specification files'
48
+ };
49
+ }
50
+
51
+ const missing = AGENT_FILES.filter(f => !fs.existsSync(path.join(agentsDir, f)));
52
+ const passed = missing.length === 0;
53
+ return {
54
+ name: 'agent specifications',
55
+ passed,
56
+ message: passed
57
+ ? 'All agent specifications exist'
58
+ : `Missing agent files: ${missing.join(', ')}`,
59
+ fix: passed ? null : 'Run `orchestr8 init` to create agent specification files'
60
+ };
61
+ }
62
+
63
+ function checkBusinessContext() {
64
+ const dir = '.business_context';
65
+ if (!fs.existsSync(dir)) {
66
+ return {
67
+ name: 'business context',
68
+ passed: false,
69
+ message: 'Missing: .business_context directory',
70
+ fix: 'Run `orchestr8 init` to create .business_context directory'
71
+ };
72
+ }
73
+
74
+ let files;
75
+ try {
76
+ files = fs.readdirSync(dir).filter(f => !f.startsWith('.'));
77
+ } catch {
78
+ files = [];
79
+ }
80
+
81
+ const passed = files.length > 0;
82
+ return {
83
+ name: 'business context',
84
+ passed,
85
+ message: passed
86
+ ? 'Business context directory has content'
87
+ : 'Business context directory is empty',
88
+ fix: passed ? null : 'Add at least one file to `.business_context/` directory'
89
+ };
90
+ }
91
+
92
+ function checkSkillsInstalled() {
93
+ const exists = fs.existsSync(SKILL_PATH);
94
+ return {
95
+ name: 'skills installed',
96
+ passed: exists,
97
+ message: exists
98
+ ? 'Required skills are installed'
99
+ : `Missing: ${SKILL_PATH}`,
100
+ fix: exists ? null : 'Run `orchestr8 init` to install required skills'
101
+ };
102
+ }
103
+
104
+ function checkNodeVersion() {
105
+ const versionStr = process.version;
106
+ const majorVersion = parseInt(versionStr.slice(1).split('.')[0], 10);
107
+ const passed = majorVersion >= MIN_NODE_VERSION;
108
+ return {
109
+ name: 'Node.js version',
110
+ passed,
111
+ message: passed
112
+ ? `Node.js ${majorVersion} meets minimum requirement (>=${MIN_NODE_VERSION})`
113
+ : `Node.js ${majorVersion} is below minimum requirement (>=${MIN_NODE_VERSION})`,
114
+ fix: passed ? null : `Upgrade Node.js to version ${MIN_NODE_VERSION} or higher`,
115
+ detectedVersion: versionStr
116
+ };
117
+ }
118
+
119
+ async function validate() {
120
+ const checks = [
121
+ checkDirectories(),
122
+ checkSystemSpec(),
123
+ checkAgentSpecs(),
124
+ checkBusinessContext(),
125
+ checkSkillsInstalled(),
126
+ checkNodeVersion()
127
+ ];
128
+
129
+ const success = checks.every(c => c.passed);
130
+ const exitCode = success ? 0 : 1;
131
+
132
+ return { success, exitCode, checks };
133
+ }
134
+
135
+ function formatOutput(result, useColor = false) {
136
+ const lines = [];
137
+
138
+ const green = useColor ? '\x1b[32m' : '';
139
+ const red = useColor ? '\x1b[31m' : '';
140
+ const reset = useColor ? '\x1b[0m' : '';
141
+
142
+ const passIndicator = useColor ? `${green}\u2713${reset}` : '[PASS]';
143
+ const failIndicator = useColor ? `${red}\u2717${reset}` : '[FAIL]';
144
+
145
+ for (const check of result.checks) {
146
+ const indicator = check.passed ? passIndicator : failIndicator;
147
+ lines.push(`${indicator} ${check.name}: ${check.message}`);
148
+ if (!check.passed && check.fix) {
149
+ lines.push(` Fix: ${check.fix}`);
150
+ }
151
+ }
152
+
153
+ lines.push('');
154
+ if (result.success) {
155
+ lines.push(useColor
156
+ ? `${green}All checks passed. Project is ready.${reset}`
157
+ : 'All checks passed. Project is ready.');
158
+ } else {
159
+ const failedCount = result.checks.filter(c => !c.passed).length;
160
+ lines.push(useColor
161
+ ? `${red}${failedCount} check(s) failed.${reset}`
162
+ : `${failedCount} check(s) failed.`);
163
+ }
164
+
165
+ return lines.join('\n');
166
+ }
167
+
168
+ module.exports = {
169
+ validate,
170
+ formatOutput,
171
+ checkNodeVersion
172
+ };
package/src/skills.js DELETED
@@ -1,93 +0,0 @@
1
- const { execSync } = require('child_process');
2
-
3
- const AGENT_SKILLS = {
4
- alex: {
5
- name: 'Alex',
6
- role: 'System Specification & Chief-of-Staff',
7
- skills: [
8
- { repo: 'https://github.com/waynesutton/convexskills', skill: 'avoid-feature-creep' },
9
- { repo: 'https://github.com/pproenca/dot-skills', skill: 'feature-spec' }
10
- ]
11
- },
12
- cass: {
13
- name: 'Cass',
14
- role: 'Story Writer / BA',
15
- skills: [
16
- { repo: 'https://github.com/aj-geddes/useful-ai-prompts', skill: 'user-story-writing' }
17
- ]
18
- },
19
- nigel: {
20
- name: 'Nigel',
21
- role: 'Tester',
22
- skills: [
23
- { repo: 'https://github.com/wshobson/agents', skill: 'javascript-testing-patterns' },
24
- { repo: 'https://github.com/wshobson/agents', skill: 'modern-javascript-patterns' }
25
- ]
26
- },
27
- codey: {
28
- name: 'Codey',
29
- role: 'Developer',
30
- skills: [
31
- { repo: 'https://github.com/martinholovsky/claude-skills-generator', skill: 'javascript-expert' },
32
- { repo: 'https://github.com/wshobson/agents', skill: 'modern-javascript-patterns' }
33
- ]
34
- }
35
- };
36
-
37
- function installSkill(repo, skill) {
38
- const cmd = `npx skills add ${repo} --skill ${skill}`;
39
- console.log(` Running: ${cmd}`);
40
- try {
41
- execSync(cmd, { stdio: 'inherit' });
42
- return true;
43
- } catch (error) {
44
- console.error(` Failed to install ${skill}`);
45
- return false;
46
- }
47
- }
48
-
49
- async function addSkills(agent) {
50
- const agents = agent === 'all' ? Object.keys(AGENT_SKILLS) : [agent.toLowerCase()];
51
-
52
- for (const agentKey of agents) {
53
- const agentConfig = AGENT_SKILLS[agentKey];
54
-
55
- if (!agentConfig) {
56
- console.error(`Unknown agent: ${agentKey}`);
57
- console.error(`Available agents: ${Object.keys(AGENT_SKILLS).join(', ')}, all`);
58
- process.exit(1);
59
- }
60
-
61
- console.log(`\nInstalling skills for ${agentConfig.name} (${agentConfig.role}):`);
62
-
63
- for (const { repo, skill } of agentConfig.skills) {
64
- installSkill(repo, skill);
65
- }
66
- }
67
-
68
- console.log('\nSkills installation complete.');
69
- }
70
-
71
- function listSkills(agent) {
72
- const agents = agent ? [agent.toLowerCase()] : Object.keys(AGENT_SKILLS);
73
-
74
- console.log('\nAgent Workflow - Recommended Skills\n');
75
-
76
- for (const agentKey of agents) {
77
- const agentConfig = AGENT_SKILLS[agentKey];
78
-
79
- if (!agentConfig) {
80
- console.error(`Unknown agent: ${agentKey}`);
81
- process.exit(1);
82
- }
83
-
84
- console.log(`${agentConfig.name} (${agentConfig.role}):`);
85
- for (const { repo, skill } of agentConfig.skills) {
86
- console.log(` - ${skill}`);
87
- console.log(` ${repo}`);
88
- }
89
- console.log('');
90
- }
91
- }
92
-
93
- module.exports = { addSkills, listSkills, AGENT_SKILLS };