@oh-my-pi/pi-coding-agent 14.6.2 → 14.6.3

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 (58) hide show
  1. package/CHANGELOG.md +71 -2
  2. package/README.md +21 -0
  3. package/package.json +23 -7
  4. package/src/cli/grievances-cli.ts +89 -4
  5. package/src/commands/grievances.ts +33 -7
  6. package/src/config/prompt-templates.ts +14 -7
  7. package/src/config/settings-schema.ts +585 -100
  8. package/src/config/settings.ts +42 -0
  9. package/src/discovery/helpers.ts +13 -6
  10. package/src/edit/index.ts +3 -3
  11. package/src/edit/line-hash.ts +73 -25
  12. package/src/edit/modes/hashline.lark +10 -3
  13. package/src/edit/modes/hashline.ts +104 -38
  14. package/src/edit/renderer.ts +3 -3
  15. package/src/hindsight/backend.ts +444 -0
  16. package/src/hindsight/bank.ts +131 -0
  17. package/src/hindsight/client.ts +445 -0
  18. package/src/hindsight/config.ts +165 -0
  19. package/src/hindsight/content.ts +205 -0
  20. package/src/hindsight/index.ts +6 -0
  21. package/src/hindsight/retain-queue.ts +166 -0
  22. package/src/hindsight/transcript.ts +71 -0
  23. package/src/main.ts +7 -10
  24. package/src/memories/index.ts +1 -1
  25. package/src/memory-backend/index.ts +4 -0
  26. package/src/memory-backend/local-backend.ts +30 -0
  27. package/src/memory-backend/off-backend.ts +16 -0
  28. package/src/memory-backend/resolve.ts +24 -0
  29. package/src/memory-backend/types.ts +69 -0
  30. package/src/modes/components/settings-defs.ts +50 -451
  31. package/src/modes/components/settings-selector.ts +2 -2
  32. package/src/modes/components/status-line/presets.ts +1 -1
  33. package/src/modes/controllers/command-controller.ts +6 -5
  34. package/src/modes/controllers/event-controller.ts +12 -0
  35. package/src/modes/controllers/selector-controller.ts +3 -12
  36. package/src/modes/theme/theme.ts +4 -0
  37. package/src/prompts/tools/github.md +3 -0
  38. package/src/prompts/tools/hashline.md +20 -16
  39. package/src/prompts/tools/read.md +10 -6
  40. package/src/prompts/tools/recall.md +5 -0
  41. package/src/prompts/tools/reflect.md +5 -0
  42. package/src/prompts/tools/retain.md +5 -0
  43. package/src/prompts/tools/search.md +1 -1
  44. package/src/sdk.ts +12 -9
  45. package/src/session/agent-session.ts +75 -3
  46. package/src/slash-commands/builtin-registry.ts +2 -12
  47. package/src/tools/ast-edit.ts +14 -5
  48. package/src/tools/ast-grep.ts +12 -3
  49. package/src/tools/find.ts +47 -7
  50. package/src/tools/gh-renderer.ts +10 -1
  51. package/src/tools/gh.ts +233 -5
  52. package/src/tools/hindsight-recall.ts +70 -0
  53. package/src/tools/hindsight-reflect.ts +57 -0
  54. package/src/tools/hindsight-retain.ts +63 -0
  55. package/src/tools/index.ts +17 -0
  56. package/src/tools/path-utils.ts +55 -0
  57. package/src/tools/read.ts +1 -1
  58. package/src/tools/search.ts +45 -8
package/CHANGELOG.md CHANGED
@@ -2,7 +2,58 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [14.6.3] - 2026-05-03
6
+
7
+ ### Breaking Changes
8
+
9
+ - Renamed hashline separator configuration from `PI_HASHLINE_SEP` to `PI_HL_SEP` and changed the default payload separator from `\\` to `>`
10
+
11
+ ### Added
12
+
13
+ - Added inline hashline edit syntax so `< ANCHOR${sep}TEXT` prepended text to an anchored line and `+ ANCHOR${sep}TEXT` appended text to it without requiring a multi-line payload block
14
+ - Added a `memory.backend` setting (off, local, hindsight) under a new Memory settings tab to control which memory subsystem is active
15
+ - Added Hindsight memory settings (`hindsight.*`) for API connection, bank identification, and recall/retain policy
16
+ - Added `retain`, `recall`, and `reflect` tools for direct long-term memory search, retention, and reflection when using the Hindsight backend
17
+ - Added `hindsight.scoping` setting (`global`, `per-project`, `per-project-tagged`) that controls whether memories are shared across projects, isolated per cwd, or tagged so global + project memories merge on recall (default: `per-project-tagged`)
18
+ - Added `search_code`, `search_commits`, and `search_repos` ops to the `github` tool so the search surface mirrors `gh search`'s subcommands
19
+
20
+ ### Changed
21
+
22
+ - Changed `hindsight` tool retains to enqueue memory writes and return `Memory queued.` immediately instead of waiting on a network request
23
+ - Changed `hindsight` tool memory writes to use automatic background batching (up to 16 items or 5 seconds) so tool calls do not block
24
+ - Changed failed `hindsight` queue flushes to be surfaced as UI warning notices rather than failing the foreground `hindsight` tool call
25
+ - Changed hashline read/search previews and diff output to keep `|` as the anchor-to-text separator while using the separate configured edit payload separator
26
+ - Mapped invalid `hindsight.scoping` settings back to the default `per-project-tagged` behavior with a warning
27
+ - Changed `/memory view`, `/memory clear`, and `/memory enqueue` to route through the selected memory backend instead of being hardcoded to local memories
28
+ - Changed compaction context assembly to include backend-provided recall context when available
29
+ - Updated multi-path `search`, `find`, `ast-edit`, and `ast-grep` calls to skip missing base paths, returning matches from remaining paths and reporting skipped paths in output
30
+ - Replaced `hindsight.dynamicBankId` (boolean) with the explicit `hindsight.scoping` enum; legacy values are migrated automatically (`dynamicBankId=true` → `scoping="per-project"`)
31
+ - Changed `search_repos` to run as a global repository search using query qualifiers without applying the `repo` filter
32
+
33
+ ### Deprecated
34
+
35
+ - Set explicit `hindsight.scoping` now takes precedence over legacy `hindsight.dynamicBankId` when migrating old settings
36
+
37
+ ### Removed
38
+
39
+ - Removed legacy `hindsight.dynamicBankId` and `hindsight.agentName` fields from the active settings model
40
+ - Removed `hindsight.agentName` and the `HINDSIGHT_AGENT_NAME` / `HINDSIGHT_CHANNEL_ID` / `HINDSIGHT_USER_ID` env vars; the legacy `agent::project::channel::user` bank tuple is gone. A user-set `agentName` is migrated onto `hindsight.bankId`.
41
+
42
+ ### Fixed
43
+
44
+ - Fixed pending `hindsight` queued memory writes to flush on agent end, clear, and enqueue operations so tool-invoked facts are not dropped when sessions transition
45
+ - Fixed retained memory writes from the `hindsight` tool to include session context and tags consistently in background batches
46
+ - Fixed inline hashline modify operations to fail fast when combined with a delete or replace on the same target line
47
+ - Fixed hashline parsing of payload blocks to handle a shared extra leading symbol prefix (such as markdown `>>`) on all payload lines by stripping it as an auto-correction instead of rejecting the edit
48
+ - Forwarded project scoping tags to `hindsight` retain, recall, and reflect operations so manual memory commands honor the active tagging mode
49
+ - Fixed legacy migrations by mapping existing `memories.enabled` values to `memory.backend` on load to preserve prior enable/disable behavior
50
+ - Fixed memory retention so recalled `<memories>` blocks and legacy `<hindsight_memories>` / `<relevant_memories>` blocks are stripped before storing transcripts and do not feed back as new memory
51
+ - Fixed `search_code` output to include each match path, repository, shortened SHA, and a one-line matching fragment
52
+ - Fixed `search_commits` output to show shortened SHAs with commit message first lines
53
+ - Fixed `search_repos` output formatting to return repository summaries including language, stars, forks, issues, visibility, and key status fields
54
+
5
55
  ## [14.6.2] - 2026-05-03
56
+
6
57
  ### Added
7
58
 
8
59
  - Added `statusLine.sessionAccent` to disable session-name accent coloring for the editor border and status line gap ([#918](https://github.com/can1357/oh-my-pi/issues/918))
@@ -13,8 +64,8 @@
13
64
 
14
65
  - Fixed SSH ControlMaster socket paths to use OpenSSH's connection hash (`%C`) so connections to the same host with different users, ports, or jump hosts do not share a master session.
15
66
 
16
-
17
67
  ## [14.6.1] - 2026-05-02
68
+
18
69
  ### Changed
19
70
 
20
71
  - Updated GitHub call headers to display operation-specific titles and contextual metadata such as repository, branch, issue/PR IDs, and search query snippets for supported operations
@@ -25,6 +76,7 @@
25
76
  - Fixed GitHub tool output fallbacks that previously always showed a GitHub Run Watch heading so they now show the actual operation and clear `no output`/`request failed` status messaging
26
77
 
27
78
  ## [14.6.0] - 2026-05-02
79
+
28
80
  ### Breaking Changes
29
81
 
30
82
  - Reworked autoresearch storage and protocol. State now lives in `~/.omp/autoresearch/<project>.db` (SQLite) and per-run logs in `~/.omp/autoresearch/<project>/runs/<id>/benchmark.log`. The repo-side artifacts `autoresearch.md`, `autoresearch.sh`, `autoresearch.checks.sh`, `autoresearch.program.md`, `autoresearch.ideas.md`, `autoresearch.jsonl`, `.autoresearch/`, and `autoresearch.config.json` are no longer read or written; they are deleted by `/autoresearch clear`. Any existing data is not migrated.
@@ -64,6 +116,7 @@
64
116
  - Fixed `log_experiment keep` silently dropping the iteration's diff on an autoresearch branch. The previous logic filtered out every path that was already dirty when `run_experiment` ran — but in the iteration cycle the agent's edits always land before `run_experiment`, so the entire iteration was filtered away and nothing was committed. On an autoresearch branch, `keep` now treats every currently-dirty path as the iteration's change and commits it.
65
117
 
66
118
  ## [14.5.14] - 2026-05-01
119
+
67
120
  ### Changed
68
121
 
69
122
  - Changed markdown conversion and archive tooling to defer loading heavy dependencies (Turndown, fflate, and browser agent content) until first use, reducing startup overhead for CLI startup and command initialization
@@ -105,6 +158,7 @@
105
158
  - Fixed eval startup messaging to report `eval` as unavailable when Python is unreachable and JavaScript backend is disabled
106
159
 
107
160
  ### Fixed
161
+
108
162
  - Stabilized MCP tool ordering so reconnects and refreshes no longer reorder the tools array sent to the model. Anthropic prompt caching is keyed on byte-identical tool definitions; previously, the order depended on connection sequence and a single MCP server reconnect could shuffle tools across servers and invalidate the tools cache breakpoint.
109
163
  - Skipped redundant system-prompt rebuilds in `AgentSession.refreshMCPTools` when the active tool set is unchanged. MCP transport flapping (e.g. routine 5-minute SSE reconnects) used to call `rebuildSystemPrompt` on every reconnect even though the resulting prompt was byte-identical, eating CPU and risking cache misses if the rebuild ever became non-deterministic. The applied-tool signature also covers `customWireName` so a wire-name flip with the rest of the tool metadata constant still forces a rebuild.
110
164
 
@@ -135,6 +189,7 @@
135
189
  - Fixed plan mode to auto-redirect `write` and `edit` calls targeting a bare `PLAN.md` (or any same-basename cwd-relative path) to the canonical `local://PLAN.md` plan artifact instead of rejecting them
136
190
 
137
191
  ## [14.5.11] - 2026-04-30
192
+
138
193
  ### Breaking Changes
139
194
 
140
195
  - `todo_write`: renamed `replace` op to `init` and reshaped its input to `list: [{phase: string, items: string[]}]`. Tasks no longer accept a `status` field; all start `pending` and the first auto-promotes to `in_progress`. The `append` op's `items` is now `string[]` (was `{id, label}[]`)
@@ -206,6 +261,7 @@
206
261
  - Fixed hash mismatch errors to include likely-shifted anchor hints when a unique matching line is found elsewhere in the file
207
262
 
208
263
  ## [14.5.8] - 2026-04-29
264
+
209
265
  ### Breaking Changes
210
266
 
211
267
  - Changed the task runner toggle from `just.enabled` to `runCommand.enabled`, so existing configurations using `just.enabled` must be migrated
@@ -231,12 +287,15 @@
231
287
  ### Fixed
232
288
 
233
289
  - Fixed hook editors to recognize Ctrl+Enter when terminals include NumLock or keypad Enter metadata.
290
+
234
291
  ## [14.5.6] - 2026-04-29
292
+
235
293
  ### Changed
236
294
 
237
295
  - Removed the atom edit mode's multi-anchor auto-rebase rejection so stale-but-uniquely-rebasable block edits apply with warnings instead of failing.
238
296
 
239
297
  ## [14.5.5] - 2026-04-29
298
+
240
299
  ### Breaking Changes
241
300
 
242
301
  - Rejected atom diffs with unrecognized operations (including lone '-' lines) by throwing parse errors instead of treating them as inserts
@@ -255,6 +314,7 @@
255
314
  - Fixed bracket-corruption caused by botched block rewrites by automatically removing a newly introduced duplicate adjacent line when removing it restores the original `{}`, `()`, and `[]` balance and by warning when automatic removal is unsafe
256
315
 
257
316
  ## [14.5.4] - 2026-04-28
317
+
258
318
  ### Breaking Changes
259
319
 
260
320
  - Changed the `atom` edit mode from JSON `{ path, edits }` calls to the compact file-oriented `input` patch language that was previously exposed as `atomd`; `atomd` is no longer a separate edit variant
@@ -308,6 +368,7 @@
308
368
  - Fixed `splice_block` for same-line `(` bodies so inline call sites like `int(port)` can be replaced correctly
309
369
 
310
370
  ## [14.5.2] - 2026-04-26
371
+
311
372
  ### Breaking Changes
312
373
 
313
374
  - Removed support for sed-style string expressions and required `sed` to be specified as an object with `pat` and `rep` (and optional `g`, `F`, `i` flags)
@@ -332,6 +393,7 @@
332
393
  - Removed the hand-rolled JSON unescape fallback in the streaming edit-arg renderer; partial fragments that fail `JSON.parse` are now surfaced raw rather than partially decoded with a non-spec-compliant unescaper that mishandled lone surrogates
333
394
 
334
395
  ## [14.4.3] - 2026-04-26
396
+
335
397
  ### Added
336
398
 
337
399
  - Added `irc` tool for agent-to-agent messaging with `list` and `send` operations, including optional broadcast to `all` and optional suppression of reply waits
@@ -351,6 +413,7 @@
351
413
  - Fixed `/btw` request replacement so issuing a new query cleanly aborts the previous active request
352
414
 
353
415
  ## [14.4.2] - 2026-04-26
416
+
354
417
  ### Breaking Changes
355
418
 
356
419
  - Changed `/todo append` from JSON payload input to `/todo append [<phase>] <task...>` with optional quoted tokens and automatic phase creation
@@ -388,6 +451,7 @@
388
451
  - Fixed `poll` wait duration parsing to fall back to `30s` when the provided value is an empty string
389
452
 
390
453
  ## [14.4.1] - 2026-04-26
454
+
391
455
  ### Breaking Changes
392
456
 
393
457
  - Replaced the legacy `gh_repo_view`, `gh_issue_view`, `gh_pr_view`, `gh_pr_diff`, `gh_pr_checkout`, `gh_pr_push`, `gh_run_watch`, `gh_search_issues`, and `gh_search_prs` tool names with only `github`, which requires updating existing callers that invoked the old `gh_*` tools
@@ -589,6 +653,7 @@
589
653
 
590
654
  - Fixed task calls in `schema-free` and `independent` modes to return clear mode-specific errors when disallowed `context` or `schema` inputs are provided
591
655
  - Fixed newly generated session IDs to use UUIDv7 for new, forked, and branched sessions while preserving resumed session IDs
656
+
592
657
  ## [14.1.1] - 2026-04-14
593
658
 
594
659
  ### Breaking Changes
@@ -630,7 +695,7 @@
630
695
  - Changed Vim page-scroll commands `C-f`, `C-b`, `C-u`, and `C-d` to move in viewport-height based increments instead of fixed constants
631
696
  - Changed `z` command behavior so `zt`, `zb`, and `z.` now align cursor movement to first non-blank in the line
632
697
  - Changed `:g`/`:v` global command handling to process matching lines safely by working in reverse order and preserving file structure
633
- - Changed vim tab breadcrumb rendering from ` → ` to `→` in the editor view
698
+ - Changed vim tab breadcrumb rendering from `→` to `→` in the editor view
634
699
  - Changed custom tool and task execution contexts to no longer expose a shared `searchDb` accessor, removing direct access to native grep/glob/fuzzyFind search backends from extension callbacks
635
700
  - Changed the `task` tool `schema` field to require JSON-encoded JTD schema text instead of a schema object, matching prompt guidance and task-subagent invocation
636
701
  - Changed chunk edit payloads to encode selectors as `path: "file:selector"` and updated chunk tool guidance and examples to match
@@ -664,6 +729,7 @@
664
729
  - Fixed retained Python kernel ownership so `AgentSession.dispose()` only shuts down kernels owned by that session, including warmup-created kernels
665
730
 
666
731
  ## [14.1.0] - 2026-04-11
732
+
667
733
  ### Added
668
734
 
669
735
  - Added richer tool rendering details in session export HTML, including metadata badges, argument formatting, and todo task tree styling for exported tool and workflow messages
@@ -705,6 +771,7 @@
705
771
  - Blocked destructive SQL execution in read-mode SQLite access by using read-only connections and rejecting bound-parameter raw SQL
706
772
 
707
773
  ## [14.0.5] - 2026-04-11
774
+
708
775
  ### Added
709
776
 
710
777
  - Added `designer` model role for UI/UX design tasks with Gemini 3.1 Pro as default model
@@ -751,6 +818,7 @@
751
818
  - Fixed MCP config docs and schema to use `~/.omp/agent/mcp.json` for user-scoped OMP-native MCP config while keeping project config at `<cwd>/.omp/mcp.json`
752
819
 
753
820
  ## [14.0.4] - 2026-04-10
821
+
754
822
  ### Added
755
823
 
756
824
  - Added `PI_CHUNK_AUTOINDENT` environment variable to control whether chunk read/edit tools normalize indentation to canonical tabs or preserve literal file whitespace
@@ -777,6 +845,7 @@
777
845
  - Fixed cached Ollama discovery rows so upgraded installs switch to the OpenAI Responses transport instead of staying on the old completions transport
778
846
 
779
847
  ## [14.0.2] - 2026-04-09
848
+
780
849
  ### Added
781
850
 
782
851
  - Added `/force` slash command to force the next agent turn to use a specific tool
package/README.md CHANGED
@@ -12,3 +12,24 @@ Package-specific references:
12
12
  - [MCP runtime lifecycle](../../docs/mcp-runtime-lifecycle.md)
13
13
  - [MCP server/tool authoring](../../docs/mcp-server-tool-authoring.md)
14
14
  - [DEVELOPMENT](./DEVELOPMENT.md)
15
+
16
+ ## Memory backends
17
+
18
+ The agent supports three mutually-exclusive memory backends, selected via the `memory.backend` setting (Settings → Memory tab, or `~/.omp/config.yml`):
19
+
20
+ - `off` (default) — no memory subsystem runs.
21
+ - `local` — existing rollout-summarisation pipeline; writes `memory_summary.md` and consolidated artifacts under the agent dir.
22
+ - `hindsight` — talks to a [Hindsight](https://hindsight.vectorize.io) server (Cloud or self-hosted Docker), retains transcripts every Nth user turn, recalls memories on the first turn of a session, and exposes `retain`, `recall`, and `reflect`.
23
+
24
+ ### Hindsight quickstart
25
+
26
+ 1. Run a Hindsight server (Cloud or `docker run -p 8888:8888 ghcr.io/vectorize-io/hindsight:latest`).
27
+ 2. Set `memory.backend = "hindsight"` and `hindsight.apiUrl = "http://localhost:8888"` (or your Cloud URL).
28
+ 3. Optional environment overrides (env wins over settings):
29
+ - `HINDSIGHT_API_URL`, `HINDSIGHT_API_TOKEN` — connection
30
+ - `HINDSIGHT_BANK_ID`, `HINDSIGHT_DYNAMIC_BANK_ID`, `HINDSIGHT_AGENT_NAME` — bank addressing
31
+ - `HINDSIGHT_AUTO_RECALL`, `HINDSIGHT_AUTO_RETAIN`, `HINDSIGHT_RETAIN_MODE` — lifecycle
32
+ - `HINDSIGHT_RECALL_BUDGET`, `HINDSIGHT_RECALL_MAX_TOKENS` — recall sizing
33
+ - `HINDSIGHT_BANK_MISSION`, `HINDSIGHT_DEBUG`
34
+
35
+ Switching backends mid-session is honoured on the next system-prompt rebuild and the next `/memory` slash command. Existing users with `memories.enabled = true|false` are migrated to `memory.backend = "local"|"off"` exactly once on first launch.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-coding-agent",
4
- "version": "14.6.2",
4
+ "version": "14.6.3",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/can1357/oh-my-pi",
7
7
  "author": "Can Boluk",
@@ -46,12 +46,12 @@
46
46
  "dependencies": {
47
47
  "@agentclientprotocol/sdk": "0.20.0",
48
48
  "@mozilla/readability": "^0.6.0",
49
- "@oh-my-pi/omp-stats": "14.6.2",
50
- "@oh-my-pi/pi-agent-core": "14.6.2",
51
- "@oh-my-pi/pi-ai": "14.6.2",
52
- "@oh-my-pi/pi-natives": "14.6.2",
53
- "@oh-my-pi/pi-tui": "14.6.2",
54
- "@oh-my-pi/pi-utils": "14.6.2",
49
+ "@oh-my-pi/omp-stats": "14.6.3",
50
+ "@oh-my-pi/pi-agent-core": "14.6.3",
51
+ "@oh-my-pi/pi-ai": "14.6.3",
52
+ "@oh-my-pi/pi-natives": "14.6.3",
53
+ "@oh-my-pi/pi-tui": "14.6.3",
54
+ "@oh-my-pi/pi-utils": "14.6.3",
55
55
  "@puppeteer/browsers": "^2.13.0",
56
56
  "@sinclair/typebox": "^0.34.49",
57
57
  "@xterm/headless": "^6.0.0",
@@ -371,6 +371,22 @@
371
371
  "types": "./src/memories/*.ts",
372
372
  "import": "./src/memories/*.ts"
373
373
  },
374
+ "./memory-backend": {
375
+ "types": "./src/memory-backend/index.ts",
376
+ "import": "./src/memory-backend/index.ts"
377
+ },
378
+ "./memory-backend/*": {
379
+ "types": "./src/memory-backend/*.ts",
380
+ "import": "./src/memory-backend/*.ts"
381
+ },
382
+ "./hindsight": {
383
+ "types": "./src/hindsight/index.ts",
384
+ "import": "./src/hindsight/index.ts"
385
+ },
386
+ "./hindsight/*": {
387
+ "types": "./src/hindsight/*.ts",
388
+ "import": "./src/hindsight/*.ts"
389
+ },
374
390
  "./modes": {
375
391
  "types": "./src/modes/index.ts",
376
392
  "import": "./src/modes/index.ts"
@@ -19,17 +19,31 @@ export interface ListGrievancesOptions {
19
19
  json: boolean;
20
20
  }
21
21
 
22
- function openDb(): Database | null {
22
+ export interface CleanGrievancesOptions {
23
+ /** Delete a single grievance by id. */
24
+ id?: number;
25
+ /** Delete every grievance recorded for this tool name. */
26
+ tool?: string;
27
+ /** Delete every grievance regardless of tool/id. */
28
+ all?: boolean;
29
+ /** Output the deletion count as JSON instead of a status message. */
30
+ json?: boolean;
31
+ }
32
+
33
+ function openDb(readonly: boolean): Database | null {
23
34
  try {
24
- const db = new Database(getAutoQaDbPath(), { readonly: true });
25
- return db;
35
+ // bun:sqlite rejects `{ readonly: false }` — it requires either readonly,
36
+ // readwrite, or create flags to be explicit. Use the default constructor
37
+ // (readwrite + create) for write mode and only pass `readonly: true` when
38
+ // listing.
39
+ return readonly ? new Database(getAutoQaDbPath(), { readonly: true }) : new Database(getAutoQaDbPath());
26
40
  } catch {
27
41
  return null;
28
42
  }
29
43
  }
30
44
 
31
45
  export async function listGrievances(options: ListGrievancesOptions): Promise<void> {
32
- const db = openDb();
46
+ const db = openDb(true);
33
47
  if (!db) {
34
48
  if (options.json) {
35
49
  console.log("[]");
@@ -76,3 +90,74 @@ export async function listGrievances(options: ListGrievancesOptions): Promise<vo
76
90
  db.close();
77
91
  }
78
92
  }
93
+
94
+ /**
95
+ * Delete grievances from the auto-QA database.
96
+ *
97
+ * Selectors are mutually exclusive in intent — exactly one of `id`, `tool`, or
98
+ * `all` is required. Multiple selectors are rejected to prevent ambiguous deletes
99
+ * (e.g. `--id 5 --all` would be a footgun). Returns silently when the database
100
+ * does not exist yet.
101
+ */
102
+ export async function cleanGrievances(options: CleanGrievancesOptions): Promise<void> {
103
+ const selectors = [options.id !== undefined, !!options.tool, !!options.all].filter(Boolean).length;
104
+ if (selectors === 0) {
105
+ console.error(chalk.red("Specify exactly one of --id, --tool, or --all."));
106
+ process.exitCode = 1;
107
+ return;
108
+ }
109
+ if (selectors > 1) {
110
+ console.error(chalk.red("--id, --tool, and --all are mutually exclusive."));
111
+ process.exitCode = 1;
112
+ return;
113
+ }
114
+
115
+ const db = openDb(false);
116
+ if (!db) {
117
+ if (options.json) {
118
+ console.log(JSON.stringify({ deleted: 0 }));
119
+ } else {
120
+ console.log(
121
+ chalk.dim("No grievances database found. Enable auto-QA with PI_AUTO_QA=1 or the dev.autoqa setting."),
122
+ );
123
+ }
124
+ return;
125
+ }
126
+
127
+ try {
128
+ let deleted = 0;
129
+ if (options.id !== undefined) {
130
+ const result = db.prepare("DELETE FROM grievances WHERE id = ?").run(options.id);
131
+ deleted = Number(result.changes);
132
+ } else if (options.tool) {
133
+ const result = db.prepare("DELETE FROM grievances WHERE tool = ?").run(options.tool);
134
+ deleted = Number(result.changes);
135
+ } else {
136
+ const result = db.prepare("DELETE FROM grievances").run();
137
+ deleted = Number(result.changes);
138
+ // Reset the autoincrement counter so a fresh slate starts at #1 again.
139
+ // `sqlite_sequence` only exists if AUTOINCREMENT was ever used; ignore failures.
140
+ try {
141
+ db.prepare("DELETE FROM sqlite_sequence WHERE name = 'grievances'").run();
142
+ } catch {
143
+ /* sequence table missing on a brand-new db — nothing to reset */
144
+ }
145
+ }
146
+
147
+ if (options.json) {
148
+ console.log(JSON.stringify({ deleted }));
149
+ return;
150
+ }
151
+
152
+ if (deleted === 0) {
153
+ console.log(chalk.dim("No matching grievances to delete."));
154
+ return;
155
+ }
156
+
157
+ const scope =
158
+ options.id !== undefined ? `#${options.id}` : options.tool ? `for ${options.tool}` : "(all entries)";
159
+ console.log(chalk.green(`Deleted ${deleted} grievance${deleted === 1 ? "" : "s"} ${scope}.`));
160
+ } finally {
161
+ db.close();
162
+ }
163
+ }
@@ -1,20 +1,46 @@
1
1
  /**
2
- * View recently reported tool issues from automated QA.
2
+ * View and clean recently reported tool issues from automated QA.
3
3
  */
4
- import { Command, Flags } from "@oh-my-pi/pi-utils/cli";
5
- import { listGrievances } from "../cli/grievances-cli";
4
+ import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
5
+ import { cleanGrievances, listGrievances } from "../cli/grievances-cli";
6
6
 
7
7
  export default class Grievances extends Command {
8
- static description = "View reported tool issues (auto-QA grievances)";
8
+ static description = "View or clean reported tool issues (auto-QA grievances)";
9
+
10
+ static args = {
11
+ // Positional action: "list" (default) or "clean". A positional arg keeps
12
+ // the historical `omp grievances` invocation working unchanged while
13
+ // reusing the same command surface for the new clean sub-action.
14
+ action: Args.string({
15
+ description: "list (default) or clean",
16
+ required: false,
17
+ options: ["list", "clean"],
18
+ default: "list",
19
+ }),
20
+ };
9
21
 
10
22
  static flags = {
11
- limit: Flags.integer({ char: "n", description: "Number of recent issues to show", default: 20 }),
12
- tool: Flags.string({ char: "t", description: "Filter by tool name" }),
23
+ limit: Flags.integer({ char: "n", description: "Number of recent issues to show (list)", default: 20 }),
24
+ tool: Flags.string({ char: "t", description: "Filter by tool name (list, clean)" }),
13
25
  json: Flags.boolean({ char: "j", description: "Output as JSON", default: false }),
26
+ id: Flags.integer({ description: "Delete a single grievance by id (clean)" }),
27
+ all: Flags.boolean({ description: "Delete every grievance (clean)", default: false }),
14
28
  };
15
29
 
30
+ static examples = [
31
+ "omp grievances",
32
+ "omp grievances list --tool find",
33
+ "omp grievances clean --id 209",
34
+ "omp grievances clean --tool find",
35
+ "omp grievances clean --all",
36
+ ];
37
+
16
38
  async run(): Promise<void> {
17
- const { flags } = await this.parse(Grievances);
39
+ const { args, flags } = await this.parse(Grievances);
40
+ if (args.action === "clean") {
41
+ await cleanGrievances({ id: flags.id, tool: flags.tool, all: flags.all, json: flags.json });
42
+ return;
43
+ }
18
44
  await listGrievances({ limit: flags.limit, tool: flags.tool, json: flags.json });
19
45
  }
20
46
  }
@@ -8,7 +8,7 @@ import {
8
8
  parseFrontmatter,
9
9
  prompt,
10
10
  } from "@oh-my-pi/pi-utils";
11
- import { computeLineHash, HASHLINE_CONTENT_SEPARATOR } from "../edit/line-hash";
11
+ import { computeLineHash, HL_BODY_SEP, HL_EDIT_SEP } from "../edit/line-hash";
12
12
  import { jtdToTypeScript } from "../tools/jtd-to-typescript";
13
13
  import { parseCommandArgs, substituteArgs } from "../utils/command-args";
14
14
 
@@ -53,10 +53,10 @@ interface HashlineHelperState {
53
53
  byLine: Map<number, HashlineHelperRef>;
54
54
  }
55
55
 
56
- const HASHLINE_HELPER_STATE = Symbol("hashlineHelperState");
56
+ const HL_HELPER_STATE = Symbol("hashlineHelperState");
57
57
 
58
58
  interface HashlineHelperStateHolder {
59
- [HASHLINE_HELPER_STATE]?: HashlineHelperState;
59
+ [HL_HELPER_STATE]?: HashlineHelperState;
60
60
  }
61
61
 
62
62
  function isHelperOptions(value: unknown): value is prompt.HelperOptions {
@@ -78,10 +78,10 @@ function getHashlineHelperState(context: unknown, options: prompt.HelperOptions
78
78
  }
79
79
 
80
80
  const holder = holderTarget as HashlineHelperStateHolder;
81
- if (!holder[HASHLINE_HELPER_STATE]) {
82
- holder[HASHLINE_HELPER_STATE] = { byLine: new Map() };
81
+ if (!holder[HL_HELPER_STATE]) {
82
+ holder[HL_HELPER_STATE] = { byLine: new Map() };
83
83
  }
84
- return holder[HASHLINE_HELPER_STATE];
84
+ return holder[HL_HELPER_STATE];
85
85
  }
86
86
 
87
87
  function isLineNumberArg(value: unknown): boolean {
@@ -156,9 +156,16 @@ prompt.registerHelper("hline", function (this: unknown, ...args: unknown[]): str
156
156
  const { num, ref, text } = formatHashlineRef(lineNum, content);
157
157
  const state = getHashlineHelperState(this, options);
158
158
  rememberHashlineRef(state, num, ref);
159
- return `${ref}${HASHLINE_CONTENT_SEPARATOR}${text}`;
159
+ return `${ref}${HL_BODY_SEP}${text}`;
160
160
  });
161
161
 
162
+ /**
163
+ * {{hsep}} — emit the configured hashline payload separator character.
164
+ * Stays in sync with {@link HL_EDIT_SEP} so edit prompt templates
165
+ * never have to hardcode the payload separator.
166
+ */
167
+ prompt.registerHelper("hsep", (): string => HL_EDIT_SEP);
168
+
162
169
  const INLINE_ARG_SHELL_PATTERN = /\$(?:ARGUMENTS|@(?:\[\d+(?::\d*)?\])?|\d+)/;
163
170
  const INLINE_ARG_TEMPLATE_PATTERN = /\{\{[\s\S]*?(?:\b(?:arguments|ARGUMENTS|args)\b|\barg\s+[^}]+)[\s\S]*?\}\}/;
164
171