@deimoscloud/coreai 0.1.9 → 0.1.11
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/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/package.json +6 -1
- package/.prettierrc +0 -9
- package/AGENT_SPEC.md +0 -347
- package/ARCHITECTURE.md +0 -547
- package/DRAFT_PRD.md +0 -1440
- package/IMPLEMENTATION_PLAN.md +0 -256
- package/PRODUCT.md +0 -473
- package/WORKFLOWS.md +0 -295
- package/commands/core/check-inbox.md +0 -34
- package/commands/core/delegate.md +0 -30
- package/commands/core/git-commit.md +0 -144
- package/commands/core/pr-create.md +0 -193
- package/commands/core/review.md +0 -56
- package/commands/core/sprint-status.md +0 -65
- package/commands/optional/docs-update.md +0 -200
- package/commands/optional/jira-create.md +0 -200
- package/commands/optional/jira-transition.md +0 -184
- package/commands/optional/worktree-cleanup.md +0 -167
- package/commands/optional/worktree-setup.md +0 -110
- package/eslint.config.js +0 -29
- package/jest.config.js +0 -22
- package/knowledge-library/README.md +0 -118
- package/knowledge-library/android-engineer/context/current.txt +0 -42
- package/knowledge-library/android-engineer/control/decisions.txt +0 -9
- package/knowledge-library/android-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/android-engineer/control/objectives.txt +0 -26
- package/knowledge-library/android-engineer/history/.gitkeep +0 -0
- package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/architecture.txt +0 -61
- package/knowledge-library/backend-engineer/context/current.txt +0 -42
- package/knowledge-library/backend-engineer/control/decisions.txt +0 -9
- package/knowledge-library/backend-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/backend-engineer/control/objectives.txt +0 -26
- package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/context.txt +0 -52
- package/knowledge-library/devops-engineer/context/current.txt +0 -42
- package/knowledge-library/devops-engineer/control/decisions.txt +0 -9
- package/knowledge-library/devops-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/devops-engineer/control/objectives.txt +0 -26
- package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/context/current.txt +0 -40
- package/knowledge-library/engineering-manager/control/decisions.txt +0 -9
- package/knowledge-library/engineering-manager/control/objectives.txt +0 -27
- package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
- package/knowledge-library/prd.txt +0 -81
- package/knowledge-library/product-manager/context/current.txt +0 -42
- package/knowledge-library/product-manager/control/decisions.txt +0 -9
- package/knowledge-library/product-manager/control/dependencies.txt +0 -19
- package/knowledge-library/product-manager/control/objectives.txt +0 -26
- package/knowledge-library/product-manager/history/.gitkeep +0 -0
- package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
- package/knowledge-library/product-manager/tech/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/context/current.txt +0 -42
- package/knowledge-library/qa-engineer/control/decisions.txt +0 -9
- package/knowledge-library/qa-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/qa-engineer/control/objectives.txt +0 -26
- package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/security-engineer/context/current.txt +0 -42
- package/knowledge-library/security-engineer/control/decisions.txt +0 -9
- package/knowledge-library/security-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/security-engineer/control/objectives.txt +0 -26
- package/knowledge-library/security-engineer/history/.gitkeep +0 -0
- package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/context/current.txt +0 -42
- package/knowledge-library/solutions-architect/control/decisions.txt +0 -9
- package/knowledge-library/solutions-architect/control/dependencies.txt +0 -19
- package/knowledge-library/solutions-architect/control/objectives.txt +0 -26
- package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/context/current.txt +0 -42
- package/knowledge-library/wearos-engineer/control/decisions.txt +0 -9
- package/knowledge-library/wearos-engineer/control/dependencies.txt +0 -19
- package/knowledge-library/wearos-engineer/control/objectives.txt +0 -26
- package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
- package/scripts/add-agent.sh +0 -323
- package/scripts/install.sh +0 -354
- package/src/adapters/factory.test.ts +0 -386
- package/src/adapters/factory.ts +0 -305
- package/src/adapters/index.ts +0 -113
- package/src/adapters/interfaces.ts +0 -268
- package/src/adapters/mcp/client.test.ts +0 -130
- package/src/adapters/mcp/client.ts +0 -451
- package/src/adapters/mcp/discovery.test.ts +0 -315
- package/src/adapters/mcp/discovery.ts +0 -340
- package/src/adapters/mcp/index.ts +0 -66
- package/src/adapters/mcp/mapper.test.ts +0 -218
- package/src/adapters/mcp/mapper.ts +0 -536
- package/src/adapters/mcp/registry.test.ts +0 -433
- package/src/adapters/mcp/registry.ts +0 -550
- package/src/adapters/mcp/types.ts +0 -258
- package/src/adapters/native/filesystem.test.ts +0 -350
- package/src/adapters/native/filesystem.ts +0 -393
- package/src/adapters/native/github.test.ts +0 -173
- package/src/adapters/native/github.ts +0 -627
- package/src/adapters/native/index.ts +0 -22
- package/src/adapters/native/selector.test.ts +0 -224
- package/src/adapters/native/selector.ts +0 -150
- package/src/adapters/types.ts +0 -270
- package/src/agents/compiler.test.ts +0 -410
- package/src/agents/compiler.ts +0 -424
- package/src/agents/index.ts +0 -37
- package/src/agents/loader.test.ts +0 -319
- package/src/agents/loader.ts +0 -143
- package/src/agents/resolver.test.ts +0 -282
- package/src/agents/resolver.ts +0 -262
- package/src/agents/types.ts +0 -97
- package/src/cache/index.ts +0 -38
- package/src/cache/interfaces.ts +0 -283
- package/src/cache/manager.test.ts +0 -266
- package/src/cache/manager.ts +0 -388
- package/src/cache/provider.test.ts +0 -485
- package/src/cache/provider.ts +0 -745
- package/src/cache/types.test.ts +0 -192
- package/src/cache/types.ts +0 -313
- package/src/cli/commands/build.test.ts +0 -248
- package/src/cli/commands/build.ts +0 -284
- package/src/cli/commands/cache.test.ts +0 -221
- package/src/cli/commands/cache.ts +0 -229
- package/src/cli/commands/index.ts +0 -63
- package/src/cli/commands/init.test.ts +0 -173
- package/src/cli/commands/init.ts +0 -296
- package/src/cli/commands/skills.test.ts +0 -272
- package/src/cli/commands/skills.ts +0 -348
- package/src/cli/commands/status.test.ts +0 -392
- package/src/cli/commands/status.ts +0 -332
- package/src/cli/commands/sync.test.ts +0 -213
- package/src/cli/commands/sync.ts +0 -251
- package/src/cli/commands/validate.test.ts +0 -216
- package/src/cli/commands/validate.ts +0 -340
- package/src/cli/index.test.ts +0 -190
- package/src/cli/index.ts +0 -493
- package/src/commands/context.test.ts +0 -163
- package/src/commands/context.ts +0 -111
- package/src/commands/index.ts +0 -56
- package/src/commands/loader.test.ts +0 -273
- package/src/commands/loader.ts +0 -355
- package/src/commands/registry.test.ts +0 -384
- package/src/commands/registry.ts +0 -248
- package/src/commands/runner.test.ts +0 -297
- package/src/commands/runner.ts +0 -222
- package/src/commands/types.ts +0 -361
- package/src/config/index.ts +0 -19
- package/src/config/loader.test.ts +0 -262
- package/src/config/loader.ts +0 -188
- package/src/config/types.ts +0 -154
- package/src/context/index.ts +0 -14
- package/src/context/loader.test.ts +0 -334
- package/src/context/loader.ts +0 -357
- package/src/index.test.ts +0 -13
- package/src/index.ts +0 -268
- package/src/knowledge-library/index.ts +0 -44
- package/src/knowledge-library/manager.test.ts +0 -536
- package/src/knowledge-library/manager.ts +0 -804
- package/src/knowledge-library/types.ts +0 -432
- package/src/skills/generator.test.ts +0 -602
- package/src/skills/generator.ts +0 -491
- package/src/skills/index.ts +0 -27
- package/src/skills/templates.ts +0 -520
- package/src/skills/types.ts +0 -251
- package/templates/completion-report.md +0 -72
- package/templates/feedback.md +0 -56
- package/templates/project-files/CLAUDE.md.template +0 -109
- package/templates/project-files/coreai.json.example +0 -47
- package/templates/project-files/mcp.json.template +0 -20
- package/templates/review-complete.md +0 -64
- package/templates/review-request.md +0 -67
- package/templates/task-assignment.md +0 -51
- package/tsconfig.build.json +0 -4
- package/tsconfig.json +0 -26
- package/tsup.config.ts +0 -23
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Command Runner Tests
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { createCommandRegistry } from './registry.js';
|
|
6
|
-
import { createCommandContext } from './context.js';
|
|
7
|
-
import {
|
|
8
|
-
runCommand,
|
|
9
|
-
executeWithDegradation,
|
|
10
|
-
createDegradingHandler,
|
|
11
|
-
StepTracker,
|
|
12
|
-
} from './runner.js';
|
|
13
|
-
import type { CommandDefinition, IntegrationDependency } from './types.js';
|
|
14
|
-
|
|
15
|
-
describe('Command Runner', () => {
|
|
16
|
-
describe('runCommand', () => {
|
|
17
|
-
it('should run a registered command', async () => {
|
|
18
|
-
const registry = createCommandRegistry();
|
|
19
|
-
const cmd: CommandDefinition<object, string> = {
|
|
20
|
-
name: 'greet',
|
|
21
|
-
description: 'Say hello',
|
|
22
|
-
category: 'core',
|
|
23
|
-
handler: async (args) => ({
|
|
24
|
-
success: true,
|
|
25
|
-
data: `Hello, ${args[0] ?? 'World'}!`,
|
|
26
|
-
}),
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
registry.register(cmd);
|
|
30
|
-
|
|
31
|
-
const result = await runCommand<string>(registry, 'greet', ['Test'], {});
|
|
32
|
-
|
|
33
|
-
expect(result.success).toBe(true);
|
|
34
|
-
expect(result.data).toBe('Hello, Test!');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('should return error for non-existent command', async () => {
|
|
38
|
-
const registry = createCommandRegistry();
|
|
39
|
-
|
|
40
|
-
const result = await runCommand(registry, 'non-existent', [], {});
|
|
41
|
-
|
|
42
|
-
expect(result.success).toBe(false);
|
|
43
|
-
expect(result.error).toContain('not found');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should return error for unavailable command', async () => {
|
|
47
|
-
const registry = createCommandRegistry();
|
|
48
|
-
registry.register({
|
|
49
|
-
name: 'needs-jira',
|
|
50
|
-
description: 'Needs Jira',
|
|
51
|
-
category: 'optional',
|
|
52
|
-
dependencies: [{ type: 'issue_tracker', required: true }],
|
|
53
|
-
handler: async () => ({ success: true }),
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const result = await runCommand(registry, 'needs-jira', [], {});
|
|
57
|
-
|
|
58
|
-
expect(result.success).toBe(false);
|
|
59
|
-
expect(result.error).toContain('Missing required integrations');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should use provided context', async () => {
|
|
63
|
-
const registry = createCommandRegistry();
|
|
64
|
-
let capturedRoot = '';
|
|
65
|
-
|
|
66
|
-
registry.register({
|
|
67
|
-
name: 'check-context',
|
|
68
|
-
description: 'Check context',
|
|
69
|
-
category: 'core',
|
|
70
|
-
handler: async (_args, _opts, ctx) => {
|
|
71
|
-
capturedRoot = ctx.projectRoot;
|
|
72
|
-
return { success: true };
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const context = createCommandContext({ projectRoot: '/custom/path' });
|
|
77
|
-
|
|
78
|
-
await runCommand(registry, 'check-context', [], { context });
|
|
79
|
-
|
|
80
|
-
expect(capturedRoot).toBe('/custom/path');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('should handle handler errors', async () => {
|
|
84
|
-
const registry = createCommandRegistry();
|
|
85
|
-
registry.register({
|
|
86
|
-
name: 'error-cmd',
|
|
87
|
-
description: 'Throws error',
|
|
88
|
-
category: 'core',
|
|
89
|
-
handler: async () => {
|
|
90
|
-
throw new Error('Handler failed');
|
|
91
|
-
},
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
const result = await runCommand(registry, 'error-cmd', [], {});
|
|
95
|
-
|
|
96
|
-
expect(result.success).toBe(false);
|
|
97
|
-
expect(result.error).toBe('Handler failed');
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('should track skipped steps for missing optional deps', async () => {
|
|
101
|
-
const registry = createCommandRegistry();
|
|
102
|
-
registry.register({
|
|
103
|
-
name: 'with-optional',
|
|
104
|
-
description: 'Has optional deps',
|
|
105
|
-
category: 'core',
|
|
106
|
-
dependencies: [{ type: 'documentation', required: false, description: 'For docs' }],
|
|
107
|
-
handler: async () => ({ success: true, data: 'done' }),
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
const result = await runCommand(registry, 'with-optional', [], {});
|
|
111
|
-
|
|
112
|
-
expect(result.success).toBe(true);
|
|
113
|
-
expect(result.skippedSteps).toBeDefined();
|
|
114
|
-
expect(result.skippedSteps?.length).toBeGreaterThan(0);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
describe('executeWithDegradation', () => {
|
|
119
|
-
it('should execute step when integration available', async () => {
|
|
120
|
-
// Create a mock context that has the integration
|
|
121
|
-
const mockContext = {
|
|
122
|
-
config: null,
|
|
123
|
-
adapterFactory: null,
|
|
124
|
-
projectRoot: process.cwd(),
|
|
125
|
-
hasIntegration: () => true,
|
|
126
|
-
getAdapterSafe: async () => ({}),
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const dep: IntegrationDependency = {
|
|
130
|
-
type: 'git',
|
|
131
|
-
required: false,
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
const result = await executeWithDegradation(mockContext, dep, async () => 'executed');
|
|
135
|
-
|
|
136
|
-
expect(result.skipped).toBe(false);
|
|
137
|
-
expect(result.result).toBe('executed');
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('should skip optional step when integration missing', async () => {
|
|
141
|
-
const mockContext = {
|
|
142
|
-
config: null,
|
|
143
|
-
adapterFactory: null,
|
|
144
|
-
projectRoot: process.cwd(),
|
|
145
|
-
hasIntegration: () => false,
|
|
146
|
-
getAdapterSafe: async () => null,
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const dep: IntegrationDependency = {
|
|
150
|
-
type: 'issue_tracker',
|
|
151
|
-
required: false,
|
|
152
|
-
description: 'For issue tracking',
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const result = await executeWithDegradation(mockContext, dep, async () => 'should not run');
|
|
156
|
-
|
|
157
|
-
expect(result.skipped).toBe(true);
|
|
158
|
-
expect(result.result).toBeNull();
|
|
159
|
-
expect(result.reason).toContain('issue tracking');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('should throw for missing required integration', async () => {
|
|
163
|
-
const mockContext = {
|
|
164
|
-
config: null,
|
|
165
|
-
adapterFactory: null,
|
|
166
|
-
projectRoot: process.cwd(),
|
|
167
|
-
hasIntegration: () => false,
|
|
168
|
-
getAdapterSafe: async () => null,
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
const dep: IntegrationDependency = {
|
|
172
|
-
type: 'git',
|
|
173
|
-
required: true,
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
await expect(
|
|
177
|
-
executeWithDegradation(mockContext, dep, async () => 'should not run')
|
|
178
|
-
).rejects.toThrow('Required integration missing');
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('should use fallback when available', async () => {
|
|
182
|
-
const mockContext = {
|
|
183
|
-
config: null,
|
|
184
|
-
adapterFactory: null,
|
|
185
|
-
projectRoot: process.cwd(),
|
|
186
|
-
hasIntegration: () => false,
|
|
187
|
-
getAdapterSafe: async () => null,
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
const dep: IntegrationDependency = {
|
|
191
|
-
type: 'documentation',
|
|
192
|
-
required: false,
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
const result = await executeWithDegradation(
|
|
196
|
-
mockContext,
|
|
197
|
-
dep,
|
|
198
|
-
async () => 'main step',
|
|
199
|
-
async () => 'fallback step'
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
expect(result.skipped).toBe(false);
|
|
203
|
-
expect(result.result).toBe('fallback step');
|
|
204
|
-
});
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
describe('createDegradingHandler', () => {
|
|
208
|
-
it('should create handler that tracks steps', async () => {
|
|
209
|
-
const handler = createDegradingHandler<object, string>(
|
|
210
|
-
async (_args, _opts, _ctx, tracker) => {
|
|
211
|
-
tracker.skip('Optional step', 'Not configured');
|
|
212
|
-
tracker.warn('Something to note');
|
|
213
|
-
return 'completed';
|
|
214
|
-
}
|
|
215
|
-
);
|
|
216
|
-
|
|
217
|
-
const mockContext = {
|
|
218
|
-
config: null,
|
|
219
|
-
adapterFactory: null,
|
|
220
|
-
projectRoot: process.cwd(),
|
|
221
|
-
hasIntegration: () => false,
|
|
222
|
-
getAdapterSafe: async () => null,
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
const result = await handler([], {}, mockContext);
|
|
226
|
-
|
|
227
|
-
expect(result.success).toBe(true);
|
|
228
|
-
expect(result.data).toBe('completed');
|
|
229
|
-
expect(result.skippedSteps).toHaveLength(1);
|
|
230
|
-
expect(result.warnings).toHaveLength(1);
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
it('should handle errors gracefully', async () => {
|
|
234
|
-
const handler = createDegradingHandler<object, string>(async () => {
|
|
235
|
-
throw new Error('Handler error');
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
const mockContext = {
|
|
239
|
-
config: null,
|
|
240
|
-
adapterFactory: null,
|
|
241
|
-
projectRoot: process.cwd(),
|
|
242
|
-
hasIntegration: () => false,
|
|
243
|
-
getAdapterSafe: async () => null,
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
const result = await handler([], {}, mockContext);
|
|
247
|
-
|
|
248
|
-
expect(result.success).toBe(false);
|
|
249
|
-
expect(result.error).toBe('Handler error');
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
describe('StepTracker', () => {
|
|
254
|
-
it('should track skipped steps', () => {
|
|
255
|
-
const tracker = new StepTracker();
|
|
256
|
-
|
|
257
|
-
tracker.skip('Step 1', 'Reason 1');
|
|
258
|
-
tracker.skip('Step 2', 'Reason 2');
|
|
259
|
-
|
|
260
|
-
expect(tracker.skippedSteps).toHaveLength(2);
|
|
261
|
-
expect(tracker.skippedSteps[0].step).toBe('Step 1');
|
|
262
|
-
expect(tracker.skippedSteps[0].reason).toBe('Reason 1');
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
it('should track warnings', () => {
|
|
266
|
-
const tracker = new StepTracker();
|
|
267
|
-
|
|
268
|
-
tracker.warn('Warning 1');
|
|
269
|
-
tracker.warn('Warning 2');
|
|
270
|
-
|
|
271
|
-
expect(tracker.warnings).toHaveLength(2);
|
|
272
|
-
expect(tracker.warnings[0]).toBe('Warning 1');
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
it('should try step with degradation', async () => {
|
|
276
|
-
const tracker = new StepTracker();
|
|
277
|
-
const mockContext = {
|
|
278
|
-
config: null,
|
|
279
|
-
adapterFactory: null,
|
|
280
|
-
projectRoot: process.cwd(),
|
|
281
|
-
hasIntegration: () => false,
|
|
282
|
-
getAdapterSafe: async () => null,
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
const result = await tracker.tryStep(
|
|
286
|
-
'Update docs',
|
|
287
|
-
mockContext,
|
|
288
|
-
{ type: 'documentation', required: false },
|
|
289
|
-
async () => 'updated'
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
expect(result).toBeNull();
|
|
293
|
-
expect(tracker.skippedSteps).toHaveLength(1);
|
|
294
|
-
expect(tracker.skippedSteps[0].step).toBe('Update docs');
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
});
|
package/src/commands/runner.ts
DELETED
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Command Runner
|
|
3
|
-
*
|
|
4
|
-
* Executes commands with graceful degradation support.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
CommandDefinition,
|
|
9
|
-
CommandResult,
|
|
10
|
-
CommandContext,
|
|
11
|
-
BaseCommandOptions,
|
|
12
|
-
IntegrationDependency,
|
|
13
|
-
} from './types.js';
|
|
14
|
-
import type { CommandRegistry } from './registry.js';
|
|
15
|
-
import { createCommandContext, cleanupContext, type CreateContextOptions } from './context.js';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Options for running a command
|
|
19
|
-
*/
|
|
20
|
-
export interface RunCommandOptions extends BaseCommandOptions {
|
|
21
|
-
/**
|
|
22
|
-
* Pre-created command context
|
|
23
|
-
*/
|
|
24
|
-
context?: CommandContext;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Whether to clean up context after execution
|
|
28
|
-
*/
|
|
29
|
-
cleanupAfter?: boolean;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Run a registered command
|
|
34
|
-
*/
|
|
35
|
-
export async function runCommand<TResult = unknown>(
|
|
36
|
-
registry: CommandRegistry,
|
|
37
|
-
name: string,
|
|
38
|
-
args: string[],
|
|
39
|
-
options: RunCommandOptions = {}
|
|
40
|
-
): Promise<CommandResult<TResult>> {
|
|
41
|
-
const entry = registry.get(name);
|
|
42
|
-
|
|
43
|
-
if (!entry) {
|
|
44
|
-
return {
|
|
45
|
-
success: false,
|
|
46
|
-
error: `Command not found: ${name}`,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (!entry.metadata.available) {
|
|
51
|
-
return {
|
|
52
|
-
success: false,
|
|
53
|
-
error: entry.metadata.unavailableReason ?? 'Command is unavailable',
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (!entry.definition) {
|
|
58
|
-
return {
|
|
59
|
-
success: false,
|
|
60
|
-
error: `Command ${name} is a markdown command and cannot be executed programmatically`,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Create or use provided context
|
|
65
|
-
const contextOptions: CreateContextOptions = {};
|
|
66
|
-
if (options.projectRoot) {
|
|
67
|
-
contextOptions.projectRoot = options.projectRoot;
|
|
68
|
-
}
|
|
69
|
-
const context = options.context ?? createCommandContext(contextOptions);
|
|
70
|
-
|
|
71
|
-
try {
|
|
72
|
-
const result = (await entry.definition.handler(
|
|
73
|
-
args,
|
|
74
|
-
options,
|
|
75
|
-
context
|
|
76
|
-
)) as CommandResult<TResult>;
|
|
77
|
-
|
|
78
|
-
// Add skipped steps for optional integrations that aren't available
|
|
79
|
-
if (entry.metadata.dependencies) {
|
|
80
|
-
// Find optional deps that can't be accessed in the current context
|
|
81
|
-
const optionalDeps = entry.metadata.dependencies.filter((d) => !d.required);
|
|
82
|
-
const optionalUnavailable = optionalDeps.filter((d) => !context.hasIntegration(d.type));
|
|
83
|
-
|
|
84
|
-
if (optionalUnavailable.length > 0) {
|
|
85
|
-
result.skippedSteps = result.skippedSteps ?? [];
|
|
86
|
-
for (const dep of optionalUnavailable) {
|
|
87
|
-
result.skippedSteps.push({
|
|
88
|
-
step: `${dep.type} integration`,
|
|
89
|
-
reason: dep.description ?? `${dep.type} not configured`,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return result;
|
|
96
|
-
} catch (error) {
|
|
97
|
-
return {
|
|
98
|
-
success: false,
|
|
99
|
-
error: error instanceof Error ? error.message : String(error),
|
|
100
|
-
};
|
|
101
|
-
} finally {
|
|
102
|
-
if (options.cleanupAfter !== false && !options.context) {
|
|
103
|
-
await cleanupContext(context);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Execute a step with graceful degradation
|
|
110
|
-
*
|
|
111
|
-
* If the required integration is not available, returns a skip result instead of failing.
|
|
112
|
-
*/
|
|
113
|
-
export async function executeWithDegradation<T>(
|
|
114
|
-
context: CommandContext,
|
|
115
|
-
dependency: IntegrationDependency,
|
|
116
|
-
step: () => Promise<T>,
|
|
117
|
-
fallback?: () => Promise<T>
|
|
118
|
-
): Promise<{ result: T | null; skipped: boolean; reason?: string }> {
|
|
119
|
-
if (!context.hasIntegration(dependency.type)) {
|
|
120
|
-
if (dependency.required) {
|
|
121
|
-
throw new Error(`Required integration missing: ${dependency.type}`);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Optional integration missing, try fallback or skip
|
|
125
|
-
if (fallback) {
|
|
126
|
-
try {
|
|
127
|
-
const result = await fallback();
|
|
128
|
-
return { result, skipped: false };
|
|
129
|
-
} catch {
|
|
130
|
-
return {
|
|
131
|
-
result: null,
|
|
132
|
-
skipped: true,
|
|
133
|
-
reason: dependency.description ?? `${dependency.type} not available`,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
result: null,
|
|
140
|
-
skipped: true,
|
|
141
|
-
reason: dependency.description ?? `${dependency.type} not available`,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Integration available, execute step
|
|
146
|
-
const result = await step();
|
|
147
|
-
return { result, skipped: false };
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Create a command handler with automatic degradation tracking
|
|
152
|
-
*/
|
|
153
|
-
export function createDegradingHandler<TOptions extends BaseCommandOptions, TResult>(
|
|
154
|
-
handler: (
|
|
155
|
-
args: string[],
|
|
156
|
-
options: TOptions,
|
|
157
|
-
context: CommandContext,
|
|
158
|
-
track: StepTracker
|
|
159
|
-
) => Promise<TResult>
|
|
160
|
-
): CommandDefinition<TOptions, TResult>['handler'] {
|
|
161
|
-
return async (args, options, context) => {
|
|
162
|
-
const tracker = new StepTracker();
|
|
163
|
-
|
|
164
|
-
try {
|
|
165
|
-
const data = await handler(args, options, context, tracker);
|
|
166
|
-
return {
|
|
167
|
-
success: true,
|
|
168
|
-
data,
|
|
169
|
-
warnings: tracker.warnings,
|
|
170
|
-
skippedSteps: tracker.skippedSteps,
|
|
171
|
-
};
|
|
172
|
-
} catch (error) {
|
|
173
|
-
return {
|
|
174
|
-
success: false,
|
|
175
|
-
error: error instanceof Error ? error.message : String(error),
|
|
176
|
-
warnings: tracker.warnings,
|
|
177
|
-
skippedSteps: tracker.skippedSteps,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Tracks skipped steps and warnings during command execution
|
|
185
|
-
*/
|
|
186
|
-
export class StepTracker {
|
|
187
|
-
readonly skippedSteps: { step: string; reason: string }[] = [];
|
|
188
|
-
readonly warnings: string[] = [];
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Record a skipped step
|
|
192
|
-
*/
|
|
193
|
-
skip(step: string, reason: string): void {
|
|
194
|
-
this.skippedSteps.push({ step, reason });
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Record a warning
|
|
199
|
-
*/
|
|
200
|
-
warn(message: string): void {
|
|
201
|
-
this.warnings.push(message);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Execute a step with optional fallback
|
|
206
|
-
*/
|
|
207
|
-
async tryStep<T>(
|
|
208
|
-
stepName: string,
|
|
209
|
-
context: CommandContext,
|
|
210
|
-
dependency: IntegrationDependency,
|
|
211
|
-
step: () => Promise<T>,
|
|
212
|
-
fallback?: () => Promise<T>
|
|
213
|
-
): Promise<T | null> {
|
|
214
|
-
const result = await executeWithDegradation(context, dependency, step, fallback);
|
|
215
|
-
|
|
216
|
-
if (result.skipped) {
|
|
217
|
-
this.skip(stepName, result.reason ?? 'Integration not available');
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return result.result;
|
|
221
|
-
}
|
|
222
|
-
}
|