apero-kit-cli 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Apero Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,252 @@
1
+ # Apero Kit CLI
2
+
3
+ > Scaffold AI agent projects with pre-configured kits for Claude Code, OpenCode, and Codex.
4
+
5
+ [![npm version](https://badge.fury.io/js/apero-kit-cli.svg)](https://www.npmjs.com/package/apero-kit-cli)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ npm install -g apero-kit-cli
12
+ ```
13
+
14
+ ## Quick Start
15
+
16
+ ```bash
17
+ # Initialize a new project with the engineer kit
18
+ ak init my-app --kit engineer
19
+
20
+ # Or use interactive mode
21
+ ak init my-app
22
+ ```
23
+
24
+ ## Commands
25
+
26
+ ### `ak init [project-name]`
27
+
28
+ Initialize a new project with an agent kit.
29
+
30
+ ```bash
31
+ ak init my-app --kit engineer # Full-stack development kit
32
+ ak init my-app --kit researcher # Research and analysis kit
33
+ ak init my-app --kit designer # UI/UX design kit
34
+ ak init my-app --kit minimal # Lightweight essential kit
35
+ ak init my-app --kit full # Everything included
36
+ ak init my-app # Interactive mode
37
+ ```
38
+
39
+ **Options:**
40
+ | Flag | Description |
41
+ |------|-------------|
42
+ | `-k, --kit <type>` | Kit type: engineer, researcher, designer, minimal, full, custom |
43
+ | `-t, --target <target>` | Target folder: claude (default), opencode, generic |
44
+ | `-s, --source <path>` | Custom source path for templates |
45
+ | `-f, --force` | Overwrite existing directory |
46
+
47
+ ### `ak add <type>:<name>`
48
+
49
+ Add an agent, skill, or command to an existing project.
50
+
51
+ ```bash
52
+ ak add skill:databases # Add databases skill
53
+ ak add agent:debugger # Add debugger agent
54
+ ak add command:fix/ci # Add fix/ci command
55
+ ```
56
+
57
+ ### `ak list [type]`
58
+
59
+ List available kits, agents, skills, or commands.
60
+
61
+ ```bash
62
+ ak list # Show available list commands
63
+ ak list kits # List all kits
64
+ ak list agents # List available agents
65
+ ak list skills # List available skills
66
+ ak list commands # List available commands
67
+ ```
68
+
69
+ ### `ak update`
70
+
71
+ Update/sync from source templates. Only updates unchanged files (safe by default).
72
+
73
+ ```bash
74
+ ak update # Update from configured source
75
+ ak update --source ~/AGENTS.md # Update from specific source
76
+ ak update --skills # Update only skills
77
+ ak update --dry-run # Preview what would be updated
78
+ ak update --force # Update without confirmation
79
+ ```
80
+
81
+ **Options:**
82
+ | Flag | Description |
83
+ |------|-------------|
84
+ | `-s, --source <path>` | Source path to sync from |
85
+ | `--agents` | Update only agents |
86
+ | `--skills` | Update only skills |
87
+ | `--commands` | Update only commands |
88
+ | `-n, --dry-run` | Show what would be updated without making changes |
89
+ | `-f, --force` | Force update without confirmation |
90
+
91
+ ### `ak status`
92
+
93
+ Show project status and file changes.
94
+
95
+ ```bash
96
+ ak status # Show status summary
97
+ ak status --verbose # Show all files
98
+ ```
99
+
100
+ ### `ak doctor`
101
+
102
+ Check project health and diagnose issues.
103
+
104
+ ```bash
105
+ ak doctor
106
+ ```
107
+
108
+ ## Available Kits
109
+
110
+ | Kit | Description | Agents | Skills | Commands |
111
+ |-----|-------------|--------|--------|----------|
112
+ | 🛠️ **engineer** | Full-stack development | 7 | 7 | 17 |
113
+ | 🔬 **researcher** | Research and analysis | 6 | 4 | 10 |
114
+ | 🎨 **designer** | UI/UX design | 3 | 3 | 5 |
115
+ | 📦 **minimal** | Lightweight essentials | 2 | 2 | 3 |
116
+ | 🚀 **full** | Everything included | ALL | ALL | ALL |
117
+ | 🔧 **custom** | Pick your own | - | - | - |
118
+
119
+ ## How It Works
120
+
121
+ ### Source Detection
122
+
123
+ The CLI automatically finds your source templates by searching up from the current directory:
124
+
125
+ ```
126
+ cwd → parent → git root
127
+ ```
128
+
129
+ It looks for:
130
+ - `AGENTS.md` file
131
+ - `.claude/` directory
132
+ - `.opencode/` directory
133
+
134
+ You can also specify a custom source with `--source`.
135
+
136
+ ### State Tracking
137
+
138
+ Each project stores its state in `.ak/state.json`:
139
+ - What kit was used
140
+ - Source location
141
+ - Installed components
142
+ - File hashes for safe updates
143
+
144
+ ### Safe Updates
145
+
146
+ When you run `ak update`:
147
+ 1. Files you haven't modified → **Updated**
148
+ 2. Files you've modified → **Skipped** (preserves your changes)
149
+ 3. New files from source → **Added**
150
+
151
+ ## Project Structure
152
+
153
+ After `ak init my-app --kit engineer`:
154
+
155
+ ```
156
+ my-app/
157
+ ├── .claude/
158
+ │ ├── agents/ # AI agent definitions
159
+ │ ├── commands/ # Slash commands
160
+ │ ├── skills/ # Knowledge packages
161
+ │ ├── router/ # Decision logic
162
+ │ ├── hooks/ # Automation scripts
163
+ │ ├── README.md
164
+ │ └── settings.json
165
+ ├── .ak/
166
+ │ └── state.json # Project state for updates
167
+ └── AGENTS.md # Core ruleset
168
+ ```
169
+
170
+ ## Examples
171
+
172
+ ### Workflow: Create and Develop
173
+
174
+ ```bash
175
+ # 1. Initialize project
176
+ ak init my-api --kit engineer
177
+ cd my-api
178
+
179
+ # 2. Start coding with Claude Code
180
+ # ... develop features ...
181
+
182
+ # 3. Check status
183
+ ak status
184
+
185
+ # 4. Add more skills as needed
186
+ ak add skill:databases
187
+ ak add skill:devops
188
+
189
+ # 5. Update when source has new content
190
+ ak update
191
+ ```
192
+
193
+ ### Workflow: Team Sharing
194
+
195
+ ```bash
196
+ # Team lead: publish CLI with your custom kits
197
+ npm publish
198
+
199
+ # Team members: install and use
200
+ npm install -g apero-kit-cli
201
+ ak init my-project --kit engineer
202
+ ```
203
+
204
+ ## Configuration
205
+
206
+ ### Global Config (Optional)
207
+
208
+ Create `~/.ak-cli.json`:
209
+
210
+ ```json
211
+ {
212
+ "defaultSource": "/path/to/your/AGENTS.md",
213
+ "defaultKit": "engineer"
214
+ }
215
+ ```
216
+
217
+ ## Troubleshooting
218
+
219
+ Run `ak doctor` to diagnose issues:
220
+
221
+ ```bash
222
+ $ ak doctor
223
+
224
+ Apero Kit Doctor
225
+
226
+ Checking project health...
227
+
228
+ ✓ ak project detected
229
+ ✓ State file (.ak/state.json) exists
230
+ ✓ Target directory exists (.claude)
231
+ ✓ AGENTS.md exists
232
+ ✓ Source directory accessible
233
+ ✓ agents/ exists (15 items)
234
+ ✓ commands/ exists (40 items)
235
+ ✓ skills/ exists (25 items)
236
+
237
+ ────────────────────────────────────────
238
+
239
+ ✓ All checks passed!
240
+ ```
241
+
242
+ ## License
243
+
244
+ MIT License - see [LICENSE](LICENSE) for details.
245
+
246
+ ## Contributing
247
+
248
+ Contributions are welcome! Please open an issue or submit a pull request.
249
+
250
+ ## Acknowledgments
251
+
252
+ Built for use with [Claude Code](https://claude.ai/code), [OpenCode](https://opencode.dev), and similar AI coding assistants.
package/bin/ak.js ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { program } from 'commander';
4
+ import chalk from 'chalk';
5
+ import { initCommand } from '../src/commands/init.js';
6
+ import { addCommand } from '../src/commands/add.js';
7
+ import { listCommand } from '../src/commands/list.js';
8
+ import { updateCommand } from '../src/commands/update.js';
9
+ import { statusCommand } from '../src/commands/status.js';
10
+ import { doctorCommand } from '../src/commands/doctor.js';
11
+
12
+ const VERSION = '1.0.0';
13
+
14
+ console.log(chalk.cyan.bold('\n Apero Kit CLI') + chalk.gray(` v${VERSION}\n`));
15
+
16
+ program
17
+ .name('ak')
18
+ .description('Scaffold AI agent projects with pre-configured kits')
19
+ .version(VERSION);
20
+
21
+ // ak init [project-name] --kit <kit-type>
22
+ program
23
+ .command('init [project-name]')
24
+ .description('Initialize a new project with an agent kit')
25
+ .option('-k, --kit <type>', 'Kit type (engineer, researcher, designer, minimal, full, custom)')
26
+ .option('-t, --target <target>', 'Target folder (claude, opencode, generic)', 'claude')
27
+ .option('-s, --source <path>', 'Custom source path for templates')
28
+ .option('-f, --force', 'Overwrite existing directory')
29
+ .action(initCommand);
30
+
31
+ // ak add <type>:<name>
32
+ program
33
+ .command('add <item>')
34
+ .description('Add agent, skill, or command (e.g., ak add skill:databases)')
35
+ .option('-s, --source <path>', 'Custom source path')
36
+ .option('-p, --path <path>', 'Target project path', '.')
37
+ .action(addCommand);
38
+
39
+ // ak list [type]
40
+ program
41
+ .command('list [type]')
42
+ .description('List available kits, agents, skills, or commands')
43
+ .option('-s, --source <path>', 'Custom source path')
44
+ .action(listCommand);
45
+
46
+ // ak update
47
+ program
48
+ .command('update')
49
+ .description('Update/sync from source templates')
50
+ .option('-s, --source <path>', 'Source path to sync from')
51
+ .option('--agents', 'Update only agents')
52
+ .option('--skills', 'Update only skills')
53
+ .option('--commands', 'Update only commands')
54
+ .option('--all', 'Update everything')
55
+ .option('-n, --dry-run', 'Show what would be updated without making changes')
56
+ .option('-f, --force', 'Force update without confirmation')
57
+ .action(updateCommand);
58
+
59
+ // ak status
60
+ program
61
+ .command('status')
62
+ .description('Show project status and file changes')
63
+ .option('-v, --verbose', 'Show all files')
64
+ .action(statusCommand);
65
+
66
+ // ak doctor
67
+ program
68
+ .command('doctor')
69
+ .description('Check project health and diagnose issues')
70
+ .action(doctorCommand);
71
+
72
+ // ak kits - alias
73
+ program
74
+ .command('kits')
75
+ .description('List all available kits')
76
+ .action(() => listCommand('kits'));
77
+
78
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "apero-kit-cli",
3
+ "version": "1.0.0",
4
+ "description": "CLI tool to scaffold AI agent projects with pre-configured kits (Claude, OpenCode, Codex)",
5
+ "type": "module",
6
+ "bin": {
7
+ "ak": "bin/ak.js",
8
+ "apero-kit": "bin/ak.js"
9
+ },
10
+ "main": "src/index.js",
11
+ "exports": {
12
+ ".": "./src/index.js",
13
+ "./kits": "./src/kits/index.js"
14
+ },
15
+ "scripts": {
16
+ "start": "node bin/ak.js",
17
+ "link": "npm link",
18
+ "unlink": "npm unlink",
19
+ "test": "node --test src/**/*.test.js",
20
+ "prepublishOnly": "echo 'Ready to publish!'"
21
+ },
22
+ "files": [
23
+ "bin",
24
+ "src",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "keywords": [
29
+ "ai",
30
+ "agent",
31
+ "claude",
32
+ "claude-code",
33
+ "opencode",
34
+ "codex",
35
+ "cli",
36
+ "scaffold",
37
+ "template",
38
+ "llm",
39
+ "apero"
40
+ ],
41
+ "author": "Apero Team",
42
+ "license": "MIT",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/Thanh-apero/apero-kit-cli.git"
46
+ },
47
+ "homepage": "https://github.com/Thanh-apero/apero-kit-cli#readme",
48
+ "bugs": {
49
+ "url": "https://github.com/Thanh-apero/apero-kit-cli/issues"
50
+ },
51
+ "engines": {
52
+ "node": ">=18.0.0"
53
+ },
54
+ "dependencies": {
55
+ "chalk": "^5.3.0",
56
+ "commander": "^12.1.0",
57
+ "fs-extra": "^11.2.0",
58
+ "inquirer": "^9.2.15",
59
+ "ora": "^8.0.1"
60
+ }
61
+ }
@@ -0,0 +1,126 @@
1
+ import fs from 'fs-extra';
2
+ import { join } from 'path';
3
+ import chalk from 'chalk';
4
+ import ora from 'ora';
5
+ import { resolveSource, isAkProject } from '../utils/paths.js';
6
+ import { copyItems, copyAllOfType, listAvailable } from '../utils/copy.js';
7
+ import { loadState, updateState } from '../utils/state.js';
8
+ import { hashDirectory } from '../utils/hash.js';
9
+
10
+ export async function addCommand(item, options = {}) {
11
+ // Parse item format: type:name (e.g., skill:databases, agent:debugger)
12
+ const parts = item.split(':');
13
+ if (parts.length !== 2) {
14
+ console.log(chalk.red('Invalid format. Use: ak add <type>:<name>'));
15
+ console.log(chalk.gray('Examples:'));
16
+ console.log(chalk.gray(' ak add skill:databases'));
17
+ console.log(chalk.gray(' ak add agent:debugger'));
18
+ console.log(chalk.gray(' ak add command:fix/ci'));
19
+ return;
20
+ }
21
+
22
+ const [type, name] = parts;
23
+ const validTypes = ['agent', 'agents', 'skill', 'skills', 'command', 'commands', 'workflow', 'workflows'];
24
+
25
+ if (!validTypes.includes(type)) {
26
+ console.log(chalk.red(`Invalid type: ${type}`));
27
+ console.log(chalk.gray(`Valid types: agent, skill, command, workflow`));
28
+ return;
29
+ }
30
+
31
+ // Normalize type to plural
32
+ const typeMap = {
33
+ agent: 'agents',
34
+ agents: 'agents',
35
+ skill: 'skills',
36
+ skills: 'skills',
37
+ command: 'commands',
38
+ commands: 'commands',
39
+ workflow: 'workflows',
40
+ workflows: 'workflows'
41
+ };
42
+ const normalizedType = typeMap[type];
43
+
44
+ const projectDir = options.path || process.cwd();
45
+
46
+ // Check if in ak project
47
+ if (!isAkProject(projectDir)) {
48
+ console.log(chalk.red('Not in an ak project.'));
49
+ console.log(chalk.gray('Run "ak init" first or use --path to specify project directory.'));
50
+ return;
51
+ }
52
+
53
+ // Load state
54
+ const state = await loadState(projectDir);
55
+ if (!state) {
56
+ console.log(chalk.yellow('Warning: No state file found. Creating fresh state after add.'));
57
+ }
58
+
59
+ // Resolve source
60
+ const sourceFlag = options.source || (state ? state.source : null);
61
+ const source = resolveSource(sourceFlag);
62
+ if (source.error) {
63
+ console.log(chalk.red(`Error: ${source.error}`));
64
+ return;
65
+ }
66
+
67
+ // Check if item exists in source
68
+ const available = listAvailable(normalizedType, source.claudeDir);
69
+ const exists = available.some(a => a.name === name);
70
+
71
+ if (!exists) {
72
+ console.log(chalk.red(`${type} "${name}" not found in source.`));
73
+ console.log(chalk.gray(`Use "ak list ${normalizedType}" to see available options.`));
74
+ return;
75
+ }
76
+
77
+ // Determine target directory
78
+ const targetFolder = state?.target || '.claude';
79
+ const targetDir = join(projectDir, targetFolder);
80
+
81
+ // Check if already installed
82
+ const destPath = join(targetDir, normalizedType, name);
83
+ const destPathMd = destPath + '.md';
84
+ if (fs.existsSync(destPath) || fs.existsSync(destPathMd)) {
85
+ console.log(chalk.yellow(`${type} "${name}" already exists in project.`));
86
+ console.log(chalk.gray('Use "ak update" to refresh from source.'));
87
+ return;
88
+ }
89
+
90
+ // Copy
91
+ const spinner = ora(`Adding ${type} "${name}"...`).start();
92
+
93
+ try {
94
+ const result = await copyItems([name], normalizedType, source.claudeDir, targetDir);
95
+
96
+ if (result.copied.length > 0) {
97
+ spinner.succeed(chalk.green(`Added ${type}: ${name}`));
98
+
99
+ // Update state
100
+ if (state) {
101
+ const installed = state.installed || {};
102
+ installed[normalizedType] = installed[normalizedType] || [];
103
+ if (!installed[normalizedType].includes(name)) {
104
+ installed[normalizedType].push(name);
105
+ }
106
+
107
+ // Recalculate hashes
108
+ const newHashes = await hashDirectory(targetDir);
109
+
110
+ await updateState(projectDir, {
111
+ installed,
112
+ originalHashes: newHashes
113
+ });
114
+ }
115
+
116
+ console.log(chalk.gray(`Location: ${targetFolder}/${normalizedType}/${name}`));
117
+ } else if (result.skipped.length > 0) {
118
+ spinner.fail(chalk.red(`Could not find ${type}: ${name}`));
119
+ } else if (result.errors.length > 0) {
120
+ spinner.fail(chalk.red(`Error adding ${type}: ${result.errors[0].error}`));
121
+ }
122
+ } catch (error) {
123
+ spinner.fail(chalk.red(`Failed to add ${type}`));
124
+ console.error(chalk.red(error.message));
125
+ }
126
+ }
@@ -0,0 +1,129 @@
1
+ import fs from 'fs-extra';
2
+ import { join } from 'path';
3
+ import chalk from 'chalk';
4
+ import { isAkProject, resolveSource, TARGETS } from '../utils/paths.js';
5
+ import { loadState } from '../utils/state.js';
6
+
7
+ export async function doctorCommand(options = {}) {
8
+ const projectDir = process.cwd();
9
+
10
+ console.log(chalk.cyan.bold('\nApero Kit Doctor\n'));
11
+ console.log(chalk.gray('Checking project health...\n'));
12
+
13
+ let issues = 0;
14
+ let warnings = 0;
15
+
16
+ // Check 1: Is this an ak project?
17
+ const isProject = isAkProject(projectDir);
18
+ if (isProject) {
19
+ console.log(chalk.green('✓ ak project detected'));
20
+ } else {
21
+ console.log(chalk.red('✗ Not an ak project'));
22
+ issues++;
23
+ }
24
+
25
+ // Check 2: State file
26
+ const state = await loadState(projectDir);
27
+ if (state) {
28
+ console.log(chalk.green('✓ State file (.ak/state.json) exists'));
29
+ } else if (isProject) {
30
+ console.log(chalk.yellow('⚠ No state file - project may have been created manually'));
31
+ warnings++;
32
+ }
33
+
34
+ // Check 3: Target directory
35
+ let targetDir = null;
36
+ for (const [name, folder] of Object.entries(TARGETS)) {
37
+ const dir = join(projectDir, folder);
38
+ if (fs.existsSync(dir)) {
39
+ targetDir = { name, folder, dir };
40
+ break;
41
+ }
42
+ }
43
+
44
+ if (targetDir) {
45
+ console.log(chalk.green(`✓ Target directory exists (${targetDir.folder})`));
46
+ } else {
47
+ console.log(chalk.red('✗ No target directory (.claude/, .opencode/, .agent/)'));
48
+ issues++;
49
+ }
50
+
51
+ // Check 4: AGENTS.md
52
+ const agentsMd = join(projectDir, 'AGENTS.md');
53
+ if (fs.existsSync(agentsMd)) {
54
+ console.log(chalk.green('✓ AGENTS.md exists'));
55
+ } else {
56
+ console.log(chalk.yellow('⚠ AGENTS.md not found (optional but recommended)'));
57
+ warnings++;
58
+ }
59
+
60
+ // Check 5: Source availability
61
+ if (state && state.source) {
62
+ if (fs.existsSync(state.source)) {
63
+ console.log(chalk.green(`✓ Source directory accessible`));
64
+ } else {
65
+ console.log(chalk.red(`✗ Source directory not found: ${state.source}`));
66
+ issues++;
67
+ }
68
+ }
69
+
70
+ // Check 6: Key subdirectories
71
+ if (targetDir) {
72
+ const checkDirs = ['agents', 'commands', 'skills'];
73
+ for (const dir of checkDirs) {
74
+ const fullPath = join(targetDir.dir, dir);
75
+ if (fs.existsSync(fullPath)) {
76
+ const items = fs.readdirSync(fullPath).length;
77
+ console.log(chalk.green(`✓ ${dir}/ exists (${items} items)`));
78
+ } else {
79
+ console.log(chalk.yellow(`⚠ ${dir}/ not found`));
80
+ warnings++;
81
+ }
82
+ }
83
+ }
84
+
85
+ // Check 7: Auto-detect source in parent directories
86
+ console.log('');
87
+ console.log(chalk.gray('Source detection:'));
88
+ const source = resolveSource();
89
+ if (source.error) {
90
+ console.log(chalk.yellow(` ⚠ ${source.error}`));
91
+ warnings++;
92
+ } else {
93
+ console.log(chalk.green(` ✓ Found source: ${source.path}`));
94
+ console.log(chalk.gray(` Type: ${source.type || 'unknown'}`));
95
+ }
96
+
97
+ // Summary
98
+ console.log('');
99
+ console.log(chalk.cyan('─'.repeat(40)));
100
+
101
+ if (issues === 0 && warnings === 0) {
102
+ console.log(chalk.green.bold('\n✓ All checks passed!\n'));
103
+ } else if (issues === 0) {
104
+ console.log(chalk.yellow(`\n⚠ ${warnings} warning(s), no critical issues\n`));
105
+ } else {
106
+ console.log(chalk.red(`\n✗ ${issues} issue(s), ${warnings} warning(s)\n`));
107
+ }
108
+
109
+ // Suggestions
110
+ if (issues > 0 || warnings > 0) {
111
+ console.log(chalk.cyan('Suggestions:'));
112
+
113
+ if (!isProject) {
114
+ console.log(chalk.white(' • Run "ak init ." to initialize this directory'));
115
+ }
116
+
117
+ if (!state && isProject) {
118
+ console.log(chalk.white(' • State file is missing. Re-run "ak init" or create manually'));
119
+ }
120
+
121
+ if (state && state.source && !fs.existsSync(state.source)) {
122
+ console.log(chalk.white(' • Update source path: ak update --source <new-path>'));
123
+ }
124
+
125
+ console.log('');
126
+ }
127
+
128
+ return { issues, warnings };
129
+ }