Compare commits
5 Commits
fb302a033e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 97a4dd22f5 | |||
| 3e7410b6bd | |||
| cb581f44b9 | |||
| 5f1c107667 | |||
| 6bc7be4c21 |
@@ -0,0 +1,18 @@
|
||||
# .atl (index)
|
||||
dir: .atl
|
||||
|
||||
## role
|
||||
Empty directory placeholder, likely intended for Atlassian tool configuration or automation artifacts that have not yet been populated.
|
||||
## parent
|
||||
index: ./.pi-map.index.md
|
||||
map: ./.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
## links
|
||||
index: .atl/.pi-map.index.md
|
||||
map: .atl/.pi-map.md
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,18 @@
|
||||
# .atl
|
||||
dir: .atl
|
||||
|
||||
index: .atl/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Empty directory placeholder, likely intended for Atlassian tool configuration or automation artifacts that have not yet been populated.
|
||||
## files
|
||||
## arch
|
||||
N/A - no files or architectural patterns present in this directory.
|
||||
## tags
|
||||
-
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -4,8 +4,6 @@ coverage/
|
||||
*.log
|
||||
.DS_Store
|
||||
.env
|
||||
.pi-map.md
|
||||
.pi-map.index.md
|
||||
# Local Pi runtime state
|
||||
.atl/
|
||||
.pi
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# . (index)
|
||||
dir: .
|
||||
|
||||
## Project Map Protocol
|
||||
|
||||
1. Read this protocol and the root `.pi-map.index.md` first.
|
||||
2. Use `index:` / `map:` references to open relevant directory indexes and maps.
|
||||
3. Load indexes before rich maps during task-start navigation.
|
||||
4. Read the local rich map and actual source before editing.
|
||||
5. Treat non-empty `## dirty` sections in either artifact as stale.
|
||||
6. If source and generated artifacts disagree, trust source.
|
||||
7. If map and index disagree, trust neither blindly; verify from source and regenerate the pair.
|
||||
8. After editing source, run `project_map_patch` for each changed file.
|
||||
9. Before broad architectural claims or final handoff, run `project_map_validate` when freshness matters.
|
||||
|
||||
Trust boundary: index routes, map orients, source decides.
|
||||
|
||||
## role
|
||||
A TypeScript/Node.js CLI tool and Pi extension that generates paired markdown analysis artifacts (.pi-map.index.md and .pi-map.md) to provide hierarchical codebase navigation and contextual orientation for AI coding agents.
|
||||
## parent
|
||||
-
|
||||
## children
|
||||
- .atl
|
||||
index: .atl/.pi-map.index.md
|
||||
map: .atl/.pi-map.md
|
||||
- fixtures
|
||||
index: fixtures/.pi-map.index.md
|
||||
map: fixtures/.pi-map.md
|
||||
- openspec
|
||||
index: openspec/.pi-map.index.md
|
||||
map: openspec/.pi-map.md
|
||||
- src
|
||||
index: src/.pi-map.index.md
|
||||
map: src/.pi-map.md
|
||||
- tests
|
||||
index: tests/.pi-map.index.md
|
||||
map: tests/.pi-map.md
|
||||
## files
|
||||
- .gitignore
|
||||
- .npmrc
|
||||
- README.md
|
||||
- SKILL.md
|
||||
- design-doc.md
|
||||
- package-lock.json
|
||||
- package.json
|
||||
- pi-extension.ts
|
||||
- troubleshooting.md
|
||||
- tsconfig.json
|
||||
- usage-guide.md
|
||||
## links
|
||||
index: ./.pi-map.index.md
|
||||
map: ./.pi-map.md
|
||||
## workflows
|
||||
- change project behavior
|
||||
read: .gitignore, .npmrc, pi-extension.ts
|
||||
- change project config
|
||||
read: package-lock.json, package.json, tsconfig.json
|
||||
- explore project subdirectories
|
||||
index: .atl/.pi-map.index.md, fixtures/.pi-map.index.md, openspec/.pi-map.index.md
|
||||
## dirty
|
||||
-
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
# .
|
||||
dir: .
|
||||
|
||||
index: ./.pi-map.index.md
|
||||
|
||||
## Project Map Protocol
|
||||
|
||||
1. Read this protocol and the root `.pi-map.index.md` first.
|
||||
2. Use `index:` / `map:` references to open relevant directory indexes and maps.
|
||||
3. Load indexes before rich maps during task-start navigation.
|
||||
4. Read the local rich map and actual source before editing.
|
||||
5. Treat non-empty `## dirty` sections in either artifact as stale.
|
||||
6. If source and generated artifacts disagree, trust source.
|
||||
7. If map and index disagree, trust neither blindly; verify from source and regenerate the pair.
|
||||
8. After editing source, run `project_map_patch` for each changed file.
|
||||
9. Before broad architectural claims or final handoff, run `project_map_validate` when freshness matters.
|
||||
|
||||
Trust boundary: index routes, map orients, source decides.
|
||||
|
||||
## role
|
||||
A TypeScript/Node.js CLI tool and Pi extension that generates paired markdown analysis artifacts (.pi-map.index.md and .pi-map.md) to provide hierarchical codebase navigation and contextual orientation for AI coding agents.
|
||||
## files
|
||||
- .gitignore | Specifies files and directories for Git to ignore in a Node.js/TypeScript project with Pi tooling integration | dep: git
|
||||
- .npmrc | Configures npm to use legacy peer dependency resolution behavior | dep: npm
|
||||
- README.md | Documents a CLI tool and Pi extension that generates paired machine-readable analysis artifacts (.pi-map.index.md and .pi-map.md) for hierarchical codebase navigation and agent orientation. | dep: npm, Node.js, Pi runtime environment, LLM provider (OpenAI, etc.), file system
|
||||
- SKILL.md | Defines a Pi skill that generates and maintains hierarchical paired project-analysis artifacts (`.pi-map.index.md` + `.pi-map.md`) to enable AI agents to navigate codebases without reading every source file. | dep: markdown, AST parsing, LLM API, JSON configuration, CLI/tool interface
|
||||
- design-doc.md | A design document explaining the internal architecture of `pi-project-map`, a TypeScript/Node.js tool that generates and maintains hierarchical paired markdown artifacts (`.pi-map.index.md` and `.pi-map.md`) to serve as navigation aids for AI coding agents, with both CLI and Pi extension runtime modes. | dep: TypeScript, Node.js, tree-sitter, ignore, LLM client (PiLLMClient/ExternalLLMClient/KimiLLMClient)
|
||||
- package-lock.json | Auto-generated npm lock file that records exact dependency versions and tree structure for reproducible installs of the "pi-project-map" Node.js CLI tool. | dep: npm, esbuild, eslint, typescript, vitest, openai, tree-sitter, tree-sitter-python, tree-sitter-typescript, p-limit, picocolors, ignore
|
||||
- package.json | Pi skill for hierarchical project analysis that generates and maintains .pi-map.md files | dep: ignore, openai, p-limit, picocolors, tree-sitter, tree-sitter-python, tree-sitter-typescript, typescript, vitest, eslint, @types/node, @typescript-eslint
|
||||
- pi-extension.ts | Pi extension that registers tools for managing project map artifacts (.pi-map.md/.pi-map.index.md) and injects contextual hints into agent sessions based on configuration modes. | dep: @mariozechner/pi-coding-agent, typebox, fs, path, ./src/index.js, ./src/config.js, ./src/llm/llm-client.js, ./src/llm/llm-error.js
|
||||
- troubleshooting.md | Troubleshooting guide for diagnosing and resolving issues with the pi-project-map tool across validation, prompt injection, strict mode, LLM provider, and testing scenarios. | dep: project_map_validate, project_map_patch, project_map_reinit, LLM client, Pi runtime, tree-sitter, npm
|
||||
- tsconfig.json | Configures TypeScript compiler options for a Node.js project targeting ES2022 with strict type checking and declaration output
|
||||
- usage-guide.md | User documentation explaining how to use the pi-project-map tool for navigating and maintaining project-map artifacts in codebases.
|
||||
## arch
|
||||
Dual-runtime architecture supporting both standalone CLI and Pi extension modes, using a hierarchical paired-file pattern (index + detail) with TypeScript/Node.js, strict type checking, and configuration-driven behavior injection for agent session integration.
|
||||
## tags
|
||||
map, project, tree, typescript, sitter, node, npm, js
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
- change project behavior
|
||||
read: .gitignore, .npmrc, pi-extension.ts
|
||||
- change project config
|
||||
read: package-lock.json, package.json, tsconfig.json
|
||||
- explore project subdirectories
|
||||
index: .atl/.pi-map.index.md, fixtures/.pi-map.index.md, openspec/.pi-map.index.md
|
||||
## dirty
|
||||
-
|
||||
@@ -1,140 +1,74 @@
|
||||
# pi-project-map
|
||||
|
||||
> Pi skill and CLI for hierarchical project analysis.
|
||||
A Pi extension and command-line tool that generates paired project-navigation artifacts: `.pi-map.index.md` files for routing and `.pi-map.md` files for directory orientation. The artifacts help agents navigate a codebase; source remains the authority.
|
||||
|
||||
`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.
|
||||
## Status and limitations
|
||||
|
||||
## What it is
|
||||
- Generation and repair commands call an LLM and write map artifacts into the target project.
|
||||
- `validate` without `--fix` checks artifacts without creating an LLM client; `validate --fix` can modify them and requires an LLM.
|
||||
- Token budgeting and relevant-turn detection are best-effort. Retrieved maps route and orient work but do not replace source verification.
|
||||
- No Node.js version requirement or deployment configuration is declared in this repository.
|
||||
|
||||
For every non-ignored directory, the tool produces two files:
|
||||
## Install and develop
|
||||
|
||||
| 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. |
|
||||
|
||||
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:
|
||||
Install the published CLI globally:
|
||||
|
||||
```bash
|
||||
npm install -g pi-project-map
|
||||
```
|
||||
|
||||
Generate paired artifacts in a repo:
|
||||
For local development, install dependencies and build from this repository:
|
||||
|
||||
```bash
|
||||
cd my-project
|
||||
project-map init
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
For standalone CLI usage, provide an LLM provider/API key. For example:
|
||||
Available development checks:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=...
|
||||
project-map init
|
||||
npm run test
|
||||
npm run lint
|
||||
npm run typecheck
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Inside Pi, the extension uses Pi's configured model automatically.
|
||||
`npm run dev` runs TypeScript in watch mode. The project declares no deployment command.
|
||||
|
||||
## Command overview
|
||||
## Use the CLI
|
||||
|
||||
| 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. |
|
||||
Run the installed `project-map` command from the project to map. Use `--help` or `--version` for CLI metadata.
|
||||
|
||||
```bash
|
||||
project-map init [path]
|
||||
project-map patch <file>
|
||||
project-map validate [--fix] [path]
|
||||
project-map reinit [path]
|
||||
project-map context <query>
|
||||
```
|
||||
|
||||
Typical workflow:
|
||||
|
||||
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>"`
|
||||
1. Run `project-map init` to generate maps for a project.
|
||||
2. After editing a file, run `project-map patch <file>`.
|
||||
3. Run `project-map validate` before relying on maps; use `--fix` only when you intend to repair artifacts.
|
||||
4. Use `project-map context "<query>"` to retrieve a ranked context bundle.
|
||||
|
||||
## Operating model
|
||||
`init`, `patch`, `reinit`, and `validate --fix` mutate `.pi-map.md` and/or `.pi-map.index.md` artifacts. `context` retrieves from existing artifacts.
|
||||
|
||||
Follow a three-tier model when consuming project maps:
|
||||
### Pi extension
|
||||
|
||||
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 repository exposes `pi-extension.ts` as a Pi extension. Inside Pi it uses Pi's configured model and registers equivalent `project_map_*` tools. The extension can also inject project-map guidance according to `promptInjectionMode`.
|
||||
|
||||
The trust boundary is always:
|
||||
## LLM configuration
|
||||
|
||||
> **index routes, map orients, source decides.**
|
||||
Standalone CLI use needs credentials for the selected provider. Do not commit keys.
|
||||
|
||||
## Prompt injection policy
|
||||
- `openai` (the default) reads `OPENAI_API_KEY`; its model can be set with `OPENAI_MODEL` or `LLM_MODEL`.
|
||||
- `kimi` reads `KIMI_API_KEY` (or `KIMI_COM_API_KEY`); its model can be set with `KIMI_MODEL` or `LLM_MODEL`.
|
||||
- CLI options `--llm-provider=openai|kimi`, `--llm-model=<model>`, and `--llm-base-url=<url>` override corresponding settings for a command.
|
||||
|
||||
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
|
||||
|
||||
No synthetic map content is injected. The agent sees only a visible startup hint telling it to run `project_map_init`.
|
||||
|
||||
### After init
|
||||
|
||||
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.
|
||||
|
||||
### Mode ladder
|
||||
|
||||
| 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
|
||||
|
||||
Default automatic-injection budget is the smaller of:
|
||||
- **15%** of the active model context window
|
||||
- **100,000 tokens** absolute cap
|
||||
|
||||
If the runtime cannot discover the model context window, it falls back to the absolute cap.
|
||||
|
||||
## Retrieval is separate
|
||||
|
||||
`project-map context <query>` and `project_map_context` are **separate, on-demand retrieval** paths. They do **not** replace automatic prompt injection.
|
||||
|
||||
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
|
||||
|
||||
Use retrieval for targeted navigation when you already have a specific question.
|
||||
|
||||
## Configuration overview
|
||||
|
||||
Create `.pi-project-map.json` in the project root:
|
||||
Create an optional `.pi-project-map.json` in the project being mapped. It is merged with the defaults:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -149,26 +83,16 @@ Create `.pi-project-map.json` in the project root:
|
||||
}
|
||||
```
|
||||
|
||||
Providing `ignorePatterns` replaces the built-in default list, so include any defaults you want to keep.
|
||||
`promptInjectionMode` accepts `off`, `advisory`, `strong`, or `strict`. Supplying `ignorePatterns` **replaces** the built-in ignore list rather than extending it. Additional supported settings include `llmBaseUrl` and `reinitFullThresholdPercent`.
|
||||
|
||||
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
|
||||
## Repository layout
|
||||
|
||||
## Documentation map
|
||||
- `src/` — CLI, map generation, validation, retrieval, configuration, and LLM clients
|
||||
- `pi-extension.ts` — Pi extension entry point
|
||||
- `SKILL.md` — Pi skill definition and operator guidance
|
||||
- `usage-guide.md`, `design-doc.md`, `troubleshooting.md` — additional usage and design documentation
|
||||
- `package.json` — package metadata, dependencies, and npm scripts
|
||||
|
||||
- [`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
|
||||
## Operations
|
||||
|
||||
## 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
|
||||
There is no server or deployment manifest. Operate the tool from the project being mapped and keep generated maps under the target project's normal review and version-control practices.
|
||||
|
||||
@@ -44,8 +44,10 @@ When project-map artifacts exist in the repo:
|
||||
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>]`
|
||||
6. if validation shows localized discrepancies, run `project_map_validate` with `fix=true` (tool) or `project-map validate --fix` (CLI)
|
||||
7. only if discrepancies are widespread or structural, run `project_map_reinit` (tool) or `project-map reinit` (CLI)
|
||||
8. for targeted exploration, use `project_map_context <query>` (tool) or `project-map context <query>` (CLI)
|
||||
9. in `strict` mode, only bypass the protocol-path guard with an explicit marker: `[PI_MAP_BYPASS: <brief justification>]`
|
||||
|
||||
## Prompt injection modes
|
||||
|
||||
@@ -102,12 +104,15 @@ Create `.pi-project-map.json` in the project root:
|
||||
"workflowHintCap": 5,
|
||||
"llmProvider": "openai",
|
||||
"llmModel": "gpt-4o-mini",
|
||||
"reinitFullThresholdPercent": 10,
|
||||
"ignorePatterns": ["node_modules", ".git", "dist", "build"]
|
||||
}
|
||||
```
|
||||
|
||||
Providing `ignorePatterns` replaces the built-in default list, so include any defaults you want to keep.
|
||||
|
||||
- `reinitFullThresholdPercent` — when `project_map_reinit` targets a subtree that covers more than this percentage of project files, it falls back to full regeneration
|
||||
|
||||
Knobs that matter in practice:
|
||||
- `promptInjectionMode` — `off` | `advisory` | `strong` | `strict`
|
||||
- `contextBudgetPercent` / `contextBudgetMaxTokens` — caps automatic map/index injection
|
||||
@@ -122,5 +127,5 @@ Knobs that matter in practice:
|
||||
| `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_reinit` | `project-map reinit [path]` | Smart regeneration: recomputes the target subtree plus ancestors, and only falls back to full regeneration when the subtree covers more than `reinitFullThresholdPercent` of project files (default 10%). |
|
||||
| `project_map_context` | `project-map context <query>` | Retrieve a ranked context bundle for a natural-language query. |
|
||||
|
||||
@@ -267,6 +267,8 @@ Payload fallback scanning is handled inside `context`-level decision logic; ther
|
||||
|
||||
`detectEditIntent()` and `detectArchitectureSensitiveReasoning()` provide heuristic fallback for generic turns.
|
||||
|
||||
In addition to marker-based deduplication, `before_agent_start` scans the active session context via `ctx.sessionManager.buildSessionContext()` for an existing `pi-project-map-hint` custom message. If one is already present in the current branch, the handler skips injection entirely. This prevents duplicate visible hints in advisory/pre-init modes and duplicate hidden hints in strong/strict modes when the session context already contains the guidance. The hint is automatically re-injected after compaction or `/tree` navigation removes it from the active path.
|
||||
|
||||
### 7.4 Protocol path and strict bypass
|
||||
|
||||
The **protocol path** is present when outgoing context contains:
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# fixtures (index)
|
||||
dir: fixtures
|
||||
|
||||
## role
|
||||
Provides test data and setup utilities for automated testing across the project.
|
||||
## parent
|
||||
index: ./.pi-map.index.md
|
||||
map: ./.pi-map.md
|
||||
## children
|
||||
- fixtures/sample-project
|
||||
index: fixtures/sample-project/.pi-map.index.md
|
||||
map: fixtures/sample-project/.pi-map.md
|
||||
## files
|
||||
## links
|
||||
index: fixtures/.pi-map.index.md
|
||||
map: fixtures/.pi-map.md
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,18 @@
|
||||
# fixtures
|
||||
dir: fixtures
|
||||
|
||||
index: fixtures/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Provides test data and setup utilities for automated testing across the project.
|
||||
## files
|
||||
## arch
|
||||
Simple static data fixtures with possible factory/helper patterns for consistent test state generation.
|
||||
## tags
|
||||
-
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -1,4 +1,3 @@
|
||||
dist/
|
||||
node_modules/
|
||||
.pi-map.md
|
||||
.env
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# fixtures/sample-project (index)
|
||||
dir: fixtures/sample-project
|
||||
|
||||
## role
|
||||
Provides a minimal sample Node.js/TypeScript project fixture for testing and demonstrating the pi-project-map functionality.
|
||||
## parent
|
||||
index: fixtures/.pi-map.index.md
|
||||
map: fixtures/.pi-map.md
|
||||
## children
|
||||
- fixtures/sample-project/docs
|
||||
index: fixtures/sample-project/docs/.pi-map.index.md
|
||||
map: fixtures/sample-project/docs/.pi-map.md
|
||||
- fixtures/sample-project/src
|
||||
index: fixtures/sample-project/src/.pi-map.index.md
|
||||
map: fixtures/sample-project/src/.pi-map.md
|
||||
- fixtures/sample-project/tests
|
||||
index: fixtures/sample-project/tests/.pi-map.index.md
|
||||
map: fixtures/sample-project/tests/.pi-map.md
|
||||
## files
|
||||
- .gitignore
|
||||
- README.md
|
||||
- package.json
|
||||
- tsconfig.json
|
||||
## links
|
||||
index: fixtures/sample-project/.pi-map.index.md
|
||||
map: fixtures/sample-project/.pi-map.md
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,22 @@
|
||||
# fixtures/sample-project
|
||||
dir: fixtures/sample-project
|
||||
|
||||
index: fixtures/sample-project/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Provides a minimal sample Node.js/TypeScript project fixture for testing and demonstrating the pi-project-map functionality.
|
||||
## files
|
||||
- .gitignore | Specifies files and directories for Git to ignore in version control | dep: git
|
||||
- README.md | Provides a brief overview and directory structure for a small test project related to pi-project-map functionality.
|
||||
- package.json | Defines a sample Node.js project configuration with TypeScript build and Vitest testing scripts. | dep: typescript, vitest
|
||||
- tsconfig.json | Configures TypeScript compiler options for a Node.js project targeting ES2022 with strict type checking.
|
||||
## arch
|
||||
Standard Node.js project structure using TypeScript with strict compilation, Vitest for testing, and ES2022 module output.
|
||||
## tags
|
||||
project, typescript, git, readme, node, vitest, package, tsconfig
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,19 @@
|
||||
# fixtures/sample-project/docs (index)
|
||||
dir: fixtures/sample-project/docs
|
||||
|
||||
## role
|
||||
Provides API documentation and usage examples for the sample project's user management functionality.
|
||||
## parent
|
||||
index: fixtures/sample-project/.pi-map.index.md
|
||||
map: fixtures/sample-project/.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- API.md
|
||||
## links
|
||||
index: fixtures/sample-project/docs/.pi-map.index.md
|
||||
map: fixtures/sample-project/docs/.pi-map.md
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,19 @@
|
||||
# fixtures/sample-project/docs
|
||||
dir: fixtures/sample-project/docs
|
||||
|
||||
index: fixtures/sample-project/docs/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Provides API documentation and usage examples for the sample project's user management functionality.
|
||||
## files
|
||||
- API.md | Documents a user management API with user creation/serialization functions and validation utilities
|
||||
## arch
|
||||
Documentation-as-code pattern with markdown-based reference material for external API consumers.
|
||||
## tags
|
||||
api, user, documents, management, creation, serialization, validation, utilities
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,30 @@
|
||||
# fixtures/sample-project/src (index)
|
||||
dir: fixtures/sample-project/src
|
||||
|
||||
## role
|
||||
Entry point module that demonstrates user creation, email validation, and logging for a sample project.
|
||||
## parent
|
||||
index: fixtures/sample-project/.pi-map.index.md
|
||||
map: fixtures/sample-project/.pi-map.md
|
||||
## children
|
||||
- fixtures/sample-project/src/components
|
||||
index: fixtures/sample-project/src/components/.pi-map.index.md
|
||||
map: fixtures/sample-project/src/components/.pi-map.md
|
||||
- fixtures/sample-project/src/models
|
||||
index: fixtures/sample-project/src/models/.pi-map.index.md
|
||||
map: fixtures/sample-project/src/models/.pi-map.md
|
||||
- fixtures/sample-project/src/utils
|
||||
index: fixtures/sample-project/src/utils/.pi-map.index.md
|
||||
map: fixtures/sample-project/src/utils/.pi-map.md
|
||||
## files
|
||||
- index.ts
|
||||
## links
|
||||
index: fixtures/sample-project/src/.pi-map.index.md
|
||||
map: fixtures/sample-project/src/.pi-map.md
|
||||
## workflows
|
||||
- change src behavior
|
||||
read: index.ts
|
||||
- explore src subdirectories
|
||||
index: fixtures/sample-project/src/components/.pi-map.index.md, fixtures/sample-project/src/models/.pi-map.index.md, fixtures/sample-project/src/utils/.pi-map.index.md
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,26 @@
|
||||
# fixtures/sample-project/src
|
||||
dir: fixtures/sample-project/src
|
||||
|
||||
index: fixtures/sample-project/src/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Entry point module that demonstrates user creation, email validation, and logging for a sample project.
|
||||
## files
|
||||
- index.ts | Entry point that creates a user, validates their email, and logs the result | exp: func:main(), call:createUser, call:validateEmail, call:logger.error, call:logger.info | dep: ./models/user.js, ./utils/validation.js, ./utils/logger.js
|
||||
## arch
|
||||
Simple procedural script with direct function calls and sequential execution pattern.
|
||||
## tags
|
||||
user, email, js, main, call:create, call:validate, call:logger.error, call:logger.info
|
||||
## symbols
|
||||
- main
|
||||
- call:createUser
|
||||
- call:validateEmail
|
||||
- call:logger.error
|
||||
- call:logger.info
|
||||
## workflows
|
||||
- change src behavior
|
||||
read: index.ts
|
||||
- explore src subdirectories
|
||||
index: fixtures/sample-project/src/components/.pi-map.index.md, fixtures/sample-project/src/models/.pi-map.index.md, fixtures/sample-project/src/utils/.pi-map.index.md
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,21 @@
|
||||
# fixtures/sample-project/src/components (index)
|
||||
dir: fixtures/sample-project/src/components
|
||||
|
||||
## role
|
||||
Provides reusable UI components for building the application's interface.
|
||||
## parent
|
||||
index: fixtures/sample-project/src/.pi-map.index.md
|
||||
map: fixtures/sample-project/src/.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- Button.tsx
|
||||
- UserCard.tsx
|
||||
## links
|
||||
index: fixtures/sample-project/src/components/.pi-map.index.md
|
||||
map: fixtures/sample-project/src/components/.pi-map.md
|
||||
## workflows
|
||||
- change components behavior
|
||||
read: Button.tsx, UserCard.tsx
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,26 @@
|
||||
# fixtures/sample-project/src/components
|
||||
dir: fixtures/sample-project/src/components
|
||||
|
||||
index: fixtures/sample-project/src/components/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Provides reusable UI components for building the application's interface.
|
||||
## files
|
||||
- Button.tsx | A reusable React button component that renders a styled button with configurable label, visual variant, click handler, and disabled state. | exp: ButtonProps, func:Button({ label, variant = "primary", onClick, disabled = false, }: ButtonProps) → JSX.Element | dep: react, React
|
||||
- UserCard.tsx | Renders a user card component with optional edit and delete action buttons. | exp: UserCardProps, func:UserCard({ user, onEdit, onDelete }: UserCardProps) → JSX.Element, call:onEdit, call:onDelete | dep: react, ../models/user.js, React
|
||||
## arch
|
||||
Simple functional React components with props-based configuration, following a basic presentational component pattern.
|
||||
## tags
|
||||
button, user, card, react, props, call:on, edit, delete
|
||||
## symbols
|
||||
- Button
|
||||
- UserCard
|
||||
- ButtonProps
|
||||
- UserCardProps
|
||||
- call:onEdit
|
||||
- call:onDelete
|
||||
## workflows
|
||||
- change components behavior
|
||||
read: Button.tsx, UserCard.tsx
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,20 @@
|
||||
# fixtures/sample-project/src/models (index)
|
||||
dir: fixtures/sample-project/src/models
|
||||
|
||||
## role
|
||||
Defines the User data model with creation and serialization capabilities including email validation.
|
||||
## parent
|
||||
index: fixtures/sample-project/src/.pi-map.index.md
|
||||
map: fixtures/sample-project/src/.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- user.ts
|
||||
## links
|
||||
index: fixtures/sample-project/src/models/.pi-map.index.md
|
||||
map: fixtures/sample-project/src/models/.pi-map.md
|
||||
## workflows
|
||||
- change models behavior
|
||||
read: user.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,26 @@
|
||||
# fixtures/sample-project/src/models
|
||||
dir: fixtures/sample-project/src/models
|
||||
|
||||
index: fixtures/sample-project/src/models/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Defines the User data model with creation and serialization capabilities including email validation.
|
||||
## files
|
||||
- user.ts | Defines a User interface and provides functions to create and serialize users with email validation. | exp: User, func:createUser(data: Omit<User, "id" | "createdAt">) → User, call:validateEmail, call:crypto.randomUUID, raise:Error, func:serializeUser(user: User) → string, call:JSON.stringify | dep: ../utils/validation.js
|
||||
## arch
|
||||
Domain model pattern with interface-based typing, pure functions for data transformation, and embedded validation logic.
|
||||
## tags
|
||||
user, create, serialize, email, validation, call:validate, call:crypto.random, uuid
|
||||
## symbols
|
||||
- createUser
|
||||
- serializeUser
|
||||
- User
|
||||
- call:validateEmail
|
||||
- call:crypto.randomUUID
|
||||
- raise:Error
|
||||
- call:JSON.stringify
|
||||
## workflows
|
||||
- change models behavior
|
||||
read: user.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,21 @@
|
||||
# fixtures/sample-project/src/utils (index)
|
||||
dir: fixtures/sample-project/src/utils
|
||||
|
||||
## role
|
||||
Provides foundational cross-cutting utility functions for logging and input validation used throughout the application.
|
||||
## parent
|
||||
index: fixtures/sample-project/src/.pi-map.index.md
|
||||
map: fixtures/sample-project/src/.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- logger.ts
|
||||
- validation.ts
|
||||
## links
|
||||
index: fixtures/sample-project/src/utils/.pi-map.index.md
|
||||
map: fixtures/sample-project/src/utils/.pi-map.md
|
||||
## workflows
|
||||
- change utils behavior
|
||||
read: logger.ts, validation.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,28 @@
|
||||
# fixtures/sample-project/src/utils
|
||||
dir: fixtures/sample-project/src/utils
|
||||
|
||||
index: fixtures/sample-project/src/utils/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Provides foundational cross-cutting utility functions for logging and input validation used throughout the application.
|
||||
## files
|
||||
- logger.ts | Provides a simple typed console logger with timestamps and convenience wrappers for different log levels. | exp: LogLevel, func:log(level: LogLevel, message: string) → void, call:new Date().toISOString, call:console.log, call:level.toUpperCase, func:debug(message: string) → void, call:log, func:info(message: string) → void, call:log, func:warn(message: string) → void, call:log, func:error(message: string) → void, call:log
|
||||
- validation.ts | Provides basic string validation utilities for emails, non-empty checks, and minimum length requirements. | exp: func:validateEmail(email: string) → boolean, call:EMAIL_REGEX.test, func:validateNotEmpty(value: string) → boolean, call:value.trim, func:validateMinLength(value: string, min: number) → boolean
|
||||
## arch
|
||||
Flat utility module pattern with pure functions, no dependencies between modules, each exporting independent typed helper functions.
|
||||
## tags
|
||||
call:log, validate, log, logger, validation, empty, length, provides
|
||||
## symbols
|
||||
- log
|
||||
- debug
|
||||
- info
|
||||
- warn
|
||||
- error
|
||||
- validateEmail
|
||||
- validateNotEmpty
|
||||
- validateMinLength
|
||||
## workflows
|
||||
- change utils behavior
|
||||
read: logger.ts, validation.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,21 @@
|
||||
# fixtures/sample-project/tests (index)
|
||||
dir: fixtures/sample-project/tests
|
||||
|
||||
## role
|
||||
Provides unit test coverage for user model behavior and string validation utilities in the sample project.
|
||||
## parent
|
||||
index: fixtures/sample-project/.pi-map.index.md
|
||||
map: fixtures/sample-project/.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- user.test.ts
|
||||
- validation.test.ts
|
||||
## links
|
||||
index: fixtures/sample-project/tests/.pi-map.index.md
|
||||
map: fixtures/sample-project/tests/.pi-map.md
|
||||
## workflows
|
||||
- update tests tests
|
||||
read: user.test.ts, validation.test.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,21 @@
|
||||
# fixtures/sample-project/tests
|
||||
dir: fixtures/sample-project/tests
|
||||
|
||||
index: fixtures/sample-project/tests/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Provides unit test coverage for user model behavior and string validation utilities in the sample project.
|
||||
## files
|
||||
- user.test.ts | Unit tests for user model creation, validation, and serialization | dep: vitest, ../src/models/user.js
|
||||
- validation.test.ts | Unit tests for string validation utility functions | dep: vitest, ../src/utils/validation.js
|
||||
## arch
|
||||
Standard test suite using isolated unit tests with file-based grouping by domain concern (model vs. utility).
|
||||
## tags
|
||||
validation, unit, tests, user, vitest, src, js, user.test
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
- update tests tests
|
||||
read: user.test.ts, validation.test.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -1,11 +0,0 @@
|
||||
// Pi skill prompt hook
|
||||
// Injected into every prompt to remind the agent to maintain .pi-map.md files
|
||||
|
||||
export const MAINTENANCE_INSTRUCTION = `
|
||||
If you modify any source file, run \`project-map:patch <file-path>\` to update the analysis.
|
||||
If you suspect staleness, run \`project-map:validate\`.
|
||||
`;
|
||||
|
||||
export function injectPrompt(originalPrompt: string): string {
|
||||
return `${originalPrompt}\n\n---\n${MAINTENANCE_INSTRUCTION}`;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
# openspec (index)
|
||||
dir: openspec
|
||||
|
||||
## role
|
||||
Defines configuration and project conventions for a TypeScript CLI tool that generates AI-oriented codebase orientation maps for coding agents.
|
||||
## parent
|
||||
index: ./.pi-map.index.md
|
||||
map: ./.pi-map.md
|
||||
## children
|
||||
- openspec/changes
|
||||
index: openspec/changes/.pi-map.index.md
|
||||
map: openspec/changes/.pi-map.md
|
||||
## files
|
||||
- config.yaml
|
||||
- project.md
|
||||
## links
|
||||
index: openspec/.pi-map.index.md
|
||||
map: openspec/.pi-map.md
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,20 @@
|
||||
# openspec
|
||||
dir: openspec
|
||||
|
||||
index: openspec/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Defines configuration and project conventions for a TypeScript CLI tool that generates AI-oriented codebase orientation maps for coding agents.
|
||||
## files
|
||||
- config.yaml | Defines project configuration, stack metadata, and software-driven development (SDD) workflow rules for a TypeScript-based CLI tool that generates AI-oriented codebase map files. | dep: TypeScript, Node.js, Vitest, npm, openspec
|
||||
- project.md | Defines project context and conventions for pi-project-map, a Pi skill and CLI tool that generates hierarchical `.pi-map.md` orientation files for coding agents.
|
||||
## arch
|
||||
YAML-driven configuration with software-driven development (SDD) workflow rules, hierarchical markdown output generation, and Pi skill integration for AI agent context provision.
|
||||
## tags
|
||||
project, map, defines, typescript, cli, tool, generates, config
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,20 @@
|
||||
# openspec/changes (index)
|
||||
dir: openspec/changes
|
||||
|
||||
## role
|
||||
Manages change tracking, versioning, and audit history for OpenAPI specification modifications
|
||||
## parent
|
||||
index: openspec/.pi-map.index.md
|
||||
map: openspec/.pi-map.md
|
||||
## children
|
||||
- openspec/changes/archive
|
||||
index: openspec/changes/archive/.pi-map.index.md
|
||||
map: openspec/changes/archive/.pi-map.md
|
||||
## files
|
||||
## links
|
||||
index: openspec/changes/.pi-map.index.md
|
||||
map: openspec/changes/.pi-map.md
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,18 @@
|
||||
# openspec/changes
|
||||
dir: openspec/changes
|
||||
|
||||
index: openspec/changes/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Manages change tracking, versioning, and audit history for OpenAPI specification modifications
|
||||
## files
|
||||
## arch
|
||||
Event-sourced or changelog-based pattern with immutable change records and versioned snapshots
|
||||
## tags
|
||||
-
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,26 @@
|
||||
# openspec/changes/archive (index)
|
||||
dir: openspec/changes/archive
|
||||
|
||||
## role
|
||||
Provides persistent storage and retrieval of historical change records in an archived format for audit trails and long-term data retention.
|
||||
## parent
|
||||
index: openspec/changes/.pi-map.index.md
|
||||
map: openspec/changes/.pi-map.md
|
||||
## children
|
||||
- openspec/changes/archive/2026-06-11-layered-map-protocol
|
||||
index: openspec/changes/archive/2026-06-11-layered-map-protocol/.pi-map.index.md
|
||||
map: openspec/changes/archive/2026-06-11-layered-map-protocol/.pi-map.md
|
||||
- openspec/changes/archive/2026-06-11-map-context-retrieval
|
||||
index: openspec/changes/archive/2026-06-11-map-context-retrieval/.pi-map.index.md
|
||||
map: openspec/changes/archive/2026-06-11-map-context-retrieval/.pi-map.md
|
||||
- openspec/changes/archive/2026-06-11-project-map-prompt-injection
|
||||
index: openspec/changes/archive/2026-06-11-project-map-prompt-injection/.pi-map.index.md
|
||||
map: openspec/changes/archive/2026-06-11-project-map-prompt-injection/.pi-map.md
|
||||
## files
|
||||
## links
|
||||
index: openspec/changes/archive/.pi-map.index.md
|
||||
map: openspec/changes/archive/.pi-map.md
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,18 @@
|
||||
# openspec/changes/archive
|
||||
dir: openspec/changes/archive
|
||||
|
||||
index: openspec/changes/archive/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Provides persistent storage and retrieval of historical change records in an archived format for audit trails and long-term data retention.
|
||||
## files
|
||||
## arch
|
||||
Simple archive storage pattern using file-based serialization with read/write operations for immutable change log records, likely with date-based or sequential naming conventions.
|
||||
## tags
|
||||
-
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,26 @@
|
||||
# openspec/changes/archive/2026-06-11-layered-map-protocol (index)
|
||||
dir: openspec/changes/archive/2026-06-11-layered-map-protocol
|
||||
|
||||
## role
|
||||
Contains archived specification documents for a deprecated layered map protocol that introduced paired navigation artifacts to replace bulk-loading of map files with a tiered directory-level routing system.
|
||||
## parent
|
||||
index: openspec/changes/archive/.pi-map.index.md
|
||||
map: openspec/changes/archive/.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- apply-progress.md
|
||||
- archive-report.md
|
||||
- design.md
|
||||
- proposal.md
|
||||
- spec.md
|
||||
- sync-report.md
|
||||
- tasks.md
|
||||
- verify-report.md
|
||||
## links
|
||||
index: openspec/changes/archive/2026-06-11-layered-map-protocol/.pi-map.index.md
|
||||
map: openspec/changes/archive/2026-06-11-layered-map-protocol/.pi-map.md
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,26 @@
|
||||
# openspec/changes/archive/2026-06-11-layered-map-protocol
|
||||
dir: openspec/changes/archive/2026-06-11-layered-map-protocol
|
||||
|
||||
index: openspec/changes/archive/2026-06-11-layered-map-protocol/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Contains archived specification documents for a deprecated layered map protocol that introduced paired navigation artifacts to replace bulk-loading of map files with a tiered directory-level routing system.
|
||||
## files
|
||||
- apply-progress.md | Documents the completion status and summary of implemented features for an "Apply Progress" project or milestone.
|
||||
- archive-report.md | Documents the archival status and metadata for a deprecated layered map protocol specification directory.
|
||||
- design.md | Design document for a paired navigation-first artifact model that generates both routing indexes and orientation rich-maps from a shared intermediate directory model while preserving existing pipeline behavior. | dep: spec.md, src/format.ts, src/init.ts, src/patch.ts, src/validate.ts, pi-extension.ts, README.md, SKILL.md, design-doc.md, CLI argument parsing, YAML config handling
|
||||
- proposal.md | Proposes a layered navigation protocol using paired index/map artifacts to replace bulk-loading of map files with a tiered, directory-level routing system.
|
||||
- spec.md | Specifies a layered navigation protocol for project maps using paired index and rich map artifacts per directory with defined generation, patching, and validation behaviors.
|
||||
- sync-report.md | Documents that a canonical spec synchronization was not performed due to legacy flat change artifact structure, with user-approved archival fallback
|
||||
- tasks.md | Defines a phased task plan for implementing a layered paired-map protocol with directory-level `.pi-map.md` and `.pi-map.index.md` artifacts, routing metadata, patch sizing, validation, and documentation. | dep: design.md, pi-extension.ts, SKILL.md, README.md, design-doc.md, npm/node toolchain
|
||||
- verify-report.md | Documents verification results for a code change implementing a layered map protocol with paired artifacts. | dep: npm, vitest, node, typescript
|
||||
## arch
|
||||
Document-driven specification archive using a layered architecture with paired index/map artifacts per directory, phased implementation tasks, and formal verification/synchronization reporting, preserved for historical reference despite legacy flat structure preventing canonical sync.
|
||||
## tags
|
||||
map, md, report, design, ts, layered, protocol, directory
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,26 @@
|
||||
# openspec/changes/archive/2026-06-11-map-context-retrieval (index)
|
||||
dir: openspec/changes/archive/2026-06-11-map-context-retrieval
|
||||
|
||||
## role
|
||||
Archives a completed specification change package for implementing metadata-driven context retrieval capabilities in a project-map tool.
|
||||
## parent
|
||||
index: openspec/changes/archive/.pi-map.index.md
|
||||
map: openspec/changes/archive/.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- apply-progress.md
|
||||
- archive-report.md
|
||||
- design.md
|
||||
- proposal.md
|
||||
- spec.md
|
||||
- sync-report.md
|
||||
- tasks.md
|
||||
- verify-report.md
|
||||
## links
|
||||
index: openspec/changes/archive/2026-06-11-map-context-retrieval/.pi-map.index.md
|
||||
map: openspec/changes/archive/2026-06-11-map-context-retrieval/.pi-map.md
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,26 @@
|
||||
# openspec/changes/archive/2026-06-11-map-context-retrieval
|
||||
dir: openspec/changes/archive/2026-06-11-map-context-retrieval
|
||||
|
||||
index: openspec/changes/archive/2026-06-11-map-context-retrieval/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Archives a completed specification change package for implementing metadata-driven context retrieval capabilities in a project-map tool.
|
||||
## files
|
||||
- apply-progress.md | Documents the completion status of a project implementing deterministic index-first context retrieval via tool and CLI, along with retrieval documentation and skill guidance.
|
||||
- archive-report.md | Documents the archival of a set of specification change documents including metadata about archive location, mode, and preserved inputs.
|
||||
- design.md | Design document for a lightweight context retrieval system that scans paired project-map metadata to find and return relevant code context as a markdown bundle for AI agents. | dep: pi-extension.ts, src/index.ts, src/cli/*, paired-artifact parser/model from layered protocol, .pi-map.index.md, .pi-map.md
|
||||
- proposal.md | Proposes a "Map Context Retrieval" tool that enables natural-language queries to return compact, metadata-driven context bundles from a layered map protocol. | dep: layered-map-protocol, Pi tool, CLI
|
||||
- spec.md | Defines a specification for adding a retrieval-oriented `context` command to a project-map tool that converts user tasks into compact routing bundles for LLM agents | dep: layered-map-protocol, project-map (Pi tool/CLI), markdown output formatting
|
||||
- sync-report.md | Documents that a canonical spec synchronization was not performed due to legacy flat change artifact structure, with user-approved archival fallback
|
||||
- tasks.md | Defines implementation tasks for adding map context retrieval functionality to a Pi tool, using paired index/map metadata for ranked, query-based context retrieval. | dep: design.md, Pi tool, project-map context, npm (typecheck, test, lint)
|
||||
- verify-report.md | Documents verification results for a code change implementing deterministic index-first context retrieval. | dep: npm, vitest, node, CLI tooling
|
||||
## arch
|
||||
Flat archival directory structure preserving legacy change artifacts (specification, design, proposal, tasks, verification, sync, apply-progress, archive-report) without hierarchical organization.
|
||||
## tags
|
||||
map, context, retrieval, report, documents, project, index, tool
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,26 @@
|
||||
# openspec/changes/archive/2026-06-11-project-map-prompt-injection (index)
|
||||
dir: openspec/changes/archive/2026-06-11-project-map-prompt-injection
|
||||
|
||||
## role
|
||||
Archives a completed prompt-injection feature specification that enables runtime injection of project-map artifacts into LLM context with configurable guidance modes and budget controls.
|
||||
## parent
|
||||
index: openspec/changes/archive/.pi-map.index.md
|
||||
map: openspec/changes/archive/.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- apply-progress.md
|
||||
- archive-report.md
|
||||
- design.md
|
||||
- proposal.md
|
||||
- spec.md
|
||||
- sync-report.md
|
||||
- tasks.md
|
||||
- verify-report.md
|
||||
## links
|
||||
index: openspec/changes/archive/2026-06-11-project-map-prompt-injection/.pi-map.index.md
|
||||
map: openspec/changes/archive/2026-06-11-project-map-prompt-injection/.pi-map.md
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,26 @@
|
||||
# openspec/changes/archive/2026-06-11-project-map-prompt-injection
|
||||
dir: openspec/changes/archive/2026-06-11-project-map-prompt-injection
|
||||
|
||||
index: openspec/changes/archive/2026-06-11-project-map-prompt-injection/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Archives a completed prompt-injection feature specification that enables runtime injection of project-map artifacts into LLM context with configurable guidance modes and budget controls.
|
||||
## files
|
||||
- apply-progress.md | Documents the completion status and implementation details of a prompt-injection feature delivered across five incremental slices.
|
||||
- archive-report.md | Documents the archival status and metadata of a deprecated project change specification directory.
|
||||
- design.md | Design document for adding a runtime prompt injection layer that guides LLM behavior using paired map/index artifacts through configurable modes, canonical markers, and budgeted context expansion. | dep: pi-extension.ts, src/config.ts, spec.md, event.messages, before_agent_start, before_provider_request, context hooks, .pi-project-map.json, .pi-map.index.md, .pi-map.md
|
||||
- proposal.md | Proposes a runtime prompt-injection policy for project-map artifacts with configurable guidance modes, hybrid context budgets, and reinjection avoidance
|
||||
- spec.md | Defines a specification for automatic runtime prompt injection of project map/index artifacts with configurable guidance modes, budgeted expansion, and reinjection avoidance based on actual outgoing context scanning.
|
||||
- sync-report.md | Documents that a canonical spec synchronization was not performed due to legacy flat change artifact structure, with user-approved archival fallback
|
||||
- tasks.md | Defines phased implementation tasks for a prompt injection policy system that controls how project map artifacts are injected into LLM context with budget constraints, mode semantics, and reinjection avoidance.
|
||||
- verify-report.md | Documents verification results for a prompt-injection security feature implementation in a software project.
|
||||
## arch
|
||||
Document-driven specification architecture using phased slice-based delivery (proposal → design → spec → tasks → verification), with flat artifact structure and canonical marker-based context expansion patterns.
|
||||
## tags
|
||||
map, prompt, injection, project, report, documents, artifacts, context
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
-
|
||||
## dirty
|
||||
-
|
||||
+49
-11
@@ -81,6 +81,25 @@ function renderProgressBar(
|
||||
return `[${bar}] ${completed}/${total}${file}`;
|
||||
}
|
||||
|
||||
const HINT_CUSTOM_TYPE = "pi-project-map-hint";
|
||||
|
||||
function hintAlreadyInContext(ctx: any): boolean {
|
||||
const manager = ctx?.sessionManager;
|
||||
if (!manager || typeof manager.buildSessionContext !== "function") {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { messages } = manager.buildSessionContext();
|
||||
if (!Array.isArray(messages)) return false;
|
||||
|
||||
return messages.some(
|
||||
(m: any) =>
|
||||
m &&
|
||||
m.role === "custom" &&
|
||||
m.customType === HINT_CUSTOM_TYPE,
|
||||
);
|
||||
}
|
||||
|
||||
export default function (pi: ExtensionAPI) {
|
||||
let lastRootPairMtimes: import("./src/index.js").RootPairMtimes = {};
|
||||
pi.registerTool({
|
||||
@@ -190,11 +209,12 @@ export default function (pi: ExtensionAPI) {
|
||||
name: "project_map_validate",
|
||||
label: "Project Map Validate",
|
||||
description:
|
||||
"Check all .pi-map.md / .pi-map.index.md files for staleness and discrepancies",
|
||||
"Check all .pi-map.md / .pi-map.index.md files for staleness and discrepancies. Optionally repair them.",
|
||||
promptSnippet: "Validate paired project map/index artifacts for accuracy",
|
||||
promptGuidelines: [
|
||||
"Use project_map_validate before making architectural decisions if you suspect stale data",
|
||||
"Use project_map_validate to detect files that were deleted or added outside the agent",
|
||||
"Set fix=true to repair localized discrepancies without running a full project_map_reinit",
|
||||
],
|
||||
parameters: Type.Object({
|
||||
path: Type.Optional(
|
||||
@@ -202,13 +222,23 @@ export default function (pi: ExtensionAPI) {
|
||||
description: "Project root path (default: current directory)",
|
||||
}),
|
||||
),
|
||||
fix: Type.Optional(
|
||||
Type.Boolean({
|
||||
description:
|
||||
"Repair discrepancies automatically (default: false). Requires an LLM client.",
|
||||
default: false,
|
||||
}),
|
||||
),
|
||||
}),
|
||||
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
||||
try {
|
||||
const targetPath = params.path || ctx.cwd;
|
||||
const client = params.fix ? getPiLLMClient(ctx) : undefined;
|
||||
const result = await validateMaps(targetPath, {
|
||||
fix: false,
|
||||
fix: params.fix ?? false,
|
||||
verbose: false,
|
||||
llmClient: client,
|
||||
cacheDir: ctx.cwd,
|
||||
});
|
||||
const text = result.clean
|
||||
? "All .pi-map.md files are clean."
|
||||
@@ -234,12 +264,14 @@ export default function (pi: ExtensionAPI) {
|
||||
name: "project_map_reinit",
|
||||
label: "Project Map Reinit",
|
||||
description:
|
||||
"Force full regeneration of all .pi-map.md / .pi-map.index.md artifacts",
|
||||
"Regenerate .pi-map.md / .pi-map.index.md artifacts for a subtree plus its ancestors, falling back to full regeneration only when the subtree covers more than the configured percentage of project files (default: 10%)",
|
||||
promptSnippet:
|
||||
"Force full regeneration of paired project map/index artifacts",
|
||||
"Regenerate paired project map/index artifacts for a subtree or the whole project",
|
||||
promptGuidelines: [
|
||||
"Use project_map_reinit when validation shows widespread staleness",
|
||||
"Use project_map_reinit after pulling major changes from version control",
|
||||
"Use project_map_reinit only after project_map_patch and project_map_validate --fix cannot resolve the staleness",
|
||||
"For localized changes, prefer project_map_patch <changed-file> or project_map_validate with fix=true",
|
||||
"Use project_map_reinit for widespread structural damage (e.g. broken links across many directories) or after large merges",
|
||||
"When reinit falls back to full regeneration, it is because the target subtree covers more than the configured reinitFullThresholdPercent of project files",
|
||||
],
|
||||
parameters: Type.Object({
|
||||
path: Type.Optional(
|
||||
@@ -345,13 +377,15 @@ export default function (pi: ExtensionAPI) {
|
||||
|
||||
if (dirtyFiles.length > 0) {
|
||||
ctx.ui.notify(
|
||||
`pi-project-map: ${dirtyFiles.length} dirty packages detected. Run project_map_validate or project_map_reinit.`,
|
||||
`pi-project-map: ${dirtyFiles.length} dirty package(s) detected. Run project_map_validate first; use project_map_reinit only if staleness is widespread.`,
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Inject maintenance instructions before agent starts
|
||||
// Inject maintenance instructions before agent starts, but only once
|
||||
// within the current branch of context. Re-inject after compaction or
|
||||
// tree navigation removes the hint from the active path.
|
||||
pi.on("before_agent_start", async (_event, _ctx) => {
|
||||
const config = loadConfig(_ctx.cwd);
|
||||
const mapFiles = findPiMapFiles(_ctx.cwd);
|
||||
@@ -366,9 +400,10 @@ export default function (pi: ExtensionAPI) {
|
||||
if (!modeAllowsPreInitHint(config.promptInjectionMode)) {
|
||||
return {};
|
||||
}
|
||||
if (hintAlreadyInContext(_ctx)) return {};
|
||||
return {
|
||||
message: {
|
||||
customType: "pi-project-map-hint",
|
||||
customType: HINT_CUSTOM_TYPE,
|
||||
content: buildPreInitHint(),
|
||||
display: true,
|
||||
},
|
||||
@@ -378,9 +413,10 @@ export default function (pi: ExtensionAPI) {
|
||||
// Slice 4: advisory mode shows a visible lightweight reminder after init.
|
||||
// No root-pair preload, no per-turn reinjection.
|
||||
if (config.promptInjectionMode === "advisory") {
|
||||
if (hintAlreadyInContext(_ctx)) return {};
|
||||
return {
|
||||
message: {
|
||||
customType: "pi-project-map-hint",
|
||||
customType: HINT_CUSTOM_TYPE,
|
||||
content: buildAdvisoryReminder(),
|
||||
display: true,
|
||||
},
|
||||
@@ -414,12 +450,14 @@ export default function (pi: ExtensionAPI) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (hintAlreadyInContext(_ctx)) return {};
|
||||
|
||||
// Slice 2: post-init root-pair preload + budgeted expansion
|
||||
const contextWindow = discoverContextWindow(_ctx);
|
||||
const payload = buildInjectionPayload(_ctx.cwd, config, contextWindow);
|
||||
return {
|
||||
message: {
|
||||
customType: "pi-project-map-hint",
|
||||
customType: HINT_CUSTOM_TYPE,
|
||||
content: payload.content,
|
||||
display: payload.display,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# src (index)
|
||||
dir: src
|
||||
|
||||
## role
|
||||
A project mapping and codebase navigation system that generates, maintains, and queries AI-readable documentation artifacts for software projects.
|
||||
## parent
|
||||
index: ./.pi-map.index.md
|
||||
map: ./.pi-map.md
|
||||
## children
|
||||
- src/ast
|
||||
index: src/ast/.pi-map.index.md
|
||||
map: src/ast/.pi-map.md
|
||||
- src/cli
|
||||
index: src/cli/.pi-map.index.md
|
||||
map: src/cli/.pi-map.md
|
||||
- src/llm
|
||||
index: src/llm/.pi-map.index.md
|
||||
map: src/llm/.pi-map.md
|
||||
- src/types
|
||||
index: src/types/.pi-map.index.md
|
||||
map: src/types/.pi-map.md
|
||||
## files
|
||||
- cli.ts
|
||||
- config.ts
|
||||
- directory-model.ts
|
||||
- discover.ts
|
||||
- format.ts
|
||||
- index.ts
|
||||
- init.ts
|
||||
- merge.ts
|
||||
- patch.ts
|
||||
- prompt-injection.ts
|
||||
- retrieve.ts
|
||||
- routing-metadata.ts
|
||||
- validate.ts
|
||||
## links
|
||||
index: src/.pi-map.index.md
|
||||
map: src/.pi-map.md
|
||||
## workflows
|
||||
- change src behavior
|
||||
read: cli.ts, config.ts, directory-model.ts
|
||||
- change src CLI
|
||||
read: cli.ts
|
||||
- change src config
|
||||
read: config.ts
|
||||
- explore src subdirectories
|
||||
index: src/ast/.pi-map.index.md, src/cli/.pi-map.index.md, src/llm/.pi-map.index.md
|
||||
## dirty
|
||||
-
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,20 @@
|
||||
# src/ast (index)
|
||||
dir: src/ast
|
||||
|
||||
## role
|
||||
Extracts structured metadata from source code ASTs across multiple languages to enable code analysis and dependency understanding.
|
||||
## parent
|
||||
index: src/.pi-map.index.md
|
||||
map: src/.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- ast-extract.ts
|
||||
## links
|
||||
index: src/ast/.pi-map.index.md
|
||||
map: src/ast/.pi-map.md
|
||||
## workflows
|
||||
- change ast behavior
|
||||
read: ast-extract.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,27 @@
|
||||
# src/ast
|
||||
dir: src/ast
|
||||
|
||||
index: src/ast/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Extracts structured metadata from source code ASTs across multiple languages to enable code analysis and dependency understanding.
|
||||
## files
|
||||
- ast-extract.ts | Extracts AST-based metadata (exports, dependencies, classes, functions, method calls, and exceptions) from source code files across multiple languages using Tree-sitter parsers. | exp: ASTFileData, func:extractFileAST(filePath: string) → Promise<ASTFileData | null>, call:extname(filePath).toLowerCase, call:require, call:parser.setLanguage, call:readFileSync, call:parser.parse, call:extractPythonData, call:extractTypeScriptData, call:extractGoData, call:extractExportsFromTree, call:extractDepsFromTree | dep: fs, path, tree-sitter, tree-sitter-typescript, tree-sitter-python, tree-sitter-go, tree-sitter-rust, tree-sitter-java, tree-sitter-c, tree-sitter-cpp, tree-sitter-ruby
|
||||
## arch
|
||||
Language-agnostic parser abstraction using Tree-sitter grammars with unified extraction pipeline for cross-language code analysis.
|
||||
## tags
|
||||
tree, sitter, call:extract, data, ast, extract, python, go
|
||||
## symbols
|
||||
- extractFileAST
|
||||
- ASTFileData
|
||||
- call:extname(filePath).toLowerCase
|
||||
- call:require
|
||||
- call:parser.setLanguage
|
||||
- call:readFileSync
|
||||
- call:parser.parse
|
||||
- call:extractPythonData
|
||||
## workflows
|
||||
- change ast behavior
|
||||
read: ast-extract.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,22 @@
|
||||
# src/cli (index)
|
||||
dir: src/cli
|
||||
|
||||
## role
|
||||
Command-line interface entry point for a project mapping tool that manages hierarchical `.pi-map.md` files through generation, patching, validation, and LLM-powered context retrieval.
|
||||
## parent
|
||||
index: src/.pi-map.index.md
|
||||
map: src/.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- cli.ts
|
||||
## links
|
||||
index: src/cli/.pi-map.index.md
|
||||
map: src/cli/.pi-map.md
|
||||
## workflows
|
||||
- change cli behavior
|
||||
read: cli.ts
|
||||
- change cli CLI
|
||||
read: cli.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,22 @@
|
||||
# src/cli
|
||||
dir: src/cli
|
||||
|
||||
index: src/cli/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Command-line interface entry point for a project mapping tool that manages hierarchical `.pi-map.md` files through generation, patching, validation, and LLM-powered context retrieval.
|
||||
## files
|
||||
- cli.ts | CLI entry point for a project mapping tool that generates, patches, validates, and retrieves context from hierarchical `.pi-map.md` files using LLM-powered analysis. | dep: ../init.js, ../patch.js, ../validate.js, ../discover.js, ../retrieve.js, ../llm/llm-client.js, ../config.js, picocolors, process, fs (implied via require)
|
||||
## arch
|
||||
Single-file CLI facade with command routing to core engine services, likely using a command pattern or direct service delegation for map lifecycle operations.
|
||||
## tags
|
||||
js, cli, llm, entry, point, project, mapping, tool
|
||||
## symbols
|
||||
-
|
||||
## workflows
|
||||
- change cli behavior
|
||||
read: cli.ts
|
||||
- change cli CLI
|
||||
read: cli.ts
|
||||
## dirty
|
||||
-
|
||||
+2
-2
@@ -28,7 +28,7 @@ function printUsage() {
|
||||
` project-map ${pc.cyan("validate")} [--fix] [path] Check for stale/missing/orphaned entries`,
|
||||
);
|
||||
console.log(
|
||||
` project-map ${pc.cyan("reinit")} [path] Force full regeneration`,
|
||||
` project-map ${pc.cyan("reinit")} [path] Regenerate a subtree or the whole project`,
|
||||
);
|
||||
console.log(
|
||||
` project-map ${pc.cyan("--help")} Show this help message`,
|
||||
@@ -252,7 +252,7 @@ async function main() {
|
||||
await reinitPath(targetPath, {
|
||||
verbose: false,
|
||||
llmClient: client,
|
||||
cacheDir: targetPath,
|
||||
cacheDir: process.cwd(),
|
||||
onProgress: (info) => {
|
||||
const line = renderProgressBar(
|
||||
info.completed,
|
||||
|
||||
@@ -16,6 +16,8 @@ export interface SkillConfig {
|
||||
promptInjectionMode: PromptInjectionMode;
|
||||
contextBudgetPercent: number;
|
||||
contextBudgetMaxTokens: number;
|
||||
/** Percentage of project files a target subtree must cover before reinit falls back to full regeneration (default: 10). */
|
||||
reinitFullThresholdPercent: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_CONFIG: SkillConfig = {
|
||||
@@ -51,6 +53,7 @@ export const DEFAULT_CONFIG: SkillConfig = {
|
||||
promptInjectionMode: "strong",
|
||||
contextBudgetPercent: 15,
|
||||
contextBudgetMaxTokens: 100_000,
|
||||
reinitFullThresholdPercent: 10,
|
||||
};
|
||||
|
||||
export function loadConfig(cwd: string = process.cwd()): SkillConfig {
|
||||
|
||||
+131
-4
@@ -7,8 +7,8 @@ import { extractFileLLM, extractPackageLLM } from "./llm/llm-extract.js";
|
||||
import { extractFileAST } from "./ast/ast-extract.js";
|
||||
import { mergeFileData } from "./merge.js";
|
||||
import { processFiles } from "./llm/llm-batch.js";
|
||||
import { writeFileSync } from "fs";
|
||||
import { join } from "path";
|
||||
import { existsSync, writeFileSync } from "fs";
|
||||
import { join, relative, resolve } from "path";
|
||||
import type { LLMClient } from "./llm/llm-client.js";
|
||||
import {
|
||||
createDirectoryModel,
|
||||
@@ -105,6 +105,60 @@ export interface DirectoryContext {
|
||||
isRoot: boolean;
|
||||
}
|
||||
|
||||
export function getAncestorEntries(
|
||||
entries: DirectoryEntry[],
|
||||
ctx: DirectoryContext,
|
||||
entry: DirectoryEntry,
|
||||
): DirectoryEntry[] {
|
||||
const result: DirectoryEntry[] = [];
|
||||
let parent = ctx.parentMap.get(entry.relativePath);
|
||||
while (parent) {
|
||||
const ancestor = entries.find(
|
||||
(candidate) => candidate.relativePath === parent,
|
||||
);
|
||||
if (ancestor) {
|
||||
result.push(ancestor);
|
||||
}
|
||||
parent = ctx.parentMap.get(parent);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function countFiles(entries: DirectoryEntry[]): number {
|
||||
return entries.reduce((sum, e) => sum + e.files.length, 0);
|
||||
}
|
||||
|
||||
function isSubdirectory(parent: string, child: string): boolean {
|
||||
if (parent === ".") return true;
|
||||
if (child === parent) return true;
|
||||
return child.startsWith(`${parent}/`);
|
||||
}
|
||||
|
||||
function findProjectRoot(targetPath: string): string {
|
||||
let current = resolve(targetPath);
|
||||
while (true) {
|
||||
if (existsSync(join(current, ".pi-project-map.json"))) {
|
||||
return current;
|
||||
}
|
||||
if (existsSync(join(current, ".git"))) {
|
||||
return current;
|
||||
}
|
||||
if (existsSync(join(current, "package.json"))) {
|
||||
return current;
|
||||
}
|
||||
const parent = resolve(current, "..");
|
||||
if (parent === current) {
|
||||
return resolve(targetPath);
|
||||
}
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeRelativePath(relPath: string): string {
|
||||
if (!relPath || relPath === ".") return ".";
|
||||
return relPath.replace(/\\/g, "/");
|
||||
}
|
||||
|
||||
export type ArtifactWriteMode = "both" | "map" | "index";
|
||||
|
||||
export function buildDirectoryContext(
|
||||
@@ -244,8 +298,81 @@ export async function reinitPath(
|
||||
path: string,
|
||||
options: InitOptions = {},
|
||||
): Promise<void> {
|
||||
// Full regeneration clears all dirty markers by overwriting every map/index pair
|
||||
await initProject(path, options);
|
||||
const targetPath = resolve(path);
|
||||
const rootPath = findProjectRoot(targetPath);
|
||||
const targetRelPath =
|
||||
rootPath === targetPath
|
||||
? "."
|
||||
: normalizeRelativePath(relative(rootPath, targetPath));
|
||||
const entries = discoverProject(rootPath);
|
||||
const config = loadConfig(rootPath);
|
||||
const threshold = config.reinitFullThresholdPercent;
|
||||
|
||||
const totalFiles = countFiles(entries);
|
||||
if (totalFiles === 0 || targetRelPath === ".") {
|
||||
await initProject(rootPath, options);
|
||||
return;
|
||||
}
|
||||
|
||||
const subtreeEntries = entries.filter((entry) =>
|
||||
isSubdirectory(targetRelPath, entry.relativePath),
|
||||
);
|
||||
const subtreeFiles = countFiles(subtreeEntries);
|
||||
const percentage = (subtreeFiles / totalFiles) * 100;
|
||||
|
||||
if (percentage > threshold) {
|
||||
await initProject(rootPath, options);
|
||||
return;
|
||||
}
|
||||
|
||||
const routingOpts: RoutingMetadataOptions = {
|
||||
tagCap: options.tagCap ?? config.tagCap,
|
||||
workflowHintCap: options.workflowHintCap ?? config.workflowHintCap,
|
||||
};
|
||||
|
||||
const targetEntry = entries.find((e) => e.relativePath === targetRelPath);
|
||||
if (!targetEntry) {
|
||||
await initProject(rootPath, options);
|
||||
return;
|
||||
}
|
||||
|
||||
const changedCtx = buildDirectoryContext(entries, targetEntry);
|
||||
const ancestors = getAncestorEntries(entries, changedCtx, targetEntry);
|
||||
const dirsToRegenerate = new Set<DirectoryEntry>([
|
||||
...subtreeEntries,
|
||||
...ancestors,
|
||||
]);
|
||||
|
||||
const dirs = Array.from(dirsToRegenerate);
|
||||
const filesToRegenerate = countFiles(dirs);
|
||||
let completedFiles = 0;
|
||||
|
||||
for (const entry of dirs) {
|
||||
const ctx = buildDirectoryContext(entries, entry);
|
||||
await generateDirectoryArtifacts(
|
||||
entry,
|
||||
ctx,
|
||||
options.llmClient,
|
||||
options.cacheDir,
|
||||
(info) => {
|
||||
options.onProgress?.({
|
||||
...info,
|
||||
completed: completedFiles + info.completed,
|
||||
total: filesToRegenerate,
|
||||
dir: entry.relativePath,
|
||||
});
|
||||
},
|
||||
routingOpts,
|
||||
"both",
|
||||
);
|
||||
completedFiles += entry.files.length;
|
||||
}
|
||||
|
||||
if (options.verbose !== false) {
|
||||
console.log(
|
||||
`Smart reinit: regenerated ${dirsToRegenerate.size} directories under ${targetRelPath} (${subtreeFiles}/${totalFiles} files, ${percentage.toFixed(1)}%).`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Backward-compatible wrapper for patch/validate compatibility
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# src/llm (index)
|
||||
dir: src/llm
|
||||
|
||||
## role
|
||||
Provides a unified abstraction layer for interacting with multiple LLM providers (OpenAI, Kimi, Pi) with caching, batching, and structured response extraction capabilities.
|
||||
## parent
|
||||
index: src/.pi-map.index.md
|
||||
map: src/.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- external-llm-client.ts
|
||||
- kimi-llm-client.ts
|
||||
- llm-batch.ts
|
||||
- llm-cache.ts
|
||||
- llm-client.ts
|
||||
- llm-error.ts
|
||||
- llm-extract.ts
|
||||
- pi-llm-client.ts
|
||||
## links
|
||||
index: src/llm/.pi-map.index.md
|
||||
map: src/llm/.pi-map.md
|
||||
## workflows
|
||||
- change llm behavior
|
||||
read: external-llm-client.ts, kimi-llm-client.ts, llm-batch.ts
|
||||
- change llm CLI
|
||||
read: external-llm-client.ts, kimi-llm-client.ts, llm-client.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,36 @@
|
||||
# src/llm
|
||||
dir: src/llm
|
||||
|
||||
index: src/llm/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Provides a unified abstraction layer for interacting with multiple LLM providers (OpenAI, Kimi, Pi) with caching, batching, and structured response extraction capabilities.
|
||||
## files
|
||||
- external-llm-client.ts | Implements an LLM client adapter for OpenAI's API to send code analysis prompts and return structured responses. | exp: class:ExternalLLMClient, method:constructor(options: LLMClientOptions), raise:LLMError, method:complete(prompt: string) → Promise<string>, call:this.client.chat.completions.create, call:response.choices[0]?.message?.content?.trim, raise:LLMError | dep: openai, ./llm-error.js, ./llm-client.js
|
||||
- kimi-llm-client.ts | Implements an LLM client for the Kimi.com API using an Anthropic-compatible HTTP interface. | exp: class:KimiLLMClient, method:constructor(options: LLMClientOptions), raise:LLMError, method:complete(prompt: string) → Promise<string>, call:fetch, call:JSON.stringify, call:response.text, call:response.json, call:data.content?.[0]?.text?.trim, raise:LLMError, raise:err | dep: ./llm-error.js, ./llm-client.js, llm-error.js, llm-client.js
|
||||
- llm-batch.ts | Provides batched, concurrent file processing with retry logic and progress callbacks for LLM operations. | exp: BatchOptions, func:withRetry(fn: () => Promise<T>, options: Pick<BatchOptions, "maxRetries" | "retryDelaysMs">) → Promise<T>, call:fn, call:sleep, raise:lastError, func:processFiles(files: T[], processor: (file: T) => Promise<R>, options: BatchOptions, onProgress: (completed: number, total: number, currentFile: T) => void) → Promise<R[]>, call:pLimit, call:files.map, call:limit, call:sleep, call:withRetry, call:processor, call:onProgress, call:Promise.all | dep: p-limit, ./llm-error.js
|
||||
- llm-cache.ts | Provides a persistent file-based caching system for LLM responses keyed by hash, storing results in JSON with atomic writes and automatic directory creation. | exp: func:getCached(hash: string, cacheDir: string) → string | undefined, call:getCachePath, call:loadCache, func:setCached(hash: string, result: string, cacheDir: string) → void, call:getCachePath, call:loadCache, call:Date.now, call:saveCache | dep: fs, path
|
||||
- llm-client.ts | Factory for creating LLM client instances based on different provider modes (pi, openai, kimi). | exp: LLMClient, LLMClientOptions, func:createLLMClient(mode: "pi" | "openai" | "kimi", options: LLMClientOptions) → LLMClient | dep: ./llm-error.js, ./external-llm-client.js, ./kimi-llm-client.js, ./pi-llm-client.js, LLMError, ExternalLLMClient, KimiLLMClient, PiLLMClient
|
||||
- llm-error.ts | Defines a custom error class for LLM-related errors with optional cause chaining | exp: class:LLMError, method:constructor(message: string, cause: unknown)
|
||||
- llm-extract.ts | Extracts structured metadata (purpose, dependencies, concepts) from source files and packages using an LLM client, with binary detection, caching, and context window management. | exp: func:extractFileLLM(filePath: string, client: LLMClient, cacheDir: string) → Promise<LLMFileData>, call:isBinaryFile, call:readFileSync, call:createHash("sha256").update(content).digest, call:getCached, call:parseFileResponse, call:statSync, call:buildFilePrompt, call:truncateForContext, call:client.complete, call:setCached, raise:LLMError, func:extractPackageLLM(relativePath: string, fileData: { name: string; purpose: string }[], client: LLMClient, _cacheDir: string) → Promise<LLMPackageData>, call:buildPackagePrompt, call:client.complete, call:parsePackageResponse, call:basename, raise:LLMError | dep: fs, crypto, path, ./llm-client.js, ./llm-cache.js, ./llm-error.js
|
||||
- pi-llm-client.ts | Implements an LLM client adapter that bridges to Pi's internal AI runtime using its built-in `complete()` function | exp: class:PiLLMClient, method:constructor(extensionContext: unknown), method:complete(prompt: string) → Promise<string>, call:ctx.modelRegistry?.get, call:ctx.modelRegistry?.getApiKeyAndHeaders, call:complete, call:Date.now, call:response.content .filter((c: any) => c.type === "text") .map((c: any) => c.text) .join("") .trim, raise:LLMError, raise:err | dep: ./llm-error.js, ./llm-client.js, @mariozechner/pi-ai
|
||||
## arch
|
||||
Adapter pattern for provider-specific LLM clients with a factory; decorator/wrapper pattern for cross-cutting concerns (caching, batching, retries, error handling); functional pipeline for file extraction with binary detection and context window management.
|
||||
## tags
|
||||
llm, client, js, raise:llmerror, cache, llmclient, error, constructor
|
||||
## symbols
|
||||
- ExternalLLMClient
|
||||
- KimiLLMClient
|
||||
- LLMError
|
||||
- PiLLMClient
|
||||
- constructor
|
||||
- complete
|
||||
- withRetry
|
||||
- processFiles
|
||||
## workflows
|
||||
- change llm behavior
|
||||
read: external-llm-client.ts, kimi-llm-client.ts, llm-batch.ts
|
||||
- change llm CLI
|
||||
read: external-llm-client.ts, kimi-llm-client.ts, llm-client.ts
|
||||
## dirty
|
||||
-
|
||||
+1
-19
@@ -5,6 +5,7 @@ import {
|
||||
discoverProject,
|
||||
generateDirectoryArtifacts,
|
||||
buildDirectoryContext,
|
||||
getAncestorEntries,
|
||||
} from "./init.js";
|
||||
import type { DirectoryEntry } from "./discover.js";
|
||||
import type { LLMClient } from "./llm/llm-client.js";
|
||||
@@ -133,25 +134,6 @@ function countDirectChildren(rootPath: string, relDir: string): number {
|
||||
}).length;
|
||||
}
|
||||
|
||||
function getAncestorEntries(
|
||||
entries: DirectoryEntry[],
|
||||
ctx: ReturnType<typeof buildDirectoryContext>,
|
||||
entry: DirectoryEntry,
|
||||
): DirectoryEntry[] {
|
||||
const result: DirectoryEntry[] = [];
|
||||
let parent = ctx.parentMap.get(entry.relativePath);
|
||||
while (parent) {
|
||||
const ancestor = entries.find(
|
||||
(candidate) => candidate.relativePath === parent,
|
||||
);
|
||||
if (ancestor) {
|
||||
result.push(ancestor);
|
||||
}
|
||||
parent = ctx.parentMap.get(parent);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function normalizeRelativePath(relPath: string): string {
|
||||
if (!relPath || relPath === ".") return ".";
|
||||
return relPath.replace(/\\/g, "/");
|
||||
|
||||
+1
-1
@@ -291,6 +291,6 @@ function renderNoResultsBundle(query: string): string {
|
||||
"-",
|
||||
"",
|
||||
"## instructions",
|
||||
"No relevant directories found. Try rephrasing the query or run `project_map_reinit` if artifacts are stale.",
|
||||
"No relevant directories found. Try rephrasing the query, or run `project_map_validate` to check whether artifacts are stale.",
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# src/types (index)
|
||||
dir: src/types
|
||||
|
||||
## role
|
||||
Provides TypeScript type declarations for the Pi AI module's LLM chat completion functionality within the Pi runtime.
|
||||
## parent
|
||||
index: src/.pi-map.index.md
|
||||
map: src/.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- pi-ai.d.ts
|
||||
## links
|
||||
index: src/types/.pi-map.index.md
|
||||
map: src/types/.pi-map.md
|
||||
## workflows
|
||||
- change types behavior
|
||||
read: pi-ai.d.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,20 @@
|
||||
# src/types
|
||||
dir: src/types
|
||||
|
||||
index: src/types/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Provides TypeScript type declarations for the Pi AI module's LLM chat completion functionality within the Pi runtime.
|
||||
## files
|
||||
- pi-ai.d.ts | TypeScript declaration file for the Pi AI module's `complete` function that provides LLM chat completions within the Pi runtime | exp: complete
|
||||
## arch
|
||||
Minimal declaration-only types package using ambient module declarations (.d.ts) to define external API interfaces without implementation.
|
||||
## tags
|
||||
complete, pi, ai.d, typescript, declaration, provides, llm, chat
|
||||
## symbols
|
||||
- complete
|
||||
## workflows
|
||||
- change types behavior
|
||||
read: pi-ai.d.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,37 @@
|
||||
# tests (index)
|
||||
dir: tests
|
||||
|
||||
## role
|
||||
Comprehensive test suite for a project mapping tool that generates AI-readable codebase documentation with LLM integration, caching, and CLI query capabilities.
|
||||
## parent
|
||||
index: ./.pi-map.index.md
|
||||
map: ./.pi-map.md
|
||||
## children
|
||||
-
|
||||
## files
|
||||
- ast-extract.test.ts
|
||||
- cli.test.ts
|
||||
- format.test.ts
|
||||
- integration.test.ts
|
||||
- llm-batch.test.ts
|
||||
- llm-cache.test.ts
|
||||
- llm-extract.test.ts
|
||||
- llm-integration.test.ts
|
||||
- merge.test.ts
|
||||
- mock-llm.ts
|
||||
- pi-extension.test.ts
|
||||
- prompt-injection.test.ts
|
||||
- retrieve.test.ts
|
||||
- routing-metadata.test.ts
|
||||
## links
|
||||
index: tests/.pi-map.index.md
|
||||
map: tests/.pi-map.md
|
||||
## workflows
|
||||
- change tests behavior
|
||||
read: mock-llm.ts
|
||||
- update tests tests
|
||||
read: ast-extract.test.ts, cli.test.ts, format.test.ts
|
||||
- change tests CLI
|
||||
read: cli.test.ts
|
||||
## dirty
|
||||
-
|
||||
@@ -0,0 +1,38 @@
|
||||
# tests
|
||||
dir: tests
|
||||
|
||||
index: tests/.pi-map.index.md
|
||||
|
||||
## role
|
||||
Comprehensive test suite for a project mapping tool that generates AI-readable codebase documentation with LLM integration, caching, and CLI query capabilities.
|
||||
## files
|
||||
- ast-extract.test.ts | Tests AST extraction of TypeScript exports, imports, and dependency resolution with fallback for unsupported file types | dep: vitest, ../src/ast/ast-extract.js, fs, path, os, ast-extract.js
|
||||
- cli.test.ts | Integration tests for a CLI tool that queries project context bundles from `.pi-map.md` and `.pi-map.index.md` files | dep: vitest, fs, path, os, child_process, url
|
||||
- format.test.ts | Tests markdown rendering and parsing functions for package maps, directory maps, and directory indexes in a project mapping tool. | dep: vitest, ../src/format.js, ../src/directory-model.js
|
||||
- integration.test.ts | Integration tests for a project mapping tool that generates and maintains .pi-map.md and .pi-map.index.md files across a codebase. | dep: vitest, fs, path, os, ../src/init.js, ../src/patch.js, ../src/validate.js, ./mock-llm.js, ../src/format.js
|
||||
- llm-batch.test.ts | Unit tests for retry and batch processing utilities in an LLM module | dep: vitest, ../src/llm/llm-batch.js, ../src/llm-error.js
|
||||
- llm-cache.test.ts | Tests a file-based caching system for LLM responses with get/set operations and cleanup. | dep: vitest, ../src/llm/llm-cache.js, fs, path, os
|
||||
- llm-extract.test.ts | Unit tests for LLM-based file extraction with mock client, testing file size limits, binary detection, and response parsing | dep: vitest, ../src/llm/llm-extract.js, fs, path, os, ../src/llm/llm-client.js
|
||||
- llm-integration.test.ts | Integration tests for LLM client functionality including Kimi API calls, file/package extraction, caching, parallel processing, and error handling | dep: vitest, fs, path, os, ../src/llm/llm-client.js, ../src/llm/llm-extract.js, ../src/llm/llm-batch.js, llm-client, llm-extract, llm-batch
|
||||
- merge.test.ts | Tests that mergeFileData normalizes multi-line function/method parameters and return types into single-line export signatures | dep: vitest, ../src/merge.js
|
||||
- mock-llm.ts | Provides mock LLM client implementations for testing purposes | exp: func:createMockFileClient(purpose) → LLMClient, func:createMockPackageClient() → LLMClient | dep: ../src/llm/llm-client.js, llm-client.js
|
||||
- pi-extension.test.ts | Tests a Pi coding agent extension that manages project map initialization, patching, validation, reinitialization, and context retrieval with configurable prompt injection modes. | dep: vitest, fs, path, os, ../src/prompt-injection.js, ../pi-extension.js, @mariozechner/pi-coding-agent, @mariozechner/pi-ai, typebox
|
||||
- prompt-injection.test.ts | Tests a prompt injection mitigation system that manages root-pair markers, context budgets, mode-based injection policies, and bypass detection for LLM interactions. | dep: vitest, fs, path, os, ../src/prompt-injection.js
|
||||
- retrieve.test.ts | Tests the `retrieveContext` function that searches and ranks project map files to build context bundles for AI queries. | dep: vitest, fs, path, os, ../src/retrieve.js
|
||||
- routing-metadata.test.ts | Tests the `populateRoutingMetadata` function which generates tags, symbols, and workflow hints from directory models for code navigation/routing purposes | dep: vitest, ../src/directory-model.js, ../src/routing-metadata.js
|
||||
## arch
|
||||
Layered testing architecture with unit, integration, and mock layers; uses file-based fixtures, mock LLM clients, and tests across AST extraction, LLM batching/caching, markdown rendering, context retrieval, and prompt injection security.
|
||||
## tags
|
||||
llm, js, src, tests, vitest, client, fs, path
|
||||
## symbols
|
||||
- createMockFileClient
|
||||
- createMockPackageClient
|
||||
## workflows
|
||||
- change tests behavior
|
||||
read: mock-llm.ts
|
||||
- update tests tests
|
||||
read: ast-extract.test.ts, cli.test.ts, format.test.ts
|
||||
- change tests CLI
|
||||
read: cli.test.ts
|
||||
## dirty
|
||||
-
|
||||
+186
-1
@@ -8,7 +8,7 @@ import {
|
||||
} from "fs";
|
||||
import { join } from "path";
|
||||
import { tmpdir } from "os";
|
||||
import { initProject } from "../src/init.js";
|
||||
import { initProject, reinitPath } from "../src/init.js";
|
||||
import { patchFile } from "../src/patch.js";
|
||||
import { validateMaps } from "../src/validate.js";
|
||||
import { createMockFileClient } from "./mock-llm.js";
|
||||
@@ -356,4 +356,189 @@ describe("integration", () => {
|
||||
readFileSync(join(dir, "src", ".pi-map.index.md"), "utf8"),
|
||||
).toContain("# src (index)");
|
||||
});
|
||||
|
||||
it("reinit on root regenerates all artifacts", async () => {
|
||||
mkdirSync(join(dir, "src"));
|
||||
writeFileSync(join(dir, "src", "a.ts"), `export const a = 1;\n`);
|
||||
const client = createMockFileClient();
|
||||
await initProject(dir, { llmClient: client, verbose: false });
|
||||
|
||||
writeFileSync(
|
||||
join(dir, ".pi-map.md"),
|
||||
readFileSync(join(dir, ".pi-map.md"), "utf8") + "\nCORRUPTED",
|
||||
);
|
||||
await reinitPath(dir, { llmClient: client, verbose: false });
|
||||
expect(readFileSync(join(dir, ".pi-map.md"), "utf8")).not.toContain(
|
||||
"CORRUPTED",
|
||||
);
|
||||
});
|
||||
|
||||
it("reinit on a small subtree regenerates subtree and ancestors but not siblings", async () => {
|
||||
mkdirSync(join(dir, "src"));
|
||||
mkdirSync(join(dir, "lib"));
|
||||
writeFileSync(join(dir, "src", "a.ts"), `export const a = 1;\n`);
|
||||
for (let i = 0; i < 10; i++) {
|
||||
writeFileSync(
|
||||
join(dir, "lib", `b${i}.ts`),
|
||||
`export const b${i} = ${i};\n`,
|
||||
);
|
||||
}
|
||||
writeFileSync(
|
||||
join(dir, ".pi-project-map.json"),
|
||||
JSON.stringify({ reinitFullThresholdPercent: 10 }),
|
||||
);
|
||||
|
||||
const client = createMockFileClient();
|
||||
await initProject(dir, { llmClient: client, verbose: false });
|
||||
|
||||
writeFileSync(
|
||||
join(dir, "src", ".pi-map.md"),
|
||||
readFileSync(join(dir, "src", ".pi-map.md"), "utf8") + "\nSUBTREE_MARKER",
|
||||
);
|
||||
writeFileSync(
|
||||
join(dir, "lib", ".pi-map.md"),
|
||||
readFileSync(join(dir, "lib", ".pi-map.md"), "utf8") + "\nSIBLING_MARKER",
|
||||
);
|
||||
|
||||
await reinitPath(join(dir, "src"), {
|
||||
llmClient: client,
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
expect(readFileSync(join(dir, "src", ".pi-map.md"), "utf8")).not.toContain(
|
||||
"SUBTREE_MARKER",
|
||||
);
|
||||
expect(readFileSync(join(dir, "lib", ".pi-map.md"), "utf8")).toContain(
|
||||
"SIBLING_MARKER",
|
||||
);
|
||||
});
|
||||
|
||||
it("reinit falls back to full regeneration when subtree exceeds threshold", async () => {
|
||||
mkdirSync(join(dir, "src"));
|
||||
mkdirSync(join(dir, "lib"));
|
||||
for (let i = 0; i < 11; i++) {
|
||||
writeFileSync(
|
||||
join(dir, "src", `a${i}.ts`),
|
||||
`export const a${i} = ${i};\n`,
|
||||
);
|
||||
}
|
||||
writeFileSync(join(dir, "lib", "b.ts"), `export const b = 1;\n`);
|
||||
writeFileSync(
|
||||
join(dir, ".pi-project-map.json"),
|
||||
JSON.stringify({ reinitFullThresholdPercent: 10 }),
|
||||
);
|
||||
|
||||
const client = createMockFileClient();
|
||||
await initProject(dir, { llmClient: client, verbose: false });
|
||||
|
||||
writeFileSync(
|
||||
join(dir, "lib", ".pi-map.md"),
|
||||
readFileSync(join(dir, "lib", ".pi-map.md"), "utf8") + "\nSIBLING_MARKER",
|
||||
);
|
||||
|
||||
await reinitPath(join(dir, "src"), {
|
||||
llmClient: client,
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
expect(readFileSync(join(dir, "lib", ".pi-map.md"), "utf8")).not.toContain(
|
||||
"SIBLING_MARKER",
|
||||
);
|
||||
});
|
||||
|
||||
it("reinit respects a custom reinitFullThresholdPercent", async () => {
|
||||
mkdirSync(join(dir, "src"));
|
||||
mkdirSync(join(dir, "lib"));
|
||||
writeFileSync(join(dir, "src", "a.ts"), `export const a = 1;\n`);
|
||||
writeFileSync(join(dir, "lib", "b.ts"), `export const b = 1;\n`);
|
||||
writeFileSync(
|
||||
join(dir, ".pi-project-map.json"),
|
||||
JSON.stringify({ reinitFullThresholdPercent: 50 }),
|
||||
);
|
||||
|
||||
const client = createMockFileClient();
|
||||
await initProject(dir, { llmClient: client, verbose: false });
|
||||
|
||||
writeFileSync(
|
||||
join(dir, "lib", ".pi-map.md"),
|
||||
readFileSync(join(dir, "lib", ".pi-map.md"), "utf8") + "\nSIBLING_MARKER",
|
||||
);
|
||||
|
||||
await reinitPath(join(dir, "src"), {
|
||||
llmClient: client,
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
expect(readFileSync(join(dir, "lib", ".pi-map.md"), "utf8")).toContain(
|
||||
"SIBLING_MARKER",
|
||||
);
|
||||
});
|
||||
|
||||
it("reinit on a small subtree regenerates ancestors up to the root", async () => {
|
||||
mkdirSync(join(dir, "src"));
|
||||
mkdirSync(join(dir, "lib"));
|
||||
writeFileSync(join(dir, "src", "a.ts"), `export const a = 1;\n`);
|
||||
for (let i = 0; i < 10; i++) {
|
||||
writeFileSync(
|
||||
join(dir, "lib", `b${i}.ts`),
|
||||
`export const b${i} = ${i};\n`,
|
||||
);
|
||||
}
|
||||
writeFileSync(
|
||||
join(dir, ".pi-project-map.json"),
|
||||
JSON.stringify({ reinitFullThresholdPercent: 10 }),
|
||||
);
|
||||
|
||||
const client = createMockFileClient();
|
||||
await initProject(dir, { llmClient: client, verbose: false });
|
||||
|
||||
writeFileSync(
|
||||
join(dir, ".pi-map.md"),
|
||||
readFileSync(join(dir, ".pi-map.md"), "utf8") + "\nROOT_MARKER",
|
||||
);
|
||||
writeFileSync(
|
||||
join(dir, "lib", ".pi-map.md"),
|
||||
readFileSync(join(dir, "lib", ".pi-map.md"), "utf8") + "\nSIBLING_MARKER",
|
||||
);
|
||||
|
||||
await reinitPath(join(dir, "src"), {
|
||||
llmClient: client,
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
expect(readFileSync(join(dir, ".pi-map.md"), "utf8")).not.toContain(
|
||||
"ROOT_MARKER",
|
||||
);
|
||||
expect(readFileSync(join(dir, "lib", ".pi-map.md"), "utf8")).toContain(
|
||||
"SIBLING_MARKER",
|
||||
);
|
||||
});
|
||||
|
||||
it("reinit on a non-existent path falls back to full regeneration", async () => {
|
||||
mkdirSync(join(dir, "src"));
|
||||
mkdirSync(join(dir, "lib"));
|
||||
writeFileSync(join(dir, "src", "a.ts"), `export const a = 1;\n`);
|
||||
writeFileSync(join(dir, "lib", "b.ts"), `export const b = 1;\n`);
|
||||
writeFileSync(
|
||||
join(dir, ".pi-project-map.json"),
|
||||
JSON.stringify({ reinitFullThresholdPercent: 10 }),
|
||||
);
|
||||
|
||||
const client = createMockFileClient();
|
||||
await initProject(dir, { llmClient: client, verbose: false });
|
||||
|
||||
writeFileSync(
|
||||
join(dir, "lib", ".pi-map.md"),
|
||||
readFileSync(join(dir, "lib", ".pi-map.md"), "utf8") + "\nSIBLING_MARKER",
|
||||
);
|
||||
|
||||
await reinitPath(join(dir, "does-not-exist"), {
|
||||
llmClient: client,
|
||||
verbose: false,
|
||||
});
|
||||
|
||||
expect(readFileSync(join(dir, "lib", ".pi-map.md"), "utf8")).not.toContain(
|
||||
"SIBLING_MARKER",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ vi.mock("typebox", () => ({
|
||||
Object: (props: unknown) => props,
|
||||
Optional: (prop: unknown) => prop,
|
||||
String: (opts: unknown) => ({ type: "string", ...(opts as object) }),
|
||||
Boolean: (opts: unknown) => ({ type: "boolean", ...(opts as object) }),
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -70,6 +71,9 @@ describe("pi-extension", () => {
|
||||
})),
|
||||
},
|
||||
ui: { notify: mockNotify },
|
||||
sessionManager: {
|
||||
buildSessionContext: vi.fn(() => ({ messages: [] })),
|
||||
},
|
||||
};
|
||||
|
||||
const mockPi = {
|
||||
@@ -248,7 +252,7 @@ describe("pi-extension", () => {
|
||||
await handler(null, mockCtx);
|
||||
|
||||
expect(mockNotify).toHaveBeenCalledWith(
|
||||
expect.stringContaining("1 dirty packages detected"),
|
||||
expect.stringContaining("1 dirty package(s) detected"),
|
||||
"warning",
|
||||
);
|
||||
});
|
||||
@@ -505,6 +509,53 @@ describe("pi-extension", () => {
|
||||
expect(result.message.content).toContain("Updated");
|
||||
expect(result.message.content).toContain("TestIndex");
|
||||
});
|
||||
|
||||
it("skips injection when hint is already present in session context", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
writeFileSync(join(dir, ".pi-map.md"), "# .\n## role\nTest\n");
|
||||
writeFileSync(
|
||||
join(dir, ".pi-project-map.json"),
|
||||
JSON.stringify({ promptInjectionMode: "advisory" }),
|
||||
);
|
||||
mockCtx.cwd = dir;
|
||||
mockCtx.sessionManager.buildSessionContext = vi.fn(() => ({
|
||||
messages: [
|
||||
{
|
||||
role: "custom",
|
||||
customType: "pi-project-map-hint",
|
||||
content: "existing hint",
|
||||
display: false,
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
const handler = registeredEvents.before_agent_start;
|
||||
const result = await handler(null, mockCtx);
|
||||
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it("injects hint again when session context does not contain it", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "pi-ext-test-"));
|
||||
writeFileSync(join(dir, ".pi-map.md"), "# .\n## role\nTest\n");
|
||||
writeFileSync(
|
||||
join(dir, ".pi-project-map.json"),
|
||||
JSON.stringify({ promptInjectionMode: "advisory" }),
|
||||
);
|
||||
mockCtx.cwd = dir;
|
||||
mockCtx.sessionManager.buildSessionContext = vi.fn(() => ({
|
||||
messages: [
|
||||
{ role: "user", content: "hello" },
|
||||
{ role: "assistant", content: [{ type: "text", text: "hi" }] },
|
||||
],
|
||||
}));
|
||||
|
||||
const handler = registeredEvents.before_agent_start;
|
||||
const result = await handler(null, mockCtx);
|
||||
|
||||
expect(result).toHaveProperty("message");
|
||||
expect(result.message.customType).toBe("pi-project-map-hint");
|
||||
});
|
||||
});
|
||||
|
||||
describe("context event", () => {
|
||||
|
||||
+5
-4
@@ -10,7 +10,7 @@ A source file was edited, added, or deleted without running `project_map_patch`
|
||||
|
||||
### 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>`
|
||||
2. if the list is small and localized, run `project_map_patch <changed-file>` or `project_map_validate` with `fix=true`
|
||||
3. if the list is large or structural, run `project_map_reinit`
|
||||
4. re-run `project_map_validate` to confirm clean state
|
||||
|
||||
@@ -38,14 +38,14 @@ Many `stale-signature` discrepancies appear.
|
||||
AST exports no longer match listed exports, or generated signatures are stale.
|
||||
|
||||
### Fix
|
||||
- patch the changed file/directory
|
||||
- patch the changed file/directory, or run `project-map validate --fix`
|
||||
- if widespread, reinit and validate again
|
||||
|
||||
### Symptom
|
||||
`broken-link` appears after moving directories.
|
||||
|
||||
### Fix
|
||||
Run `project_map_reinit`.
|
||||
Run `project-map validate --fix` first. If the damage spans many directories, run `project_map_reinit`.
|
||||
|
||||
## Prompt injection mode surprises
|
||||
|
||||
@@ -67,12 +67,13 @@ No project-map context appears in a Pi session.
|
||||
Root pair is injected repeatedly.
|
||||
|
||||
### Likely cause
|
||||
Marker scanning failed or artifact invalidation forced reinjection.
|
||||
Marker scanning failed or artifact invalidation forced reinjection, often because `project_map_reinit` is being used instead of `project_map_patch` or `project_map_validate --fix`.
|
||||
|
||||
### 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
|
||||
- prefer `project_map_patch <file>` for edits and `project_map_validate` with `fix=true` for localized discrepancies
|
||||
|
||||
### Symptom
|
||||
`advisory` mode shows a reminder but no maps are loaded.
|
||||
|
||||
+25
-12
@@ -9,7 +9,8 @@ For Pi agents and advanced users who want predictable, low-friction navigation a
|
||||
| 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` |
|
||||
| Localized discrepancies detected by validate | `project_map_validate` with `fix=true` / `project-map validate --fix` |
|
||||
| Widespread structural 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
|
||||
@@ -18,11 +19,11 @@ For Pi agents and advanced users who want predictable, low-friction navigation a
|
||||
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.
|
||||
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
|
||||
- **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.
|
||||
@@ -38,7 +39,9 @@ Run before architectural decisions, broad refactors, or final handoff. It checks
|
||||
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.
|
||||
Use as a last resort. By default, `project_map_reinit` (or `project-map reinit [path]`) regenerates only the target subtree plus its ancestor directories up to the root. It falls back to full regeneration only when the target subtree covers more than `reinitFullThresholdPercent` of the project's files (default: 10%).
|
||||
|
||||
Reach for reinit when `project_map_patch` and `project_map_validate --fix` cannot repair widespread structural damage (e.g. broken parent/child links across many directories) or after a large merge.
|
||||
|
||||
### context
|
||||
Use when you know what you are looking for:
|
||||
@@ -57,12 +60,14 @@ The returned bundle is deterministic and ranked. Read indexes first, then strong
|
||||
|
||||
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`
|
||||
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
|
||||
9. if validation reports localized discrepancies, run `project_map_validate` with `fix=true` (or `project-map validate --fix`)
|
||||
10. only if discrepancies are widespread or structural, run `project_map_reinit`
|
||||
|
||||
### Exploring an unfamiliar area
|
||||
|
||||
@@ -76,7 +81,9 @@ Treat the returned bundle as a ranked entry point, not as truth.
|
||||
|
||||
```bash
|
||||
project-map validate
|
||||
# if many discrepancies:
|
||||
# if localized discrepancies:
|
||||
project-map validate --fix
|
||||
# if widespread structural damage:
|
||||
project-map reinit
|
||||
```
|
||||
|
||||
@@ -84,7 +91,9 @@ project-map reinit
|
||||
|
||||
```bash
|
||||
project-map validate
|
||||
# if needed:
|
||||
# if localized discrepancies:
|
||||
project-map validate --fix
|
||||
# if widespread structural damage:
|
||||
project-map reinit
|
||||
```
|
||||
|
||||
@@ -155,9 +164,10 @@ Use bypass markers sparingly.
|
||||
|
||||
```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
|
||||
2. If localized discrepancies: project-map validate --fix
|
||||
3. If clean, read root .pi-map.md and key directory maps
|
||||
4. Cross-check claims against source
|
||||
5. Only if widespread: run project-map reinit
|
||||
```
|
||||
|
||||
## Retrieval vs automatic injection
|
||||
@@ -175,7 +185,8 @@ Use both together: injection for baseline orientation, retrieval for focused ent
|
||||
|
||||
- patch after every edit
|
||||
- validate before architectural claims
|
||||
- reinit when many artifacts are stale or after large merges
|
||||
- use `validate --fix` for localized discrepancies
|
||||
- use reinit only for widespread structural staleness or after large merges
|
||||
- watch for the Pi extension warning about dirty packages on session start
|
||||
|
||||
## Configuration quick reference
|
||||
@@ -187,6 +198,7 @@ Use both together: injection for baseline orientation, retrieval for focused ent
|
||||
"contextBudgetMaxTokens": 100000,
|
||||
"tagCap": 8,
|
||||
"workflowHintCap": 5,
|
||||
"reinitFullThresholdPercent": 10,
|
||||
"ignorePatterns": ["node_modules", ".git", "dist", "build"]
|
||||
}
|
||||
```
|
||||
@@ -195,4 +207,5 @@ Providing `ignorePatterns` replaces the built-in default list, so include any de
|
||||
|
||||
- lower `contextBudgetPercent` / `contextBudgetMaxTokens` to reduce token use
|
||||
- raise them if you want deeper auto-loaded context in large projects
|
||||
- `reinitFullThresholdPercent` controls when `project-map reinit [path]` falls back to full regeneration
|
||||
- `strict` is the safest enforcement mode; `strong` is the best default for everyday work
|
||||
|
||||
Reference in New Issue
Block a user