@gi4nks/ph 1.5.0 → 1.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.
Files changed (34) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/README.md +118 -152
  3. package/dist/cli.js +2049 -1055
  4. package/dist/cli.js.map +1 -1
  5. package/docs/evals/context-retrieval-golden.json +26 -0
  6. package/docs/evals/context-retrieval.md +27 -0
  7. package/docs/hooks.md +13 -7
  8. package/docs/ph-analysis.html +145 -0
  9. package/docs/ph-manual.md +337 -376
  10. package/docs/specs/SPEC-001-architecture-cli.md +82 -0
  11. package/docs/specs/SPEC-002-database-layer.md +105 -0
  12. package/docs/specs/SPEC-003-capture-modes.md +110 -0
  13. package/docs/specs/SPEC-004-filter-metadata.md +82 -0
  14. package/docs/specs/SPEC-005-search.md +102 -0
  15. package/docs/specs/SPEC-006-analysis-pipeline.md +98 -0
  16. package/docs/specs/SPEC-007-memory-summaries.md +96 -0
  17. package/docs/specs/SPEC-008-embeddings.md +90 -0
  18. package/docs/specs/SPEC-009-retention-archive.md +91 -0
  19. package/docs/specs/SPEC-010-remote-sync.md +106 -0
  20. package/docs/specs/SPEC-011-mcp-server.md +94 -0
  21. package/docs/specs/SPEC-012-tui-browser.md +98 -0
  22. package/docs/specs/SPEC-013-importers.md +109 -0
  23. package/docs/specs/SPEC-014-analytics-commands.md +109 -0
  24. package/docs/specs/SPEC-015-config-env.md +85 -0
  25. package/docs/specs/SPEC-INDEX.md +35 -0
  26. package/docs/specs/SPEC-ISSUES.md +122 -0
  27. package/docs/specs/_TEMPLATE.md +65 -0
  28. package/docs/superpowers/plans/2026-06-12-ph-evolution-phase1.md +466 -0
  29. package/docs/superpowers/plans/2026-06-12-ph-evolution-phase2.md +351 -0
  30. package/docs/superpowers/specs/2026-06-12-ph-evolution-design.md +173 -0
  31. package/hooks/codex/README.md +54 -0
  32. package/hooks/codex/ph-hook.sh +14 -0
  33. package/hooks/opencode/ph-plugin.ts +60 -57
  34. package/package.json +13 -12
@@ -0,0 +1,109 @@
1
+ # SPEC-013: Importers (Claude / Gemini / OpenCode / Codex)
2
+
3
+ - **ID**: SPEC-013
4
+ - **Cluster**: Import
5
+ - **Status**: Implemented
6
+ - **Date**: 2026-08-08
7
+ - **Source**: reverse-engineered from code (files listed in §8)
8
+
9
+ ## 1. Purpose
10
+ Bootstrap history from existing AI CLI session stores: Claude Code (JSONL),
11
+ Gemini CLI (JSONL), OpenCode (SQLite at `~/.local/share/opencode/opencode.db`),
12
+ and Codex CLI rollout JSONL files under `~/.codex/sessions/`.
13
+ All imports route through the filter pipeline (SPEC-004) and can trigger inline
14
+ LLM analysis (`--analyze`).
15
+
16
+ ## 2. Scope
17
+ - **In scope**: four importer modules, import command flow, dedup, dry-run,
18
+ filter/analyze flags.
19
+ - **Out of scope**: real-time capture (SPEC-003), remote import.
20
+ - **Entry points**: `ph import gemini|claude|opencode|codex [--dry-run] [--analyze] [--filter]`; Codex also accepts `--file <transcript>`.
21
+
22
+ ## 3. Data Model
23
+ - `ImportResult` (src/types.ts:70-77): filesScanned, promptsFound,
24
+ promptsImported, skipped, filtered, errors.
25
+ - Sources: Claude JSONL sessions; Gemini session JSON; OpenCode `opencode.db`
26
+ (`session`/`message`/`part` tables; assistant→user link via `parentID` in
27
+ `message.data`); Codex rollout JSONL.
28
+
29
+ ## 4. Flows
30
+
31
+ ### 4.1 Claude/Gemini (src/importer/claude.ts, gemini.ts)
32
+ 1. Locate session stores in `~/.claude` / `~/.gemini` (JSONL).
33
+ 2. Parse pairs (prompt + response); apply filter pipeline; dedup vs existing
34
+ (by prompt hash or content).
35
+ 3. Normalize and insert via `createCaptureRecord` in PhDB; `--analyze` runs
36
+ inline analysis per imported prompt.
37
+
38
+ ### 4.2 OpenCode (src/importer/opencode.ts)
39
+ 1. Read `~/.local/share/opencode/opencode.db` (better-sqlite3).
40
+ 2. Pair user messages with assistant responses (parentID join).
41
+ 3. Dedup (existing prompt check), insert, optional analyze.
42
+ Reference run: 186 pairs found → 151 imported (AGENTS.md).
43
+
44
+ ### 4.3 Command flow (src/commands/import.ts)
45
+ `cmdImport(db, cfg, args)` dispatches by source; `--dry-run` reports counts
46
+ without inserting; `--filter` enables the pipeline; `--analyze` runs LLM
47
+ analysis inline.
48
+
49
+ ### 4.4 Codex (src/importer/codex.ts)
50
+ 1. Recursively scan `~/.codex/sessions/` for `rollout-*.jsonl`, or use the
51
+ transcript passed with `--file` by the Codex Stop hook.
52
+ 2. Read `session_meta` for session ID and working directory; pair legacy
53
+ `event_msg.user_message` records or modern `response_item` messages with
54
+ `role=user` with the following assistant response. Prefer assistant messages
55
+ with `phase=final_answer` over commentary when that phase is present.
56
+ 3. Insert through the normalized capture record and content dedup filter.
57
+
58
+ ## 5. Invariants & Business rules
59
+ - Imports never duplicate existing prompts (content-based dedup).
60
+ - `--dry-run` performs no writes.
61
+ - OpenCode source DB is opened read-only (never modified).
62
+ - Imported prompts carry the source tool name (`claude`/`gemini`/`opencode`/`codex`).
63
+ - Importers share the capture record shape and retain source-specific metadata.
64
+
65
+ ## 6. UI / UX surface
66
+ CLI summary: `Files scanned / prompts found / imported / skipped / filtered`.
67
+
68
+ ## 7. Acceptance criteria (Given/When/Then)
69
+ - **G1**: Given a Claude JSONL with one session, When `importer/claude` runs,
70
+ Then a prompt+response pair is produced (src/importer/claude.ts).
71
+ - **G2**: Given an already-imported prompt, When the importer runs again, Then
72
+ it is skipped (not duplicated) (src/importer/claude.ts dedup path).
73
+ - **G3**: Given `--dry-run`, When `cmdImport` runs, Then no rows are inserted
74
+ and counts are reported (src/commands/import.ts).
75
+ - **G4**: Given `--analyze`, When an import runs, Then analysis runs inline for
76
+ each imported prompt (SPEC-006).
77
+ - **G5**: Given `--filter`, When an import runs, Then FilterPipeline rejects
78
+ noise before insert (SPEC-004).
79
+ - **G6**: Given a Codex rollout with user and agent events, When the Codex
80
+ importer runs, Then it stores paired text with the session working directory
81
+ and does not duplicate the turn on a subsequent import.
82
+
83
+ ## 8. Key implementation map
84
+ | Concern | File(s) |
85
+ |---|---|
86
+ | Claude importer | src/importer/claude.ts |
87
+ | Gemini importer | src/importer/gemini.ts |
88
+ | OpenCode importer | src/importer/opencode.ts |
89
+ | Codex importer | src/importer/codex.ts |
90
+ | Command dispatch | src/commands/import.ts |
91
+ | Normalized insertion | src/capture/index.ts |
92
+
93
+ ## 9. Open questions / discrepancies
94
+ - Claude JSONL, Gemini session JSON, OpenCode SQLite, and Codex rollout fixtures verify
95
+ prompt/response pairing and imported workdir against a temporary `PhDB`.
96
+ - Claude/Gemini store discovery paths are version-specific — a CLI update can
97
+ break pairing silently (returns empty).
98
+ - OpenCode parentID pairing relies on `message.data` JSON shape; schema drift in
99
+ opencode.db breaks imports.
100
+ - Codex transcript paths and rollout formats are not stable hook interfaces;
101
+ unknown or malformed JSONL records are ignored. The importer supports both
102
+ `event_msg` user/agent messages and `response_item` user/assistant messages.
103
+ - Shared content dedup hashes prompt text across tools; repeated user prompts
104
+ from different tools collapse under this policy.
105
+
106
+ ## 10. Related
107
+ - SPEC-002 (insert), SPEC-004 (filter), SPEC-006 (analyze). Source-format
108
+ importer tests still need fixtures (SPEC-ISSUES-007); normalized record
109
+ behavior is covered in `src/capture/__tests__/record.test.ts`.
@@ -0,0 +1,109 @@
1
+ # SPEC-014: Analytics Commands (Sessions / Stats / Cluster / Timeline / Context)
2
+
3
+ - **ID**: SPEC-014
4
+ - **Cluster**: Analytics
5
+ - **Status**: Implemented (baseline)
6
+ - **Date**: 2026-08-08
7
+ - **Source**: reverse-engineered from code (files listed in §8)
8
+
9
+ ## 1. Purpose
10
+ Higher-level views over the history: work-session grouping (time gaps +
11
+ semantic cohesion), statistics, K-means clustering of prompts by embedding,
12
+ per-project timelines, and RAG context output.
13
+
14
+ ## 2. Scope
15
+ - **In scope**: `ph sessions`, `ph stats`, `ph cluster`, `ph timeline`,
16
+ `ph context`, `ph chat` (context injection).
17
+ - **Out of scope**: search (SPEC-005), embeddings (SPEC-008), memories
18
+ (SPEC-007).
19
+ - **Entry points**: the five commands above.
20
+
21
+ ## 3. Data Model
22
+ - `WorkSession` (src/sessions/index.ts:4): id, project, start/end, prompts,
23
+ cohesion score.
24
+ - `Stats` (src/stats/index.ts:4): totals, per-tool, per-role, per-project,
25
+ averages.
26
+ - Cluster: `{ centroid, entryIds }` (src/cluster/index.ts:20-21).
27
+
28
+ ## 4. Flows
29
+
30
+ ### 4.1 Sessions (src/sessions/index.ts:16-63)
31
+ `groupIntoSessions(entries, gapHours)` splits on idle gaps (default 2h);
32
+ `computeSessionCohesion` (:65) uses embedding similarity. CLI `ph sessions`
33
+ with `--gap-hours`, `--min-size`, `--no-cohesion`, `--export <n>` (markdown).
34
+
35
+ ### 4.2 Stats (src/stats/index.ts:16)
36
+ `getStats(db)` computes totals + breakdowns; `ph stats` prints a table.
37
+
38
+ ### 4.3 Cluster (src/cluster/index.ts)
39
+ K-means (maxIterations, convergence at cosine ≥ 0.9999) over embeddings;
40
+ `ph cluster -k N` prints the top prompts per cluster.
41
+
42
+ ### 4.4 Timeline (src/commands/timeline.ts)
43
+ `ph timeline <project>` interleaves prompts + memories chronologically
44
+ (`getAllPromptsByProject` :740, `getAllMemoriesByProject` :774).
45
+
46
+ ### 4.5 Context & chat
47
+ `getProjectContext` in `src/context/index.ts` retrieves the accumulated project
48
+ summary, recent memories, and recent prompts for CLI, chat, and MCP consumers.
49
+ An optional semantic query uses a configured embedder and project-scoped KNN.
50
+ `formatProjectContext` renders shared Markdown, with optional prompt text for
51
+ `ph context --verbose`. CLI flags can exclude prompt or memory sources.
52
+ `ph chat <tool> <prompt>` injects the same project context into a tool run.
53
+
54
+ ## 5. Invariants & Business rules
55
+ - Sessions split purely on time gaps (2h default) unless cohesion overrides.
56
+ - Cluster requires embeddings — without them the command errors/warns.
57
+ - Context output is markdown text (pipe-ready); memory section comes from
58
+ `memories` + `project_summaries`.
59
+ - CLI, chat, and MCP project context use the shared retrieval and Markdown
60
+ formatting boundary; evidence remains structured until rendering.
61
+ - Semantic retrieval with a project scope applies the project filter before
62
+ returning the requested number of results (SPEC-005).
63
+ - Timeline is ascending chronological (oldest → newest).
64
+
65
+ ## 6. UI / UX surface
66
+ CLI tables/markdown; TUI chat mode (C key, SPEC-012) launches the tool with
67
+ context injected.
68
+
69
+ ## 7. Acceptance criteria (Given/When/Then)
70
+ - **G1**: Given prompts 3h apart, When `groupIntoSessions(entries, 2)` runs,
71
+ Then two sessions are produced (src/sessions/index.ts:16-63).
72
+ - **G2**: Given prompts 1h apart, When `groupIntoSessions(entries, 2)` runs,
73
+ Then one session is produced (src/sessions/index.ts:16-63).
74
+ - **G3**: Given `getStats(db)`, When run, Then the returned Stats match the DB
75
+ contents (src/stats/index.ts:16).
76
+ - **G4**: Given identical embedding vectors, When k-means runs with k=1, Then a
77
+ single cluster contains all entries (src/cluster/index.ts:28-75).
78
+ - **G5**: Given `ph timeline ph`, When run, Then prompts and memories are
79
+ interleaved in ascending order (src/commands/timeline.ts,
80
+ db/index.ts:740-780).
81
+ - **G6**: Given a project with a summary, memories, and prompts, When
82
+ `getProjectContext` runs, Then it returns each evidence source for consumers
83
+ to render (src/context/index.ts).
84
+ - **G7**: Given excluded sources or no stored evidence, When shared context is
85
+ retrieved and rendered, Then excluded sources are omitted and empty context
86
+ is reported (src/context/index.ts).
87
+
88
+ ## 8. Key implementation map
89
+ | Concern | File(s) |
90
+ |---|---|
91
+ | Sessions + cohesion | src/sessions/index.ts:16-80 |
92
+ | Stats | src/stats/index.ts:16-60 |
93
+ | K-means cluster | src/cluster/index.ts:1-78 |
94
+ | Timeline | src/commands/timeline.ts |
95
+ | Context retrieval and formatting | src/context/index.ts |
96
+ | Context consumers | src/commands/context.ts, src/commands/chat.ts, src/mcp/server.ts |
97
+
98
+ ## 9. Open questions / discrepancies
99
+ - Cohesion scoring cost is O(n²) embeddings comparisons; large histories make
100
+ `ph sessions` slow (no cache).
101
+ - Cluster convergence threshold (0.9999) with 768-dim floats can oscillate —
102
+ maxIterations is the real bound.
103
+ - Project-scoped semantic lookup evaluates all vectors before filtering. See
104
+ SPEC-005 for the current cost and possible indexed alternatives.
105
+
106
+ ## 10. Related
107
+ - SPEC-005/008 (inputs), SPEC-007 (memory inputs), SPEC-012 (TUI chat),
108
+ SPEC-011 (MCP). Context retrieval GWT coverage is in
109
+ `src/context/__tests__/context.test.ts`.
@@ -0,0 +1,85 @@
1
+ # SPEC-015: Config & Env Overrides
2
+
3
+ - **ID**: SPEC-015
4
+ - **Cluster**: Config
5
+ - **Status**: Implemented (baseline)
6
+ - **Date**: 2026-08-08
7
+ - **Source**: reverse-engineered from code (files listed in §8)
8
+
9
+ ## 1. Purpose
10
+ Central configuration for ph: `~/.ph_config.json` with typed keys, `ph config
11
+ get/set`, and environment overrides (`PH_DB`, `PH_REMOTE_URL`, `GEMINI_API_KEY`).
12
+
13
+ ## 2. Scope
14
+ - **In scope**: PhConfig shape, load/save, `ph config` command, env precedence.
15
+ - **Out of scope**: per-command flags (each command parses its own).
16
+ - **Entry points**: `ph config get|set <key> <value>`, env vars.
17
+
18
+ ## 3. Data Model
19
+ - `PhConfig` (src/config/index.ts:5-22): geminiApiKey, dbPath, analyzeProvider,
20
+ ollamaUrl, ollamaModel, ollamaEmbedModel, filterMinLength, filterMinRelevance,
21
+ backgroundAnalysis, remoteUrl, remoteApiKey, remoteLastPush, remoteLastPull,
22
+ retentionDays, retentionMinStarred, retentionMinAnalyzed, retentionMinRelevance.
23
+ - Env: `PH_DB` (dbPath, cli.ts:157), `PH_REMOTE_URL` (remote.ts:6-8),
24
+ `GEMINI_API_KEY` (cli.ts:153-155).
25
+ - `load()` (src/config/index.ts:27-35): reads the file, defaults `{}` on
26
+ missing/corrupt. `save(cfg)` (:38-40) writes JSON.
27
+
28
+ ## 4. Flows
29
+
30
+ ### 4.1 Command (src/commands/config.ts)
31
+ `ph config set <key> <value>` — validates key exists in PhConfig, coerces
32
+ value (boolean/number), loads → updates → saves. `ph config get <key>` prints
33
+ the value; no key prints the whole config.
34
+
35
+ ### 4.2 Precedence
36
+ 1. Env beats config: PH_DB > cfg.dbPath > defaultPath; PH_REMOTE_URL >
37
+ cfg.remoteUrl.
38
+ 2. geminiApiKey in config is exported to `process.env.GEMINI_API_KEY` before
39
+ dispatch (cli.ts:153-155).
40
+ 3. `analyzeProvider` ('ollama' | 'gemini') picks the LLM provider
41
+ (SPEC-006 factory).
42
+
43
+ ## 5. Invariants & Business rules
44
+ - Unknown keys in `ph config set` are rejected (validated against PhConfig).
45
+ - load() never throws — corrupt config degrades to defaults.
46
+ - Save is full-file write (no partial merge).
47
+
48
+ ## 6. UI / UX surface
49
+ `ph config` CLI + Settings panel in the TUI (`o`, SPEC-012) which edits the same
50
+ file.
51
+
52
+ ## 7. Acceptance criteria (Given/When/Then)
53
+ - **G1**: Given `ph config set background-analysis true`, When run, Then the
54
+ config file contains `"backgroundAnalysis": true`
55
+ (src/commands/config.ts, config/index.ts:38-40).
56
+ - **G2**: Given a missing config file, When `load()` runs, Then `{}` is returned
57
+ without throwing (src/config/index.ts:27-35).
58
+ - **G3**: Given `ph config set bogus-key 1`, When run, Then the key is rejected
59
+ (src/commands/config.ts validation).
60
+ - **G4**: Given `PH_REMOTE_URL` env and a config `remoteUrl`, When
61
+ `remoteUrl()` runs, Then the env value wins (src/commands/remote.ts:6-8).
62
+ - **G5**: Given a config with `geminiApiKey`, When main() runs, Then
63
+ `process.env.GEMINI_API_KEY` is set (src/cli.ts:153-155).
64
+
65
+ ## 8. Key implementation map
66
+ | Concern | File(s) |
67
+ |---|---|
68
+ | PhConfig + load/save | src/config/index.ts:5-40 |
69
+ | get/set command | src/commands/config.ts |
70
+ | Env precedence | src/cli.ts:157, src/commands/remote.ts:6-8 |
71
+ | TUI settings panel | src/ui/BrowseApp.tsx (SettingsView) |
72
+
73
+ ## 9. Open questions / discrepancies
74
+ - `remoteLastPush` was missing from PhConfig (FIXED 2026-08-08, SPEC-ISSUES-003).
75
+ - `save()` writes the whole object — concurrent `ph` processes (e.g. a hook
76
+ logging while the user sets config) can clobber each other's writes (no
77
+ locking).
78
+ - Config comments in AGENTS.md describe defaults that live in code
79
+ (load-time defaults in cli.ts/commands) — the doc is authoritative for intent,
80
+ code for truth.
81
+
82
+ ## 10. Related
83
+ - SPEC-001 (load at startup), SPEC-004/006/009 (filter/analysis/retention keys),
84
+ SPEC-010 (remote keys). No tests (SPEC-ISSUES-007) — G1/G2/G4 are vitest
85
+ targets with a temp HOME.
@@ -0,0 +1,35 @@
1
+ # SPEC INDEX — ph
2
+
3
+ Spec-driven baseline for **ph** (Prompt History & Analysis — transparent
4
+ observability layer for AI CLI tools). Reverse-engineered from code on
5
+ 2026-08-08 with `path:line` evidence. Published as `@gi4nks/ph` (npm,
6
+ semantic-release + OIDC provenance).
7
+
8
+ Cluster map: Core (CLI/architecture) · Capture (wrapper/hooks/log) · DB (PhDB)
9
+ · Search (FTS/semantic) · AI (analysis/embeddings) · Memory (memories/summaries)
10
+ · Sync (server/remote) · MCP · TUI · Import · Analytics · Config.
11
+
12
+ ## Registry
13
+
14
+ | ID | Feature | Cluster | Status | Tests |
15
+ |----|---------|---------|--------|-------|
16
+ | SPEC-001 | Architecture & CLI dispatch | Core | **Implemented (baseline)** | — |
17
+ | SPEC-002 | Database layer (PhDB) | DB | **Implemented (baseline)** | — |
18
+ | SPEC-003 | Capture modes (pty/inline/hooks/log) | Capture | **Implemented (baseline)** | — |
19
+ | SPEC-004 | Filter pipeline & metadata | Core | **Implemented (baseline)** | — |
20
+ | SPEC-005 | Search (FTS + filters + semantic) | Search | **Implemented (baseline)** | — |
21
+ | SPEC-006 | Analysis pipeline (LLM) | AI | **Implemented (baseline)** | — |
22
+ | SPEC-007 | Memory & project summaries | Memory | **Implemented (baseline)** | — |
23
+ | SPEC-008 | Embeddings (sqlite-vec) | AI | **Implemented (baseline)** | — |
24
+ | SPEC-009 | Retention & archive | DB | **Implemented (baseline)** | — |
25
+ | SPEC-010 | Remote sync (server + push/pull) | Sync | **Implemented (baseline)** | — |
26
+ | SPEC-011 | MCP server | MCP | **Implemented (baseline)** | — |
27
+ | SPEC-012 | TUI browser | TUI | **Implemented (baseline)** | — |
28
+ | SPEC-013 | Importers (claude/gemini/opencode/codex) | Import | **Implemented** | import fixtures |
29
+ | SPEC-014 | Analytics commands (sessions/stats/cluster/timeline) | Analytics | **Implemented (baseline)** | — |
30
+ | SPEC-015 | Config & env overrides | Config | **Implemented (baseline)** | — |
31
+
32
+ ## Cross-cutting findings (SPEC-ISSUES.md)
33
+
34
+ See SPEC-ISSUES.md for code-vs-doc discrepancies and open questions collected
35
+ while reverse-engineering the baseline.
@@ -0,0 +1,122 @@
1
+ # SPEC-ISSUES — ph cross-cutting findings
2
+
3
+ Discrepancies, dead code, and open questions found while reverse-engineering
4
+ the ph baseline (2026-08-08). Each entry cites `path:line` evidence.
5
+ Status: Open → assigned SPEC / fixed.
6
+
7
+ ## Database
8
+
9
+ - **ISSUE-001 — `response` column created by ALTER, not CREATE TABLE** — the
10
+ `prompts` CREATE TABLE (src/db/index.ts:55-66) omits `response`; the column is
11
+ added later by a migration `ALTER TABLE prompts ADD COLUMN response`
12
+ (src/db/index.ts:143-146) that runs on every open. Works, but a fresh DB goes
13
+ through two steps and the schema is documented with `response` in CREATE
14
+ (AGENTS.md). → cleanup candidate.
15
+ - **ISSUE-002 — Private `db` accessed from server/remote (FIXED 2026-08-08)** —
16
+ `server/index.ts` used `db.db.prepare(...)` in 6 places and `remote.ts` in 1
17
+ (health, sync push/pull, stats, status). Fixed by adding public PhDB methods
18
+ (`getPromptCountSince`, `getStats`, `dbPath`) and reusing existing ones
19
+ (`getPromptBySyncHash`, `getPromptsSince`, `getAllPrompts`). Fixed.
20
+ - **ISSUE-003 — `remoteLastPush` undeclared in PhConfig (FIXED 2026-08-08)** —
21
+ `remote.ts` read/wrote `cfg.remoteLastPush` (push + status) while
22
+ `PhConfig` (src/config/index.ts:5-22) declared only `remoteLastPull`; the
23
+ field silently worked at runtime but was invisible to the type system. Fixed.
24
+
25
+ ## Types / build
26
+
27
+ - **ISSUE-004 — node-pty typings blocked by package exports (FIXED 2026-08-08)** —
28
+ `@lydell/node-pty` ships `node-pty.d.ts` but its package.json `exports` field
29
+ breaks resolution under `moduleResolution: Bundler` → TS7016. Fixed with a
30
+ minimal local declaration (src/pty/node-pty.d.ts) covering the used API. Fixed.
31
+ - **ISSUE-005 — TS6/@types-node-25 type breakage (FIXED 2026-08-08)** — the
32
+ TS 5.9→6.0 upgrade broke 12 sites (Float32Array<ArrayBufferLike>, readSync
33
+ 4-arg, insert() response, private db, node-pty). All fixed; tsc green. Fixed.
34
+ - **ISSUE-006 — eslint 10 unusable with eslint-plugin-react 7.37.5** — peer range
35
+ excludes eslint 10 (ERESOLVE). Resolved by pinning eslint back to ^9.39.4 and
36
+ adding the missing `@eslint/js` devDep (eslint.config.js imported it but it was
37
+ never in package.json → lint was completely broken). Fixed (downgrade).
38
+ - **ISSUE-007 — ZERO test files (FIXED 2026-10-09)** — baseline had no test
39
+ files. Tests now cover database, search, memory, archive, sync, config,
40
+ analysis, sessions, context, capture, TUI filter transitions, importer formats,
41
+ PTY response cleanup, HTTP endpoints, and MCP tool calls over an in-memory
42
+ protocol transport. The Ink browser also has a PTY smoke test that opens its
43
+ filter panel and exits cleanly; a nested PTY test verifies wrapped prompt and
44
+ ANSI-clean response persistence. Broader screen flows remain covered by
45
+ component/module tests rather than end-to-end navigation.
46
+ - **ISSUE-016 — NON_PRINTABLE pattern matched plain words (FIXED 2026-08-08)** —
47
+ the filter's non-printable regex `^[\x00-\x1f\x7f\x1b\[\]()#;\d;A-Za-z]*$`
48
+ matched ANY all-alphanumeric string (e.g. "yes") because of `[A-Za-z\d]` in
49
+ the class, classifying normal prompts as PTY noise. Fixed with a control-char
50
+ lookahead (src/filter/index.ts:32-35). Found by the SPEC-004 GWT suite. Fixed.
51
+ - **ISSUE-017 — trivial filler list missed an Italian thank-you phrase (FIXED 2026-08-08)** —
52
+ the Italian filler set lacked a common thank-you phrase;
53
+ added (src/filter/index.ts:25). Found by the SPEC-004 GWT suite. Fixed.
54
+ - **ISSUE-018 — semantic project filter ran after global KNN limit (FIXED 2026-10-09)** —
55
+ Context consumers fetched global nearest neighbors and filtered metadata
56
+ afterward, which could omit relevant entries from the requested project.
57
+ `PhDB.searchSemantic(query, limit, project)` now scopes results before the
58
+ final limit; project-scoped searches inspect all stored vectors because
59
+ sqlite-vec applies its KNN candidate limit before ordinary SQL filters.
60
+ Fixed; performance trade-off documented in SPEC-005.
61
+
62
+ ## Docs
63
+
64
+ - **ISSUE-008 — AGENTS.md stack table stale** — AGENTS.md says ink 6.8 / TS 5.9
65
+ / eslint 9; package.json has ink 7.0.5 / TS 6.0.3 / eslint 9 (after revert).
66
+ → docs update.
67
+ - **ISSUE-009 — `standard-version` still a devDep** — replaced by
68
+ semantic-release (AGENTS.md) but still listed in package.json devDependencies.
69
+ → chore cleanup.
70
+
71
+ ## Design smells
72
+
73
+ - **ISSUE-010 — cli.ts TUI bootstrap duplicated 3× (FIXED 2026-08-08)** —
74
+ extracted `openBrowser(db, props)` and `runRerun()` in `src/cli.ts`; default,
75
+ `search -i`, and `browse` now share the alt-screen cleanup path. Fixed.
76
+ - **ISSUE-011 — BrowseApp remains a large module** — detail, edit, rerun,
77
+ provider selection, settings, and app state remain in `src/ui/BrowseApp.tsx`.
78
+ Filter policy/count generation now lives in `src/ui/filtering.ts` and keyboard
79
+ rendering in `src/ui/FilterPanel.tsx`; filter transitions are directly tested
80
+ and the keyboard path to open/close the rendered filter panel has a PTY smoke
81
+ test. Detail/edit/rerun and other screen interactions remain in BrowseApp.
82
+ - **ISSUE-012 — sync_hash excludes args/workdir (FIXED 2026-08-08)** — dedup
83
+ hash was `sha256(tool|prompt|response)` duplicated inline in server
84
+ (src/server/index.ts:104) and remote (remote.ts:116) — drift risk. Now a
85
+ shared `syncHash` helper (src/utils/syncHash.ts) that includes `args` ONLY
86
+ when present — backward compatible: entries without args keep the legacy
87
+ hash (no mass re-push), entries with different args no longer collapse. Fixed.
88
+ - **ISSUE-013 — server auth is declared but never enforced (FIXED 2026-08-08)** —
89
+ `remoteApiKey` was sent by the client but the HTTP server never checked it.
90
+ Now `createRequestHandler` (exported for tests) gates every endpoint except
91
+ `/health` with `Authorization: Bearer <key>` (timingSafeEqual) when
92
+ `remoteApiKey` is configured (src/server/index.ts:30-60). Fixed.
93
+ - **ISSUE-014 — memories prompt_ids unused after summaries** — `prompt_ids` in
94
+ `memories` (src/db/index.ts:109) is written but the summary merge path
95
+ (project_summaries) tracks prompt_count instead; the timeline joins via
96
+ project name. → verify whether prompt_ids is still load-bearing.
97
+ - **ISSUE-015 — `printResults`/`printEntry` display layer** — src/display/print.ts
98
+ duplicates formatting logic that the TUI ListEntry also renders; two code paths
99
+ for entry display can drift (e.g. badges, truncation rules).
100
+
101
+ ## Issue-to-spec map
102
+
103
+ | Issue | Spec (fix target) | Status |
104
+ |---|---|---|
105
+ | 001 | SPEC-002 | Open (cleanup) |
106
+ | 002 | SPEC-002/010 | Fixed 2026-08-08 |
107
+ | 003 | SPEC-015 | Fixed 2026-08-08 |
108
+ | 004 | SPEC-003 | Fixed 2026-08-08 |
109
+ | 005 | SPEC-002 | Fixed 2026-08-08 |
110
+ | 006 | — (tooling) | Fixed 2026-08-08 (eslint 9 + @eslint/js) |
111
+ | 007 | SPEC-002/003/005/009/010/011/012/013/014 | Fixed 2026-10-09 (core terminal and TUI paths covered; other screen flows remain module-tested) |
112
+ | 008 | — (docs) | Open |
113
+ | 009 | — (chore) | Open |
114
+ | 010 | SPEC-001 | Fixed 2026-08-08 |
115
+ | 011 | SPEC-012 | Open |
116
+ | 012 | SPEC-010 | Fixed 2026-08-08 (shared syncHash, args-aware, backward compatible) |
117
+ | 013 | SPEC-010 | Fixed 2026-08-08 (auth gate on createRequestHandler) |
118
+ | 014 | SPEC-007 | Open |
119
+ | 015 | SPEC-012 | Open |
120
+ | 016 | SPEC-004 | Fixed 2026-08-08 |
121
+ | 017 | SPEC-004 | Fixed 2026-08-08 |
122
+ | 018 | SPEC-005/014 | Fixed 2026-10-09 |
@@ -0,0 +1,65 @@
1
+ # SPEC Template — use for every ph spec
2
+
3
+ > Copy this file to `docs/specs/SPEC-XXX-<slug>.md` and fill in. Keep it in **English**.
4
+ > Reverse-engineering rule: every claim must be traceable to code. When writing specs,
5
+ > cite `path:line` evidence. If behavior is ambiguous in code, record it in
6
+ > section 9 as an open question — never invent behavior.
7
+
8
+ ```markdown
9
+ # SPEC-XXX: <Feature Name>
10
+
11
+ - **ID**: SPEC-XXX
12
+ - **Cluster**: Core | Capture | DB | AI | Search | Memory | Sync | MCP | TUI | Import | Analytics | Config
13
+ - **Status**: Draft | In Review | Accepted | Implemented
14
+ - **Date**: 2026-08-08
15
+ - **Source**: reverse-engineered from code (files listed in §8)
16
+
17
+ ## 1. Purpose
18
+ Why this feature exists, the problem it solves.
19
+
20
+ ## 2. Scope
21
+ - **In scope**: …
22
+ - **Out of scope**: …
23
+ - **Entry points**: (ph commands, hooks, MCP tools, HTTP endpoints)
24
+
25
+ ## 3. Data Model
26
+ Entities and fields the feature reads/writes.
27
+
28
+ ## 4. Flows
29
+
30
+ ### 4.1 Happy path
31
+ Numbered, concrete steps.
32
+
33
+ ### 4.2 Edge cases & error handling
34
+ Known branches, validation failures, provider errors, concurrency notes.
35
+
36
+ ## 5. Invariants & Business rules
37
+ - Rules that MUST never be violated.
38
+
39
+ ## 6. UI / UX surface
40
+ TUI components, CLI output, keybindings.
41
+
42
+ ## 7. Acceptance criteria (Given/When/Then)
43
+ - **G1**: Given … When … Then …
44
+ - **G2**: …
45
+ - **G3**: …
46
+
47
+ ## 8. Key implementation map
48
+ | Concern | File(s) |
49
+ |---|---|
50
+
51
+ ## 9. Open questions / discrepancies
52
+ - Any spec-vs-code mismatch, dead code, or undocumented behavior discovered.
53
+
54
+ ## 10. Related
55
+ - Other SPEC ids, AGENTS.md notes, tests that exercise this feature.
56
+ ```
57
+
58
+ ## Rules of the road
59
+ 1. Each MUST have ≥3 GWT acceptance criteria (they become vitest wiring later).
60
+ 2. IDs are assigned in SPEC-INDEX.md — do NOT create new IDs.
61
+ 3. One feature per file; files go to `docs/specs/SPEC-XXX-<slug>.md`.
62
+ 4. Do not modify code. Read-only probe + `write_file` for the spec only.
63
+ 5. Baseline specs (SPEC-001…) describe ph **as it is today** (regression targets).
64
+ ph is an application: integration targets (harness-style) are not planned —
65
+ keep specs baseline-only unless a target spec is explicitly requested.