kodevu 0.1.33 → 0.1.36

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodevu",
3
- "version": "0.1.33",
3
+ "version": "0.1.36",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Poll SVN revisions or Git commits, send each change diff to a reviewer CLI, and write configurable review reports.",
package/src/config.js CHANGED
@@ -214,6 +214,7 @@ export async function resolveConfig(cliArgs = {}) {
214
214
  config.target = process.cwd();
215
215
  }
216
216
 
217
+ config.baseDir = process.cwd();
217
218
  config.debug = Boolean(cliArgs.debug);
218
219
  config.reviewer = String(config.reviewer || "auto").toLowerCase();
219
220
  config.lang = String(config.lang || "auto").toLowerCase();
@@ -265,8 +266,8 @@ Options:
265
266
  --reviewer, -r Reviewer (codex | gemini | copilot | auto, default: auto)
266
267
  --prompt, -p Additional instructions or @file.txt to read from file
267
268
  --lang, -l Output language (e.g. zh, en, auto)
268
- --rev, -v Review a specific revision or commit hash
269
- --last, -n Review the latest N revisions (default: 1)
269
+ --rev, -v Review specific revision(s), hashes, branches or ranges (comma-separated)
270
+ --last, -n Review the latest N revisions (ignored if --rev is provided) (default: 1)
270
271
  --output, -o Output directory (default: ~/.kodevu)
271
272
  --format, -f Output formats (markdown, json, comma-separated)
272
273
  --debug, -d Print extra debug information
package/src/git-client.js CHANGED
@@ -105,6 +105,25 @@ export async function isValidCheckpoint(config, targetInfo, checkpointCommit, la
105
105
  return ancestorResult.code === 0;
106
106
  }
107
107
 
108
+ export async function resolveCommits(config, targetInfo, revSpec) {
109
+ const result = await runGit(
110
+ config,
111
+ ["rev-list", revSpec],
112
+ { cwd: targetInfo.repoRootPath, trim: true, allowFailure: true }
113
+ );
114
+
115
+ if (result.code !== 0) {
116
+ // Attempt fallback to a single hash resolution if rev-list fails (e.g. for non-standard specs)
117
+ const single = await runGit(config, ["rev-parse", revSpec], {
118
+ cwd: targetInfo.repoRootPath, trim: true, allowFailure: true
119
+ });
120
+ if (single.code === 0) return [single.stdout.trim()];
121
+ throw new Error(`Failed to resolve Git revision: ${revSpec}`);
122
+ }
123
+
124
+ return splitLines(result.stdout);
125
+ }
126
+
108
127
  export async function getPendingCommits(config, targetInfo, startExclusive, endInclusive, limit) {
109
128
  const args = ["rev-list", "--reverse"];
110
129
 
@@ -20,6 +20,7 @@ async function reviewChange(config, backend, targetInfo, changeId, progress) {
20
20
  logger.info(`Starting review for ${backend.changeName} ${displayId}`);
21
21
  progress?.update(0.05, "loading change details");
22
22
  const details = await backend.getChangeDetails(config, targetInfo, changeId);
23
+ const resolvedChangeId = details.id;
23
24
 
24
25
  if (details.changedPaths.length === 0) {
25
26
  progress?.update(0.7, "writing skipped report");
@@ -29,7 +30,7 @@ async function reviewChange(config, backend, targetInfo, changeId, progress) {
29
30
  "No file changes were captured for this change under the configured target."
30
31
  ].join("\n");
31
32
 
32
- const markdownReportFile = path.join(config.outputDir, backend.getReportFileName(changeId));
33
+ const markdownReportFile = path.join(config.outputDir, backend.getReportFileName(resolvedChangeId));
33
34
  const jsonReportFile = markdownReportFile.replace(/\.md$/i, ".json");
34
35
 
35
36
  if (shouldWriteFormat(config, "markdown")) {
@@ -55,7 +56,7 @@ async function reviewChange(config, backend, targetInfo, changeId, progress) {
55
56
  }
56
57
 
57
58
  progress?.update(0.2, "loading diff");
58
- const diffText = await backend.getChangeDiff(config, targetInfo, changeId);
59
+ const diffText = await backend.getChangeDiff(config, targetInfo, resolvedChangeId);
59
60
  const reviewersToTry = [config.reviewer, ...(config.fallbackReviewers || [])];
60
61
 
61
62
  let reviewer;
@@ -99,7 +100,7 @@ async function reviewChange(config, backend, targetInfo, changeId, progress) {
99
100
  progress?.update(0.82, "writing report");
100
101
  logger.debug(`Token usage: input=${tokenUsage.inputTokens} output=${tokenUsage.outputTokens} total=${tokenUsage.totalTokens} source=${tokenUsage.source}`);
101
102
  const report = buildReport(currentReviewerConfig, backend, targetInfo, details, diffPayloads, reviewer, reviewerResult, tokenUsage);
102
- const outputFile = path.join(config.outputDir, backend.getReportFileName(changeId));
103
+ const outputFile = path.join(config.outputDir, backend.getReportFileName(resolvedChangeId));
103
104
  const jsonOutputFile = outputFile.replace(/\.md$/i, ".json");
104
105
 
105
106
  if (shouldWriteFormat(config, "markdown")) {
@@ -156,7 +157,7 @@ export async function runReviewCycle(config) {
156
157
  let changeIdsToReview = [];
157
158
 
158
159
  if (config.rev) {
159
- changeIdsToReview = [config.rev];
160
+ changeIdsToReview = await backend.resolveChangeIds(config, targetInfo, config.rev);
160
161
  } else {
161
162
  changeIdsToReview = await backend.getLatestChangeIds(config, targetInfo, config.last || 1);
162
163
  }
package/src/vcs-client.js CHANGED
@@ -36,6 +36,10 @@ function createSvnBackend() {
36
36
  return `${datePrefix}-svn-r${revision}.md`;
37
37
  },
38
38
 
39
+ async resolveChangeIds(config, targetInfo, revString) {
40
+ if (!revString) return [];
41
+ return String(revString).split(',').map(s => s.trim()).filter(Boolean);
42
+ },
39
43
  async getTargetInfo(config) {
40
44
  return await svnClient.getTargetInfo(config);
41
45
  },
@@ -83,6 +87,16 @@ function createGitBackend() {
83
87
  return `${datePrefix}-git-${commitHash.slice(0, 12)}.md`;
84
88
  },
85
89
 
90
+ async resolveChangeIds(config, targetInfo, revString) {
91
+ if (!revString) return [];
92
+ const specs = String(revString).split(',').map(s => s.trim()).filter(Boolean);
93
+ const allHashes = [];
94
+ for (const spec of specs) {
95
+ const hashes = await gitClient.resolveCommits(config, targetInfo, spec);
96
+ allHashes.push(...hashes);
97
+ }
98
+ return [...new Set(allHashes)];
99
+ },
86
100
  async getTargetInfo(config) {
87
101
  return await gitClient.getTargetInfo(config);
88
102
  },
@@ -99,8 +113,8 @@ function createGitBackend() {
99
113
  const details = await gitClient.getCommitDetails(config, targetInfo, commitHash);
100
114
 
101
115
  return {
102
- id: commitHash,
103
- displayId: commitHash.slice(0, 12),
116
+ id: details.commitHash,
117
+ displayId: details.commitHash.slice(0, 12),
104
118
  author: details.author,
105
119
  date: details.date,
106
120
  message: details.message,