codiedev 0.8.1 → 0.9.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +5 -5
- package/dist/commands/createTicket.js +1 -1
- package/dist/commands/doctor.js +3 -3
- package/dist/commands/library.js +1 -1
- package/dist/commands/shared.d.ts +16 -2
- package/dist/commands/shared.js +19 -2
- package/dist/connect.js +44 -49
- package/dist/hook.js +69 -1
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +11 -11
- package/dist/utils.d.ts +6 -4
- package/dist/utils.js +61 -31
- package/package.json +2 -2
package/README.md
CHANGED
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Codiedev CLI dispatcher.
|
|
5
5
|
*
|
|
6
6
|
* Subcommands:
|
|
7
7
|
* codiedev connect — link agent CLIs to your org
|
|
@@ -35,7 +35,7 @@ const send_1 = require("./commands/send");
|
|
|
35
35
|
const react_1 = require("./commands/react");
|
|
36
36
|
const version_1 = require("./version");
|
|
37
37
|
const HELP = `
|
|
38
|
-
|
|
38
|
+
Codiedev CLI
|
|
39
39
|
|
|
40
40
|
The team artifact layer for agent-coded work. Share specs and reviews
|
|
41
41
|
with teammates, get pinged when they need your input, pick up where
|
|
@@ -132,7 +132,7 @@ Docs: codiedev docs
|
|
|
132
132
|
`.trim();
|
|
133
133
|
const COMMAND_HELP = {
|
|
134
134
|
connect: `
|
|
135
|
-
codiedev connect — link your agent CLI to
|
|
135
|
+
codiedev connect — link your agent CLI to Codiedev
|
|
136
136
|
|
|
137
137
|
Interactive setup. Prompts for an API token, then installs:
|
|
138
138
|
- ~/.codiedev/config.json (token + org + tracked repos)
|
|
@@ -272,11 +272,11 @@ codiedev create-ticket — forward-write a scoped ticket plan
|
|
|
272
272
|
`.trim(),
|
|
273
273
|
};
|
|
274
274
|
const DOCS = `
|
|
275
|
-
|
|
275
|
+
Codiedev — usage guide
|
|
276
276
|
|
|
277
277
|
## What this is
|
|
278
278
|
|
|
279
|
-
|
|
279
|
+
Codiedev is a team artifact layer on top of your coding agent (Claude
|
|
280
280
|
Code, Codex, etc.). It persists the *durable* outputs of agent work —
|
|
281
281
|
specs, reviews, decisions, proposals, and captured thoughts — so:
|
|
282
282
|
|
|
@@ -65,7 +65,7 @@ function resolveRepoIds(fullNames, repos) {
|
|
|
65
65
|
for (const fn of fullNames) {
|
|
66
66
|
const id = lookup.get(fn.toLowerCase());
|
|
67
67
|
if (!id) {
|
|
68
|
-
throw new Error(`Repo "${fn}" is not connected to your
|
|
68
|
+
throw new Error(`Repo "${fn}" is not connected to your Codiedev account. Run \`codiedev connect\` to refresh, or push the missing repo through Settings.`);
|
|
69
69
|
}
|
|
70
70
|
ids.push(id);
|
|
71
71
|
}
|
package/dist/commands/doctor.js
CHANGED
|
@@ -134,13 +134,13 @@ function hasGlabCli() {
|
|
|
134
134
|
}
|
|
135
135
|
async function runDoctor(_args) {
|
|
136
136
|
const checks = [];
|
|
137
|
-
console.log("\
|
|
137
|
+
console.log("\nCodiedev doctor — checking your setup…\n");
|
|
138
138
|
await (0, upgradeNudge_1.maybePrintUpgradeNudge)();
|
|
139
139
|
// 1. Config file
|
|
140
140
|
const config = (0, utils_1.readConfig)();
|
|
141
141
|
if (!config) {
|
|
142
142
|
checks.push({
|
|
143
|
-
name: "
|
|
143
|
+
name: "Codiedev config (~/.codiedev/config.json)",
|
|
144
144
|
status: "fail",
|
|
145
145
|
detail: "Not found. Run `codiedev connect` first.",
|
|
146
146
|
});
|
|
@@ -148,7 +148,7 @@ async function runDoctor(_args) {
|
|
|
148
148
|
process.exit(1);
|
|
149
149
|
}
|
|
150
150
|
checks.push({
|
|
151
|
-
name: "
|
|
151
|
+
name: "Codiedev config",
|
|
152
152
|
status: "pass",
|
|
153
153
|
detail: `connected to ${config.companyName}`,
|
|
154
154
|
});
|
package/dist/commands/library.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// `codiedev library [--scope mine|shared|all] [--kind doc|skill] [--tag X]
|
|
3
|
-
// [--type spec|...] [--limit N]` — list artifacts in your
|
|
3
|
+
// [--type spec|...] [--limit N]` — list artifacts in your Codiedev workspace.
|
|
4
4
|
// Server applies companyId scoping; --scope filters within that.
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.runLibrary = runLibrary;
|
|
@@ -8,12 +8,12 @@ import { CodiedevConfig } from "../utils";
|
|
|
8
8
|
*/
|
|
9
9
|
export declare function applyConfigEnvOverrides(config: CodiedevConfig): CodiedevConfig;
|
|
10
10
|
/**
|
|
11
|
-
* Require a connected
|
|
11
|
+
* Require a connected Codiedev config. Exits the process with a helpful
|
|
12
12
|
* message if the user hasn't run `codiedev connect` yet.
|
|
13
13
|
*/
|
|
14
14
|
export declare function requireConfig(): CodiedevConfig;
|
|
15
15
|
/**
|
|
16
|
-
* Perform an authenticated HTTP request against the
|
|
16
|
+
* Perform an authenticated HTTP request against the Codiedev backend.
|
|
17
17
|
* Returns the parsed JSON response body. Throws on non-2xx.
|
|
18
18
|
*/
|
|
19
19
|
export declare function apiRequest<T = unknown>(method: "GET" | "POST", path: string, options: {
|
|
@@ -33,6 +33,20 @@ export declare function recordAgentEvent(config: CodiedevConfig, event: {
|
|
|
33
33
|
latencyMs: number;
|
|
34
34
|
error?: string;
|
|
35
35
|
}): void;
|
|
36
|
+
/**
|
|
37
|
+
* Per-step install telemetry for `npx codiedev connect`. One row per phase
|
|
38
|
+
* (started, detect, install_target, validate_token, completed, failed) so
|
|
39
|
+
* the admin company page can show a timeline when a customer says "it says
|
|
40
|
+
* connected but it doesn't work." Fire-and-forget, never throws. Auto-stamps
|
|
41
|
+
* platform + node + cli version so call sites only need step/target/ok/error.
|
|
42
|
+
*/
|
|
43
|
+
export type InstallTelemetryStep = "started" | "detect" | "install_target" | "validate_token" | "completed" | "failed";
|
|
44
|
+
export declare function recordInstallEvent(config: CodiedevConfig, event: {
|
|
45
|
+
step: InstallTelemetryStep;
|
|
46
|
+
target?: string;
|
|
47
|
+
ok: boolean;
|
|
48
|
+
error?: string;
|
|
49
|
+
}): void;
|
|
36
50
|
/**
|
|
37
51
|
* Wraps a CLI subcommand body so success and failure both record telemetry
|
|
38
52
|
* and the original error propagates after the event is queued. Use this in
|
package/dist/commands/shared.js
CHANGED
|
@@ -37,6 +37,7 @@ exports.applyConfigEnvOverrides = applyConfigEnvOverrides;
|
|
|
37
37
|
exports.requireConfig = requireConfig;
|
|
38
38
|
exports.apiRequest = apiRequest;
|
|
39
39
|
exports.recordAgentEvent = recordAgentEvent;
|
|
40
|
+
exports.recordInstallEvent = recordInstallEvent;
|
|
40
41
|
exports.withTelemetry = withTelemetry;
|
|
41
42
|
exports.timeAgo = timeAgo;
|
|
42
43
|
exports.stripQuotes = stripQuotes;
|
|
@@ -60,7 +61,7 @@ function applyConfigEnvOverrides(config) {
|
|
|
60
61
|
return config;
|
|
61
62
|
}
|
|
62
63
|
/**
|
|
63
|
-
* Require a connected
|
|
64
|
+
* Require a connected Codiedev config. Exits the process with a helpful
|
|
64
65
|
* message if the user hasn't run `codiedev connect` yet.
|
|
65
66
|
*/
|
|
66
67
|
function requireConfig() {
|
|
@@ -72,7 +73,7 @@ function requireConfig() {
|
|
|
72
73
|
return applyConfigEnvOverrides(config);
|
|
73
74
|
}
|
|
74
75
|
/**
|
|
75
|
-
* Perform an authenticated HTTP request against the
|
|
76
|
+
* Perform an authenticated HTTP request against the Codiedev backend.
|
|
76
77
|
* Returns the parsed JSON response body. Throws on non-2xx.
|
|
77
78
|
*/
|
|
78
79
|
function apiRequest(method, path, options) {
|
|
@@ -155,6 +156,22 @@ function recordAgentEvent(config, event) {
|
|
|
155
156
|
// Swallow — telemetry must never affect user-facing flow.
|
|
156
157
|
});
|
|
157
158
|
}
|
|
159
|
+
function recordInstallEvent(config, event) {
|
|
160
|
+
apiRequest("POST", "/api/telemetry/install-event", {
|
|
161
|
+
config,
|
|
162
|
+
body: {
|
|
163
|
+
step: event.step,
|
|
164
|
+
target: event.target,
|
|
165
|
+
ok: event.ok,
|
|
166
|
+
error: event.error,
|
|
167
|
+
platform: process.platform,
|
|
168
|
+
nodeVersion: process.version,
|
|
169
|
+
cliVersion: version_1.CLI_VERSION,
|
|
170
|
+
},
|
|
171
|
+
}).catch(() => {
|
|
172
|
+
// Swallow — telemetry must never affect connect flow.
|
|
173
|
+
});
|
|
174
|
+
}
|
|
158
175
|
/**
|
|
159
176
|
* Wraps a CLI subcommand body so success and failure both record telemetry
|
|
160
177
|
* and the original error propagates after the event is queued. Use this in
|
package/dist/connect.js
CHANGED
|
@@ -41,6 +41,7 @@ const utils_1 = require("./utils");
|
|
|
41
41
|
const connectFlow_1 = require("./connectFlow");
|
|
42
42
|
const version_1 = require("./version");
|
|
43
43
|
const upgradeNudge_1 = require("./upgradeNudge");
|
|
44
|
+
const shared_1 = require("./commands/shared");
|
|
44
45
|
const BACKEND_URL = process.env.CODIEDEV_URL || "https://judicious-falcon-861.convex.site";
|
|
45
46
|
/**
|
|
46
47
|
* Reads a line from stdin without echoing the typed/pasted characters —
|
|
@@ -160,9 +161,9 @@ async function runConnect(args = []) {
|
|
|
160
161
|
// mint a fresh token from the portal — which is what bit Sonifi's first
|
|
161
162
|
// engineer when he interpreted "no visible token" as "the old one is gone".
|
|
162
163
|
const force = args.includes("--force") || args.includes("-f");
|
|
163
|
-
console.log("\nWelcome to
|
|
164
|
+
console.log("\nWelcome to Codiedev CLI\n");
|
|
164
165
|
await (0, upgradeNudge_1.maybePrintUpgradeNudge)();
|
|
165
|
-
console.log("This will connect your coding agent to your
|
|
166
|
+
console.log("This will connect your coding agent to your Codiedev workspace and install");
|
|
166
167
|
console.log("the session capture hook for org-wide session sharing.\n");
|
|
167
168
|
const saved = (0, utils_1.readConfig)();
|
|
168
169
|
const decision = (0, connectFlow_1.resolveConnectToken)({ savedConfig: saved, force });
|
|
@@ -174,7 +175,7 @@ async function runConnect(args = []) {
|
|
|
174
175
|
else {
|
|
175
176
|
// Hidden prompt — masks the token with `*` as it's typed/pasted so it
|
|
176
177
|
// doesn't end up in terminal scrollback or screenshots.
|
|
177
|
-
token = await promptHidden("Enter your
|
|
178
|
+
token = await promptHidden("Enter your Codiedev API token: ");
|
|
178
179
|
if (!token) {
|
|
179
180
|
console.error("Error: API token is required.");
|
|
180
181
|
process.exit(1);
|
|
@@ -214,6 +215,11 @@ async function runConnect(args = []) {
|
|
|
214
215
|
console.error(`\nError: Failed to save config — ${err.message}`);
|
|
215
216
|
process.exit(1);
|
|
216
217
|
}
|
|
218
|
+
// Once we have a valid token + config, every subsequent step posts a
|
|
219
|
+
// per-step row to /api/telemetry/install-event so the admin page can show
|
|
220
|
+
// a timeline when a customer says "it connected but it doesn't work."
|
|
221
|
+
// Fire-and-forget — never blocks the connect flow.
|
|
222
|
+
(0, shared_1.recordInstallEvent)(config, { step: "started", ok: true });
|
|
217
223
|
// Migration: strip MCP server entries written by pre-CLI-only versions
|
|
218
224
|
// of `codiedev connect`. These pointed at `npx codiedev-mcp`, a binary
|
|
219
225
|
// that was removed in 0.3.4 and that 0.7.11+ no longer ships. Without
|
|
@@ -242,64 +248,46 @@ async function runConnect(args = []) {
|
|
|
242
248
|
const hasClaude = (0, utils_1.claudeCodeInstalled)();
|
|
243
249
|
const hasCodex = (0, utils_1.codexInstalled)();
|
|
244
250
|
const hasCursor = (0, utils_1.cursorInstalled)();
|
|
251
|
+
(0, shared_1.recordInstallEvent)(config, { step: "detect", target: "claude-code", ok: hasClaude });
|
|
252
|
+
(0, shared_1.recordInstallEvent)(config, { step: "detect", target: "codex", ok: hasCodex });
|
|
253
|
+
(0, shared_1.recordInstallEvent)(config, { step: "detect", target: "cursor", ok: hasCursor });
|
|
245
254
|
const installed = [];
|
|
246
255
|
// Machine-readable target keys reported back to the backend via
|
|
247
256
|
// /api/cli/markInstalled so the portal's "Connected" state reflects
|
|
248
257
|
// what's actually wired up on this machine, not just "token validated."
|
|
249
258
|
const installedTargets = [];
|
|
250
|
-
|
|
259
|
+
// Wrap each per-target write so we emit one install_target row each. The
|
|
260
|
+
// existing user-visible behavior (console.warn on failure, continue) is
|
|
261
|
+
// unchanged — telemetry just observes the outcome.
|
|
262
|
+
const runInstall = (target, fn, label, targetKey) => {
|
|
251
263
|
try {
|
|
252
|
-
(
|
|
253
|
-
installed.push(
|
|
254
|
-
installedTargets.push(
|
|
255
|
-
|
|
256
|
-
catch (err) {
|
|
257
|
-
console.error(`\nWarning: Failed to install Claude Code hook — ${err.message}`);
|
|
258
|
-
}
|
|
259
|
-
try {
|
|
260
|
-
(0, utils_1.installClaudeCodeInstructions)();
|
|
261
|
-
installed.push("Claude Code agent instructions (~/.claude/CLAUDE.md)");
|
|
262
|
-
installedTargets.push("claudeCode-instructions");
|
|
264
|
+
fn();
|
|
265
|
+
installed.push(label);
|
|
266
|
+
installedTargets.push(targetKey);
|
|
267
|
+
(0, shared_1.recordInstallEvent)(config, { step: "install_target", target, ok: true });
|
|
263
268
|
}
|
|
264
269
|
catch (err) {
|
|
265
|
-
|
|
270
|
+
const message = err.message;
|
|
271
|
+
console.error(`\nWarning: Failed to install ${label} — ${message}`);
|
|
272
|
+
(0, shared_1.recordInstallEvent)(config, {
|
|
273
|
+
step: "install_target",
|
|
274
|
+
target,
|
|
275
|
+
ok: false,
|
|
276
|
+
error: message?.slice(0, 500),
|
|
277
|
+
});
|
|
266
278
|
}
|
|
279
|
+
};
|
|
280
|
+
if (hasClaude) {
|
|
281
|
+
runInstall("claude-code-hook", utils_1.installHook, "Claude Code SessionEnd hook (~/.claude/settings.json)", "claudeCode-hook");
|
|
282
|
+
runInstall("claude-code-instructions", utils_1.installClaudeCodeInstructions, "Claude Code agent instructions (~/.claude/CLAUDE.md)", "claudeCode-instructions");
|
|
267
283
|
}
|
|
268
284
|
if (hasCodex) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
installed.push("Codex Stop hook (~/.codex/hooks.json)");
|
|
272
|
-
installedTargets.push("codex-hook");
|
|
273
|
-
}
|
|
274
|
-
catch (err) {
|
|
275
|
-
console.error(`\nWarning: Failed to install Codex hook — ${err.message}`);
|
|
276
|
-
}
|
|
277
|
-
try {
|
|
278
|
-
(0, utils_1.installCodexInstructions)();
|
|
279
|
-
installed.push("Codex agent instructions (~/.codex/AGENTS.md)");
|
|
280
|
-
installedTargets.push("codex-instructions");
|
|
281
|
-
}
|
|
282
|
-
catch (err) {
|
|
283
|
-
console.error(`\nWarning: Failed to install Codex instructions — ${err.message}`);
|
|
284
|
-
}
|
|
285
|
+
runInstall("codex-hook", utils_1.installCodexHook, "Codex Stop hook (~/.codex/hooks.json)", "codex-hook");
|
|
286
|
+
runInstall("codex-instructions", utils_1.installCodexInstructions, "Codex agent instructions (~/.codex/AGENTS.md)", "codex-instructions");
|
|
285
287
|
}
|
|
286
288
|
if (hasCursor) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
installed.push("Cursor sessionEnd hook (~/.cursor/hooks.json)");
|
|
290
|
-
installedTargets.push("cursor-hook");
|
|
291
|
-
}
|
|
292
|
-
catch (err) {
|
|
293
|
-
console.error(`\nWarning: Failed to install Cursor hook — ${err.message}`);
|
|
294
|
-
}
|
|
295
|
-
try {
|
|
296
|
-
(0, utils_1.installCursorInstructions)();
|
|
297
|
-
installed.push("Cursor agent instructions (~/.cursor/rules/codiedev.mdc)");
|
|
298
|
-
installedTargets.push("cursor-instructions");
|
|
299
|
-
}
|
|
300
|
-
catch (err) {
|
|
301
|
-
console.error(`\nWarning: Failed to install Cursor instructions — ${err.message}`);
|
|
302
|
-
}
|
|
289
|
+
runInstall("cursor-hook", utils_1.installCursorHook, "Cursor sessionEnd hook (~/.cursor/hooks.json)", "cursor-hook");
|
|
290
|
+
runInstall("cursor-instructions", utils_1.installCursorInstructions, "Cursor agent instructions (~/.cursor/rules/codiedev.mdc)", "cursor-instructions");
|
|
303
291
|
}
|
|
304
292
|
// Tell the backend which targets we wired up so the portal's onboarding
|
|
305
293
|
// card flips to "Connected" only when something actually installed —
|
|
@@ -310,10 +298,17 @@ async function runConnect(args = []) {
|
|
|
310
298
|
await postJson(`${BACKEND_URL}/api/cli/markInstalled`, {
|
|
311
299
|
installedTargets,
|
|
312
300
|
}, { Authorization: `Bearer ${token}` });
|
|
301
|
+
(0, shared_1.recordInstallEvent)(config, { step: "completed", ok: true });
|
|
313
302
|
}
|
|
314
303
|
catch (err) {
|
|
315
|
-
|
|
304
|
+
const message = err.message;
|
|
305
|
+
console.error(`\nWarning: Failed to report install to backend — ${message}`);
|
|
316
306
|
console.error("Local install succeeded; portal may not reflect it until you re-run.");
|
|
307
|
+
(0, shared_1.recordInstallEvent)(config, {
|
|
308
|
+
step: "failed",
|
|
309
|
+
ok: false,
|
|
310
|
+
error: message?.slice(0, 500),
|
|
311
|
+
});
|
|
317
312
|
}
|
|
318
313
|
}
|
|
319
314
|
if (!hasClaude && !hasCodex && !hasCursor) {
|
package/dist/hook.js
CHANGED
|
@@ -39,6 +39,66 @@ const https = __importStar(require("https"));
|
|
|
39
39
|
const http = __importStar(require("http"));
|
|
40
40
|
const utils_1 = require("./utils");
|
|
41
41
|
const version_1 = require("./version");
|
|
42
|
+
function getJson(url, bearerToken) {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
44
|
+
const parsed = new URL(url);
|
|
45
|
+
const options = {
|
|
46
|
+
hostname: parsed.hostname,
|
|
47
|
+
port: parsed.port || (parsed.protocol === "https:" ? 443 : 80),
|
|
48
|
+
path: parsed.pathname + parsed.search,
|
|
49
|
+
method: "GET",
|
|
50
|
+
headers: {
|
|
51
|
+
Authorization: `Bearer ${bearerToken}`,
|
|
52
|
+
"X-Codiedev-Cli-Version": version_1.CLI_VERSION,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
const lib = parsed.protocol === "https:" ? https : http;
|
|
56
|
+
const req = lib.request(options, (res) => {
|
|
57
|
+
let body = "";
|
|
58
|
+
res.on("data", (c) => (body += c));
|
|
59
|
+
res.on("end", () => {
|
|
60
|
+
try {
|
|
61
|
+
resolve(JSON.parse(body));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
reject(new Error("bad json"));
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
req.on("error", reject);
|
|
69
|
+
req.end();
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// UserPromptSubmit: query skill discovery for the prompt and, if a team skill
|
|
73
|
+
// clears the confidence threshold, inject it as additionalContext so the agent
|
|
74
|
+
// can offer to pull+run it. Fully silent on miss/error — never blocks input.
|
|
75
|
+
async function runDiscover(prompt) {
|
|
76
|
+
if (!prompt || !prompt.trim())
|
|
77
|
+
process.exit(0);
|
|
78
|
+
const config = (0, utils_1.readConfig)();
|
|
79
|
+
if (!config)
|
|
80
|
+
process.exit(0);
|
|
81
|
+
try {
|
|
82
|
+
const res = await getJson(`${config.backendUrl}/api/cli/skill-discovery?q=${encodeURIComponent(prompt)}`, config.token);
|
|
83
|
+
const skills = res?.skills ?? [];
|
|
84
|
+
if (skills.length > 0) {
|
|
85
|
+
const lines = skills
|
|
86
|
+
.slice(0, 3)
|
|
87
|
+
.map((s) => `- ${s.key} — ${(s.score * 100).toFixed(0)}% match`)
|
|
88
|
+
.join("\n");
|
|
89
|
+
const additionalContext = "A CodieDev team skill may apply to this task:\n" +
|
|
90
|
+
lines +
|
|
91
|
+
"\nIf one fits, pull and use it before improvising: `codiedev pull <key>`.";
|
|
92
|
+
process.stdout.write(JSON.stringify({
|
|
93
|
+
hookSpecificOutput: { hookEventName: "UserPromptSubmit", additionalContext },
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// silent — discovery is best-effort
|
|
99
|
+
}
|
|
100
|
+
process.exit(0);
|
|
101
|
+
}
|
|
42
102
|
function postJson(url, body, bearerToken) {
|
|
43
103
|
return new Promise((resolve, reject) => {
|
|
44
104
|
const parsed = new URL(url);
|
|
@@ -94,7 +154,6 @@ function resolveMode(arg) {
|
|
|
94
154
|
return "claude-code";
|
|
95
155
|
}
|
|
96
156
|
async function main() {
|
|
97
|
-
const mode = resolveMode(process.argv[2]);
|
|
98
157
|
let hookInput = {};
|
|
99
158
|
try {
|
|
100
159
|
const raw = await readStdin();
|
|
@@ -105,6 +164,15 @@ async function main() {
|
|
|
105
164
|
catch {
|
|
106
165
|
process.exit(0);
|
|
107
166
|
}
|
|
167
|
+
// UserPromptSubmit discovery path — separate from session capture.
|
|
168
|
+
if (process.argv[2] === "discover") {
|
|
169
|
+
await runDiscover(hookInput.prompt);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const mode = resolveMode(process.argv[2]);
|
|
173
|
+
// Self-heal: a user who upgraded the CLI but hasn't re-run `connect` gets the
|
|
174
|
+
// discovery hook wired on their next SessionEnd, without re-connecting.
|
|
175
|
+
(0, utils_1.ensureDiscoveryHookInstalled)();
|
|
108
176
|
const { session_id, transcript_path, cwd, workspace_roots } = hookInput;
|
|
109
177
|
const workspaceRoot = workspace_roots && workspace_roots.length > 0
|
|
110
178
|
? workspace_roots[0]
|
package/dist/mcp.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Codiedev MCP server.
|
|
4
4
|
*
|
|
5
|
-
* Exposes the
|
|
5
|
+
* Exposes the Codiedev artifact-collaboration surface as Model Context
|
|
6
6
|
* Protocol tools so Claude Code / Codex / any MCP-capable agent can invoke
|
|
7
7
|
* them natively from natural language (e.g. "push the spec we just worked on").
|
|
8
8
|
*
|
package/dist/mcp.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Codiedev MCP server.
|
|
5
5
|
*
|
|
6
|
-
* Exposes the
|
|
6
|
+
* Exposes the Codiedev artifact-collaboration surface as Model Context
|
|
7
7
|
* Protocol tools so Claude Code / Codex / any MCP-capable agent can invoke
|
|
8
8
|
* them natively from natural language (e.g. "push the spec we just worked on").
|
|
9
9
|
*
|
|
@@ -70,7 +70,7 @@ const PKG_VERSION = (() => {
|
|
|
70
70
|
const TOOLS = [
|
|
71
71
|
{
|
|
72
72
|
name: "codiedev_push",
|
|
73
|
-
description: "Push any markdown artifact (doc or skill) to the team's
|
|
73
|
+
description: "Push any markdown artifact (doc or skill) to the team's Codiedev " +
|
|
74
74
|
"artifact layer so teammates can see, discuss, and run it. Works for " +
|
|
75
75
|
"specs, decisions, proposals, bugfixes, reviews, notes, AND " +
|
|
76
76
|
"Claude Code / Codex skills (files with SKILL.md frontmatter — " +
|
|
@@ -127,7 +127,7 @@ const TOOLS = [
|
|
|
127
127
|
},
|
|
128
128
|
{
|
|
129
129
|
name: "codiedev_pull",
|
|
130
|
-
description: "Fetch any artifact (doc or skill) from the team's
|
|
130
|
+
description: "Fetch any artifact (doc or skill) from the team's Codiedev artifact " +
|
|
131
131
|
"layer. Works for specs, decisions, proposals, bugfixes, reviews, " +
|
|
132
132
|
"notes, AND skills (SKILL.md frontmatter files surfaced as slash " +
|
|
133
133
|
"commands). Use when the user asks to pull, grab, fetch, or resume " +
|
|
@@ -171,7 +171,7 @@ const TOOLS = [
|
|
|
171
171
|
},
|
|
172
172
|
{
|
|
173
173
|
name: "codiedev_ping",
|
|
174
|
-
description: "Send a message to a teammate on
|
|
174
|
+
description: "Send a message to a teammate on Codiedev, optionally attached to a " +
|
|
175
175
|
"specific artifact. Use when the user asks to ping, ask, share with, " +
|
|
176
176
|
"or get feedback from a teammate by name (e.g. 'ping a teammate about " +
|
|
177
177
|
"this spec'). Recipients are resolved by first name, full name, or email " +
|
|
@@ -200,7 +200,7 @@ const TOOLS = [
|
|
|
200
200
|
},
|
|
201
201
|
{
|
|
202
202
|
name: "codiedev_inbox",
|
|
203
|
-
description: "Check the user's
|
|
203
|
+
description: "Check the user's Codiedev inbox for messages from teammates. Use " +
|
|
204
204
|
"when the user asks 'any messages?', 'what did [name] say?', 'check " +
|
|
205
205
|
"my inbox', or when they open a session and want to catch up on " +
|
|
206
206
|
"what's waiting for them. Returns a list with sender, subject " +
|
|
@@ -224,7 +224,7 @@ const TOOLS = [
|
|
|
224
224
|
description: "Capture a passing thought and link it to the current agent session. " +
|
|
225
225
|
"Use when the user says 'note that', 'remember', 'worth capturing', " +
|
|
226
226
|
"'jot this down', or drops a short observation that isn't a full " +
|
|
227
|
-
"spec. Notes show up in the user's
|
|
227
|
+
"spec. Notes show up in the user's Codiedev memory feed.",
|
|
228
228
|
inputSchema: {
|
|
229
229
|
type: "object",
|
|
230
230
|
properties: {
|
|
@@ -238,7 +238,7 @@ const TOOLS = [
|
|
|
238
238
|
},
|
|
239
239
|
{
|
|
240
240
|
name: "codiedev_promote",
|
|
241
|
-
description: "Promote an auto-extracted
|
|
241
|
+
description: "Promote an auto-extracted Codiedev artifact (spec/bugfix/decision " +
|
|
242
242
|
"produced by the session-capture pipeline) to an authored artifact " +
|
|
243
243
|
"so teammates can collaborate on it. Use when the user says 'publish " +
|
|
244
244
|
"the extracted spec', 'promote this', or 'make the auto-extracted X " +
|
|
@@ -248,7 +248,7 @@ const TOOLS = [
|
|
|
248
248
|
properties: {
|
|
249
249
|
artifactId: {
|
|
250
250
|
type: "string",
|
|
251
|
-
description: "The extracted artifact's id from
|
|
251
|
+
description: "The extracted artifact's id from Codiedev.",
|
|
252
252
|
},
|
|
253
253
|
keyOverride: {
|
|
254
254
|
type: "string",
|
|
@@ -545,7 +545,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
545
545
|
content: [
|
|
546
546
|
{
|
|
547
547
|
type: "text",
|
|
548
|
-
text: "
|
|
548
|
+
text: "Codiedev is not connected on this machine. Ask the user to run " +
|
|
549
549
|
"`npx codiedev connect` with an API token from " +
|
|
550
550
|
"https://codiedev.com/portal/integrations/claude-code.",
|
|
551
551
|
},
|
|
@@ -579,7 +579,7 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
|
579
579
|
content: [
|
|
580
580
|
{
|
|
581
581
|
type: "text",
|
|
582
|
-
text: `
|
|
582
|
+
text: `Codiedev error: ${err.message}`,
|
|
583
583
|
},
|
|
584
584
|
],
|
|
585
585
|
};
|
package/dist/utils.d.ts
CHANGED
|
@@ -33,24 +33,26 @@ export declare function cursorInstalled(): boolean;
|
|
|
33
33
|
* the presence of any `github.copilot*` directory as the Copilot fingerprint.
|
|
34
34
|
*/
|
|
35
35
|
export declare function vscodeCopilotInstalled(): boolean;
|
|
36
|
+
export declare function upsertCodiedevHook(hooks: Record<string, unknown>, eventName: string, subcommand: string): void;
|
|
36
37
|
export declare function installHook(): void;
|
|
38
|
+
export declare function ensureDiscoveryHookInstalled(): void;
|
|
37
39
|
export declare function installClaudeCodeInstructions(): void;
|
|
38
40
|
export declare function installCodexInstructions(): void;
|
|
39
41
|
export declare function installCursorInstructions(): void;
|
|
40
42
|
/**
|
|
41
|
-
* Install the
|
|
43
|
+
* Install the Codiedev MCP server into Claude Code's user-scope config.
|
|
42
44
|
* Safe to call multiple times — updates the existing entry if present.
|
|
43
45
|
*/
|
|
44
46
|
export declare function installClaudeCodeMcp(): void;
|
|
45
47
|
/**
|
|
46
|
-
* Install the
|
|
48
|
+
* Install the Codiedev MCP server into Cursor's user-scope config.
|
|
47
49
|
* Cursor uses the same `mcpServers` schema as Claude Code; just a different
|
|
48
50
|
* file path (~/.cursor/mcp.json). Project-scoped servers can also live at
|
|
49
51
|
* <project>/.cursor/mcp.json — we only manage the user-scope one.
|
|
50
52
|
*/
|
|
51
53
|
export declare function installCursorMcp(): void;
|
|
52
54
|
/**
|
|
53
|
-
* Install the
|
|
55
|
+
* Install the Codiedev MCP server into VS Code's user-scope MCP config so
|
|
54
56
|
* GitHub Copilot Chat picks it up. VS Code uses a different schema than
|
|
55
57
|
* Claude / Cursor: top-level key is `servers` (not `mcpServers`). On first
|
|
56
58
|
* Copilot Chat run after install, VS Code prompts the user to trust the
|
|
@@ -84,7 +86,7 @@ export declare function cleanupVSCodeMcp(): void;
|
|
|
84
86
|
*/
|
|
85
87
|
export declare function cleanupCodexMcp(): void;
|
|
86
88
|
/**
|
|
87
|
-
* Best-effort append of the
|
|
89
|
+
* Best-effort append of the Codiedev MCP server block to ~/.codex/config.toml.
|
|
88
90
|
*
|
|
89
91
|
* Codex uses TOML and we don't want a full parser for such a small addition,
|
|
90
92
|
* so we append a well-marked block if one isn't already present. Idempotent.
|
package/dist/utils.js
CHANGED
|
@@ -43,7 +43,9 @@ exports.claudeCodeInstalled = claudeCodeInstalled;
|
|
|
43
43
|
exports.codexInstalled = codexInstalled;
|
|
44
44
|
exports.cursorInstalled = cursorInstalled;
|
|
45
45
|
exports.vscodeCopilotInstalled = vscodeCopilotInstalled;
|
|
46
|
+
exports.upsertCodiedevHook = upsertCodiedevHook;
|
|
46
47
|
exports.installHook = installHook;
|
|
48
|
+
exports.ensureDiscoveryHookInstalled = ensureDiscoveryHookInstalled;
|
|
47
49
|
exports.installClaudeCodeInstructions = installClaudeCodeInstructions;
|
|
48
50
|
exports.installCodexInstructions = installCodexInstructions;
|
|
49
51
|
exports.installCursorInstructions = installCursorInstructions;
|
|
@@ -230,6 +232,27 @@ function vscodeCopilotInstalled() {
|
|
|
230
232
|
return false;
|
|
231
233
|
}
|
|
232
234
|
}
|
|
235
|
+
// Idempotently put a single codiedev hook (matcher ".*", running our hook.js
|
|
236
|
+
// with `subcommand`) into the given hook event, stripping any prior codiedev
|
|
237
|
+
// entries first so connect re-runs / upgrades refresh the resolved binary path.
|
|
238
|
+
// Mutates `hooks` in place. Exported for unit testing.
|
|
239
|
+
function upsertCodiedevHook(hooks, eventName, subcommand) {
|
|
240
|
+
if (!hooks[eventName])
|
|
241
|
+
hooks[eventName] = [];
|
|
242
|
+
const existing = hooks[eventName];
|
|
243
|
+
const filtered = existing.filter((hook) => {
|
|
244
|
+
const inner = hook.hooks;
|
|
245
|
+
if (Array.isArray(inner)) {
|
|
246
|
+
return !inner.some((h) => (0, detection_1.isCodiedevHookCommand)(h.command));
|
|
247
|
+
}
|
|
248
|
+
return !(0, detection_1.isCodiedevHookCommand)(hook.matcher);
|
|
249
|
+
});
|
|
250
|
+
filtered.push({
|
|
251
|
+
matcher: ".*",
|
|
252
|
+
hooks: [{ type: "command", command: resolveHookCommand(subcommand) }],
|
|
253
|
+
});
|
|
254
|
+
hooks[eventName] = filtered;
|
|
255
|
+
}
|
|
233
256
|
function installHook() {
|
|
234
257
|
let settings = {};
|
|
235
258
|
try {
|
|
@@ -245,35 +268,42 @@ function installHook() {
|
|
|
245
268
|
settings.hooks = {};
|
|
246
269
|
}
|
|
247
270
|
const hooks = settings.hooks;
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
// paths) before re-adding so connect re-runs upgrade the resolved binary
|
|
253
|
-
// path instead of leaving stale entries behind.
|
|
254
|
-
const existing = hooks.SessionEnd;
|
|
255
|
-
const filtered = existing.filter((hook) => {
|
|
256
|
-
const inner = hook.hooks;
|
|
257
|
-
if (Array.isArray(inner)) {
|
|
258
|
-
return !inner.some((h) => (0, detection_1.isCodiedevHookCommand)(h.command));
|
|
259
|
-
}
|
|
260
|
-
return !(0, detection_1.isCodiedevHookCommand)(hook.matcher);
|
|
261
|
-
});
|
|
262
|
-
filtered.push({
|
|
263
|
-
matcher: ".*",
|
|
264
|
-
hooks: [
|
|
265
|
-
{
|
|
266
|
-
type: "command",
|
|
267
|
-
command: resolveHookCommand("capture"),
|
|
268
|
-
},
|
|
269
|
-
],
|
|
270
|
-
});
|
|
271
|
-
hooks.SessionEnd = filtered;
|
|
271
|
+
// SessionEnd → capture (upload the finished session for the team library).
|
|
272
|
+
upsertCodiedevHook(hooks, "SessionEnd", "capture");
|
|
273
|
+
// UserPromptSubmit → discover (surface a matching team skill for the task).
|
|
274
|
+
upsertCodiedevHook(hooks, "UserPromptSubmit", "discover");
|
|
272
275
|
if (!fs.existsSync(CLAUDE_DIR)) {
|
|
273
276
|
fs.mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
274
277
|
}
|
|
275
278
|
fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf8");
|
|
276
279
|
}
|
|
280
|
+
// Self-heal: ensure the discovery (UserPromptSubmit) hook is present without a
|
|
281
|
+
// full `connect` re-run. Called from the capture hook on SessionEnd, so a user
|
|
282
|
+
// who upgrades the CLI gets the new hook wired on their next session. Only
|
|
283
|
+
// writes when missing, to avoid churning settings.json every run.
|
|
284
|
+
function ensureDiscoveryHookInstalled() {
|
|
285
|
+
try {
|
|
286
|
+
if (!fs.existsSync(CLAUDE_SETTINGS_PATH))
|
|
287
|
+
return; // not a Claude Code user
|
|
288
|
+
const raw = fs.readFileSync(CLAUDE_SETTINGS_PATH, "utf8");
|
|
289
|
+
const settings = JSON.parse(raw);
|
|
290
|
+
const hooks = (settings.hooks ?? {});
|
|
291
|
+
const ups = (hooks.UserPromptSubmit ?? []);
|
|
292
|
+
const present = ups.some((hook) => {
|
|
293
|
+
const inner = hook.hooks;
|
|
294
|
+
return (Array.isArray(inner) &&
|
|
295
|
+
inner.some((h) => (0, detection_1.isCodiedevHookCommand)(h.command)));
|
|
296
|
+
});
|
|
297
|
+
if (present)
|
|
298
|
+
return;
|
|
299
|
+
settings.hooks = hooks;
|
|
300
|
+
upsertCodiedevHook(hooks, "UserPromptSubmit", "discover");
|
|
301
|
+
fs.writeFileSync(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf8");
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
// Best-effort — never block a session on self-heal.
|
|
305
|
+
}
|
|
306
|
+
}
|
|
277
307
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
278
308
|
// MCP server installation — writes the codiedev MCP server config into each
|
|
279
309
|
// agent CLI's expected location so "npx codiedev connect" wires it up once.
|
|
@@ -284,9 +314,9 @@ const CODEX_USER_INSTRUCTIONS_PATH = path.join(os.homedir(), ".codex", "AGENTS.m
|
|
|
284
314
|
const CODEX_CONFIG_PATH = path.join(os.homedir(), ".codex", "config.toml");
|
|
285
315
|
const CODIEDEV_INSTRUCTIONS_BEGIN = "<!-- codiedev-cli:begin -->";
|
|
286
316
|
const CODIEDEV_INSTRUCTIONS_END = "<!-- codiedev-cli:end -->";
|
|
287
|
-
const CODIEDEV_INSTRUCTIONS_BODY = `##
|
|
317
|
+
const CODIEDEV_INSTRUCTIONS_BODY = `## Codiedev — team artifact layer
|
|
288
318
|
|
|
289
|
-
This machine is connected to
|
|
319
|
+
This machine is connected to Codiedev. Teammates share specs, reviews,
|
|
290
320
|
decisions, and messages via a CLI on every dev's machine.
|
|
291
321
|
|
|
292
322
|
**When the user asks to share, push, ping, check messages, or capture a
|
|
@@ -426,7 +456,7 @@ function installCodexInstructions() {
|
|
|
426
456
|
// Cursor uses .mdc rule files with YAML frontmatter (description, alwaysApply,
|
|
427
457
|
// globs). `.cursorrules` is deprecated and ignored by Agent mode as of 2026.
|
|
428
458
|
const CURSOR_INSTRUCTIONS_FRONTMATTER = `---
|
|
429
|
-
description:
|
|
459
|
+
description: Codiedev team artifact layer — push, pull, search, ping, and share artifacts with teammates
|
|
430
460
|
alwaysApply: true
|
|
431
461
|
---
|
|
432
462
|
|
|
@@ -483,7 +513,7 @@ function readMcpConfigSafely(path) {
|
|
|
483
513
|
}
|
|
484
514
|
}
|
|
485
515
|
/**
|
|
486
|
-
* Install the
|
|
516
|
+
* Install the Codiedev MCP server into Claude Code's user-scope config.
|
|
487
517
|
* Safe to call multiple times — updates the existing entry if present.
|
|
488
518
|
*/
|
|
489
519
|
function installClaudeCodeMcp() {
|
|
@@ -499,7 +529,7 @@ function installClaudeCodeMcp() {
|
|
|
499
529
|
fs.writeFileSync(CLAUDE_USER_CONFIG_PATH, JSON.stringify(config, null, 2), "utf8");
|
|
500
530
|
}
|
|
501
531
|
/**
|
|
502
|
-
* Install the
|
|
532
|
+
* Install the Codiedev MCP server into Cursor's user-scope config.
|
|
503
533
|
* Cursor uses the same `mcpServers` schema as Claude Code; just a different
|
|
504
534
|
* file path (~/.cursor/mcp.json). Project-scoped servers can also live at
|
|
505
535
|
* <project>/.cursor/mcp.json — we only manage the user-scope one.
|
|
@@ -520,7 +550,7 @@ function installCursorMcp() {
|
|
|
520
550
|
fs.writeFileSync(CURSOR_MCP_PATH, JSON.stringify(config, null, 2), "utf8");
|
|
521
551
|
}
|
|
522
552
|
/**
|
|
523
|
-
* Install the
|
|
553
|
+
* Install the Codiedev MCP server into VS Code's user-scope MCP config so
|
|
524
554
|
* GitHub Copilot Chat picks it up. VS Code uses a different schema than
|
|
525
555
|
* Claude / Cursor: top-level key is `servers` (not `mcpServers`). On first
|
|
526
556
|
* Copilot Chat run after install, VS Code prompts the user to trust the
|
|
@@ -635,7 +665,7 @@ function cleanupCodexMcp() {
|
|
|
635
665
|
fs.writeFileSync(CODEX_CONFIG_PATH, out.join("\n"), "utf8");
|
|
636
666
|
}
|
|
637
667
|
/**
|
|
638
|
-
* Best-effort append of the
|
|
668
|
+
* Best-effort append of the Codiedev MCP server block to ~/.codex/config.toml.
|
|
639
669
|
*
|
|
640
670
|
* Codex uses TOML and we don't want a full parser for such a small addition,
|
|
641
671
|
* so we append a well-marked block if one isn't already present. Idempotent.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codiedev",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Connect Claude Code, Codex, Cursor, or VS Code Copilot to
|
|
3
|
+
"version": "0.9.0-beta.0",
|
|
4
|
+
"description": "Connect Claude Code, Codex, Cursor, or VS Code Copilot to Codiedev for org-wide session capture and artifact collaboration",
|
|
5
5
|
"bin": {
|
|
6
6
|
"codiedev": "dist/cli.js",
|
|
7
7
|
"codiedev-hook": "dist/hook.js"
|