apero-kit-cli 1.7.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,263 +0,0 @@
1
- import fs from 'fs-extra';
2
- import { join, resolve } from 'path';
3
- import chalk from 'chalk';
4
- import ora from 'ora';
5
- import { KITS, getKit } from '../kits/index.js';
6
- import { resolveSource, getTargetDir, TARGETS } from '../utils/paths.js';
7
- import { copyItems, copyAllOfType, copyRouter, copyHooks, copyBaseFiles, copyAgentsMd } from '../utils/copy.js';
8
- import { createInitialState } from '../utils/state.js';
9
- import {
10
- promptProjectName,
11
- promptKit,
12
- promptTarget,
13
- promptAgents,
14
- promptSkills,
15
- promptCommands,
16
- promptIncludeRouter,
17
- promptIncludeHooks,
18
- promptConfirm,
19
- promptExistingTarget
20
- } from '../utils/prompts.js';
21
-
22
- export async function initCommand(projectName, options) {
23
- console.log('');
24
-
25
- // 1. Get project name (support current directory with "." or empty)
26
- let projectDir;
27
- let isCurrentDir = false;
28
-
29
- if (!projectName || projectName === '.') {
30
- // Use current directory
31
- projectDir = process.cwd();
32
- projectName = '.';
33
- isCurrentDir = true;
34
- console.log(chalk.gray(`Initializing in current directory: ${projectDir}`));
35
- } else {
36
- projectDir = resolve(process.cwd(), projectName);
37
- }
38
-
39
- // 2. Get target early to check existing
40
- let target = options.target || 'claude';
41
- if (!TARGETS[target]) {
42
- console.log(chalk.yellow(`Unknown target "${target}", using "claude"`));
43
- target = 'claude';
44
- }
45
-
46
- const targetDir = getTargetDir(projectDir, target);
47
- let existingAction = null;
48
-
49
- // Check if target directory (.claude, .opencode, etc.) already exists
50
- if (fs.existsSync(targetDir) && !options.force) {
51
- if (!process.stdin.isTTY) {
52
- // Non-interactive mode - skip
53
- console.log(chalk.yellow(`${TARGETS[target]} already exists. Use --force to override.`));
54
- return;
55
- }
56
-
57
- existingAction = await promptExistingTarget(TARGETS[target]);
58
-
59
- if (existingAction === 'skip') {
60
- console.log(chalk.yellow('Skipped. No changes made.'));
61
- return;
62
- }
63
- }
64
-
65
- // For new project directory, check if it exists
66
- if (!isCurrentDir && fs.existsSync(projectDir) && !options.force) {
67
- const files = fs.readdirSync(projectDir);
68
- if (files.length > 0 && !existingAction) {
69
- console.log(chalk.red(`Directory "${projectName}" already exists and is not empty.`));
70
- console.log(chalk.gray('Use --force to overwrite.'));
71
- return;
72
- }
73
- }
74
-
75
- // 3. Resolve source
76
- const source = resolveSource(options.source);
77
- if (source.error) {
78
- console.log(chalk.red(`Error: ${source.error}`));
79
- return;
80
- }
81
-
82
- console.log(chalk.gray(`Source: ${source.path}`));
83
-
84
- // 4. Get kit
85
- let kitName = options.kit;
86
- if (!kitName && !options.force) {
87
- kitName = await promptKit();
88
- } else if (!kitName) {
89
- kitName = 'engineer'; // Default kit for --force mode
90
- }
91
-
92
- // 5. Set merge mode based on existing action
93
- const mergeMode = existingAction === 'merge';
94
-
95
- // 6. Prepare what to install
96
- let toInstall = {
97
- agents: [],
98
- commands: [],
99
- skills: [],
100
- workflows: [],
101
- includeRouter: false,
102
- includeHooks: false
103
- };
104
-
105
- if (kitName === 'custom') {
106
- // Custom mode - prompt for everything
107
- console.log(chalk.cyan('\nCustom kit configuration:'));
108
- toInstall.agents = await promptAgents(source.claudeDir);
109
- toInstall.skills = await promptSkills(source.claudeDir);
110
- toInstall.commands = await promptCommands(source.claudeDir);
111
- toInstall.includeRouter = await promptIncludeRouter();
112
- toInstall.includeHooks = await promptIncludeHooks();
113
- } else {
114
- const kit = getKit(kitName);
115
- if (!kit) {
116
- console.log(chalk.red(`Unknown kit: ${kitName}`));
117
- console.log(chalk.gray(`Available kits: ${Object.keys(KITS).join(', ')}`));
118
- return;
119
- }
120
-
121
- toInstall = {
122
- agents: kit.agents,
123
- commands: kit.commands,
124
- skills: kit.skills,
125
- workflows: kit.workflows || [],
126
- includeRouter: kit.includeRouter,
127
- includeHooks: kit.includeHooks
128
- };
129
- }
130
-
131
- // 6. Confirm
132
- console.log(chalk.cyan('\nWill create:'));
133
- console.log(chalk.white(` Project: ${projectName}/`));
134
- console.log(chalk.white(` Target: ${TARGETS[target]}/`));
135
- console.log(chalk.white(` Kit: ${kitName}`));
136
-
137
- if (Array.isArray(toInstall.agents)) {
138
- console.log(chalk.gray(` Agents: ${toInstall.agents.length}`));
139
- }
140
- if (Array.isArray(toInstall.skills)) {
141
- console.log(chalk.gray(` Skills: ${toInstall.skills.length}`));
142
- }
143
- if (Array.isArray(toInstall.commands)) {
144
- console.log(chalk.gray(` Commands: ${toInstall.commands.length}`));
145
- }
146
-
147
- console.log('');
148
-
149
- // Skip confirmation if --force is set
150
- if (!options.force) {
151
- if (!await promptConfirm('Proceed?')) {
152
- console.log(chalk.yellow('Cancelled.'));
153
- return;
154
- }
155
- }
156
-
157
- // 7. Create project
158
- const spinner = ora('Creating project...').start();
159
-
160
- try {
161
- // Create project directory
162
- await fs.ensureDir(projectDir);
163
-
164
- // Create target directory
165
- const targetDir = getTargetDir(projectDir, target);
166
- await fs.ensureDir(targetDir);
167
-
168
- // Copy agents
169
- spinner.text = mergeMode ? 'Merging agents...' : 'Copying agents...';
170
- if (toInstall.agents === 'all') {
171
- await copyAllOfType('agents', source.claudeDir, targetDir, mergeMode);
172
- } else if (toInstall.agents.length > 0) {
173
- await copyItems(toInstall.agents, 'agents', source.claudeDir, targetDir, mergeMode);
174
- }
175
-
176
- // Copy skills
177
- spinner.text = mergeMode ? 'Merging skills...' : 'Copying skills...';
178
- if (toInstall.skills === 'all') {
179
- await copyAllOfType('skills', source.claudeDir, targetDir, mergeMode);
180
- } else if (toInstall.skills.length > 0) {
181
- await copyItems(toInstall.skills, 'skills', source.claudeDir, targetDir, mergeMode);
182
- }
183
-
184
- // Copy commands
185
- spinner.text = mergeMode ? 'Merging commands...' : 'Copying commands...';
186
- if (toInstall.commands === 'all') {
187
- await copyAllOfType('commands', source.claudeDir, targetDir, mergeMode);
188
- } else if (toInstall.commands.length > 0) {
189
- await copyItems(toInstall.commands, 'commands', source.claudeDir, targetDir, mergeMode);
190
- }
191
-
192
- // Copy workflows
193
- spinner.text = mergeMode ? 'Merging workflows...' : 'Copying workflows...';
194
- if (toInstall.workflows === 'all') {
195
- await copyAllOfType('workflows', source.claudeDir, targetDir, mergeMode);
196
- } else if (toInstall.workflows && toInstall.workflows.length > 0) {
197
- await copyItems(toInstall.workflows, 'workflows', source.claudeDir, targetDir, mergeMode);
198
- }
199
-
200
- // Copy router
201
- if (toInstall.includeRouter) {
202
- spinner.text = mergeMode ? 'Merging router...' : 'Copying router...';
203
- await copyRouter(source.claudeDir, targetDir, mergeMode);
204
- }
205
-
206
- // Copy hooks
207
- if (toInstall.includeHooks) {
208
- spinner.text = mergeMode ? 'Merging hooks...' : 'Copying hooks...';
209
- await copyHooks(source.claudeDir, targetDir, mergeMode);
210
- }
211
-
212
- // Copy base files
213
- spinner.text = mergeMode ? 'Merging base files...' : 'Copying base files...';
214
- await copyBaseFiles(source.claudeDir, targetDir, mergeMode);
215
-
216
- // Copy AGENTS.md
217
- if (source.agentsMd) {
218
- await copyAgentsMd(source.agentsMd, projectDir, mergeMode);
219
- }
220
-
221
- // Create state file
222
- spinner.text = 'Saving state...';
223
- await createInitialState(projectDir, {
224
- kit: kitName,
225
- source: source.path,
226
- target: TARGETS[target],
227
- installed: {
228
- agents: toInstall.agents === 'all' ? ['all'] : toInstall.agents,
229
- skills: toInstall.skills === 'all' ? ['all'] : toInstall.skills,
230
- commands: toInstall.commands === 'all' ? ['all'] : toInstall.commands,
231
- workflows: toInstall.workflows === 'all' ? ['all'] : (toInstall.workflows || []),
232
- router: toInstall.includeRouter,
233
- hooks: toInstall.includeHooks
234
- }
235
- });
236
-
237
- const actionWord = mergeMode ? 'merged' : (existingAction === 'override' ? 'overridden' : 'created');
238
- spinner.succeed(chalk.green(`Project ${actionWord} successfully!`));
239
-
240
- // Print next steps
241
- console.log('');
242
- if (!isCurrentDir) {
243
- console.log(chalk.cyan('Next steps:'));
244
- console.log(chalk.white(` cd ${projectName}`));
245
- console.log(chalk.white(' # Start coding with Claude Code'));
246
- } else {
247
- console.log(chalk.cyan('Ready to code with Claude Code!'));
248
- }
249
- console.log('');
250
- console.log(chalk.gray('Useful commands:'));
251
- console.log(chalk.gray(' ak status - Check file status'));
252
- console.log(chalk.gray(' ak add <item> - Add more agents/skills'));
253
- console.log(chalk.gray(' ak update - Sync from source'));
254
- console.log('');
255
-
256
- } catch (error) {
257
- spinner.fail(chalk.red('Failed to create project'));
258
- console.error(chalk.red(error.message));
259
- if (process.env.DEBUG) {
260
- console.error(error.stack);
261
- }
262
- }
263
- }
@@ -1,190 +0,0 @@
1
- import chalk from 'chalk';
2
- import { KITS, getKitList } from '../kits/index.js';
3
- import { resolveSource } from '../utils/paths.js';
4
- import { listAvailable } from '../utils/copy.js';
5
-
6
- export async function listCommand(type, options = {}) {
7
- const validTypes = ['kits', 'agents', 'skills', 'commands', 'workflows'];
8
-
9
- // If no type specified, show help
10
- if (!type) {
11
- console.log(chalk.cyan('\nAvailable list commands:'));
12
- console.log(chalk.white(' ak list kits - List available kits'));
13
- console.log(chalk.white(' ak list agents - List available agents'));
14
- console.log(chalk.white(' ak list skills - List available skills'));
15
- console.log(chalk.white(' ak list commands - List available commands'));
16
- console.log(chalk.white(' ak list workflows - List available workflows'));
17
- console.log('');
18
- return;
19
- }
20
-
21
- // Normalize type
22
- type = type.toLowerCase();
23
- if (!validTypes.includes(type)) {
24
- console.log(chalk.red(`Unknown type: ${type}`));
25
- console.log(chalk.gray(`Valid types: ${validTypes.join(', ')}`));
26
- return;
27
- }
28
-
29
- // List kits (doesn't need source)
30
- if (type === 'kits') {
31
- listKits();
32
- return;
33
- }
34
-
35
- // For other types, need source
36
- const source = resolveSource(options.source);
37
- if (source.error) {
38
- console.log(chalk.red(`Error: ${source.error}`));
39
- return;
40
- }
41
-
42
- console.log(chalk.gray(`Source: ${source.path}\n`));
43
-
44
- switch (type) {
45
- case 'agents':
46
- listAgents(source.claudeDir);
47
- break;
48
- case 'skills':
49
- listSkills(source.claudeDir);
50
- break;
51
- case 'commands':
52
- listCommands(source.claudeDir);
53
- break;
54
- case 'workflows':
55
- listWorkflows(source.claudeDir);
56
- break;
57
- }
58
- }
59
-
60
- function listKits() {
61
- const kits = getKitList();
62
-
63
- console.log(chalk.cyan.bold('\nAvailable Kits:\n'));
64
-
65
- for (const kit of kits) {
66
- const colorFn = chalk[kit.color] || chalk.white;
67
- console.log(` ${kit.emoji} ${colorFn.bold(kit.name.padEnd(12))} - ${kit.description}`);
68
-
69
- // Show details
70
- if (Array.isArray(kit.agents)) {
71
- console.log(chalk.gray(` Agents: ${kit.agents.length} | Skills: ${kit.skills.length} | Commands: ${kit.commands.length}`));
72
- } else {
73
- console.log(chalk.gray(' Includes: ALL agents, skills, commands'));
74
- }
75
- }
76
-
77
- console.log(`\n 🔧 ${chalk.bold('custom'.padEnd(12))} - Pick your own agents, skills, and commands`);
78
- console.log('');
79
- }
80
-
81
- function listAgents(sourceDir) {
82
- const agents = listAvailable('agents', sourceDir);
83
-
84
- console.log(chalk.cyan.bold(`Available Agents (${agents.length}):\n`));
85
-
86
- if (agents.length === 0) {
87
- console.log(chalk.gray(' No agents found'));
88
- return;
89
- }
90
-
91
- // Group into columns
92
- const cols = 3;
93
- const rows = Math.ceil(agents.length / cols);
94
-
95
- for (let i = 0; i < rows; i++) {
96
- let line = ' ';
97
- for (let j = 0; j < cols; j++) {
98
- const idx = i + j * rows;
99
- if (idx < agents.length) {
100
- line += chalk.white(agents[idx].name.padEnd(25));
101
- }
102
- }
103
- console.log(line);
104
- }
105
- console.log('');
106
- }
107
-
108
- function listSkills(sourceDir) {
109
- const skills = listAvailable('skills', sourceDir).filter(s => s.isDir);
110
-
111
- console.log(chalk.cyan.bold(`Available Skills (${skills.length}):\n`));
112
-
113
- if (skills.length === 0) {
114
- console.log(chalk.gray(' No skills found'));
115
- return;
116
- }
117
-
118
- // Group into columns
119
- const cols = 2;
120
- const rows = Math.ceil(skills.length / cols);
121
-
122
- for (let i = 0; i < rows; i++) {
123
- let line = ' ';
124
- for (let j = 0; j < cols; j++) {
125
- const idx = i + j * rows;
126
- if (idx < skills.length) {
127
- line += chalk.white(skills[idx].name.padEnd(35));
128
- }
129
- }
130
- console.log(line);
131
- }
132
- console.log('');
133
- }
134
-
135
- function listCommands(sourceDir) {
136
- const commands = listAvailable('commands', sourceDir);
137
-
138
- console.log(chalk.cyan.bold(`Available Commands (${commands.length}):\n`));
139
-
140
- if (commands.length === 0) {
141
- console.log(chalk.gray(' No commands found'));
142
- return;
143
- }
144
-
145
- // Separate files and directories
146
- const files = commands.filter(c => !c.isDir);
147
- const dirs = commands.filter(c => c.isDir);
148
-
149
- // Print main commands (files)
150
- console.log(chalk.gray(' Main commands:'));
151
- const cols = 4;
152
- let rows = Math.ceil(files.length / cols);
153
-
154
- for (let i = 0; i < rows; i++) {
155
- let line = ' ';
156
- for (let j = 0; j < cols; j++) {
157
- const idx = i + j * rows;
158
- if (idx < files.length) {
159
- line += chalk.white(('/' + files[idx].name).padEnd(18));
160
- }
161
- }
162
- console.log(line);
163
- }
164
-
165
- // Print command groups (directories)
166
- if (dirs.length > 0) {
167
- console.log(chalk.gray('\n Command groups:'));
168
- for (const dir of dirs) {
169
- console.log(chalk.yellow(` /${dir.name}/`));
170
- }
171
- }
172
-
173
- console.log('');
174
- }
175
-
176
- function listWorkflows(sourceDir) {
177
- const workflows = listAvailable('workflows', sourceDir);
178
-
179
- console.log(chalk.cyan.bold(`Available Workflows (${workflows.length}):\n`));
180
-
181
- if (workflows.length === 0) {
182
- console.log(chalk.gray(' No workflows found'));
183
- return;
184
- }
185
-
186
- for (const wf of workflows) {
187
- console.log(chalk.white(` • ${wf.name}`));
188
- }
189
- console.log('');
190
- }
@@ -1,113 +0,0 @@
1
- import chalk from 'chalk';
2
- import { isAkProject } from '../utils/paths.js';
3
- import { loadState, getFileStatuses } from '../utils/state.js';
4
- import fs from 'fs-extra';
5
- import { join } from 'path';
6
-
7
- export async function statusCommand(options = {}) {
8
- const projectDir = process.cwd();
9
-
10
- // Check if in ak project
11
- if (!isAkProject(projectDir)) {
12
- console.log(chalk.red('Not in an ak project.'));
13
- console.log(chalk.gray('Run "ak init" first.'));
14
- return;
15
- }
16
-
17
- // Load state
18
- const state = await loadState(projectDir);
19
- if (!state) {
20
- console.log(chalk.yellow('No state file found.'));
21
- console.log(chalk.gray('This project may have been created without ak.'));
22
- return;
23
- }
24
-
25
- // Print project info
26
- console.log(chalk.cyan.bold('\nProject Status\n'));
27
- console.log(chalk.white(` Kit: ${state.kit}`));
28
- console.log(chalk.white(` Target: ${state.target}`));
29
- console.log(chalk.white(` Source: ${state.source}`));
30
- console.log(chalk.gray(` Created: ${new Date(state.createdAt).toLocaleDateString()}`));
31
- console.log(chalk.gray(` Updated: ${new Date(state.lastUpdate).toLocaleDateString()}`));
32
- console.log('');
33
-
34
- // Get file statuses
35
- const result = await getFileStatuses(projectDir);
36
-
37
- if (result.error) {
38
- console.log(chalk.red(`Error: ${result.error}`));
39
- return;
40
- }
41
-
42
- const { statuses } = result;
43
-
44
- // Summary
45
- const total = statuses.unchanged.length + statuses.modified.length + statuses.added.length;
46
- console.log(chalk.cyan('File Status:'));
47
- console.log(chalk.green(` ✓ ${statuses.unchanged.length} unchanged`));
48
- if (statuses.modified.length > 0) {
49
- console.log(chalk.yellow(` ~ ${statuses.modified.length} modified`));
50
- }
51
- if (statuses.added.length > 0) {
52
- console.log(chalk.blue(` + ${statuses.added.length} added locally`));
53
- }
54
- if (statuses.deleted.length > 0) {
55
- console.log(chalk.red(` - ${statuses.deleted.length} deleted`));
56
- }
57
- console.log(chalk.gray(` Total: ${total} files tracked`));
58
- console.log('');
59
-
60
- // Show modified files
61
- if (statuses.modified.length > 0 && (options.verbose || statuses.modified.length <= 10)) {
62
- console.log(chalk.yellow('Modified files:'));
63
- for (const file of statuses.modified) {
64
- console.log(chalk.yellow(` ~ ${file}`));
65
- }
66
- console.log('');
67
- } else if (statuses.modified.length > 10) {
68
- console.log(chalk.yellow(`Modified files: (showing first 10, use --verbose for all)`));
69
- for (const file of statuses.modified.slice(0, 10)) {
70
- console.log(chalk.yellow(` ~ ${file}`));
71
- }
72
- console.log(chalk.gray(` ... and ${statuses.modified.length - 10} more`));
73
- console.log('');
74
- }
75
-
76
- // Show added files
77
- if (statuses.added.length > 0 && (options.verbose || statuses.added.length <= 5)) {
78
- console.log(chalk.blue('Added locally:'));
79
- for (const file of statuses.added) {
80
- console.log(chalk.blue(` + ${file}`));
81
- }
82
- console.log('');
83
- }
84
-
85
- // Installed components
86
- if (state.installed) {
87
- console.log(chalk.cyan('Installed Components:'));
88
- const { agents, skills, commands, workflows, router, hooks } = state.installed;
89
-
90
- if (agents && agents.length > 0) {
91
- console.log(chalk.gray(` Agents: ${agents.includes('all') ? 'ALL' : agents.length}`));
92
- }
93
- if (skills && skills.length > 0) {
94
- console.log(chalk.gray(` Skills: ${skills.includes('all') ? 'ALL' : skills.length}`));
95
- }
96
- if (commands && commands.length > 0) {
97
- console.log(chalk.gray(` Commands: ${commands.includes('all') ? 'ALL' : commands.length}`));
98
- }
99
- if (workflows && workflows.length > 0) {
100
- console.log(chalk.gray(` Workflows: ${workflows.includes('all') ? 'ALL' : workflows.length}`));
101
- }
102
- if (router) console.log(chalk.gray(' Router: ✓'));
103
- if (hooks) console.log(chalk.gray(' Hooks: ✓'));
104
- console.log('');
105
- }
106
-
107
- // Check source availability
108
- if (state.source && !fs.existsSync(state.source)) {
109
- console.log(chalk.yellow('⚠ Source directory not found. Update may not work.'));
110
- console.log(chalk.gray(` Expected: ${state.source}`));
111
- console.log('');
112
- }
113
- }