@antomanc/aginit 0.3.0 → 0.4.1
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/CHANGELOG.md +18 -0
- package/README.md +5 -3
- package/dist/adapters/agents-md.d.ts +12 -0
- package/dist/adapters/agents-md.d.ts.map +1 -1
- package/dist/adapters/agents-md.js +25 -1
- package/dist/adapters/agents-md.js.map +1 -1
- package/dist/adapters/graft.d.ts +1 -0
- package/dist/adapters/graft.d.ts.map +1 -1
- package/dist/adapters/graft.js +5 -3
- package/dist/adapters/graft.js.map +1 -1
- package/dist/adapters/skills.d.ts +20 -1
- package/dist/adapters/skills.d.ts.map +1 -1
- package/dist/adapters/skills.js +38 -1
- package/dist/adapters/skills.js.map +1 -1
- package/dist/adapters/tool-ignores.d.ts +9 -0
- package/dist/adapters/tool-ignores.d.ts.map +1 -0
- package/dist/adapters/tool-ignores.js +121 -0
- package/dist/adapters/tool-ignores.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +59 -2
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +13 -3
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/new.d.ts.map +1 -1
- package/dist/cli/new.js +17 -4
- package/dist/cli/new.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +6 -3
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/schema.d.ts +5 -0
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +2 -0
- package/dist/config/validation.js.map +1 -1
- package/dist/presets/web.d.ts.map +1 -1
- package/dist/presets/web.js +34 -3
- package/dist/presets/web.js.map +1 -1
- package/package.json +1 -1
- package/src/adapters/agents-md.ts +30 -1
- package/src/adapters/graft.ts +6 -4
- package/src/adapters/skills.ts +46 -2
- package/src/adapters/tool-ignores.ts +158 -0
- package/src/cli/doctor.ts +72 -2
- package/src/cli/main.ts +12 -3
- package/src/cli/new.ts +14 -4
- package/src/config/defaults.ts +6 -3
- package/src/config/schema.ts +5 -0
- package/src/config/validation.ts +2 -0
- package/src/presets/web.ts +35 -3
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileExists, readTextFile, safeWriteFile } from '../utils/fs.js';
|
|
3
|
+
import { readPackageManifest } from '../config/validation.js';
|
|
4
|
+
|
|
5
|
+
// Vendored skill sources ship third-party JS and Markdown that the project's own
|
|
6
|
+
// linters and formatters must not check. No ignore convention is shared across tools,
|
|
7
|
+
// so each tool below says where its ignores live and how to add to them.
|
|
8
|
+
const VENDORED_DIRS = ['.agents', '.claude'];
|
|
9
|
+
|
|
10
|
+
interface Project {
|
|
11
|
+
manifest: Record<string, any> | null;
|
|
12
|
+
has(dependency: string): boolean;
|
|
13
|
+
exists(file: string): boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ToolIgnore {
|
|
17
|
+
/** The file holding this tool's ignores, when the project uses the tool. */
|
|
18
|
+
file(project: Project): string | undefined;
|
|
19
|
+
/** Vendored directories the file does not ignore yet. */
|
|
20
|
+
missing(text: string): string[];
|
|
21
|
+
/** The file with those directories ignored, or null when it cannot be edited safely. */
|
|
22
|
+
patch(text: string, dirs: string[]): string | null;
|
|
23
|
+
advice(file: string, dirs: string[]): string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const configs = (base: string, extensions: string[]) => extensions.map((ext) => `${base}${ext}`);
|
|
27
|
+
|
|
28
|
+
/** Gitignore-syntax files: append the directories. */
|
|
29
|
+
function ignoreFile(name: string, used: (project: Project) => boolean): ToolIgnore {
|
|
30
|
+
return {
|
|
31
|
+
file: (project) => (used(project) ? name : undefined),
|
|
32
|
+
missing: (text) => {
|
|
33
|
+
const lines = text.split(/\r?\n/).map((line) => line.trim().replace(/^\/|\/(\*\*)?$/g, ''));
|
|
34
|
+
return VENDORED_DIRS.filter((dir) => !lines.includes(dir));
|
|
35
|
+
},
|
|
36
|
+
patch: (text, dirs) => {
|
|
37
|
+
const block = `# Vendored agent skills (aginit)\n${dirs.map((dir) => `${dir}/`).join('\n')}\n`;
|
|
38
|
+
return text.trim() ? `${text.trimEnd()}\n\n${block}` : block;
|
|
39
|
+
},
|
|
40
|
+
advice: (file, dirs) => `add ${dirs.map((dir) => `${dir}/`).join(' and ')} to ${file}`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const prettier = ignoreFile(
|
|
45
|
+
'.prettierignore',
|
|
46
|
+
(project) =>
|
|
47
|
+
project.has('prettier') ||
|
|
48
|
+
Boolean(project.manifest?.prettier) ||
|
|
49
|
+
[
|
|
50
|
+
...configs('.prettierrc', ['', '.json', '.json5', '.yaml', '.yml', '.toml']),
|
|
51
|
+
...configs('.prettierrc', ['.js', '.cjs', '.mjs', '.ts', '.cts', '.mts']),
|
|
52
|
+
...configs('prettier.config', ['.js', '.cjs', '.mjs', '.ts', '.cts', '.mts'])
|
|
53
|
+
].some(project.exists)
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Legacy ESLint and oxlint read .eslintignore; flat-config ESLint does not.
|
|
57
|
+
const eslintIgnore = ignoreFile(
|
|
58
|
+
'.eslintignore',
|
|
59
|
+
(project) =>
|
|
60
|
+
project.has('oxlint') ||
|
|
61
|
+
Boolean(project.manifest?.eslintConfig) ||
|
|
62
|
+
configs('.eslintrc', ['', '.js', '.cjs', '.json', '.yaml', '.yml']).some(project.exists)
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
/** Flat ESLint config: extend a global ignores array if there is an unambiguous one,
|
|
66
|
+
* `globalIgnores([...])` or an object holding only `ignores`. */
|
|
67
|
+
const eslintFlat: ToolIgnore = {
|
|
68
|
+
file: (project) =>
|
|
69
|
+
configs('eslint.config', ['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts']).find(project.exists),
|
|
70
|
+
missing: (text) => VENDORED_DIRS.filter((dir) => !text.includes(`${dir}/`)),
|
|
71
|
+
patch: (text, dirs) => {
|
|
72
|
+
const match =
|
|
73
|
+
/globalIgnores\(\s*\[/.exec(text) ??
|
|
74
|
+
/\{\s*ignores\s*:\s*\[(?=[^[\]]*\]\s*,?\s*\})/.exec(text);
|
|
75
|
+
if (!match) return null;
|
|
76
|
+
const at = match.index + match[0].length;
|
|
77
|
+
const rest = text.slice(at);
|
|
78
|
+
const quote =
|
|
79
|
+
/^\s*(['"`])/.exec(rest)?.[1] ??
|
|
80
|
+
((text.match(/"/g)?.length ?? 0) > (text.match(/'/g)?.length ?? 0) ? '"' : "'");
|
|
81
|
+
const entries = dirs.map((dir) => `${quote}${dir}/**${quote}`);
|
|
82
|
+
const indent = /^(\s*\n[ \t]*)/.exec(rest)?.[1];
|
|
83
|
+
const insertion = indent
|
|
84
|
+
? entries.map((entry) => `${indent}${entry},`).join('')
|
|
85
|
+
: entries.join(', ') + (/^\s*\]/.test(rest) ? '' : ', ');
|
|
86
|
+
return text.slice(0, at) + insertion + rest;
|
|
87
|
+
},
|
|
88
|
+
advice: (file, dirs) =>
|
|
89
|
+
`add { ignores: [${dirs.map((dir) => `'${dir}/**'`).join(', ')}] } to the exported array in ${file}`
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/** Biome 2.2+: force-ignore the folders through files.includes. */
|
|
93
|
+
const biome: ToolIgnore = {
|
|
94
|
+
file: (project) => ['biome.json', 'biome.jsonc'].find(project.exists),
|
|
95
|
+
missing: (text) => VENDORED_DIRS.filter((dir) => !text.includes(`**/${dir}`)),
|
|
96
|
+
patch: (text, dirs) => {
|
|
97
|
+
let config: Record<string, any>;
|
|
98
|
+
try {
|
|
99
|
+
config = JSON.parse(text); // a biome.jsonc with comments gets advice instead
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
const includes = config.files?.includes ?? ['**'];
|
|
104
|
+
if (!Array.isArray(includes)) return null;
|
|
105
|
+
config.files = {
|
|
106
|
+
...config.files,
|
|
107
|
+
includes: [...includes, ...dirs.map((dir) => `!!**/${dir}`)]
|
|
108
|
+
};
|
|
109
|
+
const json = JSON.stringify(config, null, /\n([ \t]+)"/.exec(text)?.[1] ?? 2) + '\n';
|
|
110
|
+
// Like Biome's formatter, keep the array on one line when it fits in 80 columns.
|
|
111
|
+
return json.replace(/^([ \t]*"includes": )\[[^\]]*\]/m, (block, key: string) => {
|
|
112
|
+
const inline = `${key}[${config.files.includes.map((p: string) => JSON.stringify(p)).join(', ')}]`;
|
|
113
|
+
return inline.replace(/\t/g, ' ').length <= 80 ? inline : block;
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
advice: (file, dirs) =>
|
|
117
|
+
`add ${dirs.map((dir) => `"!!**/${dir}"`).join(', ')} to files.includes in ${file}`
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const TOOLS = [prettier, eslintIgnore, eslintFlat, biome];
|
|
121
|
+
|
|
122
|
+
function pendingIgnores(targetDir: string) {
|
|
123
|
+
const manifest = readPackageManifest(targetDir);
|
|
124
|
+
const project: Project = {
|
|
125
|
+
manifest,
|
|
126
|
+
has: (name) => Boolean(manifest?.dependencies?.[name] || manifest?.devDependencies?.[name]),
|
|
127
|
+
exists: (file) => fileExists(path.join(targetDir, file))
|
|
128
|
+
};
|
|
129
|
+
return TOOLS.flatMap((tool) => {
|
|
130
|
+
const file = tool.file(project);
|
|
131
|
+
if (!file) return [];
|
|
132
|
+
const text = readTextFile(path.join(targetDir, file)) ?? '';
|
|
133
|
+
const missing = tool.missing(text);
|
|
134
|
+
return missing.length > 0 ? [{ tool, file, text, missing }] : [];
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Keeps the project's linters and formatters off vendored skills. Returns the
|
|
139
|
+
* advice for configs that could not be edited safely. */
|
|
140
|
+
export function ignoreVendoredSkills(
|
|
141
|
+
targetDir: string,
|
|
142
|
+
options: { dryRun?: boolean; silent?: boolean } = {}
|
|
143
|
+
): string[] {
|
|
144
|
+
const { dryRun = false, silent = false } = options;
|
|
145
|
+
const advice: string[] = [];
|
|
146
|
+
for (const { tool, file, text, missing } of pendingIgnores(targetDir)) {
|
|
147
|
+
const patched = tool.patch(text, missing);
|
|
148
|
+
if (patched === null)
|
|
149
|
+
advice.push(`Linters check vendored skills: ${tool.advice(file, missing)}`);
|
|
150
|
+
else safeWriteFile(path.join(targetDir, file), patched, { overwrite: true, dryRun, silent });
|
|
151
|
+
}
|
|
152
|
+
return advice;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Doctor view: every config that still lets a linter or formatter see vendored skills. */
|
|
156
|
+
export function vendoredSkillsExposure(targetDir: string): string[] {
|
|
157
|
+
return pendingIgnores(targetDir).map(({ tool, file, missing }) => tool.advice(file, missing));
|
|
158
|
+
}
|
package/src/cli/doctor.ts
CHANGED
|
@@ -9,7 +9,13 @@ import {
|
|
|
9
9
|
PackageManager
|
|
10
10
|
} from '../config/schema.js';
|
|
11
11
|
import { detectPackageManager } from '../adapters/package-manager.js';
|
|
12
|
-
import
|
|
12
|
+
import fs from 'node:fs';
|
|
13
|
+
import {
|
|
14
|
+
claudeCodeEnabled,
|
|
15
|
+
getInstalledSkills,
|
|
16
|
+
getSkillsMissingForClaude
|
|
17
|
+
} from '../adapters/skills.js';
|
|
18
|
+
import { claudeMarkdownImportsAgents } from '../adapters/agents-md.js';
|
|
13
19
|
import {
|
|
14
20
|
agentBrowserConfigured,
|
|
15
21
|
agentBrowserStubInstalled,
|
|
@@ -17,6 +23,7 @@ import {
|
|
|
17
23
|
runAgentBrowserDiagnostics
|
|
18
24
|
} from '../adapters/agent-browser.js';
|
|
19
25
|
import { readProjectConfig } from '../config/validation.js';
|
|
26
|
+
import { vendoredSkillsExposure } from '../adapters/tool-ignores.js';
|
|
20
27
|
import { logger } from '../utils/logger.js';
|
|
21
28
|
|
|
22
29
|
interface CheckItem {
|
|
@@ -169,7 +176,7 @@ export async function runDoctor(targetDir: string = process.cwd()): Promise<void
|
|
|
169
176
|
category: 'Project Config',
|
|
170
177
|
ok: !!projectConfig,
|
|
171
178
|
detail: projectConfig
|
|
172
|
-
? `Preset: "${projectConfig.preset}" (v${projectConfig.schemaVersion || '1.0.0'}), pm: ${activePm}, agents: ${projectConfig.agents.primary}/${projectConfig.agents.secondary}`
|
|
179
|
+
? `Preset: "${projectConfig.preset}" (v${projectConfig.schemaVersion || '1.0.0'}), pm: ${activePm}, agents: ${projectConfig.agents.primary}/${projectConfig.agents.secondary}${claudeCodeEnabled(projectConfig) ? '/claude-code' : ''}`
|
|
173
180
|
: configError
|
|
174
181
|
? `Invalid configuration: ${configError}`
|
|
175
182
|
: 'No aginit.config.json found in current directory',
|
|
@@ -220,6 +227,69 @@ export async function runDoctor(targetDir: string = process.cwd()): Promise<void
|
|
|
220
227
|
: 'Run `aginit init` or `npx skills add ...` to install skills'
|
|
221
228
|
});
|
|
222
229
|
|
|
230
|
+
// 9a. Vendored skills must stay out of the project's linters and formatters
|
|
231
|
+
if (installedSkills.length > 0) {
|
|
232
|
+
const exposure = vendoredSkillsExposure(targetDir);
|
|
233
|
+
checks.push({
|
|
234
|
+
name: 'Lint/format ignores for vendored skills',
|
|
235
|
+
category: 'Project Config',
|
|
236
|
+
ok: exposure.length === 0,
|
|
237
|
+
detail:
|
|
238
|
+
exposure.length === 0
|
|
239
|
+
? 'Detected linters and formatters skip .agents/ and .claude/'
|
|
240
|
+
: 'Project linters or formatters will check vendored skill files',
|
|
241
|
+
suggestion:
|
|
242
|
+
exposure.length === 0 ? undefined : `Run \`aginit update\`, or ${exposure.join('; ')}`
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 9b. Claude Code: skills linked into .claude/skills/ and AGENTS.md reachable
|
|
247
|
+
if (projectConfig !== null && claudeCodeEnabled(projectConfig)) {
|
|
248
|
+
if (!skillsDisabled && installedSkills.length > 0) {
|
|
249
|
+
const missing = getSkillsMissingForClaude(targetDir);
|
|
250
|
+
checks.push({
|
|
251
|
+
name: 'Claude Code Skills (.claude/skills/)',
|
|
252
|
+
category: 'Project Config',
|
|
253
|
+
ok: missing.length === 0,
|
|
254
|
+
detail:
|
|
255
|
+
missing.length === 0
|
|
256
|
+
? `All ${installedSkills.length} skills visible to Claude Code`
|
|
257
|
+
: `${missing.length} skills not linked for Claude Code: ${missing.join(', ')}`,
|
|
258
|
+
suggestion:
|
|
259
|
+
missing.length === 0
|
|
260
|
+
? undefined
|
|
261
|
+
: 'Run `aginit update` or `aginit init` to link skills into .claude/skills/ (or set agents.claudeCode: false)'
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const claudePath = path.join(targetDir, 'CLAUDE.md');
|
|
266
|
+
const claudeExists = fileExists(claudePath);
|
|
267
|
+
// A CLAUDE.md symlinked to AGENTS.md is as good as the import.
|
|
268
|
+
const claudeIsAgentsLink =
|
|
269
|
+
claudeExists &&
|
|
270
|
+
agentsExists &&
|
|
271
|
+
fs.realpathSync(claudePath) === fs.realpathSync(agentsPath);
|
|
272
|
+
const claudeOk =
|
|
273
|
+
!claudeExists ||
|
|
274
|
+
claudeIsAgentsLink ||
|
|
275
|
+
claudeMarkdownImportsAgents(readTextFile(claudePath) || '');
|
|
276
|
+
checks.push({
|
|
277
|
+
name: 'CLAUDE.md → AGENTS.md',
|
|
278
|
+
category: 'Project Config',
|
|
279
|
+
ok: claudeExists && claudeOk,
|
|
280
|
+
detail: !claudeExists
|
|
281
|
+
? 'No CLAUDE.md; only Claude Code v2.1.277+ reads AGENTS.md on its own'
|
|
282
|
+
: claudeOk
|
|
283
|
+
? 'CLAUDE.md imports AGENTS.md'
|
|
284
|
+
: 'CLAUDE.md exists without `@AGENTS.md`, so Claude Code ignores AGENTS.md',
|
|
285
|
+
suggestion: !claudeExists
|
|
286
|
+
? 'Run `aginit update` or `aginit init` to add a CLAUDE.md that imports AGENTS.md'
|
|
287
|
+
: claudeOk
|
|
288
|
+
? undefined
|
|
289
|
+
: 'Add a line `@AGENTS.md` to CLAUDE.md'
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
223
293
|
// 10. agent-browser runtime (only when the project ships the skill)
|
|
224
294
|
const agentBrowserWanted =
|
|
225
295
|
projectConfig !== null &&
|
package/src/cli/main.ts
CHANGED
|
@@ -3,7 +3,9 @@ import { readFileSync } from 'node:fs';
|
|
|
3
3
|
import { createProject } from './new.js';
|
|
4
4
|
import { initCurrentDirectory } from './init.js';
|
|
5
5
|
import { runDoctor } from './doctor.js';
|
|
6
|
-
import { updateSkills } from '../adapters/skills.js';
|
|
6
|
+
import { claudeCodeEnabled, ensureClaudeSkillLinks, updateSkills } from '../adapters/skills.js';
|
|
7
|
+
import { ignoreVendoredSkills } from '../adapters/tool-ignores.js';
|
|
8
|
+
import { setupClaudeMarkdown } from '../adapters/agents-md.js';
|
|
7
9
|
import { buildGraft } from '../adapters/graft.js';
|
|
8
10
|
import { readProjectConfig } from '../config/validation.js';
|
|
9
11
|
import { logger } from '../utils/logger.js';
|
|
@@ -161,9 +163,16 @@ export function createCliProgram(): Command {
|
|
|
161
163
|
const targetDir = process.cwd();
|
|
162
164
|
const config = readProjectConfig(targetDir);
|
|
163
165
|
const skillsOk =
|
|
164
|
-
config?.skills.sources.length === 0
|
|
166
|
+
(config?.skills.sources.length === 0
|
|
165
167
|
? true
|
|
166
|
-
: await updateSkills(targetDir, { dryRun: options.dryRun })
|
|
168
|
+
: await updateSkills(targetDir, { dryRun: options.dryRun })) &&
|
|
169
|
+
(config ? await ensureClaudeSkillLinks(targetDir, config, { dryRun: options.dryRun }) : true);
|
|
170
|
+
if (config && claudeCodeEnabled(config))
|
|
171
|
+
setupClaudeMarkdown(targetDir, { dryRun: options.dryRun });
|
|
172
|
+
if (config?.skills.sources.length !== 0) {
|
|
173
|
+
const advice = ignoreVendoredSkills(targetDir, { dryRun: options.dryRun, silent: true });
|
|
174
|
+
for (const line of advice) logger.warn(line);
|
|
175
|
+
}
|
|
167
176
|
const graftOk =
|
|
168
177
|
config?.codebase.graft === false
|
|
169
178
|
? true
|
package/src/cli/new.ts
CHANGED
|
@@ -9,9 +9,10 @@ import {
|
|
|
9
9
|
} from '../config/schema.js';
|
|
10
10
|
import { getPreset } from '../presets/registry.js';
|
|
11
11
|
import { setupGit } from '../adapters/git.js';
|
|
12
|
-
import { setupAgentsMarkdown } from '../adapters/agents-md.js';
|
|
13
|
-
import { installAllSkills } from '../adapters/skills.js';
|
|
12
|
+
import { setupAgentsMarkdown, setupClaudeMarkdown } from '../adapters/agents-md.js';
|
|
13
|
+
import { claudeCodeEnabled, installAllSkills, skillAgents } from '../adapters/skills.js';
|
|
14
14
|
import { initGraft } from '../adapters/graft.js';
|
|
15
|
+
import { ignoreVendoredSkills } from '../adapters/tool-ignores.js';
|
|
15
16
|
import {
|
|
16
17
|
agentBrowserConfigured,
|
|
17
18
|
agentBrowserStubInstalled,
|
|
@@ -137,13 +138,14 @@ export async function createProject(
|
|
|
137
138
|
// Step 4: Write or update AGENTS.md intake contract
|
|
138
139
|
if (!silent) logger.step(currentStep++, totalSteps, 'Generating AGENTS.md intake guidance');
|
|
139
140
|
setupAgentsMarkdown(targetDir, config, { dryRun, silent });
|
|
141
|
+
if (claudeCodeEnabled(config)) setupClaudeMarkdown(targetDir, { dryRun, silent });
|
|
140
142
|
if (!silent) logger.success('Configured AGENTS.md');
|
|
141
143
|
|
|
142
144
|
// Step 5: Install skills via skills.sh engine
|
|
143
145
|
if (!silent) logger.step(currentStep++, totalSteps, 'Installing upstream skills via skills.sh');
|
|
144
146
|
if (options.skills !== false && config.skills.sources.length > 0) {
|
|
145
147
|
const { installed, failed } = await installAllSkills(targetDir, config.skills.sources, {
|
|
146
|
-
agents:
|
|
148
|
+
agents: skillAgents(config),
|
|
147
149
|
dryRun,
|
|
148
150
|
silent
|
|
149
151
|
});
|
|
@@ -159,6 +161,10 @@ export async function createProject(
|
|
|
159
161
|
} else if (!silent) {
|
|
160
162
|
logger.dim('Skills installation skipped (--no-skills or empty configuration)');
|
|
161
163
|
}
|
|
164
|
+
if ((options.skills !== false && config.skills.sources.length > 0) || agentBrowserWanted) {
|
|
165
|
+
const advice = ignoreVendoredSkills(targetDir, { dryRun, silent: true });
|
|
166
|
+
if (!silent) for (const line of advice) logger.warn(line);
|
|
167
|
+
}
|
|
162
168
|
|
|
163
169
|
// Provision the agent-browser runtime the skill stub drives
|
|
164
170
|
if (agentBrowserWanted) {
|
|
@@ -171,7 +177,11 @@ export async function createProject(
|
|
|
171
177
|
// Codebase intelligence (Graft)
|
|
172
178
|
if (!silent) logger.step(currentStep++, totalSteps, 'Configuring codebase intelligence');
|
|
173
179
|
if (config.codebase.graft && options.graft !== false) {
|
|
174
|
-
const graftOk = await initGraft(targetDir, {
|
|
180
|
+
const graftOk = await initGraft(targetDir, {
|
|
181
|
+
dryRun,
|
|
182
|
+
silent,
|
|
183
|
+
claude: claudeCodeEnabled(config)
|
|
184
|
+
});
|
|
175
185
|
complete &&= graftOk;
|
|
176
186
|
if (graftOk && !silent) {
|
|
177
187
|
logger.success('Graft context graph initialized');
|
package/src/config/defaults.ts
CHANGED
|
@@ -70,7 +70,8 @@ export function getDefaultConfig(
|
|
|
70
70
|
packageManager,
|
|
71
71
|
agents: {
|
|
72
72
|
primary: 'antigravity',
|
|
73
|
-
secondary: 'codex'
|
|
73
|
+
secondary: 'codex',
|
|
74
|
+
claudeCode: true
|
|
74
75
|
},
|
|
75
76
|
skills: {
|
|
76
77
|
sources: [
|
|
@@ -120,7 +121,8 @@ export function getDefaultConfig(
|
|
|
120
121
|
packageManager,
|
|
121
122
|
agents: {
|
|
122
123
|
primary: 'antigravity',
|
|
123
|
-
secondary: 'codex'
|
|
124
|
+
secondary: 'codex',
|
|
125
|
+
claudeCode: true
|
|
124
126
|
},
|
|
125
127
|
skills: {
|
|
126
128
|
sources: [
|
|
@@ -162,7 +164,8 @@ export function getDefaultConfig(
|
|
|
162
164
|
packageManager,
|
|
163
165
|
agents: {
|
|
164
166
|
primary: 'antigravity',
|
|
165
|
-
secondary: 'codex'
|
|
167
|
+
secondary: 'codex',
|
|
168
|
+
claudeCode: true
|
|
166
169
|
},
|
|
167
170
|
skills: {
|
|
168
171
|
sources: [
|
package/src/config/schema.ts
CHANGED
|
@@ -16,6 +16,11 @@ export interface AginitConfig {
|
|
|
16
16
|
agents: {
|
|
17
17
|
primary: string;
|
|
18
18
|
secondary: string;
|
|
19
|
+
/**
|
|
20
|
+
* Also install skills, Graft wiring and a CLAUDE.md import for Claude Code.
|
|
21
|
+
* Missing in configs written before 0.4.0; treated as enabled.
|
|
22
|
+
*/
|
|
23
|
+
claudeCode?: boolean;
|
|
19
24
|
};
|
|
20
25
|
skills: {
|
|
21
26
|
/**
|
package/src/config/validation.ts
CHANGED
|
@@ -45,6 +45,8 @@ export function validateConfig(value: unknown): AginitConfig {
|
|
|
45
45
|
if (!isRecord(value.agents)) fail('agents');
|
|
46
46
|
for (const key of ['primary', 'secondary'])
|
|
47
47
|
validateUpstreamArgument(value.agents[key], `agent ${key}`);
|
|
48
|
+
if (value.agents.claudeCode !== undefined && typeof value.agents.claudeCode !== 'boolean')
|
|
49
|
+
fail('agents.claudeCode');
|
|
48
50
|
if (!isRecord(value.skills) || !Array.isArray(value.skills.sources)) fail('skills.sources');
|
|
49
51
|
if (value.skills.workflow !== undefined && !['minimal', 'spec'].includes(value.skills.workflow))
|
|
50
52
|
fail('skills.workflow');
|
package/src/presets/web.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import { fileExists, safeWriteFile, writeJsonFile } from '../utils/fs.js';
|
|
2
|
+
import { fileExists, readTextFile, safeWriteFile, writeJsonFile } from '../utils/fs.js';
|
|
3
3
|
import { readPackageManifest } from '../config/validation.js';
|
|
4
4
|
import { runExecutable } from '../utils/shell.js';
|
|
5
5
|
import { PresetContext, PresetHandler } from './types.js';
|
|
@@ -8,6 +8,34 @@ import { setupAdr } from './adr.js';
|
|
|
8
8
|
import { scaffoldCommand } from '../adapters/package-manager.js';
|
|
9
9
|
import { addTestingDependencies, setupTesting } from './testing.js';
|
|
10
10
|
|
|
11
|
+
/** Plain tsc cannot resolve framework virtual modules or single-file components,
|
|
12
|
+
* so those frameworks get their own checker (and the package that provides it). */
|
|
13
|
+
function typecheckFor(
|
|
14
|
+
pkg: Record<string, any>,
|
|
15
|
+
targetDir: string,
|
|
16
|
+
freshNext: boolean
|
|
17
|
+
): { script: string; checker?: [string, string] } {
|
|
18
|
+
const has = (name: string) => Boolean(pkg.dependencies?.[name] || pkg.devDependencies?.[name]);
|
|
19
|
+
if (freshNext) return { script: 'next typegen && tsc --noEmit' };
|
|
20
|
+
if (has('astro')) return { script: 'astro check', checker: ['@astrojs/check', '^0.9.10'] };
|
|
21
|
+
if (has('nuxt')) return { script: 'nuxi typecheck', checker: ['vue-tsc', '^3.3.13'] };
|
|
22
|
+
if (has('@sveltejs/kit'))
|
|
23
|
+
return {
|
|
24
|
+
script: 'svelte-kit sync && svelte-check --tsconfig ./tsconfig.json',
|
|
25
|
+
checker: ['svelte-check', '^4.7.6']
|
|
26
|
+
};
|
|
27
|
+
if (has('svelte')) return { script: 'svelte-check', checker: ['svelte-check', '^4.7.6'] };
|
|
28
|
+
// A solution-style tsconfig (Vite templates) has no files of its own: only build mode checks
|
|
29
|
+
// the referenced projects.
|
|
30
|
+
const build = /"references"\s*:/.test(readTextFile(path.join(targetDir, 'tsconfig.json')) || '');
|
|
31
|
+
if (has('vue'))
|
|
32
|
+
return {
|
|
33
|
+
script: build ? 'vue-tsc -b --noEmit' : 'vue-tsc --noEmit',
|
|
34
|
+
checker: ['vue-tsc', '^3.3.13']
|
|
35
|
+
};
|
|
36
|
+
return { script: build ? 'tsc -b --noEmit' : 'tsc --noEmit' };
|
|
37
|
+
}
|
|
38
|
+
|
|
11
39
|
export const webPreset: PresetHandler = {
|
|
12
40
|
name: 'web',
|
|
13
41
|
description:
|
|
@@ -55,8 +83,12 @@ export const webPreset: PresetHandler = {
|
|
|
55
83
|
);
|
|
56
84
|
addTestingDependencies(pkg, config.browser.playwright);
|
|
57
85
|
if (fileExists(path.join(targetDir, 'tsconfig.json'))) {
|
|
58
|
-
pkg.scripts.typecheck
|
|
59
|
-
|
|
86
|
+
if (pkg.scripts.typecheck === undefined) {
|
|
87
|
+
const { script, checker } = typecheckFor(pkg, targetDir, fresh && framework === 'next');
|
|
88
|
+
pkg.scripts.typecheck = script;
|
|
89
|
+
if (checker && !pkg.dependencies?.[checker[0]] && !pkg.devDependencies[checker[0]])
|
|
90
|
+
pkg.devDependencies[checker[0]] = checker[1];
|
|
91
|
+
}
|
|
60
92
|
if (!pkg.dependencies?.typescript && !pkg.devDependencies.typescript)
|
|
61
93
|
pkg.devDependencies.typescript = '^5.6.3';
|
|
62
94
|
}
|