codiedev 0.7.2 → 0.7.3

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.
@@ -211,10 +211,6 @@ async function runBranchMode(opts) {
211
211
  }
212
212
  const baseSha = git(`merge-base HEAD ${baseRef}`);
213
213
  const headSha = git("rev-parse HEAD");
214
- if (baseSha === headSha) {
215
- console.error(`No commits on ${branch} ahead of ${baseRef} — make a commit first.`);
216
- process.exit(1);
217
- }
218
214
  // Filter generated/lockfile noise from the diff body. The --stat (collected
219
215
  // separately) still includes every file untouched, so excluded files still
220
216
  // surface in the ticket's "Other files touched" section.
@@ -227,29 +223,61 @@ async function runBranchMode(opts) {
227
223
  /(^|\/)Gemfile\.lock$/.test(p) ||
228
224
  /(^|\/)poetry\.lock$/.test(p) ||
229
225
  /(^|\/)go\.sum$/.test(p);
230
- const filesRaw = git(`diff --name-only ${baseSha}...HEAD`);
231
- const filesChanged = filesRaw ? filesRaw.split("\n").filter(Boolean) : [];
226
+ const shellTolerant = (cmd) => {
227
+ try {
228
+ return (0, child_process_1.execSync)(`${cmd} || true`, {
229
+ stdio: ["pipe", "pipe", "pipe"],
230
+ maxBuffer: 32 * 1024 * 1024,
231
+ })
232
+ .toString("utf8")
233
+ .trim();
234
+ }
235
+ catch {
236
+ return "";
237
+ }
238
+ };
239
+ const shQuote = (s) => `'${s.replace(/'/g, "'\\''")}'`;
240
+ // Capture all work relative to the base ref: committed + uncommitted-tracked
241
+ // + new untracked. Matches "ticket my current changes" intent.
242
+ const trackedRaw = git(`diff --name-only ${baseSha}`);
243
+ const trackedFiles = trackedRaw ? trackedRaw.split("\n").filter(Boolean) : [];
244
+ const untrackedRaw = safeGit(() => git("ls-files --others --exclude-standard"));
245
+ const untrackedFiles = untrackedRaw
246
+ ? untrackedRaw.split("\n").filter(Boolean)
247
+ : [];
248
+ const filesChanged = Array.from(new Set([...trackedFiles, ...untrackedFiles]));
232
249
  if (filesChanged.length === 0) {
233
- console.error(`No files changed between ${baseRef} and ${branch} — nothing to write up.`);
250
+ console.error(`No changes detected between ${baseRef} and your working tree on ${branch}.`);
234
251
  process.exit(1);
235
252
  }
236
253
  let stat = "";
237
254
  try {
238
- stat = git(`diff --stat=200 ${baseSha}...HEAD`).slice(0, 8_000);
255
+ const trackedStat = git(`diff --stat=200 ${baseSha}`);
256
+ const untrackedStatLines = untrackedFiles.map((f) => {
257
+ const wc = shellTolerant(`wc -l < ${shQuote(f)}`);
258
+ const n = parseInt(wc.split(/\s+/)[0] || "0", 10) || 0;
259
+ return ` ${f} | ${n} ++++++++++ (new file)`;
260
+ });
261
+ stat = [trackedStat, ...untrackedStatLines]
262
+ .filter(Boolean)
263
+ .join("\n")
264
+ .slice(0, 8_000);
239
265
  }
240
266
  catch {
241
267
  // best effort
242
268
  }
243
269
  let diff = "";
244
270
  try {
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
- }
271
+ const sourceTracked = trackedFiles.filter((f) => !isNoise(f));
272
+ const trackedDiff = sourceTracked.length > 0
273
+ ? git(`diff ${baseSha} -- ${sourceTracked.map(shQuote).join(" ")}`)
274
+ : git(`diff ${baseSha}`);
275
+ const sourceUntracked = untrackedFiles.filter((f) => !isNoise(f));
276
+ const untrackedDiffs = sourceUntracked.map((f) => shellTolerant(`git diff --no-index --no-color -- /dev/null ${shQuote(f)}`));
277
+ diff = [trackedDiff, ...untrackedDiffs]
278
+ .filter(Boolean)
279
+ .join("\n")
280
+ .slice(0, 40_000);
253
281
  }
254
282
  catch {
255
283
  // best effort
package/dist/mcp.js CHANGED
@@ -52,7 +52,17 @@ const path = __importStar(require("path"));
52
52
  const utils_1 = require("./utils");
53
53
  const shared_1 = require("./commands/shared");
54
54
  const PKG_NAME = "codiedev";
55
- const PKG_VERSION = "0.5.0";
55
+ // Read version from the bundled package.json so the startup log + tool reports
56
+ // always reflect the actually-installed version, rather than a stale constant.
57
+ const PKG_VERSION = (() => {
58
+ try {
59
+ const pkgPath = path.resolve(__dirname, "..", "package.json");
60
+ return JSON.parse(fs.readFileSync(pkgPath, "utf8")).version ?? "unknown";
61
+ }
62
+ catch {
63
+ return "unknown";
64
+ }
65
+ })();
56
66
  // ─────────────────────────────────────────────────────────────────────────────
57
67
  // Tool definitions — descriptions tuned so Claude/Codex resolve natural-language
58
68
  // requests into the right tool without manual steering.
@@ -1086,9 +1096,6 @@ async function handleReverseTicket(args, config) {
1086
1096
  }
1087
1097
  const baseSha = git(`merge-base HEAD ${baseRef}`);
1088
1098
  const headSha = git("rev-parse HEAD");
1089
- if (baseSha === headSha) {
1090
- throw new Error(`No commits on ${branch} ahead of ${baseRef} — make a commit first.`);
1091
- }
1092
1099
  // Filter generated/lockfile noise so the LLM's diff budget gets spent on
1093
1100
  // real source. The --stat block (collected separately below) still includes
1094
1101
  // every file untouched, so excluded files still appear in the ticket's
@@ -1102,33 +1109,72 @@ async function handleReverseTicket(args, config) {
1102
1109
  /(^|\/)Gemfile\.lock$/.test(p) ||
1103
1110
  /(^|\/)poetry\.lock$/.test(p) ||
1104
1111
  /(^|\/)go\.sum$/.test(p);
1105
- const filesRaw = git(`diff --name-only ${baseSha}...HEAD`);
1106
- const filesChanged = filesRaw ? filesRaw.split("\n").filter(Boolean) : [];
1112
+ // Helper: run a shell command tolerating non-zero exit codes (git diff
1113
+ // --no-index exits 1 when files differ, which is the normal case).
1114
+ // Reuses the execSync already imported above for the git() helper.
1115
+ const shellTolerant = (cmd) => {
1116
+ try {
1117
+ return execSync(`${cmd} || true`, {
1118
+ stdio: ["pipe", "pipe", "pipe"],
1119
+ maxBuffer: 32 * 1024 * 1024,
1120
+ })
1121
+ .toString("utf8")
1122
+ .trim();
1123
+ }
1124
+ catch {
1125
+ return "";
1126
+ }
1127
+ };
1128
+ const shQuote = (s) => `'${s.replace(/'/g, "'\\''")}'`;
1129
+ // Capture all work the developer has done relative to the base ref:
1130
+ // - committed changes between base and HEAD
1131
+ // - uncommitted modifications to tracked files
1132
+ // - new untracked files (path listed + content via synthetic diff)
1133
+ // Matches the user's intent ("ticket my current changes") rather than the
1134
+ // narrower "only what's been committed" interpretation.
1135
+ const trackedRaw = git(`diff --name-only ${baseSha}`);
1136
+ const trackedFiles = trackedRaw ? trackedRaw.split("\n").filter(Boolean) : [];
1137
+ const untrackedRaw = safeGit(() => git("ls-files --others --exclude-standard"));
1138
+ const untrackedFiles = untrackedRaw
1139
+ ? untrackedRaw.split("\n").filter(Boolean)
1140
+ : [];
1141
+ const filesChanged = Array.from(new Set([...trackedFiles, ...untrackedFiles]));
1107
1142
  if (filesChanged.length === 0) {
1108
- throw new Error(`No files changed between ${baseRef} and ${branch} — nothing to write up.`);
1143
+ throw new Error(`No changes detected between ${baseRef} and your working tree on ${branch}.`);
1109
1144
  }
1110
- // Always capture --stat compact (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.
1145
+ // --stat block: compact ground truth for "every file touched". Tracked
1146
+ // changes from `git diff --stat baseSha`; untracked files appended as
1147
+ // synthesized stat rows so the LLM sees them with line counts.
1113
1148
  let stat = "";
1114
1149
  try {
1115
- stat = git(`diff --stat=200 ${baseSha}...HEAD`).slice(0, 8_000);
1150
+ const trackedStat = git(`diff --stat=200 ${baseSha}`);
1151
+ const untrackedStatLines = untrackedFiles.map((f) => {
1152
+ const wc = shellTolerant(`wc -l < ${shQuote(f)}`);
1153
+ const n = parseInt(wc.split(/\s+/)[0] || "0", 10) || 0;
1154
+ return ` ${f} | ${n} ++++++++++ (new file)`;
1155
+ });
1156
+ stat = [trackedStat, ...untrackedStatLines]
1157
+ .filter(Boolean)
1158
+ .join("\n")
1159
+ .slice(0, 8_000);
1116
1160
  }
1117
1161
  catch {
1118
1162
  // Best effort.
1119
1163
  }
1120
- // Diff body: 40k cap, noise-filtered via pathspec exclusions so lockfiles
1121
- // and generated dirs don't eat the budget.
1164
+ // Diff body: 40k cap, noise-filtered. Tracked changes via `git diff baseSha`,
1165
+ // plus synthetic add-only diffs for untracked files via `git diff --no-index`.
1122
1166
  let diff = "";
1123
1167
  try {
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
- }
1168
+ const sourceTracked = trackedFiles.filter((f) => !isNoise(f));
1169
+ const trackedDiff = sourceTracked.length > 0
1170
+ ? git(`diff ${baseSha} -- ${sourceTracked.map(shQuote).join(" ")}`)
1171
+ : git(`diff ${baseSha}`);
1172
+ const sourceUntracked = untrackedFiles.filter((f) => !isNoise(f));
1173
+ const untrackedDiffs = sourceUntracked.map((f) => shellTolerant(`git diff --no-index --no-color -- /dev/null ${shQuote(f)}`));
1174
+ diff = [trackedDiff, ...untrackedDiffs]
1175
+ .filter(Boolean)
1176
+ .join("\n")
1177
+ .slice(0, 40_000);
1132
1178
  }
1133
1179
  catch {
1134
1180
  // Best effort.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codiedev",
3
- "version": "0.7.2",
3
+ "version": "0.7.3",
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",