@karmaniverous/jeeves-watcher 0.18.4 → 0.18.6

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.
@@ -10,7 +10,7 @@ import Ajv from 'ajv';
10
10
  import addFormats from 'ajv-formats';
11
11
  import picomatch from 'picomatch';
12
12
  import { deserialize, serialize } from 'node:v8';
13
- import { normalizeWatchPaths, extractWatchPathStrings, inferenceRuleSchema, jeevesWatcherConfigSchema } from '@karmaniverous/jeeves-watcher-core';
13
+ import { normalizeWatchPaths, extractWatchPathStrings, vcsAuthorConfigSchema, inferenceRuleSchema, jeevesWatcherConfigSchema } from '@karmaniverous/jeeves-watcher-core';
14
14
  import { execFile } from 'node:child_process';
15
15
  import { promisify } from 'node:util';
16
16
  import { homedir } from 'node:os';
@@ -934,6 +934,39 @@ class CommitMessageGenerator {
934
934
  */
935
935
  /** Promisified execFile for git commands. */
936
936
  const execFileAsync = promisify(execFile);
937
+ /**
938
+ * Stage files via stdin to avoid ENAMETOOLONG on Windows.
939
+ *
940
+ * Uses `git add --pathspec-from-file=- --pathspec-file-nul` so the file list is piped
941
+ * through stdin (NUL-delimited) instead of passed as command-line
942
+ * arguments. This sidesteps the Windows CreateProcessW 32 767-char
943
+ * limit that triggers ENAMETOOLONG when batches contain many files
944
+ * with long absolute paths.
945
+ *
946
+ * @param files - Absolute paths of files to stage.
947
+ * @param cwd - Repository root (working directory for git).
948
+ */
949
+ function gitAddViaStdin(files, cwd) {
950
+ return new Promise((resolve, reject) => {
951
+ const child = execFile('git', ['add', '--pathspec-from-file=-', '--pathspec-file-nul'], { cwd }, (error) => {
952
+ if (error)
953
+ reject(error);
954
+ else
955
+ resolve();
956
+ });
957
+ if (!child.stdin) {
958
+ reject(new Error('Failed to open stdin for git add'));
959
+ return;
960
+ }
961
+ // Suppress EPIPE — expected if git exits before we finish writing
962
+ child.stdin.on('error', (err) => {
963
+ if (err.code !== 'EPIPE') {
964
+ reject(err);
965
+ }
966
+ });
967
+ child.stdin.end(files.join('\0'));
968
+ });
969
+ }
937
970
  /**
938
971
  * Normalize a path for case-insensitive comparison on Windows.
939
972
  * On Windows, lowercases the entire path; on other platforms, returns as-is.
@@ -1550,9 +1583,7 @@ class VcsManager {
1550
1583
  return;
1551
1584
  try {
1552
1585
  await retry(async (attempt) => {
1553
- await execFileAsync('git', ['add', '--', ...files], {
1554
- cwd: this.rootPath,
1555
- });
1586
+ await gitAddViaStdin(files, this.rootPath);
1556
1587
  // Build message after staging so getStagedDiff can see the changes
1557
1588
  // Skip AI for baselines and retries — use template directly
1558
1589
  const message = this.isBaseline || attempt > 1
@@ -3052,6 +3083,22 @@ async function initRepo(rootPath) {
3052
3083
  await execFileAsync('git', ['init'], { cwd: rootPath });
3053
3084
  }
3054
3085
  }
3086
+ /**
3087
+ * Configure local git identity for a repository.
3088
+ * Sets repo-level (not global) user.name and user.email.
3089
+ *
3090
+ * @param rootPath - Directory of the git repository.
3091
+ * @param name - Author name for commits.
3092
+ * @param email - Author email for commits.
3093
+ */
3094
+ async function configureRepoIdentity(rootPath, name, email) {
3095
+ await execFileAsync('git', ['config', '--local', 'user.name', name], {
3096
+ cwd: rootPath,
3097
+ });
3098
+ await execFileAsync('git', ['config', '--local', 'user.email', email], {
3099
+ cwd: rootPath,
3100
+ });
3101
+ }
3055
3102
  /**
3056
3103
  * Ensure a .gitignore file exists at rootPath with all always-on entries.
3057
3104
  * Creates the file if missing. Appends only entries not already present.
@@ -3266,7 +3313,15 @@ async function initVcs(config, logger) {
3266
3313
  initializedRoots.add(root);
3267
3314
  await initRepo(root);
3268
3315
  await ensureGitignore(root);
3316
+ // Resolve author identity: per-path → root-level → schema defaults.
3317
+ const defaults = vcsAuthorConfigSchema.parse({});
3318
+ const pathAuthor = entry.vcs?.author;
3319
+ const rootAuthor = config.vcs.author;
3320
+ const authorName = pathAuthor?.name ?? rootAuthor?.name ?? defaults.name;
3321
+ const authorEmail = pathAuthor?.email ?? rootAuthor?.email ?? defaults.email;
3322
+ await configureRepoIdentity(root, authorName, authorEmail);
3269
3323
  logger.info({ root }, 'VCS initialized for watch root');
3324
+ logger.debug({ root, authorName, authorEmail }, 'VCS author identity configured');
3270
3325
  }
3271
3326
  return true;
3272
3327
  }
@@ -224,7 +224,7 @@ async function startFromConfig(configPath, descriptor) {
224
224
  const config = await loadConfig(resolvedPath);
225
225
  // Dynamic import breaks the circular dependency between this module
226
226
  // and app/index.ts (which defines JeevesWatcher and re-exports startFromConfig).
227
- const { JeevesWatcher } = await import('./index-ChYxOGtD.js');
227
+ const { JeevesWatcher } = await import('./index-CS1wrtes.js');
228
228
  const app = new JeevesWatcher(config, resolvedPath, descriptor);
229
229
  installShutdownHandlers(() => app.stop());
230
230
  await app.start();
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { join, dirname, resolve, relative, extname, basename, isAbsolute, sep } from 'node:path';
2
2
  import { createConfigQueryHandler as createConfigQueryHandler$1, createStatusHandler, createConfigApplyHandler, substituteEnvVars, DEFAULT_PORTS, getBindAddress, postJson, fetchJson, getPackageVersion } from '@karmaniverous/jeeves';
3
- import { extractWatchPathStrings, inferenceRuleSchema, jeevesWatcherConfigSchema, normalizeWatchPaths, LOGGING_DEFAULTS, API_DEFAULTS, EMBEDDING_DEFAULTS, CONFIG_WATCH_DEFAULTS, WATCH_DEFAULTS, ROOT_DEFAULTS, INIT_CONFIG_TEMPLATE, DEFAULT_PORT as DEFAULT_PORT$1, PLUGIN_PACKAGE, SERVICE_PACKAGE, COMPONENT_NAME } from '@karmaniverous/jeeves-watcher-core';
3
+ import { extractWatchPathStrings, inferenceRuleSchema, jeevesWatcherConfigSchema, normalizeWatchPaths, vcsAuthorConfigSchema, LOGGING_DEFAULTS, API_DEFAULTS, EMBEDDING_DEFAULTS, CONFIG_WATCH_DEFAULTS, WATCH_DEFAULTS, ROOT_DEFAULTS, INIT_CONFIG_TEMPLATE, DEFAULT_PORT as DEFAULT_PORT$1, PLUGIN_PACKAGE, SERVICE_PACKAGE, COMPONENT_NAME } from '@karmaniverous/jeeves-watcher-core';
4
4
  export { apiConfigSchema, configWatchConfigSchema, embeddingConfigSchema, inferenceRuleSchema, jeevesWatcherConfigSchema, loggingConfigSchema, vectorStoreConfigSchema, watchConfigSchema } from '@karmaniverous/jeeves-watcher-core';
5
5
  import Fastify from 'fastify';
6
6
  import { readdir, stat, readFile, writeFile, mkdir, appendFile, access, rm } from 'node:fs/promises';
@@ -3375,6 +3375,39 @@ function createSearchHandler(deps) {
3375
3375
  */
3376
3376
  /** Promisified execFile for git commands. */
3377
3377
  const execFileAsync = promisify(execFile);
3378
+ /**
3379
+ * Stage files via stdin to avoid ENAMETOOLONG on Windows.
3380
+ *
3381
+ * Uses `git add --pathspec-from-file=- --pathspec-file-nul` so the file list is piped
3382
+ * through stdin (NUL-delimited) instead of passed as command-line
3383
+ * arguments. This sidesteps the Windows CreateProcessW 32 767-char
3384
+ * limit that triggers ENAMETOOLONG when batches contain many files
3385
+ * with long absolute paths.
3386
+ *
3387
+ * @param files - Absolute paths of files to stage.
3388
+ * @param cwd - Repository root (working directory for git).
3389
+ */
3390
+ function gitAddViaStdin(files, cwd) {
3391
+ return new Promise((resolve, reject) => {
3392
+ const child = execFile('git', ['add', '--pathspec-from-file=-', '--pathspec-file-nul'], { cwd }, (error) => {
3393
+ if (error)
3394
+ reject(error);
3395
+ else
3396
+ resolve();
3397
+ });
3398
+ if (!child.stdin) {
3399
+ reject(new Error('Failed to open stdin for git add'));
3400
+ return;
3401
+ }
3402
+ // Suppress EPIPE — expected if git exits before we finish writing
3403
+ child.stdin.on('error', (err) => {
3404
+ if (err.code !== 'EPIPE') {
3405
+ reject(err);
3406
+ }
3407
+ });
3408
+ child.stdin.end(files.join('\0'));
3409
+ });
3410
+ }
3378
3411
  /**
3379
3412
  * Normalize a path for case-insensitive comparison on Windows.
3380
3413
  * On Windows, lowercases the entire path; on other platforms, returns as-is.
@@ -4642,6 +4675,22 @@ async function initRepo(rootPath) {
4642
4675
  await execFileAsync('git', ['init'], { cwd: rootPath });
4643
4676
  }
4644
4677
  }
4678
+ /**
4679
+ * Configure local git identity for a repository.
4680
+ * Sets repo-level (not global) user.name and user.email.
4681
+ *
4682
+ * @param rootPath - Directory of the git repository.
4683
+ * @param name - Author name for commits.
4684
+ * @param email - Author email for commits.
4685
+ */
4686
+ async function configureRepoIdentity(rootPath, name, email) {
4687
+ await execFileAsync('git', ['config', '--local', 'user.name', name], {
4688
+ cwd: rootPath,
4689
+ });
4690
+ await execFileAsync('git', ['config', '--local', 'user.email', email], {
4691
+ cwd: rootPath,
4692
+ });
4693
+ }
4645
4694
  /**
4646
4695
  * Ensure a .gitignore file exists at rootPath with all always-on entries.
4647
4696
  * Creates the file if missing. Appends only entries not already present.
@@ -5030,9 +5079,7 @@ class VcsManager {
5030
5079
  return;
5031
5080
  try {
5032
5081
  await retry(async (attempt) => {
5033
- await execFileAsync('git', ['add', '--', ...files], {
5034
- cwd: this.rootPath,
5035
- });
5082
+ await gitAddViaStdin(files, this.rootPath);
5036
5083
  // Build message after staging so getStagedDiff can see the changes
5037
5084
  // Skip AI for baselines and retries — use template directly
5038
5085
  const message = this.isBaseline || attempt > 1
@@ -5402,7 +5449,15 @@ async function initVcs(config, logger) {
5402
5449
  initializedRoots.add(root);
5403
5450
  await initRepo(root);
5404
5451
  await ensureGitignore(root);
5452
+ // Resolve author identity: per-path → root-level → schema defaults.
5453
+ const defaults = vcsAuthorConfigSchema.parse({});
5454
+ const pathAuthor = entry.vcs?.author;
5455
+ const rootAuthor = config.vcs.author;
5456
+ const authorName = pathAuthor?.name ?? rootAuthor?.name ?? defaults.name;
5457
+ const authorEmail = pathAuthor?.email ?? rootAuthor?.email ?? defaults.email;
5458
+ await configureRepoIdentity(root, authorName, authorEmail);
5405
5459
  logger.info({ root }, 'VCS initialized for watch root');
5460
+ logger.debug({ root, authorName, authorEmail }, 'VCS author identity configured');
5406
5461
  }
5407
5462
  return true;
5408
5463
  }
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "dependencies": {
10
10
  "@commander-js/extra-typings": "^14.0.0",
11
11
  "@karmaniverous/jeeves": "^0.5.12",
12
- "@karmaniverous/jeeves-watcher-core": "^0.2.1",
12
+ "@karmaniverous/jeeves-watcher-core": "^0.2.2",
13
13
  "@karmaniverous/jsonmap": "^2.1.1",
14
14
  "@langchain/textsplitters": "*",
15
15
  "@qdrant/js-client-rest": "*",
@@ -149,5 +149,5 @@
149
149
  },
150
150
  "type": "module",
151
151
  "types": "dist/index.d.ts",
152
- "version": "0.18.4"
152
+ "version": "0.18.6"
153
153
  }