@antomanc/aginit 0.5.1 → 0.6.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/CHANGELOG.md +14 -0
- package/README.md +13 -10
- package/THIRD_PARTY_NOTICES.md +3 -3
- package/dist/adapters/agent-docs.d.ts.map +1 -1
- package/dist/adapters/agent-docs.js +4 -2
- package/dist/adapters/agent-docs.js.map +1 -1
- package/dist/adapters/agents-md.d.ts +4 -1
- package/dist/adapters/agents-md.d.ts.map +1 -1
- package/dist/adapters/agents-md.js +20 -16
- package/dist/adapters/agents-md.js.map +1 -1
- package/dist/adapters/git.d.ts.map +1 -1
- package/dist/adapters/git.js +0 -6
- package/dist/adapters/git.js.map +1 -1
- package/dist/adapters/tool-ignores.d.ts.map +1 -1
- package/dist/adapters/tool-ignores.js +25 -1
- package/dist/adapters/tool-ignores.js.map +1 -1
- package/dist/adapters/trail.d.ts +15 -0
- package/dist/adapters/{graft.d.ts.map → trail.d.ts.map} +1 -1
- package/dist/adapters/trail.js +68 -0
- package/dist/adapters/trail.js.map +1 -0
- package/dist/cli/capabilities.js +8 -8
- package/dist/cli/doctor.js +8 -8
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/main.js +13 -13
- package/dist/cli/new.d.ts +1 -1
- package/dist/cli/new.js +5 -5
- package/dist/cli/wizard.d.ts +1 -0
- package/dist/cli/wizard.d.ts.map +1 -1
- package/dist/cli/wizard.js +5 -3
- package/dist/cli/wizard.js.map +1 -1
- package/dist/config/defaults.js +1 -1
- package/dist/config/schema.d.ts +2 -2
- package/dist/config/validation.d.ts +1 -1
- package/dist/config/validation.d.ts.map +1 -1
- package/dist/config/validation.js +15 -3
- package/dist/config/validation.js.map +1 -1
- package/dist/detect/index.d.ts +2 -0
- package/dist/detect/index.d.ts.map +1 -1
- package/dist/detect/index.js +15 -2
- package/dist/detect/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/registry/catalog.d.ts.map +1 -1
- package/dist/registry/catalog.js +14 -10
- package/dist/registry/catalog.js.map +1 -1
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.js +5 -5
- package/dist/registry/setup.js +3 -3
- package/dist/registry/types.d.ts +3 -1
- package/dist/registry/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/adapters/agent-docs.ts +4 -2
- package/src/adapters/agents-md.ts +23 -16
- package/src/adapters/git.ts +0 -6
- package/src/adapters/tool-ignores.ts +27 -1
- package/src/adapters/trail.ts +94 -0
- package/src/cli/capabilities.ts +6 -6
- package/src/cli/doctor.ts +8 -8
- package/src/cli/main.ts +14 -14
- package/src/cli/new.ts +5 -5
- package/src/cli/wizard.ts +7 -3
- package/src/config/defaults.ts +1 -1
- package/src/config/schema.ts +2 -2
- package/src/config/validation.ts +17 -3
- package/src/detect/index.ts +15 -2
- package/src/index.ts +1 -1
- package/src/registry/catalog.ts +15 -10
- package/src/registry/index.ts +4 -4
- package/src/registry/setup.ts +4 -4
- package/src/registry/types.ts +4 -2
- package/dist/adapters/graft.d.ts +0 -15
- package/dist/adapters/graft.js +0 -58
- package/dist/adapters/graft.js.map +0 -1
- package/src/adapters/graft.ts +0 -82
package/src/cli/main.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { initCurrentDirectory } from './init.js';
|
|
|
5
5
|
import { runDoctor } from './doctor.js';
|
|
6
6
|
import { ensureClaudeSkillLinks, updateSkills } from '../adapters/skills.js';
|
|
7
7
|
import { ignoreVendoredSkills } from '../adapters/tool-ignores.js';
|
|
8
|
-
import {
|
|
8
|
+
import { buildTrail } from '../adapters/trail.js';
|
|
9
9
|
import { readProjectConfig } from '../config/validation.js';
|
|
10
10
|
import { logger } from '../utils/logger.js';
|
|
11
11
|
import { PresetType, WebFramework, PackageManager } from '../config/schema.js';
|
|
@@ -50,7 +50,7 @@ function withProfileOptions(command: Command): Command {
|
|
|
50
50
|
.option('--ui-driver <driver>', 'UI driver: auto | agent-browser | agent-device | none')
|
|
51
51
|
.option('--addons <ids>', 'Add-ons: context-mode, worktrunk, chrome-devtools-mcp, browserskill', list)
|
|
52
52
|
.option('--add-skill <owner/repo>', 'Add a skill repository as a custom capability (repeatable)', list)
|
|
53
|
-
.option('--without <id>', 'Disable a capability, e.g.
|
|
53
|
+
.option('--without <id>', 'Disable a capability, e.g. trail or humanizer (repeatable)', list);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
function profileFlags(options: any): ProfileFlags {
|
|
@@ -78,7 +78,7 @@ export function createCliProgram(): Command {
|
|
|
78
78
|
.option('-f, --framework <framework>', 'Web framework: none | vite | next | existing')
|
|
79
79
|
.option('-m, --package-manager <pm>', 'Package manager: pnpm | npm | yarn | bun')
|
|
80
80
|
.option('--no-skills', 'Skip installing AI skills')
|
|
81
|
-
.option('--no-
|
|
81
|
+
.option('--no-trail', 'Skip codebase graph (Trail) setup')
|
|
82
82
|
.option('--no-git', 'Skip git repository initialization')
|
|
83
83
|
.option('--dry-run', 'Simulate setup without writing changes to disk')
|
|
84
84
|
.option('-y, --yes', 'Initialize the current directory without prompts (saved or detected settings)');
|
|
@@ -101,7 +101,7 @@ export function createCliProgram(): Command {
|
|
|
101
101
|
framework: options.framework as WebFramework | undefined,
|
|
102
102
|
packageManager: options.packageManager as PackageManager | undefined,
|
|
103
103
|
skills: options.skills,
|
|
104
|
-
|
|
104
|
+
trail: options.trail,
|
|
105
105
|
git: options.git,
|
|
106
106
|
dryRun: options.dryRun,
|
|
107
107
|
profile: profileFlags(options)
|
|
@@ -117,7 +117,7 @@ export function createCliProgram(): Command {
|
|
|
117
117
|
.option('-f, --framework <framework>', 'Web framework: none | vite | next | existing', 'none')
|
|
118
118
|
.option('-m, --package-manager <pm>', 'Package manager: pnpm | npm | yarn | bun', 'pnpm')
|
|
119
119
|
.option('--no-skills', 'Skip installing AI skills')
|
|
120
|
-
.option('--no-
|
|
120
|
+
.option('--no-trail', 'Skip codebase graph (Trail) setup')
|
|
121
121
|
.option('--no-git', 'Skip git repository initialization')
|
|
122
122
|
.option('--dry-run', 'Simulate creation without writing changes to disk');
|
|
123
123
|
withProfileOptions(newCommand)
|
|
@@ -159,7 +159,7 @@ export function createCliProgram(): Command {
|
|
|
159
159
|
framework: finalFramework as WebFramework,
|
|
160
160
|
packageManager: finalPackageManager as PackageManager,
|
|
161
161
|
skills: options.skills,
|
|
162
|
-
|
|
162
|
+
trail: options.trail,
|
|
163
163
|
git: options.git,
|
|
164
164
|
dryRun: options.dryRun,
|
|
165
165
|
profile: finalProfile
|
|
@@ -175,7 +175,7 @@ export function createCliProgram(): Command {
|
|
|
175
175
|
.option('-f, --framework <framework>', 'Web framework: none | vite | next | existing')
|
|
176
176
|
.option('-m, --package-manager <pm>', 'Package manager: pnpm | npm | yarn | bun')
|
|
177
177
|
.option('--no-skills', 'Skip installing AI skills')
|
|
178
|
-
.option('--no-
|
|
178
|
+
.option('--no-trail', 'Skip codebase graph (Trail) setup')
|
|
179
179
|
.option('--no-git', 'Skip git repository initialization')
|
|
180
180
|
.option('--dry-run', 'Simulate setup without writing changes to disk')
|
|
181
181
|
.option('-y, --yes', 'Skip the prompts and use saved or detected settings');
|
|
@@ -209,7 +209,7 @@ export function createCliProgram(): Command {
|
|
|
209
209
|
framework: finalFramework as WebFramework,
|
|
210
210
|
packageManager: finalPackageManager as PackageManager,
|
|
211
211
|
skills: options.skills,
|
|
212
|
-
|
|
212
|
+
trail: options.trail,
|
|
213
213
|
git: options.git,
|
|
214
214
|
dryRun: options.dryRun,
|
|
215
215
|
profile: finalProfile
|
|
@@ -278,16 +278,16 @@ export function createCliProgram(): Command {
|
|
|
278
278
|
const advice = ignoreVendoredSkills(targetDir, { dryRun: options.dryRun, silent: true });
|
|
279
279
|
for (const line of advice) logger.warn(line);
|
|
280
280
|
}
|
|
281
|
-
const
|
|
282
|
-
config?.codebase.
|
|
281
|
+
const trailOk =
|
|
282
|
+
config?.codebase.trail === false
|
|
283
283
|
? true
|
|
284
|
-
: await
|
|
285
|
-
if (skillsOk &&
|
|
284
|
+
: await buildTrail(targetDir, { dryRun: options.dryRun });
|
|
285
|
+
if (skillsOk && trailOk) logger.success('Update finished.');
|
|
286
286
|
else {
|
|
287
287
|
if (!skillsOk) logger.warn('Skills update failed. Check connectivity and the skills CLI.');
|
|
288
|
-
if (!
|
|
288
|
+
if (!trailOk)
|
|
289
289
|
logger.warn(
|
|
290
|
-
'
|
|
290
|
+
'Trail refresh failed or Trail is unavailable. Check installation and rerun trail build.'
|
|
291
291
|
);
|
|
292
292
|
process.exitCode = 1;
|
|
293
293
|
}
|
package/src/cli/new.ts
CHANGED
|
@@ -50,7 +50,7 @@ export interface NewProjectOptions {
|
|
|
50
50
|
framework?: WebFramework;
|
|
51
51
|
packageManager?: PackageManager;
|
|
52
52
|
skills?: boolean;
|
|
53
|
-
|
|
53
|
+
trail?: boolean;
|
|
54
54
|
git?: boolean;
|
|
55
55
|
dryRun?: boolean;
|
|
56
56
|
targetDir?: string;
|
|
@@ -179,7 +179,7 @@ export async function createProject(
|
|
|
179
179
|
const manifest = readPackageManifest(targetDir); // Validate before creating or modifying anything.
|
|
180
180
|
const config = resolveConfig(projectName, targetDir, options, existingConfig, manifest);
|
|
181
181
|
if (config.preset !== 'web') delete config.framework;
|
|
182
|
-
if (options.
|
|
182
|
+
if (options.trail === false) config.codebase = { ...config.codebase, trail: false };
|
|
183
183
|
if (options.skills === false && !existingConfig) config.skills.sources = [];
|
|
184
184
|
const presetName = config.preset;
|
|
185
185
|
const packageManager = config.packageManager ?? 'pnpm';
|
|
@@ -197,14 +197,14 @@ export async function createProject(
|
|
|
197
197
|
let complete = true;
|
|
198
198
|
|
|
199
199
|
const installSkills = options.skills !== false && config.skills.sources.length > 0;
|
|
200
|
-
// Upstream setups (agent-browser runtime, e2e,
|
|
200
|
+
// Upstream setups (agent-browser runtime, e2e, Trail) in registry order, Trail last
|
|
201
201
|
// so its first index sees every generated file.
|
|
202
202
|
const setups = active
|
|
203
203
|
.flatMap((capability) =>
|
|
204
204
|
capability.install.flatMap((step) => (step.kind === 'setup' ? [step.setup] : []))
|
|
205
205
|
)
|
|
206
|
-
.filter((setup) => !(setup === '
|
|
207
|
-
.sort((a, b) => Number(a === '
|
|
206
|
+
.filter((setup) => !(setup === 'trail' && options.trail === false))
|
|
207
|
+
.sort((a, b) => Number(a === 'trail') - Number(b === 'trail'));
|
|
208
208
|
const totalSteps = 6 + setups.length;
|
|
209
209
|
let currentStep = 1;
|
|
210
210
|
|
package/src/cli/wizard.ts
CHANGED
|
@@ -201,16 +201,20 @@ async function promptProfile(
|
|
|
201
201
|
return { ...flags, platform, methodology, design, testing: testing as ProfileFlags['testing'], uiDriver, addons };
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
async function confirmSetup(answers: WizardInitAnswers): Promise<boolean> {
|
|
204
|
+
export async function confirmSetup(answers: WizardInitAnswers): Promise<boolean> {
|
|
205
205
|
const profile = answers.profile;
|
|
206
206
|
const name = (id: string | undefined) => CATALOG.find((c) => c.id === id)?.name ?? id;
|
|
207
|
+
const graph = CATALOG.find(
|
|
208
|
+
(c) => c.category === 'codebase-graph' && c.default && !profile.without?.includes(c.id)
|
|
209
|
+
);
|
|
207
210
|
p.note([
|
|
208
211
|
`Platform: ${profile.platform}`,
|
|
209
212
|
`Methodology: ${name(profile.methodology)}`,
|
|
210
213
|
`Design: ${name(profile.design)}`,
|
|
211
214
|
`Testing: ${name(profile.testing)}`,
|
|
212
215
|
`UI driver: ${name(profile.uiDriver)}`,
|
|
213
|
-
`Add-ons: ${profile.addons?.map(name).join(', ') || 'None'}
|
|
216
|
+
`Add-ons: ${profile.addons?.map(name).join(', ') || 'None'}`,
|
|
217
|
+
`Codebase graph: ${graph ? `${graph.name}. ${graph.description}` : 'None'}`
|
|
214
218
|
].join('\n'), 'Setup');
|
|
215
219
|
return assertNotCancelled(await p.confirm({ message: 'Apply this setup?', initialValue: true }));
|
|
216
220
|
}
|
|
@@ -231,7 +235,7 @@ export async function runInteractiveWizard(): Promise<void> {
|
|
|
231
235
|
{
|
|
232
236
|
value: 'init',
|
|
233
237
|
label: 'Current directory',
|
|
234
|
-
hint: 'Add AGENTS.md, skills &
|
|
238
|
+
hint: 'Add AGENTS.md, skills & Trail safely'
|
|
235
239
|
}
|
|
236
240
|
]
|
|
237
241
|
})
|
package/src/config/defaults.ts
CHANGED
|
@@ -63,7 +63,7 @@ export function getDefaultConfig(
|
|
|
63
63
|
claudeCode: true
|
|
64
64
|
},
|
|
65
65
|
skills: { sources: [] },
|
|
66
|
-
codebase: {
|
|
66
|
+
codebase: { trail: true },
|
|
67
67
|
browser: { visualAgent: false, playwright: false },
|
|
68
68
|
docs: { adr: true },
|
|
69
69
|
github: { ci: preset !== 'generic' }
|
package/src/config/schema.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface AginitConfig {
|
|
|
19
19
|
primary: string;
|
|
20
20
|
secondary: string;
|
|
21
21
|
/**
|
|
22
|
-
* Also install skills,
|
|
22
|
+
* Also install skills, Trail wiring and a CLAUDE.md import for Claude Code.
|
|
23
23
|
* Missing in configs written before 0.4.0; treated as enabled.
|
|
24
24
|
*/
|
|
25
25
|
claudeCode?: boolean;
|
|
@@ -34,7 +34,7 @@ export interface AginitConfig {
|
|
|
34
34
|
sources: SkillSourceConfig[];
|
|
35
35
|
};
|
|
36
36
|
codebase: {
|
|
37
|
-
|
|
37
|
+
trail: boolean;
|
|
38
38
|
};
|
|
39
39
|
browser: {
|
|
40
40
|
visualAgent: boolean;
|
package/src/config/validation.ts
CHANGED
|
@@ -50,11 +50,25 @@ export function validateUpstreamArgument(value: unknown, label: string): asserts
|
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
/** Configs saved before Graft became Trail name it `graft`; read them as `trail`. */
|
|
54
|
+
function migrateGraftConfig(value: Record<string, any>): Record<string, any> {
|
|
55
|
+
const rename = (ids: unknown) =>
|
|
56
|
+
Array.isArray(ids) ? ids.map((id) => (id === 'graft' ? 'trail' : id)) : ids;
|
|
57
|
+
const { graft, ...codebase } = isRecord(value.codebase) ? value.codebase : {};
|
|
58
|
+
return {
|
|
59
|
+
...value,
|
|
60
|
+
...(isRecord(value.codebase) ? { codebase: { trail: graft, ...codebase } } : {}),
|
|
61
|
+
...(value.capabilities ? { capabilities: rename(value.capabilities) } : {}),
|
|
62
|
+
...(value.disabledCapabilities ? { disabledCapabilities: rename(value.disabledCapabilities) } : {})
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function validateConfig(input: unknown): AginitConfig {
|
|
54
67
|
const fail = (field: string): never => {
|
|
55
68
|
throw new Error(`Invalid project configuration: ${field}`);
|
|
56
69
|
};
|
|
57
|
-
if (!isRecord(
|
|
70
|
+
if (!isRecord(input)) return fail('expected a JSON object');
|
|
71
|
+
const value = migrateGraftConfig(input);
|
|
58
72
|
if (value.schemaVersion !== undefined && value.schemaVersion !== '1.0.0')
|
|
59
73
|
fail('unsupported schemaVersion');
|
|
60
74
|
if (typeof value.name !== 'string' || !value.name) fail('name');
|
|
@@ -112,7 +126,7 @@ export function validateConfig(value: unknown): AginitConfig {
|
|
|
112
126
|
}
|
|
113
127
|
}
|
|
114
128
|
for (const [section, keys] of Object.entries({
|
|
115
|
-
codebase: ['
|
|
129
|
+
codebase: ['trail'],
|
|
116
130
|
browser: ['visualAgent', 'playwright'],
|
|
117
131
|
docs: ['adr'],
|
|
118
132
|
github: ['ci']
|
package/src/detect/index.ts
CHANGED
|
@@ -74,6 +74,14 @@ export function detectProject(dir: string): DetectedProject {
|
|
|
74
74
|
return { platform: null, stack: 'unknown' };
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/** The file configuring ruff, in ruff's own lookup order, when the project uses it. */
|
|
78
|
+
export function ruffConfigFile(dir: string): string | undefined {
|
|
79
|
+
const local = ['.ruff.toml', 'ruff.toml'].find((file) => fs.existsSync(path.join(dir, file)));
|
|
80
|
+
if (local) return local;
|
|
81
|
+
const pyproject = readTextFile(path.join(dir, 'pyproject.toml')) ?? '';
|
|
82
|
+
return /^\[tool\.ruff[.\]]/m.test(pyproject) ? 'pyproject.toml' : undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
77
85
|
/**
|
|
78
86
|
* The project's own commands: package scripts for Node projects, the host
|
|
79
87
|
* toolchain otherwise. Commands that would be guesses are left out.
|
|
@@ -126,8 +134,13 @@ export function projectCommands(
|
|
|
126
134
|
return { build: 'cargo build', typecheck: 'cargo check', lint: 'cargo clippy', test: 'cargo test' };
|
|
127
135
|
case 'go':
|
|
128
136
|
return { build: 'go build ./...', lint: 'go vet ./...', test: 'go test ./...' };
|
|
129
|
-
case 'python':
|
|
130
|
-
|
|
137
|
+
case 'python': {
|
|
138
|
+
const run = exists('uv.lock') ? 'uv run ' : '';
|
|
139
|
+
return {
|
|
140
|
+
...(ruffConfigFile(dir) ? { lint: `${run}ruff check . && ${run}ruff format --check .` } : {}),
|
|
141
|
+
test: `${run}pytest`
|
|
142
|
+
};
|
|
143
|
+
}
|
|
131
144
|
case 'unknown':
|
|
132
145
|
return {};
|
|
133
146
|
}
|
package/src/index.ts
CHANGED
|
@@ -6,7 +6,7 @@ export * from './presets/types.js';
|
|
|
6
6
|
export * from './adapters/package-manager.js';
|
|
7
7
|
export * from './adapters/git.js';
|
|
8
8
|
export * from './adapters/skills.js';
|
|
9
|
-
export * from './adapters/
|
|
9
|
+
export * from './adapters/trail.js';
|
|
10
10
|
export * from './adapters/agents-md.js';
|
|
11
11
|
export * from './adapters/browser.js';
|
|
12
12
|
export * from './adapters/agent-browser.js';
|
package/src/registry/catalog.ts
CHANGED
|
@@ -68,29 +68,31 @@ const uiDriverName = (ctx: GuideContext) =>
|
|
|
68
68
|
export const CATALOG: CapabilityDescriptor[] = [
|
|
69
69
|
// ── Codebase graph ────────────────────────────────────────────────────────
|
|
70
70
|
{
|
|
71
|
-
id: '
|
|
72
|
-
name: '
|
|
73
|
-
description:
|
|
71
|
+
id: 'trail',
|
|
72
|
+
name: 'Trail',
|
|
73
|
+
description:
|
|
74
|
+
'Codebase graph and team learnings (trail ask / skeleton / callers). ' +
|
|
75
|
+
'Sends anonymous usage stats: opt out with `trail telemetry disable` or DO_NOT_TRACK=1',
|
|
74
76
|
category: 'codebase-graph',
|
|
75
|
-
author: '
|
|
77
|
+
author: 'Trail (formerly Graft, by NanoNets)',
|
|
76
78
|
repoUrl: 'https://github.com/trailhq/Trail-Code',
|
|
77
79
|
license: 'MIT',
|
|
78
80
|
supportedPlatforms: ['all'],
|
|
79
81
|
requiresNodeInProjectRoot: false,
|
|
80
82
|
default: true,
|
|
81
83
|
install: [
|
|
82
|
-
{ kind: 'binary', bin: '
|
|
83
|
-
{ kind: 'setup', setup: '
|
|
84
|
+
{ kind: 'binary', bin: 'trail', install: 'npm install -g @trailhq/trail-code' },
|
|
85
|
+
{ kind: 'setup', setup: 'trail' }
|
|
84
86
|
],
|
|
85
|
-
doctor: [{ kind: 'file', path: '
|
|
87
|
+
doctor: [{ kind: 'file', path: '.trail/cache/INDEX.md', fix: 'trail build' }],
|
|
86
88
|
routing: {
|
|
87
89
|
task: 'Codebase exploration & impact analysis',
|
|
88
|
-
read: '`
|
|
90
|
+
read: '`trail ask "<query>" --source` / `trail skeleton <file>` (see the Trail section)'
|
|
89
91
|
},
|
|
90
92
|
rebuttals: [
|
|
91
93
|
{
|
|
92
94
|
excuse: "I'll grep/read the whole folder to understand the module.",
|
|
93
|
-
rule: 'Forbidden. Query `
|
|
95
|
+
rule: 'Forbidden. Query `trail ask "<query>" --source` or `trail callers <symbol>` first to save context tokens.'
|
|
94
96
|
}
|
|
95
97
|
]
|
|
96
98
|
},
|
|
@@ -442,7 +444,10 @@ export const CATALOG: CapabilityDescriptor[] = [
|
|
|
442
444
|
`## Unit & integration tests: ${ctx.stack} toolchain`,
|
|
443
445
|
`- Run: ${code(ctx.commands.test, 'the project test command')}`,
|
|
444
446
|
ctx.commands.lint ? `- Static checks: \`${ctx.commands.lint}\`` : undefined,
|
|
445
|
-
'- Write the failing test first, then the code.
|
|
447
|
+
'- Write the failing test first, then the code. ' +
|
|
448
|
+
(ctx.testDir
|
|
449
|
+
? `Put tests in \`${ctx.testDir}/\`, mirroring the module they cover.`
|
|
450
|
+
: 'Place tests where the toolchain convention puts them.')
|
|
446
451
|
]
|
|
447
452
|
.filter((line): line is string => line !== undefined)
|
|
448
453
|
.join('\n')
|
package/src/registry/index.ts
CHANGED
|
@@ -264,11 +264,11 @@ function mergeSources(
|
|
|
264
264
|
|
|
265
265
|
/**
|
|
266
266
|
* Capabilities a config written before v2 (no `capabilities` field) uses:
|
|
267
|
-
* the ones whose skills it installs, plus
|
|
267
|
+
* the ones whose skills it installs, plus Trail and its test harness.
|
|
268
268
|
*/
|
|
269
269
|
export function inferCapabilities(config: AginitConfig): string[] {
|
|
270
270
|
const ids: string[] = [];
|
|
271
|
-
if (config.codebase.
|
|
271
|
+
if (config.codebase.trail) ids.push('trail');
|
|
272
272
|
for (const capability of CATALOG) {
|
|
273
273
|
const steps = skillSteps(capability);
|
|
274
274
|
if (
|
|
@@ -297,7 +297,7 @@ export function activeCapabilities(config: AginitConfig): CapabilityDescriptor[]
|
|
|
297
297
|
.filter((c): c is CapabilityDescriptor => c !== undefined)
|
|
298
298
|
.filter((capability) => {
|
|
299
299
|
// The legacy switches still turn their capability off.
|
|
300
|
-
if (capability.id === '
|
|
300
|
+
if (capability.id === 'trail' && config.codebase.trail === false) return false;
|
|
301
301
|
if (capability.id === 'playwright' && !config.browser.playwright) return false;
|
|
302
302
|
// Skills only install from configured sources (`--no-skills` empties them).
|
|
303
303
|
const steps = skillSteps(capability).filter((step) => !step.optional);
|
|
@@ -314,7 +314,7 @@ export function activeIds(config: AginitConfig): Set<string> {
|
|
|
314
314
|
|
|
315
315
|
/** Keep the pre-v2 switches in step with the capability list. */
|
|
316
316
|
function mirrorLegacySwitches(config: AginitConfig, ids: string[]): void {
|
|
317
|
-
config.codebase = { ...config.codebase,
|
|
317
|
+
config.codebase = { ...config.codebase, trail: ids.includes('trail') };
|
|
318
318
|
config.browser = {
|
|
319
319
|
...config.browser,
|
|
320
320
|
visualAgent: ids.includes('agent-browser'),
|
package/src/registry/setup.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AginitConfig } from '../config/schema.js';
|
|
2
|
-
import {
|
|
2
|
+
import { initTrail } from '../adapters/trail.js';
|
|
3
3
|
import { setupAgentBrowser } from '../adapters/agent-browser.js';
|
|
4
4
|
import { setupE2e } from '../adapters/e2e.js';
|
|
5
5
|
import { claudeCodeEnabled } from '../adapters/skills.js';
|
|
@@ -16,8 +16,8 @@ export interface SetupContext {
|
|
|
16
16
|
|
|
17
17
|
/** Upstream setup commands, keyed by the `setup` install step that names them. */
|
|
18
18
|
export const SETUPS: Record<SetupId, (ctx: SetupContext) => Promise<boolean>> = {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
trail: (ctx) =>
|
|
20
|
+
initTrail(ctx.targetDir, {
|
|
21
21
|
dryRun: ctx.dryRun,
|
|
22
22
|
silent: ctx.silent,
|
|
23
23
|
claude: claudeCodeEnabled(ctx.config)
|
|
@@ -34,7 +34,7 @@ export const SETUPS: Record<SetupId, (ctx: SetupContext) => Promise<boolean>> =
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
export const SETUP_LABELS: Record<SetupId, string> = {
|
|
37
|
-
|
|
37
|
+
trail: 'Configuring codebase intelligence (Trail)',
|
|
38
38
|
'agent-browser': 'Provisioning agent-browser runtime',
|
|
39
39
|
e2e: 'Scaffolding e2e tests'
|
|
40
40
|
};
|
package/src/registry/types.ts
CHANGED
|
@@ -14,7 +14,7 @@ export type CapabilityCategory =
|
|
|
14
14
|
| 'methodology' // pocock, superpowers, minimal
|
|
15
15
|
| 'discipline' // ponytail, karpathy-guidelines, humanizer
|
|
16
16
|
| 'design' // impeccable
|
|
17
|
-
| 'codebase-graph' //
|
|
17
|
+
| 'codebase-graph' // trail
|
|
18
18
|
| 'ui-driver' // agent-browser, agent-device, chrome-devtools-mcp, browserskill
|
|
19
19
|
| 'testing-engine' // e2e, playwright, vitest, native
|
|
20
20
|
| 'context-guard' // context-mode, worktrunk
|
|
@@ -39,7 +39,7 @@ export type InstallStep =
|
|
|
39
39
|
* Setups that run an upstream init command. vitest and playwright have none:
|
|
40
40
|
* the presets add them to package.json when they are active.
|
|
41
41
|
*/
|
|
42
|
-
export type SetupId = '
|
|
42
|
+
export type SetupId = 'trail' | 'agent-browser' | 'e2e';
|
|
43
43
|
|
|
44
44
|
export type DoctorCheck =
|
|
45
45
|
| { kind: 'file'; path: string; fix: string }
|
|
@@ -55,6 +55,8 @@ export interface GuideContext {
|
|
|
55
55
|
commands: ProjectCommands;
|
|
56
56
|
/** Directory that holds the e2e workspace ('' for the project root). */
|
|
57
57
|
e2eDir: string;
|
|
58
|
+
/** The project's top-level test directory (tests/ or test/), when it has one. */
|
|
59
|
+
testDir?: string;
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
export interface ProjectCommands {
|
package/dist/adapters/graft.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export declare function isGraftInstalled(): Promise<boolean>;
|
|
2
|
-
export declare function initGraft(targetDir: string, options?: {
|
|
3
|
-
dryRun?: boolean;
|
|
4
|
-
silent?: boolean;
|
|
5
|
-
claude?: boolean;
|
|
6
|
-
}): Promise<boolean>;
|
|
7
|
-
export declare function buildGraft(targetDir: string, options?: {
|
|
8
|
-
dryRun?: boolean;
|
|
9
|
-
silent?: boolean;
|
|
10
|
-
}): Promise<boolean>;
|
|
11
|
-
export declare function uninstallGraft(targetDir: string, options?: {
|
|
12
|
-
dryRun?: boolean;
|
|
13
|
-
silent?: boolean;
|
|
14
|
-
}): Promise<boolean>;
|
|
15
|
-
//# sourceMappingURL=graft.d.ts.map
|
package/dist/adapters/graft.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { fileExists, readTextFile, safeWriteFile } from '../utils/fs.js';
|
|
3
|
-
import { commandExists, runExecutable } from '../utils/shell.js';
|
|
4
|
-
import { logger } from '../utils/logger.js';
|
|
5
|
-
export async function isGraftInstalled() {
|
|
6
|
-
return await commandExists('graft');
|
|
7
|
-
}
|
|
8
|
-
export async function initGraft(targetDir, options = {}) {
|
|
9
|
-
const { dryRun = false, silent = false, claude = true } = options;
|
|
10
|
-
const installed = dryRun || (await isGraftInstalled());
|
|
11
|
-
if (!installed) {
|
|
12
|
-
if (!silent) {
|
|
13
|
-
logger.warn('Graft CLI is not installed. Install it with `npm install -g @nanonets/graft`, then rerun aginit init.');
|
|
14
|
-
}
|
|
15
|
-
return false;
|
|
16
|
-
}
|
|
17
|
-
if (!silent)
|
|
18
|
-
logger.dim('Initializing Graft context graph...');
|
|
19
|
-
// Wire Antigravity, AGENTS.md readers and (optionally) Claude Code's in-repo
|
|
20
|
-
// .claude/ + .mcp.json, without touching global system files (--no-global)
|
|
21
|
-
const agents = ['antigravity', 'agents', ...(claude ? ['claude'] : [])];
|
|
22
|
-
const res = await runExecutable('graft', ['init', '--no-global', '--agents', ...agents, '-y'], {
|
|
23
|
-
cwd: targetDir,
|
|
24
|
-
dryRun,
|
|
25
|
-
silent: true
|
|
26
|
-
});
|
|
27
|
-
if (!res.ok) {
|
|
28
|
-
logger.warn(`Graft initialization warning: ${res.stderr || res.stdout}`);
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
export async function buildGraft(targetDir, options = {}) {
|
|
34
|
-
const { dryRun = false } = options;
|
|
35
|
-
const installed = dryRun || (await isGraftInstalled());
|
|
36
|
-
if (!installed)
|
|
37
|
-
return false;
|
|
38
|
-
const res = await runExecutable('graft', ['build'], { cwd: targetDir, dryRun, silent: true });
|
|
39
|
-
return res.ok;
|
|
40
|
-
}
|
|
41
|
-
export async function uninstallGraft(targetDir, options = {}) {
|
|
42
|
-
const { dryRun = false } = options;
|
|
43
|
-
const installed = dryRun || (await isGraftInstalled());
|
|
44
|
-
if (installed) {
|
|
45
|
-
await runExecutable('graft', ['uninstall'], { cwd: targetDir, dryRun, silent: true });
|
|
46
|
-
}
|
|
47
|
-
// Also clean up AGENTS.md fenced section if any remain
|
|
48
|
-
const agentsPath = path.join(targetDir, 'AGENTS.md');
|
|
49
|
-
if (fileExists(agentsPath)) {
|
|
50
|
-
const content = readTextFile(agentsPath) || '';
|
|
51
|
-
const cleaned = content
|
|
52
|
-
.replace(/<!-- graft:start -->[\s\S]*?<!-- graft:end -->\n?/g, '')
|
|
53
|
-
.trim();
|
|
54
|
-
safeWriteFile(agentsPath, cleaned + '\n', { overwrite: true, dryRun });
|
|
55
|
-
}
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
//# sourceMappingURL=graft.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"graft.js","sourceRoot":"","sources":["../../src/adapters/graft.ts"],"names":[],"mappings":"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"}
|
package/src/adapters/graft.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { fileExists, readTextFile, safeWriteFile } from '../utils/fs.js';
|
|
3
|
-
import { commandExists, runExecutable } from '../utils/shell.js';
|
|
4
|
-
import { logger } from '../utils/logger.js';
|
|
5
|
-
|
|
6
|
-
export async function isGraftInstalled(): Promise<boolean> {
|
|
7
|
-
return await commandExists('graft');
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export async function initGraft(
|
|
11
|
-
targetDir: string,
|
|
12
|
-
options: { dryRun?: boolean; silent?: boolean; claude?: boolean } = {}
|
|
13
|
-
): Promise<boolean> {
|
|
14
|
-
const { dryRun = false, silent = false, claude = true } = options;
|
|
15
|
-
|
|
16
|
-
const installed = dryRun || (await isGraftInstalled());
|
|
17
|
-
if (!installed) {
|
|
18
|
-
if (!silent) {
|
|
19
|
-
logger.warn(
|
|
20
|
-
'Graft CLI is not installed. Install it with `npm install -g @nanonets/graft`, then rerun aginit init.'
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (!silent) logger.dim('Initializing Graft context graph...');
|
|
27
|
-
|
|
28
|
-
// Wire Antigravity, AGENTS.md readers and (optionally) Claude Code's in-repo
|
|
29
|
-
// .claude/ + .mcp.json, without touching global system files (--no-global)
|
|
30
|
-
const agents = ['antigravity', 'agents', ...(claude ? ['claude'] : [])];
|
|
31
|
-
const res = await runExecutable(
|
|
32
|
-
'graft',
|
|
33
|
-
['init', '--no-global', '--agents', ...agents, '-y'],
|
|
34
|
-
{
|
|
35
|
-
cwd: targetDir,
|
|
36
|
-
dryRun,
|
|
37
|
-
silent: true
|
|
38
|
-
}
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
if (!res.ok) {
|
|
42
|
-
logger.warn(`Graft initialization warning: ${res.stderr || res.stdout}`);
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export async function buildGraft(
|
|
50
|
-
targetDir: string,
|
|
51
|
-
options: { dryRun?: boolean; silent?: boolean } = {}
|
|
52
|
-
): Promise<boolean> {
|
|
53
|
-
const { dryRun = false } = options;
|
|
54
|
-
const installed = dryRun || (await isGraftInstalled());
|
|
55
|
-
if (!installed) return false;
|
|
56
|
-
|
|
57
|
-
const res = await runExecutable('graft', ['build'], { cwd: targetDir, dryRun, silent: true });
|
|
58
|
-
return res.ok;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export async function uninstallGraft(
|
|
62
|
-
targetDir: string,
|
|
63
|
-
options: { dryRun?: boolean; silent?: boolean } = {}
|
|
64
|
-
): Promise<boolean> {
|
|
65
|
-
const { dryRun = false } = options;
|
|
66
|
-
const installed = dryRun || (await isGraftInstalled());
|
|
67
|
-
if (installed) {
|
|
68
|
-
await runExecutable('graft', ['uninstall'], { cwd: targetDir, dryRun, silent: true });
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Also clean up AGENTS.md fenced section if any remain
|
|
72
|
-
const agentsPath = path.join(targetDir, 'AGENTS.md');
|
|
73
|
-
if (fileExists(agentsPath)) {
|
|
74
|
-
const content = readTextFile(agentsPath) || '';
|
|
75
|
-
const cleaned = content
|
|
76
|
-
.replace(/<!-- graft:start -->[\s\S]*?<!-- graft:end -->\n?/g, '')
|
|
77
|
-
.trim();
|
|
78
|
-
safeWriteFile(agentsPath, cleaned + '\n', { overwrite: true, dryRun });
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return true;
|
|
82
|
-
}
|