codiedev 0.7.0 → 0.7.2

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.
@@ -215,15 +215,41 @@ async function runBranchMode(opts) {
215
215
  console.error(`No commits on ${branch} ahead of ${baseRef} — make a commit first.`);
216
216
  process.exit(1);
217
217
  }
218
+ // Filter generated/lockfile noise from the diff body. The --stat (collected
219
+ // separately) still includes every file untouched, so excluded files still
220
+ // surface in the ticket's "Other files touched" section.
221
+ const isNoise = (p) => /(^|\/)(node_modules|dist|build|\.next|\.turbo|coverage|out)\//.test(p) ||
222
+ /(^|\/)package-lock\.json$/.test(p) ||
223
+ /(^|\/)yarn\.lock$/.test(p) ||
224
+ /(^|\/)pnpm-lock\.yaml$/.test(p) ||
225
+ /(^|\/)bun\.lock(b)?$/.test(p) ||
226
+ /(^|\/)Cargo\.lock$/.test(p) ||
227
+ /(^|\/)Gemfile\.lock$/.test(p) ||
228
+ /(^|\/)poetry\.lock$/.test(p) ||
229
+ /(^|\/)go\.sum$/.test(p);
218
230
  const filesRaw = git(`diff --name-only ${baseSha}...HEAD`);
219
231
  const filesChanged = filesRaw ? filesRaw.split("\n").filter(Boolean) : [];
220
232
  if (filesChanged.length === 0) {
221
233
  console.error(`No files changed between ${baseRef} and ${branch} — nothing to write up.`);
222
234
  process.exit(1);
223
235
  }
236
+ let stat = "";
237
+ try {
238
+ stat = git(`diff --stat=200 ${baseSha}...HEAD`).slice(0, 8_000);
239
+ }
240
+ catch {
241
+ // best effort
242
+ }
224
243
  let diff = "";
225
244
  try {
226
- diff = git(`diff ${baseSha}...HEAD`).slice(0, 12_000);
245
+ const sourceFiles = filesChanged.filter((f) => !isNoise(f));
246
+ if (sourceFiles.length > 0) {
247
+ const pathspecArgs = sourceFiles.map((f) => `'${f.replace(/'/g, "'\\''")}'`).join(" ");
248
+ diff = git(`diff ${baseSha}...HEAD -- ${pathspecArgs}`).slice(0, 40_000);
249
+ }
250
+ else {
251
+ diff = git(`diff ${baseSha}...HEAD`).slice(0, 40_000);
252
+ }
227
253
  }
228
254
  catch {
229
255
  // best effort
@@ -266,6 +292,7 @@ async function runBranchMode(opts) {
266
292
  authorEmail,
267
293
  filesChanged,
268
294
  diff: diff || undefined,
295
+ stat: stat || undefined,
269
296
  },
270
297
  forcedArtifactKey: opts.forcedKey,
271
298
  },
@@ -274,13 +301,17 @@ async function runBranchMode(opts) {
274
301
  console.error("Reverse-ticket generation returned no result. The writer may have skipped due to missing context.");
275
302
  process.exit(1);
276
303
  }
304
+ const portalHost = (process.env.CODIEDEV_PORTAL_URL || "https://codiedev.com").replace(/\/$/, "");
305
+ const portalLink = res.portalUrl.startsWith("http")
306
+ ? res.portalUrl
307
+ : `${portalHost}${res.portalUrl.startsWith("/") ? "" : "/"}${res.portalUrl}`;
277
308
  const verb = res.action === "overwritten" ? "Updated" : "Created";
278
309
  console.log(`✓ ${verb} reverse-ticket draft.`);
279
310
  console.log(` artifact: ${res.artifactKey ?? res.artifactId}`);
280
- console.log(` portal: ${res.portalUrl}`);
311
+ console.log(` portal: ${portalLink}`);
281
312
  console.log("");
282
313
  console.log("Open in your portal to review, edit, and push to Jira.");
283
- console.log("When the PR opens later, the webhook dedupes against this draft one ticket, not two.");
314
+ console.log("Each call creates a fresh draft clean up duplicates from the portal if needed.");
284
315
  }
285
316
  catch (err) {
286
317
  console.error(`Reverse-ticket failed: ${err.message}`);
package/dist/mcp.js CHANGED
@@ -1089,15 +1089,46 @@ async function handleReverseTicket(args, config) {
1089
1089
  if (baseSha === headSha) {
1090
1090
  throw new Error(`No commits on ${branch} ahead of ${baseRef} — make a commit first.`);
1091
1091
  }
1092
+ // Filter generated/lockfile noise so the LLM's diff budget gets spent on
1093
+ // real source. The --stat block (collected separately below) still includes
1094
+ // every file untouched, so excluded files still appear in the ticket's
1095
+ // "Other files touched" section if relevant.
1096
+ const isNoise = (p) => /(^|\/)(node_modules|dist|build|\.next|\.turbo|coverage|out)\//.test(p) ||
1097
+ /(^|\/)package-lock\.json$/.test(p) ||
1098
+ /(^|\/)yarn\.lock$/.test(p) ||
1099
+ /(^|\/)pnpm-lock\.yaml$/.test(p) ||
1100
+ /(^|\/)bun\.lock(b)?$/.test(p) ||
1101
+ /(^|\/)Cargo\.lock$/.test(p) ||
1102
+ /(^|\/)Gemfile\.lock$/.test(p) ||
1103
+ /(^|\/)poetry\.lock$/.test(p) ||
1104
+ /(^|\/)go\.sum$/.test(p);
1092
1105
  const filesRaw = git(`diff --name-only ${baseSha}...HEAD`);
1093
1106
  const filesChanged = filesRaw ? filesRaw.split("\n").filter(Boolean) : [];
1094
1107
  if (filesChanged.length === 0) {
1095
1108
  throw new Error(`No files changed between ${baseRef} and ${branch} — nothing to write up.`);
1096
1109
  }
1097
- // Truncated diff (12k chars) writer works without it but it sharpens output.
1110
+ // Always capture --statcompact (path + line counts), fits even for huge
1111
+ // PRs, and serves as the writer's ground truth for "every file touched"
1112
+ // when the diff body below is truncated.
1113
+ let stat = "";
1114
+ try {
1115
+ stat = git(`diff --stat=200 ${baseSha}...HEAD`).slice(0, 8_000);
1116
+ }
1117
+ catch {
1118
+ // Best effort.
1119
+ }
1120
+ // Diff body: 40k cap, noise-filtered via pathspec exclusions so lockfiles
1121
+ // and generated dirs don't eat the budget.
1098
1122
  let diff = "";
1099
1123
  try {
1100
- diff = git(`diff ${baseSha}...HEAD`).slice(0, 12_000);
1124
+ const sourceFiles = filesChanged.filter((f) => !isNoise(f));
1125
+ if (sourceFiles.length > 0) {
1126
+ const pathspecArgs = sourceFiles.map((f) => `'${f.replace(/'/g, "'\\''")}'`).join(" ");
1127
+ diff = git(`diff ${baseSha}...HEAD -- ${pathspecArgs}`).slice(0, 40_000);
1128
+ }
1129
+ else {
1130
+ diff = git(`diff ${baseSha}...HEAD`).slice(0, 40_000);
1131
+ }
1101
1132
  }
1102
1133
  catch {
1103
1134
  // Best effort.
@@ -1132,6 +1163,7 @@ async function handleReverseTicket(args, config) {
1132
1163
  authorEmail,
1133
1164
  filesChanged,
1134
1165
  diff: diff || undefined,
1166
+ stat: stat || undefined,
1135
1167
  },
1136
1168
  forcedArtifactKey: forcedKey,
1137
1169
  },
@@ -1150,11 +1182,13 @@ async function handleReverseTicket(args, config) {
1150
1182
  ],
1151
1183
  };
1152
1184
  }
1153
- const portalBase = (config.backendUrl || "")
1154
- .replace(/\/$/, "")
1155
- .replace(/\.convex\.cloud$/, ".convex.cloud"); // backendUrl is the Convex site; portal is separate
1156
- // Best effort surface the portal path; the user can prefix with their portal host.
1157
- const portalPath = res.portalUrl;
1185
+ // Always return an absolute URL so MCP clients (Copilot Chat, Claude Code,
1186
+ // etc.) can render the link correctly without guessing a host. Default to
1187
+ // codiedev.com; allow override via CODIEDEV_PORTAL_URL for self-hosted.
1188
+ const portalHost = (process.env.CODIEDEV_PORTAL_URL || "https://codiedev.com").replace(/\/$/, "");
1189
+ const portalLink = res.portalUrl.startsWith("http")
1190
+ ? res.portalUrl
1191
+ : `${portalHost}${res.portalUrl.startsWith("/") ? "" : "/"}${res.portalUrl}`;
1158
1192
  const verb = res.action === "overwritten" ? "Updated" : "Created";
1159
1193
  return {
1160
1194
  content: [
@@ -1166,9 +1200,9 @@ async function handleReverseTicket(args, config) {
1166
1200
  ` diff size: ${diff.length} chars\n` +
1167
1201
  ` artifact: ${res.artifactKey ?? res.artifactId}\n\n` +
1168
1202
  `Open in your portal to review, edit, and push to Jira:\n` +
1169
- ` ${portalPath}\n\n` +
1170
- `When the PR opens later, the webhook dedupes against this draft ` +
1171
- `you'll get one ticket, not two.`,
1203
+ ` ${portalLink}\n\n` +
1204
+ `Each call creates a fresh draft if you've made several for the ` +
1205
+ `same branch, clean them up from the portal.`,
1172
1206
  },
1173
1207
  ],
1174
1208
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codiedev",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
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",