kodevu 0.1.58 → 0.1.60

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/README.md CHANGED
@@ -6,11 +6,11 @@ A Node.js tool that fetches Git commits or SVN revisions, sends the diff to a su
6
6
 
7
7
  ## Pure & Zero Config
8
8
 
9
- Kodevu is designed to be stateless and requires no configuration files. It relies entirely on command-line arguments and environment variables.
9
+ Kodevu is designed to be stateless and requires no mandatory configuration. All settings work out-of-the-box via command-line arguments and environment variables, with an optional persistent config file for convenience.
10
10
 
11
11
  1. **Automatic Detection**: Detects repository type (Git/SVN), language, and available reviewers.
12
12
  2. **Stateless**: Does not track history; reviews exactly what you ask for.
13
- 3. **Flexible**: Every setting can be overridden via CLI flags or ENV vars.
13
+ 3. **Flexible**: Every setting can be set via config file, ENV var, or CLI flag, with CLI taking highest priority.
14
14
 
15
15
  ## Quick Start
16
16
 
@@ -69,6 +69,28 @@ You can set these in your shell to change default behavior without typing flags
69
69
  - `KODEVU_OPENAI_ORG`: Optional organization ID for `openai`.
70
70
  - `KODEVU_OPENAI_PROJECT`: Optional project ID for `openai`.
71
71
 
72
+ ### Configuration File
73
+
74
+ For persistent settings that survive across shells and AI tools, create `~/.kodevu/config.json`:
75
+
76
+ ```json
77
+ {
78
+ "reviewer": "openai",
79
+ "openaiApiKey": "sk-...",
80
+ "openaiBaseUrl": "https://your-gateway.example.com/v1",
81
+ "openaiModel": "gpt-4o",
82
+ "lang": "zh"
83
+ }
84
+ ```
85
+
86
+ The file is optional and silently ignored if absent. **Priority order** (highest wins):
87
+
88
+ ```
89
+ CLI flags > Environment variables > Config file > Built-in defaults
90
+ ```
91
+
92
+ Supported keys: `reviewer`, `lang`, `outputDir`, `prompt`, `commandTimeoutMs`, `outputFormats`, `openaiApiKey`, `openaiBaseUrl`, `openaiModel`, `openaiOrganization`, `openaiProject`.
93
+
72
94
  ## Examples
73
95
 
74
96
  ### Selecting Revisions
package/SKILL.md CHANGED
@@ -5,7 +5,7 @@ description: A tool to fetch Git/SVN diffs, send them to an AI reviewer, and gen
5
5
 
6
6
  # Kodevu Skill
7
7
 
8
- Kodevu is a Node.js tool that fetches Git commits or SVN revisions, sends the diff to a supported AI reviewer CLI, and writes review results to report files. It is designed to be **stateless** and requires **no configuration files**.
8
+ Kodevu is a Node.js tool that fetches Git commits or SVN revisions, sends the diff to a supported AI reviewer CLI, and writes review results to report files. It supports an optional persistent config file at `~/.kodevu/config.json` for settings that should survive across sessions.
9
9
 
10
10
  ## Usage
11
11
 
@@ -71,6 +71,22 @@ All options can also be set via environment variables to avoid repetitive flags:
71
71
  - `KODEVU_OPENAI_BASE_URL` – Base URL for `openai`.
72
72
  - `KODEVU_OPENAI_MODEL` – Model for `openai`.
73
73
 
74
+ ### Configuration File
75
+
76
+ For persistent settings that survive across shells and AI tool invocations, create `~/.kodevu/config.json`:
77
+
78
+ ```json
79
+ {
80
+ "reviewer": "openai",
81
+ "openaiApiKey": "sk-...",
82
+ "openaiBaseUrl": "https://your-gateway.example.com/v1",
83
+ "openaiModel": "gpt-4o",
84
+ "lang": "zh"
85
+ }
86
+ ```
87
+
88
+ The file is optional and silently ignored if absent. Priority: **CLI flags > ENV vars > config file > defaults**.
89
+
74
90
  ## Working with Target Repositories
75
91
 
76
92
  - **Git**: `target` must be a local repository or subdirectory.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodevu",
3
- "version": "0.1.58",
3
+ "version": "0.1.60",
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
@@ -46,6 +46,9 @@ const ENV_MAP = {
46
46
  KODEVU_OPENAI_PROJECT: "openaiProject"
47
47
  };
48
48
 
49
+ const CONFIG_FILE_KEYS = new Set(Object.values(ENV_MAP));
50
+ const defaultConfigFilePath = path.join(defaultStorageDir, "config.json");
51
+
49
52
  function resolvePath(value) {
50
53
  if (!value) return value;
51
54
  if (value === "~") return os.homedir();
@@ -55,6 +58,33 @@ function resolvePath(value) {
55
58
  return path.isAbsolute(value) ? value : path.resolve(process.cwd(), value);
56
59
  }
57
60
 
61
+ async function loadConfigFile(configPath = defaultConfigFilePath) {
62
+ const resolvedPath = resolvePath(configPath);
63
+ let content;
64
+ try {
65
+ content = await fs.readFile(resolvedPath, "utf8");
66
+ } catch (err) {
67
+ if (err.code === "ENOENT") return {};
68
+ throw new Error(`Failed to read config file ${resolvedPath}: ${err.message}`);
69
+ }
70
+ let parsed;
71
+ try {
72
+ parsed = JSON.parse(content);
73
+ } catch (err) {
74
+ throw new Error(`Invalid JSON in config file ${resolvedPath}: ${err.message}`);
75
+ }
76
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
77
+ throw new Error(`Config file ${resolvedPath} must contain a JSON object`);
78
+ }
79
+ const result = {};
80
+ for (const [key, value] of Object.entries(parsed)) {
81
+ if (CONFIG_FILE_KEYS.has(key)) {
82
+ result[key] = value;
83
+ }
84
+ }
85
+ return result;
86
+ }
87
+
58
88
  function normalizeOutputFormats(outputFormats) {
59
89
  const source = outputFormats == null ? ["markdown"] : outputFormats;
60
90
  const values = Array.isArray(source) ? source : String(source).split(",");
@@ -260,6 +290,14 @@ export function parseCliArgs(argv) {
260
290
  export async function resolveConfig(cliArgs = {}) {
261
291
  const config = { ...defaultConfig };
262
292
 
293
+ // 0. Merge Config File (lowest priority: overridden by env vars and CLI args)
294
+ const fileConfig = await loadConfigFile();
295
+ for (const key of CONFIG_FILE_KEYS) {
296
+ if (fileConfig[key] !== undefined && fileConfig[key] !== "") {
297
+ config[key] = fileConfig[key];
298
+ }
299
+ }
300
+
263
301
  // 1. Merge Environment Variables
264
302
  for (const [envVar, configKey] of Object.entries(ENV_MAP)) {
265
303
  if (process.env[envVar] !== undefined) {
@@ -388,6 +426,12 @@ Environment Variables:
388
426
  KODEVU_OPENAI_MODEL Model for reviewer=openai
389
427
  KODEVU_OPENAI_ORG Organization ID for reviewer=openai
390
428
  KODEVU_OPENAI_PROJECT Project ID for reviewer=openai
429
+
430
+ Config File:
431
+ ~/.kodevu/config.json Optional persistent settings (overridden by env vars and CLI flags)
432
+ Supported keys: reviewer, lang, outputDir, prompt, commandTimeoutMs, outputFormats,
433
+ openaiApiKey, openaiBaseUrl, openaiModel, openaiOrganization, openaiProject
434
+ Example: { "reviewer": "openai", "openaiApiKey": "sk-...", "openaiModel": "gpt-4o" }
391
435
  `);
392
436
  }
393
437
 
package/src/reviewers.js CHANGED
@@ -227,11 +227,13 @@ export const REVIEWERS = {
227
227
  };
228
228
  } catch (error) {
229
229
  const timedOut = error?.name === "TimeoutError" || error?.name === "AbortError";
230
+ const baseMessage = error?.message || String(error);
231
+ const causeMessage = error?.cause ? ` (Cause: ${error.cause.message || String(error.cause)})` : "";
230
232
  return {
231
233
  code: 1,
232
234
  timedOut,
233
235
  stdout: "",
234
- stderr: error?.message || String(error),
236
+ stderr: `${baseMessage}${causeMessage}`,
235
237
  message: ""
236
238
  };
237
239
  }