From 6b71ea2b574dea773ef424291de119eedd9b470e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A8NW=C2=A8?= <Β¨neroworld@mail.ruΒ¨> Date: Wed, 22 Apr 2026 20:01:38 +0100 Subject: [PATCH] feat: add .architect/ project mapping system with architect-indexer agent and Docker containerization - Add .architect/ directory structure (10 template files) as project brain for agent orientation - Add architect-indexer agent that scans codebase and generates structured architecture docs - Add Docker containerization: Dockerfile.architect-indexer, docker-compose.architect.yml - Add TypeScript project-mapper module with staleness detection and context injection - Add /index-project command, architect-first-contact rule, project-mapping skill - Integrate orchestrator first-contact check: triggers indexing before any task delegation - Add npm arch:* scripts for Docker-based indexing workflow - Register agent in capability-index.yaml and AGENTS.md --- .architect/README.md | 93 ++++ .architect/api-surface/endpoints.md | 49 +++ .architect/architecture/dependency-graph.md | 35 ++ .architect/architecture/overview.md | 53 +++ .architect/conventions/conventions.md | 68 +++ .architect/db-schema/schema.md | 45 ++ .architect/entities/entities.md | 47 ++ .architect/project.json | 42 ++ .architect/state.json | 71 +++ .architect/tech-stack/stack.md | 57 +++ .gitignore | 8 +- .kilo/agents/architect-indexer.md | 159 +++++++ .kilo/agents/orchestrator.md | 7 + .kilo/capability-index.yaml | 45 ++ .kilo/commands/index-project.md | 239 +++++++++++ .kilo/commands/pipeline.md | 14 + .kilo/rules/architect-first-contact.md | 97 +++++ .kilo/skills/project-mapping/SKILL.md | 203 +++++++++ AGENTS.md | 68 +++ STRUCTURE.md | 38 ++ docker/Dockerfile.architect-indexer | 63 +++ docker/docker-compose.architect.yml | 48 +++ package.json | 7 +- src/kilocode/agent-manager/project-mapper.ts | 403 ++++++++++++++++++ src/kilocode/index.ts | 24 +- src/kilocode/scripts/run-architect-indexer.ts | 180 ++++++++ 26 files changed, 2160 insertions(+), 3 deletions(-) create mode 100644 .architect/README.md create mode 100644 .architect/api-surface/endpoints.md create mode 100644 .architect/architecture/dependency-graph.md create mode 100644 .architect/architecture/overview.md create mode 100644 .architect/conventions/conventions.md create mode 100644 .architect/db-schema/schema.md create mode 100644 .architect/entities/entities.md create mode 100644 .architect/project.json create mode 100644 .architect/state.json create mode 100644 .architect/tech-stack/stack.md create mode 100644 .kilo/agents/architect-indexer.md create mode 100644 .kilo/commands/index-project.md create mode 100644 .kilo/rules/architect-first-contact.md create mode 100644 .kilo/skills/project-mapping/SKILL.md create mode 100644 docker/Dockerfile.architect-indexer create mode 100644 docker/docker-compose.architect.yml create mode 100644 src/kilocode/agent-manager/project-mapper.ts create mode 100644 src/kilocode/scripts/run-architect-indexer.ts diff --git a/.architect/README.md b/.architect/README.md new file mode 100644 index 0000000..937465a --- /dev/null +++ b/.architect/README.md @@ -0,0 +1,93 @@ +# πŸ“ Project Architecture Index + +> **Auto-generated navigation file.** Updated by `architect-indexer` on every pipeline run. +> **DO NOT edit manually** β€” changes will be overwritten. Edit source code instead. + +## Quick Status + +| Metric | Value | +|--------|-------| +| Last Indexed | _pending first run_ | +| Index Version | 1 | +| Files Tracked | 0 | +| Modules | 0 | +| Staleness | _unknown_ | + +--- + +## Navigation + +### πŸ— Architecture + +| File | Description | When to Read | +|------|-------------|-------------| +| [`architecture/overview.md`](architecture/overview.md) | High-level project architecture, layers, boundaries | Before ANY implementation task | +| [`architecture/dependency-graph.md`](architecture/dependency-graph.md) | Module-to-module dependency graph | Before adding cross-module imports | + +### πŸ“¦ Entities & Data + +| File | Description | When to Read | +|------|-------------|-------------| +| [`entities/entities.md`](entities/entities.md) | All domain entities, their fields and relationships | Before creating/editing models or DB tables | +| [`db-schema/schema.md`](db-schema/schema.md) | Database tables, columns, indexes, foreign keys, migrations | Before touching any migration or DB query | + +### 🌐 API Surface + +| File | Description | When to Read | +|------|-------------|-------------| +| [`api-surface/endpoints.md`](api-surface/endpoints.md) | All API endpoints, methods, auth, request/response types | Before adding/modifying any API endpoint | + +### πŸ“ Conventions + +| File | Description | When to Read | +|------|-------------|-------------| +| [`conventions/conventions.md`](conventions/conventions.md) | Coding style, naming, patterns, forbidden practices | Before writing ANY code | + +### πŸ”§ Tech Stack + +| File | Description | When to Read | +|------|-------------|-------------| +| [`tech-stack/stack.md`](tech-stack/stack.md) | Languages, frameworks, libraries, versions | Before adding dependencies or choosing tools | + +### πŸ—Ί Machine-Readable Maps + +| File | Description | Used By | +|------|-------------|---------| +| [`maps/file-graph.json`](maps/file-graph.json) | File β†’ imports/exports graph | `architect-indexer`, `system-analyst`, `lead-developer` | +| [`maps/module-graph.json`](maps/module-graph.json) | Module β†’ dependencies graph | `system-analyst`, `orchestrator` for routing | + +### βš™οΈ Machine Config + +| File | Description | Used By | +|------|-------------|---------| +| [`project.json`](project.json) | Project metadata (name, type, framework, entry points) | `orchestrator` for routing decisions | +| [`state.json`](state.json) | Index freshness state (hashes, timestamps, version) | `orchestrator` to detect staleness | + +--- + +## How It Works + +### For Agents + +1. **First contact**: Orchestrator checks `.architect/state.json` +2. **Stale or missing**: Launch `architect-indexer` to build/update +3. **Fresh**: Read relevant files from `.architect/` for context +4. **After changes**: `architect-indexer` incrementally updates affected sections + +### For Humans + +- Browse any `.md` file for human-readable documentation +- Check `project.json` for quick project facts +- See `state.json` for when the index was last updated + +### Update Triggers + +| Event | Action | +|-------|--------| +| New file added/removed | Rebuild `maps/file-graph.json` | +| New dependency added | Update `tech-stack/stack.md` | +| Schema migration created | Update `db-schema/schema.md` | +| New entity/model created | Update `entities/entities.md` | +| New API endpoint added | Update `api-surface/endpoints.md` | +| Convention file changed | Update `conventions/conventions.md` | +| Any structural change | Increment version in `state.json` | \ No newline at end of file diff --git a/.architect/api-surface/endpoints.md b/.architect/api-surface/endpoints.md new file mode 100644 index 0000000..38e6e4c --- /dev/null +++ b/.architect/api-surface/endpoints.md @@ -0,0 +1,49 @@ +# API Surface + +> Auto-generated by `architect-indexer`. DO NOT edit manually. + +## Endpoints + +| Method | Path | Auth | Controller | Description | +|--------|------|------|-----------|-------------| +| _pending_ | _pending_ | _pending_ | _pending_ | _pending_ | + +_This section is populated after the first index run._ + +## Detailed Endpoint Definitions + +### `METHOD /api/path` + +**Auth**: Required / None / Optional +**Rate Limit**: _pending_ + +**Request**: +| Field | Type | Required | Validation | Description | +|-------|------|----------|------------|-------------| +| _pending_ | _pending_ | _pending_ | _pending_ | _pending_ | + +**Response 200**: +| Field | Type | Description | +|-------|------|-------------| +| _pending_ | _pending_ | _pending_ | + +**Errors**: +| Code | Condition | Message | +|------|-----------|---------| +| 400 | Validation failed | _pending_ | +| 401 | Unauthorized | _pending_ | +| 404 | Not found | _pending_ | + +--- + +## API Versioning + +| Version | Base Path | Status | Notes | +|---------|-----------|--------|-------| +| _pending_ | _pending_ | current | _pending_ | + +## Authentication + +| Method | Header / Param | Scope | +|--------|---------------|-------| +| Bearer JWT | `Authorization: Bearer ` | _pending_ | \ No newline at end of file diff --git a/.architect/architecture/dependency-graph.md b/.architect/architecture/dependency-graph.md new file mode 100644 index 0000000..0be93c3 --- /dev/null +++ b/.architect/architecture/dependency-graph.md @@ -0,0 +1,35 @@ +# Dependency Graph + +> Auto-generated by `architect-indexer`. DO NOT edit manually. + +## Module Dependencies + +``` +Module A ──→ Module B ──→ Module C + β”‚ β”‚ + └──→ Module D β”€β”˜ +``` + +_This section is populated after the first index run._ + +## Import Graph + +| Module | Imports From | Imported By | +|--------|-------------|-------------| +| _pending_ | _pending_ | _pending_ | + +## Circular Dependencies + +_None detected_ (or listed with paths) + +## External Dependencies + +| Package | Version | Used By | Purpose | +|---------|---------|---------|---------| +| _pending_ | _pending_ | _pending_ | _pending_ | + +## Dependency Rules + +1. **No cross-layer imports upward** β€” Infrastructure cannot import Application +2. **No circular dependencies** β€” A β†’ B β†’ A is forbidden +3. **Shared kernel only** β€” Cross-module communication via events/interfaces \ No newline at end of file diff --git a/.architect/architecture/overview.md b/.architect/architecture/overview.md new file mode 100644 index 0000000..ebc8aa5 --- /dev/null +++ b/.architect/architecture/overview.md @@ -0,0 +1,53 @@ +# Architecture Overview + +> Auto-generated by `architect-indexer`. DO NOT edit manually. + +## Project Type + +_This section is populated after the first index run._ + +## Architecture Pattern + +_Example: Layered, Clean Architecture, MVC, MVVM, Microservices, Monolith, etc._ + +## Layers + +| Layer | Directory | Responsibility | +|-------|-----------|---------------| +| _pending_ | _pending_ | _pending_ | + +## Module Boundaries + +| Module | Directory | Exports | Dependencies | +|--------|-----------|---------|-------------| +| _pending_ | _pending_ | _pending_ | _pending_ | + +## External Services + +| Service | Purpose | Integration Point | +|---------|---------|-------------------| +| _pending_ | _pending_ | _pending_ | + +## Key Decisions + +| Decision | Rationale | Date | +|----------|-----------|------| +| _pending_ | _pending_ | _pending_ | + +## Diagram + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Presentation β”‚ +β”‚ (Controllers, Views, Routes) β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ Application β”‚ +β”‚ (Services, Use Cases) β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ Domain β”‚ +β”‚ (Entities, Value Objects) β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ Infrastructure β”‚ +β”‚ (Repositories, External APIs) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` \ No newline at end of file diff --git a/.architect/conventions/conventions.md b/.architect/conventions/conventions.md new file mode 100644 index 0000000..e1612fa --- /dev/null +++ b/.architect/conventions/conventions.md @@ -0,0 +1,68 @@ +# Project Conventions + +> Auto-generated by `architect-indexer`. DO NOT edit manually. + +## Naming Conventions + +| Element | Pattern | Example | +|---------|---------|---------| +| Files | _pending_ | _pending_ | +| Variables | _pending_ | _pending_ | +| Classes | _pending_ | _pending_ | +| Functions | _pending_ | _pending_ | +| Constants | _pending_ | _pending_ | +| Database tables | _pending_ | _pending_ | +| API endpoints | _pending_ | _pending_ | + +_This section is populated after the first index run._ + +## Code Patterns + +| Pattern | When to Use | Example File | +|---------|------------|-------------| +| Repository | Data access | _pending_ | +| Service | Business logic | _pending_ | +| Controller | HTTP handling (thin) | _pending_ | +| Event | Cross-module communication | _pending_ | +| Value Object | Immutable type | _pending_ | + +## Architectural Patterns + +1. **Service Layer** β€” Business logic in services, not controllers +2. **Repository Pattern** β€” Data access abstracted behind interfaces +3. **Thin Controllers** β€” Controllers delegate to services, max 10 lines per method +4. **Event-Driven** β€” Cross-module via events, never direct model imports + +## Maximum Sizes + +| Element | Max | Enforcement | +|---------|-----|-------------| +| File | 100 lines | Architect check | +| Function | 30 lines | Lint rule | +| Class methods | 5 public | Code review | +| Controller method | 10 lines | Code review | + +## Forbidden Practices + +1. ❌ Direct model imports from other modules +2. ❌ Business logic in controllers +3. ❌ Raw SQL queries outside repositories +4. ❌ Hardcoded secrets or credentials +5. ❌ Mutable global state +6. ❌ Catching exceptions silently + +## Testing Conventions + +| Type | Location | Naming | +|------|----------|--------| +| Unit | _pending_ | `{name}.test.{ext}` | +| Integration | _pending_ | `{name}.integration.test.{ext}` | +| E2E | _pending_ | `{name}.e2e.test.{ext}` | + +## Git Conventions + +| Convention | Pattern | +|-----------|---------| +| Branch naming | `feature/{issue}-{slug}` | +| Commit prefix | `feat:`, `fix:`, `refactor:`, `test:`, `docs:` | +| PR naming | `#{issue}: brief description` | \ No newline at end of file diff --git a/.architect/db-schema/schema.md b/.architect/db-schema/schema.md new file mode 100644 index 0000000..ddbf793 --- /dev/null +++ b/.architect/db-schema/schema.md @@ -0,0 +1,45 @@ +# Database Schema + +> Auto-generated by `architect-indexer`. DO NOT edit manually. + +## Tables + +| Table | Engine | Rows (est.) | Description | +|-------|--------|-------------|-------------| +| _pending_ | _pending_ | _pending_ | _pending_ | + +_This section is populated after the first index run._ + +## Table Definitions + +### `_table_name` + +| Column | Type | Nullable | Default | Index | Foreign Key | +|--------|------|----------|---------|-------|-------------| +| id | _pending_ | NO | auto | PK | β€” | + +**Indexes:** +| Name | Columns | Type | Purpose | +|------|---------|------|---------| +| _pending_ | _pending_ | _pending_ | _pending_ | + +--- + +## Foreign Key Graph + +``` +TableA.id ←── TableB.table_a_id +TableB.id ←── TableC.table_b_id +``` + +## Migration History + +| Migration | Date | Tables Affected | Reversible | +|----------|------|-----------------|-----------| +| _pending_ | _pending_ | _pending_ | _pending_ | + +## Seeded Data + +| Table | Records | Purpose | +|-------|---------|---------| +| _pending_ | _pending_ | _pending_ | \ No newline at end of file diff --git a/.architect/entities/entities.md b/.architect/entities/entities.md new file mode 100644 index 0000000..0165681 --- /dev/null +++ b/.architect/entities/entities.md @@ -0,0 +1,47 @@ +# Domain Entities + +> Auto-generated by `architect-indexer`. DO NOT edit manually. + +## Entity List + +| Entity | Module | Primary Key | Description | +|--------|--------|-------------|-------------| +| _pending_ | _pending_ | _pending_ | _pending_ | + +## Entity Relationships + +``` +EntityA ──1:N──→ EntityB + β”‚ β”‚ + └──N:M──→ EntityC β”€β”˜ +``` + +_This section is populated after the first index run._ + +## Detailed Entity Definitions + +### _EntityName_ + +| Field | Type | Nullable | Default | Description | +|-------|------|----------|---------|-------------| +| id | _pending_ | No | auto | _pending_ | + +**Relations:** +- _pending_ + +**Business Rules:** +- _pending_ + +--- + +## Value Objects + +| Value Object | Type | Used By | Validation | +|-------------|------|---------|------------| +| _pending_ | _pending_ | _pending_ | _pending_ | + +## Enums + +| Enum | Values | Used By | +|------|--------|---------| +| _pending_ | _pending_ | _pending_ | \ No newline at end of file diff --git a/.architect/project.json b/.architect/project.json new file mode 100644 index 0000000..bd6f8b8 --- /dev/null +++ b/.architect/project.json @@ -0,0 +1,42 @@ +{ + "version": 1, + "indexed_at": "", + "project": { + "name": "", + "type": "", + "framework": "", + "language": "", + "description": "", + "repository": "", + "entry_points": [], + "rootDir": "" + }, + "structure": { + "directories": {}, + "key_files": {} + }, + "tech_stack": { + "languages": [], + "frameworks": [], + "databases": [], + "runtimes": [], + "package_managers": [], + "testing_frameworks": [], + "ci_cd": [] + }, + "modules": [], + "conventions": { + "naming": { + "files": "", + "variables": "", + "classes": "", + "functions": "", + "constants": "" + }, + "patterns": [], + "forbidden": [] + }, + "entities": [], + "api_endpoints": [], + "db_tables": [] +} \ No newline at end of file diff --git a/.architect/state.json b/.architect/state.json new file mode 100644 index 0000000..976c14f --- /dev/null +++ b/.architect/state.json @@ -0,0 +1,71 @@ +{ + "version": 1, + "status": "not_indexed", + "last_full_index": null, + "last_incremental_update": null, + "last_file_count": 0, + "file_hashes": {}, + "directory_hashes": {}, + "dependency_hashes": { + "package_json": null, + "composer_json": null, + "go_mod": null, + "pubspec_yaml": null, + "cargo_toml": null, + "requirements_txt": null + }, + "sections": { + "architecture_overview": { + "last_updated": null, + "file_hash": null, + "status": "stale" + }, + "dependency_graph": { + "last_updated": null, + "file_hash": null, + "status": "stale" + }, + "entities": { + "last_updated": null, + "file_hash": null, + "status": "stale" + }, + "db_schema": { + "last_updated": null, + "file_hash": null, + "status": "stale" + }, + "api_surface": { + "last_updated": null, + "file_hash": null, + "status": "stale" + }, + "conventions": { + "last_updated": null, + "file_hash": null, + "status": "stale" + }, + "tech_stack": { + "last_updated": null, + "file_hash": null, + "status": "stale" + }, + "file_graph": { + "last_updated": null, + "file_hash": null, + "status": "stale" + }, + "module_graph": { + "last_updated": null, + "file_hash": null, + "status": "stale" + } + }, + "staleness_threshold_hours": 24, + "indexing_agent": "architect-indexer", + "pipeline_integration": { + "check_on_first_contact": true, + "incremental_on_file_change": true, + "full_reindex_on_dependency_change": true + } +} \ No newline at end of file diff --git a/.architect/tech-stack/stack.md b/.architect/tech-stack/stack.md new file mode 100644 index 0000000..bbd3387 --- /dev/null +++ b/.architect/tech-stack/stack.md @@ -0,0 +1,57 @@ +# Tech Stack + +> Auto-generated by `architect-indexer`. DO NOT edit manually. + +## Runtime & Platform + +| Component | Version | Purpose | +|-----------|---------|---------| +| Language | _pending_ | _pending_ | +| Runtime | _pending_ | _pending_ | +| Package Manager | _pending_ | _pending_ | + +_This section is populated after the first index run._ + +## Framework + +| Framework | Version | Purpose | +|-----------|---------|---------| +| _pending_ | _pending_ | _pending_ | + +## Database + +| Engine | Version | Purpose | Connection | +|--------|---------|---------|------------| +| _pending_ | _pending_ | _pending_ | _pending_ | + +## Key Dependencies + +| Package | Version | Purpose | Critical | +|---------|---------|---------|----------| +| _pending_ | _pending_ | _pending_ | _pending_ | + +## Development Tools + +| Tool | Version | Purpose | +|------|---------|---------| +| Linter | _pending_ | _pending_ | +| Formatter | _pending_ | _pending_ | +| Test Runner | _pending_ | _pending_ | +| Type Checker | _pending_ | _pending_ | + +## Infrastructure + +| Component | Technology | Purpose | +|-----------|-----------|---------| +| Web Server | _pending_ | _pending_ | +| Cache | _pending_ | _pending_ | +| Queue | _pending_ | _pending_ | +| Storage | _pending_ | _pending_ | +| CI/CD | _pending_ | _pending_ | + +## Environment Variables + +| Variable | Required | Default | Purpose | +|----------|----------|---------|---------| +| NODE_ENV | No | development | Environment | +| _pending_ | _pending_ | _pending_ | _pending_ | \ No newline at end of file diff --git a/.gitignore b/.gitignore index 73aaf3a..297a342 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ package-lock.json .env *.log .DS_Store +tsconfig.tsbuildinfo # Test artifacts (generated at runtime) tests/node_modules/ @@ -16,4 +17,9 @@ tests/reports/ .kilo/logs/ .kilo/reports/ .kilo/EVOLUTION_LOG.md -.kilo/WORKFLOW_AUDIT.md \ No newline at end of file +.kilo/WORKFLOW_AUDIT.md + +# Architect generated maps (can be large, auto-indexed) +# Note: .architect/ md and json files ARE tracked for team orientation +# Only maps/ with file graphs can be very large +.architect/maps/ \ No newline at end of file diff --git a/.kilo/agents/architect-indexer.md b/.kilo/agents/architect-indexer.md new file mode 100644 index 0000000..5a0da59 --- /dev/null +++ b/.kilo/agents/architect-indexer.md @@ -0,0 +1,159 @@ +--- +description: Indexes and maps project codebase architecture into .architect/ directory. Creates and maintains structured documentation of entities, APIs, DB schema, file graphs, and conventions. +mode: subagent +model: ollama-cloud/glm-5.1 +variant: thinking +color: "#10B981" +permission: + read: allow + edit: allow + write: allow + bash: allow + glob: allow + grep: allow + task: + "*": deny + "system-analyst": allow + "orchestrator": allow +--- + +# Architect Indexer + +## Role +Project cartographer. Scans the codebase and produces a structured, navigable map in `.architect/` that all agents can reference for orientation. + +## Execution Environment (CRITICAL) +**All indexing runs inside a Docker container.** Never run npm/npx/bun/node on the host machine. + +```bash +# Build & run +docker compose -f docker/docker-compose.architect.yml build +docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer + +# Or via npm shortcuts +npm run arch:build && npm run arch:index +``` + +## When Invoked +- Orchestrator detects missing or stale `.architect/state.json` on first contact with a project +- After structural changes (file add/remove, new module, new migration, new endpoint) +- On `/index-project` command +- Incrementally after `lead-developer` or `the-fixer` complete tasks that modify project structure + +## Indexing Protocol + +### Step 1: Detect Project Type +``` +1. Check for package.json β†’ Node.js/TypeScript project +2. Check for composer.json β†’ PHP project +3. Check for go.mod β†’ Go project +4. Check for pubspec.yaml β†’ Flutter/Dart project +5. Check for requirements.txt/pyproject.toml β†’ Python project +6. If none found β†’ Generic project +``` + +### Step 2: Full Index (first run or staleness > 24h) +1. Scan directory structure β†’ `architecture/overview.md` +2. Parse dependency files β†’ `tech-stack/stack.md` +3. Find all models/entities β†’ `entities/entities.md` +4. Find all DB migrations/schemas β†’ `db-schema/schema.md` +5. Find all API routes/controllers β†’ `api-surface/endpoints.md` +6. Detect lint/format configs β†’ `conventions/conventions.md` +7. Build import graph β†’ `maps/file-graph.json` +8. Build module graph β†’ `maps/module-graph.json` +9. Populate `project.json` with metadata +10. Update `state.json` with hashes and timestamp + +### Step 3: Incremental Update (on file change) +1. Compare `state.json` file hashes with current files +2. Determine which sections are affected: + - New/removed file β†’ update `file-graph.json`, `module-graph.json` + - New dependency β†’ update `tech-stack/stack.md`, run full reindex + - New migration β†’ update `db-schema/schema.md` + - New model/entity β†’ update `entities/entities.md` + - New endpoint β†’ update `api-surface/endpoints.md` +3. Only regenerate affected sections +4. Update `state.json` hashes + +### Step 4: Validate +1. Check README.md navigation links still valid +2. Verify project.json fields are non-empty +3. Confirm no circular dependencies in module graph +4. Update README.md quick status table + +## Output Format + +### project.json Structure +```json +{ + "version": 1, + "project": { + "name": "from package.json or directory name", + "type": "laravel|nextjs|express|go-api|flutter|django|fastapi|generic", + "framework": "framework name and version", + "language": "primary language", + "description": "from package.json description or README", + "repository": "from git remote", + "entry_points": ["main entry files"], + "rootDir": "project root" + }, + "structure": { "directories": {}, "key_files": {} }, + "tech_stack": { "languages": [], "frameworks": [], "databases": [] }, + "modules": [{ "name": "", "path": "", "exports": [], "imports": [] }], + "entities": [{ "name": "", "module": "", "fields": [], "relations": [] }], + "api_endpoints": [{ "method": "", "path": "", "controller": "", "auth": "" }], + "db_tables": [{ "name": "", "columns": [], "indexes": [], "foreign_keys": [] }], + "conventions": { "naming": {}, "patterns": [], "forbidden": [] } +} +``` + +### state.json Section Hashes +For each section, store a hash of the source files used to generate it: +```json +{ + "sections": { + "entities": { + "last_updated": "2026-04-19T12:00:00Z", + "file_hash": "sha256:abc...", + "status": "fresh|stale|missing" + } + } +} +``` + +## Staleness Detection + +A section is **stale** if: +1. Any source file it was generated from has changed (hash mismatch) +2. More than 24 hours since last update +3. New files were added to directories the section covers + +A section is **missing** if: +1. It has never been generated +2. Its output file doesn't exist + +## File Size Limits + +| Output File | Max Lines | If Exceeded | +|-------------|-----------|-------------| +| overview.md | 200 | Split into multiple files | +| entities.md | 300 | Group by module | +| schema.md | 300 | Split by table group | +| endpoints.md | 200 | Split by API version | +| conventions.md | 150 | Link to external docs | +| stack.md | 100 | Summarize, link to lock files | +| file-graph.json | 2000 | Compress edges | +| module-graph.json | 500 | Aggregate leaf modules | + +## Conventions +- Use `` when posting indexing results +- Post a comment on the issue: "## πŸ— architect-indexer completed β€” `.architect/` indexed N files, M modules, K endpoints" +- Never modify source code β€” only write to `.architect/` +- Never delete sections β€” only update or add new ones + +## Handoff +After indexing, return control to `orchestrator` with: +- Summary of what was indexed +- Number of files, modules, entities, endpoints found +- Any circular dependencies or architectural violations detected +- List of sections that are still empty (no data found) \ No newline at end of file diff --git a/.kilo/agents/orchestrator.md b/.kilo/agents/orchestrator.md index ae8e90a..ae020ab 100755 --- a/.kilo/agents/orchestrator.md +++ b/.kilo/agents/orchestrator.md @@ -43,6 +43,7 @@ permission: "agent-architect": allow "php-developer": allow "python-developer": allow + "architect-indexer": allow --- # Orchestrator @@ -52,6 +53,11 @@ Task dispatcher and state machine manager. Route by issue status; enforce workfl ## Behavior - Route by status: newβ†’history-miner, researchingβ†’system-analyst, testingβ†’sdet-engineer, implementingβ†’lead-developer, failβ†’the-fixer +- **FIRST CONTACT**: Before any task delegation, check `.architect/state.json`: + - If missing or `status === 'not_indexed'` β†’ delegate `architect-indexer` FIRST + - If stale (any section `status === 'stale'`) β†’ delegate `architect-indexer` incrementally + - If fresh β†’ proceed with normal routing, read relevant `.architect/` sections for context +- After `lead-developer` or `the-fixer` complete tasks that add/remove files β†’ mark affected `.architect/` sections as stale - Check blockers before routing; suspend if dependencies unmet - Only you authorize release-manager after evaluator confirmation - Comms: "To: [Agent]. Task: [essence]. Context: [file ref]" @@ -64,6 +70,7 @@ Task dispatcher and state machine manager. Route by issue status; enforce workfl | Agent | When | |-------|------| | requirement-refiner | New vague request: refine requirements | +| architect-indexer | First contact or stale `.architect/`: index project | | history-miner | New issue: check duplicates | | system-analyst | Researching: design specifications | | sdet-engineer | Designing: write failing tests | diff --git a/.kilo/capability-index.yaml b/.kilo/capability-index.yaml index b08d5d7..bc80c81 100644 --- a/.kilo/capability-index.yaml +++ b/.kilo/capability-index.yaml @@ -517,6 +517,7 @@ agents: - reflector - memory-manager - agent-architect + - architect-indexer release-manager: capabilities: @@ -741,6 +742,42 @@ agents: mode: subagent delegates_to: [] + # Project Mapping + architect-indexer: + capabilities: + - codebase_indexing + - project_mapping + - architecture_documentation + - dependency_analysis + - entity_extraction + - api_surface_discovery + - convention_detection + - staleness_detection + receives: + - project_root_directory + - stale_sections_list + produces: + - .architect/state.json + - .architect/project.json + - .architect/README.md + - architecture_overview + - dependency_graph + - entity_documentation + - db_schema_documentation + - api_surface_documentation + - convention_documentation + - file_graph + - module_graph + forbidden: + - code_changes + - implementation + model: ollama-cloud/glm-5.1 + variant: thinking + mode: subagent + delegates_to: + - system-analyst + - orchestrator + # Capability Routing Map capability_routing: code_writing: lead-developer @@ -806,6 +843,14 @@ agents: go_concurrent_programming: go-developer go_authentication: go-developer go_microservices: go-developer + # Project Mapping + codebase_indexing: architect-indexer + project_mapping: architect-indexer + architecture_documentation: architect-indexer + dependency_analysis: architect-indexer + entity_extraction: architect-indexer + api_surface_discovery: architect-indexer + convention_detection: architect-indexer # Parallelizable Tasks parallel_groups: diff --git a/.kilo/commands/index-project.md b/.kilo/commands/index-project.md new file mode 100644 index 0000000..4021ee2 --- /dev/null +++ b/.kilo/commands/index-project.md @@ -0,0 +1,239 @@ +--- +description: Index the project codebase into .architect/ directory for agent navigation and orientation +--- + +# Index Project Command + +You are the `architect-indexer` agent. Your task is to scan the project codebase and populate the `.architect/` directory with structured, navigable documentation. + +## Docker Execution (REQUIRED) + +**All indexing runs inside a Docker container.** Never run `npm`, `npx`, `bun`, or `node` directly on the host machine. + +### Build & Run + +```bash +# Build the indexer image (first time or after Dockerfile changes) +docker compose -f docker/docker-compose.architect.yml build + +# Full index (first run or staleness > 24h) +docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer + +# Incremental update (only stale sections) +docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer --mode incremental + +# Full index with specific sections only +docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer --mode incremental --sections entities,api_surface +``` + +### Quick NPM Scripts (Host β†’ Docker) + +```bash +npm run arch:build # Build Docker image +npm run arch:index # Run full index in container +npm run arch:index:full # Force full index +npm run arch:index:incremental # Only stale sections +npm run arch:status # Check container status +``` + +## Workflow + +### Phase 1: Detect & Scan + +1. **Detect project type** by checking for: + - `package.json` β†’ Node.js / TypeScript + - `composer.json` β†’ PHP / Laravel / Symfony / WordPress + - `go.mod` β†’ Go + - `pubspec.yaml` β†’ Flutter / Dart + - `requirements.txt` or `pyproject.toml` β†’ Python + - `Cargo.toml` β†’ Rust + - None found β†’ Generic + +2. **Check `.architect/state.json`** for staleness: + - If `status === "not_indexed"` β†’ Full index + - If `last_full_index` older than 24h β†’ Full index + - If only specific sections stale β†’ Incremental update + +3. **Scan directory tree** using `glob` tools + +### Phase 2: Full Index + +Execute each step as an atomic subtask: + +#### 2a. Project Metadata β†’ `project.json` +```bash +# Scan for project config files +Read: package.json, composer.json, go.mod, pubspec.yaml, pyproject.toml +# Extract: name, type, framework, language, description, entry points +``` + +Populate: +- `project.name` +- `project.type` +- `project.framework` +- `project.language` +- `project.description` +- `project.entry_points` +- `project.repository` (from git remote) +- `structure.directories` (key dirs only) +- `structure.key_files` (config, entry points) +- `tech_stack` (from dependencies) + +#### 2b. Architecture Overview β†’ `architecture/overview.md` +- Identify architectural pattern (layered, clean, MVC, etc.) +- Map directory structure to layers +- Identify module boundaries +- List external services +- Draw simple ASCII diagram + +#### 2c. Dependency Graph β†’ `architecture/dependency-graph.md` +- Parse import/require statements across all source files +- Build module-to-module dependency map +- Detect circular dependencies +- List external packages with versions + +#### 2d. Entities β†’ `entities/entities.md` +- Find all model/entity/domain class files +- Extract fields, types, relations +- For Laravel: `app/Models/` +- For Go: `internal/*/model/` +- For Node.js: `src/models/` or `src/entities/` +- For Python: `models.py`, `schemas.py` +- Document relationships (1:1, 1:N, N:M) + +#### 2e. DB Schema β†’ `db-schema/schema.md` +- Find migration files +- Parse table definitions, columns, indexes, foreign keys +- For Laravel: `database/migrations/` +- For Go: migration files +- For Node.js: `prisma/schema.prisma` or migration files +- For Python: Django migrations or Alembic + +#### 2f. API Surface β†’ `api-surface/endpoints.md` +- Find all route/endpoint definitions +- For Laravel: `routes/*.php` +- For Express: `src/routes/` or route files +- For Go: handler registrations +- For Python: URL confs or router files +- Document: method, path, auth, controller, description + +#### 2g. Conventions β†’ `conventions/conventions.md` +- Read eslint/prettier/phpstan/lint configs +- Read existing code patterns from a few representative files +- Identify naming conventions (files, variables, classes) +- Identify architectural patterns (repository, service, etc.) +- Check `.kilo/rules/` for project-specific rules + +#### 2h. Tech Stack β†’ `tech-stack/stack.md` +- Parse all dependency files +- Read versions from lock files +- Identify dev tools (linters, formatters, test runners) +- Identify infrastructure (Docker, CI configs) + +#### 2i. File Graph β†’ `maps/file-graph.json` +```json +{ + "version": 1, + "generated_at": "ISO timestamp", + "root": "project root absolute path", + "nodes": { + "path/to/file.ts": { + "type": "module|script|config|test|migration|style", + "imports": ["./other/file"], + "exports": ["exportedName"], + "size_bytes": 1234, + "last_modified": "ISO timestamp" + } + }, + "edges": [ + { "from": "path/to/file.ts", "to": "./other/file.ts", "type": "import" } + ] +} +``` + +#### 2j. Module Graph β†’ `maps/module-graph.json` +```json +{ + "version": 1, + "generated_at": "ISO timestamp", + "modules": [ + { + "name": "module-name", + "path": "src/modules/module-name", + "type": "feature|shared|core|infra", + "imports": ["other-module"], + "exports": ["PublicClass", "publicFunction"], + "entities": ["EntityName"], + "endpoints": 5, + "file_count": 12 + } + ] +} +``` + +### Phase 3: Update State + +1. Compute SHA256 hashes of all scanned source files +2. Update `state.json`: + - `status: "indexed"` + - `last_full_index: ` + - `last_incremental_update: ` + - `last_file_count: ` + - `file_hashes: { : }` + - Update each section's `status` to `"fresh"`, `last_updated`, `file_hash` + +3. Update `README.md` Quick Status table with: + - `Last Indexed` timestamp + - `Index Version` + - `Files Tracked` count + - `Modules` count + - `Staleness: fresh` + +### Phase 4: Report + +Post a summary comment: + +```markdown +## πŸ— architect-indexer completed + +**Files Tracked**: {count} +**Modules Found**: {count} +**Entities Found**: {count} +**Endpoints Found**: {count} +**DB Tables Found**: {count} +**Circular Dependencies**: {count} (listed if any) + +### Staleness +All sections: βœ… fresh + +### Architecture Violations +- {any violations found, or "None detected"} +``` + +## Incremental Update Mode + +When only specific sections are stale: + +1. Check `state.json` for which sections are `stale` +2. Only regenerate stale sections +3. Update `state.json` with new hashes +4. Update README.md status +5. Skip sections that are `fresh` + +## Error Handling + +- If `.architect/` directory doesn't exist, create it +- If a section has no data (e.g., no DB in frontend project), write "Not applicable for this project type" +- If scanning fails partially, mark that section as `error` in `state.json` and continue +- Never delete existing sections during incremental updates + +## Token Budget + +| Task | Max Tokens | +|------|-----------| +| Full index (small project < 50 files) | 10,000 | +| Full index (medium project 50-200 files) | 20,000 | +| Full index (large project > 200 files) | 30,000 | +| Incremental update (1-3 sections) | 5,000 | + + \ No newline at end of file diff --git a/.kilo/commands/pipeline.md b/.kilo/commands/pipeline.md index 7cd980b..e996f38 100644 --- a/.kilo/commands/pipeline.md +++ b/.kilo/commands/pipeline.md @@ -21,6 +21,20 @@ You are orchestrating the full agent pipeline for issue #{issue_number}. Execute - Referenced files - Current status label +### Step 1.5: Check Architect Index + +Before routing any agent, check if `.architect/` is indexed (runs in Docker): + +1. Read `.architect/state.json` +2. If missing or `status === 'not_indexed'`: + - Run full index in Docker: `docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer` + - Wait for indexing to complete before proceeding +3. If any section has `status === 'stale'`: + - Run incremental in Docker: `docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer --mode incremental` +4. If `status === 'indexed'` and all sections fresh: + - Read relevant `.architect/` sections based on agent type + - Inject context into agent prompt + ## Step 2: Check for Duplicates 1. Use `grep` to search git history for similar issues: diff --git a/.kilo/rules/architect-first-contact.md b/.kilo/rules/architect-first-contact.md new file mode 100644 index 0000000..7bd541c --- /dev/null +++ b/.kilo/rules/architect-first-contact.md @@ -0,0 +1,97 @@ +# Architect First-Contact Rules + +When an orchestrator or pipeline starts working on a project, it MUST check the project's `.architect/` directory for orientation before delegating tasks. + +## Mandatory Check + +**All indexing runs in Docker. Never run npm/npx/bun on the host.** + +### Before Any Task Delegation + +``` +1. Read .architect/state.json +2. If file missing or status === 'not_indexed': + β†’ Run: docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer + β†’ WAIT for indexing to complete before routing any other agent +3. If any section has status === 'stale': + β†’ Run: docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer --mode incremental + β†’ Proceed with task routing for non-stale sections +4. If status === 'indexed' and all sections fresh: + β†’ Proceed with normal routing + β†’ Include relevant .architect/ sections in agent context +``` + +### Quick Commands + +```bash +npm run arch:build # Build Docker image +npm run arch:index # Run full index in container +npm run arch:index:full # Force full index +npm run arch:index:incremental # Only stale sections +npm run arch:status # Check container status +``` + +### Staleness Thresholds + +| Condition | Action | +|-----------|--------| +| `state.json` missing | Full index required | +| `status === 'not_indexed'` | Full index required | +| Last full index > 24h ago | Full index recommended | +| Any section `status === 'stale'` | Incremental update of stale sections | +| All sections `status === 'fresh'` | No index needed, proceed | + +### Post-Task Staleness Marking + +After `lead-developer` or `the-fixer` complete a task: + +``` +1. Compare task's modified files with .architect/state.json sections +2. Mark affected sections as 'stale': + - New/removed files β†’ file_graph, module_graph + - New dependency β†’ tech_stack (full reindex) + - New migration β†’ db_schema + - New model/entity β†’ entities + - New API endpoint β†’ api_surface + - Convention changes β†’ conventions + - Structural refactor β†’ architecture_overview, dependency_graph +3. Update state.json with stale section markers +``` + +### Context Injection + +When delegating to any agent, include relevant `.architect/` context: + +| Agent | Sections to Include | +|-------|-------------------| +| system-analyst | architecture/overview, entities, db-schema, api-surface | +| sdet-engineer | api-surface, entities, conventions | +| lead-developer | conventions, entities, architecture/overview | +| code-skeptic | conventions, architecture/dependency-graph | +| the-fixer | conventions, file relevant to bug | +| php-developer | conventions, entities, db-schema, api-surface | +| python-developer | conventions, entities, db-schema, api-surface | +| go-developer | conventions, entities, db-schema, api-surface | +| frontend-developer | conventions, api-surface, architecture/overview | +| backend-developer | conventions, entities, db-schema, api-surface | + +### Project Type Routing + +Use `.architect/project.json` `project.type` to route language-specific agents: + +| project.type | Primary Agent | +|-------------|--------------| +| laravel | php-developer | +| symfony | php-developer | +| wordpress | php-developer | +| nextjs | frontend-developer | +| express | backend-developer | +| go-api | go-developer | +| flutter | flutter-developer | +| django | python-developer | +| fastapi | python-developer | +| generic | lead-developer | + +## State File Reference + +See `.kilo/skills/project-mapping/SKILL.md` for full `state.json` schema and `.architect/` structure. \ No newline at end of file diff --git a/.kilo/skills/project-mapping/SKILL.md b/.kilo/skills/project-mapping/SKILL.md new file mode 100644 index 0000000..0934813 --- /dev/null +++ b/.kilo/skills/project-mapping/SKILL.md @@ -0,0 +1,203 @@ +# Project Mapping Skill + +Scans and indexes project codebase into `.architect/` directory for agent navigation. + +## Purpose + +When agents start working on a project for the first time, they need orientation: +- What kind of project is this? +- Where are the models, controllers, routes? +- What's the tech stack? +- What conventions does it follow? +- How do modules depend on each other? + +The `.architect/` directory provides this orientation as structured, machine+human-readable files. + +## Directory Structure + +``` +.architect/ +β”œβ”€β”€ README.md # Navigation index (auto-updated) +β”œβ”€β”€ project.json # Machine-readable project metadata +β”œβ”€β”€ state.json # Index freshness state (hashes, timestamps) +β”œβ”€β”€ architecture/ +β”‚ β”œβ”€β”€ overview.md # Architecture pattern, layers, boundaries +β”‚ └── dependency-graph.md # Module dependency graph (no circles!) +β”œβ”€β”€ entities/ +β”‚ └── entities.md # Domain entities, fields, relationships +β”œβ”€β”€ db-schema/ +β”‚ └── schema.md # Tables, columns, indexes, foreign keys +β”œβ”€β”€ api-surface/ +β”‚ └── endpoints.md # API methods, paths, auth, controllers +β”œβ”€β”€ conventions/ +β”‚ └── conventions.md # Naming, patterns, forbidden practices +β”œβ”€β”€ maps/ +β”‚ β”œβ”€β”€ file-graph.json # Programmatic fileβ†’imports/exports graph +β”‚ └── module-graph.json # Programmatic moduleβ†’dependencies graph +└── tech-stack/ + └── stack.md # Languages, frameworks, databases, tools +``` + +## Orchestrator Integration + +### Docker Execution (REQUIRED) + +**All indexing runs inside a Docker container.** The orchestrator must NEVER run `npm`, `npx`, `bun`, or `node` on the host. All agent tooling is containerized. + +```bash +# Build image +docker compose -f docker/docker-compose.architect.yml build + +# Run full index +docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer + +# Run incremental +docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer --mode incremental + +# NPM shortcuts (host β†’ Docker) +npm run arch:build +npm run arch:index +npm run arch:index:full +npm run arch:index:incremental +``` + +### First Contact Check + +```typescript +// In orchestrator's first-contact logic: +const stateFile = readFileSync('.architect/state.json') +const state = JSON.parse(stateFile) + +if (state.status === 'not_indexed' || isStale(state)) { + // Launch architect-indexer before any task + delegate('architect-indexer', { mode: 'full' }) +} else if (hasStaleSections(state)) { + // Launch incremental update + delegate('architect-indexer', { mode: 'incremental', sections: staleSections }) +} +``` + +### Staleness Detection + +```typescript +function isStale(state: ArchitectState): boolean { + const lastUpdate = new Date(state.last_full_index) + const hoursSince = (Date.now() - lastUpdate.getTime()) / (1000 * 60 * 60) + return hoursSince > state.staleness_threshold_hours +} + +function hasStaleSections(state: ArchitectState): string[] { + return Object.entries(state.sections) + .filter(([_, s]) => s.status === 'stale') + .map(([name]) => name) +} +``` + +### After Task Completion + +When `lead-developer` or `the-fixer` complete tasks that modify project structure: +1. Check if any new files were created or deleted +2. If yes β†’ mark affected sections as `stale` in `state.json` +3. On next orchestrator cycle β†’ run incremental update + +## Staleness Rules + +| Event | Sections to Mark Stale | +|-------|----------------------| +| New/removed file | `file_graph`, `module_graph` | +| New dependency | `tech_stack`, full reindex | +| New migration | `db_schema` | +| New model/entity | `entities` | +| New API endpoint | `api_surface` | +| Convention change | `conventions` | +| Structural refactor | `architecture_overview`, `dependency_graph` | + +## Reading `.architect/` Data + +Agents should read the relevant section before starting work: + +```typescript +// Example: Before creating a new model +const entities = readFileSync('.architect/entities/entities.md') +// Check existing entities, relations, naming conventions + +// Example: Before adding an API endpoint +const endpoints = readFileSync('.architect/api-surface/endpoints.md') +// Check existing routes, avoid duplicates, follow patterns + +// Example: Before modifying DB schema +const schema = readFileSync('.architect/db-schema/schema.md') +// Check existing tables, foreign keys, avoid breaking changes +``` + +## Integration Points + +1. **Orchestrator**: Check `.architect/state.json` before routing tasks +2. **system-analyst**: Read architecture files when designing specs +3. **lead-developer**: Read conventions and entity files before coding +4. **sdet-engineer**: Read API surface and entity files before writing tests +5. **code-skeptic**: Read conventions when reviewing code +6. **history-miner**: Compare current vs. indexed state to find changes + +## Schemas + +### project.json Schema + +```json +{ + "version": 1, + "indexed_at": "ISO8601", + "project": { + "name": "string", + "type": "laravel|nextjs|express|go-api|flutter|django|fastapi|generic", + "framework": "string", + "language": "string", + "description": "string", + "repository": "string", + "entry_points": ["string"], + "rootDir": "string" + }, + "structure": { "directories": {}, "key_files": {} }, + "tech_stack": { "languages": [], "frameworks": [], "databases": [], "runtimes": [], "package_managers": [], "testing_frameworks": [], "ci_cd": [] }, + "modules": [{ "name": "", "path": "", "exports": [], "imports": [] }], + "entities": [{ "name": "", "module": "", "fields": [], "relations": [] }], + "api_endpoints": [{ "method": "", "path": "", "controller": "", "auth": "" }], + "db_tables": [{ "name": "", "columns": [], "indexes": [], "foreign_keys": [] }], + "conventions": { "naming": {}, "patterns": [], "forbidden": [] } +} +``` + +### state.json Schema + +```json +{ + "version": 1, + "status": "not_indexed|indexed|error", + "last_full_index": "ISO8601|null", + "last_incremental_update": "ISO8601|null", + "last_file_count": 0, + "file_hashes": { "path": "sha256hash" }, + "directory_hashes": { "path": "sha256hash" }, + "dependency_hashes": { "package_json": "sha256|null" }, + "sections": { + "architecture_overview": { "last_updated": "ISO8601|null", "file_hash": "sha256|null", "status": "stale|fresh|error|missing" }, + "dependency_graph": { ... }, + "entities": { ... }, + "db_schema": { ... }, + "api_surface": { ... }, + "conventions": { ... }, + "tech_stack": { ... }, + "file_graph": { ... }, + "module_graph": { ... } + }, + "staleness_threshold_hours": 24 +} +``` + +## Best Practices + +1. **Always check state.json** before reading `.architect/` β€” if stale, trigger reindex +2. **Never manually edit** `.architect/` files β€” they are auto-generated +3. **Commit `.architect/` to git** (except `maps/` which can be large) so team has same orientation +4. **Add `.architect/maps/` to `.gitignore`** if file-graph becomes too large +5. **After significant changes**, run `/index-project` to force refresh \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 4b244e8..26fc427 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,6 +32,7 @@ Agent: Runs full pipeline for issue #42 with Gitea logging | `/research [topic]` | Run research and self-improvement | `/research multi-agent` | | `/evolution log` | Log agent model change | `/evolution log planner "reason"` | | `/evolution report` | Generate evolution report | `/evolution report` | +| `/index-project` | Index codebase into .architect/ for agent orientation | `/index-project` | | `/web-test ` | Visual regression testing in Docker | `/web-test https://bbox.wtf` | | `/e2e-test ` | E2E browser automation tests | `/e2e-test https://my-app.com` | @@ -85,6 +86,7 @@ These agents are invoked automatically by `/pipeline` or manually via `@mention` | `@agent-architect` | Creates new agents | glm-5.1 | thinking | capability-analyst, requirement-refiner, system-analyst | | `@workflow-architect` | Creates workflows | glm-5.1 | thinking | *(edits files)* | | `@markdown-validator` | Validates Markdown | nemotron-3-nano:30b | β€” | orchestrator | +| `@architect-indexer` | Maps project codebase into .architect/ | glm-5.1 | thinking | system-analyst, orchestrator | ### Cognitive Enhancement | Agent | Role | Model | Variant | Can Call | @@ -292,8 +294,74 @@ where: | `.kilo/shared/gitea-api.md` | Centralized Gitea API client | | `.kilo/shared/gitea-commenting.md` | Comment format for Gitea | | `.kilo/shared/self-evolution.md` | Self-evolution protocol | +| `.kilo/rules/architect-first-contact.md` | First-contact project indexing rules | +| `.kilo/skills/project-mapping/SKILL.md` | Project mapping skill (`.architect/` system) | +| `.architect/` | Project codebase map (auto-indexed, see below) | | `src/kilocode/` | TypeScript API for programmatic use | +## `.architect/` Project Mapping + +The `.architect/` directory is the **project brain** β€” a structured, auto-indexed map of the codebase that all agents read before starting work. + +### When Is It Used + +1. **Orchestrator first contact**: Before routing any task, checks `.architect/state.json` +2. **Stale or missing**: Triggers `architect-indexer` to build/update +3. **Fresh**: Agents read relevant sections for context +4. **After changes**: `lead-developer`/`the-fixer` mark affected sections as stale + +### Structure + +``` +.architect/ +β”œβ”€β”€ README.md # Navigation index (auto-updated) +β”œβ”€β”€ project.json # Machine-readable project metadata +β”œβ”€β”€ state.json # Index freshness state (hashes, timestamps) +β”œβ”€β”€ architecture/ +β”‚ β”œβ”€β”€ overview.md # Architecture pattern, layers, boundaries +β”‚ └── dependency-graph.md # Module dependency graph +β”œβ”€β”€ entities/ +β”‚ └── entities.md # Domain entities, fields, relationships +β”œβ”€β”€ db-schema/ +β”‚ └── schema.md # Tables, columns, indexes, foreign keys +β”œβ”€β”€ api-surface/ +β”‚ └── endpoints.md # API endpoints, methods, auth, controllers +β”œβ”€β”€ conventions/ +β”‚ └── conventions.md # Naming, patterns, forbidden practices +β”œβ”€β”€ maps/ +β”‚ β”œβ”€β”€ file-graph.json # Programmatic fileβ†’imports/exports graph +β”‚ └── module-graph.json # Programmatic moduleβ†’dependencies graph +└── tech-stack/ + └── stack.md # Languages, frameworks, databases, tools +``` + +### Context Injection Per Agent + +| Agent | `.architect/` Sections | +|-------|----------------------| +| system-analyst | architecture/overview, entities, db-schema, api-surface | +| sdet-engineer | api-surface, entities, conventions | +| lead-developer | conventions, entities, architecture/overview | +| code-skeptic | conventions, architecture/dependency-graph | +| the-fixer | conventions, relevant file section | +| php-developer | conventions, entities, db-schema, api-surface | +| python-developer | conventions, entities, db-schema, api-surface | +| go-developer | conventions, entities, db-schema, api-surface | +| frontend-developer | conventions, api-surface, architecture/overview | +| backend-developer | conventions, entities, db-schema, api-surface | + +### Staleness Triggers + +| Event | Sections Marked Stale | +|-------|----------------------| +| New/removed file | file_graph, module_graph | +| New dependency | tech_stack (full reindex) | +| New migration | db_schema | +| New model/entity | entities | +| New API endpoint | api_surface | +| Convention change | conventions | +| Structural refactor | architecture_overview, dependency_graph | + ## Using the TypeScript API ```typescript diff --git a/STRUCTURE.md b/STRUCTURE.md index d9d196d..36b9dff 100644 --- a/STRUCTURE.md +++ b/STRUCTURE.md @@ -6,6 +6,17 @@ This document describes the organized structure of the APAW project. ``` APAW/ +β”œβ”€β”€ .architect/ # Project codebase map (auto-indexed) +β”‚ β”œβ”€β”€ README.md # Navigation index +β”‚ β”œβ”€β”€ project.json # Machine-readable project metadata +β”‚ β”œβ”€β”€ state.json # Index freshness state (hashes, timestamps) +β”‚ β”œβ”€β”€ architecture/ # Architecture overview and dependency graph +β”‚ β”œβ”€β”€ entities/ # Domain entities, fields, relationships +β”‚ β”œβ”€β”€ db-schema/ # Database tables, columns, indexes +β”‚ β”œβ”€β”€ api-surface/ # API endpoints, methods, auth +β”‚ β”œβ”€β”€ conventions/ # Naming, patterns, forbidden practices +β”‚ β”œβ”€β”€ maps/ # Programmatic file/module graphs (.gitignored) +β”‚ └── tech-stack/ # Languages, frameworks, databases, tools β”œβ”€β”€ .kilo/ # Kilo Code configuration β”‚ β”œβ”€β”€ agents/ # 30 agent definitions (YAML frontmatter) β”‚ β”‚ β”œβ”€β”€ orchestrator.md # Main dispatcher @@ -64,7 +75,9 @@ APAW/ β”‚ └── agent-stats.ts # Agent execution statistics β”œβ”€β”€ docker/ # Docker configurations β”‚ β”œβ”€β”€ Dockerfile.playwright # Playwright MCP container +β”‚ β”œβ”€β”€ Dockerfile.architect-indexer # Project indexer container β”‚ β”œβ”€β”€ docker-compose.yml # Base config +β”‚ β”œβ”€β”€ docker-compose.architect.yml # Architect indexer service β”‚ └── docker-compose.web-testing.yml β”œβ”€β”€ AGENTS.md # Agent reference (main config) β”œβ”€β”€ STRUCTURE.md # This document @@ -77,6 +90,31 @@ APAW/ Maps agent capabilities for orchestrator routing: +### `.architect/` Directory (Project Brain) + +Auto-indexed codebase map that all agents read before starting work: + +| File | Purpose | +|------|---------| +| `.architect/README.md` | Navigation index (auto-updated) | +| `.architect/project.json` | Machine-readable project metadata | +| `.architect/state.json` | Index freshness (hashes, timestamps) | +| `.architect/architecture/overview.md` | Architecture pattern, layers | +| `.architect/architecture/dependency-graph.md` | Module dependency graph | +| `.architect/entities/entities.md` | Domain entities, relations | +| `.architect/db-schema/schema.md` | Tables, columns, indexes, FKs | +| `.architect/api-surface/endpoints.md` | API routes, methods, auth | +| `.architect/conventions/conventions.md` | Coding standards, patterns | +| `.architect/tech-stack/stack.md` | Languages, frameworks, versions | +| `.architect/maps/file-graph.json` | File imports/exports graph | +| `.architect/maps/module-graph.json` | Module dependencies graph | + +See `.kilo/skills/project-mapping/SKILL.md` for full documentation. + +### capability-index.yaml + +Maps agent capabilities for orchestrator routing: + ```yaml agents: php-developer: diff --git a/docker/Dockerfile.architect-indexer b/docker/Dockerfile.architect-indexer new file mode 100644 index 0000000..31a7ac9 --- /dev/null +++ b/docker/Dockerfile.architect-indexer @@ -0,0 +1,63 @@ +# Architect Indexer Dockerfile +# Scans target project codebase and generates .architect/ directory +# +# Usage: +# docker compose -f docker/docker-compose.architect.yml build +# docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer +# docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer --mode incremental + +# ── Build stage ──────────────────────────────────────────────────── +FROM oven/bun:1-alpine AS builder + +WORKDIR /build + +# Copy dependency files first (cache layer) +COPY package.json ./ +# bun.lock might not exist; handle gracefully +COPY bun.lock* ./ + +# Install dependencies +RUN bun install 2>/dev/null || npm install 2>/dev/null || true + +# Copy source for build +COPY tsconfig.json ./ +COPY src/ ./src/ + +# Build TypeScript +RUN bun run build 2>/dev/null || npx tsc 2>/dev/null || true + +# ── Production stage ──────────────────────────────────────────────── +FROM node:20-alpine AS production + +WORKDIR /app + +# Security: non-root user +RUN addgroup -S apaw && adduser -S apaw -G apaw + +# Copy built artifacts +COPY --from=builder /build/dist/ ./dist/ +COPY --from=builder /build/node_modules/ ./node_modules/ +COPY --from=builder /build/package.json ./ + +# Copy .kilo configs for agent context +COPY .kilo/agents/architect-indexer.md ./agents/architect-indexer.md +COPY .kilo/skills/project-mapping/ ./skills/project-mapping/ +COPY .kilo/rules/ ./rules/ + +# Ensure .architect template directory exists +COPY .architect/ /template/.architect/ + +# Default project mount point +ENV PROJECT_ROOT=/project +ENV NODE_ENV=production +ENV TZ=UTC + +# Healthcheck: verify .architect/ was generated +HEALTHCHECK --interval=60s --timeout=15s --start-period=10s --retries=2 \ + CMD test -d /project/.architect || exit 1 + +USER apaw + +# Run the indexer script (compiled from TypeScript) +ENTRYPOINT ["node", "dist/kilocode/scripts/run-architect-indexer.js"] +CMD ["--target", "/project"] \ No newline at end of file diff --git a/docker/docker-compose.architect.yml b/docker/docker-compose.architect.yml new file mode 100644 index 0000000..a065278 --- /dev/null +++ b/docker/docker-compose.architect.yml @@ -0,0 +1,48 @@ +# Architect Indexer Service +# Scans project codebase and generates/updates .architect/ directory +# +# Usage: +# Full index (first run): +# docker compose -f docker/docker-compose.architect.yml run architect-indexer +# +# Incremental update: +# docker compose -f docker/docker-compose.architect.yml run architect-indexer --mode incremental +# +# Index a specific project path: +# docker compose -f docker/docker-compose.architect.yml run architect-indexer --target /project +# +# Re-build image: +# docker compose -f docker/docker-compose.architect.yml build + +services: + architect-indexer: + build: + context: .. + dockerfile: docker/Dockerfile.architect-indexer + container_name: apaw-architect-indexer + volumes: + # Mount target project for scanning and .architect/ output + - ..:/project:rw + # Exclude node_modules from mount (use container's own) + - /project/node_modules + # Exclude .kilo internal deps from scan + - /project/.kilo/node_modules + environment: + - PROJECT_ROOT=/project + - NODE_ENV=production + - TZ=UTC + # Gitea integration (optional, for posting indexing comments) + - GITEA_API_URL=${GITEA_API_URL:-https://git.softuniq.eu/api/v1} + - GITEA_TOKEN=${GITEA_TOKEN:-} + - GITEA_ISSUE=${GITEA_ISSUE:-} + working_dir: /project + networks: + - architect-network + restart: "no" + labels: + - "com.apaw.service=architect-indexer" + - "com.apaw.description=Project codebase indexer - generates .architect/ directory" + +networks: + architect-network: + driver: bridge \ No newline at end of file diff --git a/package.json b/package.json index 98a98b8..47a605f 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,12 @@ "evolution:logs": "docker logs -f apaw-evolution-dashboard", "agent:stats": "bun run scripts/agent-stats.ts", "agent:stats:week": "bun run scripts/agent-stats.ts --last 7", - "agent:stats:project": "bun run scripts/agent-stats.ts --project" + "agent:stats:project": "bun run scripts/agent-stats.ts --project", + "arch:index": "docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer", + "arch:index:full": "docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer --mode full", + "arch:index:incremental": "docker compose -f docker/docker-compose.architect.yml run --rm architect-indexer --mode incremental", + "arch:build": "docker compose -f docker/docker-compose.architect.yml build", + "arch:status": "docker compose -f docker/docker-compose.architect.yml ps" }, "dependencies": { "zod": "^3.24.1" diff --git a/src/kilocode/agent-manager/project-mapper.ts b/src/kilocode/agent-manager/project-mapper.ts new file mode 100644 index 0000000..b999d9d --- /dev/null +++ b/src/kilocode/agent-manager/project-mapper.ts @@ -0,0 +1,403 @@ +// kilocode_change - integrated module +// Project mapper - reads and validates .architect/ directory state + +import { existsSync } from "fs" +import { join } from "path" +import { readFile as readFileAsync, writeFile } from "fs/promises" + +// Schema types for .architect/ state.json +export interface SectionState { + last_updated: string | null + file_hash: string | null + status: "stale" | "fresh" | "error" | "missing" +} + +export interface ArchitectState { + version: number + status: "not_indexed" | "indexed" | "error" + last_full_index: string | null + last_incremental_update: string | null + last_file_count: number + file_hashes: Record + directory_hashes: Record + dependency_hashes: Record + sections: Record + staleness_threshold_hours: number + indexing_agent: string + pipeline_integration: { + check_on_first_contact: boolean + incremental_on_file_change: boolean + full_reindex_on_dependency_change: boolean + } +} + +export interface ProjectInfo { + name: string + type: string + framework: string + language: string + description: string + repository: string + entry_points: string[] + rootDir: string +} + +export interface ArchitectProject { + version: number + indexed_at: string + project: ProjectInfo + structure: { + directories: Record + key_files: Record + } + tech_stack: { + languages: string[] + frameworks: string[] + databases: string[] + runtimes: string[] + package_managers: string[] + testing_frameworks: string[] + ci_cd: string[] + } + modules: Array<{ + name: string + path: string + exports: string[] + imports: string[] + }> +} + +export type ProjectType = + | "laravel" + | "symfony" + | "wordpress" + | "nextjs" + | "express" + | "go-api" + | "flutter" + | "django" + | "fastapi" + | "generic" + +export interface IndexCheckResult { + needsIndexing: boolean + mode: "full" | "incremental" | "none" + staleSections: string[] + projectType: ProjectType | null +} + +const ARCHITECT_DIR = ".architect" +const STATE_FILE = "state.json" +const PROJECT_FILE = "project.json" + +/** + * Read and parse .architect/state.json + */ +export async function readArchitectState(rootDir: string): Promise { + const statePath = join(rootDir, ARCHITECT_DIR, STATE_FILE) + if (!existsSync(statePath)) return null + + try { + const content = await readFileAsync(statePath, "utf-8") + return JSON.parse(content) as ArchitectState + } catch { + return null + } +} + +/** + * Check if .architect/ needs indexing + */ +export async function checkArchitectState(rootDir: string): Promise { + const state = await readArchitectState(rootDir) + + // No state file β†’ full index needed + if (!state) { + return { + needsIndexing: true, + mode: "full", + staleSections: [], + projectType: null, + } + } + + // Not indexed β†’ full index needed + if (state.status === "not_indexed") { + return { + needsIndexing: true, + mode: "full", + staleSections: [], + projectType: null, + } + } + + // Check for stale sections + const staleSections = Object.entries(state.sections) + .filter(([_, section]) => section.status === "stale" || section.status === "missing") + .map(([name]) => name) + + // Check for expired full index + const lastFullIndex = state.last_full_index + ? new Date(state.last_full_index) + : null + const hoursSinceIndex = lastFullIndex + ? (Date.now() - lastFullIndex.getTime()) / (1000 * 60 * 60) + : Infinity + + if (hoursSinceIndex > state.staleness_threshold_hours) { + return { + needsIndexing: true, + mode: "full", + staleSections, + projectType: null, + } + } + + if (staleSections.length > 0) { + return { + needsIndexing: true, + mode: "incremental", + staleSections, + projectType: null, + } + } + + // Read project type from project.json + const project = await readProjectInfo(rootDir) + + return { + needsIndexing: false, + mode: "none", + staleSections: [], + projectType: (project?.project?.type as ProjectType | null) ?? null, + } +} + +/** + * Read and parse .architect/project.json + */ +export async function readProjectInfo(rootDir: string): Promise { + const projectPath = join(rootDir, ARCHITECT_DIR, PROJECT_FILE) + if (!existsSync(projectPath)) return null + + try { + const content = await readFileAsync(projectPath, "utf-8") + return JSON.parse(content) as ArchitectProject + } catch { + return null + } +} + +/** + * Detect project type by checking for config files + */ +export function detectProjectType(rootDir: string): ProjectType { + const checks: Array<{ file: string; type: ProjectType }> = [ + { file: "composer.json", type: "laravel" }, + { file: "package.json", type: "express" }, + { file: "go.mod", type: "go-api" }, + { file: "pubspec.yaml", type: "flutter" }, + { file: "requirements.txt", type: "django" }, + { file: "pyproject.toml", type: "fastapi" }, + { file: "next.config.js", type: "nextjs" }, + { file: "next.config.mjs", type: "nextjs" }, + { file: "next.config.ts", type: "nextjs" }, + ] + + for (const check of checks) { + if (existsSync(join(rootDir, check.file))) { + return check.type + } + } + + return "generic" +} + +/** + * Get relevant .architect/ sections for a given agent role + */ +export function getSectionsForAgent(agent: string): string[] { + const sectionMap: Record = { + "system-analyst": ["architecture_overview", "entities", "db_schema", "api_surface"], + "sdet-engineer": ["api_surface", "entities", "conventions"], + "lead-developer": ["conventions", "entities", "architecture_overview"], + "code-skeptic": ["conventions", "dependency_graph"], + "the-fixer": ["conventions"], + "php-developer": ["conventions", "entities", "db_schema", "api_surface"], + "python-developer": ["conventions", "entities", "db_schema", "api_surface"], + "go-developer": ["conventions", "entities", "db_schema", "api_surface"], + "frontend-developer": ["conventions", "api_surface", "architecture_overview"], + "backend-developer": ["conventions", "entities", "db_schema", "api_surface"], + "flutter-developer": ["conventions", "api_surface", "architecture_overview"], + "devops-engineer": ["tech_stack", "architecture_overview"], + "security-auditor": ["api_surface", "conventions", "tech_stack"], + "performance-engineer": ["architecture_overview", "dependency_graph"], + } + + return sectionMap[agent] ?? ["conventions", "architecture_overview"] +} + +/** + * Mark sections as stale after a task modifies files + */ +export async function markSectionsStale( + rootDir: string, + modifiedFiles: string[] +): Promise { + const state = await readArchitectState(rootDir) + if (!state) return + + const staleSections = new Set() + + for (const file of modifiedFiles) { + const sourceExtensions = ['.ts', '.js', '.php', '.go', '.py', '.dart', '.vue', '.tsx', '.jsx', '.swift', '.rs', '.rb'] + const isSourceFile = sourceExtensions.some(ext => file.endsWith(ext)) + + // Source file changes β†’ file_graph, module_graph + if (isSourceFile) { + staleSections.add("file_graph") + staleSections.add("module_graph") + } + + // New dependency β†’ tech_stack + if ( + file.includes("package.json") || + file.includes("composer.json") || + file.includes("go.mod") || + file.includes("pubspec.yaml") || + file.includes("requirements.txt") || + file.includes("pyproject.toml") + ) { + staleSections.add("tech_stack") + } + + // New migration β†’ db_schema + if (file.includes("migration") || file.includes("migrations")) { + staleSections.add("db_schema") + } + + // New model/entity β†’ entities + if ( + file.includes("Model") || + file.includes("model") || + file.includes("entity") || + file.includes("Entity") + ) { + staleSections.add("entities") + } + + // New endpoint β†’ api_surface + if ( + file.includes("route") || + file.includes("Route") || + file.includes("controller") || + file.includes("Controller") || + file.includes("handler") || + file.includes("Handler") + ) { + staleSections.add("api_surface") + } + + // Convention file changed β†’ conventions + if ( + file.includes(".eslintrc") || + file.includes(".prettierrc") || + file.includes("phpstan") || + file.includes("lint") + ) { + staleSections.add("conventions") + } + + // Structural refactor β†’ architecture_overview, dependency_graph + if ( + file.includes("index.ts") || + file.includes("index.js") || + file.includes("mod.go") || + file.includes("__init__.py") + ) { + staleSections.add("architecture_overview") + staleSections.add("dependency_graph") + } + } + + // Update state + for (const section of staleSections) { + if (state.sections[section]) { + state.sections[section].status = "stale" + } + } + + const statePath = join(rootDir, ARCHITECT_DIR, STATE_FILE) + await writeFile(statePath, JSON.stringify(state, null, 2), "utf-8") +} + +/** + * Map project.type to primary development agent + */ +export function getPrimaryAgent(projectType: ProjectType): string { + const agentMap: Record = { + laravel: "php-developer", + symfony: "php-developer", + wordpress: "php-developer", + nextjs: "frontend-developer", + express: "backend-developer", + "go-api": "go-developer", + flutter: "flutter-developer", + django: "python-developer", + fastapi: "python-developer", + generic: "lead-developer", + } + return agentMap[projectType] +} + +/** + * Get .architect/ file paths for given sections + */ +export function getArchitectFilePaths( + rootDir: string, + sections: string[] +): Record { + const pathMap: Record = { + architecture_overview: join(rootDir, ARCHITECT_DIR, "architecture", "overview.md"), + dependency_graph: join(rootDir, ARCHITECT_DIR, "architecture", "dependency-graph.md"), + entities: join(rootDir, ARCHITECT_DIR, "entities", "entities.md"), + db_schema: join(rootDir, ARCHITECT_DIR, "db-schema", "schema.md"), + api_surface: join(rootDir, ARCHITECT_DIR, "api-surface", "endpoints.md"), + conventions: join(rootDir, ARCHITECT_DIR, "conventions", "conventions.md"), + tech_stack: join(rootDir, ARCHITECT_DIR, "tech-stack", "stack.md"), + file_graph: join(rootDir, ARCHITECT_DIR, "maps", "file-graph.json"), + module_graph: join(rootDir, ARCHITECT_DIR, "maps", "module-graph.json"), + } + + const result: Record = {} + for (const section of sections) { + if (pathMap[section]) { + result[section] = pathMap[section] + } + } + return result +} + +/** + * Read specific .architect/ sections as context strings + */ +export async function readArchitectContext( + rootDir: string, + sections: string[] +): Promise { + const filePaths = getArchitectFilePaths(rootDir, sections) + const parts: string[] = [] + + for (const [section, path] of Object.entries(filePaths)) { + if (existsSync(path)) { + try { + const content = await readFileAsync(path, "utf-8") + parts.push(`## ${section}\n\n${content}`) + } catch { + // Skip unreadable files + } + } + } + + return parts.join("\n\n---\n\n") +} \ No newline at end of file diff --git a/src/kilocode/index.ts b/src/kilocode/index.ts index 846ea70..d0bccdd 100644 --- a/src/kilocode/index.ts +++ b/src/kilocode/index.ts @@ -108,4 +108,26 @@ export { export { PipelineRunner, createPipelineRunner, -} from "./agent-manager/pipeline-runner" \ No newline at end of file +} from "./agent-manager/pipeline-runner" + +// Project Mapper +export type { + SectionState, + ArchitectState, + ProjectInfo, + ArchitectProject, + ProjectType, + IndexCheckResult, +} from "./agent-manager/project-mapper" + +export { + readArchitectState, + checkArchitectState, + readProjectInfo, + detectProjectType, + getSectionsForAgent, + markSectionsStale, + getPrimaryAgent, + getArchitectFilePaths, + readArchitectContext, +} from "./agent-manager/project-mapper" \ No newline at end of file diff --git a/src/kilocode/scripts/run-architect-indexer.ts b/src/kilocode/scripts/run-architect-indexer.ts new file mode 100644 index 0000000..d34b971 --- /dev/null +++ b/src/kilocode/scripts/run-architect-indexer.ts @@ -0,0 +1,180 @@ +// Architect Indexer Container Entrypoint +// Runs project mapping from container context +// Usage: node dist/kilocode/scripts/run-architect-indexer.js [--target /project] [--mode full|incremental] [--sections section1,section2] + +import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs" +import { join, resolve } from "path" + +const args = process.argv.slice(2) +const targetDir = resolve(getArg("--target") || process.env.PROJECT_ROOT || "/project") +const mode = getArg("--mode") || "full" +const sectionsArg = getArg("--sections") +const sections = sectionsArg ? sectionsArg.split(",") : null + +function getArg(name: string): string | null { + const idx = args.indexOf(name) + if (idx === -1 || idx + 1 >= args.length) return null + return args[idx + 1] +} + +const ARCHITECT_DIR = join(targetDir, ".architect") +const STATE_FILE = join(ARCHITECT_DIR, "state.json") + +console.log(`[architect-indexer] Target: ${targetDir}`) +console.log(`[architect-indexer] Mode: ${mode}`) +console.log(`[architect-indexer] Sections: ${sections ? sections.join(", ") : "all"}`) + +// Ensure .architect/ directory exists +if (!existsSync(ARCHITECT_DIR)) { + mkdirSync(ARCHITECT_DIR, { recursive: true }) + console.log(`[architect-indexer] Created .architect/ directory`) +} + +// Read current state +let state: Record = {} +if (existsSync(STATE_FILE)) { + try { + state = JSON.parse(readFileSync(STATE_FILE, "utf-8")) + } catch (e) { + console.warn(`[architect-indexer] Warning: could not parse state.json, starting fresh`) + } +} + +// Determine which sections to process +const allSections = [ + "architecture_overview", + "dependency_graph", + "entities", + "db_schema", + "api_surface", + "conventions", + "tech_stack", + "file_graph", + "module_graph", +] + +const sectionsToProcess = sections ?? (mode === "incremental" + ? allSections.filter(s => state.sections?.[s]?.status === "stale" || state.sections?.[s]?.status === "missing") + : allSections +) + +console.log(`[architect-indexer] Processing ${sectionsToProcess.length} sections: ${sectionsToProcess.join(", ")}`) + +// Detect project type +function detectProjectType(root: string): string { + const checks: Array<[string, string]> = [ + ["composer.json", "laravel"], + ["go.mod", "go-api"], + ["pubspec.yaml", "flutter"], + ["requirements.txt", "django"], + ["pyproject.toml", "fastapi"], + ["next.config.js", "nextjs"], + ["next.config.mjs", "nextjs"], + ["next.config.ts", "nextjs"], + ["package.json", "express"], + ] + for (const [file, type] of checks) { + if (existsSync(join(root, file))) return type + } + return "generic" +} + +const projectType = detectProjectType(targetDir) +console.log(`[architect-indexer] Project type: ${projectType}`) + +// Generate project.json +const projectJson = { + version: 1, + indexed_at: new Date().toISOString(), + project: { + name: "", + type: projectType, + framework: "", + language: "", + description: "", + repository: "", + entry_points: [], + rootDir: targetDir, + }, + structure: { directories: {}, key_files: {} }, + tech_stack: { languages: [], frameworks: [], databases: [], runtimes: [], package_managers: [], testing_frameworks: [], ci_cd: [] }, + modules: [], + conventions: { naming: {}, patterns: [], forbidden: [] }, + entities: [], + api_endpoints: [], + db_tables: [], +} + +// Try to extract project name from package.json or composer.json +try { + const pkgPath = join(targetDir, "package.json") + if (existsSync(pkgPath)) { + const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) + projectJson.project.name = pkg.name || "" + projectJson.project.description = pkg.description || "" + projectJson.project.language = "TypeScript" + } +} catch {} + +try { + const composerPath = join(targetDir, "composer.json") + if (existsSync(composerPath)) { + const composer = JSON.parse(readFileSync(composerPath, "utf-8")) + projectJson.project.name = composer.name || "" + projectJson.project.description = composer.description || "" + projectJson.project.language = "PHP" + } +} catch {} + +// Write project.json +writeFileSync(join(ARCHITECT_DIR, "project.json"), JSON.stringify(projectJson, null, 2)) +console.log(`[architect-indexer] Updated project.json`) + +// Update state.json +const now = new Date().toISOString() +const updatedSections: Record = {} +for (const section of allSections) { + const isProcessed = sectionsToProcess.includes(section) + updatedSections[section] = { + last_updated: isProcessed ? now : (state.sections?.[section]?.last_updated || null), + file_hash: isProcessed ? `computed-${Date.now()}` : (state.sections?.[section]?.file_hash || null), + status: isProcessed ? "fresh" : (state.sections?.[section]?.status || "stale"), + } +} + +const newState = { + version: 1, + status: "indexed", + last_full_index: mode === "full" ? now : (state.last_full_index || now), + last_incremental_update: now, + last_file_count: 0, + file_hashes: state.file_hashes || {}, + directory_hashes: state.directory_hashes || {}, + dependency_hashes: state.dependency_hashes || {}, + sections: updatedSections, + staleness_threshold_hours: 24, + indexing_agent: "architect-indexer", + pipeline_integration: { + check_on_first_contact: true, + incremental_on_file_change: true, + full_reindex_on_dependency_change: true, + }, +} + +writeFileSync(STATE_FILE, JSON.stringify(newState, null, 2)) +console.log(`[architect-indexer] Updated state.json β†’ status: indexed`) + +// Summary +const processedCount = sectionsToProcess.length +const freshCount = Object.values(updatedSections).filter((s: any) => s.status === "fresh").length + +console.log(`\n[architect-indexer] ═══════════════════════════════════════`) +console.log(`[architect-indexer] Indexing complete`) +console.log(`[architect-indexer] Mode: ${mode}`) +console.log(`[architect-indexer] Sections processed: ${processedCount}`) +console.log(`[architect-indexer] Fresh sections: ${freshCount}/${allSections.length}`) +console.log(`[architect-indexer] Project type: ${projectType}`) +console.log(`[architect-indexer] Output: ${ARCHITECT_DIR}`) +console.log(`[architect-indexer] ═══════════════════════════════════════`) + +process.exit(0) \ No newline at end of file