@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
package/src/cli/new.ts
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import { getDefaultConfig } from '../config/defaults.js';
|
|
4
|
+
import {
|
|
5
|
+
AGINIT_CONFIG_FILENAME,
|
|
6
|
+
PresetType,
|
|
7
|
+
WebFramework,
|
|
8
|
+
PackageManager
|
|
9
|
+
} from '../config/schema.js';
|
|
10
|
+
import { getPreset } from '../presets/registry.js';
|
|
11
|
+
import { setupGit } from '../adapters/git.js';
|
|
12
|
+
import { setupAgentsMarkdown } from '../adapters/agents-md.js';
|
|
13
|
+
import { installAllSkills } from '../adapters/skills.js';
|
|
14
|
+
import { initGraft } from '../adapters/graft.js';
|
|
15
|
+
import { ensureDir, writeJsonFile } from '../utils/fs.js';
|
|
16
|
+
import {
|
|
17
|
+
readProjectConfig,
|
|
18
|
+
readPackageManifest,
|
|
19
|
+
validateConfig,
|
|
20
|
+
validateProjectName,
|
|
21
|
+
PRESETS,
|
|
22
|
+
FRAMEWORKS,
|
|
23
|
+
SUPPORTED_PACKAGE_MANAGERS
|
|
24
|
+
} from '../config/validation.js';
|
|
25
|
+
import { logger } from '../utils/logger.js';
|
|
26
|
+
|
|
27
|
+
export interface NewProjectOptions {
|
|
28
|
+
preset?: PresetType;
|
|
29
|
+
framework?: WebFramework;
|
|
30
|
+
packageManager?: PackageManager;
|
|
31
|
+
specWorkflow?: boolean;
|
|
32
|
+
skills?: boolean;
|
|
33
|
+
graft?: boolean;
|
|
34
|
+
git?: boolean;
|
|
35
|
+
dryRun?: boolean;
|
|
36
|
+
targetDir?: string;
|
|
37
|
+
silent?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function createProject(
|
|
41
|
+
projectName: string,
|
|
42
|
+
options: NewProjectOptions = {}
|
|
43
|
+
): Promise<boolean> {
|
|
44
|
+
validateProjectName(projectName);
|
|
45
|
+
if (options.preset !== undefined && !PRESETS.includes(options.preset))
|
|
46
|
+
throw new Error('Invalid preset.');
|
|
47
|
+
if (options.framework !== undefined && !FRAMEWORKS.includes(options.framework))
|
|
48
|
+
throw new Error('Invalid framework.');
|
|
49
|
+
if (
|
|
50
|
+
options.packageManager !== undefined &&
|
|
51
|
+
!SUPPORTED_PACKAGE_MANAGERS.includes(options.packageManager)
|
|
52
|
+
)
|
|
53
|
+
throw new Error('Invalid package manager.');
|
|
54
|
+
const defaultDir =
|
|
55
|
+
projectName.startsWith('@') && projectName.includes('/')
|
|
56
|
+
? projectName.split('/').pop()!
|
|
57
|
+
: projectName;
|
|
58
|
+
const targetDir = path.resolve(options.targetDir || path.join(process.cwd(), defaultDir));
|
|
59
|
+
const existingConfig = readProjectConfig(targetDir);
|
|
60
|
+
const manifest = readPackageManifest(targetDir); // Validate before creating or modifying anything.
|
|
61
|
+
const presetName = options.preset ?? existingConfig?.preset ?? 'generic';
|
|
62
|
+
const framework =
|
|
63
|
+
options.framework ?? (presetName === 'web' ? existingConfig?.framework : undefined);
|
|
64
|
+
if (presetName !== 'web' && framework && framework !== 'none' && framework !== 'existing')
|
|
65
|
+
throw new Error('A web framework requires --preset web.');
|
|
66
|
+
if (presetName === 'web' && framework === 'existing' && !manifest)
|
|
67
|
+
throw new Error(
|
|
68
|
+
'An existing web framework requires an existing package.json. Use --framework none for a new baseline.'
|
|
69
|
+
);
|
|
70
|
+
const packageManager = options.packageManager ?? existingConfig?.packageManager ?? 'pnpm';
|
|
71
|
+
const dryRun = !!options.dryRun;
|
|
72
|
+
const silent = !!options.silent;
|
|
73
|
+
const presetHandler = getPreset(presetName);
|
|
74
|
+
const defaults = getDefaultConfig(projectName, presetName, {
|
|
75
|
+
framework,
|
|
76
|
+
packageManager,
|
|
77
|
+
specWorkflow: options.specWorkflow
|
|
78
|
+
});
|
|
79
|
+
const config = validateConfig(
|
|
80
|
+
existingConfig
|
|
81
|
+
? {
|
|
82
|
+
...existingConfig,
|
|
83
|
+
name: projectName,
|
|
84
|
+
preset: presetName,
|
|
85
|
+
packageManager,
|
|
86
|
+
...(presetName === 'web' ? { framework: framework ?? 'none' } : {}),
|
|
87
|
+
browser:
|
|
88
|
+
options.framework !== undefined || options.preset !== undefined
|
|
89
|
+
? { ...existingConfig.browser, ...defaults.browser }
|
|
90
|
+
: existingConfig.browser,
|
|
91
|
+
skills:
|
|
92
|
+
options.specWorkflow !== undefined
|
|
93
|
+
? {
|
|
94
|
+
...existingConfig.skills,
|
|
95
|
+
workflow: defaults.skills.workflow,
|
|
96
|
+
sources: existingConfig.skills.sources.map((source) =>
|
|
97
|
+
source.package === 'mattpocock/skills'
|
|
98
|
+
? {
|
|
99
|
+
...source,
|
|
100
|
+
skills: defaults.skills.sources.find((s) => s.package === source.package)!
|
|
101
|
+
.skills
|
|
102
|
+
}
|
|
103
|
+
: source
|
|
104
|
+
)
|
|
105
|
+
}
|
|
106
|
+
: existingConfig.skills
|
|
107
|
+
}
|
|
108
|
+
: defaults
|
|
109
|
+
);
|
|
110
|
+
if (presetName !== 'web') delete config.framework;
|
|
111
|
+
if (options.graft === false) config.codebase = { ...config.codebase, graft: false };
|
|
112
|
+
if (options.skills === false && !existingConfig) config.skills.sources = [];
|
|
113
|
+
if (!silent)
|
|
114
|
+
logger.banner(
|
|
115
|
+
`Aginit — ${projectName}`,
|
|
116
|
+
`Preset: ${presetName} | PM: ${packageManager} | Target: ${targetDir}${dryRun ? ' (DRY RUN)' : ''}`
|
|
117
|
+
);
|
|
118
|
+
let complete = true;
|
|
119
|
+
|
|
120
|
+
const totalSteps = 6;
|
|
121
|
+
let currentStep = 1;
|
|
122
|
+
|
|
123
|
+
// Scaffold into the clean directory before Git adds files that upstream tools reject.
|
|
124
|
+
if (!silent) logger.step(currentStep++, totalSteps, `Scaffolding preset: ${presetHandler.name}`);
|
|
125
|
+
ensureDir(targetDir, dryRun);
|
|
126
|
+
await presetHandler.scaffold({ targetDir, projectName, config, dryRun, silent: true });
|
|
127
|
+
if (!silent) logger.success(`Scaffolded ${presetHandler.name} structure`);
|
|
128
|
+
|
|
129
|
+
if (!silent) logger.step(currentStep++, totalSteps, 'Configuring Git');
|
|
130
|
+
if (options.git !== false) {
|
|
131
|
+
const gitOk = await setupGit(targetDir, { dryRun, silent: true });
|
|
132
|
+
complete &&= gitOk;
|
|
133
|
+
if (gitOk && !silent) logger.success('Git repository initialized and .gitignore configured');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Step 3: Write declarative project config
|
|
137
|
+
if (!silent) logger.step(currentStep++, totalSteps, 'Writing declarative project configuration');
|
|
138
|
+
writeJsonFile(path.join(targetDir, AGINIT_CONFIG_FILENAME), config, {
|
|
139
|
+
overwrite: true,
|
|
140
|
+
dryRun,
|
|
141
|
+
silent
|
|
142
|
+
});
|
|
143
|
+
if (!silent) logger.success(`Wrote ${AGINIT_CONFIG_FILENAME}`);
|
|
144
|
+
|
|
145
|
+
// Step 4: Write or update AGENTS.md intake contract
|
|
146
|
+
if (!silent) logger.step(currentStep++, totalSteps, 'Generating AGENTS.md intake guidance');
|
|
147
|
+
setupAgentsMarkdown(targetDir, config, { dryRun, silent });
|
|
148
|
+
if (!silent) logger.success('Configured AGENTS.md');
|
|
149
|
+
|
|
150
|
+
// Step 5: Install skills via skills.sh engine
|
|
151
|
+
if (!silent) logger.step(currentStep++, totalSteps, 'Installing upstream skills via skills.sh');
|
|
152
|
+
if (options.skills !== false && config.skills.sources.length > 0) {
|
|
153
|
+
const { installed, failed } = await installAllSkills(targetDir, config.skills.sources, {
|
|
154
|
+
agents: [config.agents.primary, config.agents.secondary],
|
|
155
|
+
dryRun,
|
|
156
|
+
silent
|
|
157
|
+
});
|
|
158
|
+
complete &&= failed.length === 0;
|
|
159
|
+
if (!silent) {
|
|
160
|
+
if (installed.length > 0) {
|
|
161
|
+
logger.success(`Installed skills: ${installed.join(', ')}`);
|
|
162
|
+
}
|
|
163
|
+
if (failed.length > 0) {
|
|
164
|
+
logger.warn(`Failed installing: ${failed.join(', ')}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} else if (!silent) {
|
|
168
|
+
logger.dim('Skills installation skipped (--no-skills or empty configuration)');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Step 6: Codebase intelligence (Graft)
|
|
172
|
+
if (!silent) logger.step(currentStep++, totalSteps, 'Configuring codebase intelligence');
|
|
173
|
+
if (config.codebase.graft && options.graft !== false) {
|
|
174
|
+
const graftOk = await initGraft(targetDir, { dryRun, silent });
|
|
175
|
+
complete &&= graftOk;
|
|
176
|
+
if (graftOk && !silent) {
|
|
177
|
+
logger.success('Graft context graph initialized');
|
|
178
|
+
}
|
|
179
|
+
} else if (!silent) {
|
|
180
|
+
logger.dim('Graft setup skipped (--no-graft or codebase.graft: false)');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (!silent) {
|
|
184
|
+
console.log();
|
|
185
|
+
if (complete) logger.success(pc.bold(`Project "${projectName}" is ready for development!`));
|
|
186
|
+
else
|
|
187
|
+
logger.warn(
|
|
188
|
+
`Project "${projectName}" was scaffolded with incomplete integrations. Resolve the warnings above and rerun aginit init.`
|
|
189
|
+
);
|
|
190
|
+
console.log();
|
|
191
|
+
console.log('Next steps:');
|
|
192
|
+
console.log(pc.cyan(` cd ${path.relative(process.cwd(), targetDir) || '.'}`));
|
|
193
|
+
console.log(pc.cyan(` ${packageManager} install`));
|
|
194
|
+
console.log(pc.cyan(' Open the repository in T3 Code'));
|
|
195
|
+
console.log(pc.dim(' Prompt the agent: ') + pc.bold(pc.green('"bootstrap this project"')));
|
|
196
|
+
console.log();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return complete;
|
|
200
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import { PresetType, WebFramework, PackageManager } from '../config/schema.js';
|
|
4
|
+
import { validateProjectName, readProjectConfig } from '../config/validation.js';
|
|
5
|
+
import { detectPackageManager } from '../adapters/package-manager.js';
|
|
6
|
+
import { createProject } from './new.js';
|
|
7
|
+
import { initCurrentDirectory } from './init.js';
|
|
8
|
+
|
|
9
|
+
export function isInteractive(): boolean {
|
|
10
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY && !process.env.CI);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function assertNotCancelled<T>(value: T | symbol): Exclude<T, symbol> {
|
|
14
|
+
if (p.isCancel(value)) {
|
|
15
|
+
p.cancel('Operation cancelled.');
|
|
16
|
+
process.exit(0);
|
|
17
|
+
}
|
|
18
|
+
return value as Exclude<T, symbol>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface WizardNewAnswers {
|
|
22
|
+
projectName: string;
|
|
23
|
+
preset: PresetType;
|
|
24
|
+
framework: WebFramework;
|
|
25
|
+
packageManager: PackageManager;
|
|
26
|
+
specWorkflow: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface WizardInitAnswers {
|
|
30
|
+
preset: PresetType;
|
|
31
|
+
framework: WebFramework;
|
|
32
|
+
packageManager: PackageManager;
|
|
33
|
+
specWorkflow: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function promptNewProject(defaults?: {
|
|
37
|
+
projectName?: string;
|
|
38
|
+
preset?: PresetType;
|
|
39
|
+
framework?: WebFramework;
|
|
40
|
+
packageManager?: PackageManager;
|
|
41
|
+
specWorkflow?: boolean;
|
|
42
|
+
}): Promise<WizardNewAnswers> {
|
|
43
|
+
let projectName: string = defaults?.projectName || '';
|
|
44
|
+
if (!projectName) {
|
|
45
|
+
const res = await p.text({
|
|
46
|
+
message: 'What is your project name?',
|
|
47
|
+
placeholder: 'my-ai-project',
|
|
48
|
+
validate: (val) => {
|
|
49
|
+
if (!val || val.trim().length === 0) return 'Project name is required';
|
|
50
|
+
try {
|
|
51
|
+
validateProjectName(val.trim());
|
|
52
|
+
} catch (error) {
|
|
53
|
+
return error instanceof Error ? error.message : 'Invalid project name';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
projectName = String(assertNotCancelled(res)).trim();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let preset = defaults?.preset;
|
|
61
|
+
if (!preset) {
|
|
62
|
+
const res = await p.select({
|
|
63
|
+
message: 'Select a project preset:',
|
|
64
|
+
initialValue: 'web',
|
|
65
|
+
options: [
|
|
66
|
+
{
|
|
67
|
+
value: 'web',
|
|
68
|
+
label: 'Web Application',
|
|
69
|
+
hint: 'React, Next.js, Vite, or agnostic + Vitest/Playwright'
|
|
70
|
+
},
|
|
71
|
+
{ value: 'cli', label: 'CLI Tool', hint: 'TypeScript, Commander, Picocolors + Vitest' },
|
|
72
|
+
{
|
|
73
|
+
value: 'generic',
|
|
74
|
+
label: 'Generic / Existing Codebase',
|
|
75
|
+
hint: 'Codebase graph + agent intake guide'
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
});
|
|
79
|
+
preset = assertNotCancelled(res) as PresetType;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let framework: WebFramework = defaults?.framework || 'none';
|
|
83
|
+
if (preset === 'web' && defaults?.framework === undefined) {
|
|
84
|
+
const res = await p.select({
|
|
85
|
+
message: 'Choose a web framework:',
|
|
86
|
+
initialValue: 'none',
|
|
87
|
+
options: [
|
|
88
|
+
{
|
|
89
|
+
value: 'none',
|
|
90
|
+
label: 'Framework-agnostic (Recommended)',
|
|
91
|
+
hint: 'Clean TypeScript + Vitest, zero bundler lock-in'
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
value: 'vite',
|
|
95
|
+
label: 'Vite',
|
|
96
|
+
hint: 'Official Vite React-TS scaffolder + Playwright E2E'
|
|
97
|
+
},
|
|
98
|
+
{ value: 'next', label: 'Next.js', hint: 'Official create-next-app + Playwright E2E' }
|
|
99
|
+
]
|
|
100
|
+
});
|
|
101
|
+
framework = assertNotCancelled(res) as WebFramework;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let specWorkflow = defaults?.specWorkflow;
|
|
105
|
+
if (specWorkflow === undefined) {
|
|
106
|
+
const res = await p.select({
|
|
107
|
+
message: 'Select AI engineering workflow:',
|
|
108
|
+
initialValue: 'minimal',
|
|
109
|
+
options: [
|
|
110
|
+
{
|
|
111
|
+
value: 'minimal',
|
|
112
|
+
label: 'Standard Workflow (Recommended)',
|
|
113
|
+
hint: 'TDD, Code Review, Diagnosing Bugs'
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
value: 'spec',
|
|
117
|
+
label: 'Spec & Tickets Workflow',
|
|
118
|
+
hint: 'Adds to-spec, to-tickets, implement-spec'
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
});
|
|
122
|
+
const selected = assertNotCancelled(res);
|
|
123
|
+
specWorkflow = selected === 'spec';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
let packageManager = defaults?.packageManager;
|
|
127
|
+
if (!packageManager) {
|
|
128
|
+
const res = await p.select({
|
|
129
|
+
message: 'Select package manager:',
|
|
130
|
+
initialValue: 'pnpm',
|
|
131
|
+
options: [
|
|
132
|
+
{
|
|
133
|
+
value: 'pnpm',
|
|
134
|
+
label: 'pnpm (Recommended)',
|
|
135
|
+
hint: 'Fast, disk-efficient & compliant workspace'
|
|
136
|
+
},
|
|
137
|
+
{ value: 'npm', label: 'npm', hint: 'Standard Node.js package manager' },
|
|
138
|
+
{ value: 'yarn', label: 'yarn', hint: 'Yarn package manager' },
|
|
139
|
+
{ value: 'bun', label: 'bun', hint: 'Fast all-in-one JavaScript runtime & toolkit' }
|
|
140
|
+
]
|
|
141
|
+
});
|
|
142
|
+
packageManager = assertNotCancelled(res) as PackageManager;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
projectName,
|
|
147
|
+
preset,
|
|
148
|
+
framework,
|
|
149
|
+
packageManager,
|
|
150
|
+
specWorkflow
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export async function promptInitProject(defaults?: {
|
|
155
|
+
targetDir?: string;
|
|
156
|
+
preset?: PresetType;
|
|
157
|
+
framework?: WebFramework;
|
|
158
|
+
packageManager?: PackageManager;
|
|
159
|
+
specWorkflow?: boolean;
|
|
160
|
+
}): Promise<WizardInitAnswers> {
|
|
161
|
+
const targetDir = defaults?.targetDir || process.cwd();
|
|
162
|
+
|
|
163
|
+
let preset = defaults?.preset;
|
|
164
|
+
if (!preset) {
|
|
165
|
+
const res = await p.select({
|
|
166
|
+
message: 'Select project preset to configure in this directory:',
|
|
167
|
+
initialValue: 'web',
|
|
168
|
+
options: [
|
|
169
|
+
{ value: 'web', label: 'Web Application', hint: 'Impeccable UI, agent-browser, Vitest' },
|
|
170
|
+
{ value: 'cli', label: 'CLI Tool', hint: 'TypeScript, Vitest' },
|
|
171
|
+
{
|
|
172
|
+
value: 'generic',
|
|
173
|
+
label: 'Generic Repository',
|
|
174
|
+
hint: 'Codebase graph + agent intake guide'
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
});
|
|
178
|
+
preset = assertNotCancelled(res) as PresetType;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let framework: WebFramework = defaults?.framework || 'existing';
|
|
182
|
+
if (preset === 'web' && defaults?.framework === undefined) {
|
|
183
|
+
const res = await p.select({
|
|
184
|
+
message: 'Select web framework setting:',
|
|
185
|
+
initialValue: 'existing',
|
|
186
|
+
options: [
|
|
187
|
+
{
|
|
188
|
+
value: 'existing',
|
|
189
|
+
label: 'Existing Web Setup (Recommended)',
|
|
190
|
+
hint: 'Preserve existing bundler and dependencies'
|
|
191
|
+
},
|
|
192
|
+
{ value: 'none', label: 'Framework-agnostic', hint: 'Baseline TypeScript' },
|
|
193
|
+
{ value: 'vite', label: 'Vite', hint: 'Vite React setup' },
|
|
194
|
+
{ value: 'next', label: 'Next.js', hint: 'Next.js setup' }
|
|
195
|
+
]
|
|
196
|
+
});
|
|
197
|
+
framework = assertNotCancelled(res) as WebFramework;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
let specWorkflow = defaults?.specWorkflow;
|
|
201
|
+
if (specWorkflow === undefined) {
|
|
202
|
+
const res = await p.select({
|
|
203
|
+
message: 'Select AI engineering workflow:',
|
|
204
|
+
initialValue: 'minimal',
|
|
205
|
+
options: [
|
|
206
|
+
{
|
|
207
|
+
value: 'minimal',
|
|
208
|
+
label: 'Standard Workflow (Recommended)',
|
|
209
|
+
hint: 'TDD, Code Review, Diagnosing Bugs'
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
value: 'spec',
|
|
213
|
+
label: 'Spec & Tickets Workflow',
|
|
214
|
+
hint: 'Adds to-spec, to-tickets, implement-spec'
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
});
|
|
218
|
+
const selected = assertNotCancelled(res);
|
|
219
|
+
specWorkflow = selected === 'spec';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Auto-detect and preserve existing package manager without prompting
|
|
223
|
+
let packageManager = defaults?.packageManager;
|
|
224
|
+
if (!packageManager) {
|
|
225
|
+
const detected = detectPackageManager(targetDir);
|
|
226
|
+
if (detected) {
|
|
227
|
+
packageManager = detected;
|
|
228
|
+
} else {
|
|
229
|
+
const res = await p.select({
|
|
230
|
+
message: 'Select package manager for this project:',
|
|
231
|
+
initialValue: 'pnpm',
|
|
232
|
+
options: [
|
|
233
|
+
{ value: 'pnpm', label: 'pnpm (Recommended)', hint: 'Fast, disk-efficient workspace' },
|
|
234
|
+
{ value: 'npm', label: 'npm', hint: 'Standard Node.js package manager' },
|
|
235
|
+
{ value: 'yarn', label: 'yarn', hint: 'Yarn package manager' },
|
|
236
|
+
{ value: 'bun', label: 'bun', hint: 'Fast runtime and package manager' }
|
|
237
|
+
]
|
|
238
|
+
});
|
|
239
|
+
packageManager = assertNotCancelled(res) as PackageManager;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
preset,
|
|
245
|
+
framework,
|
|
246
|
+
packageManager,
|
|
247
|
+
specWorkflow
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export async function runInteractiveWizard(): Promise<void> {
|
|
252
|
+
p.intro(pc.bgCyan(pc.black(' aginit ')) + pc.dim(' — AI-First Project Bootstrap'));
|
|
253
|
+
|
|
254
|
+
const action = assertNotCancelled(
|
|
255
|
+
await p.select({
|
|
256
|
+
message: 'What would you like to do?',
|
|
257
|
+
initialValue: 'new',
|
|
258
|
+
options: [
|
|
259
|
+
{
|
|
260
|
+
value: 'new',
|
|
261
|
+
label: 'Create a brand new AI-first project',
|
|
262
|
+
hint: 'Scaffold in a new directory'
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
value: 'init',
|
|
266
|
+
label: 'Initialize AI capabilities in current directory',
|
|
267
|
+
hint: 'Add AGENTS.md, skills & Graft safely'
|
|
268
|
+
}
|
|
269
|
+
]
|
|
270
|
+
})
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
let complete: boolean;
|
|
274
|
+
if (action === 'new') {
|
|
275
|
+
const answers = await promptNewProject();
|
|
276
|
+
p.outro(pc.cyan(`Scaffolding "${answers.projectName}"...`));
|
|
277
|
+
complete = await createProject(answers.projectName, {
|
|
278
|
+
preset: answers.preset,
|
|
279
|
+
framework: answers.framework,
|
|
280
|
+
packageManager: answers.packageManager,
|
|
281
|
+
specWorkflow: answers.specWorkflow
|
|
282
|
+
});
|
|
283
|
+
} else {
|
|
284
|
+
const saved = readProjectConfig(process.cwd());
|
|
285
|
+
if (saved) {
|
|
286
|
+
p.outro(pc.cyan('Configuring current directory using saved settings...'));
|
|
287
|
+
complete = await initCurrentDirectory();
|
|
288
|
+
} else {
|
|
289
|
+
const answers = await promptInitProject();
|
|
290
|
+
p.outro(pc.cyan('Configuring current directory...'));
|
|
291
|
+
complete = await initCurrentDirectory({
|
|
292
|
+
preset: answers.preset,
|
|
293
|
+
framework: answers.framework,
|
|
294
|
+
packageManager: answers.packageManager,
|
|
295
|
+
specWorkflow: answers.specWorkflow
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (!complete) process.exitCode = 1;
|
|
300
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { AginitConfig, PresetType, WebFramework, PackageManager } from './schema.js';
|
|
2
|
+
|
|
3
|
+
export interface DefaultConfigOptions {
|
|
4
|
+
framework?: WebFramework;
|
|
5
|
+
specWorkflow?: boolean;
|
|
6
|
+
packageManager?: PackageManager;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getDefaultConfig(
|
|
10
|
+
name: string,
|
|
11
|
+
preset: PresetType,
|
|
12
|
+
options: DefaultConfigOptions = {}
|
|
13
|
+
): AginitConfig {
|
|
14
|
+
const specWorkflow = !!options.specWorkflow;
|
|
15
|
+
const packageManager = options.packageManager || 'pnpm';
|
|
16
|
+
const mattSkills = specWorkflow
|
|
17
|
+
? ['tdd', 'code-review', 'diagnosing-bugs', 'to-spec', 'to-tickets', 'implement-spec']
|
|
18
|
+
: ['tdd', 'code-review', 'diagnosing-bugs'];
|
|
19
|
+
|
|
20
|
+
switch (preset) {
|
|
21
|
+
case 'web': {
|
|
22
|
+
const framework = options.framework || 'none';
|
|
23
|
+
const hasWebTarget = framework === 'vite' || framework === 'next';
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
schemaVersion: '1.0.0',
|
|
27
|
+
name,
|
|
28
|
+
preset: 'web',
|
|
29
|
+
framework,
|
|
30
|
+
packageManager,
|
|
31
|
+
agents: {
|
|
32
|
+
primary: 'antigravity',
|
|
33
|
+
secondary: 'codex'
|
|
34
|
+
},
|
|
35
|
+
skills: {
|
|
36
|
+
workflow: specWorkflow ? 'spec' : 'minimal',
|
|
37
|
+
sources: [
|
|
38
|
+
{
|
|
39
|
+
package: 'mattpocock/skills',
|
|
40
|
+
skills: mattSkills
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
package: 'pbakaus/impeccable',
|
|
44
|
+
skills: ['impeccable']
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
package: 'vercel-labs/agent-browser',
|
|
48
|
+
skills: ['agent-browser']
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
codebase: {
|
|
53
|
+
graft: true
|
|
54
|
+
},
|
|
55
|
+
browser: {
|
|
56
|
+
visualAgent: true,
|
|
57
|
+
playwright: hasWebTarget
|
|
58
|
+
},
|
|
59
|
+
docs: {
|
|
60
|
+
adr: true
|
|
61
|
+
},
|
|
62
|
+
github: {
|
|
63
|
+
ci: true
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
case 'cli':
|
|
69
|
+
return {
|
|
70
|
+
schemaVersion: '1.0.0',
|
|
71
|
+
name,
|
|
72
|
+
preset: 'cli',
|
|
73
|
+
packageManager,
|
|
74
|
+
agents: {
|
|
75
|
+
primary: 'antigravity',
|
|
76
|
+
secondary: 'codex'
|
|
77
|
+
},
|
|
78
|
+
skills: {
|
|
79
|
+
workflow: specWorkflow ? 'spec' : 'minimal',
|
|
80
|
+
sources: [
|
|
81
|
+
{
|
|
82
|
+
package: 'mattpocock/skills',
|
|
83
|
+
skills: mattSkills
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
codebase: {
|
|
88
|
+
graft: true
|
|
89
|
+
},
|
|
90
|
+
browser: {
|
|
91
|
+
visualAgent: false,
|
|
92
|
+
playwright: false
|
|
93
|
+
},
|
|
94
|
+
docs: {
|
|
95
|
+
adr: true
|
|
96
|
+
},
|
|
97
|
+
github: {
|
|
98
|
+
ci: true
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
case 'generic':
|
|
103
|
+
default:
|
|
104
|
+
return {
|
|
105
|
+
schemaVersion: '1.0.0',
|
|
106
|
+
name,
|
|
107
|
+
preset: 'generic',
|
|
108
|
+
packageManager,
|
|
109
|
+
agents: {
|
|
110
|
+
primary: 'antigravity',
|
|
111
|
+
secondary: 'codex'
|
|
112
|
+
},
|
|
113
|
+
skills: {
|
|
114
|
+
workflow: specWorkflow ? 'spec' : 'minimal',
|
|
115
|
+
sources: [
|
|
116
|
+
{
|
|
117
|
+
package: 'mattpocock/skills',
|
|
118
|
+
skills: mattSkills
|
|
119
|
+
}
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
codebase: {
|
|
123
|
+
graft: true
|
|
124
|
+
},
|
|
125
|
+
browser: {
|
|
126
|
+
visualAgent: false,
|
|
127
|
+
playwright: false
|
|
128
|
+
},
|
|
129
|
+
docs: {
|
|
130
|
+
adr: true
|
|
131
|
+
},
|
|
132
|
+
github: {
|
|
133
|
+
ci: false
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export type PresetType = 'web' | 'cli' | 'generic';
|
|
2
|
+
export type WebFramework = 'none' | 'vite' | 'next' | 'existing';
|
|
3
|
+
export type PackageManager = 'pnpm' | 'npm' | 'yarn' | 'bun';
|
|
4
|
+
|
|
5
|
+
export interface SkillSourceConfig {
|
|
6
|
+
package: string;
|
|
7
|
+
skills: string[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface AginitConfig {
|
|
11
|
+
schemaVersion: string;
|
|
12
|
+
name: string;
|
|
13
|
+
preset: PresetType;
|
|
14
|
+
framework?: WebFramework;
|
|
15
|
+
packageManager?: PackageManager;
|
|
16
|
+
agents: {
|
|
17
|
+
primary: string;
|
|
18
|
+
secondary: string;
|
|
19
|
+
};
|
|
20
|
+
skills: {
|
|
21
|
+
workflow?: 'minimal' | 'spec';
|
|
22
|
+
sources: SkillSourceConfig[];
|
|
23
|
+
};
|
|
24
|
+
codebase: {
|
|
25
|
+
graft: boolean;
|
|
26
|
+
};
|
|
27
|
+
browser: {
|
|
28
|
+
visualAgent: boolean;
|
|
29
|
+
playwright: boolean;
|
|
30
|
+
};
|
|
31
|
+
docs: {
|
|
32
|
+
adr: boolean;
|
|
33
|
+
};
|
|
34
|
+
github: {
|
|
35
|
+
ci: boolean;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Backward-compatible alias
|
|
40
|
+
export type AIProjectConfig = AginitConfig;
|
|
41
|
+
|
|
42
|
+
export const AGINIT_CONFIG_FILENAME = 'aginit.config.json';
|
|
43
|
+
export const LEGACY_CONFIG_FILENAME = 'ai.config.json';
|
|
44
|
+
export const AI_CONFIG_FILENAME = AGINIT_CONFIG_FILENAME;
|