@deimoscloud/coreai 0.1.8 → 0.1.10

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 (196) hide show
  1. package/dist/cli/index.js +5 -0
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/index.js +3 -1
  4. package/dist/index.js.map +1 -1
  5. package/package.json +6 -1
  6. package/.prettierrc +0 -9
  7. package/AGENT_SPEC.md +0 -347
  8. package/ARCHITECTURE.md +0 -547
  9. package/DRAFT_PRD.md +0 -1440
  10. package/IMPLEMENTATION_PLAN.md +0 -256
  11. package/PRODUCT.md +0 -473
  12. package/WORKFLOWS.md +0 -295
  13. package/commands/core/check-inbox.md +0 -34
  14. package/commands/core/delegate.md +0 -30
  15. package/commands/core/git-commit.md +0 -144
  16. package/commands/core/pr-create.md +0 -193
  17. package/commands/core/review.md +0 -56
  18. package/commands/core/sprint-status.md +0 -65
  19. package/commands/optional/docs-update.md +0 -200
  20. package/commands/optional/jira-create.md +0 -200
  21. package/commands/optional/jira-transition.md +0 -184
  22. package/commands/optional/worktree-cleanup.md +0 -167
  23. package/commands/optional/worktree-setup.md +0 -110
  24. package/eslint.config.js +0 -29
  25. package/jest.config.js +0 -22
  26. package/knowledge-library/README.md +0 -118
  27. package/knowledge-library/android-engineer/context/current.txt +0 -42
  28. package/knowledge-library/android-engineer/control/decisions.txt +0 -9
  29. package/knowledge-library/android-engineer/control/dependencies.txt +0 -19
  30. package/knowledge-library/android-engineer/control/objectives.txt +0 -26
  31. package/knowledge-library/android-engineer/history/.gitkeep +0 -0
  32. package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
  33. package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
  34. package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
  35. package/knowledge-library/architecture.txt +0 -61
  36. package/knowledge-library/backend-engineer/context/current.txt +0 -42
  37. package/knowledge-library/backend-engineer/control/decisions.txt +0 -9
  38. package/knowledge-library/backend-engineer/control/dependencies.txt +0 -19
  39. package/knowledge-library/backend-engineer/control/objectives.txt +0 -26
  40. package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
  41. package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
  42. package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
  43. package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
  44. package/knowledge-library/context.txt +0 -52
  45. package/knowledge-library/devops-engineer/context/current.txt +0 -42
  46. package/knowledge-library/devops-engineer/control/decisions.txt +0 -9
  47. package/knowledge-library/devops-engineer/control/dependencies.txt +0 -19
  48. package/knowledge-library/devops-engineer/control/objectives.txt +0 -26
  49. package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
  50. package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
  51. package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
  52. package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
  53. package/knowledge-library/engineering-manager/context/current.txt +0 -40
  54. package/knowledge-library/engineering-manager/control/decisions.txt +0 -9
  55. package/knowledge-library/engineering-manager/control/objectives.txt +0 -27
  56. package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
  57. package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
  58. package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
  59. package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
  60. package/knowledge-library/prd.txt +0 -81
  61. package/knowledge-library/product-manager/context/current.txt +0 -42
  62. package/knowledge-library/product-manager/control/decisions.txt +0 -9
  63. package/knowledge-library/product-manager/control/dependencies.txt +0 -19
  64. package/knowledge-library/product-manager/control/objectives.txt +0 -26
  65. package/knowledge-library/product-manager/history/.gitkeep +0 -0
  66. package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
  67. package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
  68. package/knowledge-library/product-manager/tech/.gitkeep +0 -0
  69. package/knowledge-library/qa-engineer/context/current.txt +0 -42
  70. package/knowledge-library/qa-engineer/control/decisions.txt +0 -9
  71. package/knowledge-library/qa-engineer/control/dependencies.txt +0 -19
  72. package/knowledge-library/qa-engineer/control/objectives.txt +0 -26
  73. package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
  74. package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
  75. package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
  76. package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
  77. package/knowledge-library/security-engineer/context/current.txt +0 -42
  78. package/knowledge-library/security-engineer/control/decisions.txt +0 -9
  79. package/knowledge-library/security-engineer/control/dependencies.txt +0 -19
  80. package/knowledge-library/security-engineer/control/objectives.txt +0 -26
  81. package/knowledge-library/security-engineer/history/.gitkeep +0 -0
  82. package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
  83. package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
  84. package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
  85. package/knowledge-library/solutions-architect/context/current.txt +0 -42
  86. package/knowledge-library/solutions-architect/control/decisions.txt +0 -9
  87. package/knowledge-library/solutions-architect/control/dependencies.txt +0 -19
  88. package/knowledge-library/solutions-architect/control/objectives.txt +0 -26
  89. package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
  90. package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
  91. package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
  92. package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
  93. package/knowledge-library/wearos-engineer/context/current.txt +0 -42
  94. package/knowledge-library/wearos-engineer/control/decisions.txt +0 -9
  95. package/knowledge-library/wearos-engineer/control/dependencies.txt +0 -19
  96. package/knowledge-library/wearos-engineer/control/objectives.txt +0 -26
  97. package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
  98. package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
  99. package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
  100. package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
  101. package/scripts/add-agent.sh +0 -323
  102. package/scripts/install.sh +0 -354
  103. package/src/adapters/factory.test.ts +0 -386
  104. package/src/adapters/factory.ts +0 -305
  105. package/src/adapters/index.ts +0 -113
  106. package/src/adapters/interfaces.ts +0 -268
  107. package/src/adapters/mcp/client.test.ts +0 -130
  108. package/src/adapters/mcp/client.ts +0 -451
  109. package/src/adapters/mcp/discovery.test.ts +0 -315
  110. package/src/adapters/mcp/discovery.ts +0 -340
  111. package/src/adapters/mcp/index.ts +0 -66
  112. package/src/adapters/mcp/mapper.test.ts +0 -218
  113. package/src/adapters/mcp/mapper.ts +0 -536
  114. package/src/adapters/mcp/registry.test.ts +0 -433
  115. package/src/adapters/mcp/registry.ts +0 -550
  116. package/src/adapters/mcp/types.ts +0 -258
  117. package/src/adapters/native/filesystem.test.ts +0 -350
  118. package/src/adapters/native/filesystem.ts +0 -393
  119. package/src/adapters/native/github.test.ts +0 -173
  120. package/src/adapters/native/github.ts +0 -627
  121. package/src/adapters/native/index.ts +0 -22
  122. package/src/adapters/native/selector.test.ts +0 -224
  123. package/src/adapters/native/selector.ts +0 -150
  124. package/src/adapters/types.ts +0 -270
  125. package/src/agents/compiler.test.ts +0 -399
  126. package/src/agents/compiler.ts +0 -422
  127. package/src/agents/index.ts +0 -37
  128. package/src/agents/loader.test.ts +0 -319
  129. package/src/agents/loader.ts +0 -143
  130. package/src/agents/resolver.test.ts +0 -282
  131. package/src/agents/resolver.ts +0 -262
  132. package/src/agents/types.ts +0 -97
  133. package/src/cache/index.ts +0 -38
  134. package/src/cache/interfaces.ts +0 -283
  135. package/src/cache/manager.test.ts +0 -266
  136. package/src/cache/manager.ts +0 -388
  137. package/src/cache/provider.test.ts +0 -485
  138. package/src/cache/provider.ts +0 -745
  139. package/src/cache/types.test.ts +0 -192
  140. package/src/cache/types.ts +0 -313
  141. package/src/cli/commands/build.test.ts +0 -248
  142. package/src/cli/commands/build.ts +0 -284
  143. package/src/cli/commands/cache.test.ts +0 -221
  144. package/src/cli/commands/cache.ts +0 -229
  145. package/src/cli/commands/index.ts +0 -63
  146. package/src/cli/commands/init.test.ts +0 -173
  147. package/src/cli/commands/init.ts +0 -296
  148. package/src/cli/commands/skills.test.ts +0 -272
  149. package/src/cli/commands/skills.ts +0 -348
  150. package/src/cli/commands/status.test.ts +0 -392
  151. package/src/cli/commands/status.ts +0 -332
  152. package/src/cli/commands/sync.test.ts +0 -213
  153. package/src/cli/commands/sync.ts +0 -251
  154. package/src/cli/commands/validate.test.ts +0 -216
  155. package/src/cli/commands/validate.ts +0 -340
  156. package/src/cli/index.test.ts +0 -190
  157. package/src/cli/index.ts +0 -493
  158. package/src/commands/context.test.ts +0 -163
  159. package/src/commands/context.ts +0 -111
  160. package/src/commands/index.ts +0 -56
  161. package/src/commands/loader.test.ts +0 -273
  162. package/src/commands/loader.ts +0 -355
  163. package/src/commands/registry.test.ts +0 -384
  164. package/src/commands/registry.ts +0 -248
  165. package/src/commands/runner.test.ts +0 -297
  166. package/src/commands/runner.ts +0 -222
  167. package/src/commands/types.ts +0 -361
  168. package/src/config/index.ts +0 -19
  169. package/src/config/loader.test.ts +0 -262
  170. package/src/config/loader.ts +0 -188
  171. package/src/config/types.ts +0 -154
  172. package/src/context/index.ts +0 -14
  173. package/src/context/loader.test.ts +0 -334
  174. package/src/context/loader.ts +0 -357
  175. package/src/index.test.ts +0 -13
  176. package/src/index.ts +0 -268
  177. package/src/knowledge-library/index.ts +0 -44
  178. package/src/knowledge-library/manager.test.ts +0 -536
  179. package/src/knowledge-library/manager.ts +0 -804
  180. package/src/knowledge-library/types.ts +0 -432
  181. package/src/skills/generator.test.ts +0 -602
  182. package/src/skills/generator.ts +0 -491
  183. package/src/skills/index.ts +0 -27
  184. package/src/skills/templates.ts +0 -520
  185. package/src/skills/types.ts +0 -251
  186. package/templates/completion-report.md +0 -72
  187. package/templates/feedback.md +0 -56
  188. package/templates/project-files/CLAUDE.md.template +0 -109
  189. package/templates/project-files/coreai.json.example +0 -47
  190. package/templates/project-files/mcp.json.template +0 -20
  191. package/templates/review-complete.md +0 -64
  192. package/templates/review-request.md +0 -67
  193. package/templates/task-assignment.md +0 -51
  194. package/tsconfig.build.json +0 -4
  195. package/tsconfig.json +0 -26
  196. 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
- });
@@ -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
- }