codiedev 0.9.83 → 0.9.85
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/cli.d.ts +1 -0
- package/dist/cli.js +36 -13
- package/dist/commands/inbox.js +9 -0
- package/dist/commands/pack.d.ts +5 -2
- package/dist/commands/pack.js +129 -10
- package/dist/commands/ping.js +11 -7
- package/dist/commands/pingFiles.d.ts +22 -0
- package/dist/commands/pingFiles.js +155 -0
- package/dist/commands/reply.d.ts +5 -0
- package/dist/commands/reply.js +54 -0
- package/dist/commands/shared.d.ts +11 -0
- package/dist/commands/shared.js +57 -0
- package/dist/hook.js +28 -8
- package/dist/inboxFormat.d.ts +10 -0
- package/dist/inboxFormat.js +18 -1
- package/dist/mcp.js +1 -48
- package/dist/packs/files.d.ts +46 -0
- package/dist/packs/files.js +307 -0
- package/dist/packs/harnesses.js +4 -3
- package/dist/packs/install.d.ts +4 -0
- package/dist/packs/install.js +35 -4
- package/dist/packs/manifest.d.ts +4 -0
- package/dist/packs/manifest.js +53 -5
- package/dist/packs/store.d.ts +13 -6
- package/dist/packs/store.js +23 -4
- package/dist/teamSkillContext.d.ts +1 -1
- package/dist/teamSkillContext.js +1 -1
- package/dist/unreadNotice.d.ts +22 -0
- package/dist/unreadNotice.js +81 -0
- package/dist/utils.js +5 -4
- package/package.json +1 -1
- package/dist/commands/ask.d.ts +0 -1
- package/dist/commands/ask.js +0 -58
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runReply = runReply;
|
|
4
|
+
const shared_1 = require("./shared");
|
|
5
|
+
const pingFiles_1 = require("./pingFiles");
|
|
6
|
+
const USAGE = 'Usage: codiedev reply <ping-id> "<message>" [--file <path>]';
|
|
7
|
+
/**
|
|
8
|
+
* Answer a ping. Unlike a fresh `codiedev ping` back, this threads under the
|
|
9
|
+
* original and marks it read + replied, so it stops counting as waiting.
|
|
10
|
+
*/
|
|
11
|
+
async function runReply(args) {
|
|
12
|
+
const { files, rest } = (0, pingFiles_1.takeFileArgs)(args);
|
|
13
|
+
const positional = rest.filter((a) => !a.startsWith("--"));
|
|
14
|
+
const pingId = positional[0];
|
|
15
|
+
// Join the rest in case the shell split the message despite quoting.
|
|
16
|
+
const body = (0, shared_1.stripQuotes)(positional.slice(1).join(" "));
|
|
17
|
+
if (!pingId || (!body && files.length === 0)) {
|
|
18
|
+
console.error(USAGE);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const config = (0, shared_1.requireConfig)();
|
|
22
|
+
const start = Date.now();
|
|
23
|
+
try {
|
|
24
|
+
const attachmentIds = await (0, pingFiles_1.uploadOrExit)(config, files);
|
|
25
|
+
const res = await (0, shared_1.apiRequest)("POST", "/api/cli/reply", {
|
|
26
|
+
config,
|
|
27
|
+
body: { pingId, body, ...(attachmentIds.length ? { attachmentIds } : {}) },
|
|
28
|
+
});
|
|
29
|
+
(0, shared_1.recordAgentEvent)(config, {
|
|
30
|
+
tool: "codiedev_reply",
|
|
31
|
+
ok: true,
|
|
32
|
+
latencyMs: Date.now() - start,
|
|
33
|
+
});
|
|
34
|
+
const to = res.recipient ? `${res.recipient.name} <${res.recipient.email}>` : "them";
|
|
35
|
+
const withFiles = files.length ? ` with ${files.length} file${files.length === 1 ? "" : "s"}` : "";
|
|
36
|
+
console.log(`✓ Replied to ${to}${withFiles}`);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
const e = err;
|
|
40
|
+
(0, shared_1.recordAgentEvent)(config, {
|
|
41
|
+
tool: "codiedev_reply",
|
|
42
|
+
ok: false,
|
|
43
|
+
latencyMs: Date.now() - start,
|
|
44
|
+
error: e.message,
|
|
45
|
+
});
|
|
46
|
+
if (e.status === 404 && /ping not found/.test(e.message)) {
|
|
47
|
+
console.error(`No ping ${pingId} addressed to you. Run 'codiedev inbox' for the ids.`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
console.error(`Reply failed: ${e.message}`);
|
|
51
|
+
}
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -21,6 +21,17 @@ export declare function apiRequest<T = unknown>(method: "GET" | "POST", path: st
|
|
|
21
21
|
body?: Record<string, unknown>;
|
|
22
22
|
query?: Record<string, string | number | undefined>;
|
|
23
23
|
}): Promise<T>;
|
|
24
|
+
/**
|
|
25
|
+
* Binary twin of apiRequest: GET returns the raw body; POST sends `body` as
|
|
26
|
+
* application/octet-stream and returns parsed JSON. Same auth and errors
|
|
27
|
+
* (`status` on non-2xx). Used for starter-pack files (SIG-127).
|
|
28
|
+
*/
|
|
29
|
+
export declare function apiRequestBytes(method: "GET" | "POST", path: string, options: {
|
|
30
|
+
config: CodiedevConfig;
|
|
31
|
+
body?: Buffer;
|
|
32
|
+
query?: Record<string, string | number | undefined>;
|
|
33
|
+
maxBytes?: number;
|
|
34
|
+
}): Promise<Buffer>;
|
|
24
35
|
/**
|
|
25
36
|
* Fire-and-forget per-invocation telemetry from CLI subcommands. Mirrors
|
|
26
37
|
* the MCP server's emitTelemetry but stamps `source: "cli"` so adoption
|
package/dist/commands/shared.js
CHANGED
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.applyConfigEnvOverrides = applyConfigEnvOverrides;
|
|
37
37
|
exports.requireConfig = requireConfig;
|
|
38
38
|
exports.apiRequest = apiRequest;
|
|
39
|
+
exports.apiRequestBytes = apiRequestBytes;
|
|
39
40
|
exports.recordAgentEvent = recordAgentEvent;
|
|
40
41
|
exports.recordInstallEvent = recordInstallEvent;
|
|
41
42
|
exports.withTelemetry = withTelemetry;
|
|
@@ -135,6 +136,62 @@ function apiRequest(method, path, options) {
|
|
|
135
136
|
req.end();
|
|
136
137
|
});
|
|
137
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Binary twin of apiRequest: GET returns the raw body; POST sends `body` as
|
|
141
|
+
* application/octet-stream and returns parsed JSON. Same auth and errors
|
|
142
|
+
* (`status` on non-2xx). Used for starter-pack files (SIG-127).
|
|
143
|
+
*/
|
|
144
|
+
function apiRequestBytes(method, path, options) {
|
|
145
|
+
return new Promise((resolve, reject) => {
|
|
146
|
+
const url = new URL(path, options.config.backendUrl);
|
|
147
|
+
for (const [k, v] of Object.entries(options.query ?? {})) {
|
|
148
|
+
if (v !== undefined)
|
|
149
|
+
url.searchParams.set(k, String(v));
|
|
150
|
+
}
|
|
151
|
+
const lib = url.protocol === "https:" ? https : http;
|
|
152
|
+
const req = lib.request({
|
|
153
|
+
hostname: url.hostname,
|
|
154
|
+
port: url.port || (url.protocol === "https:" ? 443 : 80),
|
|
155
|
+
path: url.pathname + url.search,
|
|
156
|
+
method,
|
|
157
|
+
headers: {
|
|
158
|
+
Authorization: `Bearer ${options.config.token}`,
|
|
159
|
+
"X-Codiedev-Cli-Version": version_1.CLI_VERSION,
|
|
160
|
+
...(options.body ? { "Content-Type": "application/octet-stream", "Content-Length": options.body.length } : {}),
|
|
161
|
+
},
|
|
162
|
+
}, (res) => {
|
|
163
|
+
const chunks = [];
|
|
164
|
+
let size = 0;
|
|
165
|
+
res.on("data", (c) => {
|
|
166
|
+
size += c.length;
|
|
167
|
+
if (options.maxBytes !== undefined && size > options.maxBytes) {
|
|
168
|
+
req.destroy(Object.assign(new Error(`response over ${options.maxBytes} bytes`), { status: res.statusCode }));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
chunks.push(c);
|
|
172
|
+
});
|
|
173
|
+
res.on("end", () => {
|
|
174
|
+
const buf = Buffer.concat(chunks);
|
|
175
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300)
|
|
176
|
+
return resolve(buf);
|
|
177
|
+
let message = `HTTP ${res.statusCode}`;
|
|
178
|
+
try {
|
|
179
|
+
const parsed = JSON.parse(buf.toString("utf8"));
|
|
180
|
+
if (parsed && typeof parsed.error === "string")
|
|
181
|
+
message = parsed.error;
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// not JSON
|
|
185
|
+
}
|
|
186
|
+
reject(Object.assign(new Error(message), { status: res.statusCode }));
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
req.on("error", reject);
|
|
190
|
+
if (options.body)
|
|
191
|
+
req.write(options.body);
|
|
192
|
+
req.end();
|
|
193
|
+
});
|
|
194
|
+
}
|
|
138
195
|
/**
|
|
139
196
|
* Fire-and-forget per-invocation telemetry from CLI subcommands. Mirrors
|
|
140
197
|
* the MCP server's emitTelemetry but stamps `source: "cli"` so adoption
|
package/dist/hook.js
CHANGED
|
@@ -54,6 +54,7 @@ const selfUpdate_1 = require("./selfUpdate");
|
|
|
54
54
|
const version_1 = require("./version");
|
|
55
55
|
// --- starter packs (SIG-113 phase 2): autosync, nudge ---
|
|
56
56
|
const sync_1 = require("./packs/sync");
|
|
57
|
+
const unreadNotice_1 = require("./unreadNotice");
|
|
57
58
|
// --- end starter packs ---
|
|
58
59
|
const captureLog_1 = require("./captureLog");
|
|
59
60
|
const subagents_1 = require("./subagents");
|
|
@@ -267,25 +268,28 @@ async function postSessionSkip(config, sessionId, reason) {
|
|
|
267
268
|
}
|
|
268
269
|
// SessionStart: the capture-coverage denominator. Record that a session opened
|
|
269
270
|
// so the backend can later compute captured ÷ started. Deliberately tiny — no
|
|
270
|
-
// transcript read, no git — so it never slows session startup. Best-effort
|
|
271
|
-
//
|
|
271
|
+
// transcript read, no git — so it never slows session startup. Best-effort.
|
|
272
|
+
// The response may carry the person's unread-ping summary, which the caller
|
|
273
|
+
// turns into a one-line notice; it rides on this request so the reminder
|
|
274
|
+
// costs no extra round trip.
|
|
272
275
|
async function runSessionStart(hookInput, source = "claude-code") {
|
|
273
276
|
const config = (0, utils_1.readConfig)();
|
|
274
277
|
if (!config)
|
|
275
|
-
return;
|
|
278
|
+
return undefined;
|
|
276
279
|
if (hookInput.session_id) {
|
|
277
280
|
try {
|
|
278
|
-
await
|
|
281
|
+
const res = await postJsonForResponse(`${config.backendUrl}/api/cli/sessionStart`, {
|
|
279
282
|
sessionId: hookInput.session_id,
|
|
280
283
|
startedAt: Date.now(),
|
|
281
284
|
source,
|
|
282
285
|
}, config.token, PING_TIMEOUT_MS);
|
|
286
|
+
return res?.unreadPings;
|
|
283
287
|
}
|
|
284
288
|
catch {
|
|
285
289
|
/* best-effort — never block session startup */
|
|
286
290
|
}
|
|
287
291
|
}
|
|
288
|
-
return;
|
|
292
|
+
return undefined;
|
|
289
293
|
}
|
|
290
294
|
function postJson(url, body, bearerToken,
|
|
291
295
|
// Socket timeout. Pass a SMALL value for tiny pings that run on (or near)
|
|
@@ -337,7 +341,10 @@ timeoutMs) {
|
|
|
337
341
|
// postJson variant that returns the parsed response body — the upload-URL
|
|
338
342
|
// handshake needs the { uploadUrl } the server hands back. No timeout: runs
|
|
339
343
|
// only in detached uploader children, never on the shutdown clock.
|
|
340
|
-
function postJsonForResponse(url, body, bearerToken
|
|
344
|
+
function postJsonForResponse(url, body, bearerToken,
|
|
345
|
+
// Socket timeout for tiny pings on the session-start path: a black-holed
|
|
346
|
+
// connection must not hang the hook. Unset = no timeout, as before.
|
|
347
|
+
timeoutMs) {
|
|
341
348
|
return new Promise((resolve, reject) => {
|
|
342
349
|
const parsed = new URL(url);
|
|
343
350
|
const data = JSON.stringify(body);
|
|
@@ -371,6 +378,11 @@ function postJsonForResponse(url, body, bearerToken) {
|
|
|
371
378
|
});
|
|
372
379
|
});
|
|
373
380
|
req.on("error", reject);
|
|
381
|
+
if (timeoutMs) {
|
|
382
|
+
req.setTimeout(timeoutMs, () => {
|
|
383
|
+
req.destroy(new Error(`timeout after ${timeoutMs}ms`));
|
|
384
|
+
});
|
|
385
|
+
}
|
|
374
386
|
req.write(data);
|
|
375
387
|
req.end();
|
|
376
388
|
});
|
|
@@ -658,7 +670,7 @@ async function main() {
|
|
|
658
670
|
if (packNag && process.argv[2] === "session-start")
|
|
659
671
|
process.stdout.write(`${packNag}\n`);
|
|
660
672
|
// --- end starter packs ---
|
|
661
|
-
await runSessionStart(hookInput, process.argv[2] === "session-start-opencode"
|
|
673
|
+
const unreadPings = await runSessionStart(hookInput, process.argv[2] === "session-start-opencode"
|
|
662
674
|
? "opencode"
|
|
663
675
|
: process.argv[2] === "session-start-cursor"
|
|
664
676
|
? "cursor"
|
|
@@ -667,13 +679,21 @@ async function main() {
|
|
|
667
679
|
: process.argv[2] === "session-start-pi"
|
|
668
680
|
? "pi"
|
|
669
681
|
: "claude-code");
|
|
682
|
+
// "A teammate is waiting on you." Claude Code reads SessionStart stdout
|
|
683
|
+
// into the session; Cursor and Copilot get it in their context below.
|
|
684
|
+
// OpenCode and pi ignore this hook's output, and Codex has no SessionStart.
|
|
685
|
+
const unreadNotice = process.argv[2] === "session-start-opencode" || process.argv[2] === "session-start-pi"
|
|
686
|
+
? null
|
|
687
|
+
: (0, unreadNotice_1.takeUnreadNotice)(unreadPings);
|
|
688
|
+
if (unreadNotice && process.argv[2] === "session-start")
|
|
689
|
+
process.stdout.write(`${unreadNotice}\n`);
|
|
670
690
|
if ((0, utils_1.readConfig)() && (process.argv[2] === "session-start-cursor" || process.argv[2] === "session-start-copilot")) {
|
|
671
691
|
const source = process.argv[2] === "session-start-cursor" ? "cursor" : "copilot";
|
|
672
692
|
const { TEAM_SKILL_CONTEXT, teamSkillSessionOutput } = await Promise.resolve().then(() => __importStar(require("./teamSkillContext")));
|
|
673
693
|
const prompt = hookInput.initialPrompt
|
|
674
694
|
?? hookInput.initial_prompt;
|
|
675
695
|
const discovered = typeof prompt === "string" ? await discoverContext(prompt) : null;
|
|
676
|
-
process.stdout.write(JSON.stringify(teamSkillSessionOutput(source, [TEAM_SKILL_CONTEXT, discovered, packNag].filter(Boolean).join("\n\n"))));
|
|
696
|
+
process.stdout.write(JSON.stringify(teamSkillSessionOutput(source, [TEAM_SKILL_CONTEXT, discovered, packNag, unreadNotice].filter(Boolean).join("\n\n"))));
|
|
677
697
|
}
|
|
678
698
|
return;
|
|
679
699
|
}
|
package/dist/inboxFormat.d.ts
CHANGED
|
@@ -11,7 +11,17 @@ export interface InboxPing {
|
|
|
11
11
|
title?: string;
|
|
12
12
|
} | null;
|
|
13
13
|
preview: string;
|
|
14
|
+
attachments?: Array<{
|
|
15
|
+
name: string;
|
|
16
|
+
size: number;
|
|
17
|
+
url?: string | null;
|
|
18
|
+
}>;
|
|
14
19
|
}
|
|
20
|
+
/** "shard-timings.csv (66 B), trace.log (1.2 MB)" */
|
|
21
|
+
export declare function fileList(files: Array<{
|
|
22
|
+
name: string;
|
|
23
|
+
size: number;
|
|
24
|
+
}>): string;
|
|
15
25
|
export interface InboxMention {
|
|
16
26
|
mentionId: string;
|
|
17
27
|
createdAt: number;
|
package/dist/inboxFormat.js
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fileList = fileList;
|
|
3
4
|
exports.formatInbox = formatInbox;
|
|
4
5
|
const shared_1 = require("./commands/shared");
|
|
6
|
+
function fileSize(bytes) {
|
|
7
|
+
if (bytes < 1024)
|
|
8
|
+
return `${bytes} B`;
|
|
9
|
+
if (bytes < 1024 * 1024)
|
|
10
|
+
return `${Math.round(bytes / 1024)} KB`;
|
|
11
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
12
|
+
}
|
|
13
|
+
/** "shard-timings.csv (66 B), trace.log (1.2 MB)" */
|
|
14
|
+
function fileList(files) {
|
|
15
|
+
return files.map((f) => `${f.name} (${fileSize(f.size)})`).join(", ");
|
|
16
|
+
}
|
|
5
17
|
/**
|
|
6
18
|
* Render the inbox for BOTH surfaces — `codiedev inbox` and the MCP
|
|
7
19
|
* `codiedev_inbox` tool an agent calls for "any messages?". Shared so the
|
|
@@ -47,6 +59,9 @@ function formatInbox(opts) {
|
|
|
47
59
|
const where = p.subjectKey ? ` · on ${p.subjectKey}` : "";
|
|
48
60
|
lines.push(`${dot} ${from}${where} · ${(0, shared_1.timeAgo)(p.createdAt)}`);
|
|
49
61
|
lines.push(` ${p.preview || "(no body)"}`);
|
|
62
|
+
if (p.attachments?.length) {
|
|
63
|
+
lines.push(` ${p.attachments.length === 1 ? "file" : "files"}: ${fileList(p.attachments)}`);
|
|
64
|
+
}
|
|
50
65
|
lines.push(` id: ${p.pingId}`);
|
|
51
66
|
lines.push("");
|
|
52
67
|
}
|
|
@@ -63,7 +78,9 @@ function formatInbox(opts) {
|
|
|
63
78
|
lines.push(` ${m.url}`);
|
|
64
79
|
lines.push("");
|
|
65
80
|
}
|
|
66
|
-
if (keptPings.length > 0)
|
|
81
|
+
if (keptPings.length > 0) {
|
|
67
82
|
lines.push("Mark read: codiedev read <ping-id>");
|
|
83
|
+
lines.push('Reply: codiedev reply <ping-id> "<message>"');
|
|
84
|
+
}
|
|
68
85
|
return lines;
|
|
69
86
|
}
|
package/dist/mcp.js
CHANGED
|
@@ -291,9 +291,7 @@ const TOOLS = [
|
|
|
291
291
|
"when the user asks 'has anyone solved X?', 'find me the spec on Y', " +
|
|
292
292
|
"or 'look for prior work on Z'. Always search before pushing a new " +
|
|
293
293
|
"artifact so the user doesn't duplicate work. Returns ranked hits " +
|
|
294
|
-
"with title, type, filename key, and a snippet.
|
|
295
|
-
"questions where the user wants a synthesized answer (not just a list " +
|
|
296
|
-
"of hits), prefer codiedev_ask instead.",
|
|
294
|
+
"with title, type, filename key, and a snippet.",
|
|
297
295
|
inputSchema: {
|
|
298
296
|
type: "object",
|
|
299
297
|
properties: {
|
|
@@ -303,32 +301,6 @@ const TOOLS = [
|
|
|
303
301
|
required: ["query"],
|
|
304
302
|
},
|
|
305
303
|
},
|
|
306
|
-
{
|
|
307
|
-
name: "codiedev_ask",
|
|
308
|
-
description: "Ask a natural-language question of the team's collective memory and " +
|
|
309
|
-
"get a synthesized answer with citations. Searches across both " +
|
|
310
|
-
"auto-extracted artifacts (specs / decisions / bugfixes) AND raw " +
|
|
311
|
-
"session transcripts, then has an LLM compose an answer that cites " +
|
|
312
|
-
"the actual sources. Use when the user asks 'why did we…?', 'what " +
|
|
313
|
-
"does the team know about X?', 'has anyone done Y here before?', " +
|
|
314
|
-
"'who decided Z and why?', or any open-ended question about prior " +
|
|
315
|
-
"work where they want context, not just a hit list. For exact-match " +
|
|
316
|
-
"lookups (find me spec-x), prefer codiedev_pull. For browsing or " +
|
|
317
|
-
"filtering by kind/tag, prefer codiedev_get_library. For surfacing " +
|
|
318
|
-
"ranked hits without LLM synthesis, prefer codiedev_search.",
|
|
319
|
-
inputSchema: {
|
|
320
|
-
type: "object",
|
|
321
|
-
properties: {
|
|
322
|
-
question: {
|
|
323
|
-
type: "string",
|
|
324
|
-
description: "Natural-language question. Open-ended is fine — e.g. 'why " +
|
|
325
|
-
"did we drop pg-bouncer?' or 'how does the team think about " +
|
|
326
|
-
"feature flags?'.",
|
|
327
|
-
},
|
|
328
|
-
},
|
|
329
|
-
required: ["question"],
|
|
330
|
-
},
|
|
331
|
-
},
|
|
332
304
|
{
|
|
333
305
|
name: "codiedev_get_library",
|
|
334
306
|
description: "List artifacts in the team library — both docs (spec / decision / " +
|
|
@@ -467,8 +439,6 @@ async function dispatchTool(name, args, config) {
|
|
|
467
439
|
return await handleShareWith(args, config);
|
|
468
440
|
case "codiedev_send_to":
|
|
469
441
|
return await handleSendTo(args, config);
|
|
470
|
-
case "codiedev_ask":
|
|
471
|
-
return await handleAsk(args, config);
|
|
472
442
|
case "codiedev_search":
|
|
473
443
|
return await handleSearch(args, config);
|
|
474
444
|
case "codiedev_get_library":
|
|
@@ -740,23 +710,6 @@ async function handleSearch(args, config) {
|
|
|
740
710
|
}
|
|
741
711
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
742
712
|
}
|
|
743
|
-
async function handleAsk(args, config) {
|
|
744
|
-
const question = asString(args.question);
|
|
745
|
-
if (!question)
|
|
746
|
-
throw new Error("question required");
|
|
747
|
-
const res = await (0, shared_1.apiRequest)("POST", "/api/cli/ask", { config, body: { question } });
|
|
748
|
-
const lines = [res.answer];
|
|
749
|
-
if (res.citations && res.citations.length > 0) {
|
|
750
|
-
lines.push("", "Sources:");
|
|
751
|
-
for (const c of res.citations) {
|
|
752
|
-
const label = c.kind === "artifact"
|
|
753
|
-
? `[${c.type}] ${c.title}`
|
|
754
|
-
: `[session] ${c.title}`;
|
|
755
|
-
lines.push(` • ${label}`);
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
759
|
-
}
|
|
760
713
|
async function handleGetLibrary(args, config) {
|
|
761
714
|
const scope = asStringOrUndefined(args.scope);
|
|
762
715
|
const type = asStringOrUndefined(args.type);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export declare const PACK_MAX_FILES = 500;
|
|
2
|
+
export declare const PACK_FILE_MAX_BYTES: number;
|
|
3
|
+
export declare const PACK_FILES_MAX_TOTAL_BYTES: number;
|
|
4
|
+
export declare const SHA256_HEX: RegExp;
|
|
5
|
+
export interface PackBundleFile {
|
|
6
|
+
path: string;
|
|
7
|
+
sha256: string;
|
|
8
|
+
size: number;
|
|
9
|
+
executable?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/** Why a plugin-relative path can't be written, or null when it's fine. */
|
|
12
|
+
export declare function packFilePathProblem(raw: unknown): string | null;
|
|
13
|
+
export declare function blobsDir(root: string): string;
|
|
14
|
+
export declare function blobPath(dir: string, sha256: string): string;
|
|
15
|
+
export declare function sha256Of(buf: Buffer): string;
|
|
16
|
+
/** Download every blob the manifest needs that isn't cached yet. Each body
|
|
17
|
+
* must match its sha256 and size or nothing is kept. */
|
|
18
|
+
export declare function ensureBlobs(dir: string, files: PackBundleFile[], download: (file: PackBundleFile) => Promise<Buffer>): Promise<{
|
|
19
|
+
downloaded: number;
|
|
20
|
+
}>;
|
|
21
|
+
export interface FilesEntry {
|
|
22
|
+
dest: string;
|
|
23
|
+
source: string;
|
|
24
|
+
line: number;
|
|
25
|
+
}
|
|
26
|
+
/** The `## Files` section of a pack.md, or null when there is none (then
|
|
27
|
+
* `pack apply` leaves the pack's files alone). Fenced blocks are skipped so
|
|
28
|
+
* agent bodies can't open a section. */
|
|
29
|
+
export declare function parseFilesSection(markdown: string): FilesEntry[] | null;
|
|
30
|
+
/** pack.md with its `## Files` section (heading and body) removed. */
|
|
31
|
+
export declare function stripFilesSection(markdown: string): string;
|
|
32
|
+
export interface LocalFile extends PackBundleFile {
|
|
33
|
+
abs: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function expandHome(p: string, home?: string): string;
|
|
36
|
+
/** Walk each `## Files` entry. Junk (caches, VCS, editor files) is skipped
|
|
37
|
+
* quietly; symlinks, hidden files and a skill folder's own SKILL.md are
|
|
38
|
+
* skipped with a note. Throws on a missing source or a bad destination. */
|
|
39
|
+
export declare function collectLocalFiles(entries: FilesEntry[], baseDir: string, home?: string): {
|
|
40
|
+
files: LocalFile[];
|
|
41
|
+
notes: string[];
|
|
42
|
+
};
|
|
43
|
+
/** Only Claude Code sets ${CLAUDE_PLUGIN_ROOT}. In the copy other tools read,
|
|
44
|
+
* replace it with the plugin dir's real path (forward slashes, which every
|
|
45
|
+
* shell and Python accept, Windows included). */
|
|
46
|
+
export declare function rewritePluginRoot(content: string, pluginRoot: string): string;
|