codiedev 0.9.65 → 0.9.67

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/mcp.js CHANGED
@@ -45,6 +45,7 @@ var __importStar = (this && this.__importStar) || (function () {
45
45
  })();
46
46
  Object.defineProperty(exports, "__esModule", { value: true });
47
47
  const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
48
+ const skillMarker_1 = require("./skillMarker");
48
49
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
49
50
  const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
50
51
  const fs = __importStar(require("fs"));
@@ -237,29 +238,6 @@ const TOOLS = [
237
238
  required: ["body"],
238
239
  },
239
240
  },
240
- {
241
- name: "codiedev_promote",
242
- description: "Promote an auto-extracted Codiedev artifact (spec/bugfix/decision " +
243
- "produced by the session-capture pipeline) to an authored artifact " +
244
- "so teammates can collaborate on it. Use when the user says 'publish " +
245
- "the extracted spec', 'promote this', or 'make the auto-extracted X " +
246
- "shareable'.",
247
- inputSchema: {
248
- type: "object",
249
- properties: {
250
- artifactId: {
251
- type: "string",
252
- description: "The extracted artifact's id from Codiedev.",
253
- },
254
- keyOverride: {
255
- type: "string",
256
- description: "Optional filename key for the promoted artifact. If omitted, " +
257
- "one is generated from the artifact's type + session id.",
258
- },
259
- },
260
- required: ["artifactId"],
261
- },
262
- },
263
241
  {
264
242
  name: "codiedev_share_with",
265
243
  description: "Grant a teammate persistent read (or edit) access to an artifact " +
@@ -306,27 +284,6 @@ const TOOLS = [
306
284
  required: ["filename", "to"],
307
285
  },
308
286
  },
309
- {
310
- name: "codiedev_react",
311
- description: "React to a feed post with an emoji. Use when the user says 'react " +
312
- "to that post with X', 'mark as used', or 'upvote'. The 🛠 " +
313
- "reaction is the signal for 'I used this' and bumps the linked " +
314
- "artifact's reuse count. Pass the post id (get it from " +
315
- "codiedev_get_library or a previous feed response).",
316
- inputSchema: {
317
- type: "object",
318
- properties: {
319
- postId: { type: "string", description: "Feed post id." },
320
- emoji: {
321
- type: "string",
322
- enum: ["👍", "🔥", "💡", "🛠", "👑"],
323
- description: "Reaction emoji. 🛠 = 'I used this' (counts heaviest toward " +
324
- "the author's score and the artifact's reuse count).",
325
- },
326
- },
327
- required: ["postId", "emoji"],
328
- },
329
- },
330
287
  {
331
288
  name: "codiedev_search",
332
289
  description: "Search the team's artifact library by natural-language query. Use " +
@@ -413,41 +370,6 @@ const TOOLS = [
413
370
  },
414
371
  },
415
372
  },
416
- {
417
- name: "codiedev_reverse_ticket",
418
- description: "Generate a Jira-ready reverse-ticket draft from the work the user has " +
419
- "done in their CURRENT git branch — mid-session, before any PR exists. " +
420
- "Reads local git state (current branch, divergence from main, diff, " +
421
- "commits, files changed) and runs it through the codiedev reverse-" +
422
- "ticket writer. The draft lands in the user's portal at " +
423
- "/portal/agentic-ticketing where they can edit the title, description, " +
424
- "and scope, then push to Jira. Use when the user asks to 'create a " +
425
- "reverse ticket', 'draft a Jira ticket from this work', 'write up what " +
426
- "I've done in this branch', or runs `codiedev reverse-ticket` directly. " +
427
- "The webhook on PR-open later dedupes against this draft so the user " +
428
- "never gets a double ticket. The draft is intentionally not auto-pushed " +
429
- "to Jira — trust comes from the user reviewing in the portal first.",
430
- inputSchema: {
431
- type: "object",
432
- properties: {
433
- title: {
434
- type: "string",
435
- description: "Optional working title for the ticket. Defaults to the most " +
436
- "recent commit subject on the branch.",
437
- },
438
- base: {
439
- type: "string",
440
- description: "Optional base ref to diff against. Defaults to 'origin/main', " +
441
- "falls back to 'origin/master'.",
442
- },
443
- forcedArtifactKey: {
444
- type: "string",
445
- description: "Optional — force the writer to link to a specific artifact key " +
446
- "for context (e.g. 'spec-auth.md').",
447
- },
448
- },
449
- },
450
- },
451
373
  ];
452
374
  // ─────────────────────────────────────────────────────────────────────────────
453
375
  // Server
@@ -538,22 +460,16 @@ async function dispatchTool(name, args, config) {
538
460
  return await handleInbox(args, config);
539
461
  case "codiedev_note":
540
462
  return await handleNote(args, config);
541
- case "codiedev_promote":
542
- return await handlePromote(args, config);
543
463
  case "codiedev_share_with":
544
464
  return await handleShareWith(args, config);
545
465
  case "codiedev_send_to":
546
466
  return await handleSendTo(args, config);
547
- case "codiedev_react":
548
- return await handleReact(args, config);
549
467
  case "codiedev_ask":
550
468
  return await handleAsk(args, config);
551
469
  case "codiedev_search":
552
470
  return await handleSearch(args, config);
553
471
  case "codiedev_get_library":
554
472
  return await handleGetLibrary(args, config);
555
- case "codiedev_reverse_ticket":
556
- return await handleReverseTicket(args, config);
557
473
  default:
558
474
  return {
559
475
  isError: true,
@@ -577,7 +493,7 @@ async function handlePush(args, config) {
577
493
  }
578
494
  const res = await (0, shared_1.apiRequest)("POST", "/api/cli/push", {
579
495
  config,
580
- body: { filename, markdown, type, kind, tags, force },
496
+ body: { filename, markdown: (0, skillMarker_1.stripSkillMarker)(markdown), type, kind, tags, force },
581
497
  });
582
498
  const tagSummary = res.tags && res.tags.length > 0 ? `, tags=[${res.tags.join(", ")}]` : "";
583
499
  return {
@@ -609,7 +525,9 @@ async function handlePull(args, config) {
609
525
  `**${kindLabel}** · v${a.version ?? 1} · ${a.lifecycle ?? "draft"}${tagSuffix} · ` +
610
526
  `updated ${(0, shared_1.timeAgo)(a.updatedAt ?? a.createdAt)}\n\n---\n\n`;
611
527
  return {
612
- content: [{ type: "text", text: header + a.markdown }],
528
+ // A skill's text carries its identity marker, so a copy the agent writes
529
+ // to disk still attributes its runs exactly (skillMarker.ts).
530
+ content: [{ type: "text", text: header + (a.kind === "skill" || (!a.kind && (0, skillMarker_1.looksLikeSkill)(a.markdown)) ? (0, skillMarker_1.stampSkillMarker)(a.markdown, a._id) : a.markdown) }],
613
531
  };
614
532
  }
615
533
  async function handleDelete(args, config) {
@@ -705,21 +623,6 @@ async function handleNote(args, config) {
705
623
  content: [{ type: "text", text: `✓ Noted (${res.key})` }],
706
624
  };
707
625
  }
708
- async function handlePromote(args, config) {
709
- const artifactId = asString(args.artifactId);
710
- const keyOverride = asStringOrUndefined(args.keyOverride);
711
- if (!artifactId)
712
- throw new Error("artifactId required");
713
- const res = await (0, shared_1.apiRequest)("POST", "/api/cli/promote", { config, body: { artifactId, keyOverride } });
714
- return {
715
- content: [
716
- {
717
- type: "text",
718
- text: `✓ Promoted to authored artifact ${res.key} (id=${res.artifactId}).`,
719
- },
720
- ],
721
- };
722
- }
723
626
  async function handleShareWith(args, config) {
724
627
  const filename = asString(args.filename);
725
628
  const to = asString(args.to);
@@ -810,26 +713,6 @@ async function handleSendTo(args, config) {
810
713
  return formatAmbiguousRecipient(err, to);
811
714
  }
812
715
  }
813
- async function handleReact(args, config) {
814
- const postId = asString(args.postId);
815
- const emoji = asString(args.emoji);
816
- if (!postId)
817
- throw new Error("postId required");
818
- if (!emoji)
819
- throw new Error("emoji required");
820
- const res = await (0, shared_1.apiRequest)("POST", "/api/cli/react", {
821
- config,
822
- body: { postId, emoji },
823
- });
824
- const verb = res.action === "added"
825
- ? "Reacted"
826
- : res.action === "removed"
827
- ? "Removed reaction"
828
- : "Swapped reaction to";
829
- return {
830
- content: [{ type: "text", text: `✓ ${verb} ${res.emoji}` }],
831
- };
832
- }
833
716
  async function handleSearch(args, config) {
834
717
  const query = asString(args.query);
835
718
  const limit = asIntOrUndefined(args.limit);
@@ -937,223 +820,6 @@ function formatAmbiguousRecipient(err, to) {
937
820
  }
938
821
  throw err;
939
822
  }
940
- async function handleReverseTicket(args, config) {
941
- const explicitTitle = asStringOrUndefined(args.title);
942
- const explicitBase = asStringOrUndefined(args.base);
943
- const forcedKey = asStringOrUndefined(args.forcedArtifactKey);
944
- // Helper that runs git from cwd with a hard-fail on error.
945
- const { execSync } = await Promise.resolve().then(() => __importStar(require("child_process")));
946
- const git = (cmd) => {
947
- try {
948
- return execSync(`git ${cmd}`, {
949
- stdio: ["pipe", "pipe", "pipe"],
950
- maxBuffer: 32 * 1024 * 1024,
951
- })
952
- .toString("utf8")
953
- .trim();
954
- }
955
- catch (err) {
956
- throw new Error(`git ${cmd} failed: ${err.message}`);
957
- }
958
- };
959
- // Confirm we're in a git repo.
960
- try {
961
- git("rev-parse --is-inside-work-tree");
962
- }
963
- catch {
964
- throw new Error("Not inside a git repository. Run this from a checked-out repo.");
965
- }
966
- // Resolve base ref — explicit, then origin/main, then origin/master.
967
- const candidates = explicitBase
968
- ? [explicitBase]
969
- : ["origin/main", "origin/master"];
970
- let baseRef = null;
971
- for (const c of candidates) {
972
- try {
973
- git(`rev-parse --verify ${c}`);
974
- baseRef = c;
975
- break;
976
- }
977
- catch {
978
- // try next
979
- }
980
- }
981
- if (!baseRef) {
982
- throw new Error(`Couldn't find a base ref (tried ${candidates.join(", ")}). ` +
983
- `Pass an explicit base via the 'base' argument.`);
984
- }
985
- const branch = git("rev-parse --abbrev-ref HEAD");
986
- if (branch === "HEAD") {
987
- throw new Error("Detached HEAD — checkout a branch first.");
988
- }
989
- const baseSha = git(`merge-base HEAD ${baseRef}`);
990
- const headSha = git("rev-parse HEAD");
991
- // Filter generated/lockfile noise so the LLM's diff budget gets spent on
992
- // real source. The --stat block (collected separately below) still includes
993
- // every file untouched, so excluded files still appear in the ticket's
994
- // "Other files touched" section if relevant.
995
- const isNoise = (p) => /(^|\/)(node_modules|dist|build|\.next|\.turbo|coverage|out)\//.test(p) ||
996
- /(^|\/)package-lock\.json$/.test(p) ||
997
- /(^|\/)yarn\.lock$/.test(p) ||
998
- /(^|\/)pnpm-lock\.yaml$/.test(p) ||
999
- /(^|\/)bun\.lock(b)?$/.test(p) ||
1000
- /(^|\/)Cargo\.lock$/.test(p) ||
1001
- /(^|\/)Gemfile\.lock$/.test(p) ||
1002
- /(^|\/)poetry\.lock$/.test(p) ||
1003
- /(^|\/)go\.sum$/.test(p);
1004
- // Helper: run a shell command tolerating non-zero exit codes (git diff
1005
- // --no-index exits 1 when files differ, which is the normal case).
1006
- // Reuses the execSync already imported above for the git() helper.
1007
- const shellTolerant = (cmd) => {
1008
- try {
1009
- return execSync(`${cmd} || true`, {
1010
- stdio: ["pipe", "pipe", "pipe"],
1011
- maxBuffer: 32 * 1024 * 1024,
1012
- })
1013
- .toString("utf8")
1014
- .trim();
1015
- }
1016
- catch {
1017
- return "";
1018
- }
1019
- };
1020
- const shQuote = (s) => `'${s.replace(/'/g, "'\\''")}'`;
1021
- // Capture all work the developer has done relative to the base ref:
1022
- // - committed changes between base and HEAD
1023
- // - uncommitted modifications to tracked files
1024
- // - new untracked files (path listed + content via synthetic diff)
1025
- // Matches the user's intent ("ticket my current changes") rather than the
1026
- // narrower "only what's been committed" interpretation.
1027
- const trackedRaw = git(`diff --name-only ${baseSha}`);
1028
- const trackedFiles = trackedRaw ? trackedRaw.split("\n").filter(Boolean) : [];
1029
- const untrackedRaw = safeGit(() => git("ls-files --others --exclude-standard"));
1030
- const untrackedFiles = untrackedRaw
1031
- ? untrackedRaw.split("\n").filter(Boolean)
1032
- : [];
1033
- const filesChanged = Array.from(new Set([...trackedFiles, ...untrackedFiles]));
1034
- if (filesChanged.length === 0) {
1035
- throw new Error(`No changes detected between ${baseRef} and your working tree on ${branch}.`);
1036
- }
1037
- // --stat block: compact ground truth for "every file touched". Tracked
1038
- // changes from `git diff --stat baseSha`; untracked files appended as
1039
- // synthesized stat rows so the LLM sees them with line counts.
1040
- let stat = "";
1041
- try {
1042
- const trackedStat = git(`diff --stat=200 ${baseSha}`);
1043
- const untrackedStatLines = untrackedFiles.map((f) => {
1044
- const wc = shellTolerant(`wc -l < ${shQuote(f)}`);
1045
- const n = parseInt(wc.split(/\s+/)[0] || "0", 10) || 0;
1046
- return ` ${f} | ${n} ++++++++++ (new file)`;
1047
- });
1048
- stat = [trackedStat, ...untrackedStatLines]
1049
- .filter(Boolean)
1050
- .join("\n")
1051
- .slice(0, 8_000);
1052
- }
1053
- catch {
1054
- // Best effort.
1055
- }
1056
- // Diff body: 40k cap, noise-filtered. Tracked changes via `git diff baseSha`,
1057
- // plus synthetic add-only diffs for untracked files via `git diff --no-index`.
1058
- let diff = "";
1059
- try {
1060
- const sourceTracked = trackedFiles.filter((f) => !isNoise(f));
1061
- const trackedDiff = sourceTracked.length > 0
1062
- ? git(`diff ${baseSha} -- ${sourceTracked.map(shQuote).join(" ")}`)
1063
- : git(`diff ${baseSha}`);
1064
- const sourceUntracked = untrackedFiles.filter((f) => !isNoise(f));
1065
- const untrackedDiffs = sourceUntracked.map((f) => shellTolerant(`git diff --no-index --no-color -- /dev/null ${shQuote(f)}`));
1066
- diff = [trackedDiff, ...untrackedDiffs]
1067
- .filter(Boolean)
1068
- .join("\n")
1069
- .slice(0, 40_000);
1070
- }
1071
- catch {
1072
- // Best effort.
1073
- }
1074
- // Repo name from origin remote.
1075
- let repo;
1076
- try {
1077
- const remoteUrl = git("config --get remote.origin.url");
1078
- const m = remoteUrl.match(/[:/]([^/:]+)\/([^/:]+?)(?:\.git)?$/);
1079
- if (!m) {
1080
- throw new Error(`Couldn't parse owner/repo from remote: ${remoteUrl}`);
1081
- }
1082
- repo = `${m[1]}/${m[2]}`;
1083
- }
1084
- catch (err) {
1085
- throw new Error(`Couldn't read git remote.origin.url: ${err.message}`);
1086
- }
1087
- const title = explicitTitle ?? safeGit(() => git("log -1 --format=%s")) ?? `WIP: ${branch}`;
1088
- const author = safeGit(() => git("config --get user.name"));
1089
- const authorEmail = safeGit(() => git("config --get user.email"));
1090
- // Post to backend.
1091
- const res = await (0, shared_1.apiRequest)("POST", "/api/cli/reverseTicketFromBranch", {
1092
- config,
1093
- body: {
1094
- branch: {
1095
- repo,
1096
- branch,
1097
- baseSha,
1098
- headSha,
1099
- title,
1100
- author,
1101
- authorEmail,
1102
- filesChanged,
1103
- diff: diff || undefined,
1104
- stat: stat || undefined,
1105
- },
1106
- forcedArtifactKey: forcedKey,
1107
- },
1108
- });
1109
- if (!res.ok || !res.artifactId) {
1110
- return {
1111
- isError: true,
1112
- content: [
1113
- {
1114
- type: "text",
1115
- text: "Reverse-ticket generation returned no result. The writer may have " +
1116
- "skipped due to missing context (no captured sessions, etc.). Try " +
1117
- "again after working on the branch a bit more, or check the " +
1118
- "server logs.",
1119
- },
1120
- ],
1121
- };
1122
- }
1123
- // Always return an absolute URL so MCP clients (Copilot Chat, Claude Code,
1124
- // etc.) can render the link correctly without guessing a host. Default to
1125
- // codiedev.com; allow override via CODIEDEV_PORTAL_URL for self-hosted.
1126
- const portalHost = (process.env.CODIEDEV_PORTAL_URL || "https://codiedev.com").replace(/\/$/, "");
1127
- const portalLink = res.portalUrl.startsWith("http")
1128
- ? res.portalUrl
1129
- : `${portalHost}${res.portalUrl.startsWith("/") ? "" : "/"}${res.portalUrl}`;
1130
- const verb = res.action === "overwritten" ? "Updated" : "Created";
1131
- return {
1132
- content: [
1133
- {
1134
- type: "text",
1135
- text: `✓ ${verb} reverse-ticket draft for branch \`${branch}\` (${repo}).\n` +
1136
- ` files changed: ${filesChanged.length}\n` +
1137
- ` base: ${baseSha.slice(0, 12)} → head: ${headSha.slice(0, 12)} (${baseRef})\n` +
1138
- ` diff size: ${diff.length} chars\n` +
1139
- ` artifact: ${res.artifactKey ?? res.artifactId}\n\n` +
1140
- `Open in your portal to review, edit, and push to Jira:\n` +
1141
- ` ${portalLink}\n\n` +
1142
- `Each call creates a fresh draft — if you've made several for the ` +
1143
- `same branch, clean them up from the portal.`,
1144
- },
1145
- ],
1146
- };
1147
- }
1148
- // Run a git invocation; return undefined instead of throwing.
1149
- function safeGit(fn) {
1150
- try {
1151
- return fn();
1152
- }
1153
- catch {
1154
- return undefined;
1155
- }
1156
- }
1157
823
  // ─────────────────────────────────────────────────────────────────────────────
1158
824
  // Helpers
1159
825
  // ─────────────────────────────────────────────────────────────────────────────
@@ -0,0 +1,6 @@
1
+ /** Is this a SKILL.md-style file (frontmatter with name + description)? */
2
+ export declare function looksLikeSkill(markdown: string): boolean;
3
+ /** Remove every marker line (idempotent). */
4
+ export declare function stripSkillMarker(markdown: string): string;
5
+ /** Stamp (or re-stamp) the marker. Leaves the text alone for a bad id. */
6
+ export declare function stampSkillMarker(markdown: string, artifactId: string | undefined | null): string;
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ // Skill identity marker. Two authors can each publish a skill with the same
3
+ // name ("/ticket"); a session's transcript only records the name it ran. A
4
+ // company skill written to disk therefore carries the artifact it came from:
5
+ //
6
+ // <!-- codiedev:artifact=<artifact id> -->
7
+ //
8
+ // appended after the body (frontmatter untouched, invisible when rendered).
9
+ // When the harness loads the skill, the marker lands in the transcript and
10
+ // parseCaptureSignals reports it as signals.skillInvocationArtifacts, so the
11
+ // portal attributes the run to the exact skill. `push` strips it, so a
12
+ // re-pushed copy never claims someone else's artifact id.
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.looksLikeSkill = looksLikeSkill;
15
+ exports.stripSkillMarker = stripSkillMarker;
16
+ exports.stampSkillMarker = stampSkillMarker;
17
+ const ID = /^[a-z0-9]{16,64}$/i;
18
+ const LINE = /^[ \t]*<!--\s*codiedev:artifact=[a-z0-9]+\s*-->[ \t]*(?:\r?\n|$)/gim;
19
+ /** Is this a SKILL.md-style file (frontmatter with name + description)? */
20
+ function looksLikeSkill(markdown) {
21
+ const fm = markdown.match(/^?---\r?\n([\s\S]*?)\r?\n---/);
22
+ if (!fm)
23
+ return false;
24
+ return /^name\s*:/im.test(fm[1]) && /^description\s*:/im.test(fm[1]);
25
+ }
26
+ /** Remove every marker line (idempotent). */
27
+ function stripSkillMarker(markdown) {
28
+ return markdown.replace(LINE, "");
29
+ }
30
+ /** Stamp (or re-stamp) the marker. Leaves the text alone for a bad id. */
31
+ function stampSkillMarker(markdown, artifactId) {
32
+ if (!artifactId || !ID.test(artifactId))
33
+ return markdown;
34
+ const body = stripSkillMarker(markdown);
35
+ const sep = body.endsWith("\n") ? "" : "\n";
36
+ return `${body}${sep}<!-- codiedev:artifact=${artifactId} -->\n`;
37
+ }
package/dist/subagents.js CHANGED
@@ -87,10 +87,14 @@ function foldSubagentSignals(parent, subs, source) {
87
87
  const invocations = new Set(parent.skillInvocations);
88
88
  const pulls = new Set(parent.skillPulls);
89
89
  const tools = new Map(parent.toolCalls.map((t) => [t.name, t.calls]));
90
+ const artifacts = new Map((parent.skillInvocationArtifacts ?? []).map((a) => [a.skill, a.artifactId]));
90
91
  let truncated = parent.truncated;
91
92
  for (const content of subs.contents) {
92
93
  const s = (0, captureSignals_1.parseCaptureSignals)(content, source);
93
94
  add(s.skillInvocationCounts, s.skillInvocations);
95
+ for (const a of s.skillInvocationArtifacts ?? [])
96
+ if (!artifacts.has(a.skill))
97
+ artifacts.set(a.skill, a.artifactId);
94
98
  for (const k of s.skillInvocations)
95
99
  invocations.add(k);
96
100
  for (const k of s.skillPulls)
@@ -108,6 +112,9 @@ function foldSubagentSignals(parent, subs, source) {
108
112
  ...parent,
109
113
  skillInvocations: cap([...invocations].sort()),
110
114
  skillInvocationCounts: cap([...counts].map(([skill, count]) => ({ skill, count: Math.min(count, 10_000) })).sort((a, b) => a.skill.localeCompare(b.skill))),
115
+ ...(artifacts.size
116
+ ? { skillInvocationArtifacts: cap([...artifacts].map(([skill, artifactId]) => ({ skill, artifactId })).sort((a, b) => a.skill.localeCompare(b.skill))) }
117
+ : {}),
111
118
  skillPulls: cap([...pulls].sort()),
112
119
  toolCalls: cap([...tools].map(([name, calls]) => ({ name, calls })).sort((a, b) => a.name.localeCompare(b.name))),
113
120
  subagents: { count: subs.count, truncated: subs.truncated },
package/dist/utils.d.ts CHANGED
@@ -147,10 +147,6 @@ export declare function installCursorMcp(): void;
147
147
  * Claude / Cursor: top-level key is `servers` (not `mcpServers`). On first
148
148
  * Copilot Chat run after install, VS Code prompts the user to trust the
149
149
  * server — that's expected and documented in the connect success message.
150
- *
151
- * Reverse-ticket and the other on-demand verbs work in Copilot identically
152
- * to Claude Code since the MCP server's reverse_ticket handler is already
153
- * 100% diff-driven (no transcript dependency).
154
150
  */
155
151
  export declare function installVSCodeMcp(): void;
156
152
  /**
package/dist/utils.js CHANGED
@@ -1502,14 +1502,12 @@ thought, use the \`codiedev\` CLI via Bash:**
1502
1502
  | "any messages?" / "check my inbox" / "what did a teammate say?" | \`codiedev inbox\` |
1503
1503
  | "read that reply" / "mark that ping read" | \`codiedev read <ping-id>\` |
1504
1504
  | "note that X is a follow-up" / "remember X" | \`codiedev note "<text>"\` |
1505
- | "promote the extracted spec" | \`codiedev promote <artifact-id>\` |
1506
1505
  | "create a ticket" / "scope this into tickets" / "ticket this for me" | \`codiedev create-ticket [optional-inline-prompt]\` |
1507
1506
  | "share with a teammate" / "give someone access" (no notification) | \`codiedev share <key> <name> [--role read\\|edit]\` |
1508
1507
  | "send this to a teammate" / "ask someone to look at this" | \`codiedev send <key> <name> ["<msg>"]\` |
1509
1508
  | "find something about X" / "has anyone solved Y?" | \`codiedev search "<query>" [--limit N]\` |
1510
1509
  | "why did we…?" / "what does the team know about X?" | \`codiedev ask "<question>"\` (synthesized answer + citations) |
1511
1510
  | "show my library" / "what artifacts exist?" | \`codiedev library [--scope mine\\|shared\\|all] [--kind doc\\|skill]\` |
1512
- | "react 🛠 to that post" / "mark as used" | \`codiedev react <postId> <emoji>\` |
1513
1511
 
1514
1512
  **Just push the file — we'll figure out where it goes.** Every artifact
1515
1513
  is classified into one of two kinds at upload time:
@@ -1706,10 +1704,6 @@ function installCursorMcp() {
1706
1704
  * Claude / Cursor: top-level key is `servers` (not `mcpServers`). On first
1707
1705
  * Copilot Chat run after install, VS Code prompts the user to trust the
1708
1706
  * server — that's expected and documented in the connect success message.
1709
- *
1710
- * Reverse-ticket and the other on-demand verbs work in Copilot identically
1711
- * to Claude Code since the MCP server's reverse_ticket handler is already
1712
- * 100% diff-driven (no transcript dependency).
1713
1707
  */
1714
1708
  function installVSCodeMcp() {
1715
1709
  if (!fs.existsSync(VSCODE_USER_DIR)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codiedev",
3
- "version": "0.9.65",
3
+ "version": "0.9.67",
4
4
  "description": "Connect Claude Code, Codex, Cursor, OpenCode, or VS Code Copilot to Codiedev for team session capture and artifact collaboration — and run the governed Codie harness",
5
5
  "bin": {
6
6
  "codiedev": "dist/cli.js",
@@ -1 +0,0 @@
1
- export declare function runPromote(args: string[]): Promise<void>;
@@ -1,53 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.runPromote = runPromote;
4
- const shared_1 = require("./shared");
5
- function parseArgs(args) {
6
- let artifactId;
7
- let keyOverride;
8
- for (let i = 0; i < args.length; i++) {
9
- const a = args[i];
10
- if ((a === "--key" || a === "-k") && i + 1 < args.length) {
11
- keyOverride = args[++i];
12
- }
13
- else if (a.startsWith("--key=")) {
14
- keyOverride = a.slice("--key=".length);
15
- }
16
- else if (!a.startsWith("--") && !artifactId) {
17
- artifactId = a;
18
- }
19
- }
20
- if (!artifactId) {
21
- console.error("Usage: codiedev promote <extracted-artifact-id> [--key spec-foo.md]");
22
- process.exit(1);
23
- }
24
- return { artifactId, keyOverride };
25
- }
26
- async function runPromote(args) {
27
- const { artifactId, keyOverride } = parseArgs(args);
28
- const config = (0, shared_1.requireConfig)();
29
- const start = Date.now();
30
- try {
31
- const res = await (0, shared_1.apiRequest)("POST", "/api/cli/promote", {
32
- config,
33
- body: { artifactId, keyOverride },
34
- });
35
- (0, shared_1.recordAgentEvent)(config, {
36
- tool: "codiedev_promote",
37
- ok: true,
38
- latencyMs: Date.now() - start,
39
- });
40
- console.log(`✓ Promoted to authored artifact ${res.key}`);
41
- console.log(` id: ${res.artifactId}`);
42
- }
43
- catch (err) {
44
- (0, shared_1.recordAgentEvent)(config, {
45
- tool: "codiedev_promote",
46
- ok: false,
47
- latencyMs: Date.now() - start,
48
- error: err.message,
49
- });
50
- console.error(`Promote failed: ${err.message}`);
51
- process.exit(1);
52
- }
53
- }
@@ -1,2 +0,0 @@
1
- import type { CodiedevConfig } from "../utils";
2
- export declare function runReact(args: string[], configOverride?: CodiedevConfig): Promise<void>;
@@ -1,40 +0,0 @@
1
- "use strict";
2
- // `codiedev react <postId> <emoji>` — toggle a reaction on a feed post.
3
- // Calling twice with the same emoji removes it.
4
- Object.defineProperty(exports, "__esModule", { value: true });
5
- exports.runReact = runReact;
6
- const shared_1 = require("./shared");
7
- async function runReact(args, configOverride) {
8
- const positionals = args.filter((a) => !a.startsWith("--"));
9
- const [postId, emoji] = positionals;
10
- if (!postId || !emoji) {
11
- console.error("Usage: codiedev react <postId> <emoji>");
12
- process.exit(1);
13
- }
14
- const config = configOverride ?? (0, shared_1.requireConfig)();
15
- const start = Date.now();
16
- try {
17
- const res = await (0, shared_1.apiRequest)("POST", "/api/cli/react", {
18
- config,
19
- body: { postId, emoji },
20
- });
21
- (0, shared_1.recordAgentEvent)(config, {
22
- tool: "codiedev_react",
23
- ok: true,
24
- latencyMs: Date.now() - start,
25
- });
26
- console.log(res.action === "removed"
27
- ? `Removed ${res.emoji ?? emoji} from post ${postId}.`
28
- : `Reacted to post ${postId} with ${res.emoji ?? emoji}.`);
29
- }
30
- catch (err) {
31
- (0, shared_1.recordAgentEvent)(config, {
32
- tool: "codiedev_react",
33
- ok: false,
34
- latencyMs: Date.now() - start,
35
- error: err.message,
36
- });
37
- console.error(`React failed: ${err.message}`);
38
- process.exit(1);
39
- }
40
- }
@@ -1,2 +0,0 @@
1
- export declare function isSafeRef(ref: string): boolean;
2
- export declare function runReverseTicket(args: string[]): Promise<void>;