@gi4nks/ph 1.5.0 → 1.7.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 +9 -2
- package/LICENSE +1 -1
- package/README.md +155 -145
- package/dist/cli.js +2279 -1052
- package/dist/cli.js.map +1 -1
- package/docs/evals/context-retrieval-golden.json +26 -0
- package/docs/evals/context-retrieval.md +27 -0
- package/docs/hooks.md +13 -7
- package/docs/ph-analysis.html +145 -0
- package/docs/ph-manual.md +375 -373
- package/docs/specs/SPEC-001-architecture-cli.md +82 -0
- package/docs/specs/SPEC-002-database-layer.md +105 -0
- package/docs/specs/SPEC-003-capture-modes.md +110 -0
- package/docs/specs/SPEC-004-filter-metadata.md +82 -0
- package/docs/specs/SPEC-005-search.md +102 -0
- package/docs/specs/SPEC-006-analysis-pipeline.md +98 -0
- package/docs/specs/SPEC-007-memory-summaries.md +96 -0
- package/docs/specs/SPEC-008-embeddings.md +90 -0
- package/docs/specs/SPEC-009-retention-archive.md +91 -0
- package/docs/specs/SPEC-010-remote-sync.md +106 -0
- package/docs/specs/SPEC-011-mcp-server.md +94 -0
- package/docs/specs/SPEC-012-tui-browser.md +98 -0
- package/docs/specs/SPEC-013-importers.md +109 -0
- package/docs/specs/SPEC-014-analytics-commands.md +109 -0
- package/docs/specs/SPEC-015-config-env.md +85 -0
- package/docs/specs/SPEC-INDEX.md +35 -0
- package/docs/specs/SPEC-ISSUES.md +122 -0
- package/docs/specs/_TEMPLATE.md +65 -0
- package/docs/superpowers/plans/2026-06-12-ph-evolution-phase1.md +466 -0
- package/docs/superpowers/plans/2026-06-12-ph-evolution-phase2.md +351 -0
- package/docs/superpowers/specs/2026-06-12-ph-evolution-design.md +173 -0
- package/hooks/claude/README.md +1 -1
- package/hooks/codex/README.md +54 -0
- package/hooks/codex/ph-hook.sh +14 -0
- package/hooks/gemini/README.md +1 -1
- package/hooks/opencode/ph-plugin.ts +60 -57
- package/package.json +18 -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.
|