@output.ai/cli 0.8.1 → 0.8.3
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/assets/docker/docker-compose-dev.yml +1 -1
- package/dist/commands/agents/{init.d.ts → update.d.ts} +2 -4
- package/dist/commands/agents/update.js +29 -0
- package/dist/commands/agents/update.spec.js +84 -0
- package/dist/commands/dev/index.js +3 -0
- package/dist/commands/dev/index.spec.js +36 -0
- package/dist/generated/framework_version.json +1 -1
- package/dist/services/claude_client.js +1 -1
- package/dist/services/coding_agents.d.ts +12 -12
- package/dist/services/coding_agents.js +39 -91
- package/dist/services/coding_agents.spec.js +43 -59
- package/dist/services/workflow_planner.d.ts +1 -1
- package/dist/services/workflow_planner.js +1 -1
- package/dist/services/workflow_planner.spec.js +2 -2
- package/dist/templates/agent_instructions/CLAUDE.md.template +19 -0
- package/dist/test_helpers/mocks.d.ts +1 -1
- package/dist/test_helpers/mocks.js +1 -1
- package/dist/utils/process.js +4 -2
- package/package.json +2 -3
- package/dist/commands/agents/init.js +0 -43
- package/dist/commands/agents/init.spec.js +0 -109
- package/dist/templates/agent_instructions/dotoutputai/AGENTS.md.template +0 -435
- /package/dist/commands/agents/{init.spec.d.ts → update.spec.d.ts} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
-
import {
|
|
2
|
+
import { checkAgentStructure, prepareTemplateVariables, initializeAgentConfig, ensureOutputAISystem, ensureClaudePlugin } from './coding_agents.js';
|
|
3
3
|
import { access } from 'node:fs/promises';
|
|
4
4
|
import fs from 'node:fs/promises';
|
|
5
5
|
vi.mock('node:fs/promises');
|
|
@@ -26,35 +26,17 @@ describe('coding_agents service', () => {
|
|
|
26
26
|
beforeEach(() => {
|
|
27
27
|
vi.clearAllMocks();
|
|
28
28
|
});
|
|
29
|
-
describe('getAgentConfigDir', () => {
|
|
30
|
-
it('should return the correct path to .outputai directory', () => {
|
|
31
|
-
const result = getAgentConfigDir('/test/project');
|
|
32
|
-
expect(result).toBe('/test/project/.outputai');
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
describe('checkAgentConfigDirExists', () => {
|
|
36
|
-
it('should return true when .outputai directory exists', async () => {
|
|
37
|
-
vi.mocked(access).mockResolvedValue(undefined);
|
|
38
|
-
const result = await checkAgentConfigDirExists('/test/project');
|
|
39
|
-
expect(result).toBe(true);
|
|
40
|
-
expect(access).toHaveBeenCalledWith('/test/project/.outputai');
|
|
41
|
-
});
|
|
42
|
-
it('should return false when .outputai directory does not exist', async () => {
|
|
43
|
-
vi.mocked(access).mockRejectedValue({ code: 'ENOENT' });
|
|
44
|
-
const result = await checkAgentConfigDirExists('/test/project');
|
|
45
|
-
expect(result).toBe(false);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
29
|
describe('checkAgentStructure', () => {
|
|
49
|
-
it('should return needsInit true when
|
|
30
|
+
it('should return needsInit true when settings.json does not exist', async () => {
|
|
50
31
|
vi.mocked(access).mockRejectedValue({ code: 'ENOENT' });
|
|
32
|
+
vi.mocked(fs.readFile).mockRejectedValue({ code: 'ENOENT' });
|
|
51
33
|
const result = await checkAgentStructure('/test/project');
|
|
52
34
|
expect(result).toEqual({
|
|
53
35
|
isComplete: false,
|
|
54
36
|
needsInit: true
|
|
55
37
|
});
|
|
56
38
|
});
|
|
57
|
-
it('should return complete when
|
|
39
|
+
it('should return complete when settings and CLAUDE.md exist with valid configuration', async () => {
|
|
58
40
|
vi.mocked(access).mockResolvedValue(undefined);
|
|
59
41
|
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify({
|
|
60
42
|
extraKnownMarketplaces: {
|
|
@@ -113,19 +95,6 @@ describe('coding_agents service', () => {
|
|
|
113
95
|
expect(result.isComplete).toBe(false);
|
|
114
96
|
expect(result.needsInit).toBe(true);
|
|
115
97
|
});
|
|
116
|
-
it('should return needsInit true when settings.json does not exist', async () => {
|
|
117
|
-
vi.mocked(access).mockImplementation(async (path) => {
|
|
118
|
-
const pathStr = path.toString();
|
|
119
|
-
if (pathStr.endsWith('.claude/settings.json')) {
|
|
120
|
-
throw { code: 'ENOENT' };
|
|
121
|
-
}
|
|
122
|
-
return undefined;
|
|
123
|
-
});
|
|
124
|
-
vi.mocked(fs.readFile).mockRejectedValue({ code: 'ENOENT' });
|
|
125
|
-
const result = await checkAgentStructure('/test/project');
|
|
126
|
-
expect(result.isComplete).toBe(false);
|
|
127
|
-
expect(result.needsInit).toBe(true);
|
|
128
|
-
});
|
|
129
98
|
});
|
|
130
99
|
describe('prepareTemplateVariables', () => {
|
|
131
100
|
it('should return template variables with formatted date', () => {
|
|
@@ -141,19 +110,18 @@ describe('coding_agents service', () => {
|
|
|
141
110
|
vi.mocked(access).mockRejectedValue({ code: 'ENOENT' });
|
|
142
111
|
vi.mocked(fs.readFile).mockResolvedValue('template content');
|
|
143
112
|
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
144
|
-
vi.mocked(fs.symlink).mockResolvedValue(undefined);
|
|
145
113
|
});
|
|
146
|
-
it('should create exactly
|
|
114
|
+
it('should create exactly 2 outputs: settings.json and CLAUDE.md file', async () => {
|
|
147
115
|
await initializeAgentConfig({
|
|
148
116
|
projectRoot: '/test/project',
|
|
149
117
|
force: false
|
|
150
118
|
});
|
|
151
|
-
expect(fs.mkdir).toHaveBeenCalledTimes(
|
|
152
|
-
expect(fs.mkdir).toHaveBeenCalledWith('/test/project/.outputai', expect.objectContaining({ recursive: true }));
|
|
119
|
+
expect(fs.mkdir).toHaveBeenCalledTimes(1);
|
|
153
120
|
expect(fs.mkdir).toHaveBeenCalledWith('/test/project/.claude', expect.objectContaining({ recursive: true }));
|
|
154
|
-
expect(fs.writeFile).toHaveBeenCalledWith('/test/project/.outputai/AGENTS.md', expect.any(String), 'utf-8');
|
|
155
121
|
expect(fs.writeFile).toHaveBeenCalledWith('/test/project/.claude/settings.json', expect.any(String), 'utf-8');
|
|
156
|
-
expect(fs.
|
|
122
|
+
expect(fs.writeFile).toHaveBeenCalledWith('/test/project/CLAUDE.md', expect.any(String), 'utf-8');
|
|
123
|
+
// No symlink should be created - CLAUDE.md is now a real file
|
|
124
|
+
expect(fs.symlink).not.toHaveBeenCalled();
|
|
157
125
|
});
|
|
158
126
|
it('should skip existing files when force is false', async () => {
|
|
159
127
|
vi.mocked(access).mockResolvedValue(undefined);
|
|
@@ -162,7 +130,6 @@ describe('coding_agents service', () => {
|
|
|
162
130
|
force: false
|
|
163
131
|
});
|
|
164
132
|
expect(fs.writeFile).not.toHaveBeenCalled();
|
|
165
|
-
expect(fs.symlink).not.toHaveBeenCalled();
|
|
166
133
|
});
|
|
167
134
|
it('should overwrite existing files when force is true', async () => {
|
|
168
135
|
vi.mocked(access).mockResolvedValue(undefined);
|
|
@@ -173,15 +140,40 @@ describe('coding_agents service', () => {
|
|
|
173
140
|
});
|
|
174
141
|
expect(fs.writeFile).toHaveBeenCalled();
|
|
175
142
|
});
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
143
|
+
});
|
|
144
|
+
describe('ensureClaudePlugin', () => {
|
|
145
|
+
beforeEach(() => {
|
|
146
|
+
vi.mocked(fs.mkdir).mockResolvedValue(undefined);
|
|
147
|
+
vi.mocked(access).mockRejectedValue({ code: 'ENOENT' });
|
|
148
|
+
vi.mocked(fs.readFile).mockResolvedValue('template content');
|
|
149
|
+
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
150
|
+
});
|
|
151
|
+
it('should call registerPluginMarketplace and installOutputAIPlugin', async () => {
|
|
152
|
+
const { executeClaudeCommand } = await import('../utils/claude.js');
|
|
153
|
+
await ensureClaudePlugin('/test/project');
|
|
154
|
+
expect(executeClaudeCommand).toHaveBeenCalledWith(['plugin', 'marketplace', 'add', 'growthxai/output-claude-plugins'], '/test/project', { ignoreFailure: true });
|
|
155
|
+
expect(executeClaudeCommand).toHaveBeenCalledWith(['plugin', 'marketplace', 'update', 'outputai'], '/test/project');
|
|
156
|
+
expect(executeClaudeCommand).toHaveBeenCalledWith(['plugin', 'install', 'outputai@outputai', '--scope', 'project'], '/test/project');
|
|
157
|
+
});
|
|
158
|
+
it('should show error and prompt user when plugin commands fail', async () => {
|
|
159
|
+
const { executeClaudeCommand } = await import('../utils/claude.js');
|
|
160
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
161
|
+
vi.mocked(executeClaudeCommand)
|
|
162
|
+
.mockResolvedValueOnce(undefined) // marketplace add
|
|
163
|
+
.mockRejectedValueOnce(new Error('Plugin update failed')); // marketplace update
|
|
164
|
+
vi.mocked(confirm).mockResolvedValue(true);
|
|
165
|
+
await expect(ensureClaudePlugin('/test/project')).resolves.not.toThrow();
|
|
166
|
+
expect(confirm).toHaveBeenCalledWith(expect.objectContaining({
|
|
167
|
+
message: expect.stringContaining('proceed')
|
|
168
|
+
}));
|
|
169
|
+
});
|
|
170
|
+
it('should allow user to proceed without plugin setup if they confirm', async () => {
|
|
171
|
+
const { executeClaudeCommand } = await import('../utils/claude.js');
|
|
172
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
173
|
+
vi.mocked(executeClaudeCommand)
|
|
174
|
+
.mockRejectedValue(new Error('All plugin commands fail'));
|
|
175
|
+
vi.mocked(confirm).mockResolvedValue(true);
|
|
176
|
+
await expect(ensureClaudePlugin('/test/project')).resolves.not.toThrow();
|
|
185
177
|
});
|
|
186
178
|
});
|
|
187
179
|
describe('ensureOutputAISystem', () => {
|
|
@@ -190,7 +182,6 @@ describe('coding_agents service', () => {
|
|
|
190
182
|
vi.mocked(access).mockRejectedValue({ code: 'ENOENT' });
|
|
191
183
|
vi.mocked(fs.readFile).mockResolvedValue('template content');
|
|
192
184
|
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
193
|
-
vi.mocked(fs.symlink).mockResolvedValue(undefined);
|
|
194
185
|
});
|
|
195
186
|
it('should return immediately when agent structure is complete', async () => {
|
|
196
187
|
vi.mocked(access).mockResolvedValue(undefined);
|
|
@@ -205,12 +196,6 @@ describe('coding_agents service', () => {
|
|
|
205
196
|
await ensureOutputAISystem('/test/project');
|
|
206
197
|
expect(fs.mkdir).not.toHaveBeenCalled();
|
|
207
198
|
});
|
|
208
|
-
it('should auto-initialize when directory does not exist', async () => {
|
|
209
|
-
vi.mocked(access).mockRejectedValue({ code: 'ENOENT' });
|
|
210
|
-
await ensureOutputAISystem('/test/project');
|
|
211
|
-
expect(fs.mkdir).toHaveBeenCalled();
|
|
212
|
-
expect(fs.writeFile).toHaveBeenCalled();
|
|
213
|
-
});
|
|
214
199
|
it('should auto-initialize when settings.json is invalid', async () => {
|
|
215
200
|
vi.mocked(access).mockResolvedValue(undefined);
|
|
216
201
|
vi.mocked(fs.readFile).mockResolvedValue(JSON.stringify({
|
|
@@ -231,7 +216,6 @@ describe('coding_agents service', () => {
|
|
|
231
216
|
vi.mocked(access).mockRejectedValue({ code: 'ENOENT' });
|
|
232
217
|
vi.mocked(fs.readFile).mockResolvedValue('template content');
|
|
233
218
|
vi.mocked(fs.writeFile).mockResolvedValue(undefined);
|
|
234
|
-
vi.mocked(fs.symlink).mockResolvedValue(undefined);
|
|
235
219
|
});
|
|
236
220
|
it('should show error and prompt user when registerPluginMarketplace fails', async () => {
|
|
237
221
|
const { executeClaudeCommand } = await import('../utils/claude.js');
|
|
@@ -8,7 +8,7 @@ export declare function generatePlanName(description: string, date?: Date): Prom
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function writePlanFile(planName: string, content: string, projectRoot: string): Promise<string>;
|
|
10
10
|
/**
|
|
11
|
-
* Update agent templates by
|
|
11
|
+
* Update agent templates by reinitializing with force flag
|
|
12
12
|
* This recreates all agent configuration files, overwriting existing ones
|
|
13
13
|
* @param projectRoot - Root directory of the project
|
|
14
14
|
*/
|
|
@@ -36,7 +36,7 @@ export async function writePlanFile(planName, content, projectRoot) {
|
|
|
36
36
|
return planFilePath;
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
|
-
* Update agent templates by
|
|
39
|
+
* Update agent templates by reinitializing with force flag
|
|
40
40
|
* This recreates all agent configuration files, overwriting existing ones
|
|
41
41
|
* @param projectRoot - Root directory of the project
|
|
42
42
|
*/
|
|
@@ -98,7 +98,7 @@ describe('workflow-planner service', () => {
|
|
|
98
98
|
});
|
|
99
99
|
});
|
|
100
100
|
describe('updateAgentTemplates', () => {
|
|
101
|
-
it('should invoke
|
|
101
|
+
it('should invoke initializeAgentConfig with force flag', async () => {
|
|
102
102
|
vi.mocked(initializeAgentConfig).mockResolvedValue();
|
|
103
103
|
await updateAgentTemplates('/test/project');
|
|
104
104
|
expect(initializeAgentConfig).toHaveBeenCalledWith({
|
|
@@ -106,7 +106,7 @@ describe('workflow-planner service', () => {
|
|
|
106
106
|
force: true
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
|
-
it('should propagate errors from
|
|
109
|
+
it('should propagate errors from initializeAgentConfig', async () => {
|
|
110
110
|
vi.mocked(initializeAgentConfig).mockRejectedValue(new Error('Failed to write templates'));
|
|
111
111
|
await expect(updateAgentTemplates('/test/project'))
|
|
112
112
|
.rejects.toThrow('Failed to write templates');
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This is an **Output.ai** project - a framework for building reliable, production-ready LLM workflows and agents.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
For full framework documentation, commands, and AI-assisted workflow development, install our Claude Code plugins:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
claude plugin marketplace add growthxai/output-claude-plugins
|
|
11
|
+
claude plugin install outputai@outputai --scope project
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Project-Specific Instructions
|
|
17
|
+
|
|
18
|
+
<!-- Add your project-specific instructions below -->
|
|
19
|
+
|
|
@@ -11,7 +11,7 @@ export declare const mockLLM: {
|
|
|
11
11
|
generateText: import("vitest").Mock<(...args: any[]) => any>;
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
|
-
* Mock for child_process spawn (for
|
|
14
|
+
* Mock for child_process spawn (for agent commands)
|
|
15
15
|
*/
|
|
16
16
|
export declare const mockSpawn: import("vitest").Mock<(...args: any[]) => any>;
|
|
17
17
|
/**
|
package/dist/utils/process.js
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { ux } from '@oclif/core';
|
|
3
|
+
import debugFactory from 'debug';
|
|
3
4
|
import { getErrorMessage } from './error_utils.js';
|
|
5
|
+
const debug = debugFactory('output-cli:process');
|
|
4
6
|
export async function executeCommand(command, args, cwd) {
|
|
5
7
|
const stderrLines = [];
|
|
6
8
|
const proc = spawn(command, args, { cwd });
|
|
7
9
|
const handleStdout = (data) => {
|
|
8
10
|
const line = data.toString().trim();
|
|
9
11
|
if (line) {
|
|
10
|
-
|
|
12
|
+
debug(line);
|
|
11
13
|
}
|
|
12
14
|
};
|
|
13
15
|
const handleStderr = (data) => {
|
|
14
16
|
const line = data.toString().trim();
|
|
15
17
|
if (line) {
|
|
16
18
|
stderrLines.push(line);
|
|
17
|
-
|
|
19
|
+
debug(line);
|
|
18
20
|
}
|
|
19
21
|
};
|
|
20
22
|
proc.stdout.on('data', handleStdout);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@output.ai/cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.3",
|
|
4
4
|
"description": "CLI for Output.ai workflow generation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -18,8 +18,7 @@
|
|
|
18
18
|
"copy-assets": "./bin/copyassets.sh",
|
|
19
19
|
"test": "vitest run",
|
|
20
20
|
"generate:api": "orval --config ./orval.config.ts",
|
|
21
|
-
"
|
|
22
|
-
"prebuild": "npm run generate:api && npm run update:versions"
|
|
21
|
+
"prebuild": "npm run generate:api"
|
|
23
22
|
},
|
|
24
23
|
"dependencies": {
|
|
25
24
|
"@anthropic-ai/claude-agent-sdk": "0.1.71",
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { Command, Flags } from '@oclif/core';
|
|
2
|
-
import { initializeAgentConfig } from '#services/coding_agents.js';
|
|
3
|
-
import { getErrorMessage, getErrorCode } from '#utils/error_utils.js';
|
|
4
|
-
export default class Init extends Command {
|
|
5
|
-
static description = 'Initialize agent configuration files for Claude Code plugin integration';
|
|
6
|
-
static examples = [
|
|
7
|
-
'<%= config.bin %> <%= command.id %>',
|
|
8
|
-
'<%= config.bin %> <%= command.id %> --force'
|
|
9
|
-
];
|
|
10
|
-
static args = {};
|
|
11
|
-
static flags = {
|
|
12
|
-
force: Flags.boolean({
|
|
13
|
-
char: 'f',
|
|
14
|
-
description: 'Overwrite existing files',
|
|
15
|
-
default: false
|
|
16
|
-
})
|
|
17
|
-
};
|
|
18
|
-
async run() {
|
|
19
|
-
const { flags } = await this.parse(Init);
|
|
20
|
-
this.log('Initializing agent configuration for Claude Code...');
|
|
21
|
-
try {
|
|
22
|
-
await initializeAgentConfig({
|
|
23
|
-
projectRoot: process.cwd(),
|
|
24
|
-
force: flags.force
|
|
25
|
-
});
|
|
26
|
-
this.log('Agent configuration initialized successfully!');
|
|
27
|
-
this.log('');
|
|
28
|
-
this.log('Configured:');
|
|
29
|
-
this.log(' - Registered marketplace: growthxai/output-claude-plugins');
|
|
30
|
-
this.log(' - Updated marketplace: outputai');
|
|
31
|
-
this.log(' - Installed plugin: outputai@outputai');
|
|
32
|
-
this.log('');
|
|
33
|
-
this.log('Claude Code will automatically use the OutputAI plugin.');
|
|
34
|
-
}
|
|
35
|
-
catch (error) {
|
|
36
|
-
if (getErrorCode(error) === 'EACCES') {
|
|
37
|
-
this.error('Permission denied. Please check file permissions and try again.');
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
this.error(`Failed to initialize agent configuration: ${getErrorMessage(error)}`);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
3
|
-
import Init from './init.js';
|
|
4
|
-
import { initializeAgentConfig } from '#services/coding_agents.js';
|
|
5
|
-
vi.mock('#services/coding_agents.js', () => ({
|
|
6
|
-
initializeAgentConfig: vi.fn(),
|
|
7
|
-
AGENT_CONFIGS: { 'claude-code': { id: 'claude-code' } }
|
|
8
|
-
}));
|
|
9
|
-
vi.mock('#config.js', () => ({
|
|
10
|
-
AGENT_CONFIG_DIR: '.outputai'
|
|
11
|
-
}));
|
|
12
|
-
describe('agents init', () => {
|
|
13
|
-
const createTestCommand = (args = []) => {
|
|
14
|
-
const cmd = new Init(args, {});
|
|
15
|
-
cmd.log = vi.fn();
|
|
16
|
-
cmd.warn = vi.fn();
|
|
17
|
-
cmd.error = vi.fn();
|
|
18
|
-
cmd.debug = vi.fn();
|
|
19
|
-
cmd.parse = vi.fn();
|
|
20
|
-
return cmd;
|
|
21
|
-
};
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
vi.clearAllMocks();
|
|
24
|
-
vi.mocked(initializeAgentConfig).mockResolvedValue(undefined);
|
|
25
|
-
});
|
|
26
|
-
afterEach(() => {
|
|
27
|
-
vi.restoreAllMocks();
|
|
28
|
-
});
|
|
29
|
-
describe('command structure', () => {
|
|
30
|
-
it('should have correct description', () => {
|
|
31
|
-
expect(Init.description).toBeDefined();
|
|
32
|
-
expect(Init.description).toContain('agent configuration');
|
|
33
|
-
});
|
|
34
|
-
it('should have correct examples without agent-provider flag', () => {
|
|
35
|
-
expect(Init.examples).toBeDefined();
|
|
36
|
-
expect(Array.isArray(Init.examples)).toBe(true);
|
|
37
|
-
const examplesStr = Init.examples.join(' ');
|
|
38
|
-
expect(examplesStr).not.toContain('agent-provider');
|
|
39
|
-
});
|
|
40
|
-
it('should have no required arguments', () => {
|
|
41
|
-
expect(Init.args).toBeDefined();
|
|
42
|
-
expect(Object.keys(Init.args)).toHaveLength(0);
|
|
43
|
-
});
|
|
44
|
-
it('should only have force flag', () => {
|
|
45
|
-
expect(Init.flags).toBeDefined();
|
|
46
|
-
expect(Init.flags.force).toBeDefined();
|
|
47
|
-
expect(Object.keys(Init.flags)).toHaveLength(1);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
describe('successful execution', () => {
|
|
51
|
-
it('should call initializeAgentConfig with correct options', async () => {
|
|
52
|
-
const cmd = createTestCommand();
|
|
53
|
-
cmd.parse.mockResolvedValue({ flags: { force: false }, args: {} });
|
|
54
|
-
await cmd.run();
|
|
55
|
-
expect(initializeAgentConfig).toHaveBeenCalledWith({
|
|
56
|
-
projectRoot: expect.any(String),
|
|
57
|
-
force: false
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
it('should pass force flag to initializeAgentConfig', async () => {
|
|
61
|
-
const cmd = createTestCommand(['--force']);
|
|
62
|
-
cmd.parse.mockResolvedValue({ flags: { force: true }, args: {} });
|
|
63
|
-
await cmd.run();
|
|
64
|
-
expect(initializeAgentConfig).toHaveBeenCalledWith({
|
|
65
|
-
projectRoot: expect.any(String),
|
|
66
|
-
force: true
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
it('should display success messages', async () => {
|
|
70
|
-
const cmd = createTestCommand();
|
|
71
|
-
cmd.parse.mockResolvedValue({ flags: { force: false }, args: {} });
|
|
72
|
-
await cmd.run();
|
|
73
|
-
expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('initialized successfully'));
|
|
74
|
-
expect(cmd.log).toHaveBeenCalledWith('Configured:');
|
|
75
|
-
expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('OutputAI plugin'));
|
|
76
|
-
});
|
|
77
|
-
it('should display plugin configuration messages', async () => {
|
|
78
|
-
const cmd = createTestCommand();
|
|
79
|
-
cmd.parse.mockResolvedValue({ flags: { force: false }, args: {} });
|
|
80
|
-
await cmd.run();
|
|
81
|
-
expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('marketplace'));
|
|
82
|
-
expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('plugin'));
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
describe('error handling', () => {
|
|
86
|
-
it('should handle permission errors', async () => {
|
|
87
|
-
vi.mocked(initializeAgentConfig).mockRejectedValue({ code: 'EACCES' });
|
|
88
|
-
const cmd = createTestCommand();
|
|
89
|
-
cmd.parse.mockResolvedValue({ flags: { force: false }, args: {} });
|
|
90
|
-
await cmd.run();
|
|
91
|
-
expect(cmd.error).toHaveBeenCalledWith(expect.stringContaining('Permission denied'));
|
|
92
|
-
});
|
|
93
|
-
it('should handle general errors with message', async () => {
|
|
94
|
-
vi.mocked(initializeAgentConfig).mockRejectedValue(new Error('Something went wrong'));
|
|
95
|
-
const cmd = createTestCommand();
|
|
96
|
-
cmd.parse.mockResolvedValue({ flags: { force: false }, args: {} });
|
|
97
|
-
await cmd.run();
|
|
98
|
-
expect(cmd.error).toHaveBeenCalledWith(expect.stringContaining('Failed to initialize agent configuration'));
|
|
99
|
-
expect(cmd.error).toHaveBeenCalledWith(expect.stringContaining('Something went wrong'));
|
|
100
|
-
});
|
|
101
|
-
it('should handle Claude CLI not found error', async () => {
|
|
102
|
-
vi.mocked(initializeAgentConfig).mockRejectedValue(new Error('Claude CLI not found. Please install Claude Code CLI and ensure \'claude\' is in your PATH.'));
|
|
103
|
-
const cmd = createTestCommand();
|
|
104
|
-
cmd.parse.mockResolvedValue({ flags: { force: false }, args: {} });
|
|
105
|
-
await cmd.run();
|
|
106
|
-
expect(cmd.error).toHaveBeenCalledWith(expect.stringContaining('Claude CLI not found'));
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
});
|