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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # codiedev
2
2
 
3
- Connect Claude Code or Codex to CodieDev for org-wide session capture.
3
+ Connect Claude Code or Codex to Codiedev for org-wide session capture.
4
4
 
5
5
  ## Usage
6
6
 
package/dist/cli.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * CodieDev CLI dispatcher.
3
+ * Codiedev CLI dispatcher.
4
4
  *
5
5
  * Subcommands:
6
6
  * codiedev connect — link agent CLIs to your org
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
3
  /**
4
- * CodieDev CLI dispatcher.
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
- CodieDev CLI
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 CodieDev
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
- CodieDev — usage guide
275
+ Codiedev — usage guide
276
276
 
277
277
  ## What this is
278
278
 
279
- CodieDev is a team artifact layer on top of your coding agent (Claude
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 CodieDev account. Run \`codiedev connect\` to refresh, or push the missing repo through Settings.`);
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
  }
@@ -134,13 +134,13 @@ function hasGlabCli() {
134
134
  }
135
135
  async function runDoctor(_args) {
136
136
  const checks = [];
137
- console.log("\nCodieDev doctor — checking your setup…\n");
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: "CodieDev config (~/.codiedev/config.json)",
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: "CodieDev config",
151
+ name: "Codiedev config",
152
152
  status: "pass",
153
153
  detail: `connected to ${config.companyName}`,
154
154
  });
@@ -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 CodieDev workspace.
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 CodieDev config. Exits the process with a helpful
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 CodieDev backend.
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
@@ -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 CodieDev config. Exits the process with a helpful
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 CodieDev backend.
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 CodieDev CLI\n");
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 CodieDev workspace and install");
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 CodieDev API token: ");
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
- if (hasClaude) {
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
- (0, utils_1.installHook)();
253
- installed.push("Claude Code SessionEnd hook (~/.claude/settings.json)");
254
- installedTargets.push("claudeCode-hook");
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
- console.error(`\nWarning: Failed to install Claude Code instructions — ${err.message}`);
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
- try {
270
- (0, utils_1.installCodexHook)();
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
- try {
288
- (0, utils_1.installCursorHook)();
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
- console.error(`\nWarning: Failed to report install to backend — ${err.message}`);
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
- * CodieDev MCP server.
3
+ * Codiedev MCP server.
4
4
  *
5
- * Exposes the CodieDev artifact-collaboration surface as Model Context
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
- * CodieDev MCP server.
4
+ * Codiedev MCP server.
5
5
  *
6
- * Exposes the CodieDev artifact-collaboration surface as Model Context
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 CodieDev " +
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 CodieDev artifact " +
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 CodieDev, optionally attached to a " +
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 CodieDev inbox for messages from teammates. Use " +
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 CodieDev memory feed.",
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 CodieDev artifact (spec/bugfix/decision " +
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 CodieDev.",
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: "CodieDev is not connected on this machine. Ask the user to run " +
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: `CodieDev error: ${err.message}`,
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 CodieDev MCP server into Claude Code's user-scope config.
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 CodieDev MCP server into Cursor's user-scope config.
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 CodieDev MCP server into VS Code's user-scope MCP config so
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 CodieDev MCP server block to ~/.codex/config.toml.
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
- if (!hooks.SessionEnd) {
249
- hooks.SessionEnd = [];
250
- }
251
- // Drop any prior codiedev entries (legacy `npx` form or older absolute
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 = `## CodieDev — team artifact layer
317
+ const CODIEDEV_INSTRUCTIONS_BODY = `## Codiedev — team artifact layer
288
318
 
289
- This machine is connected to CodieDev. Teammates share specs, reviews,
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: CodieDev team artifact layer — push, pull, search, ping, and share artifacts with teammates
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 CodieDev MCP server into Claude Code's user-scope config.
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 CodieDev MCP server into Cursor's user-scope config.
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 CodieDev MCP server into VS Code's user-scope MCP config so
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 CodieDev MCP server block to ~/.codex/config.toml.
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.8.1",
4
- "description": "Connect Claude Code, Codex, Cursor, or VS Code Copilot to CodieDev for org-wide session capture and artifact collaboration",
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"