@soleri/cli 9.7.2 → 9.9.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.
Files changed (70) hide show
  1. package/dist/commands/add-domain.js +1 -0
  2. package/dist/commands/add-domain.js.map +1 -1
  3. package/dist/commands/add-pack.js +7 -147
  4. package/dist/commands/add-pack.js.map +1 -1
  5. package/dist/commands/agent.js +130 -0
  6. package/dist/commands/agent.js.map +1 -1
  7. package/dist/commands/create.js +96 -4
  8. package/dist/commands/create.js.map +1 -1
  9. package/dist/commands/dev.js +13 -3
  10. package/dist/commands/dev.js.map +1 -1
  11. package/dist/commands/doctor.js +2 -0
  12. package/dist/commands/doctor.js.map +1 -1
  13. package/dist/commands/extend.js +17 -0
  14. package/dist/commands/extend.js.map +1 -1
  15. package/dist/commands/install-knowledge.js +1 -0
  16. package/dist/commands/install-knowledge.js.map +1 -1
  17. package/dist/commands/install.d.ts +2 -0
  18. package/dist/commands/install.js +79 -20
  19. package/dist/commands/install.js.map +1 -1
  20. package/dist/commands/test.js +140 -1
  21. package/dist/commands/test.js.map +1 -1
  22. package/dist/commands/vault.d.ts +9 -0
  23. package/dist/commands/vault.js +66 -0
  24. package/dist/commands/vault.js.map +1 -0
  25. package/dist/hook-packs/flock-guard/manifest.json +2 -1
  26. package/dist/hook-packs/marketing-research/manifest.json +2 -1
  27. package/dist/hook-packs/registry.d.ts +2 -0
  28. package/dist/hook-packs/registry.js.map +1 -1
  29. package/dist/hook-packs/registry.ts +2 -0
  30. package/dist/main.js +7 -0
  31. package/dist/main.js.map +1 -1
  32. package/dist/prompts/create-wizard.d.ts +16 -2
  33. package/dist/prompts/create-wizard.js +84 -11
  34. package/dist/prompts/create-wizard.js.map +1 -1
  35. package/dist/utils/checks.d.ts +8 -5
  36. package/dist/utils/checks.js +105 -10
  37. package/dist/utils/checks.js.map +1 -1
  38. package/dist/utils/format-paths.d.ts +14 -0
  39. package/dist/utils/format-paths.js +27 -0
  40. package/dist/utils/format-paths.js.map +1 -0
  41. package/dist/utils/git.d.ts +29 -0
  42. package/dist/utils/git.js +88 -0
  43. package/dist/utils/git.js.map +1 -0
  44. package/dist/utils/logger.d.ts +1 -0
  45. package/dist/utils/logger.js +4 -0
  46. package/dist/utils/logger.js.map +1 -1
  47. package/package.json +1 -1
  48. package/src/__tests__/create-wizard-git.test.ts +208 -0
  49. package/src/__tests__/git-utils.test.ts +268 -0
  50. package/src/__tests__/install.test.ts +88 -0
  51. package/src/__tests__/scaffold-git-e2e.test.ts +112 -0
  52. package/src/commands/add-domain.ts +1 -0
  53. package/src/commands/add-pack.ts +10 -163
  54. package/src/commands/agent.ts +161 -0
  55. package/src/commands/create.ts +109 -5
  56. package/src/commands/dev.ts +13 -3
  57. package/src/commands/doctor.ts +1 -0
  58. package/src/commands/extend.ts +20 -1
  59. package/src/commands/install-knowledge.ts +1 -0
  60. package/src/commands/install.ts +87 -20
  61. package/src/commands/test.ts +141 -2
  62. package/src/commands/vault.ts +79 -0
  63. package/src/hook-packs/flock-guard/manifest.json +2 -1
  64. package/src/hook-packs/marketing-research/manifest.json +2 -1
  65. package/src/hook-packs/registry.ts +2 -0
  66. package/src/main.ts +10 -0
  67. package/src/prompts/create-wizard.ts +109 -14
  68. package/src/utils/checks.ts +122 -13
  69. package/src/utils/git.ts +118 -0
  70. package/src/utils/logger.ts +5 -0
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import { join, dirname } from 'node:path';
9
+ import { createRequire } from 'node:module';
9
10
  import {
10
11
  existsSync,
11
12
  readFileSync,
@@ -20,6 +21,7 @@ import { homedir } from 'node:os';
20
21
  import { execFileSync } from 'node:child_process';
21
22
  import type { Command } from 'commander';
22
23
  import * as p from '@clack/prompts';
24
+ import { parse as parseYaml } from 'yaml';
23
25
  import { PackLockfile, checkNpmVersion, checkVersionCompat, SOLERI_HOME } from '@soleri/core';
24
26
  import {
25
27
  generateClaudeMdTemplate,
@@ -47,6 +49,75 @@ export function registerAgent(program: Command): void {
47
49
  return;
48
50
  }
49
51
 
52
+ if (ctx.format === 'filetree') {
53
+ // ─── File-tree agent (v7+) ─────────────────────────────
54
+ const yamlPath = join(ctx.agentPath, 'agent.yaml');
55
+ const yaml = parseYaml(readFileSync(yamlPath, 'utf-8'));
56
+ const agentName = yaml.name || yaml.id || 'unknown';
57
+ const agentId = yaml.id || 'unknown';
58
+ const domains: string[] = Array.isArray(yaml.domains) ? yaml.domains : [];
59
+
60
+ // Count skills (directories inside skills/)
61
+ const skillsDir = join(ctx.agentPath, 'skills');
62
+ let skillsCount = 0;
63
+ if (existsSync(skillsDir)) {
64
+ skillsCount = readdirSync(skillsDir, { withFileTypes: true }).filter((d) =>
65
+ d.isDirectory(),
66
+ ).length;
67
+ }
68
+
69
+ // Resolve engine version via require.resolve
70
+ let engineVersion = 'not installed';
71
+ try {
72
+ const req = createRequire(join(ctx.agentPath, 'package.json'));
73
+ const corePkgPath = req.resolve('@soleri/core/package.json');
74
+ engineVersion = JSON.parse(readFileSync(corePkgPath, 'utf-8')).version || 'unknown';
75
+ } catch {
76
+ engineVersion = 'not installed';
77
+ }
78
+
79
+ // Check for core update
80
+ const latestCore = checkNpmVersion('@soleri/core');
81
+
82
+ // Count vault entries if db exists
83
+ const dbPath = join(ctx.agentPath, 'data', 'vault.db');
84
+ const hasVault = existsSync(dbPath);
85
+
86
+ if (opts.json) {
87
+ console.log(
88
+ JSON.stringify(
89
+ {
90
+ agent: agentName,
91
+ id: agentId,
92
+ format: 'file-tree (v7)',
93
+ engine: engineVersion,
94
+ engineLatest: latestCore,
95
+ domains,
96
+ skills: skillsCount,
97
+ vault: { exists: hasVault },
98
+ },
99
+ null,
100
+ 2,
101
+ ),
102
+ );
103
+ return;
104
+ }
105
+
106
+ console.log(`\n Agent: ${agentName}`);
107
+ console.log(` ID: ${agentId}`);
108
+ console.log(` Format: file-tree (v7)`);
109
+ console.log(
110
+ ` Engine: @soleri/core ${engineVersion}${latestCore && latestCore !== engineVersion ? ` (update available: ${latestCore})` : ''}`,
111
+ );
112
+ console.log(` Domains: ${domains.length > 0 ? domains.join(', ') : 'none'}`);
113
+ console.log(` Skills: ${skillsCount}`);
114
+ console.log(`\n Vault: ${hasVault ? 'initialized' : 'not initialized'}`);
115
+ console.log('');
116
+ return;
117
+ }
118
+
119
+ // ─── Legacy TypeScript agent ──────────────────────────────
120
+
50
121
  // Read agent package.json
51
122
  const pkgPath = join(ctx.agentPath, 'package.json');
52
123
  const pkg = existsSync(pkgPath) ? JSON.parse(readFileSync(pkgPath, 'utf-8')) : {};
@@ -128,6 +199,96 @@ export function registerAgent(program: Command): void {
128
199
  return;
129
200
  }
130
201
 
202
+ // ─── File-tree agent (v7+) ────────────────────────────────
203
+ if (ctx.format === 'filetree') {
204
+ // Resolve installed @soleri/core version
205
+ let installedVersion: string | null = null;
206
+ try {
207
+ const req = createRequire(import.meta.url);
208
+ const corePkgPath = req.resolve('@soleri/core/package.json');
209
+ installedVersion = JSON.parse(readFileSync(corePkgPath, 'utf-8')).version ?? null;
210
+ } catch {
211
+ // @soleri/core not resolvable — will show as unknown
212
+ }
213
+
214
+ // Check latest version on npm
215
+ const latestCore = checkNpmVersion('@soleri/core');
216
+
217
+ if (opts.check) {
218
+ const installed = installedVersion ?? 'unknown';
219
+ const latest = latestCore ?? 'unknown';
220
+ if (installed === latest) {
221
+ console.log(`\n Engine: @soleri/core ${installed} (up to date)`);
222
+ } else {
223
+ console.log(`\n Engine: @soleri/core ${installed} → ${latest}`);
224
+ }
225
+ console.log(` Format: file-tree (updates via template refresh)`);
226
+ console.log('');
227
+ return;
228
+ }
229
+
230
+ if (opts.dryRun) {
231
+ p.log.info('Would refresh templates (regenerate _engine.md, CLAUDE.md, sync skills)');
232
+ if (latestCore && installedVersion && latestCore !== installedVersion) {
233
+ p.log.info(
234
+ `Engine: ${installedVersion} → ${latestCore} (update @soleri/core globally to upgrade)`,
235
+ );
236
+ }
237
+ return;
238
+ }
239
+
240
+ // Run refresh: regenerate _engine.md, recompose CLAUDE.md, sync skills
241
+ const enginePath = join(ctx.agentPath, 'instructions', '_engine.md');
242
+ const claudeMdPath = join(ctx.agentPath, 'CLAUDE.md');
243
+
244
+ const skillFiles = generateSkills({ id: ctx.agentId } as AgentConfig);
245
+
246
+ // 1. Sync skills
247
+ if (skillFiles.length > 0) {
248
+ let newCount = 0;
249
+ let updatedCount = 0;
250
+ for (const [relPath, content] of skillFiles) {
251
+ const fullPath = join(ctx.agentPath, relPath);
252
+ const dirPath = dirname(fullPath);
253
+ const isNew = !existsSync(fullPath);
254
+ mkdirSync(dirPath, { recursive: true });
255
+ writeFileSync(fullPath, content, 'utf-8');
256
+ if (isNew) newCount++;
257
+ else updatedCount++;
258
+ }
259
+ p.log.success(
260
+ `Synced ${skillFiles.length} skills (${newCount} new, ${updatedCount} updated)`,
261
+ );
262
+ }
263
+
264
+ // 2. Regenerate _engine.md
265
+ mkdirSync(join(ctx.agentPath, 'instructions'), { recursive: true });
266
+ writeFileSync(enginePath, getEngineRulesContent(), 'utf-8');
267
+ p.log.success(`Regenerated ${enginePath}`);
268
+
269
+ // 3. Recompose CLAUDE.md
270
+ const result = composeClaudeMd(ctx.agentPath);
271
+ writeFileSync(claudeMdPath, result.content, 'utf-8');
272
+ p.log.success(
273
+ `Regenerated ${claudeMdPath} (${result.sources.length} sources, ${result.content.length} bytes)`,
274
+ );
275
+
276
+ // 4. Show engine version status
277
+ const installed = installedVersion ?? 'unknown';
278
+ const latest = latestCore ?? 'unknown';
279
+ if (installed !== 'unknown' && latest !== 'unknown' && installed !== latest) {
280
+ p.log.info(
281
+ `Engine: ${installed} → ${latest} (run \`npm update -g @soleri/cli\` to upgrade)`,
282
+ );
283
+ } else if (installed !== 'unknown') {
284
+ p.log.info(`Engine: @soleri/core ${installed} (up to date)`);
285
+ }
286
+
287
+ p.log.info('File-tree agents update by refreshing templates from the installed engine.');
288
+ return;
289
+ }
290
+
291
+ // ─── Legacy TypeScript agent ──────────────────────────────
131
292
  const pkgPath = join(ctx.agentPath, 'package.json');
132
293
  if (!existsSync(pkgPath)) {
133
294
  p.log.error('No package.json found in agent directory.');
@@ -1,4 +1,4 @@
1
- import { readFileSync, existsSync } from 'node:fs';
1
+ import { accessSync, constants as fsConstants, readFileSync, existsSync } from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
3
  import type { Command } from 'commander';
4
4
  import * as p from '@clack/prompts';
@@ -10,9 +10,17 @@ import {
10
10
  type SetupTarget,
11
11
  scaffoldFileTree,
12
12
  } from '@soleri/forge/lib';
13
- import { runCreateWizard } from '../prompts/create-wizard.js';
13
+ import { runCreateWizard, type WizardGitConfig } from '../prompts/create-wizard.js';
14
14
  import { listPacks } from '../hook-packs/registry.js';
15
15
  import { installPack } from '../hook-packs/installer.js';
16
+ import {
17
+ isGitInstalled,
18
+ gitInit,
19
+ gitInitialCommit,
20
+ gitAddRemote,
21
+ gitPush,
22
+ ghCreateRepo,
23
+ } from '../utils/git.js';
16
24
 
17
25
  function parseSetupTarget(value?: string): SetupTarget | undefined {
18
26
  if (!value) return undefined;
@@ -40,6 +48,7 @@ export function registerCreate(program: Command): void {
40
48
  .option('--dir <path>', `Parent directory for the agent (default: current directory)`)
41
49
  .option('--filetree', 'Create a file-tree agent (v7 — no TypeScript, no build step)')
42
50
  .option('--legacy', 'Create a legacy TypeScript agent (v6 — requires npm install + build)')
51
+ .option('--no-git', 'Skip git repository initialization (git init is on by default)')
43
52
  .description('Create a new Soleri agent')
44
53
  .action(
45
54
  async (
@@ -51,11 +60,15 @@ export function registerCreate(program: Command): void {
51
60
  setupTarget?: string;
52
61
  filetree?: boolean;
53
62
  legacy?: boolean;
63
+ git?: boolean;
54
64
  },
55
65
  ) => {
56
66
  try {
57
67
  let config;
58
68
 
69
+ let gitConfig: WizardGitConfig | undefined;
70
+ const skipGit = opts?.git === false; // Commander sets git=false when --no-git is passed
71
+
59
72
  if (name && opts?.yes && !opts?.config) {
60
73
  // Quick non-interactive: name + --yes = Italian Craftsperson defaults
61
74
  const id = name
@@ -73,6 +86,10 @@ export function registerCreate(program: Command): void {
73
86
  tone: 'mentor',
74
87
  greeting: `Ciao! I'm ${name}. Ready to build something beautiful today?`,
75
88
  });
89
+ // Non-interactive default: git init yes, no remote
90
+ if (!skipGit) {
91
+ gitConfig = { init: true };
92
+ }
76
93
  } else if (opts?.config) {
77
94
  // Non-interactive: read from config file
78
95
  const configPath = resolve(opts.config);
@@ -80,13 +97,26 @@ export function registerCreate(program: Command): void {
80
97
  p.log.error(`Config file not found: ${configPath}`);
81
98
  process.exit(1);
82
99
  }
83
- const raw = JSON.parse(readFileSync(configPath, 'utf-8'));
100
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
101
+ let raw: any;
102
+ try {
103
+ raw = JSON.parse(readFileSync(configPath, 'utf-8'));
104
+ } catch {
105
+ p.log.error(
106
+ `Failed to parse ${configPath}. The file may be corrupted. Delete it and try again.`,
107
+ );
108
+ process.exit(1);
109
+ }
84
110
  const parsed = AgentConfigSchema.safeParse(raw);
85
111
  if (!parsed.success) {
86
112
  p.log.error(`Invalid config: ${parsed.error.message}`);
87
113
  process.exit(1);
88
114
  }
89
115
  config = parsed.data;
116
+ // Config path: default to git init unless --no-git or config specifies git
117
+ if (!skipGit) {
118
+ gitConfig = raw.git ?? { init: true };
119
+ }
90
120
  } else {
91
121
  // Interactive wizard
92
122
  const wizardResult = await runCreateWizard(name);
@@ -94,12 +124,15 @@ export function registerCreate(program: Command): void {
94
124
  p.outro('Cancelled.');
95
125
  return;
96
126
  }
97
- const parsed = AgentConfigSchema.safeParse(wizardResult);
127
+ const parsed = AgentConfigSchema.safeParse(wizardResult.config);
98
128
  if (!parsed.success) {
99
129
  p.log.error(`Invalid config: ${parsed.error.message}`);
100
130
  process.exit(1);
101
131
  }
102
132
  config = parsed.data;
133
+ if (!skipGit) {
134
+ gitConfig = wizardResult.git;
135
+ }
103
136
  }
104
137
 
105
138
  const setupTarget = parseSetupTarget(opts?.setupTarget);
@@ -151,6 +184,15 @@ export function registerCreate(program: Command): void {
151
184
  };
152
185
 
153
186
  const outputDir = opts?.dir ? resolve(opts.dir) : (config.outputDir ?? process.cwd());
187
+
188
+ // Preflight: check output directory is writable
189
+ try {
190
+ accessSync(outputDir, fsConstants.W_OK);
191
+ } catch {
192
+ p.log.error(`Cannot write to ${outputDir} — check permissions`);
193
+ process.exit(1);
194
+ }
195
+
154
196
  const nonInteractive = !!(opts?.yes || opts?.config);
155
197
 
156
198
  if (!nonInteractive) {
@@ -177,6 +219,68 @@ export function registerCreate(program: Command): void {
177
219
  process.exit(1);
178
220
  }
179
221
 
222
+ // ─── Git initialization ──────────────────────────────
223
+ if (gitConfig?.init) {
224
+ const hasGit = await isGitInstalled();
225
+ if (!hasGit) {
226
+ p.log.warn(
227
+ 'git is not installed — skipping repository initialization. Install git from https://git-scm.com/',
228
+ );
229
+ } else {
230
+ const agentDir = result.agentDir;
231
+ s.start('Initializing git repository...');
232
+
233
+ const initResult = await gitInit(agentDir);
234
+ if (!initResult.ok) {
235
+ s.stop('git init failed');
236
+ p.log.warn(`git init failed: ${initResult.error}`);
237
+ } else {
238
+ const commitResult = await gitInitialCommit(
239
+ agentDir,
240
+ `feat: scaffold agent "${config.name}"`,
241
+ );
242
+ if (!commitResult.ok) {
243
+ s.stop('Initial commit failed');
244
+ p.log.warn(`Initial commit failed: ${commitResult.error}`);
245
+ } else {
246
+ s.stop('Git repository initialized with initial commit');
247
+ }
248
+
249
+ // Remote setup
250
+ if (gitConfig.remote && initResult.ok && commitResult.ok) {
251
+ if (gitConfig.remote.type === 'gh') {
252
+ s.start('Creating GitHub repository...');
253
+ const ghResult = await ghCreateRepo(config.id, {
254
+ visibility: gitConfig.remote.visibility ?? 'private',
255
+ dir: agentDir,
256
+ });
257
+ if (!ghResult.ok) {
258
+ s.stop('GitHub repo creation failed');
259
+ p.log.warn(`gh repo create failed: ${ghResult.error}`);
260
+ } else {
261
+ s.stop(`Pushed to ${ghResult.url ?? 'GitHub'}`);
262
+ }
263
+ } else if (gitConfig.remote.type === 'manual' && gitConfig.remote.url) {
264
+ s.start('Setting up remote...');
265
+ const remoteResult = await gitAddRemote(agentDir, gitConfig.remote.url);
266
+ if (!remoteResult.ok) {
267
+ s.stop('Failed to add remote');
268
+ p.log.warn(`git remote add failed: ${remoteResult.error}`);
269
+ } else {
270
+ const pushResult = await gitPush(agentDir);
271
+ if (!pushResult.ok) {
272
+ s.stop('Push failed');
273
+ p.log.warn(`git push failed: ${pushResult.error}`);
274
+ } else {
275
+ s.stop('Pushed to remote');
276
+ }
277
+ }
278
+ }
279
+ }
280
+ }
281
+ }
282
+ }
283
+
180
284
  p.note(result.summary, 'Next steps');
181
285
  p.outro('Done!');
182
286
  return;
@@ -208,7 +312,7 @@ export function registerCreate(program: Command): void {
208
312
  selectedPacks = selectedPacks.filter((pk) => available.includes(pk));
209
313
  }
210
314
  } else if (!nonInteractive && claudeSetup) {
211
- const packs = listPacks();
315
+ const packs = listPacks().filter((pk) => pk.scaffoldDefault !== false);
212
316
  const packChoices = packs.map((pk) => ({
213
317
  value: pk.name,
214
318
  label: pk.name,
@@ -35,7 +35,14 @@ function runFileTreeDev(agentPath: string, agentId: string): void {
35
35
  regenerateClaudeMd(agentPath);
36
36
 
37
37
  // Start the engine server
38
- const engineBin = require.resolve('@soleri/core/dist/engine/bin/soleri-engine.js');
38
+ let engineBin: string;
39
+ try {
40
+ engineBin = require.resolve('@soleri/core/dist/engine/bin/soleri-engine.js');
41
+ } catch {
42
+ p.log.error('Engine not found. Run: npm install @soleri/core');
43
+ process.exit(1);
44
+ }
45
+
39
46
  const engine = spawn('node', [engineBin, '--agent', join(agentPath, 'agent.yaml')], {
40
47
  stdio: ['pipe', 'inherit', 'inherit'],
41
48
  env: { ...process.env },
@@ -72,8 +79,11 @@ function runFileTreeDev(agentPath: string, agentId: string): void {
72
79
  regenerateClaudeMd(agentPath);
73
80
  }, 200);
74
81
  });
75
- } catch {
76
- // Directory may not exist yet that's OK
82
+ } catch (err: unknown) {
83
+ const msg = err instanceof Error ? err.message : String(err);
84
+ if (!msg.includes('ENOENT')) {
85
+ p.log.warn(`File watch stopped: ${msg}. Restart soleri dev if changes stop updating.`);
86
+ }
77
87
  }
78
88
  }
79
89
 
@@ -15,6 +15,7 @@ export function registerDoctor(program: Command): void {
15
15
 
16
16
  for (const r of results) {
17
17
  if (r.status === 'pass') log.pass(r.label, r.detail);
18
+ else if (r.status === 'skip') log.skip(r.label, r.detail);
18
19
  else if (r.status === 'warn') {
19
20
  log.warn(r.label, r.detail);
20
21
  hasWarnings = true;
@@ -2,7 +2,22 @@ import type { Command } from 'commander';
2
2
  import * as p from '@clack/prompts';
3
3
  import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
4
4
  import { join } from 'node:path';
5
- import { detectAgent } from '../utils/agent-context.js';
5
+ import { detectAgent, type AgentContext } from '../utils/agent-context.js';
6
+
7
+ function warnFiletreeAndExit(ctx: AgentContext): boolean {
8
+ if (ctx.format !== 'filetree') return false;
9
+ p.log.warn(
10
+ [
11
+ `The 'extend' command requires a TypeScript agent format.`,
12
+ '',
13
+ 'For file-tree agents, use these alternatives:',
14
+ ' • Custom skills: add SKILL.md files to skills/{name}/SKILL.md',
15
+ ' • Hook packs: soleri hooks add-pack <pack>',
16
+ ' • Custom knowledge: add JSON files to knowledge/',
17
+ ].join('\n'),
18
+ );
19
+ process.exit(0);
20
+ }
6
21
 
7
22
  export function registerExtend(program: Command): void {
8
23
  const extend = program
@@ -18,6 +33,7 @@ export function registerExtend(program: Command): void {
18
33
  p.log.error('No agent project detected. Run this from an agent root.');
19
34
  process.exit(1);
20
35
  }
36
+ warnFiletreeAndExit(ctx);
21
37
 
22
38
  const extDir = join(ctx.agentPath, 'src', 'extensions');
23
39
  if (existsSync(join(extDir, 'index.ts'))) {
@@ -53,6 +69,7 @@ export function registerExtend(program: Command): void {
53
69
  p.log.error('No agent project detected. Run this from an agent root.');
54
70
  process.exit(1);
55
71
  }
72
+ warnFiletreeAndExit(ctx);
56
73
 
57
74
  const opsDir = join(ctx.agentPath, 'src', 'extensions', 'ops');
58
75
  mkdirSync(opsDir, { recursive: true });
@@ -108,6 +125,7 @@ export function ${fnName}(runtime: AgentRuntime): OpDefinition {
108
125
  p.log.error('No agent project detected. Run this from an agent root.');
109
126
  process.exit(1);
110
127
  }
128
+ warnFiletreeAndExit(ctx);
111
129
 
112
130
  const facadesDir = join(ctx.agentPath, 'src', 'extensions', 'facades');
113
131
  mkdirSync(facadesDir, { recursive: true });
@@ -162,6 +180,7 @@ export function create${className}Facade(runtime: AgentRuntime): FacadeConfig {
162
180
  p.log.error('No agent project detected. Run this from an agent root.');
163
181
  process.exit(1);
164
182
  }
183
+ warnFiletreeAndExit(ctx);
165
184
 
166
185
  const mwDir = join(ctx.agentPath, 'src', 'extensions', 'middleware');
167
186
  mkdirSync(mwDir, { recursive: true });
@@ -98,6 +98,7 @@ export function registerInstallKnowledge(program: Command): void {
98
98
  agentPath: ctx.agentPath,
99
99
  bundlePath,
100
100
  generateFacades: opts.facades,
101
+ format: ctx.format,
101
102
  });
102
103
 
103
104
  s.stop(result.success ? result.summary : 'Installation failed');