@ghostpaw/questlog 0.9.1 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +180 -18
- package/SKILL.md +161 -32
- package/dist/{chunk-3Y5F3SMB.js → chunk-55JAQORX.js} +245 -665
- package/dist/{questlog_skill_spec-LOQ2FNOF.js → chunk-D2W5MYNO.js} +9 -54
- package/dist/chunk-GVD6A3RR.js +108 -0
- package/dist/chunk-IL2QRKZL.js +14 -0
- package/dist/chunk-N7V7VUI5.js +982 -0
- package/dist/chunk-QYSIJ2YN.js +47 -0
- package/dist/{chunk-3B6FMHZU.js → chunk-ZHTX64KD.js} +24 -0
- package/dist/cli.js +1 -1
- package/dist/{cli_main-ELAKW6KW.js → cli_main-7AIEZV75.js} +136 -120
- package/dist/index.cjs +2046 -1393
- package/dist/index.d.cts +142 -11
- package/dist/index.d.ts +142 -11
- package/dist/index.js +2046 -1394
- package/dist/mcp-server.js +959 -386
- package/dist/mcp.cjs +810 -328
- package/dist/mcp.js +811 -329
- package/dist/open_questlog_db-LAEH7UDM.js +2 -0
- package/dist/questlog_skill_spec-HEW2B6EB.js +76 -0
- package/dist/run_hook_command-C42BQC36.js +95 -0
- package/dist/{serve_questlog_mcp-QSHMNNFT.js → serve_questlog_mcp-W3R63HW4.js} +14 -4
- package/dist/{skill_markdown-7HCMSJYR.js → skill_markdown-HXYQ3K6U.js} +34 -6
- package/package.json +6 -3
- package/skills/questlog/SKILL.md +161 -32
- package/skills/questlog/references/webhooks.md +91 -0
- package/dist/chunk-KRQDV4KF.js +0 -58
- package/dist/open_questlog_db-SH3DAU4C.js +0 -2
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// src/surfaces/schema_actions.ts
|
|
3
|
+
var requirements = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
function registerSchemaActions(schema, actions) {
|
|
5
|
+
requirements.set(schema, actions);
|
|
6
|
+
}
|
|
7
|
+
function requiredForAction(schema, action) {
|
|
8
|
+
return requirements.get(schema)?.[String(action)] ?? [];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/surfaces/schema_examples.ts
|
|
12
|
+
function minimalInput(schema, action, name = "") {
|
|
13
|
+
const constant = "const" in schema ? schema.const : void 0;
|
|
14
|
+
if (constant !== void 0) return constant;
|
|
15
|
+
if (schema.enum)
|
|
16
|
+
return action !== void 0 && schema.enum.includes(action) ? action : schema.enum[0];
|
|
17
|
+
const branch = schema.oneOf?.[0];
|
|
18
|
+
if (schema.type === "object" || schema.properties) {
|
|
19
|
+
action ??= schema.properties?.action?.enum?.[0];
|
|
20
|
+
const properties = { ...schema.properties, ...branch?.properties };
|
|
21
|
+
return Object.fromEntries(
|
|
22
|
+
[
|
|
23
|
+
.../* @__PURE__ */ new Set([
|
|
24
|
+
...schema.required ?? [],
|
|
25
|
+
...requiredForAction(schema, action),
|
|
26
|
+
...schema.properties?.outcome && ["finish", "abandon", "abandon_and_spawn_followups"].includes(String(action)) ? ["outcome"] : [],
|
|
27
|
+
...branch?.required ?? []
|
|
28
|
+
])
|
|
29
|
+
].map((key) => [
|
|
30
|
+
key,
|
|
31
|
+
minimalInput(properties[key] ?? {}, key === "action" ? action : void 0, key)
|
|
32
|
+
])
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (branch) return minimalInput(branch, action, name);
|
|
36
|
+
if (schema.type === "array") return [minimalInput(schema.items ?? {}, void 0, name)];
|
|
37
|
+
if (schema.type === "integer" || schema.type === "number")
|
|
38
|
+
return name === "anchorAt" ? 18e11 : name === "toQuestId" ? 2 : 1;
|
|
39
|
+
if (schema.type === "boolean") return false;
|
|
40
|
+
if (schema.type === "null") return null;
|
|
41
|
+
if (name === "rrule") return "FREQ=WEEKLY";
|
|
42
|
+
if (name === "title") return "Review release notes";
|
|
43
|
+
if (name === "objective") return "Check changes against the release";
|
|
44
|
+
return "release";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { minimalInput, registerSchemaActions, requiredForAction };
|
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
import { mkdirSync } from 'node:fs';
|
|
3
3
|
import { dirname } from 'node:path';
|
|
4
4
|
|
|
5
|
+
// src/hooks/init_hook_tables.ts
|
|
6
|
+
function initHookTables(db) {
|
|
7
|
+
db.exec(`
|
|
8
|
+
CREATE TABLE IF NOT EXISTS hook_targets (
|
|
9
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT, url TEXT NOT NULL, secret TEXT NOT NULL,
|
|
10
|
+
tag TEXT, events TEXT NOT NULL, min_gap INTEGER NOT NULL, created_at INTEGER NOT NULL,
|
|
11
|
+
last_wake INTEGER, lease_until INTEGER, claim TEXT);
|
|
12
|
+
CREATE TABLE IF NOT EXISTS hook_events (
|
|
13
|
+
id TEXT PRIMARY KEY, occurred_at INTEGER NOT NULL, event TEXT NOT NULL);
|
|
14
|
+
CREATE TABLE IF NOT EXISTS hook_assignments (
|
|
15
|
+
target_id INTEGER NOT NULL REFERENCES hook_targets(id) ON DELETE CASCADE,
|
|
16
|
+
event_id TEXT NOT NULL REFERENCES hook_events(id), delivery_id TEXT,
|
|
17
|
+
PRIMARY KEY(target_id, event_id));
|
|
18
|
+
CREATE TABLE IF NOT EXISTS hook_deliveries (
|
|
19
|
+
id TEXT PRIMARY KEY, target_id INTEGER NOT NULL REFERENCES hook_targets(id) ON DELETE CASCADE,
|
|
20
|
+
events TEXT NOT NULL, status TEXT NOT NULL, attempts INTEGER NOT NULL DEFAULT 0,
|
|
21
|
+
last_attempt_at INTEGER, http_status INTEGER, error_class TEXT,
|
|
22
|
+
next_attempt_at INTEGER NOT NULL);
|
|
23
|
+
CREATE INDEX IF NOT EXISTS idx_hook_deliveries_target ON hook_deliveries(target_id, status, next_attempt_at);
|
|
24
|
+
CREATE TABLE IF NOT EXISTS hook_settings (id INTEGER PRIMARY KEY CHECK(id = 1), label TEXT NOT NULL);
|
|
25
|
+
`);
|
|
26
|
+
}
|
|
27
|
+
|
|
5
28
|
// src/questlines/init_questline_tables.ts
|
|
6
29
|
function initQuestlineTables(db) {
|
|
7
30
|
db.exec(`
|
|
@@ -350,6 +373,7 @@ function initQuestlogTables(db) {
|
|
|
350
373
|
initTagTables(db);
|
|
351
374
|
initQuestSearch(db);
|
|
352
375
|
initRumorSearch(db);
|
|
376
|
+
initHookTables(db);
|
|
353
377
|
}
|
|
354
378
|
|
|
355
379
|
// src/surface/open_questlog_db.ts
|
package/dist/cli.js
CHANGED
|
@@ -1,111 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { CliUsageError, renderCliHelp, parseCliArgs } from './chunk-GVD6A3RR.js';
|
|
3
|
+
import { isErrorResult, resolveQuestlogDbPath } from './chunk-IL2QRKZL.js';
|
|
4
|
+
import { questlogSkills } from './chunk-D2W5MYNO.js';
|
|
5
|
+
import { reviewQuestlogTool, validateInput, getQuestDetail, questlogSurfaceTools } from './chunk-55JAQORX.js';
|
|
6
|
+
import { listDeliveries, questlogVersion } from './chunk-N7V7VUI5.js';
|
|
7
|
+
import { minimalInput } from './chunk-QYSIJ2YN.js';
|
|
8
|
+
import { isSea } from 'node:sea';
|
|
9
|
+
import { existsSync } from 'node:fs';
|
|
5
10
|
import { resolve } from 'node:path';
|
|
6
11
|
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
var keyOf = (name) => name.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
11
|
-
function parseCliArgs(argv, spec) {
|
|
12
|
-
const values = {};
|
|
13
|
-
const positions = [];
|
|
14
|
-
const flags = spec.flags ?? [];
|
|
15
|
-
const convert = (raw, arg) => {
|
|
16
|
-
let value = raw;
|
|
17
|
-
if (raw.startsWith("@")) {
|
|
18
|
-
try {
|
|
19
|
-
value = readFileSync(raw.slice(1), "utf8");
|
|
20
|
-
} catch {
|
|
21
|
-
throw new CliUsageError(`Cannot read value file: ${raw.slice(1)}`);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
let result = value;
|
|
25
|
-
if (arg.type === "number" || arg.type === "integer") {
|
|
26
|
-
result = Number(value);
|
|
27
|
-
if (!value.trim() || !Number.isFinite(result) || arg.type === "integer" && !Number.isInteger(result))
|
|
28
|
-
throw new CliUsageError(`Expected ${arg.type} for ${arg.name}: ${value}`);
|
|
29
|
-
} else if (arg.type === "boolean") {
|
|
30
|
-
if (!["true", "false"].includes(value))
|
|
31
|
-
throw new CliUsageError(`Expected boolean for ${arg.name}`);
|
|
32
|
-
result = value === "true";
|
|
33
|
-
} else if (arg.type === "json") {
|
|
34
|
-
try {
|
|
35
|
-
result = JSON.parse(value);
|
|
36
|
-
} catch {
|
|
37
|
-
throw new CliUsageError(`Invalid JSON for ${arg.name}`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
if (arg.enum && !arg.enum.includes(result))
|
|
41
|
-
throw new CliUsageError(`${arg.name} must be one of: ${arg.enum.join(", ")}`);
|
|
42
|
-
return result;
|
|
43
|
-
};
|
|
44
|
-
let ended = false;
|
|
45
|
-
for (let i = 0; i < argv.length; i++) {
|
|
46
|
-
const token = argv[i];
|
|
47
|
-
if (!ended && token === "--") {
|
|
48
|
-
ended = true;
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (ended || token === "-" || !token.startsWith("-")) {
|
|
52
|
-
positions.push(token);
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
const [flag, inline] = token.split(/=(.*)/s);
|
|
56
|
-
const negative = flag?.startsWith("--no-");
|
|
57
|
-
const name = flag?.replace(/^--?/, "").replace(/^no-/, "");
|
|
58
|
-
const arg = flags.find(
|
|
59
|
-
(entry) => entry.name === name || keyOf(entry.name) === name || entry.short === name
|
|
60
|
-
);
|
|
61
|
-
if (!arg) {
|
|
62
|
-
const prefix = (name ?? "").slice(0, 3);
|
|
63
|
-
const suggestion = prefix ? flags.find((entry) => entry.name.startsWith(prefix)) : void 0;
|
|
64
|
-
throw new CliUsageError(
|
|
65
|
-
`Unknown flag: ${flag}${suggestion ? `; did you mean --${suggestion.name}?` : ""}`
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
if (negative && arg.type !== "boolean")
|
|
69
|
-
throw new CliUsageError(`--no-${arg.name} requires a boolean flag`);
|
|
70
|
-
let raw = inline;
|
|
71
|
-
if (arg.type === "boolean" && raw === void 0) raw = negative ? "false" : "true";
|
|
72
|
-
if (raw === void 0) {
|
|
73
|
-
const next = argv[i + 1];
|
|
74
|
-
if (next === void 0 || next.startsWith("--"))
|
|
75
|
-
throw new CliUsageError(`Missing value: --${arg.name}`);
|
|
76
|
-
raw = next;
|
|
77
|
-
i++;
|
|
78
|
-
}
|
|
79
|
-
const key = keyOf(arg.name);
|
|
80
|
-
if (arg.type === "array") {
|
|
81
|
-
const item = convert(raw, { ...arg, type: arg.itemType ?? "string" });
|
|
82
|
-
values[key] = [...values[key] ?? [], item];
|
|
83
|
-
} else values[key] = convert(raw, arg);
|
|
84
|
-
}
|
|
85
|
-
let index = 0;
|
|
86
|
-
for (const arg of spec.positionals ?? []) {
|
|
87
|
-
if (index >= positions.length) continue;
|
|
88
|
-
values[keyOf(arg.name)] = arg.variadic ? positions.slice(index).map((value) => convert(value, { ...arg, type: arg.itemType ?? arg.type })) : convert(positions[index], arg);
|
|
89
|
-
index = arg.variadic ? positions.length : index + 1;
|
|
90
|
-
}
|
|
91
|
-
if (index < positions.length)
|
|
92
|
-
throw new CliUsageError(`Unexpected positional: ${positions[index]}`);
|
|
93
|
-
for (const arg of [...flags, ...spec.positionals ?? []]) {
|
|
94
|
-
if (arg.required && values[keyOf(arg.name)] === void 0)
|
|
95
|
-
throw new CliUsageError(`Missing required argument: ${arg.name}`);
|
|
96
|
-
}
|
|
97
|
-
return values;
|
|
12
|
+
var npmEntry = false;
|
|
13
|
+
function markNpmEntry() {
|
|
14
|
+
npmEntry = true;
|
|
98
15
|
}
|
|
99
|
-
function
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
16
|
+
function cliVersion(version, {
|
|
17
|
+
sea = isSea,
|
|
18
|
+
npm = npmEntry,
|
|
19
|
+
platform = process.platform,
|
|
20
|
+
arch = process.arch,
|
|
21
|
+
args = []
|
|
22
|
+
} = {}) {
|
|
23
|
+
let binary = false;
|
|
24
|
+
try {
|
|
25
|
+
binary = sea();
|
|
26
|
+
} catch {
|
|
27
|
+
}
|
|
28
|
+
const target = `${platform === "win32" ? "windows" : platform}-${arch}`;
|
|
29
|
+
const variant = binary ? "binary" : npm ? "npm" : "library";
|
|
30
|
+
if (args.includes("--json"))
|
|
31
|
+
return JSON.stringify({ name: "questlog", version, variant, target });
|
|
32
|
+
if (args.includes("--short")) return version;
|
|
33
|
+
return `questlog ${version} (${binary ? `binary, ${target}` : npm ? "npm" : "library"})`;
|
|
109
34
|
}
|
|
110
35
|
|
|
111
36
|
// src/surface/plain_dates.ts
|
|
@@ -189,6 +114,8 @@ function renderQuestlogPlainResult(result, db, values) {
|
|
|
189
114
|
Missing: ${r.clarification.missing.map((name) => `--${name}`).join(", ")}
|
|
190
115
|
` : `${r.summary}
|
|
191
116
|
${r.error.message}
|
|
117
|
+
`;
|
|
118
|
+
if (r.data.deliveries && !r.data.item) return `${JSON.stringify(r.data.deliveries, null, 2)}
|
|
192
119
|
`;
|
|
193
120
|
if (r.data.items) {
|
|
194
121
|
const items = values.limit === void 0 ? r.data.items : r.data.items.slice(0, Math.max(0, Number(values.limit)));
|
|
@@ -214,6 +141,11 @@ ${r.error.message}
|
|
|
214
141
|
]);
|
|
215
142
|
if (detail.outcome != null) fields.push(["outcome", detail.outcome]);
|
|
216
143
|
if (detail.questlineId != null) fields.push(["questline", detail.questlineId]);
|
|
144
|
+
for (const delivery of listDeliveries(db, { questId: detail.id }))
|
|
145
|
+
fields.push([
|
|
146
|
+
"delivery",
|
|
147
|
+
`${delivery.id} target ${delivery.targetId} ${delivery.status} attempts ${delivery.attempts}`
|
|
148
|
+
]);
|
|
217
149
|
const width = Math.max(...fields.map(([label]) => label.length));
|
|
218
150
|
return `${fields.map(([label, value]) => `${label.padEnd(width)}: ${String(value).replace(/[\r\n]+/g, " ")}`).join("\n")}
|
|
219
151
|
`;
|
|
@@ -237,12 +169,41 @@ function questlogPlainCommands() {
|
|
|
237
169
|
const id = { name: "id", type: "integer", required: true };
|
|
238
170
|
const views = reviewQuestlogTool.inputSchema.properties?.view?.enum ?? [];
|
|
239
171
|
const commands = [
|
|
172
|
+
{
|
|
173
|
+
spec: {
|
|
174
|
+
command: "tick",
|
|
175
|
+
description: "Detect events and deliver outbound webhooks once.",
|
|
176
|
+
flags: [{ name: "dry-run", type: "boolean" }]
|
|
177
|
+
},
|
|
178
|
+
tool: "manage_hooks",
|
|
179
|
+
input: (v) => ({ action: "tick", dry_run: v.dryRun ?? false }),
|
|
180
|
+
render: (r, _db, values) => {
|
|
181
|
+
const result = r;
|
|
182
|
+
if (!result.ok || !result.data) return `${result.summary}
|
|
183
|
+
`;
|
|
184
|
+
const d = result.data;
|
|
185
|
+
return `${d.events.length} events found; ${d.sent} deliveries sent, ${d.queued} queued, ${d.retrying} retrying, ${d.failed} failed.
|
|
186
|
+
${values.dryRun ? `${JSON.stringify({ events: d.events, batches: d.batches }, null, 2)}
|
|
187
|
+
` : ""}`;
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
spec: {
|
|
192
|
+
command: "review",
|
|
193
|
+
description: "Review an operational view.",
|
|
194
|
+
positionals: [{ name: "view", type: "string", required: true, enum: views }]
|
|
195
|
+
},
|
|
196
|
+
tool: "review_questlog",
|
|
197
|
+
input: (v) => ({ view: v.view }),
|
|
198
|
+
render: renderQuestlogPlainResult
|
|
199
|
+
},
|
|
240
200
|
{
|
|
241
201
|
spec: {
|
|
242
202
|
command: "add",
|
|
243
|
-
description:
|
|
203
|
+
description: 'Create a quest: add "My title" or add --title "My title". Objective defaults to the title.',
|
|
204
|
+
positionals: [{ name: "positionalTitle", type: "string" }],
|
|
244
205
|
flags: [
|
|
245
|
-
{ name: "title", type: "string"
|
|
206
|
+
{ name: "title", type: "string" },
|
|
246
207
|
{ name: "tag", type: "array" },
|
|
247
208
|
{ name: "objective", type: "string" },
|
|
248
209
|
{
|
|
@@ -255,9 +216,15 @@ function questlogPlainCommands() {
|
|
|
255
216
|
},
|
|
256
217
|
tool: "shape_work",
|
|
257
218
|
input: (v) => {
|
|
219
|
+
if (v.title !== void 0 && v.positionalTitle !== void 0)
|
|
220
|
+
throw new CliUsageError(
|
|
221
|
+
"give the title once: either as the first argument or with --title"
|
|
222
|
+
);
|
|
223
|
+
const title = v.positionalTitle ?? v.title;
|
|
224
|
+
if (title === void 0) throw new CliUsageError("Missing required argument: title");
|
|
258
225
|
const quest = {
|
|
259
|
-
title
|
|
260
|
-
objective: v.objective ??
|
|
226
|
+
title,
|
|
227
|
+
objective: v.objective ?? title
|
|
261
228
|
};
|
|
262
229
|
if (v.tag !== void 0)
|
|
263
230
|
quest.tags = v.tag.flatMap(
|
|
@@ -342,12 +309,13 @@ function selectedDbPath(options) {
|
|
|
342
309
|
var questlogCliOptions = {
|
|
343
310
|
bin: "ghostpaw-questlog",
|
|
344
311
|
version: questlogVersion,
|
|
312
|
+
versionLine: (args) => cliVersion(questlogVersion, { args }),
|
|
345
313
|
description: "Local task and calendar library",
|
|
346
314
|
tools: questlogSurfaceTools,
|
|
347
315
|
describeDb: (options) => `Database: ${selectedDbPath(options)}
|
|
348
316
|
Use --db <path> or GHOSTPAW_QUESTLOG_DB to override the default ~/.ghostpaw/questlog.db.`,
|
|
349
317
|
openContext: async (options) => {
|
|
350
|
-
const { openQuestlogDb } = await import('./open_questlog_db-
|
|
318
|
+
const { openQuestlogDb } = await import('./open_questlog_db-LAEH7UDM.js');
|
|
351
319
|
const path = selectedDbPath(options);
|
|
352
320
|
const created = path !== ":memory:" && !existsSync(path);
|
|
353
321
|
const db = await openQuestlogDb(path);
|
|
@@ -357,14 +325,24 @@ Use --db <path> or GHOSTPAW_QUESTLOG_DB to override the default ~/.ghostpaw/ques
|
|
|
357
325
|
},
|
|
358
326
|
closeContext: (db) => db.close(),
|
|
359
327
|
commands: {
|
|
328
|
+
names: ["hook", "watch"],
|
|
329
|
+
run: async (command, args, options, io) => {
|
|
330
|
+
if (command !== "hook" && command !== "watch") return void 0;
|
|
331
|
+
const { runHookCommand } = await import('./run_hook_command-C42BQC36.js');
|
|
332
|
+
return runHookCommand(command, args, io, async () => {
|
|
333
|
+
const { openQuestlogDb } = await import('./open_questlog_db-LAEH7UDM.js');
|
|
334
|
+
return openQuestlogDb(selectedDbPath(options));
|
|
335
|
+
});
|
|
336
|
+
},
|
|
360
337
|
plain: questlogPlainCommands(),
|
|
338
|
+
guides: questlogSkills,
|
|
361
339
|
mcp: async (options) => {
|
|
362
|
-
const { serveQuestlogMcp } = await import('./serve_questlog_mcp-
|
|
340
|
+
const { serveQuestlogMcp } = await import('./serve_questlog_mcp-W3R63HW4.js');
|
|
363
341
|
await serveQuestlogMcp(options.db === void 0 ? {} : { dbPath: options.db });
|
|
364
342
|
},
|
|
365
343
|
skill: async () => {
|
|
366
|
-
const { renderSkillMarkdown } = await import('./skill_markdown-
|
|
367
|
-
const { questlogSkillSpec } = await import('./questlog_skill_spec-
|
|
344
|
+
const { renderSkillMarkdown } = await import('./skill_markdown-HXYQ3K6U.js');
|
|
345
|
+
const { questlogSkillSpec } = await import('./questlog_skill_spec-HEW2B6EB.js');
|
|
368
346
|
return renderSkillMarkdown(questlogSkillSpec);
|
|
369
347
|
}
|
|
370
348
|
}
|
|
@@ -433,6 +411,12 @@ async function runToolCli(argv, options) {
|
|
|
433
411
|
i--;
|
|
434
412
|
}
|
|
435
413
|
}
|
|
414
|
+
const versionArgs = args.filter((arg) => arg !== "--short" && arg !== "--json");
|
|
415
|
+
if (versionArgs[0] === "--version" || versionArgs[0] === "-v") {
|
|
416
|
+
stdout.write(`${options.versionLine?.(args) ?? options.version}
|
|
417
|
+
`);
|
|
418
|
+
return 0;
|
|
419
|
+
}
|
|
436
420
|
const ctxOptions = db === void 0 ? {} : { db };
|
|
437
421
|
const command = args.shift() ?? "help";
|
|
438
422
|
const normalize = (name2) => name2.replace(/[-_]/g, "").toLowerCase();
|
|
@@ -442,8 +426,14 @@ async function runToolCli(argv, options) {
|
|
|
442
426
|
${options.description}
|
|
443
427
|
|
|
444
428
|
Usage: ${options.bin} [--db path] <command>
|
|
445
|
-
Commands: ${
|
|
429
|
+
Commands: ${[
|
|
430
|
+
...options.commands?.plain?.map((entry) => entry.spec.command) ?? [],
|
|
431
|
+
...options.commands?.names ?? []
|
|
432
|
+
].map((name2) => `${name2}, `).join(
|
|
433
|
+
""
|
|
434
|
+
)}tools [--json], describe <tool>, call <tool> [JSON|-], <tool> --parameter value, mcp, skill
|
|
446
435
|
Options: --help, --version, --compact, --db <path>
|
|
436
|
+
Version formats: --version --short, --version --json
|
|
447
437
|
${options.describeDb ? `${options.describeDb(ctxOptions)}
|
|
448
438
|
` : ""}
|
|
449
439
|
${options.tools.map((tool2) => `${tool2.name} ${tool2.description}`).join("\n")}
|
|
@@ -451,11 +441,6 @@ ${options.tools.map((tool2) => `${tool2.name} ${tool2.description}`).join("\n")
|
|
|
451
441
|
);
|
|
452
442
|
return 0;
|
|
453
443
|
}
|
|
454
|
-
if (["--version", "-v"].includes(command)) {
|
|
455
|
-
stdout.write(`${options.version}
|
|
456
|
-
`);
|
|
457
|
-
return 0;
|
|
458
|
-
}
|
|
459
444
|
if (command === "tools") {
|
|
460
445
|
stdout.write(
|
|
461
446
|
args.includes("--json") ? `${JSON.stringify(
|
|
@@ -470,7 +455,26 @@ ${options.tools.map((tool2) => `${tool2.name} ${tool2.description}`).join("\n")
|
|
|
470
455
|
}
|
|
471
456
|
if (command === "skill") {
|
|
472
457
|
if (!options.commands?.skill) return fail("skill command unavailable");
|
|
473
|
-
|
|
458
|
+
if (args.length > 1) return fail(`Usage: ${options.bin} skill [--list | <guide> | --help]`);
|
|
459
|
+
const guide = args[0];
|
|
460
|
+
if (guide === "--help" || guide === "-h") {
|
|
461
|
+
stdout.write(
|
|
462
|
+
`Usage:
|
|
463
|
+
${options.bin} skill
|
|
464
|
+
${options.bin} skill --list
|
|
465
|
+
${options.bin} skill <guide>
|
|
466
|
+
`
|
|
467
|
+
);
|
|
468
|
+
} else if (guide === "--list") {
|
|
469
|
+
stdout.write(
|
|
470
|
+
`${(options.commands.guides ?? []).map((entry) => `${entry.name} ${entry.description.replace(/\s+/g, " ")}`).join("\n")}
|
|
471
|
+
`
|
|
472
|
+
);
|
|
473
|
+
} else if (guide) {
|
|
474
|
+
const entry = options.commands.guides?.find((entry2) => entry2.name === guide);
|
|
475
|
+
if (!entry) return fail(`unknown guide ${guide}; run ${options.bin} skill --list`);
|
|
476
|
+
stdout.write(entry.content);
|
|
477
|
+
} else stdout.write(await options.commands.skill());
|
|
474
478
|
return 0;
|
|
475
479
|
}
|
|
476
480
|
if (command === "mcp") {
|
|
@@ -478,6 +482,12 @@ ${options.tools.map((tool2) => `${tool2.name} ${tool2.description}`).join("\n")
|
|
|
478
482
|
await options.commands.mcp(ctxOptions);
|
|
479
483
|
return 0;
|
|
480
484
|
}
|
|
485
|
+
const custom = await options.commands?.run?.(command, args, ctxOptions, {
|
|
486
|
+
stdin,
|
|
487
|
+
stdout,
|
|
488
|
+
stderr
|
|
489
|
+
});
|
|
490
|
+
if (custom !== void 0) return custom;
|
|
481
491
|
const plain = options.commands?.plain?.find((entry) => entry.spec.command === command);
|
|
482
492
|
let plainValues = {};
|
|
483
493
|
if (plain) {
|
|
@@ -507,6 +517,7 @@ ${options.tools.map((tool2) => `${tool2.name} ${tool2.description}`).join("\n")
|
|
|
507
517
|
if (command === "call" || command === "describe") return fail(`Unknown tool: ${name ?? ""}`);
|
|
508
518
|
const suggestion = suggestCommand(command, [
|
|
509
519
|
...options.commands?.plain?.map((entry) => entry.spec.command) ?? [],
|
|
520
|
+
...options.commands?.names ?? [],
|
|
510
521
|
"help",
|
|
511
522
|
"tools",
|
|
512
523
|
"describe",
|
|
@@ -599,7 +610,11 @@ ${JSON.stringify(tool.inputSchema, null, 2)}
|
|
|
599
610
|
return fail(`Invalid input: ${cause instanceof Error ? cause.message : String(cause)}`, 1);
|
|
600
611
|
}
|
|
601
612
|
const errors = validateInput(tool.inputSchema, input);
|
|
602
|
-
if (errors.length)
|
|
613
|
+
if (errors.length)
|
|
614
|
+
return fail(
|
|
615
|
+
`${errors.join("\n")}; pass ${JSON.stringify(minimalInput(tool.inputSchema, input?.action))}`,
|
|
616
|
+
1
|
|
617
|
+
);
|
|
603
618
|
let context;
|
|
604
619
|
try {
|
|
605
620
|
context = await options.openContext(ctxOptions);
|
|
@@ -623,4 +638,5 @@ ${JSON.stringify(tool.inputSchema, null, 2)}
|
|
|
623
638
|
}
|
|
624
639
|
|
|
625
640
|
// src/cli_main.ts
|
|
641
|
+
markNpmEntry();
|
|
626
642
|
process.exitCode = await runToolCli(process.argv.slice(2), questlogCliOptions);
|