@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.
- package/dist/commands/add-domain.js +1 -0
- package/dist/commands/add-domain.js.map +1 -1
- package/dist/commands/add-pack.js +7 -147
- package/dist/commands/add-pack.js.map +1 -1
- package/dist/commands/agent.js +130 -0
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/create.js +96 -4
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/dev.js +13 -3
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.js +2 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/extend.js +17 -0
- package/dist/commands/extend.js.map +1 -1
- package/dist/commands/install-knowledge.js +1 -0
- package/dist/commands/install-knowledge.js.map +1 -1
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +79 -20
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/test.js +140 -1
- package/dist/commands/test.js.map +1 -1
- package/dist/commands/vault.d.ts +9 -0
- package/dist/commands/vault.js +66 -0
- package/dist/commands/vault.js.map +1 -0
- package/dist/hook-packs/flock-guard/manifest.json +2 -1
- package/dist/hook-packs/marketing-research/manifest.json +2 -1
- package/dist/hook-packs/registry.d.ts +2 -0
- package/dist/hook-packs/registry.js.map +1 -1
- package/dist/hook-packs/registry.ts +2 -0
- package/dist/main.js +7 -0
- package/dist/main.js.map +1 -1
- package/dist/prompts/create-wizard.d.ts +16 -2
- package/dist/prompts/create-wizard.js +84 -11
- package/dist/prompts/create-wizard.js.map +1 -1
- package/dist/utils/checks.d.ts +8 -5
- package/dist/utils/checks.js +105 -10
- package/dist/utils/checks.js.map +1 -1
- package/dist/utils/format-paths.d.ts +14 -0
- package/dist/utils/format-paths.js +27 -0
- package/dist/utils/format-paths.js.map +1 -0
- package/dist/utils/git.d.ts +29 -0
- package/dist/utils/git.js +88 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/logger.d.ts +1 -0
- package/dist/utils/logger.js +4 -0
- package/dist/utils/logger.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/create-wizard-git.test.ts +208 -0
- package/src/__tests__/git-utils.test.ts +268 -0
- package/src/__tests__/install.test.ts +88 -0
- package/src/__tests__/scaffold-git-e2e.test.ts +112 -0
- package/src/commands/add-domain.ts +1 -0
- package/src/commands/add-pack.ts +10 -163
- package/src/commands/agent.ts +161 -0
- package/src/commands/create.ts +109 -5
- package/src/commands/dev.ts +13 -3
- package/src/commands/doctor.ts +1 -0
- package/src/commands/extend.ts +20 -1
- package/src/commands/install-knowledge.ts +1 -0
- package/src/commands/install.ts +87 -20
- package/src/commands/test.ts +141 -2
- package/src/commands/vault.ts +79 -0
- package/src/hook-packs/flock-guard/manifest.json +2 -1
- package/src/hook-packs/marketing-research/manifest.json +2 -1
- package/src/hook-packs/registry.ts +2 -0
- package/src/main.ts +10 -0
- package/src/prompts/create-wizard.ts +109 -14
- package/src/utils/checks.ts +122 -13
- package/src/utils/git.ts +118 -0
- package/src/utils/logger.ts +5 -0
package/src/commands/agent.ts
CHANGED
|
@@ -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.');
|
package/src/commands/create.ts
CHANGED
|
@@ -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
|
-
|
|
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,
|
package/src/commands/dev.ts
CHANGED
|
@@ -35,7 +35,14 @@ function runFileTreeDev(agentPath: string, agentId: string): void {
|
|
|
35
35
|
regenerateClaudeMd(agentPath);
|
|
36
36
|
|
|
37
37
|
// Start the engine server
|
|
38
|
-
|
|
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
|
-
|
|
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
|
|
package/src/commands/doctor.ts
CHANGED
|
@@ -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;
|
package/src/commands/extend.ts
CHANGED
|
@@ -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 });
|