- Design doc with full spec (dense markdown format, hybrid AST+LLM pipeline, consumption model, stale data mitigation) - Implementation plan with 6 milestones and rollout strategy - TypeScript package structure with all source stubs - CLI entry point, formatter, discover, init, patch, validate, extract, merge - Pi SKILL.md with tool definitions and format documentation
10 KiB
Implementation Plan: Hierarchical Project Analysis Skill for Pi
Overview
Build a Pi skill package (pi-project-map) that generates and maintains a hierarchical, machine-readable analysis of a software project. Each directory gets a .pi-map.md file. The skill provides custom tools for init, patch, validate, and re-init, plus a prompt hook that ensures the agent keeps analysis files in sync.
Milestones
M1: Foundation and Format (Week 1)
Goal: A working dense-markdown formatter and the ability to generate .pi-map.md files from a directory listing.
Tasks
-
Bootstrap the npm package
- Create
package.json,tsconfig.json, basic CLI entry point. - Set up test runner (vitest or jest).
- Create stub
SKILL.mdfor Pi integration.
- Create
-
Implement the formatter (
src/format.ts)- Define the
.pi-map.mdschema as a TypeScript interface. - Implement
renderPackageMap(data) -> stringthat outputs dense markdown. - Implement
parsePackageMap(markdown) -> datafor reading existing files. - Unit tests for round-trip serialization.
- Define the
-
Implement file discovery (
src/discover.ts)- Walk a directory tree.
- Respect
.gitignoreand a built-in ignore list (node_modules, .git, dist, etc.). - Return a flat list of
(dirPath, filePaths[])tuples. - Unit tests with fixture directories.
-
Implement init skeleton (
src/init.ts)- Given a project root, create one
.pi-map.mdper directory. - Populate
## fileswith filenames only (no analysis yet). - Populate
## roleand## archwith placeholder text. - Command:
project-map:init --root <path>
- Given a project root, create one
Acceptance Criteria
- Running
project-map:initon a test repo creates.pi-map.mdin every directory. - Files match
.gitignorerules correctly. - Output format follows the dense-markdown spec from the design doc.
Dependencies: None.
M2: LLM Extraction Layer (Week 2)
Goal: The LLM layer generates one-line purposes and package-level architecture notes.
Tasks
-
Implement LLM file extraction (
src/llm-extract.ts)- Prompt template: given a file's contents and path, return a one-line purpose and a list of exported symbols (if any).
- Prompt template: given a file's contents, infer its dependencies.
- Integrate with Pi's LLM tool calls (or a generic OpenAI-compatible client for standalone testing).
- Add caching: hash file contents, skip LLM call if unchanged.
-
Implement LLM package extraction (
src/llm-extract.ts)- Prompt template: given all per-file summaries in a directory, generate
## roleand## arch. - Keep prompts token-efficient (truncate large files, focus on headers/exports).
- Prompt template: given all per-file summaries in a directory, generate
-
Wire LLM layer into init (
src/init.ts)- After file discovery, run LLM extraction on every file.
- Then run package-level LLM extraction.
- Write fully populated
.pi-map.mdfiles.
-
Add configuration (
src/config.ts)- Configurable ignore patterns.
- Configurable LLM model and token limits.
- Configurable context budget for large projects.
Acceptance Criteria
project-map:initproduces.pi-map.mdfiles with meaningful## files,## role, and## archsections.- Caching works: second run on unchanged repo is fast.
- Unit tests mock LLM responses to verify prompt structure and output parsing.
Dependencies: M1.
M3: AST Extraction Layer (Week 3)
Goal: Code files get precise symbol and dependency data from tree-sitter or LSP.
Tasks
-
Set up tree-sitter (
src/ast-extract.ts)- Add tree-sitter dependencies for target languages (TypeScript, Python, Go, Rust as first-class).
- Implement
extractSymbols(filePath, language) -> { exports, imports, types }. - Implement
extractDependencies(filePath, language) -> string[].
-
Language detection
- Map file extensions to tree-sitter grammars.
- Graceful fallback to LLM-only if no parser available.
-
Merge AST + LLM outputs (
src/merge.ts)- Combine AST symbols with LLM purpose descriptions.
- AST provides
exp:anddep:; LLM provides the one-line purpose. - If AST and LLM disagree on dependencies, prefer AST for imports, LLM for inferred architectural deps.
-
Wire AST into init (
src/init.ts)- For each code file, run AST extraction in parallel with LLM extraction.
- Merge results before formatting.
Acceptance Criteria
- TypeScript/Python files show precise exported function/class names in
exp:. - Import statements are captured in
dep:. - Files with no AST parser still get LLM-only analysis.
- Unit tests with sample source files verify symbol extraction accuracy.
Dependencies: M2.
M4: Patch and Update (Week 4)
Goal: The agent can incrementally update .pi-map.md files after editing source files.
Tasks
-
Implement patch strategy (
src/patch.ts)- Determine directory size (file count).
- If < 10 files: trigger full rewrite of that directory's
.pi-map.md. - If >= 10 files: parse existing
.pi-map.md, identify the section(s) for changed file(s), and rewrite only those lines.
-
Implement dirty markers (
src/patch.ts)- When a section-level patch occurs, append a dirty note:
## dirty 2024-06-09T14:32:00Z: tokens.ts patched (section-level)
- When a section-level patch occurs, append a dirty note:
-
Command:
project-map:patch <file-path>- Given a changed file path, find its directory.
- Re-run LLM + AST extraction on that file only.
- Apply patch strategy.
- Update
.pi-map.md.
-
Command:
project-map:reinit [path]- Force full re-init of entire project or a subtree.
- Clear all dirty markers.
Acceptance Criteria
- Editing one file in a small package triggers full rewrite of that package's
.pi-map.md. - Editing one file in a large package patches only the relevant line.
- Dirty markers are correctly added on section-level patches.
- Re-init clears dirty markers and regenerates everything.
Dependencies: M3.
M5: Validation and Consistency (Week 5)
Goal: The agent can detect and recover from stale analysis files.
Tasks
-
Implement validation (
src/validate.ts)- Walk all
.pi-map.mdfiles. - For each listed file, check existence (catch deletions).
- For each directory, check for new files not in
.pi-map.md. - For code files with AST support, compare listed
exp:against actual symbols. - Report discrepancies with severity (missing, orphaned, stale-signature).
- Walk all
-
Implement reconciliation (
src/validate.ts)- Option
--fix: automatically patch or flag discrepancies. - Option
--reinit-if <threshold>: if > N discrepancies, recommend full re-init.
- Option
-
Command:
project-map:validate- Run checks and print a report.
- Exit code 0 if clean, 1 if discrepancies found.
Acceptance Criteria
- Validation detects a deleted file that still appears in
.pi-map.md. - Validation detects a new file not yet in
.pi-map.md. - Validation detects a changed function signature.
--fixpatches or flags all found issues.
Dependencies: M4.
M6: Pi Integration (Week 6)
Goal: The skill is installable and functional within the Pi agent harness.
Tasks
-
Finalize
SKILL.md- Document all custom tools:
project-map:init,project-map:patch,project-map:validate,project-map:reinit. - Document the dense-markdown format for the agent.
- Include usage examples and configuration options.
- Document all custom tools:
-
Implement prompt hook (
hooks/on-prompt.ts)- Inject a lightweight instruction into every prompt:
"If you modify any source file, run
project-map:patch <path>. If you suspect staleness, runproject-map:validate." - Make injection configurable (toggle on/off, customize message).
- Inject a lightweight instruction into every prompt:
-
Auto-inject session-start behavior
- On session start, if
.pi-map.mdfiles exist, the skill auto-reads them into context. - If they don't exist, the skill offers to run
project-map:init. - If dirty markers exist, the skill warns the agent.
- On session start, if
-
Packaging and publishing
- Ensure
package.jsonhas correctbinentries. - Write human README with install instructions.
- Tag and publish to npm (or internal registry).
- Ensure
Acceptance Criteria
- Installing the skill in Pi makes the four tools available.
- The prompt hook injects maintenance instructions reliably.
- Session start auto-detects and loads
.pi-map.mdfiles. - The skill can be published and installed via npm.
Dependencies: M5.
Sequencing and Dependencies
M1 (Foundation) → M2 (LLM) → M3 (AST) → M4 (Patch) → M5 (Validate) → M6 (Pi Integration)
No parallel tracks — each milestone builds on the previous. Total estimated time: 6 weeks at a steady pace, or 3-4 weeks with focused effort.
Validation Criteria (Overall)
- Functional:
project-map:initcorrectly generates.pi-map.mdfor a test repo of 50+ files across 10+ directories. - Accuracy: AST-extracted symbols match actual source code (verified by unit tests).
- Freshness: After editing a file,
project-map:patchupdates the analysis within 5 seconds. - Consistency:
project-map:validatedetects 100% of synthetic staleness scenarios (deleted files, new files, signature changes). - Token efficiency: A 100-file project's full
.pi-map.mdset fits within a 4k token budget. - Pi integration: The skill installs cleanly, tools are discoverable, and the prompt hook works.
Rollout Plan
- Internal dogfooding (Week 7): Use the skill on 2-3 real projects. Gather feedback on format density and agent behavior.
- Format refinement (Week 8): Adjust abbreviations, section structure, and dirty-marker format based on dogfooding.
- Open beta (Week 9): Share with a small group of Pi users. Collect bug reports and feature requests.
- v1.0 release (Week 10): Stable API, documented format, published to npm.
Open Questions for Implementation
- Which tree-sitter grammars to bundle by default? (Start with TypeScript, Python, Go.)
- Should the LLM extraction use the agent's current model or a dedicated cheaper model?
- How to handle monorepos with multiple
.gitignorefiles at different depths? - Should
.pi-map.mdfiles be committed to git or kept in.gitignore? (Recommendation:.gitignore— they are derived artifacts.)