codiedev 0.10.0-beta.16 → 0.10.0-beta.17
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.js +63 -18
- package/dist/codie.js +19 -2
- package/dist/codieLauncher.js +20 -1
- package/dist/commands/agents.js +18 -1
- package/dist/commands/agentsSdk.generated.js +3 -3
- package/dist/commands/inbox.js +9 -0
- package/dist/commands/pack.js +258 -12
- package/dist/commands/ping.js +35 -10
- package/dist/commands/pingFiles.js +145 -0
- package/dist/commands/push.js +33 -4
- package/dist/commands/reply.js +49 -0
- package/dist/commands/scrub.js +1 -2
- package/dist/commands/send.js +11 -51
- package/dist/commands/shared.js +51 -0
- package/dist/commands/work.js +761 -0
- package/dist/connect.js +3 -0
- package/dist/hook.js +50 -5
- package/dist/inboxFormat.js +17 -1
- package/dist/launchContext.js +56 -0
- package/dist/launcher.js +24 -1
- package/dist/managedPi.js +1 -0
- package/dist/mcp.js +15 -77
- package/dist/packs/files.js +291 -0
- package/dist/packs/harnesses.js +2 -3
- package/dist/packs/install.js +31 -3
- package/dist/packs/localSkills.js +120 -0
- package/dist/packs/manifest.js +51 -2
- package/dist/packs/skillFiles.js +106 -0
- package/dist/packs/store.js +21 -3
- package/dist/promptGuard.js +223 -0
- package/dist/scrub/rules.js +260 -21
- package/dist/teamSkillContext.js +1 -1
- package/dist/unreadNotice.js +72 -0
- package/dist/utils.js +120 -25
- package/package.json +2 -2
- package/dist/commands/ask.js +0 -55
package/dist/connect.js
CHANGED
|
@@ -223,6 +223,9 @@ async function runConnect(args = []) {
|
|
|
223
223
|
deviceName: saved?.deviceName || os.hostname(),
|
|
224
224
|
captureIgnoredPaths: saved?.captureIgnoredPaths ?? [],
|
|
225
225
|
...(auth ? { auth } : {}),
|
|
226
|
+
features: {
|
|
227
|
+
projects: responseData.features?.projects === true,
|
|
228
|
+
},
|
|
226
229
|
};
|
|
227
230
|
try {
|
|
228
231
|
(0, utils_1.writeConfig)(config);
|
package/dist/hook.js
CHANGED
|
@@ -53,6 +53,7 @@ const launchContext_1 = require("./launchContext");
|
|
|
53
53
|
const selfUpdate_1 = require("./selfUpdate");
|
|
54
54
|
const version_1 = require("./version");
|
|
55
55
|
const sync_1 = require("./packs/sync");
|
|
56
|
+
const unreadNotice_1 = require("./unreadNotice");
|
|
56
57
|
const captureLog_1 = require("./captureLog");
|
|
57
58
|
const subagents_1 = require("./subagents");
|
|
58
59
|
const captureExclusions_1 = require("./captureExclusions");
|
|
@@ -61,6 +62,7 @@ const copilotHooks_1 = require("./copilotHooks");
|
|
|
61
62
|
const piHooks_1 = require("./piHooks");
|
|
62
63
|
const cliSession_1 = require("./cliSession");
|
|
63
64
|
const utils_2 = require("./utils");
|
|
65
|
+
const promptGuard_1 = require("./promptGuard");
|
|
64
66
|
function buildDiscoveryContext(skills) {
|
|
65
67
|
if (skills.length === 0)
|
|
66
68
|
return null;
|
|
@@ -222,19 +224,20 @@ async function postSessionSkip(config, sessionId, reason) {
|
|
|
222
224
|
async function runSessionStart(hookInput, source = "claude-code") {
|
|
223
225
|
const config = (0, utils_1.readConfig)();
|
|
224
226
|
if (!config)
|
|
225
|
-
return;
|
|
227
|
+
return undefined;
|
|
226
228
|
if (hookInput.session_id) {
|
|
227
229
|
try {
|
|
228
|
-
await
|
|
230
|
+
const res = await postJsonForResponse(`${config.backendUrl}/api/cli/sessionStart`, {
|
|
229
231
|
sessionId: hookInput.session_id,
|
|
230
232
|
startedAt: Date.now(),
|
|
231
233
|
source,
|
|
232
234
|
}, config.token, PING_TIMEOUT_MS);
|
|
235
|
+
return res?.unreadPings;
|
|
233
236
|
}
|
|
234
237
|
catch {
|
|
235
238
|
}
|
|
236
239
|
}
|
|
237
|
-
return;
|
|
240
|
+
return undefined;
|
|
238
241
|
}
|
|
239
242
|
function postJson(url, body, bearerToken, timeoutMs) {
|
|
240
243
|
return new Promise((resolve, reject) => {
|
|
@@ -478,6 +481,40 @@ async function main() {
|
|
|
478
481
|
if (process.argv[2]?.startsWith("security-"))
|
|
479
482
|
process.exit(0);
|
|
480
483
|
const captureIgnored = (0, captureExclusions_1.shouldIgnoreCaptureInput)((0, utils_1.readConfig)(), hookInput);
|
|
484
|
+
if (process.argv[2] === "prompt-guard-refresh") {
|
|
485
|
+
await (0, cliSession_1.ensureFreshToken)({ configPath: utils_2.CONFIG_PATH }).catch(() => undefined);
|
|
486
|
+
await (0, promptGuard_1.refreshPromptGuardPolicy)((0, utils_1.readConfig)());
|
|
487
|
+
process.exit(0);
|
|
488
|
+
}
|
|
489
|
+
const PROMPT_HOOKS = {
|
|
490
|
+
discover: "claude-code",
|
|
491
|
+
"discover-codex": "codex",
|
|
492
|
+
"discover-codie": "codie-harness",
|
|
493
|
+
"discover-copilot": "copilot",
|
|
494
|
+
"discover-pi": "pi",
|
|
495
|
+
"prompt-guard-cursor": "cursor",
|
|
496
|
+
};
|
|
497
|
+
const promptHarness = PROMPT_HOOKS[process.argv[2] ?? ""];
|
|
498
|
+
if (promptHarness) {
|
|
499
|
+
const guard = (0, promptGuard_1.checkPrompt)(hookInput.prompt, promptHarness, (0, promptGuard_1.readPromptGuardPolicy)());
|
|
500
|
+
(0, promptGuard_1.maybeSpawnPromptGuardRefresh)({ connected: Boolean((0, utils_1.readConfig)()) });
|
|
501
|
+
if (guard.blocked) {
|
|
502
|
+
(0, captureLog_1.appendCaptureLog)({
|
|
503
|
+
stage: "prompt-guard",
|
|
504
|
+
sessionId: hookInput.session_id ?? "unknown",
|
|
505
|
+
outcome: "ok",
|
|
506
|
+
detail: `blocked harness=${promptHarness} rule=${guard.key.rule}`,
|
|
507
|
+
});
|
|
508
|
+
process.stdout.write(JSON.stringify(guard.response));
|
|
509
|
+
if (!captureIgnored)
|
|
510
|
+
await (0, promptGuard_1.reportPromptBlocked)((0, utils_1.readConfig)(), guard.key.rule, promptHarness);
|
|
511
|
+
process.exit(0);
|
|
512
|
+
}
|
|
513
|
+
if (promptHarness === "cursor") {
|
|
514
|
+
process.stdout.write(JSON.stringify({ continue: true }));
|
|
515
|
+
process.exit(0);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
481
518
|
const promptCommand = process.argv[2] ?? "";
|
|
482
519
|
if (process.argv[2] === "discover" ||
|
|
483
520
|
process.argv[2] === "discover-codex" ||
|
|
@@ -501,6 +538,8 @@ async function main() {
|
|
|
501
538
|
(0, sync_1.maybeSpawnPackSync)({ connected: Boolean((0, utils_1.readConfig)()), cli: path.join(__dirname, "cli.js") });
|
|
502
539
|
if (source === "codex")
|
|
503
540
|
(0, localScrub_1.maybeSpawnLocalScrub)({ connected: Boolean((0, utils_1.readConfig)()), cwd: hookInput.cwd });
|
|
541
|
+
if ((0, launchContext_1.isScriptedPrompt)(source, hookInput))
|
|
542
|
+
return;
|
|
504
543
|
if (source !== "copilot")
|
|
505
544
|
await runDiscover(hookInput.prompt);
|
|
506
545
|
return;
|
|
@@ -543,10 +582,11 @@ async function main() {
|
|
|
543
582
|
process.argv[2] === "session-start-pi") {
|
|
544
583
|
(0, sync_1.maybeSpawnPackSync)({ connected: Boolean((0, utils_1.readConfig)()), cli: path.join(__dirname, "cli.js") });
|
|
545
584
|
(0, localScrub_1.maybeSpawnLocalScrub)({ connected: Boolean((0, utils_1.readConfig)()), cwd: hookInput.cwd });
|
|
585
|
+
(0, promptGuard_1.maybeSpawnPromptGuardRefresh)({ connected: Boolean((0, utils_1.readConfig)()) });
|
|
546
586
|
const packNag = process.argv[2] === "session-start-opencode" || process.argv[2] === "session-start-pi" ? null : (0, sync_1.packNagLine)();
|
|
547
587
|
if (packNag && process.argv[2] === "session-start")
|
|
548
588
|
process.stdout.write(`${packNag}\n`);
|
|
549
|
-
await runSessionStart(hookInput, process.argv[2] === "session-start-opencode"
|
|
589
|
+
const unreadPings = await runSessionStart(hookInput, process.argv[2] === "session-start-opencode"
|
|
550
590
|
? "opencode"
|
|
551
591
|
: process.argv[2] === "session-start-cursor"
|
|
552
592
|
? "cursor"
|
|
@@ -555,13 +595,18 @@ async function main() {
|
|
|
555
595
|
: process.argv[2] === "session-start-pi"
|
|
556
596
|
? "pi"
|
|
557
597
|
: "claude-code");
|
|
598
|
+
const unreadNotice = process.argv[2] === "session-start-opencode" || process.argv[2] === "session-start-pi"
|
|
599
|
+
? null
|
|
600
|
+
: (0, unreadNotice_1.takeUnreadNotice)(unreadPings);
|
|
601
|
+
if (unreadNotice && process.argv[2] === "session-start")
|
|
602
|
+
process.stdout.write(`${unreadNotice}\n`);
|
|
558
603
|
if ((0, utils_1.readConfig)() && (process.argv[2] === "session-start-cursor" || process.argv[2] === "session-start-copilot")) {
|
|
559
604
|
const source = process.argv[2] === "session-start-cursor" ? "cursor" : "copilot";
|
|
560
605
|
const { TEAM_SKILL_CONTEXT, teamSkillSessionOutput } = await Promise.resolve().then(() => __importStar(require("./teamSkillContext")));
|
|
561
606
|
const prompt = hookInput.initialPrompt
|
|
562
607
|
?? hookInput.initial_prompt;
|
|
563
608
|
const discovered = typeof prompt === "string" ? await discoverContext(prompt) : null;
|
|
564
|
-
process.stdout.write(JSON.stringify(teamSkillSessionOutput(source, [TEAM_SKILL_CONTEXT, discovered, packNag].filter(Boolean).join("\n\n"))));
|
|
609
|
+
process.stdout.write(JSON.stringify(teamSkillSessionOutput(source, [TEAM_SKILL_CONTEXT, discovered, packNag, unreadNotice].filter(Boolean).join("\n\n"))));
|
|
565
610
|
}
|
|
566
611
|
return;
|
|
567
612
|
}
|
package/dist/inboxFormat.js
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
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
|
+
function fileList(files) {
|
|
14
|
+
return files.map((f) => `${f.name} (${fileSize(f.size)})`).join(", ");
|
|
15
|
+
}
|
|
5
16
|
function formatInbox(opts) {
|
|
6
17
|
const mentions = opts.mentions ?? [];
|
|
7
18
|
const DEFAULT_LIMIT = 50;
|
|
@@ -25,6 +36,9 @@ function formatInbox(opts) {
|
|
|
25
36
|
const where = p.subjectKey ? ` · on ${p.subjectKey}` : "";
|
|
26
37
|
lines.push(`${dot} ${from}${where} · ${(0, shared_1.timeAgo)(p.createdAt)}`);
|
|
27
38
|
lines.push(` ${p.preview || "(no body)"}`);
|
|
39
|
+
if (p.attachments?.length) {
|
|
40
|
+
lines.push(` ${p.attachments.length === 1 ? "file" : "files"}: ${fileList(p.attachments)}`);
|
|
41
|
+
}
|
|
28
42
|
lines.push(` id: ${p.pingId}`);
|
|
29
43
|
lines.push("");
|
|
30
44
|
}
|
|
@@ -39,7 +53,9 @@ function formatInbox(opts) {
|
|
|
39
53
|
lines.push(` ${m.url}`);
|
|
40
54
|
lines.push("");
|
|
41
55
|
}
|
|
42
|
-
if (keptPings.length > 0)
|
|
56
|
+
if (keptPings.length > 0) {
|
|
43
57
|
lines.push("Mark read: codiedev read <ping-id>");
|
|
58
|
+
lines.push('Reply: codiedev reply <ping-id> "<message>"');
|
|
59
|
+
}
|
|
44
60
|
return lines;
|
|
45
61
|
}
|
package/dist/launchContext.js
CHANGED
|
@@ -7,7 +7,10 @@ exports.windowsAncestors = windowsAncestors;
|
|
|
7
7
|
exports.detectCursorLaunch = detectCursorLaunch;
|
|
8
8
|
exports.detectCopilotLaunch = detectCopilotLaunch;
|
|
9
9
|
exports.hookLaunch = hookLaunch;
|
|
10
|
+
exports.codexLaunchFromRolloutHead = codexLaunchFromRolloutHead;
|
|
11
|
+
exports.isScriptedPrompt = isScriptedPrompt;
|
|
10
12
|
const node_child_process_1 = require("node:child_process");
|
|
13
|
+
const node_fs_1 = require("node:fs");
|
|
11
14
|
const VALUE = /^[a-z0-9][a-z0-9_.:-]{0,39}$/;
|
|
12
15
|
const CODIE_SUBCOMMANDS = ["run", "serve", "acp", "web", "attach"];
|
|
13
16
|
function codieLaunchFromArgv(argv) {
|
|
@@ -148,3 +151,56 @@ function hookLaunch(hookCommand, input, deps = {}) {
|
|
|
148
151
|
}
|
|
149
152
|
return undefined;
|
|
150
153
|
}
|
|
154
|
+
const SCRIPTED_LAUNCHES = new Set([
|
|
155
|
+
"codex:exec", "codex:mcp",
|
|
156
|
+
"codie:run", "codie:serve", "codie:acp",
|
|
157
|
+
"cursor:print", "copilot:print",
|
|
158
|
+
"pi:print", "pi:json", "pi:rpc",
|
|
159
|
+
]);
|
|
160
|
+
const isScriptedLaunch = (launch) => launch.startsWith("sdk") || SCRIPTED_LAUNCHES.has(launch);
|
|
161
|
+
function codexLaunchFromRolloutHead(head) {
|
|
162
|
+
const end = head.indexOf("\n");
|
|
163
|
+
if (end < 0)
|
|
164
|
+
return undefined;
|
|
165
|
+
try {
|
|
166
|
+
const first = JSON.parse(head.slice(0, end));
|
|
167
|
+
if (first?.type !== "session_meta")
|
|
168
|
+
return undefined;
|
|
169
|
+
const src = first.payload?.source;
|
|
170
|
+
if (typeof src === "string")
|
|
171
|
+
return VALUE.test(`codex:${src}`) ? `codex:${src}` : undefined;
|
|
172
|
+
if (src && typeof src === "object")
|
|
173
|
+
return `codex:${Object.keys(src)[0] ?? "other"}`;
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
}
|
|
177
|
+
return undefined;
|
|
178
|
+
}
|
|
179
|
+
function readHead(file, bytes = 256 * 1024) {
|
|
180
|
+
let fd;
|
|
181
|
+
try {
|
|
182
|
+
fd = (0, node_fs_1.openSync)(file, "r");
|
|
183
|
+
const buf = Buffer.alloc(bytes);
|
|
184
|
+
return buf.toString("utf8", 0, (0, node_fs_1.readSync)(fd, buf, 0, bytes, 0));
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return "";
|
|
188
|
+
}
|
|
189
|
+
finally {
|
|
190
|
+
if (fd !== undefined)
|
|
191
|
+
(0, node_fs_1.closeSync)(fd);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function isScriptedPrompt(harness, input, deps = {}) {
|
|
195
|
+
if (typeof input.launch === "string" && VALUE.test(input.launch))
|
|
196
|
+
return isScriptedLaunch(input.launch);
|
|
197
|
+
if (harness === "claude-code") {
|
|
198
|
+
const entrypoint = (deps.env ?? process.env).CLAUDE_CODE_ENTRYPOINT;
|
|
199
|
+
return typeof entrypoint === "string" && entrypoint.startsWith("sdk");
|
|
200
|
+
}
|
|
201
|
+
if (harness === "codex" && typeof input.transcript_path === "string" && input.transcript_path) {
|
|
202
|
+
const launch = codexLaunchFromRolloutHead((deps.readHead ?? readHead)(input.transcript_path));
|
|
203
|
+
return launch !== undefined && isScriptedLaunch(launch);
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
}
|
package/dist/launcher.js
CHANGED
|
@@ -37,15 +37,17 @@ exports.LAUNCHER_FILE = void 0;
|
|
|
37
37
|
exports.launcherPath = launcherPath;
|
|
38
38
|
exports.usesLauncher = usesLauncher;
|
|
39
39
|
exports.readInstallRecord = readInstallRecord;
|
|
40
|
+
exports.isInTempDir = isInTempDir;
|
|
40
41
|
exports.stableHookScript = stableHookScript;
|
|
41
42
|
exports.recordCurrentInstall = recordCurrentInstall;
|
|
42
43
|
exports.ensureLauncher = ensureLauncher;
|
|
43
44
|
exports.resetLauncherCacheForTests = resetLauncherCacheForTests;
|
|
44
45
|
const fs = __importStar(require("fs"));
|
|
46
|
+
const os = __importStar(require("os"));
|
|
45
47
|
const path = __importStar(require("path"));
|
|
46
48
|
const configDir_1 = require("./configDir");
|
|
47
49
|
exports.LAUNCHER_FILE = "codiedev-hook-launcher.cjs";
|
|
48
|
-
const LAUNCHER_REVISION =
|
|
50
|
+
const LAUNCHER_REVISION = 2;
|
|
49
51
|
function launcherPath() {
|
|
50
52
|
return path.join((0, configDir_1.codiedevStateDir)(), "bin", exports.LAUNCHER_FILE);
|
|
51
53
|
}
|
|
@@ -67,6 +69,12 @@ try {
|
|
|
67
69
|
if (rec && typeof rec.hookScript === "string" && fs.existsSync(rec.hookScript)) target = rec.hookScript;
|
|
68
70
|
} catch {}
|
|
69
71
|
if (!target && fs.existsSync(FALLBACK)) target = FALLBACK;
|
|
72
|
+
if (!target) {
|
|
73
|
+
// Both stranded (e.g. a deleted temp build): use a global install next to this node.
|
|
74
|
+
const prefix = path.dirname(path.dirname(process.execPath));
|
|
75
|
+
target = [path.join(prefix, "lib", "node_modules", "codiedev", "dist", "hook.js"),
|
|
76
|
+
path.join(prefix, "node_modules", "codiedev", "dist", "hook.js")].find((p) => fs.existsSync(p)) || null;
|
|
77
|
+
}
|
|
70
78
|
if (!target) process.exit(0);
|
|
71
79
|
process.env.CODIEDEV_VIA_LAUNCHER = "1";
|
|
72
80
|
process.argv[1] = target;
|
|
@@ -97,11 +105,26 @@ function packageVersionFor(hookScript) {
|
|
|
97
105
|
return undefined;
|
|
98
106
|
}
|
|
99
107
|
}
|
|
108
|
+
function isInTempDir(p, tmp = os.tmpdir()) {
|
|
109
|
+
const real = (x) => {
|
|
110
|
+
try {
|
|
111
|
+
return fs.realpathSync(x);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return path.resolve(x);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
const rel = path.relative(real(tmp), real(p));
|
|
118
|
+
return rel !== "" && !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
119
|
+
}
|
|
100
120
|
function stableHookScript(hookScript, globalCandidates = globalHookScripts()) {
|
|
101
121
|
if (!hookScript || !path.isAbsolute(hookScript) || hookScript.includes(`${path.sep}~${path.sep}`))
|
|
102
122
|
return null;
|
|
103
123
|
if (!fs.existsSync(hookScript))
|
|
104
124
|
return null;
|
|
125
|
+
if (process.env.CODIEDEV_ALLOW_TEMP_HOOK !== "1" && isInTempDir(hookScript)) {
|
|
126
|
+
return globalCandidates.find((p) => path.isAbsolute(p) && fs.existsSync(p) && !isInTempDir(p)) ?? null;
|
|
127
|
+
}
|
|
105
128
|
if (hookScript.includes(`${path.sep}_npx${path.sep}`)) {
|
|
106
129
|
const global = globalCandidates.find((p) => path.isAbsolute(p) && fs.existsSync(p));
|
|
107
130
|
if (global)
|
package/dist/managedPi.js
CHANGED
|
@@ -190,6 +190,7 @@ function prepareManagedPiDir(opts) {
|
|
|
190
190
|
});
|
|
191
191
|
fs.rmSync(path.join(agentDir, "auth.json"), { force: true });
|
|
192
192
|
fs.rmSync(path.join(agentDir, "extensions"), { recursive: true, force: true });
|
|
193
|
+
fs.writeFileSync(path.join(agentDir, "AGENTS.md"), (0, utils_1.codiedevInstructionsText)(), { mode: 0o600 });
|
|
193
194
|
const guard = path.join(agentDir, exports.GUARD_FILE);
|
|
194
195
|
fs.writeFileSync(guard, exports.GUARD_SOURCE, { mode: 0o600 });
|
|
195
196
|
const capture = path.join(agentDir, exports.CAPTURE_FILE);
|
package/dist/mcp.js
CHANGED
|
@@ -158,10 +158,10 @@ const TOOLS = [
|
|
|
158
158
|
},
|
|
159
159
|
{
|
|
160
160
|
name: "codiedev_ping",
|
|
161
|
-
description: "Send a message to a teammate on Codiedev, optionally
|
|
162
|
-
"
|
|
163
|
-
"or get feedback from a teammate by name
|
|
164
|
-
"this spec'). Recipients are resolved by first name, full name, or email " +
|
|
161
|
+
description: "Send a message to a teammate on Codiedev, optionally with an " +
|
|
162
|
+
"artifact (they also get read access to it). Use when the user asks to " +
|
|
163
|
+
"ping, ask, send something to, or get feedback from a teammate by name " +
|
|
164
|
+
"(e.g. 'send this spec to a teammate'). Recipients are resolved by first name, full name, or email " +
|
|
165
165
|
"within the current org. They're notified by email and can reply " +
|
|
166
166
|
"from their own agent.",
|
|
167
167
|
inputSchema: {
|
|
@@ -178,8 +178,13 @@ const TOOLS = [
|
|
|
178
178
|
},
|
|
179
179
|
subjectKey: {
|
|
180
180
|
type: "string",
|
|
181
|
-
description: "Optional filename key of an artifact
|
|
182
|
-
"
|
|
181
|
+
description: "Optional filename key of an artifact to send (e.g. " +
|
|
182
|
+
"'spec-214.md'). The teammate also gets read access to it. " +
|
|
183
|
+
"Leaving blank sends a standalone ping.",
|
|
184
|
+
},
|
|
185
|
+
edit: {
|
|
186
|
+
type: "boolean",
|
|
187
|
+
description: "With subjectKey: give edit access instead of read (owner only).",
|
|
183
188
|
},
|
|
184
189
|
},
|
|
185
190
|
required: ["to", "body"],
|
|
@@ -229,7 +234,7 @@ const TOOLS = [
|
|
|
229
234
|
"without sending a notification. Use when the user says 'share this " +
|
|
230
235
|
"with a teammate' or 'give someone access', and doesn't want to surface " +
|
|
231
236
|
"it as a message. If they want the teammate actively notified, use " +
|
|
232
|
-
"
|
|
237
|
+
"codiedev_ping with subjectKey instead.",
|
|
233
238
|
inputSchema: {
|
|
234
239
|
type: "object",
|
|
235
240
|
properties: {
|
|
@@ -248,36 +253,13 @@ const TOOLS = [
|
|
|
248
253
|
required: ["filename", "to"],
|
|
249
254
|
},
|
|
250
255
|
},
|
|
251
|
-
{
|
|
252
|
-
name: "codiedev_send_to",
|
|
253
|
-
description: "Grant access to an artifact AND send a short message to a " +
|
|
254
|
-
"teammate. Use when the user asks to 'send this to a teammate', 'ask " +
|
|
255
|
-
"someone to look at this', or 'share this with a teammate and tell them X'. " +
|
|
256
|
-
"This is the active handoff version of codiedev_share_with — the " +
|
|
257
|
-
"recipient gets a ping in their inbox.",
|
|
258
|
-
inputSchema: {
|
|
259
|
-
type: "object",
|
|
260
|
-
properties: {
|
|
261
|
-
filename: { type: "string", description: "Artifact filename." },
|
|
262
|
-
to: { type: "string", description: "Teammate first name, full name, or email." },
|
|
263
|
-
message: {
|
|
264
|
-
type: "string",
|
|
265
|
-
description: "Optional short note to send with the share. If omitted, only " +
|
|
266
|
-
"the grant is created (no ping).",
|
|
267
|
-
},
|
|
268
|
-
},
|
|
269
|
-
required: ["filename", "to"],
|
|
270
|
-
},
|
|
271
|
-
},
|
|
272
256
|
{
|
|
273
257
|
name: "codiedev_search",
|
|
274
258
|
description: "Search the team's artifact library by natural-language query. Use " +
|
|
275
259
|
"when the user asks 'has anyone solved X?', 'find me the spec on Y', " +
|
|
276
260
|
"or 'look for prior work on Z'. Always search before pushing a new " +
|
|
277
261
|
"artifact so the user doesn't duplicate work. Returns ranked hits " +
|
|
278
|
-
"with title, type, filename key, and a snippet.
|
|
279
|
-
"questions where the user wants a synthesized answer (not just a list " +
|
|
280
|
-
"of hits), prefer codiedev_ask instead.",
|
|
262
|
+
"with title, type, filename key, and a snippet.",
|
|
281
263
|
inputSchema: {
|
|
282
264
|
type: "object",
|
|
283
265
|
properties: {
|
|
@@ -287,32 +269,6 @@ const TOOLS = [
|
|
|
287
269
|
required: ["query"],
|
|
288
270
|
},
|
|
289
271
|
},
|
|
290
|
-
{
|
|
291
|
-
name: "codiedev_ask",
|
|
292
|
-
description: "Ask a natural-language question of the team's collective memory and " +
|
|
293
|
-
"get a synthesized answer with citations. Searches across both " +
|
|
294
|
-
"auto-extracted artifacts (specs / decisions / bugfixes) AND raw " +
|
|
295
|
-
"session transcripts, then has an LLM compose an answer that cites " +
|
|
296
|
-
"the actual sources. Use when the user asks 'why did we…?', 'what " +
|
|
297
|
-
"does the team know about X?', 'has anyone done Y here before?', " +
|
|
298
|
-
"'who decided Z and why?', or any open-ended question about prior " +
|
|
299
|
-
"work where they want context, not just a hit list. For exact-match " +
|
|
300
|
-
"lookups (find me spec-x), prefer codiedev_pull. For browsing or " +
|
|
301
|
-
"filtering by kind/tag, prefer codiedev_get_library. For surfacing " +
|
|
302
|
-
"ranked hits without LLM synthesis, prefer codiedev_search.",
|
|
303
|
-
inputSchema: {
|
|
304
|
-
type: "object",
|
|
305
|
-
properties: {
|
|
306
|
-
question: {
|
|
307
|
-
type: "string",
|
|
308
|
-
description: "Natural-language question. Open-ended is fine — e.g. 'why " +
|
|
309
|
-
"did we drop pg-bouncer?' or 'how does the team think about " +
|
|
310
|
-
"feature flags?'.",
|
|
311
|
-
},
|
|
312
|
-
},
|
|
313
|
-
required: ["question"],
|
|
314
|
-
},
|
|
315
|
-
},
|
|
316
272
|
{
|
|
317
273
|
name: "codiedev_get_library",
|
|
318
274
|
description: "List artifacts in the team library — both docs (spec / decision / " +
|
|
@@ -442,8 +398,6 @@ async function dispatchTool(name, args, config) {
|
|
|
442
398
|
return await handleShareWith(args, config);
|
|
443
399
|
case "codiedev_send_to":
|
|
444
400
|
return await handleSendTo(args, config);
|
|
445
|
-
case "codiedev_ask":
|
|
446
|
-
return await handleAsk(args, config);
|
|
447
401
|
case "codiedev_search":
|
|
448
402
|
return await handleSearch(args, config);
|
|
449
403
|
case "codiedev_get_library":
|
|
@@ -533,12 +487,13 @@ async function handlePing(args, config) {
|
|
|
533
487
|
const to = asString(args.to);
|
|
534
488
|
const body = asString(args.body);
|
|
535
489
|
const subjectKey = asStringOrUndefined(args.subjectKey);
|
|
490
|
+
const grant = subjectKey ? (args.edit === true ? "edit" : "read") : undefined;
|
|
536
491
|
if (!to)
|
|
537
492
|
throw new Error("to required");
|
|
538
493
|
if (!body)
|
|
539
494
|
throw new Error("body required");
|
|
540
495
|
try {
|
|
541
|
-
const res = await (0, shared_1.apiRequest)("POST", "/api/cli/ping", { config, body: { to, body, subjectKey } });
|
|
496
|
+
const res = await (0, shared_1.apiRequest)("POST", "/api/cli/ping", { config, body: { to, body, subjectKey, ...(grant ? { grant } : {}) } });
|
|
542
497
|
return {
|
|
543
498
|
content: [
|
|
544
499
|
{
|
|
@@ -704,23 +659,6 @@ async function handleSearch(args, config) {
|
|
|
704
659
|
}
|
|
705
660
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
706
661
|
}
|
|
707
|
-
async function handleAsk(args, config) {
|
|
708
|
-
const question = asString(args.question);
|
|
709
|
-
if (!question)
|
|
710
|
-
throw new Error("question required");
|
|
711
|
-
const res = await (0, shared_1.apiRequest)("POST", "/api/cli/ask", { config, body: { question } });
|
|
712
|
-
const lines = [res.answer];
|
|
713
|
-
if (res.citations && res.citations.length > 0) {
|
|
714
|
-
lines.push("", "Sources:");
|
|
715
|
-
for (const c of res.citations) {
|
|
716
|
-
const label = c.kind === "artifact"
|
|
717
|
-
? `[${c.type}] ${c.title}`
|
|
718
|
-
: `[session] ${c.title}`;
|
|
719
|
-
lines.push(` • ${label}`);
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
723
|
-
}
|
|
724
662
|
async function handleGetLibrary(args, config) {
|
|
725
663
|
const scope = asStringOrUndefined(args.scope);
|
|
726
664
|
const type = asStringOrUndefined(args.type);
|