@liuyoumi/codex-history 0.5.1 → 0.6.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 - 2026-07-13
4
+
5
+ ### Changed
6
+
7
+ - Replace typed short-id and command-phrase purge confirmations with a default-no `[y/N]` prompt. Only explicit `y` or `yes` proceeds.
8
+
3
9
  ## 0.5.1 - 2026-05-30
4
10
 
5
11
  ### Fixed
package/README.en.md CHANGED
@@ -57,7 +57,7 @@ codex-history purge --grep "Astro"
57
57
  codex-history purge-orphans
58
58
  ```
59
59
 
60
- `purge` prints the resolved conversation and asks you to type the standard short id before it deletes anything:
60
+ `purge` prints the resolved conversation, then shows a default-no `[y/N]` prompt. It deletes only after an explicit `y` or `yes`; pressing Enter, entering `n`, or entering anything else cancels:
61
61
 
62
62
  ```text
63
63
  About to purge this local Codex conversation:
@@ -68,7 +68,7 @@ cwd: /Users/me/Projects/example
68
68
  updated: 2026-05-28T03:16:01.959Z
69
69
 
70
70
  This cannot be undone.
71
- Type 019e6885 to confirm:
71
+ Proceed with permanent deletion? [y/N]:
72
72
  ```
73
73
 
74
74
  ## Commands
@@ -147,7 +147,7 @@ codex-history purge 019e6885
147
147
  codex-history purge 019e6885 019e6874
148
148
  ```
149
149
 
150
- When multiple targets are provided, the tool resolves all targets and prints a batch plan before deletion. If any id is missing, ambiguous, or active, the whole batch is refused before mutation. Interactive batch purge requires typing `purge-selected`.
150
+ When multiple targets are provided, the tool resolves all targets and prints a batch plan before deletion. If any id is missing, ambiguous, or active, the whole batch is refused before mutation. Interactive batch purge uses the same default-no `[y/N]` prompt and proceeds only after `y` or `yes`.
151
151
 
152
152
  You can also select local conversations with filters:
153
153
 
@@ -158,7 +158,7 @@ codex-history purge --archived
158
158
  codex-history purge --cwd /Users/me/Projects/example --grep "Astro"
159
159
  ```
160
160
 
161
- `--cwd`, `--grep`, and `--archived` can be combined. Combined filters delete only conversations that match every provided condition. `--cwd` matches working-directory path fragments, while `--grep` matches only title, first user message, and preview. By default, filter mode matches non-archived conversations only; use `--archived` to target archived conversations. Filtered purge prints a batch plan and matched cwd values, then requires typing `purge-selected`. Filters cannot be combined with explicit ids.
161
+ `--cwd`, `--grep`, and `--archived` can be combined. Combined filters delete only conversations that match every provided condition. `--cwd` matches working-directory path fragments, while `--grep` matches only title, first user message, and preview. By default, filter mode matches non-archived conversations only; use `--archived` to target archived conversations. Filtered purge prints a batch plan and matched cwd values, then uses the default-no `[y/N]` prompt. Filters cannot be combined with explicit ids.
162
162
 
163
163
  For scripts or non-interactive shells, use `--force`:
164
164
 
@@ -180,7 +180,7 @@ Remove orphaned local data: conversations whose `state_5.sqlite.threads.rollout_
180
180
  codex-history purge-orphans
181
181
  ```
182
182
 
183
- The command first prints a cleanup plan, affected SQLite row counts, files to delete, and an estimated local disk space impact. You must type `purge-orphans` before it executes.
183
+ The command first prints a cleanup plan, affected SQLite row counts, files to delete, and an estimated local disk space impact. It then shows the default-no `[y/N]` prompt and executes only after `y` or `yes`.
184
184
 
185
185
  For scripts or non-interactive shells, use `--force`:
186
186
 
package/README.md CHANGED
@@ -57,7 +57,7 @@ codex-history purge --grep "Astro"
57
57
  codex-history purge-orphans
58
58
  ```
59
59
 
60
- `purge` 会先展示解析到的对话信息,并要求你输入标准短 id,确认后才会删除:
60
+ `purge` 会先展示解析到的对话信息,再显示默认拒绝的 `[y/N]` 确认提示。只有显式输入 `y` 或 `yes` 才会删除;直接回车、输入 `n` 或任何其他内容都会取消:
61
61
 
62
62
  ```text
63
63
  About to purge this local Codex conversation:
@@ -68,7 +68,7 @@ cwd: /Users/me/Projects/example
68
68
  updated: 2026-05-28T03:16:01.959Z
69
69
 
70
70
  This cannot be undone.
71
- Type 019e6885 to confirm:
71
+ Proceed with permanent deletion? [y/N]:
72
72
  ```
73
73
 
74
74
  ## 命令
@@ -147,7 +147,7 @@ codex-history purge 019e6885
147
147
  codex-history purge 019e6885 019e6874
148
148
  ```
149
149
 
150
- 删除多个目标时,工具会先解析全部目标并展示批量计划;只要任意一个 id 不存在、不唯一,或命中 active thread,整个批量操作都会拒绝执行,不会部分删除。交互式批量删除需要输入 `purge-selected` 确认。
150
+ 删除多个目标时,工具会先解析全部目标并展示批量计划;只要任意一个 id 不存在、不唯一,或命中 active thread,整个批量操作都会拒绝执行,不会部分删除。交互式批量删除同样使用默认拒绝的 `[y/N]` 确认提示,只有输入 `y` 或 `yes` 才会继续。
151
151
 
152
152
  也可以直接用过滤条件选择要删除的本地对话:
153
153
 
@@ -158,7 +158,7 @@ codex-history purge --archived
158
158
  codex-history purge --cwd /Users/me/Projects/example --grep "Astro"
159
159
  ```
160
160
 
161
- `--cwd`、`--grep`、`--archived` 可以组合,组合后只删除同时满足全部条件的对话。`--cwd` 会匹配工作目录路径片段,`--grep` 只匹配标题、首条用户消息和预览。默认只匹配未归档对话;如果要删除已归档对话,请使用 `--archived`。过滤式删除会展示批量计划和匹配到的 cwd,并要求输入 `purge-selected` 确认。过滤条件不能和 id 同时使用。
161
+ `--cwd`、`--grep`、`--archived` 可以组合,组合后只删除同时满足全部条件的对话。`--cwd` 会匹配工作目录路径片段,`--grep` 只匹配标题、首条用户消息和预览。默认只匹配未归档对话;如果要删除已归档对话,请使用 `--archived`。过滤式删除会展示批量计划和匹配到的 cwd,并使用默认拒绝的 `[y/N]` 确认提示。过滤条件不能和 id 同时使用。
162
162
 
163
163
  脚本或非交互环境可以使用 `--force`:
164
164
 
@@ -180,7 +180,7 @@ codex-history purge --cwd /Users/me/Projects/example --force
180
180
  codex-history purge-orphans
181
181
  ```
182
182
 
183
- 该命令会先展示清理计划、受影响的 SQLite 行数、将删除的文件数量,以及估算的本地磁盘空间影响。确认后需要输入 `purge-orphans` 才会执行。
183
+ 该命令会先展示清理计划、受影响的 SQLite 行数、将删除的文件数量,以及估算的本地磁盘空间影响。随后会显示默认拒绝的 `[y/N]` 确认提示;只有输入 `y` 或 `yes` 才会执行。
184
184
 
185
185
  脚本或非交互环境可以使用 `--force`:
186
186
 
package/dist/cli.js CHANGED
@@ -1105,6 +1105,12 @@ function executePurgeOrphansPlanCommand(paths, plan) {
1105
1105
  return executePurgeOrphans(paths, plan);
1106
1106
  }
1107
1107
 
1108
+ // src/core/confirmation.ts
1109
+ var affirmativeResponses = /* @__PURE__ */ new Set(["y", "yes"]);
1110
+ function isDestructiveActionConfirmed(answer) {
1111
+ return affirmativeResponses.has(answer.trim().toLowerCase());
1112
+ }
1113
+
1108
1114
  // src/core/output.ts
1109
1115
  function printOutput(value) {
1110
1116
  if (typeof value === "string") {
@@ -1180,12 +1186,18 @@ program.command("purge").argument("[threadIds...]", "Codex thread id(s) or uniqu
1180
1186
  }
1181
1187
  if (plan.plans.length === 1 && !useBatchConfirmation) {
1182
1188
  if (!force) {
1183
- await confirmPurge(plan.plans[0]);
1189
+ await confirmDestructiveAction(
1190
+ "Purge requires an interactive terminal. Use --force to skip confirmation.",
1191
+ formatPurgeConfirmation(plan.plans[0])
1192
+ );
1184
1193
  }
1185
1194
  return formatPurgeResult(executePurgePlanCommand(paths, plan.plans[0]));
1186
1195
  }
1187
1196
  if (!force) {
1188
- await confirmBatchPurge(plan);
1197
+ await confirmDestructiveAction(
1198
+ "Batch purge requires an interactive terminal. Use --force to skip confirmation.",
1199
+ formatBatchPurgeConfirmation(plan)
1200
+ );
1189
1201
  }
1190
1202
  return formatBatchPurgeResult(executeBatchPurgePlanCommand(paths, plan));
1191
1203
  })
@@ -1199,7 +1211,10 @@ program.command("purge-orphans").option("--force", "Skip interactive confirmatio
1199
1211
  return formatPurgeOrphansPlan(plan);
1200
1212
  }
1201
1213
  if (!force) {
1202
- await confirmPurgeOrphans(plan);
1214
+ await confirmDestructiveAction(
1215
+ "purge-orphans requires an interactive terminal. Use --force to skip confirmation.",
1216
+ formatPurgeOrphansConfirmation(plan)
1217
+ );
1203
1218
  }
1204
1219
  return formatPurgeOrphansResult(executePurgeOrphansPlanCommand(paths, plan));
1205
1220
  })
@@ -1234,58 +1249,19 @@ function parsePretty(value) {
1234
1249
  function shouldUsePager(options) {
1235
1250
  return Boolean(options.limit === void 0 && process.stdout.isTTY);
1236
1251
  }
1237
- async function confirmPurge(plan) {
1238
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
1239
- throw new SafetyRefusalError("Purge requires an interactive terminal. Use --force to skip confirmation.");
1240
- }
1241
- const expected = shortId(plan.target.id);
1242
- process.stdout.write(formatPurgeConfirmation(plan));
1243
- const readline = createInterface({
1244
- input: process.stdin,
1245
- output: process.stdout
1246
- });
1247
- try {
1248
- const answer = await readline.question(`Type ${colorize("yellow", expected)} to confirm: `);
1249
- if (answer.trim() !== expected) {
1250
- throw new SafetyRefusalError("Confirmation did not match. No local Codex data was modified.");
1251
- }
1252
- } finally {
1253
- readline.close();
1254
- }
1255
- }
1256
- async function confirmBatchPurge(plan) {
1257
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
1258
- throw new SafetyRefusalError("Batch purge requires an interactive terminal. Use --force to skip confirmation.");
1259
- }
1260
- const expected = "purge-selected";
1261
- process.stdout.write(formatBatchPurgeConfirmation(plan));
1262
- const readline = createInterface({
1263
- input: process.stdin,
1264
- output: process.stdout
1265
- });
1266
- try {
1267
- const answer = await readline.question(`Type ${colorize("yellow", expected)} to confirm: `);
1268
- if (answer.trim() !== expected) {
1269
- throw new SafetyRefusalError("Confirmation did not match. No local Codex data was modified.");
1270
- }
1271
- } finally {
1272
- readline.close();
1273
- }
1274
- }
1275
- async function confirmPurgeOrphans(plan) {
1252
+ async function confirmDestructiveAction(terminalRefusalMessage, confirmationOutput) {
1276
1253
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
1277
- throw new SafetyRefusalError("purge-orphans requires an interactive terminal. Use --force to skip confirmation.");
1254
+ throw new SafetyRefusalError(terminalRefusalMessage);
1278
1255
  }
1279
- const expected = "purge-orphans";
1280
- process.stdout.write(formatPurgeOrphansConfirmation(plan));
1256
+ process.stdout.write(confirmationOutput);
1281
1257
  const readline = createInterface({
1282
1258
  input: process.stdin,
1283
1259
  output: process.stdout
1284
1260
  });
1285
1261
  try {
1286
- const answer = await readline.question(`Type ${colorize("yellow", expected)} to confirm: `);
1287
- if (answer.trim() !== expected) {
1288
- throw new SafetyRefusalError("Confirmation did not match. No local Codex data was modified.");
1262
+ const answer = await readline.question(`Proceed with permanent deletion? ${colorize("yellow", "[y/N]")}: `);
1263
+ if (!isDestructiveActionConfirmed(answer)) {
1264
+ throw new SafetyRefusalError("Confirmation was declined. No local Codex data was modified.");
1289
1265
  }
1290
1266
  } finally {
1291
1267
  readline.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liuyoumi/codex-history",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "CLI for listing and safely purging local Codex conversation history",
5
5
  "type": "module",
6
6
  "bin": {