docs: rewrite documentation system
This commit is contained in:
@@ -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 <file>` | `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 <query>` | `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 <changed-file>`
|
||||
3. before broad architectural decisions, `project-map validate`
|
||||
4. for targeted exploration, `project-map context "<query>"`
|
||||
|
||||
## 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: <brief justification>]`.
|
||||
| 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: <brief justification>]
|
||||
```
|
||||
|
||||
### 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 <query>` 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 <query>` 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 <file>
|
||||
project-map validate [--fix]
|
||||
project-map reinit
|
||||
project-map context <query>
|
||||
```
|
||||
|
||||
### Context retrieval
|
||||
|
||||
`project-map context <query>` 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
|
||||
|
||||
@@ -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 <file>` (tool) or `project-map patch <file>` (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 <query>` (tool) or `project-map context <query>` (CLI)
|
||||
7. in `strict` mode, only bypass the protocol-path guard with an explicit marker: `[PI_MAP_BYPASS: <brief justification>]`
|
||||
|
||||
## 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: <brief justification>]`.
|
||||
|
||||
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 <path>` after editing source
|
||||
5. Run `project-map validate` before freshness-sensitive architectural decisions
|
||||
6. In `strict` mode, include `[PI_MAP_BYPASS: <brief justification>]` 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: <query>`
|
||||
|
||||
**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 <file>` | 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 <query>` | Retrieve a ranked context bundle for a natural-language query. |
|
||||
|
||||
+288
-396
@@ -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
|
||||
# <relative-path> (index)
|
||||
dir: <relative-path>
|
||||
## role
|
||||
<short routing summary>
|
||||
## parent
|
||||
<parent links or ->
|
||||
## children
|
||||
<child links or ->
|
||||
## files
|
||||
<likely files>
|
||||
## links
|
||||
index: <self-index>
|
||||
map: <self-map>
|
||||
## workflows
|
||||
<compact task -> route hints>
|
||||
## dirty
|
||||
<timestamp or ->
|
||||
```
|
||||
The artifacts are **navigation aids, not source-of-truth**. Source code is always the final authority.
|
||||
|
||||
```markdown
|
||||
# <relative-path>
|
||||
dir: <relative-path>
|
||||
index: <sibling-index>
|
||||
## role
|
||||
<one-line package role>
|
||||
## files
|
||||
- <filename> | <one-line purpose> | exp: <exported symbols> | dep: <internal/external deps>
|
||||
## arch
|
||||
<free-form architectural notes>
|
||||
## tags
|
||||
<compact tags>
|
||||
## symbols
|
||||
<prioritized symbols>
|
||||
## workflows
|
||||
<compact workflow hints>
|
||||
## dirty
|
||||
<timestamp or ->
|
||||
```
|
||||
> **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<string>;
|
||||
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: <file-path>
|
||||
`discoverContextWindow()` inspects the Pi runtime model for context metadata and falls back to the absolute cap when unavailable.
|
||||
|
||||
```
|
||||
<file-contents-truncated>
|
||||
```
|
||||
## 8. Known limits and tradeoffs
|
||||
|
||||
Respond in this exact format:
|
||||
PURPOSE: <concise description>
|
||||
DEPS: <comma-separated list, or "none">
|
||||
CONCEPTS: <comma-separated list, or "none">
|
||||
```
|
||||
### 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: <dir-path>
|
||||
Files:
|
||||
- <file1>: <purpose1>
|
||||
- <file2>: <purpose2>
|
||||
...
|
||||
### Patch mode inference
|
||||
- auto-mode heuristics are good but imperfect
|
||||
- contributors can force structural mode when needed
|
||||
|
||||
Respond in this exact format:
|
||||
ROLE: <one-line description of this directory's role in the project>
|
||||
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 <file-path>` — Update analysis for a specific file/directory.
|
||||
- `project-map:validate` — Run consistency check across all paired artifacts.
|
||||
- `project-map:context <query>` — 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
|
||||
|
||||
@@ -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<string>;
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
@@ -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 <changed-file>`
|
||||
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 `<!-- PI_MAP_ROOT_PAIR_START -->` 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
|
||||
+198
@@ -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 <file>` / `project-map patch <file>` |
|
||||
| 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 <query>` / `project-map context <query>` |
|
||||
|
||||
## 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 <file> --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 <changed-file>`
|
||||
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
|
||||
Reference in New Issue
Block a user