claude-yes 1.34.0 → 1.35.1

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/dist/cli.js CHANGED
@@ -20732,24 +20732,81 @@ var init_fifo = __esm(() => {
20732
20732
  });
20733
20733
 
20734
20734
  // ts/pidStore.ts
20735
- import Datastore from "@seald-io/nedb";
20736
20735
  import { mkdir as mkdir3 } from "fs/promises";
20737
20736
  import path9 from "path";
20738
20737
 
20738
+ class SqliteAdapter {
20739
+ db;
20740
+ async init(dbPath) {
20741
+ if (typeof globalThis.Bun !== "undefined") {
20742
+ try {
20743
+ const { Database } = await import("bun:sqlite");
20744
+ this.db = new Database(dbPath);
20745
+ } catch (error) {
20746
+ logger.warn("[pidStore] bun:sqlite not available, falling back to better-sqlite3");
20747
+ const Database = (await import("better-sqlite3")).default;
20748
+ this.db = new Database(dbPath);
20749
+ }
20750
+ } else {
20751
+ const Database = (await import("better-sqlite3")).default;
20752
+ this.db = new Database(dbPath);
20753
+ }
20754
+ }
20755
+ query(sql, params = []) {
20756
+ if (typeof this.db.prepare === "function") {
20757
+ return this.db.prepare(sql).all(params);
20758
+ } else {
20759
+ return this.db.query(sql).all(params);
20760
+ }
20761
+ }
20762
+ run(sql, params = []) {
20763
+ if (typeof this.db.prepare === "function") {
20764
+ return this.db.prepare(sql).run(params);
20765
+ } else {
20766
+ this.db.run(sql, params);
20767
+ return {};
20768
+ }
20769
+ }
20770
+ close() {
20771
+ if (this.db.close) {
20772
+ this.db.close();
20773
+ }
20774
+ }
20775
+ }
20776
+
20739
20777
  class PidStore {
20740
20778
  db;
20741
20779
  baseDir;
20780
+ dbPath;
20742
20781
  constructor(workingDir) {
20743
20782
  this.baseDir = path9.resolve(workingDir, ".agent-yes");
20783
+ this.dbPath = path9.join(this.baseDir, "pid.sqlite");
20744
20784
  }
20745
20785
  async init() {
20746
20786
  await mkdir3(path9.join(this.baseDir, "logs"), { recursive: true });
20747
20787
  await mkdir3(path9.join(this.baseDir, "fifo"), { recursive: true });
20748
- this.db = new Datastore({
20749
- filename: path9.join(this.baseDir, "pid.jsonl"),
20750
- autoload: true
20751
- });
20752
- await this.db.loadDatabaseAsync();
20788
+ this.db = new SqliteAdapter;
20789
+ await this.db.init(this.dbPath);
20790
+ this.db.run("PRAGMA journal_mode=WAL");
20791
+ this.db.run("PRAGMA synchronous=NORMAL");
20792
+ this.db.run("PRAGMA cache_size=1000");
20793
+ this.db.run("PRAGMA temp_store=memory");
20794
+ this.db.run(`
20795
+ CREATE TABLE IF NOT EXISTS pid_records (
20796
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
20797
+ pid INTEGER NOT NULL UNIQUE,
20798
+ cli TEXT NOT NULL,
20799
+ args TEXT NOT NULL,
20800
+ prompt TEXT,
20801
+ logFile TEXT NOT NULL,
20802
+ fifoFile TEXT NOT NULL,
20803
+ status TEXT NOT NULL DEFAULT 'active',
20804
+ exitReason TEXT NOT NULL DEFAULT '',
20805
+ exitCode INTEGER,
20806
+ startedAt INTEGER NOT NULL,
20807
+ updatedAt INTEGER NOT NULL
20808
+ )
20809
+ `);
20753
20810
  await this.cleanStaleRecords();
20754
20811
  }
20755
20812
  async registerProcess({
@@ -20759,29 +20816,43 @@ class PidStore {
20759
20816
  prompt
20760
20817
  }) {
20761
20818
  const now = Date.now();
20762
- const record = {
20763
- pid,
20764
- cli,
20765
- args,
20766
- prompt,
20767
- logFile: this.getLogPath(pid),
20768
- fifoFile: this.getFifoPath(pid),
20769
- status: "active",
20770
- exitReason: "",
20771
- startedAt: now,
20772
- updatedAt: now
20773
- };
20774
- await this.db.insertAsync(record);
20819
+ const argsJson = JSON.stringify(args);
20820
+ const logFile = this.getLogPath(pid);
20821
+ const fifoFile = this.getFifoPath(pid);
20822
+ try {
20823
+ this.db.run(`
20824
+ INSERT INTO pid_records (pid, cli, args, prompt, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
20825
+ VALUES (?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
20826
+ `, [pid, cli, argsJson, prompt, logFile, fifoFile, now, now]);
20827
+ } catch (error) {
20828
+ if (error.code === "SQLITE_CONSTRAINT_UNIQUE") {
20829
+ this.db.run(`
20830
+ UPDATE pid_records
20831
+ SET cli = ?, args = ?, prompt = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
20832
+ WHERE pid = ?
20833
+ `, [cli, argsJson, prompt, logFile, fifoFile, now, now, pid]);
20834
+ } else {
20835
+ throw error;
20836
+ }
20837
+ }
20838
+ const result = this.db.query("SELECT * FROM pid_records WHERE pid = ?", [pid])[0];
20839
+ if (!result) {
20840
+ const allRecords = this.db.query("SELECT * FROM pid_records");
20841
+ logger.error(`[pidStore] Failed to find record for PID ${pid}. All records:`, allRecords);
20842
+ throw new Error(`Failed to register process ${pid}`);
20843
+ }
20775
20844
  logger.debug(`[pidStore] Registered process ${pid}`);
20776
- return record;
20845
+ return result;
20777
20846
  }
20778
20847
  async updateStatus(pid, status, extra) {
20779
- const update2 = {
20780
- status,
20781
- updatedAt: Date.now(),
20782
- ...extra
20783
- };
20784
- await this.db.updateAsync({ pid }, { $set: update2 }, {});
20848
+ const updatedAt = Date.now();
20849
+ const exitReason = extra?.exitReason || "";
20850
+ const exitCode = extra?.exitCode;
20851
+ if (exitCode !== undefined) {
20852
+ this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, exitCode = ?, updatedAt = ? WHERE pid = ?", [status, exitReason, exitCode, updatedAt, pid]);
20853
+ } else {
20854
+ this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, updatedAt = ? WHERE pid = ?", [status, exitReason, updatedAt, pid]);
20855
+ }
20785
20856
  logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
20786
20857
  }
20787
20858
  getLogPath(pid) {
@@ -20791,25 +20862,23 @@ class PidStore {
20791
20862
  return path9.resolve(this.baseDir, "fifo", `${pid}.stdin`);
20792
20863
  }
20793
20864
  async cleanStaleRecords() {
20794
- const activeRecords = await this.db.findAsync({
20795
- status: { $ne: "exited" }
20796
- });
20865
+ const activeRecords = this.db.query("SELECT * FROM pid_records WHERE status != 'exited'");
20797
20866
  for (const record of activeRecords) {
20798
20867
  if (!this.isProcessAlive(record.pid)) {
20799
- await this.db.updateAsync({ pid: record.pid }, {
20800
- $set: {
20801
- status: "exited",
20802
- exitReason: "stale-cleanup",
20803
- updatedAt: Date.now()
20804
- }
20805
- }, {});
20868
+ this.db.run("UPDATE pid_records SET status = 'exited', exitReason = 'stale-cleanup', updatedAt = ? WHERE pid = ?", [Date.now(), record.pid]);
20806
20869
  logger.debug(`[pidStore] Cleaned stale record for PID ${record.pid}`);
20807
20870
  }
20808
20871
  }
20809
20872
  }
20810
20873
  async close() {
20811
- await this.db.compactDatafileAsync();
20812
- logger.debug("[pidStore] Database compacted and closed");
20874
+ try {
20875
+ this.db.run("PRAGMA optimize");
20876
+ this.db.run("VACUUM");
20877
+ } catch (error) {
20878
+ logger.warn("[pidStore] Failed to optimize database:", error);
20879
+ }
20880
+ this.db.close();
20881
+ logger.debug("[pidStore] Database optimized and closed");
20813
20882
  }
20814
20883
  isProcessAlive(pid) {
20815
20884
  try {
@@ -20822,10 +20891,9 @@ class PidStore {
20822
20891
  static async findActiveFifo(workingDir) {
20823
20892
  const store = new PidStore(workingDir);
20824
20893
  await store.init();
20825
- const records = await store.db.findAsync({ status: { $ne: "exited" } });
20894
+ const records = store.db.query("SELECT * FROM pid_records WHERE status != 'exited' ORDER BY startedAt DESC LIMIT 1");
20826
20895
  await store.close();
20827
- const sorted = records.sort((a2, b) => b.startedAt - a2.startedAt);
20828
- return sorted[0]?.fifoFile ?? null;
20896
+ return records[0]?.fifoFile ?? null;
20829
20897
  }
20830
20898
  }
20831
20899
  var init_pidStore = __esm(() => {
@@ -20959,7 +21027,10 @@ function getDefaultConfig() {
20959
21027
  },
20960
21028
  amp: {
20961
21029
  help: "",
20962
- install: "npm i -g @sourcegraph/amp"
21030
+ install: "npm i -g @sourcegraph/amp",
21031
+ enter: [
21032
+ /^.{0,4} Approve /
21033
+ ]
20963
21034
  }
20964
21035
  }
20965
21036
  });
@@ -27688,7 +27759,7 @@ var package_default = {
27688
27759
  registry: "https://registry.npmjs.org/"
27689
27760
  },
27690
27761
  scripts: {
27691
- build: "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external=@seald-io/nedb --external=@snomiao/bun-pty --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
27762
+ build: "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external=better-sqlite3 --external=@snomiao/bun-pty --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
27692
27763
  postbuild: "bun ./ts/postbuild.ts",
27693
27764
  demo: "bun run build && bun link && claude-yes -- demo",
27694
27765
  dev: "bun ts/index.ts",
@@ -27700,9 +27771,9 @@ var package_default = {
27700
27771
  test: "bun test --coverage"
27701
27772
  },
27702
27773
  dependencies: {
27703
- "@seald-io/nedb": "^4.0.4",
27704
27774
  "@snomiao/bun-pty": "^0.3.4",
27705
27775
  "bun-pty": "^0.4.8",
27776
+ "better-sqlite3": "^12.1.0",
27706
27777
  "from-node-stream": "^0.1.2"
27707
27778
  },
27708
27779
  devDependencies: {
@@ -27714,6 +27785,7 @@ var package_default = {
27714
27785
  "@types/bun": "^1.3.6",
27715
27786
  "@types/jest": "^30.0.0",
27716
27787
  "@types/ms": "^2.1.0",
27788
+ "@types/better-sqlite3": "^7.6.12",
27717
27789
  "@types/node": "^25.0.10",
27718
27790
  "@types/yargs": "^17.0.35",
27719
27791
  "cpu-wait": "^0.0.10",
@@ -27775,7 +27847,7 @@ var package_default = {
27775
27847
 
27776
27848
  // ts/parseCliArgs.ts
27777
27849
  function parseCliArgs(argv) {
27778
- const cliName = argv[1]?.split(/[/\\]/).at(-1)?.replace(/(\.[jt]s)?$/, "").replace(/^(cli|agent)(-yes$)?/, "").replace(/-yes$/, "") || undefined;
27850
+ const cliName = argv[1]?.split(/[/\\]/).at(-1)?.replace(/(\.[jt]s)?$/, "").replace(/^(cli|agent)(-yes$)?/, "").replace(/^ay$/, "").replace(/-yes$/, "") || undefined;
27779
27851
  const parsedArgv = yargs_default(hideBin(argv)).usage("Usage: $0 [cli] [agent-yes args] [agent-cli args] [--] [prompts...]").example("$0 claude --idle=30s -- solve all todos in my codebase, commit one by one", "Run Claude with a 30 seconds idle timeout, and the prompt is everything after `--`").option("robust", {
27780
27852
  type: "boolean",
27781
27853
  default: true,
@@ -27889,7 +27961,7 @@ function parseCliArgs(argv) {
27889
27961
  return {
27890
27962
  cwd: process.cwd(),
27891
27963
  env: process.env,
27892
- cli: cliName || parsedArgv.cli || parsedArgv._[0]?.toString()?.replace?.(/-yes$/, ""),
27964
+ cli: cliName || parsedArgv.cli || (dashIndex !== 0 ? parsedArgv._[0]?.toString()?.replace?.(/-yes$/, "") : undefined),
27893
27965
  cliArgs: cliArgsForSpawn,
27894
27966
  prompt: [parsedArgv.prompt, dashPrompt].filter(Boolean).join(" ") || undefined,
27895
27967
  install: parsedArgv.install,
@@ -27924,24 +27996,81 @@ var logger2 = import_winston3.default.createLogger({
27924
27996
 
27925
27997
  // ts/pidStore.ts
27926
27998
  init_logger();
27927
- import Datastore2 from "@seald-io/nedb";
27928
27999
  import { mkdir as mkdir6 } from "fs/promises";
27929
28000
  import path12 from "path";
27930
28001
 
28002
+ class SqliteAdapter2 {
28003
+ db;
28004
+ async init(dbPath) {
28005
+ if (typeof globalThis.Bun !== "undefined") {
28006
+ try {
28007
+ const { Database } = await import("bun:sqlite");
28008
+ this.db = new Database(dbPath);
28009
+ } catch (error) {
28010
+ logger.warn("[pidStore] bun:sqlite not available, falling back to better-sqlite3");
28011
+ const Database = (await import("better-sqlite3")).default;
28012
+ this.db = new Database(dbPath);
28013
+ }
28014
+ } else {
28015
+ const Database = (await import("better-sqlite3")).default;
28016
+ this.db = new Database(dbPath);
28017
+ }
28018
+ }
28019
+ query(sql, params = []) {
28020
+ if (typeof this.db.prepare === "function") {
28021
+ return this.db.prepare(sql).all(params);
28022
+ } else {
28023
+ return this.db.query(sql).all(params);
28024
+ }
28025
+ }
28026
+ run(sql, params = []) {
28027
+ if (typeof this.db.prepare === "function") {
28028
+ return this.db.prepare(sql).run(params);
28029
+ } else {
28030
+ this.db.run(sql, params);
28031
+ return {};
28032
+ }
28033
+ }
28034
+ close() {
28035
+ if (this.db.close) {
28036
+ this.db.close();
28037
+ }
28038
+ }
28039
+ }
28040
+
27931
28041
  class PidStore2 {
27932
28042
  db;
27933
28043
  baseDir;
28044
+ dbPath;
27934
28045
  constructor(workingDir) {
27935
28046
  this.baseDir = path12.resolve(workingDir, ".agent-yes");
28047
+ this.dbPath = path12.join(this.baseDir, "pid.sqlite");
27936
28048
  }
27937
28049
  async init() {
27938
28050
  await mkdir6(path12.join(this.baseDir, "logs"), { recursive: true });
27939
28051
  await mkdir6(path12.join(this.baseDir, "fifo"), { recursive: true });
27940
- this.db = new Datastore2({
27941
- filename: path12.join(this.baseDir, "pid.jsonl"),
27942
- autoload: true
27943
- });
27944
- await this.db.loadDatabaseAsync();
28052
+ this.db = new SqliteAdapter2;
28053
+ await this.db.init(this.dbPath);
28054
+ this.db.run("PRAGMA journal_mode=WAL");
28055
+ this.db.run("PRAGMA synchronous=NORMAL");
28056
+ this.db.run("PRAGMA cache_size=1000");
28057
+ this.db.run("PRAGMA temp_store=memory");
28058
+ this.db.run(`
28059
+ CREATE TABLE IF NOT EXISTS pid_records (
28060
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
28061
+ pid INTEGER NOT NULL UNIQUE,
28062
+ cli TEXT NOT NULL,
28063
+ args TEXT NOT NULL,
28064
+ prompt TEXT,
28065
+ logFile TEXT NOT NULL,
28066
+ fifoFile TEXT NOT NULL,
28067
+ status TEXT NOT NULL DEFAULT 'active',
28068
+ exitReason TEXT NOT NULL DEFAULT '',
28069
+ exitCode INTEGER,
28070
+ startedAt INTEGER NOT NULL,
28071
+ updatedAt INTEGER NOT NULL
28072
+ )
28073
+ `);
27945
28074
  await this.cleanStaleRecords();
27946
28075
  }
27947
28076
  async registerProcess({
@@ -27951,29 +28080,43 @@ class PidStore2 {
27951
28080
  prompt
27952
28081
  }) {
27953
28082
  const now = Date.now();
27954
- const record = {
27955
- pid,
27956
- cli,
27957
- args,
27958
- prompt,
27959
- logFile: this.getLogPath(pid),
27960
- fifoFile: this.getFifoPath(pid),
27961
- status: "active",
27962
- exitReason: "",
27963
- startedAt: now,
27964
- updatedAt: now
27965
- };
27966
- await this.db.insertAsync(record);
28083
+ const argsJson = JSON.stringify(args);
28084
+ const logFile = this.getLogPath(pid);
28085
+ const fifoFile = this.getFifoPath(pid);
28086
+ try {
28087
+ this.db.run(`
28088
+ INSERT INTO pid_records (pid, cli, args, prompt, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
28089
+ VALUES (?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
28090
+ `, [pid, cli, argsJson, prompt, logFile, fifoFile, now, now]);
28091
+ } catch (error) {
28092
+ if (error.code === "SQLITE_CONSTRAINT_UNIQUE") {
28093
+ this.db.run(`
28094
+ UPDATE pid_records
28095
+ SET cli = ?, args = ?, prompt = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
28096
+ WHERE pid = ?
28097
+ `, [cli, argsJson, prompt, logFile, fifoFile, now, now, pid]);
28098
+ } else {
28099
+ throw error;
28100
+ }
28101
+ }
28102
+ const result = this.db.query("SELECT * FROM pid_records WHERE pid = ?", [pid])[0];
28103
+ if (!result) {
28104
+ const allRecords = this.db.query("SELECT * FROM pid_records");
28105
+ logger.error(`[pidStore] Failed to find record for PID ${pid}. All records:`, allRecords);
28106
+ throw new Error(`Failed to register process ${pid}`);
28107
+ }
27967
28108
  logger.debug(`[pidStore] Registered process ${pid}`);
27968
- return record;
28109
+ return result;
27969
28110
  }
27970
28111
  async updateStatus(pid, status, extra) {
27971
- const update2 = {
27972
- status,
27973
- updatedAt: Date.now(),
27974
- ...extra
27975
- };
27976
- await this.db.updateAsync({ pid }, { $set: update2 }, {});
28112
+ const updatedAt = Date.now();
28113
+ const exitReason = extra?.exitReason || "";
28114
+ const exitCode = extra?.exitCode;
28115
+ if (exitCode !== undefined) {
28116
+ this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, exitCode = ?, updatedAt = ? WHERE pid = ?", [status, exitReason, exitCode, updatedAt, pid]);
28117
+ } else {
28118
+ this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, updatedAt = ? WHERE pid = ?", [status, exitReason, updatedAt, pid]);
28119
+ }
27977
28120
  logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
27978
28121
  }
27979
28122
  getLogPath(pid) {
@@ -27983,25 +28126,23 @@ class PidStore2 {
27983
28126
  return path12.resolve(this.baseDir, "fifo", `${pid}.stdin`);
27984
28127
  }
27985
28128
  async cleanStaleRecords() {
27986
- const activeRecords = await this.db.findAsync({
27987
- status: { $ne: "exited" }
27988
- });
28129
+ const activeRecords = this.db.query("SELECT * FROM pid_records WHERE status != 'exited'");
27989
28130
  for (const record of activeRecords) {
27990
28131
  if (!this.isProcessAlive(record.pid)) {
27991
- await this.db.updateAsync({ pid: record.pid }, {
27992
- $set: {
27993
- status: "exited",
27994
- exitReason: "stale-cleanup",
27995
- updatedAt: Date.now()
27996
- }
27997
- }, {});
28132
+ this.db.run("UPDATE pid_records SET status = 'exited', exitReason = 'stale-cleanup', updatedAt = ? WHERE pid = ?", [Date.now(), record.pid]);
27998
28133
  logger.debug(`[pidStore] Cleaned stale record for PID ${record.pid}`);
27999
28134
  }
28000
28135
  }
28001
28136
  }
28002
28137
  async close() {
28003
- await this.db.compactDatafileAsync();
28004
- logger.debug("[pidStore] Database compacted and closed");
28138
+ try {
28139
+ this.db.run("PRAGMA optimize");
28140
+ this.db.run("VACUUM");
28141
+ } catch (error) {
28142
+ logger.warn("[pidStore] Failed to optimize database:", error);
28143
+ }
28144
+ this.db.close();
28145
+ logger.debug("[pidStore] Database optimized and closed");
28005
28146
  }
28006
28147
  isProcessAlive(pid) {
28007
28148
  try {
@@ -28014,10 +28155,9 @@ class PidStore2 {
28014
28155
  static async findActiveFifo(workingDir) {
28015
28156
  const store = new PidStore2(workingDir);
28016
28157
  await store.init();
28017
- const records = await store.db.findAsync({ status: { $ne: "exited" } });
28158
+ const records = store.db.query("SELECT * FROM pid_records WHERE status != 'exited' ORDER BY startedAt DESC LIMIT 1");
28018
28159
  await store.close();
28019
- const sorted = records.sort((a2, b) => b.startedAt - a2.startedAt);
28020
- return sorted[0]?.fifoFile ?? null;
28160
+ return records[0]?.fifoFile ?? null;
28021
28161
  }
28022
28162
  }
28023
28163
 
@@ -28050,5 +28190,5 @@ var { exitCode } = await cliYes(config3);
28050
28190
  console.log("exiting process");
28051
28191
  process.exit(exitCode ?? 1);
28052
28192
 
28053
- //# debugId=12710EC867DEB2CA64756E2164756E21
28193
+ //# debugId=624A56792A6F741964756E2164756E21
28054
28194
  //# sourceMappingURL=cli.js.map
package/dist/index.js CHANGED
@@ -20730,24 +20730,81 @@ var init_fifo = __esm(() => {
20730
20730
  });
20731
20731
 
20732
20732
  // ts/pidStore.ts
20733
- import Datastore from "@seald-io/nedb";
20734
20733
  import { mkdir as mkdir3 } from "fs/promises";
20735
20734
  import path9 from "path";
20736
20735
 
20736
+ class SqliteAdapter {
20737
+ db;
20738
+ async init(dbPath) {
20739
+ if (typeof globalThis.Bun !== "undefined") {
20740
+ try {
20741
+ const { Database } = await import("bun:sqlite");
20742
+ this.db = new Database(dbPath);
20743
+ } catch (error) {
20744
+ logger.warn("[pidStore] bun:sqlite not available, falling back to better-sqlite3");
20745
+ const Database = (await import("better-sqlite3")).default;
20746
+ this.db = new Database(dbPath);
20747
+ }
20748
+ } else {
20749
+ const Database = (await import("better-sqlite3")).default;
20750
+ this.db = new Database(dbPath);
20751
+ }
20752
+ }
20753
+ query(sql, params = []) {
20754
+ if (typeof this.db.prepare === "function") {
20755
+ return this.db.prepare(sql).all(params);
20756
+ } else {
20757
+ return this.db.query(sql).all(params);
20758
+ }
20759
+ }
20760
+ run(sql, params = []) {
20761
+ if (typeof this.db.prepare === "function") {
20762
+ return this.db.prepare(sql).run(params);
20763
+ } else {
20764
+ this.db.run(sql, params);
20765
+ return {};
20766
+ }
20767
+ }
20768
+ close() {
20769
+ if (this.db.close) {
20770
+ this.db.close();
20771
+ }
20772
+ }
20773
+ }
20774
+
20737
20775
  class PidStore {
20738
20776
  db;
20739
20777
  baseDir;
20778
+ dbPath;
20740
20779
  constructor(workingDir) {
20741
20780
  this.baseDir = path9.resolve(workingDir, ".agent-yes");
20781
+ this.dbPath = path9.join(this.baseDir, "pid.sqlite");
20742
20782
  }
20743
20783
  async init() {
20744
20784
  await mkdir3(path9.join(this.baseDir, "logs"), { recursive: true });
20745
20785
  await mkdir3(path9.join(this.baseDir, "fifo"), { recursive: true });
20746
- this.db = new Datastore({
20747
- filename: path9.join(this.baseDir, "pid.jsonl"),
20748
- autoload: true
20749
- });
20750
- await this.db.loadDatabaseAsync();
20786
+ this.db = new SqliteAdapter;
20787
+ await this.db.init(this.dbPath);
20788
+ this.db.run("PRAGMA journal_mode=WAL");
20789
+ this.db.run("PRAGMA synchronous=NORMAL");
20790
+ this.db.run("PRAGMA cache_size=1000");
20791
+ this.db.run("PRAGMA temp_store=memory");
20792
+ this.db.run(`
20793
+ CREATE TABLE IF NOT EXISTS pid_records (
20794
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
20795
+ pid INTEGER NOT NULL UNIQUE,
20796
+ cli TEXT NOT NULL,
20797
+ args TEXT NOT NULL,
20798
+ prompt TEXT,
20799
+ logFile TEXT NOT NULL,
20800
+ fifoFile TEXT NOT NULL,
20801
+ status TEXT NOT NULL DEFAULT 'active',
20802
+ exitReason TEXT NOT NULL DEFAULT '',
20803
+ exitCode INTEGER,
20804
+ startedAt INTEGER NOT NULL,
20805
+ updatedAt INTEGER NOT NULL
20806
+ )
20807
+ `);
20751
20808
  await this.cleanStaleRecords();
20752
20809
  }
20753
20810
  async registerProcess({
@@ -20757,29 +20814,43 @@ class PidStore {
20757
20814
  prompt
20758
20815
  }) {
20759
20816
  const now = Date.now();
20760
- const record = {
20761
- pid,
20762
- cli,
20763
- args,
20764
- prompt,
20765
- logFile: this.getLogPath(pid),
20766
- fifoFile: this.getFifoPath(pid),
20767
- status: "active",
20768
- exitReason: "",
20769
- startedAt: now,
20770
- updatedAt: now
20771
- };
20772
- await this.db.insertAsync(record);
20817
+ const argsJson = JSON.stringify(args);
20818
+ const logFile = this.getLogPath(pid);
20819
+ const fifoFile = this.getFifoPath(pid);
20820
+ try {
20821
+ this.db.run(`
20822
+ INSERT INTO pid_records (pid, cli, args, prompt, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
20823
+ VALUES (?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
20824
+ `, [pid, cli, argsJson, prompt, logFile, fifoFile, now, now]);
20825
+ } catch (error) {
20826
+ if (error.code === "SQLITE_CONSTRAINT_UNIQUE") {
20827
+ this.db.run(`
20828
+ UPDATE pid_records
20829
+ SET cli = ?, args = ?, prompt = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
20830
+ WHERE pid = ?
20831
+ `, [cli, argsJson, prompt, logFile, fifoFile, now, now, pid]);
20832
+ } else {
20833
+ throw error;
20834
+ }
20835
+ }
20836
+ const result = this.db.query("SELECT * FROM pid_records WHERE pid = ?", [pid])[0];
20837
+ if (!result) {
20838
+ const allRecords = this.db.query("SELECT * FROM pid_records");
20839
+ logger.error(`[pidStore] Failed to find record for PID ${pid}. All records:`, allRecords);
20840
+ throw new Error(`Failed to register process ${pid}`);
20841
+ }
20773
20842
  logger.debug(`[pidStore] Registered process ${pid}`);
20774
- return record;
20843
+ return result;
20775
20844
  }
20776
20845
  async updateStatus(pid, status, extra) {
20777
- const update2 = {
20778
- status,
20779
- updatedAt: Date.now(),
20780
- ...extra
20781
- };
20782
- await this.db.updateAsync({ pid }, { $set: update2 }, {});
20846
+ const updatedAt = Date.now();
20847
+ const exitReason = extra?.exitReason || "";
20848
+ const exitCode = extra?.exitCode;
20849
+ if (exitCode !== undefined) {
20850
+ this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, exitCode = ?, updatedAt = ? WHERE pid = ?", [status, exitReason, exitCode, updatedAt, pid]);
20851
+ } else {
20852
+ this.db.run("UPDATE pid_records SET status = ?, exitReason = ?, updatedAt = ? WHERE pid = ?", [status, exitReason, updatedAt, pid]);
20853
+ }
20783
20854
  logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
20784
20855
  }
20785
20856
  getLogPath(pid) {
@@ -20789,25 +20860,23 @@ class PidStore {
20789
20860
  return path9.resolve(this.baseDir, "fifo", `${pid}.stdin`);
20790
20861
  }
20791
20862
  async cleanStaleRecords() {
20792
- const activeRecords = await this.db.findAsync({
20793
- status: { $ne: "exited" }
20794
- });
20863
+ const activeRecords = this.db.query("SELECT * FROM pid_records WHERE status != 'exited'");
20795
20864
  for (const record of activeRecords) {
20796
20865
  if (!this.isProcessAlive(record.pid)) {
20797
- await this.db.updateAsync({ pid: record.pid }, {
20798
- $set: {
20799
- status: "exited",
20800
- exitReason: "stale-cleanup",
20801
- updatedAt: Date.now()
20802
- }
20803
- }, {});
20866
+ this.db.run("UPDATE pid_records SET status = 'exited', exitReason = 'stale-cleanup', updatedAt = ? WHERE pid = ?", [Date.now(), record.pid]);
20804
20867
  logger.debug(`[pidStore] Cleaned stale record for PID ${record.pid}`);
20805
20868
  }
20806
20869
  }
20807
20870
  }
20808
20871
  async close() {
20809
- await this.db.compactDatafileAsync();
20810
- logger.debug("[pidStore] Database compacted and closed");
20872
+ try {
20873
+ this.db.run("PRAGMA optimize");
20874
+ this.db.run("VACUUM");
20875
+ } catch (error) {
20876
+ logger.warn("[pidStore] Failed to optimize database:", error);
20877
+ }
20878
+ this.db.close();
20879
+ logger.debug("[pidStore] Database optimized and closed");
20811
20880
  }
20812
20881
  isProcessAlive(pid) {
20813
20882
  try {
@@ -20820,10 +20889,9 @@ class PidStore {
20820
20889
  static async findActiveFifo(workingDir) {
20821
20890
  const store = new PidStore(workingDir);
20822
20891
  await store.init();
20823
- const records = await store.db.findAsync({ status: { $ne: "exited" } });
20892
+ const records = store.db.query("SELECT * FROM pid_records WHERE status != 'exited' ORDER BY startedAt DESC LIMIT 1");
20824
20893
  await store.close();
20825
- const sorted = records.sort((a2, b) => b.startedAt - a2.startedAt);
20826
- return sorted[0]?.fifoFile ?? null;
20894
+ return records[0]?.fifoFile ?? null;
20827
20895
  }
20828
20896
  }
20829
20897
  var init_pidStore = __esm(() => {
@@ -20957,7 +21025,10 @@ function getDefaultConfig() {
20957
21025
  },
20958
21026
  amp: {
20959
21027
  help: "",
20960
- install: "npm i -g @sourcegraph/amp"
21028
+ install: "npm i -g @sourcegraph/amp",
21029
+ enter: [
21030
+ /^.{0,4} Approve /
21031
+ ]
20961
21032
  }
20962
21033
  }
20963
21034
  });
@@ -21654,5 +21725,5 @@ export {
21654
21725
  CLIS_CONFIG
21655
21726
  };
21656
21727
 
21657
- //# debugId=6BCBE693D722CAEF64756E2164756E21
21728
+ //# debugId=41F67CAFCE0253CB64756E2164756E21
21658
21729
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-yes",
3
- "version": "1.34.0",
3
+ "version": "1.35.1",
4
4
  "description": "A wrapper tool that automates interactions with various AI CLI tools by automatically handling common prompts and responses.",
5
5
  "keywords": [
6
6
  "ai",
@@ -62,7 +62,7 @@
62
62
  "registry": "https://registry.npmjs.org/"
63
63
  },
64
64
  "scripts": {
65
- "build": "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external=@seald-io/nedb --external=@snomiao/bun-pty --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
65
+ "build": "bun build ./ts/cli.ts ./ts/index.ts --outdir=dist --target=node --sourcemap --external=better-sqlite3 --external=@snomiao/bun-pty --external=bun-pty --external=node-pty --external=from-node-stream --external=bun",
66
66
  "postbuild": "bun ./ts/postbuild.ts",
67
67
  "demo": "bun run build && bun link && claude-yes -- demo",
68
68
  "dev": "bun ts/index.ts",
@@ -74,8 +74,8 @@
74
74
  "test": "bun test --coverage"
75
75
  },
76
76
  "dependencies": {
77
- "@seald-io/nedb": "^4.0.4",
78
77
  "@snomiao/bun-pty": "^0.3.4",
78
+ "better-sqlite3": "^12.1.0",
79
79
  "bun-pty": "^0.4.8",
80
80
  "from-node-stream": "^0.1.2"
81
81
  },
@@ -85,6 +85,7 @@
85
85
  "@semantic-release/exec": "^7.1.0",
86
86
  "@semantic-release/git": "^10.0.1",
87
87
  "@semantic-release/release-notes-generator": "^14.1.0",
88
+ "@types/better-sqlite3": "^7.6.12",
88
89
  "@types/bun": "^1.3.6",
89
90
  "@types/jest": "^30.0.0",
90
91
  "@types/ms": "^2.1.0",
@@ -228,4 +228,21 @@ describe("CLI argument parsing", () => {
228
228
  expect(result.cli).toBe("codex");
229
229
  expect(result.prompt).toBe("Implement feature");
230
230
  });
231
+
232
+ it("should parse ay -- hello command (ay is wrapper, so CLI is undefined)", () => {
233
+ const result = parseCliArgs([
234
+ "node",
235
+ "/path/to/ay",
236
+ "--",
237
+ "hello",
238
+ ]);
239
+
240
+ // "ay" is a wrapper script like "agent-yes", so cliName is stripped to undefined
241
+ // cli.ts will default this to "claude" at runtime
242
+ expect(result.cli).toBeUndefined();
243
+ expect(result.cliArgs).toEqual([]);
244
+ expect(result.prompt).toBe("hello");
245
+ expect(result.verbose).toBe(false);
246
+ expect(result.robust).toBe(true);
247
+ });
231
248
  });
@@ -17,6 +17,7 @@ export function parseCliArgs(argv: string[]) {
17
17
  .at(-1)
18
18
  ?.replace(/(\.[jt]s)?$/, "")
19
19
  .replace(/^(cli|agent)(-yes$)?/, "")
20
+ .replace(/^ay$/, "") // treat standalone "ay" same as "agent-yes"
20
21
  .replace(/-yes$/, "") || undefined;
21
22
 
22
23
  // Parse args with yargs (same logic as cli.ts:16-73)
@@ -174,7 +175,7 @@ export function parseCliArgs(argv: string[]) {
174
175
  env: process.env as Record<string, string>,
175
176
  cli: (cliName ||
176
177
  parsedArgv.cli ||
177
- parsedArgv._[0]?.toString()?.replace?.(/-yes$/, "")) as (typeof SUPPORTED_CLIS)[number],
178
+ (dashIndex !== 0 ? parsedArgv._[0]?.toString()?.replace?.(/-yes$/, "") : undefined)) as (typeof SUPPORTED_CLIS)[number],
178
179
  cliArgs: cliArgsForSpawn,
179
180
  prompt: [parsedArgv.prompt, dashPrompt].filter(Boolean).join(" ") || undefined,
180
181
  install: parsedArgv.install,
package/ts/pidStore.ts CHANGED
@@ -1,12 +1,12 @@
1
- import Datastore from "@seald-io/nedb";
2
1
  import { mkdir } from "fs/promises";
3
2
  import path from "path";
4
3
  import { logger } from "./logger.ts";
5
4
 
6
5
  export interface PidRecord {
6
+ id?: number;
7
7
  pid: number;
8
8
  cli: string;
9
- args: string[];
9
+ args: string;
10
10
  prompt?: string;
11
11
  logFile: string;
12
12
  fifoFile: string;
@@ -17,23 +17,95 @@ export interface PidRecord {
17
17
  updatedAt: number;
18
18
  }
19
19
 
20
+ // Direct SQLite implementation to avoid Kysely compatibility issues
21
+ class SqliteAdapter {
22
+ private db: any;
23
+
24
+ async init(dbPath: string) {
25
+ if (typeof globalThis.Bun !== "undefined") {
26
+ try {
27
+ const { Database } = await import("bun:sqlite");
28
+ this.db = new Database(dbPath);
29
+ } catch (error) {
30
+ logger.warn("[pidStore] bun:sqlite not available, falling back to better-sqlite3");
31
+ const Database = (await import("better-sqlite3")).default;
32
+ this.db = new Database(dbPath);
33
+ }
34
+ } else {
35
+ const Database = (await import("better-sqlite3")).default;
36
+ this.db = new Database(dbPath);
37
+ }
38
+ }
39
+
40
+ query(sql: string, params: any[] = []): any[] {
41
+ if (typeof this.db.prepare === "function") {
42
+ // better-sqlite3 style
43
+ return this.db.prepare(sql).all(params);
44
+ } else {
45
+ // bun:sqlite style
46
+ return this.db.query(sql).all(params);
47
+ }
48
+ }
49
+
50
+ run(sql: string, params: any[] = []): { lastInsertRowid?: number; changes?: number } {
51
+ if (typeof this.db.prepare === "function") {
52
+ // better-sqlite3 style
53
+ return this.db.prepare(sql).run(params);
54
+ } else {
55
+ // bun:sqlite style
56
+ this.db.run(sql, params);
57
+ return {}; // Bun doesn't return metadata in the same way
58
+ }
59
+ }
60
+
61
+ close() {
62
+ if (this.db.close) {
63
+ this.db.close();
64
+ }
65
+ }
66
+ }
67
+
20
68
  export class PidStore {
21
- protected db!: Datastore<PidRecord>;
69
+ protected db!: SqliteAdapter;
22
70
  private baseDir: string;
71
+ private dbPath: string;
23
72
 
24
73
  constructor(workingDir: string) {
25
74
  this.baseDir = path.resolve(workingDir, ".agent-yes");
75
+ this.dbPath = path.join(this.baseDir, "pid.sqlite");
26
76
  }
27
77
 
28
78
  async init(): Promise<void> {
29
79
  await mkdir(path.join(this.baseDir, "logs"), { recursive: true });
30
80
  await mkdir(path.join(this.baseDir, "fifo"), { recursive: true });
31
81
 
32
- this.db = new Datastore<PidRecord>({
33
- filename: path.join(this.baseDir, "pid.jsonl"),
34
- autoload: true,
35
- });
36
- await this.db.loadDatabaseAsync();
82
+ this.db = new SqliteAdapter();
83
+ await this.db.init(this.dbPath);
84
+
85
+ // Enable WAL mode for better concurrency and performance
86
+ this.db.run("PRAGMA journal_mode=WAL");
87
+ this.db.run("PRAGMA synchronous=NORMAL");
88
+ this.db.run("PRAGMA cache_size=1000");
89
+ this.db.run("PRAGMA temp_store=memory");
90
+
91
+ // Create table if it doesn't exist
92
+ this.db.run(`
93
+ CREATE TABLE IF NOT EXISTS pid_records (
94
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
95
+ pid INTEGER NOT NULL UNIQUE,
96
+ cli TEXT NOT NULL,
97
+ args TEXT NOT NULL,
98
+ prompt TEXT,
99
+ logFile TEXT NOT NULL,
100
+ fifoFile TEXT NOT NULL,
101
+ status TEXT NOT NULL DEFAULT 'active',
102
+ exitReason TEXT NOT NULL DEFAULT '',
103
+ exitCode INTEGER,
104
+ startedAt INTEGER NOT NULL,
105
+ updatedAt INTEGER NOT NULL
106
+ )
107
+ `);
108
+
37
109
  await this.cleanStaleRecords();
38
110
  }
39
111
 
@@ -49,21 +121,40 @@ export class PidStore {
49
121
  prompt?: string;
50
122
  }): Promise<PidRecord> {
51
123
  const now = Date.now();
52
- const record: PidRecord = {
53
- pid,
54
- cli,
55
- args,
56
- prompt,
57
- logFile: this.getLogPath(pid),
58
- fifoFile: this.getFifoPath(pid),
59
- status: "active",
60
- exitReason: "",
61
- startedAt: now,
62
- updatedAt: now,
63
- };
64
- await this.db.insertAsync(record);
124
+ const argsJson = JSON.stringify(args);
125
+ const logFile = this.getLogPath(pid);
126
+ const fifoFile = this.getFifoPath(pid);
127
+
128
+ try {
129
+ this.db.run(`
130
+ INSERT INTO pid_records (pid, cli, args, prompt, logFile, fifoFile, status, exitReason, startedAt, updatedAt)
131
+ VALUES (?, ?, ?, ?, ?, ?, 'active', '', ?, ?)
132
+ `, [pid, cli, argsJson, prompt, logFile, fifoFile, now, now]);
133
+ } catch (error: any) {
134
+ // Handle unique constraint violation by updating existing record
135
+ if (error.code === "SQLITE_CONSTRAINT_UNIQUE") {
136
+ this.db.run(`
137
+ UPDATE pid_records
138
+ SET cli = ?, args = ?, prompt = ?, logFile = ?, fifoFile = ?, status = 'active', exitReason = '', startedAt = ?, updatedAt = ?
139
+ WHERE pid = ?
140
+ `, [cli, argsJson, prompt, logFile, fifoFile, now, now, pid]);
141
+ } else {
142
+ throw error;
143
+ }
144
+ }
145
+
146
+ // Fetch the record
147
+ const result = this.db.query("SELECT * FROM pid_records WHERE pid = ?", [pid])[0];
148
+
149
+ if (!result) {
150
+ // Log all records for debugging
151
+ const allRecords = this.db.query("SELECT * FROM pid_records");
152
+ logger.error(`[pidStore] Failed to find record for PID ${pid}. All records:`, allRecords);
153
+ throw new Error(`Failed to register process ${pid}`);
154
+ }
155
+
65
156
  logger.debug(`[pidStore] Registered process ${pid}`);
66
- return record;
157
+ return result;
67
158
  }
68
159
 
69
160
  async updateStatus(
@@ -71,12 +162,22 @@ export class PidStore {
71
162
  status: PidRecord["status"],
72
163
  extra?: { exitReason?: string; exitCode?: number },
73
164
  ): Promise<void> {
74
- const update: Partial<PidRecord> = {
75
- status,
76
- updatedAt: Date.now(),
77
- ...extra,
78
- };
79
- await this.db.updateAsync({ pid }, { $set: update }, {});
165
+ const updatedAt = Date.now();
166
+ const exitReason = extra?.exitReason || "";
167
+ const exitCode = extra?.exitCode;
168
+
169
+ if (exitCode !== undefined) {
170
+ this.db.run(
171
+ "UPDATE pid_records SET status = ?, exitReason = ?, exitCode = ?, updatedAt = ? WHERE pid = ?",
172
+ [status, exitReason, exitCode, updatedAt, pid]
173
+ );
174
+ } else {
175
+ this.db.run(
176
+ "UPDATE pid_records SET status = ?, exitReason = ?, updatedAt = ? WHERE pid = ?",
177
+ [status, exitReason, updatedAt, pid]
178
+ );
179
+ }
180
+
80
181
  logger.debug(`[pidStore] Updated process ${pid} status=${status}`);
81
182
  }
82
183
 
@@ -89,30 +190,31 @@ export class PidStore {
89
190
  }
90
191
 
91
192
  async cleanStaleRecords(): Promise<void> {
92
- const activeRecords = await this.db.findAsync({
93
- status: { $ne: "exited" } as any,
94
- });
193
+ const activeRecords = this.db.query("SELECT * FROM pid_records WHERE status != 'exited'");
194
+
95
195
  for (const record of activeRecords) {
96
196
  if (!this.isProcessAlive(record.pid)) {
97
- await this.db.updateAsync(
98
- { pid: record.pid },
99
- {
100
- $set: {
101
- status: "exited" as const,
102
- exitReason: "stale-cleanup",
103
- updatedAt: Date.now(),
104
- },
105
- },
106
- {},
197
+ this.db.run(
198
+ "UPDATE pid_records SET status = 'exited', exitReason = 'stale-cleanup', updatedAt = ? WHERE pid = ?",
199
+ [Date.now(), record.pid]
107
200
  );
201
+
108
202
  logger.debug(`[pidStore] Cleaned stale record for PID ${record.pid}`);
109
203
  }
110
204
  }
111
205
  }
112
206
 
113
207
  async close(): Promise<void> {
114
- await this.db.compactDatafileAsync();
115
- logger.debug("[pidStore] Database compacted and closed");
208
+ // Optimize the database (equivalent to compacting in nedb)
209
+ try {
210
+ this.db.run("PRAGMA optimize");
211
+ this.db.run("VACUUM");
212
+ } catch (error) {
213
+ logger.warn("[pidStore] Failed to optimize database:", error);
214
+ }
215
+
216
+ this.db.close();
217
+ logger.debug("[pidStore] Database optimized and closed");
116
218
  }
117
219
 
118
220
  private isProcessAlive(pid: number): boolean {
@@ -127,9 +229,12 @@ export class PidStore {
127
229
  static async findActiveFifo(workingDir: string): Promise<string | null> {
128
230
  const store = new PidStore(workingDir);
129
231
  await store.init();
130
- const records = await store.db.findAsync({ status: { $ne: "exited" } as any });
232
+
233
+ const records = store.db.query(
234
+ "SELECT * FROM pid_records WHERE status != 'exited' ORDER BY startedAt DESC LIMIT 1"
235
+ );
236
+
131
237
  await store.close();
132
- const sorted = records.sort((a, b) => b.startedAt - a.startedAt);
133
- return sorted[0]?.fifoFile ?? null;
238
+ return records[0]?.fifoFile ?? null;
134
239
  }
135
- }
240
+ }