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
This commit is contained in:
93
.architect/README.md
Normal file
93
.architect/README.md
Normal file
@@ -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` |
|
||||
49
.architect/api-surface/endpoints.md
Normal file
49
.architect/api-surface/endpoints.md
Normal file
@@ -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 <token>` | _pending_ |
|
||||
35
.architect/architecture/dependency-graph.md
Normal file
35
.architect/architecture/dependency-graph.md
Normal file
@@ -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
|
||||
53
.architect/architecture/overview.md
Normal file
53
.architect/architecture/overview.md
Normal file
@@ -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) │
|
||||
└──────────────────────────────────┘
|
||||
```
|
||||
68
.architect/conventions/conventions.md
Normal file
68
.architect/conventions/conventions.md
Normal file
@@ -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` |
|
||||
45
.architect/db-schema/schema.md
Normal file
45
.architect/db-schema/schema.md
Normal file
@@ -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_ |
|
||||
47
.architect/entities/entities.md
Normal file
47
.architect/entities/entities.md
Normal file
@@ -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_ |
|
||||
42
.architect/project.json
Normal file
42
.architect/project.json
Normal file
@@ -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": []
|
||||
}
|
||||
71
.architect/state.json
Normal file
71
.architect/state.json
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
57
.architect/tech-stack/stack.md
Normal file
57
.architect/tech-stack/stack.md
Normal file
@@ -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_ |
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -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
|
||||
.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/
|
||||
159
.kilo/agents/architect-indexer.md
Normal file
159
.kilo/agents/architect-indexer.md
Normal file
@@ -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 `<gitea-commenting required="true" />` 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)
|
||||
@@ -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 |
|
||||
|
||||
@@ -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:
|
||||
|
||||
239
.kilo/commands/index-project.md
Normal file
239
.kilo/commands/index-project.md
Normal file
@@ -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: <ISO timestamp>`
|
||||
- `last_incremental_update: <ISO timestamp>`
|
||||
- `last_file_count: <total files>`
|
||||
- `file_hashes: { <path>: <hash> }`
|
||||
- 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 |
|
||||
|
||||
<gitea-commenting required="true" />
|
||||
@@ -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:
|
||||
|
||||
97
.kilo/rules/architect-first-contact.md
Normal file
97
.kilo/rules/architect-first-contact.md
Normal file
@@ -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.
|
||||
203
.kilo/skills/project-mapping/SKILL.md
Normal file
203
.kilo/skills/project-mapping/SKILL.md
Normal file
@@ -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
|
||||
68
AGENTS.md
68
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 <url>` | Visual regression testing in Docker | `/web-test https://bbox.wtf` |
|
||||
| `/e2e-test <url>` | 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
|
||||
|
||||
38
STRUCTURE.md
38
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:
|
||||
|
||||
63
docker/Dockerfile.architect-indexer
Normal file
63
docker/Dockerfile.architect-indexer
Normal file
@@ -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"]
|
||||
48
docker/docker-compose.architect.yml
Normal file
48
docker/docker-compose.architect.yml
Normal file
@@ -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
|
||||
@@ -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"
|
||||
|
||||
403
src/kilocode/agent-manager/project-mapper.ts
Normal file
403
src/kilocode/agent-manager/project-mapper.ts
Normal file
@@ -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<string, string>
|
||||
directory_hashes: Record<string, string>
|
||||
dependency_hashes: Record<string, string | null>
|
||||
sections: Record<string, SectionState>
|
||||
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<string, string>
|
||||
key_files: Record<string, string>
|
||||
}
|
||||
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<ArchitectState | null> {
|
||||
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<IndexCheckResult> {
|
||||
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<ArchitectProject | null> {
|
||||
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<string, string[]> = {
|
||||
"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<void> {
|
||||
const state = await readArchitectState(rootDir)
|
||||
if (!state) return
|
||||
|
||||
const staleSections = new Set<string>()
|
||||
|
||||
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<ProjectType, string> = {
|
||||
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<string, string> {
|
||||
const pathMap: Record<string, string> = {
|
||||
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<string, string> = {}
|
||||
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<string> {
|
||||
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")
|
||||
}
|
||||
@@ -108,4 +108,26 @@ export {
|
||||
export {
|
||||
PipelineRunner,
|
||||
createPipelineRunner,
|
||||
} from "./agent-manager/pipeline-runner"
|
||||
} 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"
|
||||
180
src/kilocode/scripts/run-architect-indexer.ts
Normal file
180
src/kilocode/scripts/run-architect-indexer.ts
Normal file
@@ -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<string, any> = {}
|
||||
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<string, any> = {}
|
||||
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)
|
||||
Reference in New Issue
Block a user