codiedev 0.2.0 → 0.3.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.
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.requireConfig = requireConfig;
37
+ exports.apiRequest = apiRequest;
38
+ exports.timeAgo = timeAgo;
39
+ exports.stripQuotes = stripQuotes;
40
+ const https = __importStar(require("https"));
41
+ const http = __importStar(require("http"));
42
+ const utils_1 = require("../utils");
43
+ /**
44
+ * Require a connected CodieDev config. Exits the process with a helpful
45
+ * message if the user hasn't run `codiedev connect` yet.
46
+ */
47
+ function requireConfig() {
48
+ const config = (0, utils_1.readConfig)();
49
+ if (!config) {
50
+ console.error("Not connected. Run `npx codiedev connect` first and enter your API token.");
51
+ process.exit(1);
52
+ }
53
+ return config;
54
+ }
55
+ /**
56
+ * Perform an authenticated HTTP request against the CodieDev backend.
57
+ * Returns the parsed JSON response body. Throws on non-2xx.
58
+ */
59
+ function apiRequest(method, path, options) {
60
+ return new Promise((resolve, reject) => {
61
+ const base = options.config.backendUrl;
62
+ const url = new URL(path, base);
63
+ if (options.query) {
64
+ for (const [k, v] of Object.entries(options.query)) {
65
+ if (v === undefined)
66
+ continue;
67
+ url.searchParams.set(k, String(v));
68
+ }
69
+ }
70
+ const data = options.body ? JSON.stringify(options.body) : undefined;
71
+ const requestOptions = {
72
+ hostname: url.hostname,
73
+ port: url.port || (url.protocol === "https:" ? 443 : 80),
74
+ path: url.pathname + url.search,
75
+ method,
76
+ headers: {
77
+ Authorization: `Bearer ${options.config.token}`,
78
+ ...(data
79
+ ? {
80
+ "Content-Type": "application/json",
81
+ "Content-Length": Buffer.byteLength(data),
82
+ }
83
+ : {}),
84
+ },
85
+ };
86
+ const lib = url.protocol === "https:" ? https : http;
87
+ const req = lib.request(requestOptions, (res) => {
88
+ let chunks = "";
89
+ res.on("data", (c) => (chunks += c));
90
+ res.on("end", () => {
91
+ try {
92
+ const parsed = chunks ? JSON.parse(chunks) : {};
93
+ if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
94
+ resolve(parsed);
95
+ }
96
+ else {
97
+ const errMsg = (parsed && typeof parsed === "object" && "error" in parsed
98
+ ? parsed.error
99
+ : undefined) || `HTTP ${res.statusCode}`;
100
+ reject(Object.assign(new Error(errMsg), {
101
+ status: res.statusCode,
102
+ body: parsed,
103
+ }));
104
+ }
105
+ }
106
+ catch {
107
+ reject(new Error(`Failed to parse response: ${chunks}`));
108
+ }
109
+ });
110
+ });
111
+ req.on("error", reject);
112
+ if (data)
113
+ req.write(data);
114
+ req.end();
115
+ });
116
+ }
117
+ /**
118
+ * Relative-time formatter for inbox / listing output.
119
+ */
120
+ function timeAgo(ts) {
121
+ const diff = Date.now() - ts;
122
+ const mins = Math.floor(diff / 60000);
123
+ if (mins < 1)
124
+ return "just now";
125
+ if (mins < 60)
126
+ return `${mins}m ago`;
127
+ const hours = Math.floor(mins / 60);
128
+ if (hours < 24)
129
+ return `${hours}h ago`;
130
+ const days = Math.floor(hours / 24);
131
+ return `${days}d ago`;
132
+ }
133
+ /**
134
+ * Strip surrounding quotes from a CLI argument if the user wrapped it.
135
+ * `codiedev ping maya "hey"` on some shells passes the quotes through.
136
+ */
137
+ function stripQuotes(s) {
138
+ if ((s.startsWith('"') && s.endsWith('"')) ||
139
+ (s.startsWith("'") && s.endsWith("'"))) {
140
+ return s.slice(1, -1);
141
+ }
142
+ return s;
143
+ }
package/dist/connect.d.ts CHANGED
@@ -1,2 +1 @@
1
- #!/usr/bin/env node
2
- export {};
1
+ export declare function runConnect(): Promise<void>;
package/dist/connect.js CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  "use strict";
3
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
3
  if (k2 === undefined) k2 = k;
@@ -34,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
33
  };
35
34
  })();
36
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runConnect = runConnect;
37
37
  const readline = __importStar(require("readline"));
38
38
  const https = __importStar(require("https"));
39
39
  const http = __importStar(require("http"));
@@ -86,13 +86,13 @@ function postJson(url, body) {
86
86
  req.end();
87
87
  });
88
88
  }
89
- async function main() {
89
+ async function runConnect() {
90
90
  const rl = readline.createInterface({
91
91
  input: process.stdin,
92
92
  output: process.stdout,
93
93
  });
94
94
  console.log("\nWelcome to CodieDev CLI\n");
95
- console.log("This will connect Claude Code to your CodieDev workspace and install");
95
+ console.log("This will connect your coding agent to your CodieDev workspace and install");
96
96
  console.log("the session capture hook for org-wide session sharing.\n");
97
97
  let token;
98
98
  try {
@@ -145,20 +145,36 @@ async function main() {
145
145
  if (hasClaude) {
146
146
  try {
147
147
  (0, utils_1.installHook)();
148
- installed.push("Claude Code (SessionEnd ~/.claude/settings.json)");
148
+ installed.push("Claude Code SessionEnd hook (~/.claude/settings.json)");
149
149
  }
150
150
  catch (err) {
151
151
  console.error(`\nWarning: Failed to install Claude Code hook — ${err.message}`);
152
152
  }
153
+ try {
154
+ (0, utils_1.installClaudeCodeMcp)();
155
+ installed.push("Claude Code MCP server (~/.claude.json)");
156
+ }
157
+ catch (err) {
158
+ console.error(`\nWarning: Failed to install Claude Code MCP server — ${err.message}`);
159
+ }
153
160
  }
154
161
  if (hasCodex) {
155
162
  try {
156
163
  (0, utils_1.installCodexHook)();
157
- installed.push("Codex (Stop ~/.codex/hooks.json)");
164
+ installed.push("Codex Stop hook (~/.codex/hooks.json)");
158
165
  }
159
166
  catch (err) {
160
167
  console.error(`\nWarning: Failed to install Codex hook — ${err.message}`);
161
168
  }
169
+ try {
170
+ const added = (0, utils_1.installCodexMcp)();
171
+ if (added) {
172
+ installed.push("Codex MCP server (~/.codex/config.toml)");
173
+ }
174
+ }
175
+ catch (err) {
176
+ console.error(`\nWarning: Failed to install Codex MCP server — ${err.message}`);
177
+ }
162
178
  }
163
179
  if (!hasClaude && !hasCodex) {
164
180
  console.warn("\nNo Claude Code (~/.claude) or Codex (~/.codex) install detected.");
@@ -177,7 +193,3 @@ async function main() {
177
193
  console.log();
178
194
  }
179
195
  }
180
- main().catch((err) => {
181
- console.error("Unexpected error:", err);
182
- process.exit(1);
183
- });
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CodieDev MCP server.
4
+ *
5
+ * Exposes the CodieDev artifact-collaboration surface as Model Context
6
+ * Protocol tools so Claude Code / Codex / any MCP-capable agent can invoke
7
+ * them natively from natural language (e.g. "push the spec we just worked on").
8
+ *
9
+ * Reads the same ~/.codiedev/config.json the CLI uses. Install + wire up by
10
+ * running `npx codiedev connect`.
11
+ */
12
+ export {};
package/dist/mcp.js ADDED
@@ -0,0 +1,425 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * CodieDev MCP server.
5
+ *
6
+ * Exposes the CodieDev artifact-collaboration surface as Model Context
7
+ * Protocol tools so Claude Code / Codex / any MCP-capable agent can invoke
8
+ * them natively from natural language (e.g. "push the spec we just worked on").
9
+ *
10
+ * Reads the same ~/.codiedev/config.json the CLI uses. Install + wire up by
11
+ * running `npx codiedev connect`.
12
+ */
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || (function () {
30
+ var ownKeys = function(o) {
31
+ ownKeys = Object.getOwnPropertyNames || function (o) {
32
+ var ar = [];
33
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
34
+ return ar;
35
+ };
36
+ return ownKeys(o);
37
+ };
38
+ return function (mod) {
39
+ if (mod && mod.__esModule) return mod;
40
+ var result = {};
41
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
42
+ __setModuleDefault(result, mod);
43
+ return result;
44
+ };
45
+ })();
46
+ Object.defineProperty(exports, "__esModule", { value: true });
47
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
48
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
49
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
50
+ const fs = __importStar(require("fs"));
51
+ const path = __importStar(require("path"));
52
+ const utils_1 = require("./utils");
53
+ const shared_1 = require("./commands/shared");
54
+ const PKG_NAME = "codiedev";
55
+ const PKG_VERSION = "0.3.0";
56
+ // ─────────────────────────────────────────────────────────────────────────────
57
+ // Tool definitions — descriptions tuned so Claude/Codex resolve natural-language
58
+ // requests into the right tool without manual steering.
59
+ // ─────────────────────────────────────────────────────────────────────────────
60
+ const TOOLS = [
61
+ {
62
+ name: "codiedev_push",
63
+ description: "Push a spec, review, decision, proposal, bugfix, or note to the " +
64
+ "team's CodieDev artifact layer so teammates can see and discuss it. " +
65
+ "Use when the user asks to share, push, save, or publish something " +
66
+ "(e.g., 'push this spec', 'share the review with the team', 'save as " +
67
+ "a decision'). Prefer passing the filename of a recently-edited local " +
68
+ "file — provide its current contents as `markdown`. Filename should " +
69
+ "end in .md and start with the artifact type (spec-, review-, " +
70
+ "decision-, proposal-, bugfix-, or note-).",
71
+ inputSchema: {
72
+ type: "object",
73
+ properties: {
74
+ filename: {
75
+ type: "string",
76
+ description: "Basename of the artifact file, e.g. 'spec-214.md'. Becomes the " +
77
+ "artifact's key for pulling and pinging.",
78
+ },
79
+ markdown: {
80
+ type: "string",
81
+ description: "Full markdown content of the artifact.",
82
+ },
83
+ type: {
84
+ type: "string",
85
+ enum: ["spec", "bugfix", "decision", "proposal", "review", "note"],
86
+ description: "Override the inferred type. Usually leave unset — type is " +
87
+ "inferred from filename prefix.",
88
+ },
89
+ },
90
+ required: ["filename", "markdown"],
91
+ },
92
+ },
93
+ {
94
+ name: "codiedev_pull",
95
+ description: "Fetch an artifact (spec, review, decision, proposal, bugfix, or " +
96
+ "note) from the team's CodieDev artifact layer. Use when the user " +
97
+ "asks to pull, grab, fetch, or resume work on a named artifact, or " +
98
+ "when they want to see what a teammate pushed. Returns the full " +
99
+ "markdown content.",
100
+ inputSchema: {
101
+ type: "object",
102
+ properties: {
103
+ key: {
104
+ type: "string",
105
+ description: "Filename key of the artifact to pull, e.g. 'spec-214.md'.",
106
+ },
107
+ version: {
108
+ type: "integer",
109
+ description: "Specific version to pull. Omit for the latest version.",
110
+ },
111
+ },
112
+ required: ["key"],
113
+ },
114
+ },
115
+ {
116
+ name: "codiedev_ping",
117
+ description: "Send a message to a teammate on CodieDev, optionally attached to a " +
118
+ "specific artifact. Use when the user asks to ping, ask, share with, " +
119
+ "or get feedback from a teammate by name (e.g. 'ping Maya about this " +
120
+ "spec'). Recipients are resolved by first name, full name, or email " +
121
+ "within the current org. They're notified by email and can reply " +
122
+ "from their own agent.",
123
+ inputSchema: {
124
+ type: "object",
125
+ properties: {
126
+ to: {
127
+ type: "string",
128
+ description: "Teammate's first name, full name, or email. Ambiguous matches " +
129
+ "return a list of candidates — reprompt the user if so.",
130
+ },
131
+ body: {
132
+ type: "string",
133
+ description: "Message body — the actual ask/comment.",
134
+ },
135
+ subjectKey: {
136
+ type: "string",
137
+ description: "Optional filename key of an artifact the message is about " +
138
+ "(e.g. 'spec-214.md'). Leaving blank sends a standalone ping.",
139
+ },
140
+ },
141
+ required: ["to", "body"],
142
+ },
143
+ },
144
+ {
145
+ name: "codiedev_inbox",
146
+ description: "Check the user's CodieDev inbox for messages from teammates. Use " +
147
+ "when the user asks 'any messages?', 'what did [name] say?', 'check " +
148
+ "my inbox', or when they open a session and want to catch up on " +
149
+ "what's waiting for them. Returns a list with sender, subject " +
150
+ "artifact, time, and preview.",
151
+ inputSchema: {
152
+ type: "object",
153
+ properties: {
154
+ unreadOnly: {
155
+ type: "boolean",
156
+ description: "Only return unread pings. Default false.",
157
+ },
158
+ limit: {
159
+ type: "integer",
160
+ description: "Max pings to return. Default 50.",
161
+ },
162
+ },
163
+ },
164
+ },
165
+ {
166
+ name: "codiedev_note",
167
+ description: "Capture a passing thought and link it to the current agent session. " +
168
+ "Use when the user says 'note that', 'remember', 'worth capturing', " +
169
+ "'jot this down', or drops a short observation that isn't a full " +
170
+ "spec. Notes show up in the user's CodieDev memory feed.",
171
+ inputSchema: {
172
+ type: "object",
173
+ properties: {
174
+ body: {
175
+ type: "string",
176
+ description: "The thought to capture.",
177
+ },
178
+ },
179
+ required: ["body"],
180
+ },
181
+ },
182
+ {
183
+ name: "codiedev_promote",
184
+ description: "Promote an auto-extracted CodieDev artifact (spec/bugfix/decision " +
185
+ "produced by the session-capture pipeline) to an authored artifact " +
186
+ "so teammates can collaborate on it. Use when the user says 'publish " +
187
+ "the extracted spec', 'promote this', or 'make the auto-extracted X " +
188
+ "shareable'.",
189
+ inputSchema: {
190
+ type: "object",
191
+ properties: {
192
+ artifactId: {
193
+ type: "string",
194
+ description: "The extracted artifact's id from CodieDev.",
195
+ },
196
+ keyOverride: {
197
+ type: "string",
198
+ description: "Optional filename key for the promoted artifact. If omitted, " +
199
+ "one is generated from the artifact's type + session id.",
200
+ },
201
+ },
202
+ required: ["artifactId"],
203
+ },
204
+ },
205
+ ];
206
+ // ─────────────────────────────────────────────────────────────────────────────
207
+ // Server
208
+ // ─────────────────────────────────────────────────────────────────────────────
209
+ const server = new index_js_1.Server({ name: PKG_NAME, version: PKG_VERSION }, { capabilities: { tools: {} } });
210
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
211
+ tools: TOOLS,
212
+ }));
213
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
214
+ const config = (0, utils_1.readConfig)();
215
+ if (!config) {
216
+ return {
217
+ isError: true,
218
+ content: [
219
+ {
220
+ type: "text",
221
+ text: "CodieDev is not connected on this machine. Ask the user to run " +
222
+ "`npx codiedev connect` with an API token from " +
223
+ "https://codiedev.com/portal/integrations/claude-code.",
224
+ },
225
+ ],
226
+ };
227
+ }
228
+ const { name, arguments: argsRaw } = request.params;
229
+ const args = (argsRaw ?? {});
230
+ try {
231
+ switch (name) {
232
+ case "codiedev_push":
233
+ return await handlePush(args, config);
234
+ case "codiedev_pull":
235
+ return await handlePull(args, config);
236
+ case "codiedev_ping":
237
+ return await handlePing(args, config);
238
+ case "codiedev_inbox":
239
+ return await handleInbox(args, config);
240
+ case "codiedev_note":
241
+ return await handleNote(args, config);
242
+ case "codiedev_promote":
243
+ return await handlePromote(args, config);
244
+ default:
245
+ return {
246
+ isError: true,
247
+ content: [{ type: "text", text: `Unknown tool: ${name}` }],
248
+ };
249
+ }
250
+ }
251
+ catch (err) {
252
+ return {
253
+ isError: true,
254
+ content: [
255
+ {
256
+ type: "text",
257
+ text: `CodieDev error: ${err.message}`,
258
+ },
259
+ ],
260
+ };
261
+ }
262
+ });
263
+ async function handlePush(args, config) {
264
+ const filename = asString(args.filename);
265
+ const markdown = asString(args.markdown);
266
+ const type = asStringOrUndefined(args.type);
267
+ if (!filename)
268
+ throw new Error("filename required");
269
+ if (!markdown)
270
+ throw new Error("markdown required");
271
+ const res = await (0, shared_1.apiRequest)("POST", "/api/cli/push", {
272
+ config,
273
+ body: { filename, markdown, type },
274
+ });
275
+ return {
276
+ content: [
277
+ {
278
+ type: "text",
279
+ text: `✓ Pushed ${filename} (type=${res.type}, version=v${res.version}, id=${res.artifactId}).\n` +
280
+ `Teammates can now pull it with \`codiedev pull ${filename}\`.`,
281
+ },
282
+ ],
283
+ };
284
+ }
285
+ async function handlePull(args, config) {
286
+ const key = asString(args.key);
287
+ const version = asIntOrUndefined(args.version);
288
+ if (!key)
289
+ throw new Error("key required");
290
+ const res = await (0, shared_1.apiRequest)("GET", "/api/cli/pull", { config, query: { key, version } });
291
+ const a = res.artifact;
292
+ const header = `# ${a.title}\n\n` +
293
+ `**${a.type}** · v${a.version ?? 1} · ${a.lifecycle ?? "draft"} · ` +
294
+ `updated ${(0, shared_1.timeAgo)(a.updatedAt ?? a.createdAt)}\n\n---\n\n`;
295
+ return {
296
+ content: [{ type: "text", text: header + a.markdown }],
297
+ };
298
+ }
299
+ async function handlePing(args, config) {
300
+ const to = asString(args.to);
301
+ const body = asString(args.body);
302
+ const subjectKey = asStringOrUndefined(args.subjectKey);
303
+ if (!to)
304
+ throw new Error("to required");
305
+ if (!body)
306
+ throw new Error("body required");
307
+ try {
308
+ const res = await (0, shared_1.apiRequest)("POST", "/api/cli/ping", { config, body: { to, body, subjectKey } });
309
+ return {
310
+ content: [
311
+ {
312
+ type: "text",
313
+ text: `✓ Pinged ${res.recipient.name} <${res.recipient.email}>` +
314
+ (subjectKey ? ` about ${subjectKey}.` : "."),
315
+ },
316
+ ],
317
+ };
318
+ }
319
+ catch (err) {
320
+ const e = err;
321
+ if (e.status === 409 && e.body?.candidates?.length) {
322
+ const list = e.body.candidates
323
+ .map((c) => `- ${c.name} <${c.email}>`)
324
+ .join("\n");
325
+ return {
326
+ isError: true,
327
+ content: [
328
+ {
329
+ type: "text",
330
+ text: `Multiple recipients matched "${to}". Ask the user which:\n${list}`,
331
+ },
332
+ ],
333
+ };
334
+ }
335
+ throw err;
336
+ }
337
+ }
338
+ async function handleInbox(args, config) {
339
+ const unreadOnly = args.unreadOnly === true;
340
+ const limit = asIntOrUndefined(args.limit);
341
+ const res = await (0, shared_1.apiRequest)("GET", "/api/cli/inbox", {
342
+ config,
343
+ query: { unreadOnly: unreadOnly ? "1" : undefined, limit },
344
+ });
345
+ if (res.pings.length === 0) {
346
+ return {
347
+ content: [
348
+ {
349
+ type: "text",
350
+ text: unreadOnly ? "No unread messages." : "Inbox is empty.",
351
+ },
352
+ ],
353
+ };
354
+ }
355
+ const unreadCount = res.pings.filter((p) => !p.readAt).length;
356
+ const lines = [
357
+ `Inbox · ${res.pings.length} ${unreadOnly ? "unread" : "recent"}${unreadOnly ? "" : ` · ${unreadCount} unread`}`,
358
+ "",
359
+ ];
360
+ for (const p of res.pings) {
361
+ const status = p.readAt ? "·" : "●";
362
+ const from = p.from?.name ?? "unknown";
363
+ const where = p.subjectKey ? ` · on ${p.subjectKey}` : "";
364
+ lines.push(`${status} ${from}${where} · ${(0, shared_1.timeAgo)(p.createdAt)}`);
365
+ lines.push(` ${p.preview || "(no body)"}`);
366
+ lines.push(` id: ${p.pingId}`);
367
+ lines.push("");
368
+ }
369
+ return {
370
+ content: [{ type: "text", text: lines.join("\n") }],
371
+ };
372
+ }
373
+ async function handleNote(args, config) {
374
+ const body = asString(args.body);
375
+ if (!body)
376
+ throw new Error("body required");
377
+ const res = await (0, shared_1.apiRequest)("POST", "/api/cli/note", { config, body: { body } });
378
+ return {
379
+ content: [{ type: "text", text: `✓ Noted (${res.key})` }],
380
+ };
381
+ }
382
+ async function handlePromote(args, config) {
383
+ const artifactId = asString(args.artifactId);
384
+ const keyOverride = asStringOrUndefined(args.keyOverride);
385
+ if (!artifactId)
386
+ throw new Error("artifactId required");
387
+ const res = await (0, shared_1.apiRequest)("POST", "/api/cli/promote", { config, body: { artifactId, keyOverride } });
388
+ return {
389
+ content: [
390
+ {
391
+ type: "text",
392
+ text: `✓ Promoted to authored artifact ${res.key} (id=${res.artifactId}).`,
393
+ },
394
+ ],
395
+ };
396
+ }
397
+ // ─────────────────────────────────────────────────────────────────────────────
398
+ // Helpers
399
+ // ─────────────────────────────────────────────────────────────────────────────
400
+ function asString(v) {
401
+ return typeof v === "string" ? v : "";
402
+ }
403
+ function asStringOrUndefined(v) {
404
+ return typeof v === "string" ? v : undefined;
405
+ }
406
+ function asIntOrUndefined(v) {
407
+ return typeof v === "number" && Number.isInteger(v) ? v : undefined;
408
+ }
409
+ // ─────────────────────────────────────────────────────────────────────────────
410
+ // Main
411
+ // ─────────────────────────────────────────────────────────────────────────────
412
+ async function main() {
413
+ const transport = new stdio_js_1.StdioServerTransport();
414
+ await server.connect(transport);
415
+ // stdio transport handles I/O until process exits.
416
+ // Log to stderr only — stdout is reserved for MCP protocol.
417
+ process.stderr.write(`[codiedev-mcp] ready (v${PKG_VERSION}, config ${(0, utils_1.readConfig)() ? "present" : "missing"})\n`);
418
+ }
419
+ main().catch((err) => {
420
+ process.stderr.write(`[codiedev-mcp] fatal: ${err.message}\n`);
421
+ process.exit(1);
422
+ });
423
+ // Avoid "Cannot find name 'path', 'fs'" since we imported them but may not use.
424
+ void path;
425
+ void fs;
package/dist/utils.d.ts CHANGED
@@ -27,6 +27,18 @@ export declare function hashToken(token: string): string;
27
27
  export declare function claudeCodeInstalled(): boolean;
28
28
  export declare function codexInstalled(): boolean;
29
29
  export declare function installHook(): void;
30
+ /**
31
+ * Install the CodieDev MCP server into Claude Code's user-scope config.
32
+ * Safe to call multiple times — updates the existing entry if present.
33
+ */
34
+ export declare function installClaudeCodeMcp(): void;
35
+ /**
36
+ * Best-effort append of the CodieDev MCP server block to ~/.codex/config.toml.
37
+ *
38
+ * Codex uses TOML and we don't want a full parser for such a small addition,
39
+ * so we append a well-marked block if one isn't already present. Idempotent.
40
+ */
41
+ export declare function installCodexMcp(): boolean;
30
42
  export declare function installCodexHook(): void;
31
43
  export interface ParsedStats {
32
44
  messageCount: number;