claude-auto 0.13.8 → 0.14.0

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.
Files changed (59) hide show
  1. package/.claude-auto/agents/validator.md +4 -0
  2. package/dist/bundle/scripts/auto-continue.js +136 -79
  3. package/dist/bundle/scripts/pre-tool-use.js +57 -55
  4. package/dist/bundle/scripts/session-start.js +68 -52
  5. package/dist/bundle/scripts/user-prompt-submit.js +85 -64
  6. package/dist/scripts/auto-continue.js +28 -12
  7. package/dist/scripts/auto-continue.js.map +1 -1
  8. package/dist/scripts/pre-tool-use.js +3 -1
  9. package/dist/scripts/pre-tool-use.js.map +1 -1
  10. package/dist/scripts/session-start.js +3 -1
  11. package/dist/scripts/session-start.js.map +1 -1
  12. package/dist/scripts/user-prompt-submit.js +4 -2
  13. package/dist/scripts/user-prompt-submit.js.map +1 -1
  14. package/dist/src/cli/install.d.ts.map +1 -1
  15. package/dist/src/cli/install.js +1 -0
  16. package/dist/src/cli/install.js.map +1 -1
  17. package/dist/src/cli/install.test.js +7 -0
  18. package/dist/src/cli/install.test.js.map +1 -1
  19. package/dist/src/commit-validator.d.ts.map +1 -1
  20. package/dist/src/commit-validator.js +3 -3
  21. package/dist/src/commit-validator.js.map +1 -1
  22. package/dist/src/commit-validator.test.js +40 -29
  23. package/dist/src/commit-validator.test.js.map +1 -1
  24. package/dist/src/e2e.test.js +93 -0
  25. package/dist/src/e2e.test.js.map +1 -1
  26. package/dist/src/hook-input.d.ts +1 -0
  27. package/dist/src/hook-input.d.ts.map +1 -1
  28. package/dist/src/hook-input.js.map +1 -1
  29. package/dist/src/hook-input.test.js +13 -0
  30. package/dist/src/hook-input.test.js.map +1 -1
  31. package/dist/src/hooks/pre-tool-use.d.ts +1 -0
  32. package/dist/src/hooks/pre-tool-use.d.ts.map +1 -1
  33. package/dist/src/hooks/pre-tool-use.js +4 -3
  34. package/dist/src/hooks/pre-tool-use.js.map +1 -1
  35. package/dist/src/hooks/session-start.d.ts +1 -1
  36. package/dist/src/hooks/session-start.d.ts.map +1 -1
  37. package/dist/src/hooks/session-start.js +16 -1
  38. package/dist/src/hooks/session-start.js.map +1 -1
  39. package/dist/src/hooks/session-start.test.js +17 -0
  40. package/dist/src/hooks/session-start.test.js.map +1 -1
  41. package/dist/src/hooks/user-prompt-submit.d.ts +5 -2
  42. package/dist/src/hooks/user-prompt-submit.d.ts.map +1 -1
  43. package/dist/src/hooks/user-prompt-submit.js +24 -8
  44. package/dist/src/hooks/user-prompt-submit.js.map +1 -1
  45. package/dist/src/hooks/user-prompt-submit.test.js +44 -8
  46. package/dist/src/hooks/user-prompt-submit.test.js.map +1 -1
  47. package/dist/src/validator-session.d.ts +2 -0
  48. package/dist/src/validator-session.d.ts.map +1 -0
  49. package/dist/src/validator-session.js +12 -0
  50. package/dist/src/validator-session.js.map +1 -0
  51. package/dist/src/validator-session.test.d.ts +2 -0
  52. package/dist/src/validator-session.test.d.ts.map +1 -0
  53. package/dist/src/validator-session.test.js +57 -0
  54. package/dist/src/validator-session.test.js.map +1 -0
  55. package/package.json +1 -1
  56. package/scripts/auto-continue.ts +31 -17
  57. package/scripts/pre-tool-use.ts +3 -1
  58. package/scripts/session-start.ts +3 -1
  59. package/scripts/user-prompt-submit.ts +4 -2
@@ -0,0 +1,4 @@
1
+ ---
2
+ name: validator
3
+ description: Commit validator subagent
4
+ ---
@@ -30,9 +30,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
30
30
  var require_resolve_from = __commonJS({
31
31
  "node_modules/.pnpm/resolve-from@4.0.0/node_modules/resolve-from/index.js"(exports2, module2) {
32
32
  "use strict";
33
- var path5 = require("path");
33
+ var path6 = require("path");
34
34
  var Module = require("module");
35
- var fs4 = require("fs");
35
+ var fs5 = require("fs");
36
36
  var resolveFrom = (fromDir, moduleId, silent) => {
37
37
  if (typeof fromDir !== "string") {
38
38
  throw new TypeError(`Expected \`fromDir\` to be of type \`string\`, got \`${typeof fromDir}\``);
@@ -41,17 +41,17 @@ var require_resolve_from = __commonJS({
41
41
  throw new TypeError(`Expected \`moduleId\` to be of type \`string\`, got \`${typeof moduleId}\``);
42
42
  }
43
43
  try {
44
- fromDir = fs4.realpathSync(fromDir);
44
+ fromDir = fs5.realpathSync(fromDir);
45
45
  } catch (err) {
46
46
  if (err.code === "ENOENT") {
47
- fromDir = path5.resolve(fromDir);
47
+ fromDir = path6.resolve(fromDir);
48
48
  } else if (silent) {
49
49
  return null;
50
50
  } else {
51
51
  throw err;
52
52
  }
53
53
  }
54
- const fromFile = path5.join(fromDir, "noop.js");
54
+ const fromFile = path6.join(fromDir, "noop.js");
55
55
  const resolveFileName = () => Module._resolveFilename(moduleId, {
56
56
  id: fromFile,
57
57
  filename: fromFile,
@@ -123,7 +123,7 @@ var require_parent_module = __commonJS({
123
123
  var require_import_fresh = __commonJS({
124
124
  "node_modules/.pnpm/import-fresh@3.3.1/node_modules/import-fresh/index.js"(exports2, module2) {
125
125
  "use strict";
126
- var path5 = require("path");
126
+ var path6 = require("path");
127
127
  var resolveFrom = require_resolve_from();
128
128
  var parentModule = require_parent_module();
129
129
  module2.exports = (moduleId) => {
@@ -131,7 +131,7 @@ var require_import_fresh = __commonJS({
131
131
  throw new TypeError("Expected a string");
132
132
  }
133
133
  const parentPath = parentModule(__filename);
134
- const cwd = parentPath ? path5.dirname(parentPath) : __dirname;
134
+ const cwd = parentPath ? path6.dirname(parentPath) : __dirname;
135
135
  const filePath = resolveFrom(cwd, moduleId);
136
136
  const oldModule = require.cache[filePath];
137
137
  if (oldModule && oldModule.parent) {
@@ -3889,7 +3889,7 @@ ${error.message}`;
3889
3889
  return typescript.sys.fileExists(fileName);
3890
3890
  });
3891
3891
  if (filePath !== void 0) {
3892
- const { config, error } = typescript.readConfigFile(filePath, (path5) => typescript.sys.readFile(path5));
3892
+ const { config, error } = typescript.readConfigFile(filePath, (path6) => typescript.sys.readFile(path6));
3893
3893
  if (error) {
3894
3894
  throw new Error(`Error in ${filePath}: ${error.messageText.toString()}`);
3895
3895
  }
@@ -4012,42 +4012,42 @@ var require_defaults = __commonJS({
4012
4012
  var require_env_paths = __commonJS({
4013
4013
  "node_modules/.pnpm/env-paths@2.2.1/node_modules/env-paths/index.js"(exports2, module2) {
4014
4014
  "use strict";
4015
- var path5 = require("path");
4015
+ var path6 = require("path");
4016
4016
  var os = require("os");
4017
4017
  var homedir = os.homedir();
4018
4018
  var tmpdir = os.tmpdir();
4019
4019
  var { env } = process;
4020
4020
  var macos = (name) => {
4021
- const library = path5.join(homedir, "Library");
4021
+ const library = path6.join(homedir, "Library");
4022
4022
  return {
4023
- data: path5.join(library, "Application Support", name),
4024
- config: path5.join(library, "Preferences", name),
4025
- cache: path5.join(library, "Caches", name),
4026
- log: path5.join(library, "Logs", name),
4027
- temp: path5.join(tmpdir, name)
4023
+ data: path6.join(library, "Application Support", name),
4024
+ config: path6.join(library, "Preferences", name),
4025
+ cache: path6.join(library, "Caches", name),
4026
+ log: path6.join(library, "Logs", name),
4027
+ temp: path6.join(tmpdir, name)
4028
4028
  };
4029
4029
  };
4030
4030
  var windows = (name) => {
4031
- const appData = env.APPDATA || path5.join(homedir, "AppData", "Roaming");
4032
- const localAppData = env.LOCALAPPDATA || path5.join(homedir, "AppData", "Local");
4031
+ const appData = env.APPDATA || path6.join(homedir, "AppData", "Roaming");
4032
+ const localAppData = env.LOCALAPPDATA || path6.join(homedir, "AppData", "Local");
4033
4033
  return {
4034
4034
  // Data/config/cache/log are invented by me as Windows isn't opinionated about this
4035
- data: path5.join(localAppData, name, "Data"),
4036
- config: path5.join(appData, name, "Config"),
4037
- cache: path5.join(localAppData, name, "Cache"),
4038
- log: path5.join(localAppData, name, "Log"),
4039
- temp: path5.join(tmpdir, name)
4035
+ data: path6.join(localAppData, name, "Data"),
4036
+ config: path6.join(appData, name, "Config"),
4037
+ cache: path6.join(localAppData, name, "Cache"),
4038
+ log: path6.join(localAppData, name, "Log"),
4039
+ temp: path6.join(tmpdir, name)
4040
4040
  };
4041
4041
  };
4042
4042
  var linux = (name) => {
4043
- const username = path5.basename(homedir);
4043
+ const username = path6.basename(homedir);
4044
4044
  return {
4045
- data: path5.join(env.XDG_DATA_HOME || path5.join(homedir, ".local", "share"), name),
4046
- config: path5.join(env.XDG_CONFIG_HOME || path5.join(homedir, ".config"), name),
4047
- cache: path5.join(env.XDG_CACHE_HOME || path5.join(homedir, ".cache"), name),
4045
+ data: path6.join(env.XDG_DATA_HOME || path6.join(homedir, ".local", "share"), name),
4046
+ config: path6.join(env.XDG_CONFIG_HOME || path6.join(homedir, ".config"), name),
4047
+ cache: path6.join(env.XDG_CACHE_HOME || path6.join(homedir, ".cache"), name),
4048
4048
  // https://wiki.debian.org/XDGBaseDirectorySpecification#state
4049
- log: path5.join(env.XDG_STATE_HOME || path5.join(homedir, ".local", "state"), name),
4050
- temp: path5.join(tmpdir, username, name)
4049
+ log: path6.join(env.XDG_STATE_HOME || path6.join(homedir, ".local", "state"), name),
4050
+ temp: path6.join(tmpdir, username, name)
4051
4051
  };
4052
4052
  };
4053
4053
  var envPaths = (name, options) => {
@@ -4115,11 +4115,11 @@ var require_util = __commonJS({
4115
4115
  return result;
4116
4116
  }
4117
4117
  exports2.emplace = emplace;
4118
- function getPropertyByPath(source, path5) {
4119
- if (typeof path5 === "string" && Object.prototype.hasOwnProperty.call(source, path5)) {
4120
- return source[path5];
4118
+ function getPropertyByPath(source, path6) {
4119
+ if (typeof path6 === "string" && Object.prototype.hasOwnProperty.call(source, path6)) {
4120
+ return source[path6];
4121
4121
  }
4122
- const parsedPath = typeof path5 === "string" ? path5.split(".") : path5;
4122
+ const parsedPath = typeof path6 === "string" ? path6.split(".") : path6;
4123
4123
  return parsedPath.reduce((previous, key) => {
4124
4124
  if (previous === void 0) {
4125
4125
  return previous;
@@ -4132,9 +4132,9 @@ var require_util = __commonJS({
4132
4132
  return Object.fromEntries(Object.entries(options).filter(([, value]) => value !== void 0));
4133
4133
  }
4134
4134
  exports2.removeUndefinedValuesFromObject = removeUndefinedValuesFromObject;
4135
- async function isDirectory(path5) {
4135
+ async function isDirectory(path6) {
4136
4136
  try {
4137
- const stat = await fs_1.promises.stat(path5);
4137
+ const stat = await fs_1.promises.stat(path6);
4138
4138
  return stat.isDirectory();
4139
4139
  } catch (e) {
4140
4140
  if (e.code === "ENOENT") {
@@ -4144,9 +4144,9 @@ var require_util = __commonJS({
4144
4144
  }
4145
4145
  }
4146
4146
  exports2.isDirectory = isDirectory;
4147
- function isDirectorySync(path5) {
4147
+ function isDirectorySync(path6) {
4148
4148
  try {
4149
- const stat = fs_1.default.statSync(path5);
4149
+ const stat = fs_1.default.statSync(path6);
4150
4150
  return stat.isDirectory();
4151
4151
  } catch (e) {
4152
4152
  if (e.code === "ENOENT") {
@@ -4244,7 +4244,7 @@ var require_ExplorerBase = __commonJS({
4244
4244
  const idx = importStack.indexOf(fullPath);
4245
4245
  if (idx !== -1) {
4246
4246
  throw new Error(`Circular import detected:
4247
- ${[...importStack, fullPath].map((path5, i) => `${i + 1}. ${path5}`).join("\n")} (same as ${idx + 1}.)`);
4247
+ ${[...importStack, fullPath].map((path6, i) => `${i + 1}. ${path6}`).join("\n")} (same as ${idx + 1}.)`);
4248
4248
  }
4249
4249
  }
4250
4250
  }
@@ -4429,9 +4429,9 @@ var require_Explorer = __commonJS({
4429
4429
  throw error;
4430
4430
  }
4431
4431
  }
4432
- async #fileExists(path5) {
4432
+ async #fileExists(path6) {
4433
4433
  try {
4434
- await promises_1.default.stat(path5);
4434
+ await promises_1.default.stat(path6);
4435
4435
  return true;
4436
4436
  } catch (e) {
4437
4437
  return false;
@@ -4587,9 +4587,9 @@ var require_ExplorerSync = __commonJS({
4587
4587
  throw error;
4588
4588
  }
4589
4589
  }
4590
- #fileExists(path5) {
4590
+ #fileExists(path6) {
4591
4591
  try {
4592
- fs_1.default.statSync(path5);
4592
+ fs_1.default.statSync(path6);
4593
4593
  return true;
4594
4594
  } catch (e) {
4595
4595
  return false;
@@ -4709,7 +4709,7 @@ var require_dist = __commonJS({
4709
4709
  };
4710
4710
  }
4711
4711
  function getResolvedSearchPlaces(moduleName, toolDefinedSearchPlaces, userConfiguredOptions) {
4712
- const userConfiguredSearchPlaces = userConfiguredOptions.searchPlaces?.map((path5) => path5.replace("{name}", moduleName));
4712
+ const userConfiguredSearchPlaces = userConfiguredOptions.searchPlaces?.map((path6) => path6.replace("{name}", moduleName));
4713
4713
  if (userConfiguredOptions.mergeSearchPlaces) {
4714
4714
  return [...userConfiguredSearchPlaces ?? [], ...toolDefinedSearchPlaces];
4715
4715
  }
@@ -4807,22 +4807,65 @@ var require_dist = __commonJS({
4807
4807
  });
4808
4808
 
4809
4809
  // scripts/auto-continue.ts
4810
- var fs3 = __toESM(require("node:fs"));
4811
- var path4 = __toESM(require("node:path"));
4810
+ var fs4 = __toESM(require("node:fs"));
4811
+ var path5 = __toESM(require("node:path"));
4812
+
4813
+ // src/activity-logger.ts
4814
+ var import_node_fs = __toESM(require("node:fs"));
4815
+ var import_node_path = __toESM(require("node:path"));
4816
+ function matchesFilter(hookName, message) {
4817
+ const filter = process.env.KETCHUP_LOG;
4818
+ if (!filter) {
4819
+ return true;
4820
+ }
4821
+ const patterns = filter.split(",").map((p) => p.trim());
4822
+ const includes = patterns.filter((p) => !p.startsWith("-"));
4823
+ const excludes = patterns.filter((p) => p.startsWith("-")).map((p) => p.slice(1));
4824
+ const searchText = `${hookName}: ${message}`;
4825
+ const excluded = excludes.some((pattern) => searchText.includes(pattern));
4826
+ if (excluded) {
4827
+ return false;
4828
+ }
4829
+ if (includes.length === 0 || includes.includes("*")) {
4830
+ return true;
4831
+ }
4832
+ return includes.some((pattern) => searchText.includes(pattern));
4833
+ }
4834
+ function activityLog(autoDir, sessionId, hookName, message) {
4835
+ if (!matchesFilter(hookName, message)) {
4836
+ return;
4837
+ }
4838
+ const logsDir = import_node_path.default.join(autoDir, "logs");
4839
+ if (!import_node_fs.default.existsSync(logsDir)) {
4840
+ import_node_fs.default.mkdirSync(logsDir, { recursive: true });
4841
+ }
4842
+ const logPath = import_node_path.default.join(logsDir, "activity.log");
4843
+ const now = /* @__PURE__ */ new Date();
4844
+ const month = String(now.getMonth() + 1).padStart(2, "0");
4845
+ const day = String(now.getDate()).padStart(2, "0");
4846
+ const hours = String(now.getHours()).padStart(2, "0");
4847
+ const minutes = String(now.getMinutes()).padStart(2, "0");
4848
+ const seconds = String(now.getSeconds()).padStart(2, "0");
4849
+ const timestamp = `${month}-${day} ${hours}:${minutes}:${seconds}`;
4850
+ const shortSessionId = sessionId.slice(-8);
4851
+ const entry = `${timestamp} [${shortSessionId}] ${hookName}: ${message}
4852
+ `;
4853
+ import_node_fs.default.appendFileSync(logPath, entry);
4854
+ }
4812
4855
 
4813
4856
  // src/hook-logger.ts
4814
- var fs = __toESM(require("node:fs"));
4815
- var path = __toESM(require("node:path"));
4857
+ var fs2 = __toESM(require("node:fs"));
4858
+ var path2 = __toESM(require("node:path"));
4816
4859
  function sanitizeForFilename(hookName) {
4817
4860
  return hookName.replace(/[^a-zA-Z0-9-]/g, "-").toLowerCase();
4818
4861
  }
4819
4862
  function writeHookLog(autoDir, entry) {
4820
- const logsDir = path.join(autoDir, "logs", "hooks");
4821
- if (!fs.existsSync(logsDir)) {
4822
- fs.mkdirSync(logsDir, { recursive: true });
4863
+ const logsDir = path2.join(autoDir, "logs", "hooks");
4864
+ if (!fs2.existsSync(logsDir)) {
4865
+ fs2.mkdirSync(logsDir, { recursive: true });
4823
4866
  }
4824
4867
  const sanitizedName = sanitizeForFilename(entry.hookName);
4825
- const logPath = path.join(logsDir, `${sanitizedName}.log`);
4868
+ const logPath = path2.join(logsDir, `${sanitizedName}.log`);
4826
4869
  const lines = [];
4827
4870
  lines.push(`=== ${entry.hookName} hook log ===`);
4828
4871
  lines.push(`Timestamp: ${entry.timestamp}`);
@@ -4862,13 +4905,13 @@ function writeHookLog(autoDir, entry) {
4862
4905
  lines.push("--- Output ---");
4863
4906
  lines.push(JSON.stringify(entry.output, null, 2));
4864
4907
  lines.push("");
4865
- fs.appendFileSync(logPath, `${lines.join("\n")}
4908
+ fs2.appendFileSync(logPath, `${lines.join("\n")}
4866
4909
  `);
4867
4910
  }
4868
4911
 
4869
4912
  // src/hook-state.ts
4870
- var fs2 = __toESM(require("node:fs"));
4871
- var path2 = __toESM(require("node:path"));
4913
+ var fs3 = __toESM(require("node:fs"));
4914
+ var path3 = __toESM(require("node:path"));
4872
4915
  var DEFAULT_HOOK_STATE = {
4873
4916
  autoContinue: {
4874
4917
  mode: "smart",
@@ -4896,18 +4939,18 @@ var DEFAULT_HOOK_STATE = {
4896
4939
  updatedBy: "default"
4897
4940
  };
4898
4941
  function createHookState(autoDir) {
4899
- if (!fs2.existsSync(autoDir)) {
4900
- fs2.mkdirSync(autoDir, { recursive: true });
4942
+ if (!fs3.existsSync(autoDir)) {
4943
+ fs3.mkdirSync(autoDir, { recursive: true });
4901
4944
  }
4902
- const stateFile = path2.join(autoDir, ".claude.hooks.json");
4945
+ const stateFile = path3.join(autoDir, ".claude.hooks.json");
4903
4946
  function read() {
4904
- if (!fs2.existsSync(stateFile)) {
4947
+ if (!fs3.existsSync(stateFile)) {
4905
4948
  const state = { ...DEFAULT_HOOK_STATE, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), updatedBy: "init" };
4906
- fs2.writeFileSync(stateFile, `${JSON.stringify(state, null, 2)}
4949
+ fs3.writeFileSync(stateFile, `${JSON.stringify(state, null, 2)}
4907
4950
  `);
4908
4951
  return state;
4909
4952
  }
4910
- const content = fs2.readFileSync(stateFile, "utf-8");
4953
+ const content = fs3.readFileSync(stateFile, "utf-8");
4911
4954
  const partial = JSON.parse(content);
4912
4955
  return {
4913
4956
  autoContinue: { ...DEFAULT_HOOK_STATE.autoContinue, ...partial.autoContinue },
@@ -4921,7 +4964,7 @@ function createHookState(autoDir) {
4921
4964
  }
4922
4965
  function write(state) {
4923
4966
  state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
4924
- fs2.writeFileSync(stateFile, `${JSON.stringify(state, null, 2)}
4967
+ fs3.writeFileSync(stateFile, `${JSON.stringify(state, null, 2)}
4925
4968
  `);
4926
4969
  }
4927
4970
  function update(updates, updatedBy) {
@@ -4979,7 +5022,7 @@ function handleStop(autoDir, input2) {
4979
5022
  }
4980
5023
 
4981
5024
  // src/path-resolver.ts
4982
- var path3 = __toESM(require("node:path"));
5025
+ var path4 = __toESM(require("node:path"));
4983
5026
 
4984
5027
  // src/config-loader.ts
4985
5028
  var import_cosmiconfig = __toESM(require_dist());
@@ -4992,22 +5035,22 @@ async function loadConfig(searchFrom) {
4992
5035
 
4993
5036
  // src/path-resolver.ts
4994
5037
  async function resolvePaths(claudeDir2) {
4995
- const projectRoot = path3.dirname(claudeDir2);
5038
+ const projectRoot = path4.dirname(claudeDir2);
4996
5039
  const config = await loadConfig(projectRoot);
4997
5040
  const autoDirName = config.autoDir ?? DEFAULT_AUTO_DIR;
4998
- const autoDir = path3.join(projectRoot, autoDirName);
5041
+ const autoDir = path4.join(projectRoot, autoDirName);
4999
5042
  return {
5000
5043
  projectRoot,
5001
5044
  claudeDir: claudeDir2,
5002
5045
  autoDir,
5003
- remindersDir: path3.join(autoDir, "reminders"),
5004
- validatorsDir: path3.join(autoDir, "validators")
5046
+ remindersDir: path4.join(autoDir, "reminders"),
5047
+ validatorsDir: path4.join(autoDir, "validators")
5005
5048
  };
5006
5049
  }
5007
5050
 
5008
5051
  // scripts/auto-continue.ts
5009
- var claudeDir = path4.resolve(process.cwd(), ".claude");
5010
- var stdin = fs3.readFileSync(0, "utf8").trim();
5052
+ var claudeDir = path5.resolve(process.cwd(), ".claude");
5053
+ var stdin = fs4.readFileSync(0, "utf8").trim();
5011
5054
  if (!stdin) {
5012
5055
  process.exit(0);
5013
5056
  }
@@ -5015,19 +5058,33 @@ var input = JSON.parse(stdin);
5015
5058
  var startTime = Date.now();
5016
5059
  (async () => {
5017
5060
  const { autoDir } = await resolvePaths(claudeDir);
5018
- const result = handleStop(autoDir, input);
5019
- const output = result.decision === "block" ? { stopReason: result.reason, forceResult: { behaviour: "block" } } : null;
5020
- writeHookLog(autoDir, {
5021
- hookName: "auto-continue",
5022
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5023
- input,
5024
- output: output ?? { decision: result.decision, reason: result.reason },
5025
- durationMs: Date.now() - startTime
5026
- });
5027
- if (output) {
5028
- console.log(JSON.stringify(output));
5061
+ try {
5062
+ const result = handleStop(autoDir, input);
5063
+ const output = result.decision === "block" ? { decision: "block", reason: result.reason } : null;
5064
+ writeHookLog(autoDir, {
5065
+ hookName: "auto-continue",
5066
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5067
+ input,
5068
+ output: output ?? { decision: result.decision, reason: result.reason },
5069
+ durationMs: Date.now() - startTime
5070
+ });
5071
+ if (output) {
5072
+ console.log(JSON.stringify(output));
5073
+ }
5074
+ process.exit(0);
5075
+ } catch (err) {
5076
+ activityLog(autoDir, input.session_id ?? "unknown", "auto-continue", `error: ${String(err)}`);
5077
+ writeHookLog(autoDir, {
5078
+ hookName: "auto-continue",
5079
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
5080
+ input,
5081
+ output: null,
5082
+ error: String(err),
5083
+ durationMs: Date.now() - startTime
5084
+ });
5085
+ console.error("auto-continue hook failed:", err);
5086
+ process.exit(1);
5029
5087
  }
5030
- process.exit(0);
5031
5088
  })();
5032
5089
  /*! Bundled license information:
5033
5090
 
@@ -11046,6 +11046,49 @@ var require_gray_matter = __commonJS({
11046
11046
  var fs8 = __toESM(require("node:fs"));
11047
11047
  var path9 = __toESM(require("node:path"));
11048
11048
 
11049
+ // src/activity-logger.ts
11050
+ var import_node_fs = __toESM(require("node:fs"));
11051
+ var import_node_path = __toESM(require("node:path"));
11052
+ function matchesFilter(hookName, message) {
11053
+ const filter = process.env.KETCHUP_LOG;
11054
+ if (!filter) {
11055
+ return true;
11056
+ }
11057
+ const patterns = filter.split(",").map((p) => p.trim());
11058
+ const includes = patterns.filter((p) => !p.startsWith("-"));
11059
+ const excludes = patterns.filter((p) => p.startsWith("-")).map((p) => p.slice(1));
11060
+ const searchText = `${hookName}: ${message}`;
11061
+ const excluded = excludes.some((pattern) => searchText.includes(pattern));
11062
+ if (excluded) {
11063
+ return false;
11064
+ }
11065
+ if (includes.length === 0 || includes.includes("*")) {
11066
+ return true;
11067
+ }
11068
+ return includes.some((pattern) => searchText.includes(pattern));
11069
+ }
11070
+ function activityLog(autoDir, sessionId, hookName, message) {
11071
+ if (!matchesFilter(hookName, message)) {
11072
+ return;
11073
+ }
11074
+ const logsDir = import_node_path.default.join(autoDir, "logs");
11075
+ if (!import_node_fs.default.existsSync(logsDir)) {
11076
+ import_node_fs.default.mkdirSync(logsDir, { recursive: true });
11077
+ }
11078
+ const logPath = import_node_path.default.join(logsDir, "activity.log");
11079
+ const now = /* @__PURE__ */ new Date();
11080
+ const month = String(now.getMonth() + 1).padStart(2, "0");
11081
+ const day = String(now.getDate()).padStart(2, "0");
11082
+ const hours = String(now.getHours()).padStart(2, "0");
11083
+ const minutes = String(now.getMinutes()).padStart(2, "0");
11084
+ const seconds = String(now.getSeconds()).padStart(2, "0");
11085
+ const timestamp = `${month}-${day} ${hours}:${minutes}:${seconds}`;
11086
+ const shortSessionId = sessionId.slice(-8);
11087
+ const entry = `${timestamp} [${shortSessionId}] ${hookName}: ${message}
11088
+ `;
11089
+ import_node_fs.default.appendFileSync(logPath, entry);
11090
+ }
11091
+
11049
11092
  // src/commit-validator.ts
11050
11093
  var import_node_child_process = require("node:child_process");
11051
11094
  function spawnAsync(cmd, args, _options) {
@@ -11198,7 +11241,7 @@ async function validateCommit(validators, context, executor = spawnAsync, onLog,
11198
11241
  onLog?.("spawn", `batch-${chunkIndex}`, `validators: ${names.join(", ")}`);
11199
11242
  try {
11200
11243
  const prompt = buildBatchedPrompt(chunk, context);
11201
- const args = ["-p", "--no-session-persistence", prompt, "--output-format", "json"];
11244
+ const args = ["-p", "--no-session-persistence", "--agent", "validator", prompt, "--output-format", "json"];
11202
11245
  const opts = { encoding: "utf8" };
11203
11246
  const spawnResult = await executor("claude", args, opts);
11204
11247
  const batchResults2 = parseBatchedOutput(spawnResult.stdout, names);
@@ -11240,18 +11283,18 @@ function parseHookInput(json) {
11240
11283
  }
11241
11284
 
11242
11285
  // src/hook-logger.ts
11243
- var fs = __toESM(require("node:fs"));
11244
- var path = __toESM(require("node:path"));
11286
+ var fs2 = __toESM(require("node:fs"));
11287
+ var path2 = __toESM(require("node:path"));
11245
11288
  function sanitizeForFilename(hookName) {
11246
11289
  return hookName.replace(/[^a-zA-Z0-9-]/g, "-").toLowerCase();
11247
11290
  }
11248
11291
  function writeHookLog(autoDir, entry) {
11249
- const logsDir = path.join(autoDir, "logs", "hooks");
11250
- if (!fs.existsSync(logsDir)) {
11251
- fs.mkdirSync(logsDir, { recursive: true });
11292
+ const logsDir = path2.join(autoDir, "logs", "hooks");
11293
+ if (!fs2.existsSync(logsDir)) {
11294
+ fs2.mkdirSync(logsDir, { recursive: true });
11252
11295
  }
11253
11296
  const sanitizedName = sanitizeForFilename(entry.hookName);
11254
- const logPath = path.join(logsDir, `${sanitizedName}.log`);
11297
+ const logPath = path2.join(logsDir, `${sanitizedName}.log`);
11255
11298
  const lines = [];
11256
11299
  lines.push(`=== ${entry.hookName} hook log ===`);
11257
11300
  lines.push(`Timestamp: ${entry.timestamp}`);
@@ -11291,53 +11334,10 @@ function writeHookLog(autoDir, entry) {
11291
11334
  lines.push("--- Output ---");
11292
11335
  lines.push(JSON.stringify(entry.output, null, 2));
11293
11336
  lines.push("");
11294
- fs.appendFileSync(logPath, `${lines.join("\n")}
11337
+ fs2.appendFileSync(logPath, `${lines.join("\n")}
11295
11338
  `);
11296
11339
  }
11297
11340
 
11298
- // src/activity-logger.ts
11299
- var import_node_fs = __toESM(require("node:fs"));
11300
- var import_node_path = __toESM(require("node:path"));
11301
- function matchesFilter(hookName, message) {
11302
- const filter = process.env.KETCHUP_LOG;
11303
- if (!filter) {
11304
- return true;
11305
- }
11306
- const patterns = filter.split(",").map((p) => p.trim());
11307
- const includes = patterns.filter((p) => !p.startsWith("-"));
11308
- const excludes = patterns.filter((p) => p.startsWith("-")).map((p) => p.slice(1));
11309
- const searchText = `${hookName}: ${message}`;
11310
- const excluded = excludes.some((pattern) => searchText.includes(pattern));
11311
- if (excluded) {
11312
- return false;
11313
- }
11314
- if (includes.length === 0 || includes.includes("*")) {
11315
- return true;
11316
- }
11317
- return includes.some((pattern) => searchText.includes(pattern));
11318
- }
11319
- function activityLog(autoDir, sessionId, hookName, message) {
11320
- if (!matchesFilter(hookName, message)) {
11321
- return;
11322
- }
11323
- const logsDir = import_node_path.default.join(autoDir, "logs");
11324
- if (!import_node_fs.default.existsSync(logsDir)) {
11325
- import_node_fs.default.mkdirSync(logsDir, { recursive: true });
11326
- }
11327
- const logPath = import_node_path.default.join(logsDir, "activity.log");
11328
- const now = /* @__PURE__ */ new Date();
11329
- const month = String(now.getMonth() + 1).padStart(2, "0");
11330
- const day = String(now.getDate()).padStart(2, "0");
11331
- const hours = String(now.getHours()).padStart(2, "0");
11332
- const minutes = String(now.getMinutes()).padStart(2, "0");
11333
- const seconds = String(now.getSeconds()).padStart(2, "0");
11334
- const timestamp = `${month}-${day} ${hours}:${minutes}:${seconds}`;
11335
- const shortSessionId = sessionId.slice(-8);
11336
- const entry = `${timestamp} [${shortSessionId}] ${hookName}: ${message}
11337
- `;
11338
- import_node_fs.default.appendFileSync(logPath, entry);
11339
- }
11340
-
11341
11341
  // src/debug-logger.ts
11342
11342
  var import_node_fs2 = __toESM(require("node:fs"));
11343
11343
  var import_node_path2 = __toESM(require("node:path"));
@@ -11578,7 +11578,8 @@ async function handlePreToolUse(claudeDir2, sessionId, toolInput, options2 = {})
11578
11578
  const paths = await resolvePaths(claudeDir2);
11579
11579
  const command = toolInput.command;
11580
11580
  if (command && isCommitCommand(command)) {
11581
- return handleCommitValidation(claudeDir2, sessionId, command, options2, paths.autoDir);
11581
+ const gitCwd = options2.cwd ?? process.cwd();
11582
+ return handleCommitValidation(claudeDir2, sessionId, command, options2, paths.autoDir, gitCwd);
11582
11583
  }
11583
11584
  const patterns = loadDenyPatterns(claudeDir2);
11584
11585
  const filePath = toolInput.file_path;
@@ -11614,7 +11615,7 @@ async function handlePreToolUse(claudeDir2, sessionId, toolInput, options2 = {})
11614
11615
  }
11615
11616
  };
11616
11617
  }
11617
- async function handleCommitValidation(claudeDir2, sessionId, command, options2, autoDir) {
11618
+ async function handleCommitValidation(claudeDir2, sessionId, command, options2, autoDir, gitCwd) {
11618
11619
  const paths = await resolvePaths(claudeDir2);
11619
11620
  const allValidators = loadValidators([paths.validatorsDir]);
11620
11621
  const validators = allValidators.filter((v) => v.name !== "appeal-system");
@@ -11627,7 +11628,7 @@ async function handleCommitValidation(claudeDir2, sessionId, command, options2,
11627
11628
  }
11628
11629
  };
11629
11630
  }
11630
- const context = getCommitContext(process.cwd(), command);
11631
+ const context = getCommitContext(gitCwd, command);
11631
11632
  const state = createHookState(autoDir).read();
11632
11633
  const onLog = (event, name, detail) => {
11633
11634
  activityLog(autoDir, sessionId, "pre-tool-use", `validator ${event}: ${name} \u2192 ${detail}`);
@@ -11665,7 +11666,7 @@ var startTime = Date.now();
11665
11666
  try {
11666
11667
  const toolInput = input.tool_input || {};
11667
11668
  const command = toolInput.command;
11668
- const result = await handlePreToolUse(claudeDir, input.session_id, toolInput);
11669
+ const result = await handlePreToolUse(claudeDir, input.session_id, toolInput, { cwd: input.cwd });
11669
11670
  if (command && isCommitCommand(command)) {
11670
11671
  writeHookLog(autoDir, {
11671
11672
  hookName: "pre-tool-use",
@@ -11678,6 +11679,7 @@ var startTime = Date.now();
11678
11679
  console.log(JSON.stringify(result));
11679
11680
  process.exit(0);
11680
11681
  } catch (err) {
11682
+ activityLog(autoDir, input.session_id, "pre-tool-use", `error: ${String(err)}`);
11681
11683
  writeHookLog(autoDir, {
11682
11684
  hookName: "pre-tool-use",
11683
11685
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),