@antomanc/aginit 0.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.
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/THIRD_PARTY_NOTICES.md +36 -0
- package/bin/aginit.js +8 -0
- package/dist/adapters/agents-md.d.ts +8 -0
- package/dist/adapters/agents-md.d.ts.map +1 -0
- package/dist/adapters/agents-md.js +121 -0
- package/dist/adapters/agents-md.js.map +1 -0
- package/dist/adapters/browser.d.ts +5 -0
- package/dist/adapters/browser.d.ts.map +1 -0
- package/dist/adapters/browser.js +42 -0
- package/dist/adapters/browser.js.map +1 -0
- package/dist/adapters/git.d.ts +5 -0
- package/dist/adapters/git.d.ts.map +1 -0
- package/dist/adapters/git.js +90 -0
- package/dist/adapters/git.js.map +1 -0
- package/dist/adapters/graft.d.ts +14 -0
- package/dist/adapters/graft.d.ts.map +1 -0
- package/dist/adapters/graft.js +56 -0
- package/dist/adapters/graft.js.map +1 -0
- package/dist/adapters/package-manager.d.ts +20 -0
- package/dist/adapters/package-manager.d.ts.map +1 -0
- package/dist/adapters/package-manager.js +89 -0
- package/dist/adapters/package-manager.js.map +1 -0
- package/dist/adapters/skills.d.ts +29 -0
- package/dist/adapters/skills.d.ts.map +1 -0
- package/dist/adapters/skills.js +80 -0
- package/dist/adapters/skills.js.map +1 -0
- package/dist/cli/doctor.d.ts +2 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +212 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +32 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +162 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/new.d.ts +15 -0
- package/dist/cli/new.d.ts.map +1 -0
- package/dist/cli/new.js +161 -0
- package/dist/cli/new.js.map +1 -0
- package/dist/cli/wizard.d.ts +31 -0
- package/dist/cli/wizard.d.ts.map +1 -0
- package/dist/cli/wizard.js +260 -0
- package/dist/cli/wizard.js.map +1 -0
- package/dist/config/defaults.d.ts +8 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +122 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/schema.d.ts +40 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +4 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/validation.d.ts +11 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +99 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/presets/adr.d.ts +5 -0
- package/dist/presets/adr.d.ts.map +1 -0
- package/dist/presets/adr.js +33 -0
- package/dist/presets/adr.js.map +1 -0
- package/dist/presets/cli.d.ts +3 -0
- package/dist/presets/cli.d.ts.map +1 -0
- package/dist/presets/cli.js +126 -0
- package/dist/presets/cli.js.map +1 -0
- package/dist/presets/generic.d.ts +3 -0
- package/dist/presets/generic.d.ts.map +1 -0
- package/dist/presets/generic.js +29 -0
- package/dist/presets/generic.js.map +1 -0
- package/dist/presets/registry.d.ts +6 -0
- package/dist/presets/registry.d.ts.map +1 -0
- package/dist/presets/registry.js +20 -0
- package/dist/presets/registry.js.map +1 -0
- package/dist/presets/testing.d.ts +6 -0
- package/dist/presets/testing.d.ts.map +1 -0
- package/dist/presets/testing.js +46 -0
- package/dist/presets/testing.js.map +1 -0
- package/dist/presets/types.d.ts +14 -0
- package/dist/presets/types.d.ts.map +1 -0
- package/dist/presets/types.js +2 -0
- package/dist/presets/types.js.map +1 -0
- package/dist/presets/web.d.ts +3 -0
- package/dist/presets/web.d.ts.map +1 -0
- package/dist/presets/web.js +84 -0
- package/dist/presets/web.js.map +1 -0
- package/dist/utils/fs.d.ts +13 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +60 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +20 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/shell.d.ts +17 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +87 -0
- package/dist/utils/shell.js.map +1 -0
- package/package.json +71 -0
- package/src/adapters/agents-md.ts +143 -0
- package/src/adapters/browser.ts +49 -0
- package/src/adapters/git.ts +100 -0
- package/src/adapters/graft.ts +80 -0
- package/src/adapters/package-manager.ts +116 -0
- package/src/adapters/skills.ts +117 -0
- package/src/cli/doctor.ts +244 -0
- package/src/cli/init.ts +34 -0
- package/src/cli/main.ts +191 -0
- package/src/cli/new.ts +200 -0
- package/src/cli/wizard.ts +300 -0
- package/src/config/defaults.ts +137 -0
- package/src/config/schema.ts +44 -0
- package/src/config/validation.ts +96 -0
- package/src/index.ts +15 -0
- package/src/presets/adr.ts +37 -0
- package/src/presets/cli.ts +136 -0
- package/src/presets/generic.ts +32 -0
- package/src/presets/registry.ts +24 -0
- package/src/presets/testing.ts +56 -0
- package/src/presets/types.ts +15 -0
- package/src/presets/web.ts +99 -0
- package/src/utils/fs.ts +79 -0
- package/src/utils/logger.ts +20 -0
- package/src/utils/shell.ts +116 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileExists, readJsonFile } from '../utils/fs.js';
|
|
4
|
+
import { runExecutable } from '../utils/shell.js';
|
|
5
|
+
import { logger } from '../utils/logger.js';
|
|
6
|
+
import { validateUpstreamArgument } from '../config/validation.js';
|
|
7
|
+
import { SkillSourceConfig } from '../config/schema.js';
|
|
8
|
+
|
|
9
|
+
export interface SkillsLockFile {
|
|
10
|
+
version: number;
|
|
11
|
+
skills: Record<
|
|
12
|
+
string,
|
|
13
|
+
{
|
|
14
|
+
source: string;
|
|
15
|
+
sourceType: string;
|
|
16
|
+
skillPath: string;
|
|
17
|
+
computedHash: string;
|
|
18
|
+
}
|
|
19
|
+
>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface SkillInstallOptions {
|
|
23
|
+
agents?: string[];
|
|
24
|
+
dryRun?: boolean;
|
|
25
|
+
silent?: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function installSkillSource(
|
|
29
|
+
targetDir: string,
|
|
30
|
+
source: SkillSourceConfig,
|
|
31
|
+
options: SkillInstallOptions = {}
|
|
32
|
+
): Promise<{ ok: boolean; message?: string }> {
|
|
33
|
+
const { agents = ['antigravity', 'codex'], dryRun = false, silent = false } = options;
|
|
34
|
+
|
|
35
|
+
validateUpstreamArgument(source.package, 'skill package');
|
|
36
|
+
for (const skill of source.skills) validateUpstreamArgument(skill, 'skill name');
|
|
37
|
+
for (const agent of agents) validateUpstreamArgument(agent, 'agent');
|
|
38
|
+
const args = [
|
|
39
|
+
'-y',
|
|
40
|
+
'skills',
|
|
41
|
+
'add',
|
|
42
|
+
source.package,
|
|
43
|
+
'--skill',
|
|
44
|
+
...(source.skills.length ? source.skills : ['*']),
|
|
45
|
+
'--agent',
|
|
46
|
+
...agents,
|
|
47
|
+
'-y'
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
if (!silent) {
|
|
51
|
+
logger.dim(`Installing skills from ${source.package}: ${source.skills.join(', ')}...`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const res = await runExecutable('npx', args, { cwd: targetDir, dryRun, silent: true });
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
logger.warn(`Failed installing skills from ${source.package}: ${res.stderr || res.stdout}`);
|
|
57
|
+
return { ok: false, message: res.stderr || res.stdout };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { ok: true };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function installAllSkills(
|
|
64
|
+
targetDir: string,
|
|
65
|
+
sources: SkillSourceConfig[],
|
|
66
|
+
options: SkillInstallOptions = {}
|
|
67
|
+
): Promise<{ installed: string[]; failed: string[] }> {
|
|
68
|
+
const installed: string[] = [];
|
|
69
|
+
const failed: string[] = [];
|
|
70
|
+
|
|
71
|
+
for (const source of sources) {
|
|
72
|
+
const res = await installSkillSource(targetDir, source, options);
|
|
73
|
+
if (res.ok) {
|
|
74
|
+
installed.push(...(source.skills.length ? source.skills : [`${source.package}:*`]));
|
|
75
|
+
} else {
|
|
76
|
+
failed.push(...(source.skills.length ? source.skills : [`${source.package}:*`]));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { installed, failed };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getInstalledSkills(targetDir: string): string[] {
|
|
84
|
+
const lockPath = path.join(targetDir, 'skills-lock.json');
|
|
85
|
+
if (fileExists(lockPath)) {
|
|
86
|
+
const lock = readJsonFile<SkillsLockFile>(lockPath);
|
|
87
|
+
if (lock && lock.skills) {
|
|
88
|
+
return Object.keys(lock.skills);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check .agents/skills directory directly
|
|
93
|
+
const skillsDir = path.join(targetDir, '.agents', 'skills');
|
|
94
|
+
if (fileExists(skillsDir)) {
|
|
95
|
+
try {
|
|
96
|
+
return fs.readdirSync(skillsDir).filter((f) => !f.startsWith('.'));
|
|
97
|
+
} catch {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function updateSkills(
|
|
106
|
+
targetDir: string,
|
|
107
|
+
options: { dryRun?: boolean; silent?: boolean } = {}
|
|
108
|
+
): Promise<boolean> {
|
|
109
|
+
const { dryRun = false, silent = false } = options;
|
|
110
|
+
if (!silent) logger.dim('Updating installed skills...');
|
|
111
|
+
const res = await runExecutable('npx', ['-y', 'skills', 'update', '-p', '-y'], {
|
|
112
|
+
cwd: targetDir,
|
|
113
|
+
dryRun,
|
|
114
|
+
silent: true
|
|
115
|
+
});
|
|
116
|
+
return res.ok;
|
|
117
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import { runExecutable, commandExists } from '../utils/shell.js';
|
|
4
|
+
import { fileExists, readTextFile } from '../utils/fs.js';
|
|
5
|
+
import {
|
|
6
|
+
AGINIT_CONFIG_FILENAME,
|
|
7
|
+
LEGACY_CONFIG_FILENAME,
|
|
8
|
+
AginitConfig,
|
|
9
|
+
PackageManager
|
|
10
|
+
} from '../config/schema.js';
|
|
11
|
+
import { detectPackageManager } from '../adapters/package-manager.js';
|
|
12
|
+
import { getInstalledSkills } from '../adapters/skills.js';
|
|
13
|
+
import { readProjectConfig } from '../config/validation.js';
|
|
14
|
+
import { logger } from '../utils/logger.js';
|
|
15
|
+
|
|
16
|
+
interface CheckItem {
|
|
17
|
+
name: string;
|
|
18
|
+
category: 'Environment' | 'AI Tooling' | 'Project Config';
|
|
19
|
+
ok: boolean;
|
|
20
|
+
version?: string;
|
|
21
|
+
detail: string;
|
|
22
|
+
suggestion?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function runDoctor(targetDir: string = process.cwd()): Promise<void> {
|
|
26
|
+
logger.banner('Aginit — System & Project Doctor', `Target: ${targetDir}`);
|
|
27
|
+
|
|
28
|
+
// Resolve project config early to detect configured package manager
|
|
29
|
+
let configPath = path.join(targetDir, AGINIT_CONFIG_FILENAME);
|
|
30
|
+
if (!fileExists(configPath)) {
|
|
31
|
+
const legacyPath = path.join(targetDir, LEGACY_CONFIG_FILENAME);
|
|
32
|
+
if (fileExists(legacyPath)) {
|
|
33
|
+
configPath = legacyPath;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const configExists = fileExists(configPath);
|
|
38
|
+
let projectConfig: AginitConfig | null = null;
|
|
39
|
+
let configError = '';
|
|
40
|
+
try {
|
|
41
|
+
projectConfig = readProjectConfig(targetDir);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
configError = error instanceof Error ? error.message : String(error);
|
|
44
|
+
}
|
|
45
|
+
const activePm: PackageManager =
|
|
46
|
+
projectConfig?.packageManager || detectPackageManager(targetDir) || 'pnpm';
|
|
47
|
+
|
|
48
|
+
const checks: CheckItem[] = [];
|
|
49
|
+
|
|
50
|
+
// 1. Node.js check
|
|
51
|
+
const nodeRes = await runExecutable('node', ['-v'], { silent: true });
|
|
52
|
+
let nodeOk = nodeRes.ok;
|
|
53
|
+
let nodeDetail = 'Node.js is not found in PATH';
|
|
54
|
+
let nodeSuggestion: string | undefined =
|
|
55
|
+
'Install Node.js (>= 22.12.0 LTS recommended) via nvm, fnm, or brew';
|
|
56
|
+
if (nodeRes.ok) {
|
|
57
|
+
const rawVer = nodeRes.stdout.replace(/^v/, '');
|
|
58
|
+
const [major = 0, minor = 0] = rawVer.split('.').map(Number);
|
|
59
|
+
const meetsEngine = major > 22 || (major === 22 && minor >= 12);
|
|
60
|
+
if (meetsEngine) {
|
|
61
|
+
nodeDetail = `Installed (${nodeRes.stdout})`;
|
|
62
|
+
nodeSuggestion = undefined;
|
|
63
|
+
} else {
|
|
64
|
+
nodeOk = false;
|
|
65
|
+
nodeDetail = `Installed (${nodeRes.stdout}) — requires Node.js >= 22.12.0`;
|
|
66
|
+
nodeSuggestion = 'Upgrade Node.js to >= 22.12.0 (LTS) via nvm, fnm, or brew';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
checks.push({
|
|
70
|
+
name: 'Node.js runtime',
|
|
71
|
+
category: 'Environment',
|
|
72
|
+
ok: nodeOk,
|
|
73
|
+
version: nodeRes.stdout,
|
|
74
|
+
detail: nodeDetail,
|
|
75
|
+
suggestion: nodeSuggestion
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// 2. Active Package Manager check (pnpm, npm, yarn, or bun)
|
|
79
|
+
const pmRes = await runExecutable(activePm, ['-v'], { silent: true });
|
|
80
|
+
checks.push({
|
|
81
|
+
name: `${activePm} package manager`,
|
|
82
|
+
category: 'Environment',
|
|
83
|
+
ok: pmRes.ok,
|
|
84
|
+
version: pmRes.stdout,
|
|
85
|
+
detail: pmRes.ok ? `Installed (${pmRes.stdout})` : `${activePm} is not found in PATH`,
|
|
86
|
+
suggestion: pmRes.ok
|
|
87
|
+
? undefined
|
|
88
|
+
: `Install ${activePm} (${activePm === 'pnpm' ? 'npm install -g pnpm' : activePm === 'bun' ? 'curl -fsSL https://bun.sh/install | bash' : 'install ' + activePm})`
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// 3. Git check
|
|
92
|
+
const gitExists = await commandExists('git');
|
|
93
|
+
let isGitRepo = fileExists(path.join(targetDir, '.git'));
|
|
94
|
+
let inWorkTree = false;
|
|
95
|
+
if (gitExists && !isGitRepo) {
|
|
96
|
+
const revParse = await runExecutable('git', ['rev-parse', '--is-inside-work-tree'], {
|
|
97
|
+
cwd: targetDir,
|
|
98
|
+
silent: true
|
|
99
|
+
});
|
|
100
|
+
if (revParse.ok && revParse.stdout === 'true') {
|
|
101
|
+
isGitRepo = true;
|
|
102
|
+
inWorkTree = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
checks.push({
|
|
106
|
+
name: 'Git VCS',
|
|
107
|
+
category: 'Environment',
|
|
108
|
+
ok: gitExists && isGitRepo,
|
|
109
|
+
detail: gitExists
|
|
110
|
+
? isGitRepo
|
|
111
|
+
? inWorkTree
|
|
112
|
+
? 'Git installed and target directory is within an active git repository'
|
|
113
|
+
: 'Git installed and repository initialized'
|
|
114
|
+
: 'Git installed, but target directory is not a git repo'
|
|
115
|
+
: 'Git is not installed',
|
|
116
|
+
suggestion: !gitExists
|
|
117
|
+
? 'Install git via brew or Xcode Command Line Tools'
|
|
118
|
+
: !isGitRepo
|
|
119
|
+
? 'Run `git init` or `aginit init` to initialize repository'
|
|
120
|
+
: undefined
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// 4. Graft CLI check
|
|
124
|
+
const graftExists = await commandExists('graft');
|
|
125
|
+
let graftVersion = '';
|
|
126
|
+
if (graftExists) {
|
|
127
|
+
const gv = await runExecutable('graft', ['--version'], { silent: true });
|
|
128
|
+
graftVersion = gv.stdout;
|
|
129
|
+
}
|
|
130
|
+
checks.push({
|
|
131
|
+
name: 'Graft codebase graph CLI',
|
|
132
|
+
category: 'AI Tooling',
|
|
133
|
+
ok: graftExists,
|
|
134
|
+
version: graftVersion,
|
|
135
|
+
detail: graftExists ? `Installed globally (${graftVersion})` : 'Graft CLI not found in PATH',
|
|
136
|
+
suggestion: graftExists ? undefined : 'Install Graft: `npm install -g @nanonets/graft`'
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// 5. Codex CLI check
|
|
140
|
+
const codexExists = await commandExists('codex');
|
|
141
|
+
checks.push({
|
|
142
|
+
name: 'Codex CLI (secondary agent)',
|
|
143
|
+
category: 'AI Tooling',
|
|
144
|
+
ok: codexExists,
|
|
145
|
+
detail: codexExists ? 'Codex CLI available in PATH' : 'Codex CLI not found in PATH',
|
|
146
|
+
suggestion: codexExists ? undefined : 'Install Codex CLI if you want dual-agent orchestration'
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// 6. Skills CLI check (skills.sh engine)
|
|
150
|
+
const skillsRes = await runExecutable('npx', ['-y', 'skills', '--version'], { silent: true });
|
|
151
|
+
checks.push({
|
|
152
|
+
name: 'Skills CLI (skills.sh engine)',
|
|
153
|
+
category: 'AI Tooling',
|
|
154
|
+
ok: skillsRes.ok,
|
|
155
|
+
version: skillsRes.stdout,
|
|
156
|
+
detail: skillsRes.ok ? `Functional (${skillsRes.stdout})` : 'Failed running npx skills',
|
|
157
|
+
suggestion: skillsRes.ok ? undefined : 'Check network connectivity or npm/npx configuration'
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// 7. Project-level checks
|
|
161
|
+
checks.push({
|
|
162
|
+
name: `Project configuration (${path.basename(configPath)})`,
|
|
163
|
+
category: 'Project Config',
|
|
164
|
+
ok: !!projectConfig,
|
|
165
|
+
detail: projectConfig
|
|
166
|
+
? `Preset: "${projectConfig.preset}" (v${projectConfig.schemaVersion || '1.0.0'}), pm: ${activePm}, agents: ${projectConfig.agents.primary}/${projectConfig.agents.secondary}`
|
|
167
|
+
: configError
|
|
168
|
+
? `Invalid configuration: ${configError}`
|
|
169
|
+
: 'No aginit.config.json found in current directory',
|
|
170
|
+
suggestion: projectConfig
|
|
171
|
+
? undefined
|
|
172
|
+
: configError
|
|
173
|
+
? 'Correct the configuration fields before running initialization'
|
|
174
|
+
: 'Run `aginit init` to generate declarative configuration'
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// 8. AGENTS.md check
|
|
178
|
+
const agentsPath = path.join(targetDir, 'AGENTS.md');
|
|
179
|
+
const agentsExists = fileExists(agentsPath);
|
|
180
|
+
const agentsContent = agentsExists ? readTextFile(agentsPath) || '' : '';
|
|
181
|
+
const hasIntakeSection = agentsContent.includes('Bootstrap & First Session');
|
|
182
|
+
|
|
183
|
+
checks.push({
|
|
184
|
+
name: 'AGENTS.md Context',
|
|
185
|
+
category: 'Project Config',
|
|
186
|
+
ok: agentsExists && hasIntakeSection,
|
|
187
|
+
detail: agentsExists
|
|
188
|
+
? hasIntakeSection
|
|
189
|
+
? 'AGENTS.md present with intake/bootstrap guidance'
|
|
190
|
+
: 'AGENTS.md present but missing intake guidance'
|
|
191
|
+
: 'AGENTS.md not found in directory',
|
|
192
|
+
suggestion: !agentsExists
|
|
193
|
+
? 'Run `aginit init` to generate standard AGENTS.md'
|
|
194
|
+
: !hasIntakeSection
|
|
195
|
+
? 'Run `aginit init` to add intake section to AGENTS.md'
|
|
196
|
+
: undefined
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// 9. Installed skills check
|
|
200
|
+
const installedSkills = getInstalledSkills(targetDir);
|
|
201
|
+
checks.push({
|
|
202
|
+
name: 'Local Agent Skills (.agents/skills/)',
|
|
203
|
+
category: 'Project Config',
|
|
204
|
+
ok: installedSkills.length > 0,
|
|
205
|
+
detail:
|
|
206
|
+
installedSkills.length > 0
|
|
207
|
+
? `${installedSkills.length} skills installed: ${installedSkills.join(', ')}`
|
|
208
|
+
: 'No skills found in .agents/skills/',
|
|
209
|
+
suggestion:
|
|
210
|
+
installedSkills.length === 0
|
|
211
|
+
? 'Run `aginit init` or `npx skills add ...` to install skills'
|
|
212
|
+
: undefined
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Print grouped results
|
|
216
|
+
const categories: Array<'Environment' | 'AI Tooling' | 'Project Config'> = [
|
|
217
|
+
'Environment',
|
|
218
|
+
'AI Tooling',
|
|
219
|
+
'Project Config'
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
for (const cat of categories) {
|
|
223
|
+
console.log(pc.bold(pc.underline(cat)));
|
|
224
|
+
const catChecks = checks.filter((c) => c.category === cat);
|
|
225
|
+
for (const c of catChecks) {
|
|
226
|
+
const icon = c.ok ? pc.green('✓') : pc.yellow('⚠');
|
|
227
|
+
const name = pc.bold(c.name);
|
|
228
|
+
console.log(` ${icon} ${name}: ${c.detail}`);
|
|
229
|
+
if (c.suggestion) {
|
|
230
|
+
console.log(` ${pc.cyan('→ Suggestion:')} ${pc.dim(c.suggestion)}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
console.log();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const passed = checks.filter((c) => c.ok).length;
|
|
237
|
+
const total = checks.length;
|
|
238
|
+
|
|
239
|
+
if (passed === total) {
|
|
240
|
+
logger.success(pc.bold(`All ${total} health checks passed! System and repo are fully ready.`));
|
|
241
|
+
} else {
|
|
242
|
+
logger.info(`Doctor finished: ${passed}/${total} checks healthy.`);
|
|
243
|
+
}
|
|
244
|
+
}
|
package/src/cli/init.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { readPackageManifest, readProjectConfig } from '../config/validation.js';
|
|
3
|
+
import { createProject, NewProjectOptions } from './new.js';
|
|
4
|
+
import { detectPackageManager } from '../adapters/package-manager.js';
|
|
5
|
+
|
|
6
|
+
function sanitizeProjectName(rawName: string): string {
|
|
7
|
+
const sanitized = rawName
|
|
8
|
+
.toLowerCase()
|
|
9
|
+
.trim()
|
|
10
|
+
.replace(/\s+/g, '-')
|
|
11
|
+
.replace(/[^a-z0-9._-]/g, '')
|
|
12
|
+
.replace(/^[^a-z0-9]+/, '');
|
|
13
|
+
return sanitized || 'ai-project';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function initCurrentDirectory(options: NewProjectOptions = {}): Promise<boolean> {
|
|
17
|
+
const currentDir = process.cwd();
|
|
18
|
+
let projectName = path.basename(currentDir);
|
|
19
|
+
|
|
20
|
+
const saved = readProjectConfig(currentDir);
|
|
21
|
+
const pkg = readPackageManifest(currentDir);
|
|
22
|
+
if (pkg?.name) projectName = pkg.name;
|
|
23
|
+
else if (saved?.name) projectName = saved.name;
|
|
24
|
+
else projectName = sanitizeProjectName(projectName);
|
|
25
|
+
|
|
26
|
+
const packageManager =
|
|
27
|
+
options.packageManager ?? saved?.packageManager ?? detectPackageManager(currentDir) ?? 'pnpm';
|
|
28
|
+
|
|
29
|
+
return createProject(projectName, {
|
|
30
|
+
...options,
|
|
31
|
+
packageManager,
|
|
32
|
+
targetDir: currentDir
|
|
33
|
+
});
|
|
34
|
+
}
|
package/src/cli/main.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { createProject } from './new.js';
|
|
3
|
+
import { initCurrentDirectory } from './init.js';
|
|
4
|
+
import { runDoctor } from './doctor.js';
|
|
5
|
+
import { updateSkills } from '../adapters/skills.js';
|
|
6
|
+
import { buildGraft } from '../adapters/graft.js';
|
|
7
|
+
import { readProjectConfig } from '../config/validation.js';
|
|
8
|
+
import { logger } from '../utils/logger.js';
|
|
9
|
+
import { PresetType, WebFramework, PackageManager } from '../config/schema.js';
|
|
10
|
+
import {
|
|
11
|
+
isInteractive,
|
|
12
|
+
promptNewProject,
|
|
13
|
+
promptInitProject,
|
|
14
|
+
runInteractiveWizard
|
|
15
|
+
} from './wizard.js';
|
|
16
|
+
|
|
17
|
+
export function createCliProgram(): Command {
|
|
18
|
+
const program = new Command();
|
|
19
|
+
|
|
20
|
+
program
|
|
21
|
+
.name('aginit')
|
|
22
|
+
.description(
|
|
23
|
+
'Minimal, modular AI-first project bootstrapper for T3 Code, Antigravity, and Codex'
|
|
24
|
+
)
|
|
25
|
+
.version('0.1.0');
|
|
26
|
+
|
|
27
|
+
// Root action: interactive wizard when in TTY, else display help
|
|
28
|
+
program.action(async () => {
|
|
29
|
+
if (isInteractive()) {
|
|
30
|
+
await runInteractiveWizard();
|
|
31
|
+
} else {
|
|
32
|
+
program.outputHelp();
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Command: new
|
|
37
|
+
program
|
|
38
|
+
.command('new [projectName]')
|
|
39
|
+
.description('Create a brand new AI-first project')
|
|
40
|
+
.option('-p, --preset <preset>', 'Project preset: web | cli | generic', 'generic')
|
|
41
|
+
.option('-f, --framework <framework>', 'Web framework: none | vite | next | existing', 'none')
|
|
42
|
+
.option('-m, --package-manager <pm>', 'Package manager: pnpm | npm | yarn | bun', 'pnpm')
|
|
43
|
+
.option(
|
|
44
|
+
'--spec-workflow',
|
|
45
|
+
'Include full spec and tickets skills (to-spec, to-tickets, implement-spec)'
|
|
46
|
+
)
|
|
47
|
+
.option('--no-skills', 'Skip installing AI skills')
|
|
48
|
+
.option('--no-graft', 'Skip codebase graph (Graft) setup')
|
|
49
|
+
.option('--no-git', 'Skip git repository initialization')
|
|
50
|
+
.option('--dry-run', 'Simulate creation without writing changes to disk')
|
|
51
|
+
.action(async (projectName: string | undefined, options: any, command: Command) => {
|
|
52
|
+
let finalName = projectName;
|
|
53
|
+
let finalPreset = options.preset;
|
|
54
|
+
let finalFramework = options.framework;
|
|
55
|
+
let finalPackageManager = options.packageManager;
|
|
56
|
+
let finalSpecWorkflow = options.specWorkflow;
|
|
57
|
+
|
|
58
|
+
if (!finalName) {
|
|
59
|
+
if (!isInteractive()) {
|
|
60
|
+
program.error("error: missing required argument 'projectName'");
|
|
61
|
+
}
|
|
62
|
+
const answers = await promptNewProject({
|
|
63
|
+
preset:
|
|
64
|
+
command.getOptionValueSource('preset') === 'cli'
|
|
65
|
+
? (options.preset as PresetType)
|
|
66
|
+
: undefined,
|
|
67
|
+
framework:
|
|
68
|
+
command.getOptionValueSource('framework') === 'cli'
|
|
69
|
+
? (options.framework as WebFramework)
|
|
70
|
+
: undefined,
|
|
71
|
+
packageManager:
|
|
72
|
+
command.getOptionValueSource('packageManager') === 'cli'
|
|
73
|
+
? (options.packageManager as PackageManager)
|
|
74
|
+
: undefined,
|
|
75
|
+
specWorkflow:
|
|
76
|
+
command.getOptionValueSource('specWorkflow') === 'cli'
|
|
77
|
+
? options.specWorkflow
|
|
78
|
+
: undefined
|
|
79
|
+
});
|
|
80
|
+
finalName = answers.projectName;
|
|
81
|
+
finalPreset = answers.preset;
|
|
82
|
+
finalFramework = answers.framework;
|
|
83
|
+
finalPackageManager = answers.packageManager;
|
|
84
|
+
finalSpecWorkflow = answers.specWorkflow;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const complete = await createProject(finalName, {
|
|
88
|
+
preset: finalPreset as PresetType,
|
|
89
|
+
framework: finalFramework as WebFramework,
|
|
90
|
+
packageManager: finalPackageManager as PackageManager,
|
|
91
|
+
specWorkflow: finalSpecWorkflow,
|
|
92
|
+
skills: options.skills,
|
|
93
|
+
graft: options.graft,
|
|
94
|
+
git: options.git,
|
|
95
|
+
dryRun: options.dryRun
|
|
96
|
+
});
|
|
97
|
+
if (!complete && !options.dryRun) process.exitCode = 1;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Command: init
|
|
101
|
+
program
|
|
102
|
+
.command('init')
|
|
103
|
+
.description('Initialize or update AI-first capabilities in the current directory')
|
|
104
|
+
.option('-p, --preset <preset>', 'Project preset: web | cli | generic')
|
|
105
|
+
.option('-f, --framework <framework>', 'Web framework: none | vite | next | existing')
|
|
106
|
+
.option('-m, --package-manager <pm>', 'Package manager: pnpm | npm | yarn | bun')
|
|
107
|
+
.option(
|
|
108
|
+
'--spec-workflow',
|
|
109
|
+
'Include full spec and tickets skills (to-spec, to-tickets, implement-spec)'
|
|
110
|
+
)
|
|
111
|
+
.option('--no-skills', 'Skip installing AI skills')
|
|
112
|
+
.option('--no-graft', 'Skip codebase graph (Graft) setup')
|
|
113
|
+
.option('--no-git', 'Skip git repository initialization')
|
|
114
|
+
.option('--dry-run', 'Simulate setup without writing changes to disk')
|
|
115
|
+
.action(async (options: any, command: Command) => {
|
|
116
|
+
let finalPreset = options.preset;
|
|
117
|
+
let finalFramework = options.framework;
|
|
118
|
+
let finalPackageManager = options.packageManager;
|
|
119
|
+
let finalSpecWorkflow = options.specWorkflow;
|
|
120
|
+
|
|
121
|
+
const isPresetExplicit = command.getOptionValueSource('preset') === 'cli';
|
|
122
|
+
const isFrameworkExplicit = command.getOptionValueSource('framework') === 'cli';
|
|
123
|
+
const isPmExplicit = command.getOptionValueSource('packageManager') === 'cli';
|
|
124
|
+
const isSpecExplicit = command.getOptionValueSource('specWorkflow') === 'cli';
|
|
125
|
+
|
|
126
|
+
const savedConfig = readProjectConfig(process.cwd());
|
|
127
|
+
if (isInteractive() && !isPresetExplicit && !options.dryRun && !savedConfig) {
|
|
128
|
+
const answers = await promptInitProject({
|
|
129
|
+
preset: isPresetExplicit ? (options.preset as PresetType) : undefined,
|
|
130
|
+
framework: isFrameworkExplicit ? (options.framework as WebFramework) : undefined,
|
|
131
|
+
packageManager: isPmExplicit ? (options.packageManager as PackageManager) : undefined,
|
|
132
|
+
specWorkflow: isSpecExplicit ? options.specWorkflow : undefined
|
|
133
|
+
});
|
|
134
|
+
finalPreset = answers.preset;
|
|
135
|
+
finalFramework = answers.framework;
|
|
136
|
+
finalPackageManager = answers.packageManager;
|
|
137
|
+
finalSpecWorkflow = answers.specWorkflow;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const complete = await initCurrentDirectory({
|
|
141
|
+
preset: finalPreset as PresetType,
|
|
142
|
+
framework: finalFramework as WebFramework,
|
|
143
|
+
packageManager: finalPackageManager as PackageManager,
|
|
144
|
+
specWorkflow: finalSpecWorkflow,
|
|
145
|
+
skills: options.skills,
|
|
146
|
+
graft: options.graft,
|
|
147
|
+
git: options.git,
|
|
148
|
+
dryRun: options.dryRun
|
|
149
|
+
});
|
|
150
|
+
if (!complete && !options.dryRun) process.exitCode = 1;
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Command: doctor
|
|
154
|
+
program
|
|
155
|
+
.command('doctor')
|
|
156
|
+
.description('Run system and project diagnostics for AI agent readiness')
|
|
157
|
+
.option('-d, --dir <path>', 'Target directory to check', process.cwd())
|
|
158
|
+
.action(async (options: any) => {
|
|
159
|
+
await runDoctor(options.dir);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Command: update
|
|
163
|
+
program
|
|
164
|
+
.command('update')
|
|
165
|
+
.description('Update installed skills and refresh codebase graph')
|
|
166
|
+
.option('--dry-run', 'Simulate update')
|
|
167
|
+
.action(async (options: any) => {
|
|
168
|
+
logger.banner('Aginit — Update');
|
|
169
|
+
const targetDir = process.cwd();
|
|
170
|
+
const config = readProjectConfig(targetDir);
|
|
171
|
+
const skillsOk =
|
|
172
|
+
config?.skills.sources.length === 0
|
|
173
|
+
? true
|
|
174
|
+
: await updateSkills(targetDir, { dryRun: options.dryRun });
|
|
175
|
+
const graftOk =
|
|
176
|
+
config?.codebase.graft === false
|
|
177
|
+
? true
|
|
178
|
+
: await buildGraft(targetDir, { dryRun: options.dryRun });
|
|
179
|
+
if (skillsOk && graftOk) logger.success('Update finished.');
|
|
180
|
+
else {
|
|
181
|
+
if (!skillsOk) logger.warn('Skills update failed. Check connectivity and the skills CLI.');
|
|
182
|
+
if (!graftOk)
|
|
183
|
+
logger.warn(
|
|
184
|
+
'Graft refresh failed or Graft is unavailable. Check installation and rerun graft build.'
|
|
185
|
+
);
|
|
186
|
+
process.exitCode = 1;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
return program;
|
|
191
|
+
}
|