codiedev 0.7.9 → 0.7.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +47 -6
- package/dist/commands/ask.js +12 -0
- package/dist/commands/delete.js +12 -0
- package/dist/commands/doctor.js +6 -31
- package/dist/commands/inbox.js +30 -0
- package/dist/commands/library.d.ts +2 -0
- package/dist/commands/library.js +105 -0
- package/dist/commands/note.js +12 -0
- package/dist/commands/ping.js +12 -0
- package/dist/commands/post.d.ts +2 -0
- package/dist/commands/post.js +96 -0
- package/dist/commands/promote.js +12 -0
- package/dist/commands/pull.js +12 -0
- package/dist/commands/push.js +12 -0
- package/dist/commands/react.d.ts +2 -0
- package/dist/commands/react.js +40 -0
- package/dist/commands/reverseTicket.js +30 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +72 -0
- package/dist/commands/send.d.ts +2 -0
- package/dist/commands/send.js +67 -0
- package/dist/commands/share.d.ts +2 -0
- package/dist/commands/share.js +87 -0
- package/dist/commands/shared.d.ts +19 -1
- package/dist/commands/shared.js +46 -1
- package/dist/connect.js +118 -52
- package/dist/detection.d.ts +18 -0
- package/dist/detection.js +28 -0
- package/dist/hook.js +0 -0
- package/dist/mcp.js +7 -7
- package/dist/secret.d.ts +1 -0
- package/dist/secret.js +14 -0
- package/dist/utils.d.ts +22 -0
- package/dist/utils.js +122 -32
- package/package.json +1 -1
package/dist/connect.js
CHANGED
|
@@ -42,14 +42,76 @@ const connectFlow_1 = require("./connectFlow");
|
|
|
42
42
|
const version_1 = require("./version");
|
|
43
43
|
const upgradeNudge_1 = require("./upgradeNudge");
|
|
44
44
|
const BACKEND_URL = process.env.CODIEDEV_URL || "https://judicious-falcon-861.convex.site";
|
|
45
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Reads a line from stdin without echoing the typed/pasted characters —
|
|
47
|
+
* for tokens, passwords, anything that shouldn't end up in terminal
|
|
48
|
+
* scrollback or screenshots. Echoes `*` per character so the user gets
|
|
49
|
+
* visual feedback that input is being received.
|
|
50
|
+
*
|
|
51
|
+
* Falls back to the regular `prompt()` if stdin isn't a TTY (e.g., piped
|
|
52
|
+
* input in CI). Without a TTY there's no echo to suppress anyway, and
|
|
53
|
+
* raw mode would error.
|
|
54
|
+
*/
|
|
55
|
+
function promptHidden(question) {
|
|
46
56
|
return new Promise((resolve) => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
const stdin = process.stdin;
|
|
58
|
+
const stdout = process.stdout;
|
|
59
|
+
if (!stdin.isTTY) {
|
|
60
|
+
// CI / piped input — no terminal to mute. Fall back to readline.
|
|
61
|
+
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
62
|
+
rl.question(question, (answer) => {
|
|
63
|
+
rl.close();
|
|
64
|
+
resolve(answer.trim());
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
stdout.write(question);
|
|
69
|
+
let buf = "";
|
|
70
|
+
const cleanup = () => {
|
|
71
|
+
stdin.removeListener("data", onData);
|
|
72
|
+
stdin.setRawMode(false);
|
|
73
|
+
stdin.pause();
|
|
74
|
+
};
|
|
75
|
+
const onData = (chunk) => {
|
|
76
|
+
const key = chunk.toString("utf8");
|
|
77
|
+
// Iterate so a single `chunk` containing a multi-char paste still
|
|
78
|
+
// masks each character individually.
|
|
79
|
+
for (const ch of key) {
|
|
80
|
+
// Ctrl+C — abort like a normal terminal interrupt.
|
|
81
|
+
if (ch === "") {
|
|
82
|
+
cleanup();
|
|
83
|
+
stdout.write("\n");
|
|
84
|
+
process.exit(130);
|
|
85
|
+
}
|
|
86
|
+
// Enter — finalize.
|
|
87
|
+
if (ch === "\r" || ch === "\n") {
|
|
88
|
+
cleanup();
|
|
89
|
+
stdout.write("\n");
|
|
90
|
+
resolve(buf.trim());
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
// Backspace / DEL — erase one masked char.
|
|
94
|
+
if (ch === "" || ch === "\b") {
|
|
95
|
+
if (buf.length > 0) {
|
|
96
|
+
buf = buf.slice(0, -1);
|
|
97
|
+
stdout.write("\b \b");
|
|
98
|
+
}
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
// Ignore other control bytes (escape sequences from arrow keys, etc.)
|
|
102
|
+
if (ch.charCodeAt(0) < 0x20)
|
|
103
|
+
continue;
|
|
104
|
+
buf += ch;
|
|
105
|
+
stdout.write("*");
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
stdin.setRawMode(true);
|
|
109
|
+
stdin.resume();
|
|
110
|
+
stdin.setEncoding("utf8");
|
|
111
|
+
stdin.on("data", onData);
|
|
50
112
|
});
|
|
51
113
|
}
|
|
52
|
-
function postJson(url, body) {
|
|
114
|
+
function postJson(url, body, extraHeaders = {}) {
|
|
53
115
|
return new Promise((resolve, reject) => {
|
|
54
116
|
const parsed = new URL(url);
|
|
55
117
|
const data = JSON.stringify(body);
|
|
@@ -62,6 +124,7 @@ function postJson(url, body) {
|
|
|
62
124
|
"Content-Type": "application/json",
|
|
63
125
|
"Content-Length": Buffer.byteLength(data),
|
|
64
126
|
"X-Codiedev-Cli-Version": version_1.CLI_VERSION,
|
|
127
|
+
...extraHeaders,
|
|
65
128
|
},
|
|
66
129
|
};
|
|
67
130
|
const lib = parsed.protocol === "https:" ? https : http;
|
|
@@ -109,16 +172,9 @@ async function runConnect(args = []) {
|
|
|
109
172
|
console.log(`Reusing API token from ~/.codiedev/config.json (run with --force to use a new one).\n`);
|
|
110
173
|
}
|
|
111
174
|
else {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
});
|
|
116
|
-
try {
|
|
117
|
-
token = await prompt(rl, "Enter your CodieDev API token: ");
|
|
118
|
-
}
|
|
119
|
-
finally {
|
|
120
|
-
rl.close();
|
|
121
|
-
}
|
|
175
|
+
// Hidden prompt — masks the token with `*` as it's typed/pasted so it
|
|
176
|
+
// doesn't end up in terminal scrollback or screenshots.
|
|
177
|
+
token = await promptHidden("Enter your CodieDev API token: ");
|
|
122
178
|
if (!token) {
|
|
123
179
|
console.error("Error: API token is required.");
|
|
124
180
|
process.exit(1);
|
|
@@ -158,16 +214,44 @@ async function runConnect(args = []) {
|
|
|
158
214
|
console.error(`\nError: Failed to save config — ${err.message}`);
|
|
159
215
|
process.exit(1);
|
|
160
216
|
}
|
|
217
|
+
// Migration: strip MCP server entries written by pre-CLI-only versions
|
|
218
|
+
// of `codiedev connect`. These pointed at `npx codiedev-mcp`, a binary
|
|
219
|
+
// that was removed in 0.3.4 and that 0.7.11+ no longer ships. Without
|
|
220
|
+
// cleanup, customers upgrading from any prior version see persistent
|
|
221
|
+
// "MCP server failed to start" errors in their agent UIs even though
|
|
222
|
+
// the CLI works fine. Each helper is idempotent and a no-op when the
|
|
223
|
+
// relevant config file doesn't exist.
|
|
224
|
+
//
|
|
225
|
+
// Each call is wrapped because the user's upgrade path matters more
|
|
226
|
+
// than the cleanup. A locked / wrong-owner config file (rare but real)
|
|
227
|
+
// shouldn't block the install loop or strand them on an old version.
|
|
228
|
+
for (const [label, fn] of [
|
|
229
|
+
["Claude Code", utils_1.cleanupClaudeCodeMcp],
|
|
230
|
+
["Codex", utils_1.cleanupCodexMcp],
|
|
231
|
+
["Cursor", utils_1.cleanupCursorMcp],
|
|
232
|
+
["VS Code", utils_1.cleanupVSCodeMcp],
|
|
233
|
+
]) {
|
|
234
|
+
try {
|
|
235
|
+
fn();
|
|
236
|
+
}
|
|
237
|
+
catch (err) {
|
|
238
|
+
console.warn(`\nWarning: Couldn't clean up legacy ${label} MCP entry — ${err.message}`);
|
|
239
|
+
console.warn("Continuing with install. The stale entry won't break the CLI but the agent may show a broken-MCP indicator until you remove it manually.");
|
|
240
|
+
}
|
|
241
|
+
}
|
|
161
242
|
const hasClaude = (0, utils_1.claudeCodeInstalled)();
|
|
162
243
|
const hasCodex = (0, utils_1.codexInstalled)();
|
|
163
244
|
const hasCursor = (0, utils_1.cursorInstalled)();
|
|
164
|
-
const hasVSCodeCopilot = (0, utils_1.vscodeCopilotInstalled)();
|
|
165
245
|
const installed = [];
|
|
166
|
-
|
|
246
|
+
// Machine-readable target keys reported back to the backend via
|
|
247
|
+
// /api/cli/markInstalled so the portal's "Connected" state reflects
|
|
248
|
+
// what's actually wired up on this machine, not just "token validated."
|
|
249
|
+
const installedTargets = [];
|
|
167
250
|
if (hasClaude) {
|
|
168
251
|
try {
|
|
169
252
|
(0, utils_1.installHook)();
|
|
170
253
|
installed.push("Claude Code SessionEnd hook (~/.claude/settings.json)");
|
|
254
|
+
installedTargets.push("claudeCode-hook");
|
|
171
255
|
}
|
|
172
256
|
catch (err) {
|
|
173
257
|
console.error(`\nWarning: Failed to install Claude Code hook — ${err.message}`);
|
|
@@ -175,22 +259,17 @@ async function runConnect(args = []) {
|
|
|
175
259
|
try {
|
|
176
260
|
(0, utils_1.installClaudeCodeInstructions)();
|
|
177
261
|
installed.push("Claude Code agent instructions (~/.claude/CLAUDE.md)");
|
|
262
|
+
installedTargets.push("claudeCode-instructions");
|
|
178
263
|
}
|
|
179
264
|
catch (err) {
|
|
180
265
|
console.error(`\nWarning: Failed to install Claude Code instructions — ${err.message}`);
|
|
181
266
|
}
|
|
182
|
-
try {
|
|
183
|
-
(0, utils_1.installClaudeCodeMcp)();
|
|
184
|
-
installed.push("Claude Code MCP server (~/.claude.json)");
|
|
185
|
-
}
|
|
186
|
-
catch (err) {
|
|
187
|
-
console.error(`\nWarning: Failed to install Claude Code MCP server — ${err.message}`);
|
|
188
|
-
}
|
|
189
267
|
}
|
|
190
268
|
if (hasCodex) {
|
|
191
269
|
try {
|
|
192
270
|
(0, utils_1.installCodexHook)();
|
|
193
271
|
installed.push("Codex Stop hook (~/.codex/hooks.json)");
|
|
272
|
+
installedTargets.push("codex-hook");
|
|
194
273
|
}
|
|
195
274
|
catch (err) {
|
|
196
275
|
console.error(`\nWarning: Failed to install Codex hook — ${err.message}`);
|
|
@@ -198,22 +277,17 @@ async function runConnect(args = []) {
|
|
|
198
277
|
try {
|
|
199
278
|
(0, utils_1.installCodexInstructions)();
|
|
200
279
|
installed.push("Codex agent instructions (~/.codex/AGENTS.md)");
|
|
280
|
+
installedTargets.push("codex-instructions");
|
|
201
281
|
}
|
|
202
282
|
catch (err) {
|
|
203
283
|
console.error(`\nWarning: Failed to install Codex instructions — ${err.message}`);
|
|
204
284
|
}
|
|
205
|
-
try {
|
|
206
|
-
(0, utils_1.installCodexMcp)();
|
|
207
|
-
installed.push("Codex MCP server (~/.codex/config.toml)");
|
|
208
|
-
}
|
|
209
|
-
catch (err) {
|
|
210
|
-
console.error(`\nWarning: Failed to install Codex MCP server — ${err.message}`);
|
|
211
|
-
}
|
|
212
285
|
}
|
|
213
286
|
if (hasCursor) {
|
|
214
287
|
try {
|
|
215
288
|
(0, utils_1.installCursorHook)();
|
|
216
289
|
installed.push("Cursor sessionEnd hook (~/.cursor/hooks.json)");
|
|
290
|
+
installedTargets.push("cursor-hook");
|
|
217
291
|
}
|
|
218
292
|
catch (err) {
|
|
219
293
|
console.error(`\nWarning: Failed to install Cursor hook — ${err.message}`);
|
|
@@ -221,30 +295,29 @@ async function runConnect(args = []) {
|
|
|
221
295
|
try {
|
|
222
296
|
(0, utils_1.installCursorInstructions)();
|
|
223
297
|
installed.push("Cursor agent instructions (~/.cursor/rules/codiedev.mdc)");
|
|
298
|
+
installedTargets.push("cursor-instructions");
|
|
224
299
|
}
|
|
225
300
|
catch (err) {
|
|
226
301
|
console.error(`\nWarning: Failed to install Cursor instructions — ${err.message}`);
|
|
227
302
|
}
|
|
228
|
-
try {
|
|
229
|
-
(0, utils_1.installCursorMcp)();
|
|
230
|
-
installed.push("Cursor MCP server (~/.cursor/mcp.json)");
|
|
231
|
-
}
|
|
232
|
-
catch (err) {
|
|
233
|
-
console.error(`\nWarning: Failed to install Cursor MCP server — ${err.message}`);
|
|
234
|
-
}
|
|
235
303
|
}
|
|
236
|
-
|
|
304
|
+
// Tell the backend which targets we wired up so the portal's onboarding
|
|
305
|
+
// card flips to "Connected" only when something actually installed —
|
|
306
|
+
// not on bare token validation. Best-effort: a network failure here
|
|
307
|
+
// doesn't undo the local install, and `codiedev doctor` still works.
|
|
308
|
+
if (installedTargets.length > 0) {
|
|
237
309
|
try {
|
|
238
|
-
(
|
|
239
|
-
|
|
240
|
-
|
|
310
|
+
await postJson(`${BACKEND_URL}/api/cli/markInstalled`, {
|
|
311
|
+
installedTargets,
|
|
312
|
+
}, { Authorization: `Bearer ${token}` });
|
|
241
313
|
}
|
|
242
314
|
catch (err) {
|
|
243
|
-
console.error(`\nWarning: Failed to install
|
|
315
|
+
console.error(`\nWarning: Failed to report install to backend — ${err.message}`);
|
|
316
|
+
console.error("Local install succeeded; portal may not reflect it until you re-run.");
|
|
244
317
|
}
|
|
245
318
|
}
|
|
246
|
-
if (!hasClaude && !hasCodex && !hasCursor
|
|
247
|
-
console.warn("\nNo Claude Code, Codex,
|
|
319
|
+
if (!hasClaude && !hasCodex && !hasCursor) {
|
|
320
|
+
console.warn("\nNo Claude Code, Codex, or Cursor install detected.");
|
|
248
321
|
console.warn("If you just installed Claude Code, launch it once (so ~/.claude is created),");
|
|
249
322
|
console.warn("then re-run `npx codiedev connect` to wire up the capture hook.");
|
|
250
323
|
}
|
|
@@ -257,13 +330,6 @@ async function runConnect(args = []) {
|
|
|
257
330
|
}
|
|
258
331
|
console.log("Sessions will be captured automatically.");
|
|
259
332
|
}
|
|
260
|
-
if (vscodeMcpInstalled) {
|
|
261
|
-
console.log();
|
|
262
|
-
console.log("VS Code: open Copilot Chat — it will prompt you to trust the codiedev MCP server.");
|
|
263
|
-
console.log("Click Allow once, then ask Copilot: \"create a reverse ticket from my current changes\".");
|
|
264
|
-
console.log("Note: Copilot doesn't expose chat transcripts to MCP, so on-demand tools (reverse_ticket,");
|
|
265
|
-
console.log("push, pull, ask, ping) work fully — auto-captured decisions need Claude Code or Codex.");
|
|
266
|
-
}
|
|
267
333
|
console.log();
|
|
268
334
|
console.log("Run `codiedev doctor` to verify everything's wired up.");
|
|
269
335
|
console.log();
|
package/dist/detection.d.ts
CHANGED
|
@@ -10,3 +10,21 @@ export interface ClaudeCodeProbe {
|
|
|
10
10
|
* obviously committed to the tool.
|
|
11
11
|
*/
|
|
12
12
|
export declare function detectClaudeCode(probe: ClaudeCodeProbe): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* True iff `cmd` looks like a hook command codiedev itself wrote. Used
|
|
15
|
+
* both by the install filter (so `connect` replaces the existing entry
|
|
16
|
+
* instead of stacking another one) and by `codiedev doctor` (so it can
|
|
17
|
+
* grade whether the hook is wired up).
|
|
18
|
+
*
|
|
19
|
+
* Must match every form codiedev can write:
|
|
20
|
+
* - Legacy `npx codiedev-hook <subcommand>`
|
|
21
|
+
* - Absolute path from npm-registry install:
|
|
22
|
+
* `<node> /.../node_modules/codiedev/dist/hook.js <subcommand>`
|
|
23
|
+
* - Absolute path from `npm link` dev install (the monorepo folder
|
|
24
|
+
* is named `codiedev-cli`, not `codiedev`):
|
|
25
|
+
* `<node> /.../codiedev-cli/dist/hook.js <subcommand>`
|
|
26
|
+
*
|
|
27
|
+
* The `codiedev[^/\\]*` allows the trailing `-cli` (or any future suffix)
|
|
28
|
+
* without matching unrelated paths like `notcodiedev/dist/hook.js`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isCodiedevHookCommand(cmd: string | undefined): boolean;
|
package/dist/detection.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
// mocking fs or process.
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.detectClaudeCode = detectClaudeCode;
|
|
7
|
+
exports.isCodiedevHookCommand = isCodiedevHookCommand;
|
|
7
8
|
/**
|
|
8
9
|
* Claude Code is "installed" if either ~/.claude exists (the user has
|
|
9
10
|
* launched the app at least once) OR the `claude` binary is on PATH (the
|
|
@@ -14,3 +15,30 @@ exports.detectClaudeCode = detectClaudeCode;
|
|
|
14
15
|
function detectClaudeCode(probe) {
|
|
15
16
|
return probe.dirExists || probe.binOnPath;
|
|
16
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* True iff `cmd` looks like a hook command codiedev itself wrote. Used
|
|
20
|
+
* both by the install filter (so `connect` replaces the existing entry
|
|
21
|
+
* instead of stacking another one) and by `codiedev doctor` (so it can
|
|
22
|
+
* grade whether the hook is wired up).
|
|
23
|
+
*
|
|
24
|
+
* Must match every form codiedev can write:
|
|
25
|
+
* - Legacy `npx codiedev-hook <subcommand>`
|
|
26
|
+
* - Absolute path from npm-registry install:
|
|
27
|
+
* `<node> /.../node_modules/codiedev/dist/hook.js <subcommand>`
|
|
28
|
+
* - Absolute path from `npm link` dev install (the monorepo folder
|
|
29
|
+
* is named `codiedev-cli`, not `codiedev`):
|
|
30
|
+
* `<node> /.../codiedev-cli/dist/hook.js <subcommand>`
|
|
31
|
+
*
|
|
32
|
+
* The `codiedev[^/\\]*` allows the trailing `-cli` (or any future suffix)
|
|
33
|
+
* without matching unrelated paths like `notcodiedev/dist/hook.js`.
|
|
34
|
+
*/
|
|
35
|
+
function isCodiedevHookCommand(cmd) {
|
|
36
|
+
if (!cmd)
|
|
37
|
+
return false;
|
|
38
|
+
if (cmd.includes("codiedev-hook"))
|
|
39
|
+
return true;
|
|
40
|
+
// Anchor at a path separator (or string start) so we don't match
|
|
41
|
+
// unrelated paths like `notcodiedev/dist/hook.js` that happen to contain
|
|
42
|
+
// the substring `codiedev`.
|
|
43
|
+
return /(?:^|[\\/])codiedev[^/\\]*[\\/]dist[\\/]hook/.test(cmd);
|
|
44
|
+
}
|
package/dist/hook.js
CHANGED
|
File without changes
|
package/dist/mcp.js
CHANGED
|
@@ -173,8 +173,8 @@ const TOOLS = [
|
|
|
173
173
|
name: "codiedev_ping",
|
|
174
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
|
-
"or get feedback from a teammate by name (e.g. 'ping
|
|
177
|
-
"spec'). Recipients are resolved by first name, full name, or email " +
|
|
176
|
+
"or get feedback from a teammate by name (e.g. 'ping a teammate about " +
|
|
177
|
+
"this spec'). Recipients are resolved by first name, full name, or email " +
|
|
178
178
|
"within the current org. They're notified by email and can reply " +
|
|
179
179
|
"from their own agent.",
|
|
180
180
|
inputSchema: {
|
|
@@ -325,8 +325,8 @@ const TOOLS = [
|
|
|
325
325
|
name: "codiedev_share_with",
|
|
326
326
|
description: "Grant a teammate persistent read (or edit) access to an artifact " +
|
|
327
327
|
"without sending a notification. Use when the user says 'share this " +
|
|
328
|
-
"with
|
|
329
|
-
"as a message. If they want the teammate actively notified, use " +
|
|
328
|
+
"with a teammate' or 'give someone access', and doesn't want to surface " +
|
|
329
|
+
"it as a message. If they want the teammate actively notified, use " +
|
|
330
330
|
"codiedev_send_to instead.",
|
|
331
331
|
inputSchema: {
|
|
332
332
|
type: "object",
|
|
@@ -349,8 +349,8 @@ const TOOLS = [
|
|
|
349
349
|
{
|
|
350
350
|
name: "codiedev_send_to",
|
|
351
351
|
description: "Grant access to an artifact AND send a short message to a " +
|
|
352
|
-
"teammate. Use when the user asks to 'send this to
|
|
353
|
-
"
|
|
352
|
+
"teammate. Use when the user asks to 'send this to a teammate', 'ask " +
|
|
353
|
+
"someone to look at this', or 'share this with a teammate and tell them X'. " +
|
|
354
354
|
"This is the active handoff version of codiedev_share_with — the " +
|
|
355
355
|
"recipient gets a ping in their inbox.",
|
|
356
356
|
inputSchema: {
|
|
@@ -439,7 +439,7 @@ const TOOLS = [
|
|
|
439
439
|
"files surfaced as slash commands). Each row reports its `kind` " +
|
|
440
440
|
"(doc | skill) and `tags`. Use when the user asks 'show me my " +
|
|
441
441
|
"artifacts', 'what skills do we have?', 'what did I push?', 'what " +
|
|
442
|
-
"has
|
|
442
|
+
"has a teammate shared with me?', or 'browse the library'. Filter by " +
|
|
443
443
|
"`kind` to narrow to docs-only or skills-only.",
|
|
444
444
|
inputSchema: {
|
|
445
445
|
type: "object",
|
package/dist/secret.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function maskToken(token: string | null | undefined): string;
|
package/dist/secret.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Display helpers for credential-shaped strings. Use anywhere a token /
|
|
3
|
+
// secret might end up in user-visible output (logs, success lines, error
|
|
4
|
+
// messages) so a stray `console.log` can't leak the full value.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.maskToken = maskToken;
|
|
7
|
+
const PREFIX_LEN = 12;
|
|
8
|
+
function maskToken(token) {
|
|
9
|
+
if (typeof token !== "string" || token.length === 0)
|
|
10
|
+
return "";
|
|
11
|
+
if (token.length <= PREFIX_LEN)
|
|
12
|
+
return token;
|
|
13
|
+
return `${token.slice(0, PREFIX_LEN)}...`;
|
|
14
|
+
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -61,6 +61,28 @@ export declare function installCursorMcp(): void;
|
|
|
61
61
|
* 100% diff-driven (no transcript dependency).
|
|
62
62
|
*/
|
|
63
63
|
export declare function installVSCodeMcp(): void;
|
|
64
|
+
/**
|
|
65
|
+
* Removes `mcpServers.codiedev` from `~/.claude.json` if present. Leaves
|
|
66
|
+
* other MCP servers and unrelated top-level settings untouched.
|
|
67
|
+
*/
|
|
68
|
+
export declare function cleanupClaudeCodeMcp(): void;
|
|
69
|
+
/**
|
|
70
|
+
* Removes `mcpServers.codiedev` from `~/.cursor/mcp.json` if present.
|
|
71
|
+
*/
|
|
72
|
+
export declare function cleanupCursorMcp(): void;
|
|
73
|
+
/**
|
|
74
|
+
* Removes `servers.codiedev` from VS Code's user-scope mcp.json. VS Code
|
|
75
|
+
* uses a different schema (`servers` not `mcpServers`), so this is a
|
|
76
|
+
* separate helper.
|
|
77
|
+
*/
|
|
78
|
+
export declare function cleanupVSCodeMcp(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Removes the `# codiedev-mcp` block from `~/.codex/config.toml`. The
|
|
81
|
+
* install side appends a well-marked block; we strip it the same way —
|
|
82
|
+
* the marker line through the end of its TOML section. Whitespace-only
|
|
83
|
+
* gaps between sections are preserved.
|
|
84
|
+
*/
|
|
85
|
+
export declare function cleanupCodexMcp(): void;
|
|
64
86
|
/**
|
|
65
87
|
* Best-effort append of the CodieDev MCP server block to ~/.codex/config.toml.
|
|
66
88
|
*
|
package/dist/utils.js
CHANGED
|
@@ -50,6 +50,10 @@ exports.installCursorInstructions = installCursorInstructions;
|
|
|
50
50
|
exports.installClaudeCodeMcp = installClaudeCodeMcp;
|
|
51
51
|
exports.installCursorMcp = installCursorMcp;
|
|
52
52
|
exports.installVSCodeMcp = installVSCodeMcp;
|
|
53
|
+
exports.cleanupClaudeCodeMcp = cleanupClaudeCodeMcp;
|
|
54
|
+
exports.cleanupCursorMcp = cleanupCursorMcp;
|
|
55
|
+
exports.cleanupVSCodeMcp = cleanupVSCodeMcp;
|
|
56
|
+
exports.cleanupCodexMcp = cleanupCodexMcp;
|
|
53
57
|
exports.installCodexMcp = installCodexMcp;
|
|
54
58
|
exports.installCodexHook = installCodexHook;
|
|
55
59
|
exports.installCursorHook = installCursorHook;
|
|
@@ -186,11 +190,8 @@ function resolveHookCommand(subcommand) {
|
|
|
186
190
|
}
|
|
187
191
|
return `npx codiedev-hook ${subcommand}`;
|
|
188
192
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return false;
|
|
192
|
-
return cmd.includes("codiedev-hook") || /codiedev[\\/]dist[\\/]hook/.test(cmd);
|
|
193
|
-
}
|
|
193
|
+
// `isCodiedevHookCommand` is defined in `./detection` so it's testable
|
|
194
|
+
// without mocking fs/path. Re-imported below; do not re-define here.
|
|
194
195
|
function claudeBinOnPath() {
|
|
195
196
|
try {
|
|
196
197
|
(0, child_process_1.execSync)("command -v claude", { stdio: ["pipe", "pipe", "pipe"] });
|
|
@@ -254,9 +255,9 @@ function installHook() {
|
|
|
254
255
|
const filtered = existing.filter((hook) => {
|
|
255
256
|
const inner = hook.hooks;
|
|
256
257
|
if (Array.isArray(inner)) {
|
|
257
|
-
return !inner.some((h) => isCodiedevHookCommand(h.command));
|
|
258
|
+
return !inner.some((h) => (0, detection_1.isCodiedevHookCommand)(h.command));
|
|
258
259
|
}
|
|
259
|
-
return !isCodiedevHookCommand(hook.matcher);
|
|
260
|
+
return !(0, detection_1.isCodiedevHookCommand)(hook.matcher);
|
|
260
261
|
});
|
|
261
262
|
filtered.push({
|
|
262
263
|
matcher: ".*",
|
|
@@ -294,20 +295,20 @@ thought, use the \`codiedev\` CLI via Bash:**
|
|
|
294
295
|
| Intent | Command |
|
|
295
296
|
|---|---|
|
|
296
297
|
| "push this spec" / "save this as a skill" | \`codiedev push <file.md>\` |
|
|
297
|
-
| "pull X" / "grab the latest spec-X" / "what did
|
|
298
|
+
| "pull X" / "grab the latest spec-X" / "what did a teammate push?" | \`codiedev pull <key>\` |
|
|
298
299
|
| "delete X" / "remove that test push" / "rm spec-old" | \`codiedev delete <key>\` (always confirm with the user first) |
|
|
299
|
-
| "ping
|
|
300
|
-
| "any messages?" / "check my inbox" / "what did
|
|
301
|
-
| "read
|
|
300
|
+
| "ping a teammate about this" / "ask someone for their take" | \`codiedev ping <name> "<msg>" [--with <key>]\` |
|
|
301
|
+
| "any messages?" / "check my inbox" / "what did a teammate say?" | \`codiedev inbox\` |
|
|
302
|
+
| "read that reply" / "mark that ping read" | \`codiedev read <ping-id>\` |
|
|
302
303
|
| "note that X is a follow-up" / "remember X" | \`codiedev note "<text>"\` |
|
|
303
304
|
| "promote the extracted spec" | \`codiedev promote <artifact-id>\` |
|
|
304
|
-
| "post this to the team" / "publish to the feed" / "share with the team" |
|
|
305
|
-
| "share with
|
|
306
|
-
| "send this to
|
|
307
|
-
| "find something about X" / "has anyone solved Y?" |
|
|
308
|
-
| "why did we…?" / "what does the team know about X?" |
|
|
309
|
-
| "show my library" / "what artifacts exist?" |
|
|
310
|
-
| "react 🛠 to that post" / "mark as used" |
|
|
305
|
+
| "post this to the team" / "publish to the feed" / "share with the team" | \`codiedev post --title "<t>" --body "<b>" [--intent <intent>] [--filename <key>] [--mention <name>]\` |
|
|
306
|
+
| "share with a teammate" / "give someone access" (no notification) | \`codiedev share <key> <name> [--role read\\|edit]\` |
|
|
307
|
+
| "send this to a teammate" / "ask someone to look at this" | \`codiedev send <key> <name> ["<msg>"]\` |
|
|
308
|
+
| "find something about X" / "has anyone solved Y?" | \`codiedev search "<query>" [--limit N]\` |
|
|
309
|
+
| "why did we…?" / "what does the team know about X?" | \`codiedev ask "<question>"\` (synthesized answer + citations) |
|
|
310
|
+
| "show my library" / "what artifacts exist?" | \`codiedev library [--scope mine\\|shared\\|all] [--kind doc\\|skill]\` |
|
|
311
|
+
| "react 🛠 to that post" / "mark as used" | \`codiedev react <postId> <emoji>\` |
|
|
311
312
|
|
|
312
313
|
**Just push the file — we'll figure out where it goes.** Every artifact
|
|
313
314
|
is classified into one of two kinds at upload time:
|
|
@@ -332,16 +333,17 @@ read it back to the user so they can confirm.
|
|
|
332
333
|
- If content was drafted inline in the conversation, write it to a file
|
|
333
334
|
first (e.g., \`/tmp/spec-xxx.md\`) then push.
|
|
334
335
|
|
|
335
|
-
**Teammate names:** first name usually works (\`codiedev ping
|
|
336
|
+
**Teammate names:** first name usually works (\`codiedev ping <name> ...\`).
|
|
336
337
|
If ambiguous, the CLI returns candidates — retry with the full email.
|
|
337
338
|
|
|
338
|
-
**Feed posts (broad team reach):** use \`
|
|
339
|
-
|
|
340
|
-
asking the team who knows about X, proposing a change. Always pass
|
|
341
|
-
|
|
339
|
+
**Feed posts (broad team reach):** use \`codiedev post\` when the user
|
|
340
|
+
wants visibility beyond one teammate — e.g., announcing a new skill,
|
|
341
|
+
asking the team who knows about X, proposing a change. Always pass
|
|
342
|
+
\`--intent\` to match the request: \`share\` (here's a skill),
|
|
342
343
|
\`request_review\`, \`request_expertise\` (stuck, who knows this),
|
|
343
|
-
\`link_share\` (fyi), or \`rfc\` (proposing a change).
|
|
344
|
-
to link a specific artifact and
|
|
344
|
+
\`link_share\` (fyi), or \`rfc\` (proposing a change). Pass \`--filename\`
|
|
345
|
+
to link a specific artifact and \`--mention <name>\` (repeatable) to
|
|
346
|
+
tag teammates.
|
|
345
347
|
|
|
346
348
|
**Default feed-post shape.** When the user explicitly says "push/share/post
|
|
347
349
|
to the feed", default to this shape unless they ask for something else:
|
|
@@ -359,12 +361,12 @@ to the feed", default to this shape unless they ask for something else:
|
|
|
359
361
|
This is the default. If the user wants a one-liner or a different
|
|
360
362
|
structure, follow their lead.
|
|
361
363
|
|
|
362
|
-
**Search before pushing.** For any "save this as …" request,
|
|
363
|
-
\`
|
|
364
|
-
|
|
364
|
+
**Search before pushing.** For any "save this as …" request, run
|
|
365
|
+
\`codiedev search "<query>"\` first. If prior art exists, pull it and
|
|
366
|
+
iterate rather than duplicating.
|
|
365
367
|
|
|
366
|
-
**Sharing vs sending:** \`
|
|
367
|
-
notification). \`
|
|
368
|
+
**Sharing vs sending:** \`codiedev share\` is silent (access only, no
|
|
369
|
+
notification). \`codiedev send\` both grants access AND pings the
|
|
368
370
|
recipient. Use send when the user is actively looping someone in.
|
|
369
371
|
|
|
370
372
|
**Errors:**
|
|
@@ -538,6 +540,94 @@ function installVSCodeMcp() {
|
|
|
538
540
|
config.servers = servers;
|
|
539
541
|
fs.writeFileSync(VSCODE_USER_MCP_PATH, JSON.stringify(config, null, 2), "utf8");
|
|
540
542
|
}
|
|
543
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
544
|
+
// Migration cleanup — strips MCP server entries written by pre-CLI-only
|
|
545
|
+
// versions of `codiedev connect`. Those entries pointed at `npx codiedev-mcp`,
|
|
546
|
+
// a binary that was removed from package.json in 0.3.4. Without these
|
|
547
|
+
// cleanups, customers who upgrade to 0.7.11+ keep seeing "MCP server failed
|
|
548
|
+
// to start" errors in their agent UIs even though the CLI works fine.
|
|
549
|
+
// All cleanup helpers are idempotent and safe to run when no entry exists.
|
|
550
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
551
|
+
function removeJsonKey(filePath, parentKey) {
|
|
552
|
+
if (!fs.existsSync(filePath))
|
|
553
|
+
return;
|
|
554
|
+
let parsed;
|
|
555
|
+
try {
|
|
556
|
+
parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
557
|
+
}
|
|
558
|
+
catch {
|
|
559
|
+
return; // Don't damage a file we can't parse.
|
|
560
|
+
}
|
|
561
|
+
const parent = parsed[parentKey];
|
|
562
|
+
if (!parent || typeof parent !== "object")
|
|
563
|
+
return;
|
|
564
|
+
if (!("codiedev" in parent))
|
|
565
|
+
return;
|
|
566
|
+
delete parent.codiedev;
|
|
567
|
+
fs.writeFileSync(filePath, JSON.stringify(parsed, null, 2), "utf8");
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Removes `mcpServers.codiedev` from `~/.claude.json` if present. Leaves
|
|
571
|
+
* other MCP servers and unrelated top-level settings untouched.
|
|
572
|
+
*/
|
|
573
|
+
function cleanupClaudeCodeMcp() {
|
|
574
|
+
removeJsonKey(CLAUDE_USER_CONFIG_PATH, "mcpServers");
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Removes `mcpServers.codiedev` from `~/.cursor/mcp.json` if present.
|
|
578
|
+
*/
|
|
579
|
+
function cleanupCursorMcp() {
|
|
580
|
+
removeJsonKey(CURSOR_MCP_PATH, "mcpServers");
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Removes `servers.codiedev` from VS Code's user-scope mcp.json. VS Code
|
|
584
|
+
* uses a different schema (`servers` not `mcpServers`), so this is a
|
|
585
|
+
* separate helper.
|
|
586
|
+
*/
|
|
587
|
+
function cleanupVSCodeMcp() {
|
|
588
|
+
removeJsonKey(VSCODE_USER_MCP_PATH, "servers");
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* Removes the `# codiedev-mcp` block from `~/.codex/config.toml`. The
|
|
592
|
+
* install side appends a well-marked block; we strip it the same way —
|
|
593
|
+
* the marker line through the end of its TOML section. Whitespace-only
|
|
594
|
+
* gaps between sections are preserved.
|
|
595
|
+
*/
|
|
596
|
+
function cleanupCodexMcp() {
|
|
597
|
+
if (!fs.existsSync(CODEX_CONFIG_PATH))
|
|
598
|
+
return;
|
|
599
|
+
const existing = fs.readFileSync(CODEX_CONFIG_PATH, "utf8");
|
|
600
|
+
const marker = "# codiedev-mcp";
|
|
601
|
+
if (!existing.includes(marker))
|
|
602
|
+
return;
|
|
603
|
+
const lines = existing.split("\n");
|
|
604
|
+
const out = [];
|
|
605
|
+
let skipping = false;
|
|
606
|
+
for (const line of lines) {
|
|
607
|
+
if (line.trim() === marker) {
|
|
608
|
+
skipping = true;
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
if (skipping) {
|
|
612
|
+
// Stay in skip mode through the [mcp_servers.codiedev] block until
|
|
613
|
+
// we hit a blank line or a different TOML section header.
|
|
614
|
+
const trimmed = line.trim();
|
|
615
|
+
if (trimmed === "") {
|
|
616
|
+
skipping = false;
|
|
617
|
+
continue; // also drop the blank that immediately follows the block
|
|
618
|
+
}
|
|
619
|
+
if (trimmed.startsWith("[") && !trimmed.startsWith("[mcp_servers.codiedev")) {
|
|
620
|
+
skipping = false;
|
|
621
|
+
out.push(line);
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
// Skip lines belonging to the codiedev block.
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
out.push(line);
|
|
628
|
+
}
|
|
629
|
+
fs.writeFileSync(CODEX_CONFIG_PATH, out.join("\n"), "utf8");
|
|
630
|
+
}
|
|
541
631
|
/**
|
|
542
632
|
* Best-effort append of the CodieDev MCP server block to ~/.codex/config.toml.
|
|
543
633
|
*
|
|
@@ -592,7 +682,7 @@ function installCodexHook() {
|
|
|
592
682
|
const inner = hook.hooks;
|
|
593
683
|
if (!Array.isArray(inner))
|
|
594
684
|
return true;
|
|
595
|
-
return !inner.some((h) => isCodiedevHookCommand(h.command));
|
|
685
|
+
return !inner.some((h) => (0, detection_1.isCodiedevHookCommand)(h.command));
|
|
596
686
|
});
|
|
597
687
|
filtered.push({
|
|
598
688
|
hooks: [
|
|
@@ -638,7 +728,7 @@ function installCursorHook() {
|
|
|
638
728
|
hooks.sessionEnd = [];
|
|
639
729
|
}
|
|
640
730
|
const existing = hooks.sessionEnd;
|
|
641
|
-
const filtered = existing.filter((h) => !isCodiedevHookCommand(h.command));
|
|
731
|
+
const filtered = existing.filter((h) => !(0, detection_1.isCodiedevHookCommand)(h.command));
|
|
642
732
|
filtered.push({
|
|
643
733
|
command: resolveHookCommand("capture-cursor"),
|
|
644
734
|
type: "command",
|
package/package.json
CHANGED