chainlesschain 0.37.8 → 0.37.9
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/README.md +109 -4
- package/bin/chainlesschain.js +0 -0
- package/package.json +7 -2
- package/src/commands/agent.js +30 -0
- package/src/commands/ask.js +114 -0
- package/src/commands/chat.js +35 -0
- package/src/commands/db.js +152 -0
- package/src/commands/llm.js +137 -0
- package/src/commands/note.js +302 -0
- package/src/commands/skill.js +479 -0
- package/src/index.js +17 -0
- package/src/lib/platform.js +15 -0
- package/src/repl/agent-repl.js +782 -0
- package/src/repl/chat-repl.js +262 -0
- package/src/runtime/bootstrap.js +159 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill management and execution commands
|
|
3
|
+
* chainlesschain skill list|info|run|search|categories
|
|
4
|
+
*
|
|
5
|
+
* Loads built-in skills directly from the desktop app's bundled skill definitions.
|
|
6
|
+
* Most skills (110+/138) are pure JS and run headless without Electron.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import ora from "ora";
|
|
11
|
+
import fs from "fs";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
import { logger } from "../lib/logger.js";
|
|
15
|
+
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Find the bundled skills directory
|
|
20
|
+
*/
|
|
21
|
+
function findSkillsDir() {
|
|
22
|
+
// Walk up from CLI package to find desktop-app-vue
|
|
23
|
+
const candidates = [
|
|
24
|
+
path.resolve(
|
|
25
|
+
__dirname,
|
|
26
|
+
"../../../../desktop-app-vue/src/main/ai-engine/cowork/skills/builtin",
|
|
27
|
+
),
|
|
28
|
+
path.resolve(
|
|
29
|
+
process.cwd(),
|
|
30
|
+
"desktop-app-vue/src/main/ai-engine/cowork/skills/builtin",
|
|
31
|
+
),
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
for (const dir of candidates) {
|
|
35
|
+
if (fs.existsSync(dir)) return dir;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Simple YAML frontmatter parser (no dependencies)
|
|
43
|
+
*/
|
|
44
|
+
function parseSkillMd(content) {
|
|
45
|
+
const lines = content.split("\n");
|
|
46
|
+
if (lines[0].trim() !== "---") return { data: {}, body: content };
|
|
47
|
+
|
|
48
|
+
let endIndex = -1;
|
|
49
|
+
for (let i = 1; i < lines.length; i++) {
|
|
50
|
+
if (lines[i].trim() === "---") {
|
|
51
|
+
endIndex = i;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (endIndex === -1) return { data: {}, body: content };
|
|
57
|
+
|
|
58
|
+
const yamlLines = lines.slice(1, endIndex);
|
|
59
|
+
const body = lines
|
|
60
|
+
.slice(endIndex + 1)
|
|
61
|
+
.join("\n")
|
|
62
|
+
.trim();
|
|
63
|
+
const data = {};
|
|
64
|
+
|
|
65
|
+
let currentKey = null;
|
|
66
|
+
let currentArray = null;
|
|
67
|
+
|
|
68
|
+
for (const line of yamlLines) {
|
|
69
|
+
if (!line.trim() || line.trim().startsWith("#")) continue;
|
|
70
|
+
|
|
71
|
+
const trimmed = line.trim();
|
|
72
|
+
|
|
73
|
+
if (trimmed.startsWith("- ")) {
|
|
74
|
+
const value = trimmed
|
|
75
|
+
.slice(2)
|
|
76
|
+
.trim()
|
|
77
|
+
.replace(/^['"]|['"]$/g, "");
|
|
78
|
+
if (currentArray) currentArray.push(value);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const colonIndex = trimmed.indexOf(":");
|
|
83
|
+
if (colonIndex > 0) {
|
|
84
|
+
const key = trimmed.slice(0, colonIndex).trim();
|
|
85
|
+
let value = trimmed.slice(colonIndex + 1).trim();
|
|
86
|
+
|
|
87
|
+
// Convert kebab-case to camelCase
|
|
88
|
+
const camelKey = key.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
89
|
+
|
|
90
|
+
if (value === "") {
|
|
91
|
+
// Could be start of nested object or array
|
|
92
|
+
currentKey = camelKey;
|
|
93
|
+
currentArray = null;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Handle inline arrays [a, b, c]
|
|
98
|
+
if (value.startsWith("[") && value.endsWith("]")) {
|
|
99
|
+
data[camelKey] = value
|
|
100
|
+
.slice(1, -1)
|
|
101
|
+
.split(",")
|
|
102
|
+
.map((v) => v.trim().replace(/^['"]|['"]$/g, ""))
|
|
103
|
+
.filter(Boolean);
|
|
104
|
+
currentArray = null;
|
|
105
|
+
currentKey = null;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Handle booleans and numbers
|
|
110
|
+
if (value === "true") value = true;
|
|
111
|
+
else if (value === "false") value = false;
|
|
112
|
+
else if (value === "null") value = null;
|
|
113
|
+
else if (/^\d+(\.\d+)?$/.test(value)) value = parseFloat(value);
|
|
114
|
+
else value = value.replace(/^['"]|['"]$/g, "");
|
|
115
|
+
|
|
116
|
+
data[camelKey] = value;
|
|
117
|
+
|
|
118
|
+
// If next lines might be array items for this key
|
|
119
|
+
if (Array.isArray(data[camelKey])) {
|
|
120
|
+
currentArray = data[camelKey];
|
|
121
|
+
} else {
|
|
122
|
+
currentArray = null;
|
|
123
|
+
}
|
|
124
|
+
currentKey = camelKey;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { data, body };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Load all skill metadata from the bundled directory
|
|
133
|
+
*/
|
|
134
|
+
function loadSkillMetadata(skillsDir) {
|
|
135
|
+
const skills = [];
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const dirs = fs.readdirSync(skillsDir, { withFileTypes: true });
|
|
139
|
+
|
|
140
|
+
for (const dir of dirs) {
|
|
141
|
+
if (!dir.isDirectory()) continue;
|
|
142
|
+
|
|
143
|
+
const skillMd = path.join(skillsDir, dir.name, "SKILL.md");
|
|
144
|
+
if (!fs.existsSync(skillMd)) continue;
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const content = fs.readFileSync(skillMd, "utf8");
|
|
148
|
+
const { data, body } = parseSkillMd(content);
|
|
149
|
+
|
|
150
|
+
skills.push({
|
|
151
|
+
id: data.name || dir.name,
|
|
152
|
+
displayName: data.displayName || dir.name,
|
|
153
|
+
description: data.description || "",
|
|
154
|
+
version: data.version || "1.0.0",
|
|
155
|
+
category: data.category || "uncategorized",
|
|
156
|
+
tags: data.tags || [],
|
|
157
|
+
userInvocable: data.userInvocable !== false,
|
|
158
|
+
handler: data.handler || null,
|
|
159
|
+
capabilities: data.capabilities || [],
|
|
160
|
+
os: data.os || [],
|
|
161
|
+
dirName: dir.name,
|
|
162
|
+
hasHandler: fs.existsSync(
|
|
163
|
+
path.join(skillsDir, dir.name, "handler.js"),
|
|
164
|
+
),
|
|
165
|
+
body,
|
|
166
|
+
});
|
|
167
|
+
} catch {
|
|
168
|
+
// Skip malformed skill files
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} catch (err) {
|
|
172
|
+
logger.error(`Failed to read skills directory: ${err.message}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return skills;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Check if a skill can run on current platform
|
|
180
|
+
*/
|
|
181
|
+
function canRunOnPlatform(skill) {
|
|
182
|
+
if (!skill.os || skill.os.length === 0) return true;
|
|
183
|
+
return skill.os.includes(process.platform);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function registerSkillCommand(program) {
|
|
187
|
+
const skill = program
|
|
188
|
+
.command("skill")
|
|
189
|
+
.description("Manage and run built-in AI skills (138 available)");
|
|
190
|
+
|
|
191
|
+
// skill list
|
|
192
|
+
skill
|
|
193
|
+
.command("list")
|
|
194
|
+
.description("List all available skills")
|
|
195
|
+
.option("--category <category>", "Filter by category")
|
|
196
|
+
.option("--tag <tag>", "Filter by tag")
|
|
197
|
+
.option("--runnable", "Only show skills that can run headless")
|
|
198
|
+
.option("--json", "Output as JSON")
|
|
199
|
+
.action(async (options) => {
|
|
200
|
+
const skillsDir = findSkillsDir();
|
|
201
|
+
if (!skillsDir) {
|
|
202
|
+
logger.error(
|
|
203
|
+
"Skills directory not found. Make sure you're in the ChainlessChain project root.",
|
|
204
|
+
);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const spinner = ora("Loading skills...").start();
|
|
209
|
+
let skills = loadSkillMetadata(skillsDir);
|
|
210
|
+
spinner.stop();
|
|
211
|
+
|
|
212
|
+
// Filter
|
|
213
|
+
if (options.category) {
|
|
214
|
+
skills = skills.filter(
|
|
215
|
+
(s) => s.category.toLowerCase() === options.category.toLowerCase(),
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
if (options.tag) {
|
|
219
|
+
skills = skills.filter((s) =>
|
|
220
|
+
s.tags.some((t) =>
|
|
221
|
+
t.toLowerCase().includes(options.tag.toLowerCase()),
|
|
222
|
+
),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (options.runnable) {
|
|
226
|
+
skills = skills.filter((s) => s.hasHandler && canRunOnPlatform(s));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (options.json) {
|
|
230
|
+
console.log(
|
|
231
|
+
JSON.stringify(
|
|
232
|
+
skills.map(({ body, ...rest }) => rest),
|
|
233
|
+
null,
|
|
234
|
+
2,
|
|
235
|
+
),
|
|
236
|
+
);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Group by category
|
|
241
|
+
const byCategory = {};
|
|
242
|
+
for (const s of skills) {
|
|
243
|
+
const cat = s.category || "uncategorized";
|
|
244
|
+
if (!byCategory[cat]) byCategory[cat] = [];
|
|
245
|
+
byCategory[cat].push(s);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
logger.log(chalk.bold(`\nSkills (${skills.length}):\n`));
|
|
249
|
+
|
|
250
|
+
for (const [cat, catSkills] of Object.entries(byCategory).sort()) {
|
|
251
|
+
logger.log(chalk.yellow(` ${cat} (${catSkills.length})`));
|
|
252
|
+
for (const s of catSkills) {
|
|
253
|
+
const handler = s.hasHandler ? chalk.green("●") : chalk.gray("○");
|
|
254
|
+
const name = chalk.cyan(s.id.padEnd(30));
|
|
255
|
+
const desc = chalk.gray((s.description || "").substring(0, 50));
|
|
256
|
+
logger.log(` ${handler} ${name} ${desc}`);
|
|
257
|
+
}
|
|
258
|
+
logger.log("");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
logger.log(
|
|
262
|
+
chalk.gray("● = has handler (runnable) ○ = documentation only\n"),
|
|
263
|
+
);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// skill categories
|
|
267
|
+
skill
|
|
268
|
+
.command("categories")
|
|
269
|
+
.description("List skill categories")
|
|
270
|
+
.action(async () => {
|
|
271
|
+
const skillsDir = findSkillsDir();
|
|
272
|
+
if (!skillsDir) {
|
|
273
|
+
logger.error("Skills directory not found.");
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const skills = loadSkillMetadata(skillsDir);
|
|
278
|
+
const cats = {};
|
|
279
|
+
for (const s of skills) {
|
|
280
|
+
const cat = s.category || "uncategorized";
|
|
281
|
+
cats[cat] = (cats[cat] || 0) + 1;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
logger.log(chalk.bold("\nSkill Categories:\n"));
|
|
285
|
+
for (const [cat, count] of Object.entries(cats).sort()) {
|
|
286
|
+
logger.log(` ${chalk.cyan(cat.padEnd(25))} ${count} skills`);
|
|
287
|
+
}
|
|
288
|
+
logger.log("");
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// skill info
|
|
292
|
+
skill
|
|
293
|
+
.command("info")
|
|
294
|
+
.description("Show detailed info about a skill")
|
|
295
|
+
.argument("<name>", "Skill name")
|
|
296
|
+
.option("--json", "Output as JSON")
|
|
297
|
+
.action(async (name, options) => {
|
|
298
|
+
const skillsDir = findSkillsDir();
|
|
299
|
+
if (!skillsDir) {
|
|
300
|
+
logger.error("Skills directory not found.");
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const skills = loadSkillMetadata(skillsDir);
|
|
305
|
+
const s = skills.find(
|
|
306
|
+
(s) => s.id === name || s.dirName === name || s.id.includes(name),
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
if (!s) {
|
|
310
|
+
logger.error(`Skill not found: ${name}`);
|
|
311
|
+
const close = skills
|
|
312
|
+
.filter((s) => s.id.includes(name) || s.dirName.includes(name))
|
|
313
|
+
.slice(0, 5);
|
|
314
|
+
if (close.length > 0) {
|
|
315
|
+
logger.info(`Did you mean: ${close.map((s) => s.id).join(", ")}?`);
|
|
316
|
+
}
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (options.json) {
|
|
321
|
+
const { body, ...rest } = s;
|
|
322
|
+
console.log(JSON.stringify(rest, null, 2));
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
logger.log(chalk.bold(`\n${s.displayName}`));
|
|
327
|
+
logger.log(chalk.gray(`ID: ${s.id} v${s.version}`));
|
|
328
|
+
logger.log(chalk.gray(`Category: ${s.category}`));
|
|
329
|
+
if (s.tags.length > 0) {
|
|
330
|
+
logger.log(chalk.gray(`Tags: ${s.tags.join(", ")}`));
|
|
331
|
+
}
|
|
332
|
+
logger.log("");
|
|
333
|
+
logger.log(s.description || "No description");
|
|
334
|
+
logger.log("");
|
|
335
|
+
logger.log(
|
|
336
|
+
`Handler: ${s.hasHandler ? chalk.green("Available") : chalk.gray("None")}`,
|
|
337
|
+
);
|
|
338
|
+
logger.log(
|
|
339
|
+
`Platform: ${canRunOnPlatform(s) ? chalk.green("Compatible") : chalk.red("Not supported")}`,
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
if (s.body) {
|
|
343
|
+
logger.log(chalk.bold("\n--- Documentation ---\n"));
|
|
344
|
+
logger.log(s.body.substring(0, 2000));
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// skill search
|
|
349
|
+
skill
|
|
350
|
+
.command("search")
|
|
351
|
+
.description("Search skills by keyword")
|
|
352
|
+
.argument("<query>", "Search query")
|
|
353
|
+
.action(async (query) => {
|
|
354
|
+
const skillsDir = findSkillsDir();
|
|
355
|
+
if (!skillsDir) {
|
|
356
|
+
logger.error("Skills directory not found.");
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const skills = loadSkillMetadata(skillsDir);
|
|
361
|
+
const q = query.toLowerCase();
|
|
362
|
+
const matches = skills.filter(
|
|
363
|
+
(s) =>
|
|
364
|
+
s.id.includes(q) ||
|
|
365
|
+
s.displayName.toLowerCase().includes(q) ||
|
|
366
|
+
s.description.toLowerCase().includes(q) ||
|
|
367
|
+
s.tags.some((t) => t.toLowerCase().includes(q)),
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
if (matches.length === 0) {
|
|
371
|
+
logger.info(`No skills matching "${query}"`);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
logger.log(
|
|
376
|
+
chalk.bold(`\nSearch results for "${query}" (${matches.length}):\n`),
|
|
377
|
+
);
|
|
378
|
+
for (const s of matches) {
|
|
379
|
+
const handler = s.hasHandler ? chalk.green("●") : chalk.gray("○");
|
|
380
|
+
logger.log(
|
|
381
|
+
` ${handler} ${chalk.cyan(s.id.padEnd(30))} ${chalk.gray(s.description.substring(0, 50))}`,
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
logger.log("");
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
// skill run
|
|
388
|
+
skill
|
|
389
|
+
.command("run")
|
|
390
|
+
.description("Execute a skill")
|
|
391
|
+
.argument("<name>", "Skill name")
|
|
392
|
+
.argument("[input...]", "Input for the skill")
|
|
393
|
+
.option("--json", "Output as JSON")
|
|
394
|
+
.action(async (name, inputParts, options) => {
|
|
395
|
+
const skillsDir = findSkillsDir();
|
|
396
|
+
if (!skillsDir) {
|
|
397
|
+
logger.error("Skills directory not found.");
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const skills = loadSkillMetadata(skillsDir);
|
|
402
|
+
const s = skills.find((sk) => sk.id === name || sk.dirName === name);
|
|
403
|
+
|
|
404
|
+
if (!s) {
|
|
405
|
+
logger.error(`Skill not found: ${name}`);
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (!s.hasHandler) {
|
|
410
|
+
logger.error(
|
|
411
|
+
`Skill "${s.id}" has no handler (documentation only). Cannot execute.`,
|
|
412
|
+
);
|
|
413
|
+
process.exit(1);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (!canRunOnPlatform(s)) {
|
|
417
|
+
logger.error(`Skill "${s.id}" is not supported on ${process.platform}`);
|
|
418
|
+
process.exit(1);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const spinner = ora(`Running ${s.displayName}...`).start();
|
|
422
|
+
const input = inputParts.join(" ");
|
|
423
|
+
|
|
424
|
+
try {
|
|
425
|
+
// Load the handler
|
|
426
|
+
const handlerPath = path.join(skillsDir, s.dirName, "handler.js");
|
|
427
|
+
const imported = await import(
|
|
428
|
+
`file://${handlerPath.replace(/\\/g, "/")}`
|
|
429
|
+
);
|
|
430
|
+
const handler = imported.default || imported;
|
|
431
|
+
|
|
432
|
+
// Initialize if needed
|
|
433
|
+
if (handler.init) {
|
|
434
|
+
await handler.init(s);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Execute
|
|
438
|
+
const task = {
|
|
439
|
+
params: { input },
|
|
440
|
+
input,
|
|
441
|
+
action: input,
|
|
442
|
+
};
|
|
443
|
+
const context = {
|
|
444
|
+
projectRoot: process.cwd(),
|
|
445
|
+
workspacePath: process.cwd(),
|
|
446
|
+
workspaceRoot: process.cwd(),
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const result = await handler.execute(task, context, s);
|
|
450
|
+
spinner.stop();
|
|
451
|
+
|
|
452
|
+
if (options.json) {
|
|
453
|
+
console.log(JSON.stringify(result, null, 2));
|
|
454
|
+
} else if (result.success) {
|
|
455
|
+
logger.success(result.message || "Done");
|
|
456
|
+
if (result.result && typeof result.result === "object") {
|
|
457
|
+
// Pretty print result
|
|
458
|
+
for (const [key, val] of Object.entries(result.result)) {
|
|
459
|
+
if (typeof val === "string" && val.length > 200) {
|
|
460
|
+
logger.log(` ${chalk.cyan(key)}: ${val.substring(0, 200)}...`);
|
|
461
|
+
} else {
|
|
462
|
+
logger.log(` ${chalk.cyan(key)}: ${JSON.stringify(val)}`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
} else {
|
|
467
|
+
logger.error(
|
|
468
|
+
result.message || result.error || "Skill execution failed",
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
} catch (err) {
|
|
472
|
+
spinner.fail(`Skill execution failed: ${err.message}`);
|
|
473
|
+
if (program.opts().verbose) {
|
|
474
|
+
console.error(err.stack);
|
|
475
|
+
}
|
|
476
|
+
process.exit(1);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
}
|
package/src/index.js
CHANGED
|
@@ -8,6 +8,13 @@ import { registerServicesCommand } from "./commands/services.js";
|
|
|
8
8
|
import { registerConfigCommand } from "./commands/config.js";
|
|
9
9
|
import { registerUpdateCommand } from "./commands/update.js";
|
|
10
10
|
import { registerDoctorCommand } from "./commands/doctor.js";
|
|
11
|
+
import { registerDbCommand } from "./commands/db.js";
|
|
12
|
+
import { registerNoteCommand } from "./commands/note.js";
|
|
13
|
+
import { registerChatCommand } from "./commands/chat.js";
|
|
14
|
+
import { registerAskCommand } from "./commands/ask.js";
|
|
15
|
+
import { registerLlmCommand } from "./commands/llm.js";
|
|
16
|
+
import { registerAgentCommand } from "./commands/agent.js";
|
|
17
|
+
import { registerSkillCommand } from "./commands/skill.js";
|
|
11
18
|
|
|
12
19
|
export function createProgram() {
|
|
13
20
|
const program = new Command();
|
|
@@ -21,6 +28,7 @@ export function createProgram() {
|
|
|
21
28
|
.option("--verbose", "Enable verbose output")
|
|
22
29
|
.option("--quiet", "Suppress non-essential output");
|
|
23
30
|
|
|
31
|
+
// Existing commands
|
|
24
32
|
registerSetupCommand(program);
|
|
25
33
|
registerStartCommand(program);
|
|
26
34
|
registerStopCommand(program);
|
|
@@ -30,5 +38,14 @@ export function createProgram() {
|
|
|
30
38
|
registerUpdateCommand(program);
|
|
31
39
|
registerDoctorCommand(program);
|
|
32
40
|
|
|
41
|
+
// New headless commands
|
|
42
|
+
registerDbCommand(program);
|
|
43
|
+
registerNoteCommand(program);
|
|
44
|
+
registerChatCommand(program);
|
|
45
|
+
registerAskCommand(program);
|
|
46
|
+
registerLlmCommand(program);
|
|
47
|
+
registerAgentCommand(program);
|
|
48
|
+
registerSkillCommand(program);
|
|
49
|
+
|
|
33
50
|
return program;
|
|
34
51
|
}
|
package/src/lib/platform.js
CHANGED
|
@@ -22,3 +22,18 @@ export function isMac() {
|
|
|
22
22
|
export function isLinux() {
|
|
23
23
|
return getPlatform() === "linux";
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
export function getBinaryName(version) {
|
|
27
|
+
const p = getPlatform();
|
|
28
|
+
const a = getArch();
|
|
29
|
+
const ext = getBinaryExtension();
|
|
30
|
+
return `chainlesschain-${version}-${p}-${a}${ext}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function getBinaryExtension() {
|
|
34
|
+
const p = getPlatform();
|
|
35
|
+
if (p === "win32") return ".exe";
|
|
36
|
+
if (p === "darwin") return ".dmg";
|
|
37
|
+
if (p === "linux") return ".deb";
|
|
38
|
+
return "";
|
|
39
|
+
}
|