From fb302a033ee0b1d632938ed64e94ea7537e16be3 Mon Sep 17 00:00:00 2001 From: Alex Blank Date: Fri, 12 Jun 2026 12:04:28 +0200 Subject: [PATCH] docs: rewrite documentation system --- README.md | 216 +++++++------ SKILL.md | 147 +++++---- design-doc.md | 684 +++++++++++++++++------------------------ implementation-plan.md | 241 --------------- troubleshooting.md | 185 +++++++++++ usage-guide.md | 198 ++++++++++++ 6 files changed, 872 insertions(+), 799 deletions(-) delete mode 100644 implementation-plan.md create mode 100644 troubleshooting.md create mode 100644 usage-guide.md diff --git a/README.md b/README.md index 47089d5..4304a78 100644 --- a/README.md +++ b/README.md @@ -1,148 +1,174 @@ # pi-project-map -Pi skill for hierarchical project analysis. +> Pi skill and CLI for hierarchical project analysis. -## What it does +`pi-project-map` generates and maintains paired, machine-readable analysis artifacts throughout a codebase so agents can navigate quickly, orient themselves, and then verify details from source. -Generates **paired** project-analysis artifacts throughout your project: +## What it is -- `.pi-map.index.md` — routing-first index for deciding what to open next -- `.pi-map.md` — orientation-first rich map for understanding a directory +For every non-ignored directory, the tool produces two files: -This gives Pi agents fast navigation plus deeper architectural context without reading every source file up front. +| File | Purpose | +|------|---------| +| `.pi-map.index.md` | Routing-first index for deciding what to open next. | +| `.pi-map.md` | Orientation-first rich map for understanding a directory. | -## Quick Start +Together they form a **paired artifact model**: +- indexes are small and routing-optimized +- maps are denser and architecture-optimized +- source remains the final authority + +`pi-project-map` runs as both: +- a **CLI** (`project-map`) +- a **Pi extension** (`pi-extension.ts`) that registers tools and optional runtime prompt injection + +## Quick start + +Install: ```bash npm install -g pi-project-map +``` + +Generate paired artifacts in a repo: + +```bash +cd my-project project-map init ``` -## Agent operating model +For standalone CLI usage, provide an LLM provider/API key. For example: -### Tier 0 -Always start with: -- `Project Map Protocol` -- root `.pi-map.index.md` +```bash +export OPENAI_API_KEY=... +project-map init +``` -### Tier 1 -Load likely relevant directory indexes first, then open the strongest-match rich maps. +Inside Pi, the extension uses Pi's configured model automatically. -### Tier 2 -Read actual source, tests, config, and docs before editing or making exact runtime claims. +## Command overview -**Trust boundary:** index routes, map orients, source decides. +| CLI command | Pi tool | Purpose | +|-------------|---------|---------| +| `project-map init [path]` | `project_map_init` | Generate paired artifacts for the whole project or a subdirectory. | +| `project-map patch ` | `project_map_patch` | Regenerate artifacts for the directory containing the changed file and refresh ancestors appropriately. | +| `project-map validate [--fix]` | `project_map_validate` | Check paired artifacts for staleness or inconsistency. | +| `project-map reinit [path]` | `project_map_reinit` | Force full regeneration of all artifacts. | +| `project-map context ` | `project_map_context` | Return a ranked context bundle for a natural-language query. | -## Prompt Injection Policy +Typical workflow: -The Pi extension can automatically inject lightweight project-map guidance into the agent context according to the configured `promptInjectionMode`. +1. `project-map init` on first use +2. after editing source, `project-map patch ` +3. before broad architectural decisions, `project-map validate` +4. for targeted exploration, `project-map context ""` + +## Operating model + +Follow a three-tier model when consuming project maps: + +1. **Tier 0 — Protocol and root index** + Start with the root `Project Map Protocol` and root `.pi-map.index.md`. +2. **Tier 1 — Indexes and maps** + Use indexes to route, then open the strongest-match `.pi-map.md` files for orientation. +3. **Tier 2 — Source and tests** + Read actual source, config, tests, and docs before editing or making exact runtime claims. + +The trust boundary is always: + +> **index routes, map orients, source decides.** + +## Prompt injection policy + +The Pi extension can automatically inject lightweight project-map guidance into the agent context. Behavior is controlled by `promptInjectionMode` in `.pi-project-map.json`. ### Before init -When no `.pi-map.md` / `.pi-map.index.md` artifacts exist, only a visible startup hint is injected. It tells the agent that the project-map extension is active and to run `project_map_init`. No synthetic or fake map content is ever injected before real artifacts exist. + +No synthetic map content is injected. The agent sees only a visible startup hint telling it to run `project_map_init`. ### After init -Once real artifacts exist, the runtime guarantees that the root pair is loaded before any budgeted expansion: +The root pair is guaranteed to load first: - root `.pi-map.index.md` - root `.pi-map.md` -Additional directory pairs are expanded only while the configured context budget allows, in shallow-first order. - -### Trust boundary -Injected maps and indexes are navigation and orientation aids, not final authority: - -> **index routes, map orients, source decides** - -If an injected artifact and the source disagree, source wins. Always verify critical behavior from source before editing or making exact runtime claims. +Additional directory pairs are expanded only while the configured context budget allows. ### Mode ladder -- `off`: no automatic injection beyond existing tool/docs discovery. -- `advisory`: inject startup/init hints and allow optional root-pair preload; use light reminders. -- `strong` (default): inject the root pair, expand under the configured budget, run reinjection checks on relevant turns, and remind before edits or architecture-sensitive reasoning. -- `strict`: same as `strong`, plus require an explicit bypass justification before sensitive edits or architectural claims when the protocol path is missing. -The **protocol path** is present when the outgoing context contains the canonical injected root-pair block and the trust-boundary instruction. In `strict` mode, sensitive actions without it are blocked unless the agent includes `[PI_MAP_BYPASS: ]`. +| Mode | Behavior | +|------|----------| +| `off` | No automatic injection. | +| `advisory` | Visible hints/reminders only; maps are read manually. | +| `strong` | Root pair injection, budgeted expansion, reinjection on relevant turns. | +| `strict` | Same as `strong`, plus a visible guard for sensitive turns when the protocol path is missing. | + +The **protocol path** is present when outgoing context contains: +- the canonical root-pair marker/block +- the trust-boundary instruction + +In `strict` mode, a sensitive action can be bypassed explicitly with: + +```text +[PI_MAP_BYPASS: ] +``` ### Context budget -The default budget is **15% of the active model context window**, capped at **100k tokens**. The smaller of the relative and absolute values wins. If the runtime cannot discover the active model's context window, it falls back to the absolute cap. -Configure it in `.pi-project-map.json`: +Default automatic-injection budget is the smaller of: +- **15%** of the active model context window +- **100,000 tokens** absolute cap -```json -{ - "promptInjectionMode": "strong", - "contextBudgetPercent": 15, - "contextBudgetMaxTokens": 100000 -} -``` +If the runtime cannot discover the model context window, it falls back to the absolute cap. -## Retrieval -When you have a specific query (e.g. "authentication logic" or "routing metadata"): -1. Run `project-map context ` or use the Pi tool `project_map_context` -2. Read the returned **Context bundle** — it contains relevant indexes, maps, likely files, and symbols -3. Always verify critical behavior from source before editing +## Retrieval is separate -Retrieval via `project_map_context` (tool) or `project-map context` (CLI) remains a separate, on-demand path. Use it for targeted navigation when you have a specific query; automatic injection does not replace it. +`project-map context ` and `project_map_context` are **separate, on-demand retrieval** paths. They do **not** replace automatic prompt injection. -### Integration-test expectations and known limitations -The implementation is validated by integration tests covering: +Retrieval is deterministic and metadata-driven: +1. score every directory's paired map/index metadata against the query +2. keep the top matches (default: 3) +3. return a compact markdown bundle with indexes, maps, likely files, and symbols -- pre-init hint behavior, -- post-init root-pair preload, -- budgeted expansion behavior, -- canonical-marker dedupe, -- reinjection after compaction, -- reinjection after root-pair artifact changes, -- mixed visibility behavior, -- guidance-mode differences, -- strict-mode bypass behavior. +Use retrieval for targeted navigation when you already have a specific question. -Known limitations: - -- Token estimation is best-effort (≈ 4 chars per token); actual provider token counts may differ. -- Relevant-turn detection uses explicit event types when available and falls back to heuristics on generic turns. -- Message-layer scanning is preferred; provider-payload serialization quirks require a fallback scan path. - -## Commands - -```bash -project-map init -project-map patch -project-map validate [--fix] -project-map reinit -project-map context -``` - -### Context retrieval - -`project-map context ` searches the paired map/index artifacts and returns a compact markdown bundle with the most relevant directories, files, and symbols. No LLM call is needed — it uses deterministic metadata scoring. - -## Configuration +## Configuration overview Create `.pi-project-map.json` in the project root: ```json { - "ignorePatterns": ["node_modules", ".git"], - "smallPackageThreshold": 10, - "contextBudget": 4000, - "autoInjectPrompt": true, - "tagCap": 8, - "workflowHintCap": 5, "promptInjectionMode": "strong", "contextBudgetPercent": 15, - "contextBudgetMaxTokens": 100000 + "contextBudgetMaxTokens": 100000, + "llmProvider": "openai", + "llmModel": "gpt-4o-mini", + "ignorePatterns": ["node_modules", ".git"], + "tagCap": 8, + "workflowHintCap": 5 } ``` -- `promptInjectionMode`: `off`, `advisory`, `strong` (default), or `strict`. -- `contextBudgetPercent`: relative share of the active model context window used for automatic map/index injection. -- `contextBudgetMaxTokens`: hard absolute cap on the injection budget. +Providing `ignorePatterns` replaces the built-in default list, so include any defaults you want to keep. -## Design +Key knobs: +- `promptInjectionMode` — `off`, `advisory`, `strong`, `strict` +- `contextBudgetPercent` — relative share of model context used for automatic injection +- `contextBudgetMaxTokens` — hard absolute cap on automatic injection +- `llmProvider` / `llmModel` / `llmBaseUrl` — standalone CLI provider settings +- `ignorePatterns` — directories/files to skip +- `tagCap` / `workflowHintCap` — routing metadata limits -See [design-doc.md](design-doc.md) for the full specification. +## Documentation map -## Implementation Plan +- [`SKILL.md`](SKILL.md) — skill definition and agent/operator instructions +- [`usage-guide.md`](usage-guide.md) — practical workflows and examples +- [`design-doc.md`](design-doc.md) — architecture and implementation details +- [`troubleshooting.md`](troubleshooting.md) — common issues and recovery steps -See [implementation-plan.md](implementation-plan.md) for the engineering roadmap. +## Known limitations + +- token budgeting is best-effort, not tokenizer-exact +- relevant-turn detection uses explicit event types plus heuristics +- provider payload fallback depends on runtime serialization shapes +- retrieval routes and orients; it never replaces source verification diff --git a/SKILL.md b/SKILL.md index 98d1a88..9c1cf9c 100644 --- a/SKILL.md +++ b/SKILL.md @@ -1,71 +1,93 @@ --- name: pi-map -description: Generates and maintains hierarchical, machine-readable paired project analysis artifacts (.pi-map.index.md and .pi-map.md) for fast codebase navigation and orientation. +description: Generates and maintains hierarchical paired project-analysis artifacts (`.pi-map.index.md` + `.pi-map.md`) so Pi agents can navigate and orient in a codebase without reading every source file. --- # pi-project-map -A Pi skill that generates and maintains a paired analysis for each non-ignored directory: +A Pi skill that creates and maintains one paired analysis artifact set per non-ignored directory: -- `.pi-map.index.md` for routing -- `.pi-map.md` for orientation +- `.pi-map.index.md` — routing-first index +- `.pi-map.md` — orientation-first rich map -## What It Does +## What it does -- **Scans** your project and creates one paired map/index artifact set per directory -- **Extracts** exports, imports, and dependencies via AST parsing and LLM heuristics -- **Updates** generated artifacts after source edits -- **Validates** stale, missing, broken, or inconsistent paired artifacts -- **Retrieves** relevant context on demand via deterministic metadata scoring over the paired artifacts +- scans the project and emits one paired map/index set per directory +- extracts per-file purpose, dependencies, and concepts via an LLM +- extracts exact exports/imports via AST parsing where supported +- patches artifacts incrementally after source edits +- validates stale, missing, broken, or inconsistent paired artifacts +- retrieves relevant context on demand via deterministic metadata scoring +- injects lightweight project-map guidance into agent context according to a configurable mode ladder -## Quick Start +## Operating model -```bash -project-map init -project-map patch src/components/Button.tsx -project-map validate -project-map reinit -project-map context "authentication logic" -``` +### Tier 0 — protocol +Always read the root `.pi-map.index.md` and the `Project Map Protocol` first. -## Operating Model +### Tier 1 — routing +Use indexes first to decide where to go next. Open the strongest-match `.pi-map.md` files for orientation. -### Tier 0 -Read the root `.pi-map.index.md` and the `Project Map Protocol` first. +### Tier 2 — source +Read actual source, tests, config, and docs before editing or asserting exact runtime behavior. -### Tier 1 -Use indexes first for routing. Open the strongest-match `.pi-map.md` files next. +**Trust boundary:** -### Tier 2 -Read actual source before editing or asserting exact behavior. +> **index routes, map orients, source decides** -**Trust boundary:** index routes, map orients, source decides. +## Agent instructions -## Prompt Injection Policy +When project-map artifacts exist in the repo: -This skill can automatically inject lightweight project-map guidance into your context. The behavior is controlled by `promptInjectionMode` in `.pi-project-map.json`. +1. start with the root `.pi-map.index.md` and the `Project Map Protocol` +2. use indexes first to route into the right directory +3. read the local `.pi-map.md` plus relevant source before editing +4. run `project_map_patch ` (tool) or `project-map patch ` (CLI) after each source edit +5. run `project_map_validate` (tool) or `project-map validate` (CLI) before freshness-sensitive architectural decisions or final handoff +6. for targeted exploration, use `project_map_context ` (tool) or `project-map context ` (CLI) +7. in `strict` mode, only bypass the protocol-path guard with an explicit marker: `[PI_MAP_BYPASS: ]` + +## Prompt injection modes + +The Pi extension can inject project-map guidance automatically. Behavior is controlled by `promptInjectionMode` in `.pi-project-map.json`. ### Before init -When no `.pi-map.md` / `.pi-map.index.md` artifacts exist, you see a visible startup hint telling you to run `project_map_init`. No synthetic map content is injected before real artifacts exist. +If no `.pi-map.md` / `.pi-map.index.md` artifacts exist, the extension emits a lightweight visible hint to run `project_map_init`. No synthetic or fake map content is injected. ### After init -Once artifacts exist, the runtime guarantees that the root pair is loaded first: +Once real artifacts exist, the runtime guarantees that the **root pair** is loaded first: - root `.pi-map.index.md` - root `.pi-map.md` -Additional directory pairs may be added while the configured context budget allows. +Additional directory pairs may be expanded while the configured context budget allows, in shallow-first order. ### Mode ladder -- `off`: no automatic injection beyond existing tool/docs discovery. -- `advisory`: startup/init hints are shown; you may read the root pair manually when you want routing/orientation context. -- `strong` (default): the root pair is injected automatically, expansion stays within the context budget, and reinjection checks run on relevant turns (agent start, before edits, before architecture-sensitive reasoning, after compaction, after root-pair artifact changes). -- `strict`: same as `strong`, but before sensitive edits or architectural claims you must either have the protocol path in context or include an explicit bypass marker: `[PI_MAP_BYPASS: ]`. -The **protocol path** means the outgoing context contains the canonical injected root-pair block and the trust-boundary instruction (`index routes, map orients, source decides`). +| Mode | Behavior | +|------|----------| +| `off` | No automatic injection. Use tools/CLI manually. | +| `advisory` | Startup/init hints are shown. Root pair is not auto-loaded; read it manually when needed. | +| `strong` (default) | Root pair is auto-loaded, expansion stays within budget, and reinjection runs on relevant turns. | +| `strict` | Same as `strong`, but sensitive edits or architectural claims are guarded unless the protocol path is present or a bypass marker is provided. | + +The **protocol path** means the outgoing context contains the canonical injected root-pair block and the trust-boundary text. ### Context budget -Default budget: **15% of the active model context window**, capped at **100k tokens**. The smaller of the relative and absolute values wins. If the runtime cannot discover the active model's context window, it uses the absolute cap. +Default budget: **15% of the active model context window**, capped at **100k tokens**. The smaller of the relative and absolute values wins. If the runtime cannot discover the context window, it uses the absolute cap. + +## Retrieval usage + +`project_map_context` (tool) and `project-map context` (CLI) are **on-demand retrieval**, separate from automatic injection. + +They do not call the LLM. They score paired metadata against the query and return a compact markdown bundle with: + +- relevant indexes +- relevant maps +- likely files +- relevant symbols when useful + +Always read the suggested indexes first, then maps, then verify critical behavior from source. ## Configuration @@ -73,41 +95,32 @@ Create `.pi-project-map.json` in the project root: ```json { - "tagCap": 8, - "workflowHintCap": 5, "promptInjectionMode": "strong", "contextBudgetPercent": 15, - "contextBudgetMaxTokens": 100000 + "contextBudgetMaxTokens": 100000, + "tagCap": 8, + "workflowHintCap": 5, + "llmProvider": "openai", + "llmModel": "gpt-4o-mini", + "ignorePatterns": ["node_modules", ".git", "dist", "build"] } ``` -- `promptInjectionMode`: `off`, `advisory`, `strong` (default), or `strict`. -- `contextBudgetPercent`: relative share of the active model context window to use for automatic map/index injection. -- `contextBudgetMaxTokens`: hard absolute cap on the injection budget. +Providing `ignorePatterns` replaces the built-in default list, so include any defaults you want to keep. -## Agent Instructions +Knobs that matter in practice: +- `promptInjectionMode` — `off` | `advisory` | `strong` | `strict` +- `contextBudgetPercent` / `contextBudgetMaxTokens` — caps automatic map/index injection +- `tagCap` / `workflowHintCap` — caps routing metadata per directory +- `llmProvider` / `llmModel` / `llmBaseUrl` — standalone CLI only +- `ignorePatterns` — discovery exclusions -When project map artifacts exist in the repo: +## Tools and commands -1. Start with the root `.pi-map.index.md` -2. Use indexes first to route into the right directory -3. Read the local `.pi-map.md` plus source before editing -4. Run `project-map patch ` after editing source -5. Run `project-map validate` before freshness-sensitive architectural decisions -6. In `strict` mode, include `[PI_MAP_BYPASS: ]` only when you deliberately need to proceed without the protocol path -7. For **targeted navigation**, use `project_map_context` (Pi tool) or `project-map context` (CLI) with a natural-language query. It returns a compact markdown bundle with the strongest-match indexes, maps, likely files, and symbols. - -## Retrieval Model - -`project_map_context` and `project-map context` implement **index-first retrieval**: - -1. Score every directory's paired map/index metadata against the query -2. Keep the top 3 strongest matches -3. Expand those matches into: - - Relevant indexes (routing-first) - - Relevant maps (orientation-first) - - Likely files - - Relevant symbols (only when useful) -4. Return a stable markdown bundle titled `# Context bundle: ` - -**Trust boundary still applies:** the bundle routes and orients, but source decides. Always read actual source before editing. +| Tool (Pi) | CLI command | Purpose | +|-----------|-------------|---------| +| `project_map_init` | `project-map init [path]` | Generate all paired artifacts. | +| `project_map_patch` | `project-map patch ` | Regenerate the pair for the changed file's directory and refresh ancestors. | +| `project_map_validate` | `project-map validate [--fix]` | Check paired artifacts for staleness and discrepancies; optionally repair. | +| `project_map_reinit` | `project-map reinit [path]` | Force full regeneration. | +| `project_map_context` | `project-map context ` | Retrieve a ranked context bundle for a natural-language query. | diff --git a/design-doc.md b/design-doc.md index fe2cf65..f7e3983 100644 --- a/design-doc.md +++ b/design-doc.md @@ -1,441 +1,333 @@ -# Design Doc: Hierarchical Project Analysis Skill for Pi +# Design Reference: pi-project-map -## 1. Goals and Success Criteria +> Audience: maintainers and contributors. +> Purpose: explain how `pi-project-map` works internally, not how to install or use it. -### Primary Goal -Enable a Pi coding agent to understand a software project's architecture and code relationships without scanning the entire repository. The agent should have a compact, hierarchical "internal representation" of the project that it can consume in-context. +## 1. Overview -### Success Criteria -- The agent can orient itself in a new or familiar project without reading dozens of source files. -- The agent understands cross-package dependencies, data flows, and architectural patterns from the analysis files alone. -- Analysis files stay sufficiently fresh that the agent does not make decisions based on stale information. -- The representation is token-dense: maximum information per token, optimized for LLM consumption, not human readability. +`pi-project-map` is a TypeScript/Node.js skill package that generates and maintains hierarchical, paired project-map artifacts for AI coding agents: -## 2. Format Specification: Dense Markdown with Conventions +- `.pi-map.index.md` — routing-first, sparse directory metadata +- `.pi-map.md` — orientation-first, richer directory metadata -### Design Rationale -- **Not JSON/YAML**: Brackets, quotes, and indentation add token overhead with no benefit to LLM comprehension. -- **Not a custom DSL**: Fragile, requires a parser, and LLMs may hallucinate syntax. -- **Dense markdown**: Hierarchical headings, bullet points, and abbreviations are natively understood by LLMs and extremely token-efficient. +It runs as both: +- a standalone CLI (`project-map`) +- a Pi extension (`pi-extension.ts`) -### Structure -Each non-ignored directory in the project gets **two** hidden analysis files: +The extension registers tools and event hooks that keep the artifacts fresh and can inject them into agent context at runtime. -- `.pi-map.index.md` — routing-first index -- `.pi-map.md` — orientation-first rich map +### Core design principle -```markdown -# (index) -dir: -## role - -## parent - -## children - -## files - -## links -index: -map: -## workflows - route hints> -## dirty - -``` +The artifacts are **navigation aids, not source-of-truth**. Source code is always the final authority. -```markdown -# -dir: -index: -## role - -## files -- | | exp: | dep: -## arch - -## tags - -## symbols - -## workflows - -## dirty - -``` +> **index routes, map orients, source decides.** -### Abbreviation Conventions -| Abbreviation | Meaning | -|-------------|---------| -| `exp:` | exported symbols (functions, classes, types, constants) | -| `dep:` | dependencies (other packages, files, or external libs) | -| `pkg/` | project-internal package reference | -| `ext/` | external dependency reference | -| `->` | data flow direction | -| `|` | field delimiter within a line | +## 2. Artifact model -### Example +Each non-ignored directory receives a matched pair. -```markdown -# pkg/auth -## role -Auth layer: JWT issuance, validation, refresh. Stateless. Dep: pkg/crypto, pkg/db. -## files -- tokens.ts | JWT gen/val | exp: issueToken, verifyToken, refreshToken | dep: crypto/hmac, db/sessions -- middleware.ts | HTTP auth guard | exp: requireAuth, requireRole | dep: tokens/verifyToken -- types.ts | shared auth types | exp: AuthToken, UserClaims, Role -## arch -Guard pattern on routes. Tokens short-lived (15m), refresh long-lived (7d). Rotation on every use. -Session state stored in Redis via db/sessions. No server-side JWT storage. -## dirty -- -``` +### 2.1 Shared model -### Rules -- One file per directory, placed inside that directory. -- Every non-excluded file in the directory gets one bullet under `## files`. -- Subdirectories are referenced in `## role` via `Dep:` or in `## arch` as structural notes, not duplicated. -- The `## dirty` section is empty (`-`) when clean, or contains a timestamp/flag when stale. +Both files are generated from the same in-memory `DirectoryArtifactModel`: -## 3. Pipeline Architecture +```ts +interface DirectoryArtifactModel { + dir: string; + role: string; + files: FileEntry[]; + arch: string; + dirty?: string; + isRoot: boolean; + parent?: string; + children: string[]; + tags: string[]; + symbols: string[]; + workflows: WorkflowHint[]; +} -### Hybrid Extraction: LLM + AST - -Two independent extraction layers contribute to the same output file. - -#### Layer 1: LLM-Based Extraction (All Files) -- **Input**: Raw file contents of every non-excluded file in the directory. -- **Output**: Purpose description, architectural role, and cross-file relationships. -- **Applies to**: Code files, config files, Dockerfiles, READMEs, YAML, JSON, shell scripts — everything. -- **When it runs**: Once per file during init; again on changed files during patching. -- **Implementation**: Calls an actual LLM (not regex heuristics). Inside Pi, it uses Pi's built-in LLM via the ExtensionAPI. Standalone CLI falls back to an external LLM API (OpenAI-compatible). - -#### Layer 2: AST-Based Extraction (Code Files Only) -- **Input**: Source code of files where a tree-sitter or LSP parser is available. -- **Output**: Precise symbol lists (functions, classes, types), signatures, import/export graphs, class hierarchies. -- **Applies to**: Supported languages only (TypeScript, Python, Go, Rust, etc.). -- **When it runs**: Once per file during init; again on changed files during patching. - -#### Merging -The two layers merge into a single line per file under `## files`: - -``` -- tokens.ts | JWT gen/val | exp: issueToken, verifyToken, refreshToken | dep: crypto/hmac, db/sessions - ^ LLM ^ LLM ^ AST ^ AST + LLM -``` - -- File name and purpose: LLM. -- Exported symbols and signatures: AST (augmented by LLM if AST unavailable). -- Dependency list: AST for imports; LLM for inferred architectural dependencies. - -### LLM Client Architecture - -The LLM client is abstracted behind a unified interface: - -```typescript -interface LLMClient { - complete(prompt: string): Promise; +interface FileEntry { + name: string; + purpose: string; + exports: string[]; + deps: string[]; } ``` -Two implementations: +### 2.2 `.pi-map.md` (rich map) -1. **PiLLMClient** (Pi extension): Uses `ctx.model` or `ctx.modelRegistry` to invoke Pi's configured LLM. Called from `pi-extension.ts` when the skill runs inside Pi. -2. **ExternalLLMClient** (standalone CLI): Calls an external OpenAI-compatible API. Configured via environment variable (e.g., `OPENAI_API_KEY`) or config file. +Rendered by `src/format.ts` → `renderDirectoryMap()`. -### Caching +Contains: +- `dir:` line and sibling `index:` link +- `Project Map Protocol` (root only) +- `## role` +- `## files` +- `## arch` +- `## tags` +- `## symbols` +- `## workflows` +- `## dirty` -LLM results are cached to avoid re-querying unchanged files. +### 2.3 `.pi-map.index.md` (index) -- **Key**: SHA-256 hash of file contents. -- **Storage**: JSON file at `~/.cache/pi-project-map/llm-cache.json`. -- **Behavior**: Before calling the LLM, compute the file hash and check the cache. If hit, reuse the cached result. If miss, call the LLM and store the result. -- **Invalidation**: Cache entries are implicitly invalidated when the file content changes (because the hash changes). There is no TTL; the cache is append-only. +Rendered by `src/format.ts` → `renderDirectoryIndex()`. -### Parallelization and Rate Limiting +Contains: +- same protocol (root only) +- `## role` +- `## parent` +- `## children` +- `## files` +- `## links` +- `## workflows` +- `## dirty` -- **Concurrency**: 4-8 LLM calls in parallel, controlled by `p-limit`. -- **Batch delays**: A small delay (e.g., 100ms) is inserted between batches to avoid triggering rate limits. -- **Retry policy**: Each LLM call retries up to 3 times with exponential backoff (1s, 2s, 4s). If all retries fail, the entire operation stops with a hard error. +### 2.4 Why a paired format? -### Error Handling +- **indexes are cheap** — many can be loaded without consuming much context +- **maps are dense** — loaded only after an index suggests relevance +- **paired generation** guarantees structural consistency -- **Hard error on failure**: If an LLM call fails after all retries, `init` or `patch` stops immediately and prints a clear error. There is no heuristic fallback. The user must resolve the issue (set API key, wait for rate limit, check network). -- **Context limit protection**: Files larger than the LLM's context window are truncated from the end (with a note in the prompt) before being sent. +## 3. High-level architecture -### Init Pipeline -``` -For each directory (depth-first): - 1. List all non-excluded files. - 2. For each file (parallel, 4-8 concurrent): - a. Compute SHA-256 of file contents. - b. Check disk cache. If hit, use cached result. - c. If miss: call LLM (with retries/backoff) to extract purpose and role. - d. Store result in cache. - e. If code file + parser available: run AST extraction (symbols, imports). - 3. Merge per-file outputs into lines. - 4. Run LLM on merged lines + directory context to generate: - - `## role` (package-level summary) - - `## arch` (architectural notes) - 5. Write `.pi-map.md` to directory. +### 3.1 Main modules + +```text +discover → directory tree, .gitignore-aware +init → full generation +llm-extract → LLM-based file/package analysis +ast-extract → tree-sitter parsing +merge → combine LLM + AST into FileEntry +routing-metadata → tags, symbols, workflow hints +format → render / parse the markdown pair +patch → incremental update after edits +validate → consistency checking with optional repair +retrieve → deterministic query scoring +prompt-injection → runtime context policy +pi-extension → Pi tool/event registration +cli → standalone command dispatcher +config → defaults and .pi-project-map.json loader ``` -### Patch Pipeline -``` -When agent edits file(s): - 1. Classify patch mode: auto, small, or structural. - 2. Always regenerate the changed directory pair: - - `.pi-map.md` - - `.pi-map.index.md` - 3. For small changes: refresh ancestor indexes. - 4. For structural changes: refresh ancestor map/index pairs. - 5. Use `validate --fix` to repair affected chains when paired artifacts are stale or missing. +### 3.2 Runtime modes + +| Mode | Entry point | LLM client | +|------|-------------|------------| +| Pi extension | `pi-extension.ts` | `PiLLMClient` via Pi runtime | +| Standalone CLI | `src/cli/cli.ts` | `ExternalLLMClient` or `KimiLLMClient` | + +## 4. Extraction pipeline + +### 4.1 Discovery + +`src/discover.ts` walks the filesystem with `ignore`, merging built-in exclusions and `.gitignore`. + +### 4.2 Per-directory generation + +`src/init.ts` → `generateDirectoryArtifacts()`: + +1. `processFiles()` runs in parallel over directory files +2. for each file: + - `extractFileLLM()` gets `purpose`, `deps`, `concepts` + - `extractFileAST()` gets exports/imports/calls where possible + - `mergeFileData()` combines both into a `FileEntry` +3. `extractPackageLLM()` produces directory `role` and `arch` +4. `createDirectoryModel()` builds the shared model +5. `populateRoutingMetadata()` derives `tags`, `symbols`, `workflows` +6. `writeDirectoryArtifacts()` writes both `.pi-map.md` and `.pi-map.index.md` + +Directories are processed sequentially; files within a directory are processed concurrently. + +### 4.3 LLM extraction + +`src/llm/llm-extract.ts`: +- prompts are minimal and line-oriented +- binary files are skipped +- files over 500KB are labeled large and skipped +- source is truncated before prompting +- results are cached by SHA-256 of file content +- missing client throws `LLMError` + +### 4.4 AST extraction + +`src/ast/ast-extract.ts` uses `tree-sitter` for supported languages to extract: +- imports / requires +- exported classes, functions, constants +- methods, parameters, return types +- direct calls and raised exceptions + +Unsupported languages fall back to LLM-only extraction. + +### 4.5 Merging + +`src/merge.ts`: +- purpose/concepts come from the LLM +- exports come from AST when available +- rich AST symbols are encoded as compact DSL: + - `class:Foo` + - `method:bar(a: string) → number` + - `call:baz` + - `raise:Error` +- deps are deduplicated union of AST + LLM deps + +### 4.6 Routing metadata + +`src/routing-metadata.ts` generates deterministic metadata used by retrieval and injection: +- **tags** +- **symbols** +- **workflow hints** + +Caps are configurable via `tagCap` and `workflowHintCap`. + +## 5. Patch / validate / reinit behavior + +### 5.1 Patch + +`src/patch.ts`: +1. resolve directory containing changed file +2. rediscover project tree +3. regenerate changed directory pair +4. refresh ancestors according to patch mode + +Patch mode: +- **small** — refresh ancestor indexes only +- **structural** — refresh ancestor map/index pairs + +### 5.2 Validate + +`src/validate.ts` compares artifacts against filesystem and AST. + +Important discrepancy types: +- `missing` +- `orphaned` +- `stale-signature` +- `dirty` +- `stale-map` +- `stale-index` +- `broken-link` +- `structural` + +With `--fix`, validate builds a repair plan and regenerates directories deepest-first. + +### 5.3 Reinit + +`reinitPath()` is the blunt instrument for widespread staleness. + +## 6. Retrieval architecture + +`src/retrieve.ts` implements deterministic, index-first context retrieval. + +1. walk the project for paired artifacts +2. parse indexes/maps into `DirectoryArtifactModel` +3. normalize the query +4. score every directory +5. return top-K (default: 3) as a markdown bundle with: + - relevant indexes + - relevant maps + - likely files + - relevant symbols + - instructions to verify from source + +No LLM is used during retrieval. It is intentionally separate from automatic prompt injection. + +## 7. Prompt injection architecture + +`src/prompt-injection.ts` and `pi-extension.ts` implement runtime guidance injection. + +### 7.1 Mode ladder + +| Mode | Behavior | +|------|----------| +| `off` | No automatic injection | +| `advisory` | Visible startup/init hints; no artifact preload | +| `strong` (default) | Root pair preloaded, budgeted expansion, reinjection on relevant turns | +| `strict` | Same as strong, plus bypass guard for sensitive edits/architecture reasoning without protocol path | + +### 7.2 Event hooks + +The extension currently registers: +- `session_start` +- `before_agent_start` +- `context` + +Payload fallback scanning is handled inside `context`-level decision logic; there is no separately registered `before_provider_request` hook in the current implementation. + +### 7.3 Reinjection policy + +`shouldReinjectForEvent()` decides whether to inject: +- only active in `strong` or `strict` +- skips if the canonical marker is already present in outgoing messages or payload +- triggers on: + - `agent_start` + - `edit_intent` + - `architecture_sensitive` + - `compaction` + - `artifact_change` +- `artifact_change` always forces reinjection + +`detectEditIntent()` and `detectArchitectureSensitiveReasoning()` provide heuristic fallback for generic turns. + +### 7.4 Protocol path and strict bypass + +The **protocol path** is present when outgoing context contains: +1. the canonical root-pair marker/block +2. the trust-boundary text + +In `strict` mode, a sensitive turn without the protocol path is blocked with a visible guard. The agent can override with: + +```text +[PI_MAP_BYPASS: brief justification] ``` -## 4. LLM Prompt Design +Empty or whitespace reasons are rejected. -### File-Level Prompt +### 7.5 Budgeted expansion -The LLM prompt for a single file is designed to produce a structured, concise analysis. +`buildInjectionPayload()`: +- computes budget as `min(relative, absolute)` +- default is 15% of context window, capped at 100k tokens +- always includes the root pair +- adds additional pairs shallow-first until budget is exhausted +- prepends a maintenance reminder -``` -You are analyzing a source file for a project map. Read the file below and summarize: +Token estimation is best-effort: `ceil(char_count / 4)`. -1. PURPOSE: What does this file do? Describe its role in the project (2-3 sentences max). -2. DEPENDENCIES: What does this file depend on? List internal modules/packages and external libraries. -3. KEY CONCEPTS: Mention any important patterns, algorithms, or domain concepts. +### 7.6 Context-window discovery -File path: +`discoverContextWindow()` inspects the Pi runtime model for context metadata and falls back to the absolute cap when unavailable. -``` - -``` +## 8. Known limits and tradeoffs -Respond in this exact format: -PURPOSE: -DEPS: -CONCEPTS: -``` +### Correctness vs cost +- init/patch/repair make LLM calls +- large repositories can be expensive +- caching reduces duplicate work -### Package-Level Prompt +### AST coverage +- TypeScript/TSX, Python, and Go have the richest support +- other languages may be partial or LLM-only -After all file summaries are collected for a directory, a second LLM call synthesizes the package role and architecture. +### Token estimation +- 4 chars/token is only a heuristic +- oversized files may be truncated or skipped -``` -You are analyzing a directory in a software project. Below is a list of files in this directory with their purposes. +### Staleness +- there is no filesystem watcher +- maps go stale when edits happen outside the patch flow +- validate detects but does not prevent staleness -Directory: -Files: -- : -- : -... +### Patch mode inference +- auto-mode heuristics are good but imperfect +- contributors can force structural mode when needed -Respond in this exact format: -ROLE: -ARCH: <2-4 sentences describing architecture, data flow, patterns, and design decisions> -``` +### Strict mode ergonomics +- strict guards can be surprising on casual phrasing +- bypass markers are intentionally explicit and user-visible -### Output Parsing +### Retrieval scoring +- deterministic scoring is reproducible but not semantic-search-smart +- broader queries may still need manual browsing -The LLM client's response is parsed to extract `PURPOSE`, `DEPS`, `CONCEPTS`, `ROLE`, and `ARCH` fields. These are merged with AST data into the final `.pi-map.md` format. - -### Context Limit Protection - -- Files are truncated from the end if they exceed a configurable max token budget (default: 4000 tokens of source). -- A marker `[...truncated]` is appended to the truncated content so the LLM knows it is not seeing the full file. -- Very large binary or generated files are skipped entirely for LLM analysis (they still appear in `.pi-map.md` with a note like "Large/generated file"). - -## 5. Consumption Model - -### Session Start -1. Agent reads the root `Project Map Protocol` and root `.pi-map.index.md`. -2. For architecture/system or ambiguous tasks, agent also reads the root `.pi-map.md`. -3. Agent does **not** preload every directory map by default. - -In the Pi extension, this session-start consumption is assisted by automatic prompt injection: - -- **Before init**: only a lightweight visible startup hint is injected, telling the agent to run `project_map_init`. No synthetic map content is injected. -- **After init**: the root pair (`.pi-map.index.md` + `.pi-map.md`) is guaranteed to be preloaded automatically. Additional directory pairs are expanded only while the configured context budget allows. - -### Automatic Prompt Injection - -The Pi extension uses event hooks (`before_agent_start`, `context`, etc.) to maintain guidance context. - -- The **mode ladder** controls how much is injected: - - `off`: no automatic injection. - - `advisory`: visible startup/init hints and optional root-pair preload. - - `strong` (default): root pair + budgeted expansion + relevant-turn reinjection checks. - - `strict`: same as `strong`, plus explicit bypass justification for sensitive edits/architectural claims when the protocol path is missing. -- The **protocol path** requires both the canonical injected root-pair block and the trust-boundary instruction (`index routes, map orients, source decides`) to be present in outgoing context. -- **Reinjection avoidance** scans actual outgoing messages (and falls back to provider payload) for a stable canonical marker before adding the root pair again. -- **Relevant-turn triggers** are: agent start, before edits, before architecture-sensitive reasoning, after compaction, and after root-pair artifact changes. -- **Visibility** is mixed: startup/init hints are user-visible; raw injected artifact blocks are agent-visible by default. - -### Context Budget - -Default automatic-injection budget: **15% of the active model context window**, capped at **100k tokens**. The smaller of the relative and absolute values wins. If the runtime cannot discover the active model's context window, it falls back to the absolute cap. - -Configurable via `.pi-project-map.json`: - -```json -{ - "promptInjectionMode": "strong", - "contextBudgetPercent": 15, - "contextBudgetMaxTokens": 100000 -} -``` - -### During Session -- Use directory indexes first to decide what to open next. -- For **targeted queries**, run `project_map_context` (tool) or `project-map context` (CLI). The retrieval engine scores all paired metadata and returns a compact markdown bundle with the top-3 strongest matches: indexes, maps, likely files, and symbols. -- Open the strongest-match `.pi-map.md` files for richer orientation. -- Read actual source before editing or making exact runtime claims. - -### Context Management -- Tier 0 stays tiny and stable. -- Tier 1 loads only likely relevant indexes/maps. -- Tier 2 is real source, tests, config, and docs. -- Automatic injection stays within the configured budget and avoids redundant reinjection by scanning outgoing context. - -## 6. Stale Data Mitigation - -### Combined Strategy - -#### 5.1 Dirty Markers -- Whenever the agent edits a file, it appends a dirty flag to the directory's `.pi-map.md`: - ```markdown - ## dirty - 2024-06-09T14:32:00Z: tokens.ts modified - ``` -- A background or post-session reconciliation step regenerates dirty files. -- The agent can also be instructed to reconcile before making architectural decisions. - -#### 5.2 Periodic Full Re-init -- On every new session start, or on a configurable schedule (e.g., daily), the skill offers to run a full re-scan. -- This catches any changes made outside the agent's awareness (e.g., by other developers). - -#### 5.3 Validation Command -- A `validate` tool/command that the agent can invoke: - - Checks for missing files (new files not in `.pi-map.md`). - - Checks for orphaned entries (files listed but deleted). - - Checks for changed signatures (AST mismatch between listed symbols and actual code). - - Reports discrepancies and suggests corrections. - -### Recovery -- If validation finds staleness beyond a threshold (e.g., > 3 dirty packages), the skill recommends a full re-init. -- The agent can also trigger re-init for a specific subtree. - -## 7. Scope Boundaries and Non-Goals - -### In Scope -- Every directory in the project gets a `.pi-map.md` file. -- Every non-excluded file gets analyzed by the LLM layer. -- Code files get augmented by the AST layer where parsers exist. -- Respect `.gitignore` and known junk patterns (node_modules, .git, dist, build, coverage, .next, .venv, __pycache__, .DS_Store). - -### Out of Scope (Non-Goals) -- **Human-readable documentation**: These files are machine-only. Human docs live elsewhere. -- **Line-by-line code explanation**: The format captures symbols and architecture, not implementation details. -- **Auto-regeneration on filesystem events**: The skill relies on agent-initiated updates and periodic re-init, not filesystem watchers. -- **Cross-project analysis**: Each project is independent. No global index across repos. -- **IDE integration**: This is a Pi agent skill, not a VS Code extension or LSP server. - -## 8. Pi Skill Package Structure - -``` -pi-project-map/ -├── SKILL.md # Skill definition for Pi -├── package.json # npm package metadata -├── src/ -│ ├── init.ts # Full project scan + generation -│ ├── patch.ts # Incremental patch logic -│ ├── validate.ts # Consistency checker -│ ├── ast-extract.ts # Tree-sitter / LSP wrappers -│ ├── llm-extract.ts # LLM prompt templates for extraction -│ ├── merge.ts # Merge AST + LLM outputs -│ ├── format.ts # Dense markdown formatter -│ ├── config.ts # Skill configuration (thresholds, ignore patterns) -│ └── prompt-injection.ts # Runtime guidance injection policy and helpers -├── hooks/ -│ └── on-prompt.ts # Injects maintenance command into prompts (legacy; Pi extension uses event hooks) -└── README.md # Setup and usage for humans -``` - -### Custom Tools -- `project-map:init` — Run full project scan. Creates all paired map/index artifacts. -- `project-map:patch ` — Update analysis for a specific file/directory. -- `project-map:validate` — Run consistency check across all paired artifacts. -- `project-map:context ` — Retrieve a compact markdown bundle of the most relevant directories, files, and symbols for a natural-language query. -- `project-map:reinit [path]` — Force re-initialization of entire project or subtree. - -### Prompt Injection Hooks - -The Pi extension registers event hooks instead of a single per-prompt append: - -- `before_agent_start`: emits the pre-init hint when no artifacts exist, or preloads the root pair (plus budgeted expansion) after init. -- `context`: performs relevant-turn reinjection checks, detects compaction/artifact-change invalidation, and enforces `strict`-mode bypass guards. -- `before_provider_request`: optional fallback for marker scanning when message-layer detection is insufficient. - -The injected maintenance reminder is: - -> Start with the root `.pi-map.index.md`, use indexes first for routing, read the local `.pi-map.md` plus source before edits, run `project_map_patch` after source edits, and run `project_map_validate` before freshness-sensitive architectural handoff. - -This is layered on top of the canonical root-pair block, which includes the trust boundary (`index routes, map orients, source decides`). - -## 9. Risks and Tradeoffs - -| Risk | Likelihood | Impact | Mitigation | -|------|-----------|--------|------------| -| Token bloat (1000+ dirs) | Medium | High | Summary mode, lazy loading, context budget | -| Stale analysis files | High | High | Dirty markers + periodic re-init + validation | -| Agent trusts stale data | Medium | High | Clear instructions to validate before architectural decisions | -| Expensive init on large repos | Medium | Medium | Parallelization, caching, optional incremental init | -| Overlap with LSP/typedoc | Low | Low | This is agent-context, not IDE tooling. Different use case. | -| AST parser unavailable | Medium | Low | Graceful fallback to LLM-only extraction | -| Message-level marker scanning misses provider serialization quirks | Medium | Medium | Add payload fallback scanning | -| Root-pair marker becomes brittle | Low | Medium | Stable deterministic boundaries and normalized artifact identity lines | -| 15% / 100k default budget too aggressive for some fleets | Low | Medium | Both knobs are configurable | -| Relevant-turn detection fuzzy | Medium | Medium | Centralized heuristics + extensive integration tests | -| `strict` mode friction | Low | Medium | Keep `strong` as default; isolate strict-only bypass behavior | - -### Prompt Injection Known Limitations - -- Token estimation is best-effort (≈ 4 chars per token); actual provider token counts may differ. -- Relevant-turn detection relies on explicit event types when available, with heuristic fallback for generic turns. -- Provider payload serialization may require the fallback scan path. -- The mode ladder is config-driven in v1; future UX may expose runtime controls. -- Retrieval (`project_map_context`) remains separate from automatic injection. - -## 10. Concrete Example: Full Project Snapshot - -``` -project-root/ -├── .pi-map.md -├── src/ -│ ├── .pi-map.md -│ ├── auth/ -│ │ ├── .pi-map.md -│ │ ├── tokens.ts -│ │ ├── middleware.ts -│ │ └── types.ts -│ └── db/ -│ ├── .pi-map.md -│ ├── connection.ts -│ └── migrations/ -│ ├── .pi-map.md -│ └── 001_init.sql -├── docker/ -│ ├── .pi-map.md -│ ├── Dockerfile -│ └── docker-compose.yml -└── README.md -``` - -Each `.pi-map.md` follows the format in Section 2, creating a navigable hierarchy. - -## 11. Future Extensions - -- **Cross-reference graph**: A top-level `project-graph.md` linking all packages with dependency arrows. -- **Search index**: A lightweight FTS5 index over all `.pi-map.md` files for fast symbol lookup. -- **Diff-aware patching**: Only re-run LLM on changed functions, not entire files. -- **Multi-repo workspaces**: Support monorepos with independent package boundaries. +### Cache +- cache grows unless manually cleaned +- corrupted cache files are recovered by starting fresh diff --git a/implementation-plan.md b/implementation-plan.md deleted file mode 100644 index ece02f0..0000000 --- a/implementation-plan.md +++ /dev/null @@ -1,241 +0,0 @@ -# Implementation Plan: Hierarchical Project Analysis Skill for Pi - -## Overview - -Build a Pi skill package (`pi-project-map`) that generates and maintains a hierarchical, machine-readable analysis of a software project. Each directory gets a `.pi-map.md` file. The skill provides custom tools for init, patch, validate, and re-init, plus a prompt hook that ensures the agent keeps analysis files in sync. - ---- - -## Current Status - -- **M1: Foundation and Format** — ✅ Complete -- **M2: Heuristic Extraction** — ✅ Complete (placeholder only; to be replaced by real LLM) -- **M3: AST Extraction** — ✅ Complete -- **M4: Patch and Update** — ✅ Complete -- **M5: Validation and `--fix`** — ✅ Complete -- **M6: Pi Skill Integration** — ✅ Complete (basic) -- **M7: Proper LLM Integration** — 🔄 In Progress / Next up - -The remaining major work is to replace the heuristic "LLM" extraction with **actual LLM API calls**. - ---- - -## Remaining Milestone: M7 — Proper LLM Integration - -**Goal**: Replace the regex heuristic `llm-extract.ts` with real LLM calls, including dual-provider support (Pi native + external fallback), disk caching, parallelization, and retries. - -**Estimated time**: 1 week of focused work. - ---- - -### Task 1: LLM Client Abstraction (`src/llm-client.ts`) - -Create a unified interface for LLM calls. - -```typescript -interface LLMClient { - complete(prompt: string): Promise; -} -``` - -Sub-tasks: -- Define the `LLMClient` interface. -- Add a factory function `createLLMClient(mode: 'pi' | 'external', options)`. -- Handle mode selection: - - `'pi'`: used inside `pi-extension.ts` with Pi's model registry. - - `'external'`: used in standalone CLI with OpenAI-compatible API. - -**Acceptance Criteria** -- `LLMClient` interface exists and compiles. -- Factory correctly selects implementation based on mode. - ---- - -### Task 2: External LLM Client (`src/external-llm-client.ts`) - -Implement the standalone CLI LLM client using an OpenAI-compatible API. - -Sub-tasks: -- Add `openai` (or a lightweight fetch-based client) as a dependency. -- Read configuration from: - - Environment variable `OPENAI_API_KEY` (or `ANTHROPIC_API_KEY`, etc.) - - Config file `.pi-project-map.json` fields: `llmProvider`, `llmModel`, `llmBaseUrl` -- Implement `complete(prompt)` using chat completions API. -- Default to `gpt-4o-mini` or similar cheap model. - -**Acceptance Criteria** -- `project-map init` works standalone with `OPENAI_API_KEY` set. -- Missing API key produces a clear, actionable error message. -- Failed API call throws a descriptive `LLMError`. - ---- - -### Task 3: Pi LLM Client (`src/pi-llm-client.ts`) - -Implement the Pi-native LLM client for use inside the extension. - -Sub-tasks: -- Accept Pi's `ExtensionContext` or model registry as a constructor argument. -- Use `ctx.model` / `ctx.modelRegistry` to get the configured model and API key. -- Call the provider directly (likely using the same OpenAI-compatible endpoint Pi uses). -- If Pi does not expose direct LLM calls, fall back to emitting a tool call / follow-up message pattern. - -**Acceptance Criteria** -- Pi extension can successfully call an LLM when running inside Pi. -- Errors surface clearly to the user. - ---- - -### Task 4: Disk Cache (`src/llm-cache.ts`) - -Implement persistent SHA-256 → LLM result caching. - -Sub-tasks: -- Cache directory: `~/.cache/pi-project-map/` (create if missing). -- Cache file: `llm-cache.json` (simple JSON object). -- Functions: - - `getCached(hash: string): string | null` - - `setCached(hash: string, result: string): void` -- Ensure atomic writes (write to temp file, rename). -- Add cache size limit (e.g., 10,000 entries, LRU eviction). - -**Acceptance Criteria** -- Second `init` run on unchanged files does not call LLM. -- Cache persists across process restarts. -- Corrupted cache file does not crash the tool. - ---- - -### Task 5: Parallelization and Retries (`src/llm-batch.ts`) - -Run LLM requests in parallel with retries and backoff. - -Sub-tasks: -- Add `p-limit` dependency for concurrency control. -- Default concurrency: 4 (configurable via `.pi-project-map.json` `llmConcurrency`). -- Add small delay (100ms) between batches. -- Implement retry logic: 3 retries, delays 1s → 2s → 4s. -- On final failure, throw a hard error and stop the entire process. - -**Acceptance Criteria** -- 100-file project completes significantly faster than sequential. -- Simulated transient failures are retried and recovered. -- Persistent failure stops the tool with a clear error. - ---- - -### Task 6: Rewrite `llm-extract.ts` to Use Real LLM - -Replace regex heuristics with LLM calls. - -Sub-tasks: -- Accept an `LLMClient` in `extractFileLLM` and `extractPackageLLM`. -- Check disk cache before calling LLM. -- Construct file-level prompt (see design doc Section 4). -- Parse response into `purpose`, `deps`, `concepts`. -- Construct package-level prompt. -- Parse response into `role`, `arch`. -- Remove heuristic code from production path; keep only for test mocks if useful. - -**Acceptance Criteria** -- `llm-extract.ts` calls the LLM client for every file. -- Output includes rich, non-trivial descriptions for typical files. -- Unit tests mock the LLM client to verify prompt structure and parsing. - ---- - -### Task 7: Context Limit Handling - -Protect against oversized files. - -Sub-tasks: -- Measure prompt + file content tokens (approximate: 1 token ≈ 4 chars for ASCII). -- If file exceeds max context budget (configurable, default 4000 tokens), truncate from the end. -- Append `[...truncated]` marker in the prompt. -- Skip LLM for binary/generated files over a hard limit (e.g., 50KB) and mark them as "Large/generated file". - -**Acceptance Criteria** -- A 1MB minified JS file does not crash or consume excessive tokens. -- Truncated files still produce useful output. - ---- - -### Task 8: Update CLI and Extension - -Wire the new LLM client into all entry points. - -Sub-tasks: -- `src/cli.ts`: create external LLM client, pass into `initProject` / `patchFile`. -- `pi-extension.ts`: create Pi LLM client, pass into tools. -- Update `init.ts` and `patch.ts` signatures to accept an optional `LLMClient`. -- Add CLI flag `--llm-provider=openai` for explicit selection. -- Update error handling to catch `LLMError` and print helpful messages. - -**Acceptance Criteria** -- CLI works with external API key. -- Extension works inside Pi (if Pi exposes LLM access). -- Clear errors on misconfiguration. - ---- - -### Task 9: Update Tests - -Sub-tasks: -- Replace heuristic tests with mocked LLM client tests. -- Add integration test: create a fake LLM client, run `initProject`, verify output contains LLM-provided text. -- Add cache test: verify cache hit skips LLM call. -- Add retry test: verify transient failures retry, persistent failures hard-stop. - -**Acceptance Criteria** -- All tests pass. -- Test coverage includes: LLM client, cache, batching, prompt parsing, error handling. - ---- - -## Sequencing and Dependencies - -``` -Task 1 (LLMClient interface) - ├── Task 2 (External client) - ├── Task 3 (Pi client) - ├── Task 4 (Cache) - ├── Task 5 (Batch + retries) - │ - ├── Task 6 (Rewrite llm-extract.ts) - ├── Task 7 (Context limits) - │ - ├── Task 8 (Wire CLI + extension) - └── Task 9 (Tests) -``` - ---- - -## Validation Criteria (for LLM Integration) - -1. **Real LLM calls**: `llm-extract.ts` invokes the configured LLM client for every file. -2. **Cache hit**: Second `init` on unchanged repo completes with zero LLM calls. -3. **Parallel speed**: 100-file project init completes in under 30 seconds (assuming average LLM latency 500ms). -4. **Retry works**: Transient 429/5xx errors are retried; permanent failures stop with a clear error. -5. **Context limit safety**: Files > max token budget are truncated, not rejected. -6. **Dual provider**: CLI uses external API; Pi extension uses Pi's LLM. -7. **Quality**: LLM output is visibly richer than the old heuristic output (verified by manual inspection). - ---- - -## Rollout Plan - -1. **Test on real projects** (Day 1-2): Run `init` on 2-3 real codebases with the new LLM integration. -2. **Cost audit** (Day 3): Measure token usage per project; adjust defaults if too expensive. -3. **Prompt tuning** (Day 4-5): Iterate prompt design based on output quality. -4. **Release** (Day 6-7): Publish updated npm package, update Pi extension docs. - ---- - -## Completed Milestones (for reference) - -- **M1**: Foundation and Format -- **M2**: Heuristic Extraction (placeholder, to be replaced by M7) -- **M3**: AST Extraction -- **M4**: Patch and Update -- **M5**: Validation and `--fix` -- **M6**: Pi Skill Integration diff --git a/troubleshooting.md b/troubleshooting.md new file mode 100644 index 0000000..b4323ea --- /dev/null +++ b/troubleshooting.md @@ -0,0 +1,185 @@ +# Troubleshooting pi-project-map + +## Stale map issues + +### Symptom +`project_map_validate` reports `missing`, `orphaned`, or `stale-signature` discrepancies, or a directory map describes files that no longer exist. + +### Likely cause +A source file was edited, added, or deleted without running `project_map_patch` or `project_map_reinit`. + +### What to do +1. run `project_map_validate` to inspect discrepancies +2. if the list is small and localized, run `project_map_patch ` +3. if the list is large or structural, run `project_map_reinit` +4. re-run `project_map_validate` to confirm clean state + +### Prevention +- patch immediately after editing source +- in Pi, use the `project_map_patch` tool with the changed file path +- watch for the `session_start` dirty warning + +## Validate discrepancies + +### Symptom +`validate --fix` fails with `validate --fix requires an LLM client`. + +### Cause +Repair needs an LLM client to regenerate stale artifacts. + +### Fix +- CLI: provide `--llm-provider=...` and the required API key +- Pi: use the tool surface so the runtime provides the LLM client + +### Symptom +Many `stale-signature` discrepancies appear. + +### Cause +AST exports no longer match listed exports, or generated signatures are stale. + +### Fix +- patch the changed file/directory +- if widespread, reinit and validate again + +### Symptom +`broken-link` appears after moving directories. + +### Fix +Run `project_map_reinit`. + +## Prompt injection mode surprises + +### Symptom +No project-map context appears in a Pi session. + +### Checks before init +1. confirm `.pi-project-map.json` does not set `promptInjectionMode` to `off` +2. confirm root `.pi-map.md` / `.pi-map.index.md` do not exist yet +3. confirm the session is at agent start; the pre-init hint is emitted from `before_agent_start` + +### Checks after init +1. confirm root `.pi-map.md` and `.pi-map.index.md` exist +2. confirm mode is `strong` or `strict` if you expect automatic artifact injection +3. confirm the outgoing context does not already contain the root-pair marker +4. confirm the turn is actually relevant (`agent_start`, `edit_intent`, `architecture_sensitive`, `compaction`, `artifact_change`) + +### Symptom +Root pair is injected repeatedly. + +### Likely cause +Marker scanning failed or artifact invalidation forced reinjection. + +### Fix +- inspect whether `` is present in outgoing context +- check whether root artifacts changed on disk +- confirm payload/message serialization still exposes marker text + +### Symptom +`advisory` mode shows a reminder but no maps are loaded. + +### Expected behavior +That is intentional. Advisory mode is reminder-only; read the root pair manually. + +## Strict bypass confusion + +### Symptom +A sensitive edit is blocked with the strict guard. + +### What is happening +In `strict` mode, the turn was classified as sensitive and the protocol path is missing from outgoing context. + +### Resolution options +1. restore the protocol path by ensuring the root pair and trust boundary are present in context +2. include an explicit bypass marker: + +```text +[PI_MAP_BYPASS: emergency fix needed without full context] +``` + +3. switch to `strong` mode if strict is too noisy for the task + +### Symptom +Bypass marker is ignored. + +### Cause +The marker must contain a non-empty reason. Empty or whitespace-only reasons are rejected. + +### Symptom +Strict guard triggers on obviously non-sensitive turns. + +### Cause +Heuristic sensitive-turn detection can false-positive on words like `refactor`, `redesign`, or `dependency graph`. + +## LLM / provider / cache issues + +### Symptom +`project-map init` fails with `No LLM client configured`. + +### CLI fix +- set `OPENAI_API_KEY` for OpenAI provider +- set `KIMI_API_KEY` for Kimi provider +- or configure provider/model in `.pi-project-map.json` + +### Pi fix +- select a model in Pi +- log in if the provider requires credentials +- ensure Pi runtime LLM access is working + +### Symptom +External/provider request fails. + +### Checks +1. API key validity +2. network connectivity +3. `llmBaseUrl` correctness if using a proxy +4. provider rate limits + +### Symptom +Second init still makes many LLM calls. + +### Cause +Cache miss. + +### Checks +- consistent cache directory between runs +- cache file exists and is readable +- file contents did not change + +### Symptom +Cache file is huge. + +### Cause +There is no eviction policy yet. + +### Fix +Remove the local cache directory if needed. + +## Test / lint expectations + +### Main checks + +```bash +npm test +npm run typecheck +npm run build +node dist/cli.js validate . +``` + +### Lint note +`npm run lint` is currently not a reliable gate if the repo has no ESLint config. Treat missing-lint-config failures as repository hygiene issues, not feature regressions. + +### Common failures + +| Failure | Likely cause | +|---------|--------------| +| Pi LLM client tests fail | Pi runtime or mocks drifted | +| Prompt-injection sequence tests fail | Reinjection / strict logic changed | +| Validate tests fail | Format parsing, merge encoding, or discovery rules changed | +| AST tests fail | tree-sitter grammar/runtime drift | + +## Known limitations + +- token budgeting is heuristic, not tokenizer-exact +- retrieval is deterministic, not semantic-search-driven +- strict mode is a prompt-level guard, not a hard tool sandbox +- payload scanning assumes string or JSON-serializable structures diff --git a/usage-guide.md b/usage-guide.md new file mode 100644 index 0000000..30229c8 --- /dev/null +++ b/usage-guide.md @@ -0,0 +1,198 @@ +# pi-project-map Usage Guide + +For Pi agents and advanced users who want predictable, low-friction navigation and maintenance of project-map artifacts. + +## When to use each command + +| Situation | Command | +|-----------|---------| +| New project, no `.pi-map.md` files yet, or artifacts are severely outdated | `project_map_init` / `project-map init` | +| You just edited one or more source files | `project_map_patch ` / `project-map patch ` | +| You suspect stale data, or you are about to make an architectural decision | `project_map_validate` / `project-map validate` | +| Validation shows widespread staleness, or you pulled major changes from version control | `project_map_reinit` / `project-map reinit` | +| You have a specific question like "where is auth handled?" | `project_map_context ` / `project-map context ` | + +## Command behavior + +### init +Run once when you start work on a repo, or after large restructuring. It discovers every non-ignored directory, analyzes files with LLM + AST, and writes both `.pi-map.md` and `.pi-map.index.md` for every directory. + +### patch +Run **immediately after editing a source file**. The command regenerates the pair for that file’s directory and refreshes ancestor artifacts. + +Patch mode is chosen automatically: +- **small** — refresh ancestor indexes only +- **structural** — refresh ancestor map/index pairs + +You can force a mode with `project-map patch --patch-mode=small|structural`. +`auto` remains the default. + +### validate +Run before architectural decisions, broad refactors, or final handoff. It checks for: +- missing/orphaned files +- stale signatures +- dirty markers +- broken parent/children/sibling links +- map/index disagreements + +Use `project-map validate --fix` to repair affected chains. `--fix` requires an LLM client. + +### reinit +Use sparingly. It regenerates every pair from scratch and is the blunt instrument for widespread staleness. + +### context +Use when you know what you are looking for: + +```bash +project-map context "authentication logic" +project-map context "routing metadata generation" +project-map context "LLM client error handling" +``` + +The returned bundle is deterministic and ranked. Read indexes first, then strongest-match maps, then the actual source files. + +## Practical workflows + +### Starting a new task in a mapped project + +1. read the root `.pi-map.index.md` and the `Project Map Protocol` +2. use the root index to find the relevant child directory +3. read that directory’s `.pi-map.index.md`, then its `.pi-map.md` +4. read the relevant source files +5. edit source +6. run `project_map_patch ` +7. run tests/build +8. run `project_map_validate` before architectural summary or handoff + +### Exploring an unfamiliar area + +```bash +project-map context "how is validation implemented" +``` + +Treat the returned bundle as a ranked entry point, not as truth. + +### After pulling changes from version control + +```bash +project-map validate +# if many discrepancies: +project-map reinit +``` + +### Before a big refactor + +```bash +project-map validate +# if needed: +project-map reinit +``` + +## Prompt injection behavior before and after init + +### Before init +If no paired artifacts exist: +- `off`: nothing happens +- `advisory`, `strong`, `strict`: a visible hint appears telling you to run `project_map_init` + +No map content is fabricated. + +### After init +Once the root pair exists: +- `off`: no automatic injection +- `advisory`: a visible reminder that maps are available, but the root pair is **not** auto-loaded +- `strong`: root pair is auto-loaded; additional pairs are added within the budget; reinjection happens on relevant turns +- `strict`: same as `strong`, plus enforcement of the protocol path for sensitive actions + +Relevant turns that trigger reinjection in `strong`/`strict`: +- agent start +- edit intent +- architecture-sensitive reasoning +- compaction +- root-pair artifact changes + +## Advisory vs strong vs strict in practice + +| Concern | Use | +|---------|-----| +| You want maps available but do not want automatic context expansion | `advisory` | +| Normal daily work; you want routing/orientation preloaded without friction | `strong` (default) | +| High-stakes codebase or you want explicit justification before bypassing context discipline | `strict` | +| You prefer fully manual control | `off` | + +In `strict`, if you attempt a sensitive edit or architectural claim without the protocol path in context, a guard appears. To proceed, either restore the project-map context or include: + +```text +[PI_MAP_BYPASS: editing a one-line comment, map context not needed] +``` + +Use bypass markers sparingly. + +## Example task flows + +### Fix a bug in `src/utils/validation.ts` + +```text +1. project-map context "validation utilities" +2. Read src/utils/.pi-map.index.md and .pi-map.md +3. Read src/utils/validation.ts +4. Edit the file +5. project_map_patch src/utils/validation.ts +6. Run tests +7. project_map_validate +``` + +### Add a new file to `src/llm/` + +```text +1. Create src/llm/new-client.ts +2. Implement the file +3. project_map_patch src/llm/new-client.ts +4. project_map_validate +``` + +### Review architecture before approving a PR + +```text +1. project-map validate +2. If clean, read root .pi-map.md and key directory maps +3. Cross-check claims against source +4. If stale, run project-map reinit first +``` + +## Retrieval vs automatic injection + +| | Automatic injection | `project_map_context` / `project-map context` | +|---|---|---| +| Trigger | Configured mode + relevant turn | Explicit request | +| Content | Root pair + budgeted expansion | Top-ranked directories for a query | +| Cost | No LLM call; reads artifacts | No LLM call; deterministic scoring | +| Best use | Maintain baseline orientation | Targeted navigation for a specific task | + +Use both together: injection for baseline orientation, retrieval for focused entry points. + +## Keeping artifacts fresh + +- patch after every edit +- validate before architectural claims +- reinit when many artifacts are stale or after large merges +- watch for the Pi extension warning about dirty packages on session start + +## Configuration quick reference + +```json +{ + "promptInjectionMode": "strong", + "contextBudgetPercent": 15, + "contextBudgetMaxTokens": 100000, + "tagCap": 8, + "workflowHintCap": 5, + "ignorePatterns": ["node_modules", ".git", "dist", "build"] +} +``` + +Providing `ignorePatterns` replaces the built-in default list, so include any defaults you want to keep. + +- lower `contextBudgetPercent` / `contextBudgetMaxTokens` to reduce token use +- raise them if you want deeper auto-loaded context in large projects +- `strict` is the safest enforcement mode; `strong` is the best default for everyday work