mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-21 19:08:17 +08:00
Remove IDAW run and status commands, consolidating functionality and streamlining task execution and progress tracking.
This commit is contained in:
@@ -1,359 +0,0 @@
|
||||
---
|
||||
name: auto
|
||||
description: Chain command - automated document-driven development flow. Detects project state and runs the appropriate chain for new or existing projects.
|
||||
argument-hint: "[-y|--yes] [--skip-spec] [--skip-build] [--spec <session-id>] [--resume] \"project idea or task description\""
|
||||
allowed-tools: TodoWrite(*), Agent(*), AskUserQuestion(*), Read(*), Grep(*), Glob(*), Bash(*), Edit(*), Write(*)
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: All sub-commands run in auto mode. Minimal human intervention.
|
||||
|
||||
# DDD Auto Command (/ddd:auto)
|
||||
|
||||
## Purpose
|
||||
|
||||
Orchestrate the full document-driven development lifecycle. **Adapts to project state** — works for both new projects and existing codebases.
|
||||
|
||||
## Flow Variants
|
||||
|
||||
### Variant 1: New Project (no code, no spec)
|
||||
```
|
||||
spec-generator → ddd:index-build → ddd:plan → ddd:execute → verify → ddd:sync
|
||||
```
|
||||
|
||||
### Variant 2: Existing Project (has code, no spec)
|
||||
```
|
||||
ddd:scan → ddd:plan → ddd:execute → verify → ddd:sync
|
||||
```
|
||||
|
||||
### Variant 3: Existing Project with Spec (has code + spec)
|
||||
```
|
||||
ddd:index-build → ddd:plan → ddd:execute → verify → ddd:sync
|
||||
```
|
||||
|
||||
### Variant 4: Index Exists (has doc-index.json)
|
||||
```
|
||||
ddd:plan → ddd:execute → verify → ddd:sync
|
||||
```
|
||||
|
||||
## Flow Diagram
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ /ddd:auto │
|
||||
│ │
|
||||
│ Stage 0: Detect Project State │
|
||||
│ ┌───────────────────────────────────┐ │
|
||||
│ │ has_codebase? has_spec? has_index?│ │
|
||||
│ └────────────┬──────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────┼──────────────┐ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ No Code Code Only Code + Spec Index Exists │
|
||||
│ │ │ │ │ │
|
||||
│ ▼ │ │ │ │
|
||||
│ Stage 1 │ │ │ │
|
||||
│ Spec Gen │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ ▼ │ ▼ │ │
|
||||
│ Stage 2a Stage 2b Stage 2a │ │
|
||||
│ index-build ddd:scan index-build │ │
|
||||
│ (Path A or Path B auto-detected) │ │
|
||||
│ │ │ │
|
||||
│ └───────────────────┬───────────────────┘ │
|
||||
│ ▼ │
|
||||
│ Stage 3: DDD Plan (enhanced) │
|
||||
│ (doc-index query + exploration + │
|
||||
│ clarification + task planning) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ Stage 4: Execute │
|
||||
│ (ddd:execute = doc-aware execution) │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ Stage 4.5: Verify Gate │
|
||||
│ (convergence + build + lint + tests │
|
||||
│ → execution-manifest.json) │
|
||||
│ │ │
|
||||
│ PASS / WARN → continue │
|
||||
│ FAIL → ask user │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ Stage 5: Doc Sync │
|
||||
│ (auto-triggered with --from-manifest, │
|
||||
│ or manual /ddd:sync) │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Stage 0: Project State Detection
|
||||
|
||||
Automatically detect project state to determine which stages to run:
|
||||
|
||||
```
|
||||
Check 1: doc-index.json exists? → has_index
|
||||
Check 2: SPEC-* directories exist? → has_spec
|
||||
Check 3: Source code directories? → has_codebase
|
||||
Check 4: project-tech.json exists? → has_tech_analysis
|
||||
```
|
||||
|
||||
### Decision Matrix
|
||||
|
||||
| has_codebase | has_spec | has_index | Action |
|
||||
|:---:|:---:|:---:|--------|
|
||||
| No | No | No | Stage 1 (spec-gen) → Stage 2a (index-build) → Stage 3-5 |
|
||||
| No | Yes | No | Stage 2a (index-build) → Stage 3-5 |
|
||||
| Yes | No | No | **Stage 2b (ddd:scan)** → Stage 3-5 |
|
||||
| Yes | Yes | No | Stage 2a (index-build) → Stage 3-5 |
|
||||
| Yes | * | Yes | **Skip to Stage 3** (index exists) |
|
||||
|
||||
### Override Flags
|
||||
|
||||
| Flag | Effect |
|
||||
|------|--------|
|
||||
| `--skip-spec` | Never run spec-generator |
|
||||
| `--skip-build` | Never run index-build |
|
||||
| `--spec <id>` | Use specific spec session, force Path A |
|
||||
| `--from-scratch` | Rebuild index even if exists |
|
||||
|
||||
## Stage 1: Specification (conditional)
|
||||
|
||||
### Run When
|
||||
- No codebase AND no spec AND `--skip-spec` not set
|
||||
- User provides a new project idea (not an existing task description)
|
||||
|
||||
### Skip When
|
||||
- `--skip-spec` flag
|
||||
- Codebase already exists (existing project)
|
||||
- `--spec <id>` pointing to existing session
|
||||
|
||||
### Execution
|
||||
```
|
||||
Invoke /spec-generator with user input
|
||||
→ Output: .workflow/.doc-index/specs/SPEC-{slug}-{date}/
|
||||
```
|
||||
|
||||
## Stage 2: Index Construction (conditional)
|
||||
|
||||
### Run When
|
||||
- `doc-index.json` does not exist
|
||||
- OR `--from-scratch` flag
|
||||
|
||||
### Route Selection
|
||||
|
||||
```
|
||||
Has spec outputs → Stage 2a: /ddd:index-build (spec-first)
|
||||
No spec, has code → Stage 2b: /ddd:scan (code-first)
|
||||
```
|
||||
|
||||
### Stage 2a: /ddd:index-build (has spec)
|
||||
```
|
||||
Invoke /ddd:index-build [-y] [-s <spec-id>]
|
||||
→ Output: doc-index.json from spec entities + code mapping
|
||||
```
|
||||
|
||||
### Stage 2b: /ddd:scan (no spec, has code)
|
||||
```
|
||||
Invoke /ddd:scan [-y]
|
||||
→ Output: doc-index.json from code analysis + inferred features
|
||||
```
|
||||
|
||||
### Skip When
|
||||
- `--skip-build` flag
|
||||
- `doc-index.json` exists AND NOT `--from-scratch`
|
||||
- In this case, suggest `/ddd:update` for incremental refresh
|
||||
|
||||
## Stage 3: Planning (always runs)
|
||||
|
||||
### Execution
|
||||
|
||||
```
|
||||
Invoke /ddd:plan [-y] "task description"
|
||||
```
|
||||
|
||||
The enhanced `/ddd:plan` now performs:
|
||||
1. Doc-index query (instant context from features, requirements, components, ADRs)
|
||||
2. Doc-index-guided exploration (1-4 angles based on affected features)
|
||||
3. Clarification (aggregate ambiguities from exploration + doc-index gaps)
|
||||
4. Task planning (plan.json + TASK-*.json with doc_context traceability)
|
||||
5. Handoff selection
|
||||
|
||||
Output:
|
||||
- `plan.json` — plan overview with doc_context
|
||||
- `.task/TASK-*.json` — individual tasks with doc_context
|
||||
- `exploration-{angle}.json` — exploration results (if Phase 2 ran)
|
||||
- `planning-context.md` — legacy context package
|
||||
|
||||
### Handoff Decision
|
||||
|
||||
After planning, `/ddd:plan` presents execution options:
|
||||
|
||||
| Option | Description | Auto-Select When |
|
||||
|--------|-------------|-----------------|
|
||||
| **ddd:execute** | Document-aware execution (recommended) | Default in ddd workflow |
|
||||
| **lite-execute** | Standard execution (no doc awareness) | When doc traceability not needed |
|
||||
| **direct** | Start coding with context | User prefers manual |
|
||||
| **stop** | Just the plan context | Planning/research only |
|
||||
|
||||
With `-y`: Auto-select `ddd:execute`.
|
||||
|
||||
## Stage 4: Execution
|
||||
|
||||
Based on Stage 3 handoff decision:
|
||||
|
||||
| Mode | Delegates To |
|
||||
|------|-------------|
|
||||
| **ddd:execute** | `/ddd:execute --in-memory` with plan.json + doc-index enrichment |
|
||||
| lite-execute | `/workflow:lite-execute` with plan.json path |
|
||||
| direct | Output context package, developer works manually |
|
||||
| stop | End here, no execution |
|
||||
|
||||
### ddd:execute Features (when selected)
|
||||
- Doc-enriched task prompts (feature context + component docs + ADR constraints)
|
||||
- Per-batch impact verification (changes stay within planned scope)
|
||||
- Result persistence (`TASK-*.result.json` per task, `execution-manifest.json` per session)
|
||||
- Post-execution verify gate (Stage 4.5, unless `--skip-verify`)
|
||||
- Post-completion auto-sync with manifest (Stage 5 triggered automatically)
|
||||
|
||||
**Note**: When using `ddd:execute`, Stage 4.5 and Stage 5 are auto-triggered. For other modes, run Stage 5 manually.
|
||||
|
||||
## Stage 4.5: Verify Gate
|
||||
|
||||
Embedded within `ddd:execute` (Step 4.5). Runs after all batches complete, before doc sync.
|
||||
|
||||
### Purpose
|
||||
|
||||
Quality gate ensuring execution output is correct before committing to documentation updates. Prevents bad code from being "blessed" into the doc-index.
|
||||
|
||||
### Checks Performed
|
||||
|
||||
| Check | Description | Gate Behavior |
|
||||
|-------|-------------|---------------|
|
||||
| **Convergence** | Run `task.convergence.verification` for each task | FAIL if any critical task fails |
|
||||
| **Build** | Run project build command (`tsc --noEmit`, etc.) | FAIL on build errors |
|
||||
| **Lint** | Run project linter (`eslint`, etc.) | WARN only (non-blocking) |
|
||||
| **Regression** | Run full test suite, compare to baseline | FAIL on new test failures |
|
||||
|
||||
### Gate Results
|
||||
|
||||
| Result | Action |
|
||||
|--------|--------|
|
||||
| **PASS** | All checks passed → proceed to Stage 5 |
|
||||
| **WARN** | Non-critical issues (lint warnings) → proceed with warnings logged |
|
||||
| **FAIL** | Critical issues → ask user: fix now / skip sync / abort |
|
||||
| **FAIL + `-y`** | Log failures, set `error_state` in session, stop |
|
||||
|
||||
### Output
|
||||
|
||||
- `execution-manifest.json` — persisted to session folder, consumed by Stage 5
|
||||
- Contains: task results, files_modified (with task attribution), verify gate results
|
||||
|
||||
## Stage 5: Post-Task Sync
|
||||
|
||||
### Trigger
|
||||
- **Auto**: `/ddd:execute` triggers `/ddd:sync --from-manifest` automatically after verify gate passes
|
||||
- **Manual**: User runs `/ddd:sync` after completing work in direct/lite-execute mode
|
||||
- **Resume**: `/ddd:auto --resume` after task completion
|
||||
|
||||
### Execution
|
||||
```
|
||||
# Auto mode (from ddd:execute): uses manifest for precise change tracking
|
||||
Invoke /ddd:sync [-y] --task-id <id> --from-manifest {session}/execution-manifest.json "task summary"
|
||||
|
||||
# Manual mode (from direct/lite-execute): falls back to git diff
|
||||
Invoke /ddd:sync [-y] [--task-id <id>] "task summary"
|
||||
|
||||
→ Updates: doc-index.json, feature-maps/, tech-registry/, action-logs/
|
||||
```
|
||||
|
||||
## State Tracking
|
||||
|
||||
### Session File: `.workflow/.doc-index/.auto-session.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "DAUTO-{timestamp}",
|
||||
"input": "user's original input",
|
||||
"detected_state": {
|
||||
"has_codebase": true,
|
||||
"has_spec": false,
|
||||
"has_index": false,
|
||||
"build_path": "code-first"
|
||||
},
|
||||
"stages_completed": ["detect", "index-build", "plan"],
|
||||
"current_stage": "execute",
|
||||
"spec_session": "SPEC-{slug}-{date}|null",
|
||||
"plan_session": "planning/{task-slug}-{date}/",
|
||||
"plan_context": "planning/{task-slug}-{date}/plan.json",
|
||||
"execution_mode": "ddd:execute|lite-execute|direct|stop",
|
||||
"execution_manifest": "planning/{task-slug}-{date}/execution-manifest.json|null",
|
||||
"verify_gate": "PASS|WARN|FAIL|null",
|
||||
"error_state": null,
|
||||
"last_error": {
|
||||
"stage": "execute",
|
||||
"message": "Task TASK-002 failed: compilation error",
|
||||
"timestamp": "ISO8601",
|
||||
"recoverable": true
|
||||
},
|
||||
"created_at": "ISO8601",
|
||||
"last_updated": "ISO8601"
|
||||
}
|
||||
```
|
||||
|
||||
### Resume
|
||||
```
|
||||
/ddd:auto --resume → Resume from current_stage in .auto-session.json
|
||||
```
|
||||
|
||||
### Error Recovery
|
||||
```
|
||||
/ddd:auto --resume
|
||||
IF error_state is set:
|
||||
Display last error context
|
||||
Ask: retry current stage / skip to next / abort
|
||||
ELSE:
|
||||
Resume from current_stage normally
|
||||
```
|
||||
|
||||
## Example Workflows
|
||||
|
||||
### New Project (Full Flow)
|
||||
```
|
||||
/ddd:auto "Build a task management API with user auth and team features"
|
||||
→ Stage 0: No code, no spec → need spec-gen
|
||||
→ Stage 1: spec-generator produces full spec
|
||||
→ Stage 2: index-build creates index from spec + empty codebase
|
||||
→ Stage 3: ddd:plan produces plan.json + TASK-*.json with doc_context
|
||||
→ Stage 4: ddd:execute runs tasks with feature context enrichment
|
||||
→ Stage 4.5: verify gate — convergence ✓, build ✓, tests ✓ → PASS
|
||||
→ Stage 5: ddd:sync --from-manifest auto-triggered, updates index
|
||||
```
|
||||
|
||||
### Existing Project, No Spec (Code-First)
|
||||
```
|
||||
/ddd:auto "Add rate limiting to API endpoints"
|
||||
→ Stage 0: Has code, no spec, no index
|
||||
→ Stage 2b: ddd:scan analyzes code, infers features from codebase
|
||||
→ Stage 3: ddd:plan queries index, explores with security + patterns angles
|
||||
→ Stage 4: ddd:execute runs with rate-limit component docs as context
|
||||
→ Stage 4.5: verify gate — convergence ✓, tests 41/42 (1 regression) → WARN
|
||||
→ Stage 5: ddd:sync --from-manifest, registers new rate-limit component
|
||||
```
|
||||
|
||||
### Existing Project with Index (Incremental)
|
||||
```
|
||||
/ddd:auto "Fix auth token expiration bug"
|
||||
→ Stage 0: Has code, has index → skip to plan
|
||||
→ Stage 3: ddd:plan finds feat-auth, REQ-002, tech-auth-service (Low complexity, skip exploration)
|
||||
→ Stage 4: ddd:execute runs single task with auth feature context
|
||||
→ Stage 4.5: verify gate — convergence ✓, build ✓, tests ✓ → PASS
|
||||
→ Stage 5: ddd:sync --from-manifest, updates tech-auth-service code locations
|
||||
```
|
||||
|
||||
### Planning Only
|
||||
```
|
||||
/ddd:auto "Investigate payment module architecture"
|
||||
→ Stage 0-2: (as needed)
|
||||
→ Stage 3: ddd:plan shows full context with exploration results
|
||||
→ Stage 4: user selects "stop" → gets plan.json + context package only
|
||||
```
|
||||
@@ -1,222 +0,0 @@
|
||||
---
|
||||
name: doc-generate
|
||||
description: Generate full document tree from doc-index.json. Layer 3 (components) → Layer 2 (features) → Layer 1 (indexes/overview). Standalone or called by scan/index-build.
|
||||
argument-hint: "[-y|--yes] [--layer <3|2|1|all>] [--force] [--skip-overview] [--skip-schema]"
|
||||
allowed-tools: TodoWrite(*), Agent(*), AskUserQuestion(*), Read(*), Grep(*), Glob(*), Bash(*), Edit(*), Write(*)
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Auto-generate all layers without confirmation prompts.
|
||||
|
||||
# DDD Doc Generate Command (/ddd:doc-generate)
|
||||
|
||||
## Purpose
|
||||
|
||||
Generate the complete document tree from `doc-index.json`. **Single source of truth** for all document generation logic, following bottom-up Layer 3 → 2 → 1 strategy.
|
||||
|
||||
```
|
||||
doc-index.json → tech-registry/*.md (L3) → feature-maps/*.md (L2) → _index.md + README + ARCHITECTURE (L1)
|
||||
```
|
||||
|
||||
## When to Use
|
||||
|
||||
| Scenario | Use |
|
||||
|----------|-----|
|
||||
| After `/ddd:scan` builds doc-index.json | **doc-generate** (auto-called by scan) |
|
||||
| After `/ddd:index-build` builds doc-index.json | **doc-generate** (auto-called by index-build) |
|
||||
| Rebuild all docs from existing index | **doc-generate --force** |
|
||||
| Regenerate only component docs | **doc-generate --layer 3** |
|
||||
| Regenerate only overview/index docs | **doc-generate --layer 1** |
|
||||
|
||||
## Prerequisite
|
||||
|
||||
- `doc-index.json` must exist at `.workflow/.doc-index/doc-index.json`
|
||||
- If not found → error: "No doc-index.json found. Run /ddd:scan or /ddd:index-build first."
|
||||
|
||||
## Storage Output
|
||||
|
||||
```
|
||||
.workflow/.doc-index/
|
||||
├── doc-index.json ← Input (read-only, not modified)
|
||||
├── SCHEMA.md ← Schema documentation
|
||||
├── README.md ← Project overview (Layer 1)
|
||||
├── ARCHITECTURE.md ← Architecture overview (Layer 1)
|
||||
├── feature-maps/ ← Feature documentation (Layer 2)
|
||||
│ ├── _index.md
|
||||
│ └── {feature-slug}.md
|
||||
├── tech-registry/ ← Component documentation (Layer 3)
|
||||
│ ├── _index.md
|
||||
│ └── {component-slug}.md
|
||||
└── planning/ ← Planning sessions (Layer 1)
|
||||
├── _index.md ← Planning sessions index
|
||||
└── {task-slug}-{date}/ ← Individual session folders
|
||||
```
|
||||
|
||||
## Phase 1: Load & Validate
|
||||
|
||||
### 1.1 Load doc-index.json
|
||||
|
||||
```
|
||||
Read .workflow/.doc-index/doc-index.json
|
||||
Validate: features[], technicalComponents[] are non-empty arrays
|
||||
```
|
||||
|
||||
### 1.2 Schema Version Check
|
||||
|
||||
```javascript
|
||||
const schemaVersion = docIndex.schema_version || '0.0';
|
||||
if (schemaVersion !== '1.0') {
|
||||
warn(`Schema version mismatch: found ${schemaVersion}, expected 1.0`);
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 Determine Generation Scope
|
||||
|
||||
```
|
||||
IF --layer 3: generate Layer 3 only
|
||||
IF --layer 2: generate Layer 2 only (requires Layer 3 exists)
|
||||
IF --layer 1: generate Layer 1 only (requires Layer 2 exists)
|
||||
IF --layer all (default): generate Layer 3 → 2 → 1
|
||||
```
|
||||
|
||||
### 1.4 Check Existing Docs
|
||||
|
||||
```
|
||||
IF docs already exist AND NOT --force:
|
||||
Warn: "Documents already exist. Use --force to overwrite."
|
||||
Ask user (unless -y → overwrite)
|
||||
```
|
||||
|
||||
## Phase 2: Layer 3 -- Component Documentation
|
||||
|
||||
For each component in `technicalComponents[]`, call the generate_ddd_docs endpoint:
|
||||
|
||||
```bash
|
||||
for COMPONENT_ID in "${technicalComponents[@]}"; do
|
||||
ccw tool exec generate_ddd_docs '{"strategy":"component","entityId":"'"$COMPONENT_ID"'","tool":"gemini"}'
|
||||
done
|
||||
```
|
||||
|
||||
The endpoint handles:
|
||||
- Loading the component entity from doc-index.json
|
||||
- Building YAML frontmatter (layer: 3, component_id, name, type, features, code_locations, generated_at)
|
||||
- Constructing the CLI prompt with code context paths
|
||||
- **Including Change History section**: Pull related entries from `doc-index.json.actions[]` where `affectedComponents` includes this component ID. Display as timeline (date, action type, description)
|
||||
- Writing output to `.workflow/.doc-index/tech-registry/{slug}.md`
|
||||
- Tool fallback (gemini -> qwen -> codex) on failure
|
||||
|
||||
Output: `.workflow/.doc-index/tech-registry/{component-slug}.md`
|
||||
|
||||
## Phase 3: Layer 2 -- Feature Documentation
|
||||
|
||||
For each feature in `features[]`, call the generate_ddd_docs endpoint:
|
||||
|
||||
```bash
|
||||
for FEATURE_ID in "${features[@]}"; do
|
||||
ccw tool exec generate_ddd_docs '{"strategy":"feature","entityId":"'"$FEATURE_ID"'","tool":"gemini"}'
|
||||
done
|
||||
```
|
||||
|
||||
The endpoint handles:
|
||||
- Loading the feature entity from doc-index.json
|
||||
- Building YAML frontmatter (layer: 2, feature_id, name, epic_id, status, requirements, components, tags, generated_at)
|
||||
- Constructing the CLI prompt referencing Layer 3 component docs
|
||||
- **Including Change History section**: Pull related entries from `doc-index.json.actions[]` where `affectedFeatures` includes this feature ID. Display as timeline (date, action type, description)
|
||||
- Writing output to `.workflow/.doc-index/feature-maps/{slug}.md`
|
||||
- Tool fallback (gemini -> qwen -> codex) on failure
|
||||
|
||||
Output: `.workflow/.doc-index/feature-maps/{feature-slug}.md`
|
||||
|
||||
## Phase 4: Layer 1 -- Index & Overview Documentation
|
||||
|
||||
### 4.1 Index Documents
|
||||
|
||||
Generate catalog files for each subdirectory:
|
||||
|
||||
```bash
|
||||
# Feature maps index
|
||||
ccw tool exec generate_ddd_docs '{"strategy":"index","entityId":"feature-maps","tool":"gemini"}'
|
||||
|
||||
# Tech registry index
|
||||
ccw tool exec generate_ddd_docs '{"strategy":"index","entityId":"tech-registry","tool":"gemini"}'
|
||||
|
||||
# Action logs index
|
||||
ccw tool exec generate_ddd_docs '{"strategy":"index","entityId":"action-logs","tool":"gemini"}'
|
||||
|
||||
# Planning sessions index
|
||||
ccw tool exec generate_ddd_docs '{"strategy":"index","entityId":"planning","tool":"gemini"}'
|
||||
```
|
||||
|
||||
Or generate all indexes at once (omit entityId):
|
||||
|
||||
```bash
|
||||
ccw tool exec generate_ddd_docs '{"strategy":"index","tool":"gemini"}'
|
||||
```
|
||||
|
||||
### 4.2 README.md (unless --skip-overview)
|
||||
|
||||
```bash
|
||||
ccw tool exec generate_ddd_docs '{"strategy":"overview","tool":"gemini"}'
|
||||
```
|
||||
|
||||
### 4.3 ARCHITECTURE.md (unless --skip-overview)
|
||||
|
||||
```bash
|
||||
ccw tool exec generate_ddd_docs '{"strategy":"overview","entityId":"architecture","tool":"gemini"}'
|
||||
```
|
||||
|
||||
## Phase 5: SCHEMA.md (unless --skip-schema)
|
||||
|
||||
### 5.1 Generate Schema Documentation
|
||||
|
||||
```bash
|
||||
ccw tool exec generate_ddd_docs '{"strategy":"schema","tool":"gemini"}'
|
||||
```
|
||||
|
||||
### 5.2 Versioning Policy
|
||||
|
||||
**Semantic Versioning**:
|
||||
- **Major** (X.0): Breaking changes (field removal, type changes, incompatible structure)
|
||||
- **Minor** (X.Y): Non-breaking additions (new optional fields, new sections)
|
||||
|
||||
**Migration Protocol**:
|
||||
1. Detect version mismatch in ddd:plan/ddd:sync
|
||||
2. Log warning with migration instructions
|
||||
3. Provide migration script or regeneration option
|
||||
4. Update schema_version after successful migration
|
||||
|
||||
## Phase 6: Generation Report
|
||||
|
||||
```
|
||||
Document Generation Report
|
||||
|
||||
Project: {name}
|
||||
Source: doc-index.json (build_path: {spec-first|code-first})
|
||||
|
||||
Generated:
|
||||
Layer 3 (Components): {N} documents in tech-registry/
|
||||
Layer 2 (Features): {N} documents in feature-maps/
|
||||
Layer 1 (Indexes): {N} documents (_index.md, README, ARCHITECTURE)
|
||||
Schema: SCHEMA.md
|
||||
|
||||
Total: {N} documents generated
|
||||
```
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Effect |
|
||||
|------|--------|
|
||||
| `-y, --yes` | Auto-confirm all decisions |
|
||||
| `--layer <3\|2\|1\|all>` | Generate specific layer only (default: all) |
|
||||
| `--force` | Overwrite existing documents |
|
||||
| `--skip-overview` | Skip README.md, ARCHITECTURE.md, planning/_index.md |
|
||||
| `--skip-schema` | Skip SCHEMA.md generation |
|
||||
|
||||
## Integration Points
|
||||
|
||||
- **Input from**: `doc-index.json` (from `/ddd:scan` or `/ddd:index-build`)
|
||||
- **Called by**: `/ddd:scan` (after index assembly), `/ddd:index-build` (after index assembly)
|
||||
- **Standalone**: Can be run independently on any project with existing doc-index.json
|
||||
- **Output**: Complete document tree in `.workflow/.doc-index/`
|
||||
- **Endpoint**: `ccw tool exec generate_ddd_docs` handles prompt construction, frontmatter, tool fallback, and file creation
|
||||
@@ -1,218 +0,0 @@
|
||||
---
|
||||
name: doc-refresh
|
||||
description: Incrementally update affected documents based on changed components/features. Layer 3 → 2 → 1 selective refresh. Called by sync/update or used standalone.
|
||||
argument-hint: "[-y|--yes] [--components <id1,id2,...>] [--features <id1,id2,...>] [--minimal] [--dry-run]"
|
||||
allowed-tools: TodoWrite(*), Agent(*), AskUserQuestion(*), Read(*), Grep(*), Glob(*), Bash(*), Edit(*), Write(*)
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Auto-update affected documents without confirmation prompts.
|
||||
|
||||
# DDD Doc Refresh Command (/ddd:doc-refresh)
|
||||
|
||||
## Purpose
|
||||
|
||||
Incrementally update **only the affected documents** after code changes. Unlike `/ddd:doc-generate` (full generation), this command selectively refreshes documents for specific components and features.
|
||||
|
||||
```
|
||||
affected component/feature IDs → update tech-registry (L3) → update feature-maps (L2) → update indexes (L1)
|
||||
```
|
||||
|
||||
## When to Use
|
||||
|
||||
| Scenario | Use |
|
||||
|----------|-----|
|
||||
| After `/ddd:sync` detects code changes | **doc-refresh** (auto-called by sync) |
|
||||
| After `/ddd:update` traces impact | **doc-refresh** (auto-called by update) |
|
||||
| Manual refresh of specific component docs | **doc-refresh --components tech-auth-service** |
|
||||
| Quick metadata-only update | **doc-refresh --minimal --components tech-auth-service** |
|
||||
| Preview what would change | **doc-refresh --dry-run --components tech-auth-service** |
|
||||
|
||||
## Prerequisite
|
||||
|
||||
- `doc-index.json` must exist at `.workflow/.doc-index/doc-index.json`
|
||||
- At least one of `--components` or `--features` must be provided (or received from caller)
|
||||
- Corresponding documents must already exist (generated by `/ddd:doc-generate`)
|
||||
- If documents missing → error: "Documents not found. Run /ddd:doc-generate first."
|
||||
|
||||
## Phase 1: Determine Refresh Scope
|
||||
|
||||
### 1.1 Resolve Affected Entities
|
||||
|
||||
```
|
||||
IF --components provided:
|
||||
affected_components = parse comma-separated IDs
|
||||
affected_features = lookup featureIds for each component in doc-index.json
|
||||
|
||||
IF --features provided:
|
||||
affected_features = parse comma-separated IDs
|
||||
affected_components = union of all component IDs linked to features
|
||||
|
||||
Merge both sets if both flags provided.
|
||||
```
|
||||
|
||||
### 1.2 Validate Entities Exist
|
||||
|
||||
For each ID, verify it exists in doc-index.json. Warn and skip missing IDs.
|
||||
|
||||
### 1.3 Check Document Existence
|
||||
|
||||
For each affected entity, check if corresponding .md file exists:
|
||||
- Missing Layer 3 doc → warn: "Component doc not found. Run /ddd:doc-generate first."
|
||||
- Missing Layer 2 doc → warn: "Feature doc not found. Run /ddd:doc-generate first."
|
||||
|
||||
## Phase 2: Layer 3 — Update Component Docs
|
||||
|
||||
For each affected component's `tech-registry/{slug}.md`:
|
||||
|
||||
```bash
|
||||
ccw cli -p "PURPOSE: Update component documentation for {component.name} after code changes
|
||||
TASK:
|
||||
• Update code locations and line ranges
|
||||
• Update symbol list (add new exports, remove deleted)
|
||||
• Add change entry to history table
|
||||
• Refresh usage examples if API changed
|
||||
MODE: write
|
||||
CONTEXT: @{component.codeLocations[].path}
|
||||
EXPECTED: Updated markdown with current API state
|
||||
CONSTRAINTS: Preserve existing structure | Only update changed sections
|
||||
" --tool gemini --mode write --cd .workflow/.doc-index/tech-registry/
|
||||
```
|
||||
|
||||
Update frontmatter:
|
||||
```markdown
|
||||
---
|
||||
layer: 3
|
||||
component_id: tech-auth-service
|
||||
generated_at: {original}
|
||||
last_updated: ISO8601
|
||||
---
|
||||
```
|
||||
|
||||
### 2.1 Minimal Mode (--minimal)
|
||||
|
||||
With `--minimal`, only update:
|
||||
- `codeLocations` in frontmatter
|
||||
- `last_updated` timestamp
|
||||
- Skip CLI call for content regeneration
|
||||
|
||||
## Phase 3: Layer 2 — Update Feature Docs
|
||||
|
||||
For each affected feature's `feature-maps/{slug}.md`:
|
||||
|
||||
```bash
|
||||
ccw cli -p "PURPOSE: Update feature documentation for {feature.name} after component changes
|
||||
TASK:
|
||||
• Update component list if new components added
|
||||
• Update status if requirements now fully implemented
|
||||
• Add change entry to history table
|
||||
• Refresh integration guide if component APIs changed
|
||||
MODE: write
|
||||
CONTEXT: @.workflow/.doc-index/tech-registry/{affected-components}.md
|
||||
EXPECTED: Updated markdown reflecting current feature state
|
||||
CONSTRAINTS: Reference updated Layer 3 docs | Preserve business language
|
||||
" --tool gemini --mode write --cd .workflow/.doc-index/feature-maps/
|
||||
```
|
||||
|
||||
Update frontmatter:
|
||||
```markdown
|
||||
---
|
||||
layer: 2
|
||||
feature_id: feat-auth
|
||||
depends_on_layer3: [tech-auth-service, tech-user-model]
|
||||
generated_at: {original}
|
||||
last_updated: ISO8601
|
||||
---
|
||||
```
|
||||
|
||||
### 3.1 Minimal Mode (--minimal)
|
||||
|
||||
With `--minimal`, only update:
|
||||
- Component list in frontmatter
|
||||
- Feature status (if requirements mapping changed)
|
||||
- `last_updated` timestamp
|
||||
- Skip CLI call for content regeneration
|
||||
|
||||
## Phase 4: Layer 1 — Update Index Docs (conditional)
|
||||
|
||||
### 4.1 Trigger Conditions
|
||||
|
||||
| Condition | Action |
|
||||
|-----------|--------|
|
||||
| New component registered | Update `tech-registry/_index.md` |
|
||||
| Component removed | Update `tech-registry/_index.md` |
|
||||
| Feature status changed | Update `feature-maps/_index.md` |
|
||||
| New feature added | Update `feature-maps/_index.md` + README.md |
|
||||
| Major architecture change | Update ARCHITECTURE.md |
|
||||
| New action-log entry | Update `action-logs/_index.md` |
|
||||
|
||||
### 4.2 Update _index.md Files
|
||||
|
||||
Refresh table entries for affected features/components:
|
||||
- `feature-maps/_index.md`
|
||||
- `tech-registry/_index.md`
|
||||
- `action-logs/_index.md` (if new action entry)
|
||||
|
||||
### 4.3 Update Overview Docs (only if significant)
|
||||
|
||||
If new features added or major status changes:
|
||||
|
||||
```bash
|
||||
ccw cli -p "PURPOSE: Update project overview docs after feature changes
|
||||
TASK:
|
||||
• Update README.md feature list
|
||||
• Update ARCHITECTURE.md if new components added
|
||||
• Update planning/_index.md with new planning sessions
|
||||
MODE: write
|
||||
CONTEXT: @.workflow/.doc-index/feature-maps/*.md @.workflow/.doc-index/doc-index.json
|
||||
EXPECTED: Updated overview docs with current project state
|
||||
CONSTRAINTS: High-level only | Link to Layer 2 for details
|
||||
" --tool gemini --mode write --cd .workflow/.doc-index/
|
||||
```
|
||||
|
||||
### 4.4 Skip Layer 1
|
||||
|
||||
With `--minimal` or when changes are minor (only code location updates): skip Layer 1 entirely.
|
||||
|
||||
## Phase 5: Refresh Report
|
||||
|
||||
```
|
||||
Document Refresh Report
|
||||
|
||||
Affected Scope:
|
||||
Components: {N} ({component IDs})
|
||||
Features: {N} ({feature IDs})
|
||||
|
||||
Updated:
|
||||
Layer 3 (Components): {N} documents refreshed
|
||||
Layer 2 (Features): {N} documents refreshed
|
||||
Layer 1 (Indexes): {N} documents refreshed (or "skipped")
|
||||
|
||||
Mode: {full|minimal}
|
||||
```
|
||||
|
||||
## Dry-Run Mode
|
||||
|
||||
With `--dry-run`:
|
||||
- Execute Phase 1 (scope determination)
|
||||
- Display what would be updated (affected files list)
|
||||
- Skip all file writes (Phase 2-4)
|
||||
- Output: "Dry-run complete. {N} documents would be refreshed."
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Effect |
|
||||
|------|--------|
|
||||
| `-y, --yes` | Auto-confirm all updates |
|
||||
| `--components <ids>` | Comma-separated component IDs to refresh |
|
||||
| `--features <ids>` | Comma-separated feature IDs to refresh |
|
||||
| `--minimal` | Only update metadata/frontmatter, skip content regeneration |
|
||||
| `--dry-run` | Preview what would change without modifying files |
|
||||
|
||||
## Integration Points
|
||||
|
||||
- **Input from**: `doc-index.json`, affected entity IDs (from `/ddd:sync` or `/ddd:update`)
|
||||
- **Called by**: `/ddd:sync` (after change detection + index update), `/ddd:update` (after impact tracing + index update)
|
||||
- **Standalone**: Can be run independently to refresh specific component/feature docs
|
||||
- **Prerequisite**: Documents must exist (generated by `/ddd:doc-generate`)
|
||||
@@ -1,416 +0,0 @@
|
||||
---
|
||||
name: execute
|
||||
description: Document-aware execution engine — executes plan.json + TASK-*.json with doc-index context enrichment, per-batch impact verification, and post-completion doc sync.
|
||||
argument-hint: "[-y|--yes] [--skip-sync] [--skip-verify] [--plan <path>] [--in-memory] \"optional task description\""
|
||||
allowed-tools: TodoWrite(*), Agent(*), AskUserQuestion(*), Read(*), Grep(*), Glob(*), Bash(*), Edit(*), Write(*), mcp__ace-tool__search_context(*)
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Auto-confirm all decisions, auto-sync after completion.
|
||||
|
||||
# DDD Execute Command (/ddd:execute)
|
||||
|
||||
## Purpose
|
||||
|
||||
Same execution engine model as lite-execute, but each step is **doc-index-aware**:
|
||||
- Tasks are enriched with feature context, component docs, and architecture constraints
|
||||
- Per-batch impact verification ensures changes stay within planned scope
|
||||
- Post-completion automatically syncs the document index
|
||||
|
||||
### Core Differentiator
|
||||
Unlike generic execution engines, ddd:execute leverages the document architecture:
|
||||
- Feature-maps provide business context for each task
|
||||
- Tech-registry provides implementation patterns to follow
|
||||
- ADRs surface as hard constraints during execution
|
||||
- Requirement acceptance criteria inform convergence verification
|
||||
|
||||
## Prerequisite
|
||||
|
||||
- `plan.json` + `.task/TASK-*.json` files from `/ddd:plan`
|
||||
- `doc-index.json` at `.workflow/.doc-index/doc-index.json`
|
||||
- If `--in-memory`: receives executionContext from `/ddd:plan` handoff
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Initialize & Load Context
|
||||
|
||||
### 1.1 Locate Plan
|
||||
|
||||
```
|
||||
IF --in-memory:
|
||||
Load executionContext from ddd:plan handoff
|
||||
plan_path = executionContext.plan_path
|
||||
task_dir = executionContext.task_dir
|
||||
ELIF --plan <path>:
|
||||
plan_path = <path>
|
||||
task_dir = {dirname(path)}/.task/
|
||||
ELSE:
|
||||
Scan .workflow/.doc-index/planning/ for most recent session
|
||||
plan_path = {latest_session}/plan.json
|
||||
task_dir = {latest_session}/.task/
|
||||
```
|
||||
|
||||
### 1.2 Load Plan & Tasks
|
||||
|
||||
- Read `plan.json` — validate against plan-overview-base-schema
|
||||
- Read all `TASK-*.json` from `.task/` directory — validate against task-schema
|
||||
- Read `doc-index.json` from `.workflow/.doc-index/`
|
||||
|
||||
### 1.3 Pre-Load Doc Context
|
||||
|
||||
For each task with `doc_context`:
|
||||
- Load referenced `feature_docs` (feature-maps/{slug}.md)
|
||||
- Load referenced `component_docs` (tech-registry/{slug}.md)
|
||||
- Load ADR excerpts from doc-index `architectureDecisions[]`
|
||||
- Extract requirement acceptance criteria from doc-index `requirements[]`
|
||||
- Load `doc_context.symbol_docs[]` documentation content (if present)
|
||||
|
||||
### 1.4 Echo Strategy
|
||||
|
||||
Display execution summary:
|
||||
|
||||
```
|
||||
DDD Execute: {plan.summary}
|
||||
Complexity: {plan.complexity}
|
||||
Tasks: {plan.task_count}
|
||||
|
||||
Doc-Index Impact:
|
||||
Features: {doc_context.affected_features}
|
||||
Requirements: {doc_context.affected_requirements}
|
||||
Components: {doc_context.affected_components}
|
||||
Constraints: {doc_context.architecture_constraints}
|
||||
|
||||
Execution plan: {batch count} batches, {parallel tasks} parallel where possible
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Task Grouping & Batch Creation
|
||||
|
||||
### 2.1 Extract Dependencies
|
||||
|
||||
From each `TASK-*.json`, read `depends_on[]` to build dependency graph.
|
||||
|
||||
### 2.2 Group into Batches
|
||||
|
||||
```
|
||||
Batch 1: Tasks with no dependencies (depends_on: [])
|
||||
Batch 2: Tasks depending only on Batch 1 tasks
|
||||
Batch 3: Tasks depending on Batch 1 + 2 tasks
|
||||
...
|
||||
```
|
||||
|
||||
Within each batch, tasks with the same `parallel_group` can run concurrently.
|
||||
|
||||
### 2.3 Assign Executor per Task
|
||||
|
||||
| Signal | Executor |
|
||||
|--------|----------|
|
||||
| `meta.execution_config.method == "cli"` | CLI tool (gemini/codex/qwen) |
|
||||
| `meta.execution_config.method == "agent"` | Agent (code-developer/universal-executor) |
|
||||
| Default | Agent (code-developer) |
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Doc-Enriched Execution
|
||||
|
||||
For each task in batch order, build an enriched prompt:
|
||||
|
||||
### 3.1 Task Prompt Template
|
||||
|
||||
```markdown
|
||||
## Goal
|
||||
${plan.summary} — specifically: ${task.title}
|
||||
|
||||
## Document Context
|
||||
|
||||
### Feature: ${feature.name} (${feature.id})
|
||||
${feature-map content excerpt — overview + requirements section}
|
||||
|
||||
### Components
|
||||
${for each component in task.doc_context.component_ids:
|
||||
tech-registry excerpt — responsibility + code locations + key patterns}
|
||||
|
||||
### Symbol Documentation
|
||||
${if doc_context.symbol_docs is non-empty:
|
||||
for each component in doc_context.component_ids:
|
||||
#### ${component.name} Symbols (Top-5)
|
||||
${for each symbol in component.symbol_docs.slice(0, 5):
|
||||
- **${symbol.name}** (${symbol.type}): ${symbol.doc_summary}
|
||||
Source: `${symbol.source_path}` | Freshness: ${symbol.freshness}
|
||||
}
|
||||
}
|
||||
|
||||
### Architecture Constraints
|
||||
${for each ADR in task.doc_context.adr_ids:
|
||||
ADR title + decision + rationale from doc-index}
|
||||
|
||||
### Requirement Acceptance Criteria
|
||||
${for each requirement in task.doc_context.requirement_ids:
|
||||
requirement title + priority + success criteria from doc-index}
|
||||
|
||||
## Task Details
|
||||
${task.description}
|
||||
|
||||
### Files to Modify
|
||||
${task.files[] — path, action, changes}
|
||||
|
||||
### Implementation Steps
|
||||
${task.implementation[] — step-by-step guide}
|
||||
|
||||
## Done When
|
||||
${task.convergence.criteria[]}
|
||||
${task.convergence.verification}
|
||||
```
|
||||
|
||||
### 3.2 Execute Task
|
||||
|
||||
**Agent execution**:
|
||||
```
|
||||
Agent(subagent_type="code-developer", prompt="{enriched prompt}")
|
||||
```
|
||||
|
||||
**CLI execution**:
|
||||
```bash
|
||||
ccw cli -p "{enriched prompt}" --tool {cli_tool} --mode write
|
||||
```
|
||||
|
||||
### 3.3 Record & Persist Result
|
||||
|
||||
After each task completes:
|
||||
- Update `TASK-*.json` with `status`, `executed_at`, `result`
|
||||
- Track `result.files_modified` for impact verification
|
||||
- **Persist** result to `TASK-{id}.result.json` alongside the task file:
|
||||
|
||||
```json
|
||||
{
|
||||
"task_id": "TASK-001",
|
||||
"status": "completed|failed",
|
||||
"executed_at": "ISO8601",
|
||||
"executor": "code-developer|gemini|codex",
|
||||
"files_modified": [
|
||||
{ "path": "src/services/auth.ts", "action": "modified", "symbols_changed": ["AuthService.validate"] },
|
||||
{ "path": "src/routes/login.ts", "action": "created", "symbols_changed": ["loginRoute"] }
|
||||
],
|
||||
"convergence_result": {
|
||||
"criteria_met": ["Rate limiter middleware exists"],
|
||||
"criteria_unmet": [],
|
||||
"verification_output": "test output snippet..."
|
||||
},
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
This file serves as the durable handoff between execute and sync — survives process interruptions.
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Per-Batch Impact Verification
|
||||
|
||||
After each batch completes (unless `--skip-verify`):
|
||||
|
||||
### 4.1 Trace Changed Files
|
||||
|
||||
For each file modified in the batch:
|
||||
```
|
||||
changed_file → match to doc-index.technicalComponents[].codeLocations[].path
|
||||
→ component_ids → featureIds → requirementIds
|
||||
```
|
||||
|
||||
### 4.2 Scope Verification
|
||||
|
||||
Compare actual impact to planned impact:
|
||||
|
||||
```
|
||||
Planned scope:
|
||||
Features: [feat-auth]
|
||||
Components: [tech-auth-service, tech-user-model]
|
||||
|
||||
Actual impact:
|
||||
Features: [feat-auth] ← OK, within scope
|
||||
Components: [tech-auth-service, tech-user-model, tech-email-service]
|
||||
← WARNING: tech-email-service not in plan
|
||||
```
|
||||
|
||||
### 4.3 Flag Unexpected Impact
|
||||
|
||||
If changes affect features/components NOT in `plan.doc_context`:
|
||||
- **Warning**: Display unexpected impact
|
||||
- **No -y**: Ask user to confirm continuation
|
||||
- **With -y**: Log warning, continue execution
|
||||
|
||||
### 4.4 Skip Conditions
|
||||
|
||||
Skip verification when:
|
||||
- `--skip-verify` flag is set
|
||||
- Only 1 batch (no intermediate verification needed for simple plans)
|
||||
|
||||
---
|
||||
|
||||
## Step 4.5: Post-Execution Verify Gate
|
||||
|
||||
After all batches complete, before doc sync (unless `--skip-verify`):
|
||||
|
||||
### 4.5.1 Convergence Verification
|
||||
|
||||
For each completed task with `convergence.verification`:
|
||||
```
|
||||
Execute: {task.convergence.verification}
|
||||
→ e.g., "npm test -- --grep rate-limit"
|
||||
Record: pass/fail → update TASK-{id}.result.json.convergence_result
|
||||
```
|
||||
|
||||
### 4.5.2 Build & Lint Check
|
||||
|
||||
```
|
||||
Run project build command (if configured):
|
||||
→ npm run build / tsc --noEmit / etc.
|
||||
Run project lint command (if configured):
|
||||
→ npm run lint / eslint src/ / etc.
|
||||
```
|
||||
|
||||
If build or lint fails:
|
||||
- **No -y**: Display errors, ask user: fix now / continue anyway / abort
|
||||
- **With -y**: Log warning, continue (non-blocking)
|
||||
|
||||
### 4.5.3 Regression Test
|
||||
|
||||
```
|
||||
Run project test suite:
|
||||
→ npm test / pytest / etc.
|
||||
Compare: test results before execution (baseline) vs after
|
||||
```
|
||||
|
||||
If tests fail:
|
||||
- **No -y**: Display failures, ask user: fix now / skip sync / abort
|
||||
- **With -y**: Log failures as warning in execution results, continue
|
||||
|
||||
### 4.5.4 Verify Summary
|
||||
|
||||
```
|
||||
Verify Gate Results:
|
||||
Convergence: {passed}/{total} tasks verified
|
||||
Build: pass|fail|skipped
|
||||
Lint: pass|fail|skipped
|
||||
Tests: {passed}/{total} ({new_failures} regressions)
|
||||
|
||||
Gate: PASS / WARN (continue with warnings) / FAIL (blocked)
|
||||
```
|
||||
|
||||
### 4.5.5 Persist Verify Manifest
|
||||
|
||||
Write `execution-manifest.json` to session folder:
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "{session-id}",
|
||||
"plan_path": "planning/{slug}/plan.json",
|
||||
"completed_at": "ISO8601",
|
||||
"tasks": [
|
||||
{
|
||||
"task_id": "TASK-001",
|
||||
"status": "completed",
|
||||
"result_file": ".task/TASK-001.result.json"
|
||||
}
|
||||
],
|
||||
"files_modified": [
|
||||
{ "path": "src/services/auth.ts", "action": "modified", "task_id": "TASK-001" },
|
||||
{ "path": "src/routes/login.ts", "action": "created", "task_id": "TASK-001" }
|
||||
],
|
||||
"verify": {
|
||||
"convergence": { "passed": 2, "total": 2 },
|
||||
"build": "pass",
|
||||
"lint": "pass",
|
||||
"tests": { "passed": 42, "total": 42, "regressions": 0 },
|
||||
"gate": "PASS"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This manifest is the **single source of truth** consumed by `ddd:sync --from-manifest`.
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Post-Completion Doc Sync
|
||||
|
||||
After all batches complete (unless `--skip-sync`):
|
||||
|
||||
### 5.1 Auto-Trigger ddd:sync
|
||||
|
||||
```
|
||||
Invoke /ddd:sync [-y] --task-id {session-id} --from-manifest {session}/execution-manifest.json "{plan.summary}"
|
||||
```
|
||||
|
||||
Note: `/ddd:sync` automatically creates a backup of `doc-index.json` before modifications.
|
||||
|
||||
When `--from-manifest` is provided, sync uses the **execution manifest** as its primary data source instead of git diff. This ensures:
|
||||
- Precise file-level and symbol-level change tracking (from TASK-*.result.json)
|
||||
- Task-to-file attribution (which task modified which file)
|
||||
- Convergence verification results carried forward
|
||||
- Survives process interruptions (manifest is persisted to disk)
|
||||
|
||||
Fallback: If manifest is unavailable (e.g., manual mode), sync falls back to git diff discovery.
|
||||
|
||||
### 5.2 Generate Action Log
|
||||
|
||||
Create action entry with:
|
||||
- All tasks executed and their results
|
||||
- Files modified across all batches
|
||||
- Features and requirements addressed
|
||||
|
||||
### 5.3 Update Feature Status
|
||||
|
||||
Based on execution results:
|
||||
- Requirements with verified convergence → update status
|
||||
- Features with all requirements met → `status: "implemented"`
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Summary & Follow-up
|
||||
|
||||
### 6.1 Execution Results
|
||||
|
||||
```
|
||||
DDD Execute Complete
|
||||
|
||||
Tasks: {completed}/{total} ({failed} failed)
|
||||
Files modified: {count}
|
||||
Batches: {batch_count}
|
||||
|
||||
Doc-Index Changes:
|
||||
Features updated: {list}
|
||||
Components updated: {list}
|
||||
New components registered: {list}
|
||||
Requirements addressed: {list}
|
||||
|
||||
Convergence:
|
||||
{for each task: task.id — criteria met: X/Y}
|
||||
```
|
||||
|
||||
### 6.2 Follow-up Suggestions
|
||||
|
||||
Based on execution results, suggest:
|
||||
- **New issues**: If unexpected scope expansion was detected
|
||||
- **Additional tests**: If convergence criteria only partially met
|
||||
- **Documentation gaps**: If new components were created without docs
|
||||
- **Next tasks**: If plan had tasks marked as future/deferred
|
||||
|
||||
---
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Effect |
|
||||
|------|--------|
|
||||
| `-y, --yes` | Auto-confirm, auto-sync |
|
||||
| `--skip-sync` | Skip post-completion ddd:sync (Step 5) |
|
||||
| `--skip-verify` | Skip per-batch impact verification (Step 4) AND post-execution verify gate (Step 4.5) |
|
||||
| `--plan <path>` | Explicit plan.json path |
|
||||
| `--in-memory` | Accept executionContext from ddd:plan handoff |
|
||||
|
||||
## Integration Points
|
||||
|
||||
- **Input from**: `/ddd:plan` output (plan.json + TASK-*.json), `doc-index.json`
|
||||
- **Output to**: Updated `doc-index.json` (via ddd:sync), `TASK-*.result.json` (per-task), `execution-manifest.json` (session-level)
|
||||
- **Schemas**: `plan-overview-ddd-schema.json` (input), `task-schema.json` + `task-ddd-extension-schema.json` (input), `doc-index.json` (enrichment)
|
||||
- **Delegates to**: `/ddd:sync` for post-completion synchronization
|
||||
@@ -1,212 +0,0 @@
|
||||
---
|
||||
name: index-build
|
||||
description: Build document index from spec-generator outputs + codebase mapping. Requires existing spec session. For projects without specs, use /ddd:scan instead.
|
||||
argument-hint: "[-y|--yes] [-s|--spec <spec-session-id>] [--from-scratch]"
|
||||
allowed-tools: TodoWrite(*), Agent(*), AskUserQuestion(*), Read(*), Grep(*), Glob(*), Bash(*), Edit(*), Write(*), mcp__ace-tool__search_context(*)
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Auto-confirm all decisions, use inferred mappings, skip interactive review.
|
||||
|
||||
# DDD Index Build Command (/ddd:index-build)
|
||||
|
||||
## Purpose
|
||||
|
||||
From **spec-generator outputs** (requirements, architecture, epics), construct the central document index and map spec entities to actual code locations.
|
||||
|
||||
```
|
||||
Spec outputs (REQ, ADR, EPIC) + Codebase → doc-index.json
|
||||
```
|
||||
|
||||
> **No spec?** Use `/ddd:scan` instead — it reverse-engineers the index from code alone.
|
||||
|
||||
## Prerequisite
|
||||
|
||||
- At least one spec session in `.workflow/.doc-index/specs/` or `.workflow/.spec/`
|
||||
- If no spec found → error with suggestion: "No spec session found. Run /ddd:scan for code-first indexing, or /spec-generator to create specs."
|
||||
|
||||
## Storage Location
|
||||
|
||||
```
|
||||
.workflow/.doc-index/
|
||||
├── doc-index.json ← Central index (primary output)
|
||||
├── specs/ ← Spec-generator outputs
|
||||
│ └── SPEC-{slug}-{date}/
|
||||
├── feature-maps/ ← Feature documentation (from Epics)
|
||||
│ ├── _index.md
|
||||
│ └── {feature-slug}.md
|
||||
├── tech-registry/ ← Technical component docs (from code mapping)
|
||||
│ ├── _index.md
|
||||
│ └── {component-slug}.md
|
||||
└── action-logs/ ← Change history (initially empty)
|
||||
└── _index.md
|
||||
```
|
||||
|
||||
## Phase 1: Discover & Parse Spec Sources
|
||||
|
||||
### 1.1 Locate Spec Session
|
||||
|
||||
```
|
||||
IF --spec <id> provided:
|
||||
Load from .workflow/.doc-index/specs/<id>/ OR .workflow/.spec/<id>/
|
||||
ELSE:
|
||||
Scan for all SPEC-* directories
|
||||
IF multiple → present list, ask user to select (-y picks latest)
|
||||
IF none → ERROR: "No spec session found. Use /ddd:scan or /spec-generator."
|
||||
```
|
||||
|
||||
### 1.2 Migrate Specs (if needed)
|
||||
|
||||
If spec in `.workflow/.spec/` but not in `.workflow/.doc-index/specs/`:
|
||||
- Copy to `.workflow/.doc-index/specs/`
|
||||
- Preserve original (backward compatibility)
|
||||
|
||||
### 1.3 Extract Structured Entities
|
||||
|
||||
| Source File | Extract To |
|
||||
|------------|------------|
|
||||
| `spec-config.json` | project name, domain, spec_type |
|
||||
| `glossary.json` | → index glossary[] |
|
||||
| `product-brief.md` | vision, goals |
|
||||
| `requirements/REQ-*.md` | → index requirements[] (with MoSCoW priority) |
|
||||
| `requirements/NFR-*.md` | → index requirements[] (non-functional) |
|
||||
| `architecture/ADR-*.md` | → index architectureDecisions[] |
|
||||
| `epics/EPIC-*.md` | → feature grouping seeds |
|
||||
|
||||
## Phase 2: Codebase Mapping
|
||||
|
||||
Map spec entities to actual code locations using Gemini:
|
||||
|
||||
```bash
|
||||
ccw cli -p "PURPOSE: Map codebase to specification entities for documentation indexing.
|
||||
TASK:
|
||||
• Scan the codebase and identify all major modules/components
|
||||
• For each component: extract file paths, exported symbols (classes, functions, types)
|
||||
• Match components to these specification entities by name/domain similarity:
|
||||
Requirements: {REQ-001: desc, REQ-002: desc, ...extracted from Phase 1}
|
||||
Architecture decisions: {ADR-001: title, ...extracted from Phase 1}
|
||||
• Report unmatched components (exist in code but no spec counterpart)
|
||||
• Report unmatched requirements (in spec but no code found)
|
||||
MODE: analysis
|
||||
CONTEXT: @**/*
|
||||
EXPECTED: JSON: { components: [{ name, type, files, symbols, matched_req_ids, matched_adr_id, is_orphan }], unmatched_reqs: [REQ-NNN] }
|
||||
CONSTRAINTS: Focus on source directories | Ignore node_modules, dist, build" --tool gemini --mode analysis
|
||||
```
|
||||
|
||||
### 2.1 Generate Component IDs & Link
|
||||
|
||||
For each discovered component:
|
||||
- ID: `tech-{kebab-case-name}`
|
||||
- Link to matched `REQ-NNN` and `ADR-NNN`
|
||||
- Flag orphans for user review
|
||||
|
||||
## Phase 3: Build Feature Map (from Epics)
|
||||
|
||||
### 3.1 Epic → Feature Mapping
|
||||
|
||||
```
|
||||
Each EPIC-NNN → one feat-{slug}
|
||||
- id: feat-{slug} (from epic slug)
|
||||
- name: from Epic name
|
||||
- epicId: EPIC-NNN
|
||||
- status: inferred from code mapping
|
||||
- all requirements have matched components → "implemented"
|
||||
- some matched → "in-progress"
|
||||
- none matched → "planned"
|
||||
- requirementIds: from Epic's stories → requirement links
|
||||
- tags: from domain keywords
|
||||
```
|
||||
|
||||
### 3.2 Document Generation (delegated)
|
||||
|
||||
Feature-map and tech-registry document generation is handled by `/ddd:doc-generate` in Phase 5.
|
||||
Phase 3 only builds the data structures (feature → requirement → component mappings) that doc-generate consumes.
|
||||
|
||||
## Phase 4: Assemble doc-index.json
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"project": "{project-name}",
|
||||
"build_path": "spec-first",
|
||||
"spec_session": "SPEC-{slug}-{date}",
|
||||
"last_updated": "ISO8601",
|
||||
"glossary": [
|
||||
{ "id": "gloss-{slug}", "term": "Term", "definition": "...", "aliases": [], "category": "core|technical|business" }
|
||||
],
|
||||
"features": [
|
||||
{ "id": "feat-{slug}", "name": "...", "epicId": "EPIC-NNN", "status": "...", "docPath": "feature-maps/{slug}.md", "requirementIds": ["REQ-NNN"], "tags": [] }
|
||||
],
|
||||
"requirements": [
|
||||
{ "id": "REQ-NNN", "title": "...", "source": "spec", "priority": "Must|Should|Could|Won't", "sourcePath": "specs/SPEC-*/requirements/REQ-NNN-*.md", "techComponentIds": ["tech-{slug}"], "featureId": "feat-{slug}" }
|
||||
],
|
||||
"technicalComponents": [
|
||||
{ "id": "tech-{slug}", "name": "...", "type": "...", "responsibility": "...", "adrId": "ADR-NNN|null", "docPath": "tech-registry/{slug}.md", "codeLocations": [{ "path": "...", "symbols": [], "lineRange": [0,0] }], "dependsOn": [], "featureIds": ["feat-{slug}"], "actionIds": [] }
|
||||
],
|
||||
"architectureDecisions": [
|
||||
{ "id": "ADR-NNN", "title": "...", "source": "spec", "sourcePath": "specs/SPEC-*/architecture/ADR-NNN-*.md", "componentIds": ["tech-{slug}"] }
|
||||
],
|
||||
"actions": []
|
||||
}
|
||||
```
|
||||
|
||||
### Merge with Existing Code-First Index
|
||||
|
||||
If a code-first index exists (from prior `/ddd:scan`):
|
||||
- Replace `IREQ-NNN` with matching `REQ-NNN` where content overlaps
|
||||
- Keep `IREQ-NNN` without spec counterpart (mark `source: "legacy-inferred"`)
|
||||
- Replace `IADR-NNN` with `ADR-NNN` where applicable
|
||||
- Update `build_path` to `"spec-first"`
|
||||
- Preserve existing `tech-*` components (update links only)
|
||||
|
||||
## Phase 5: Generate Documents
|
||||
|
||||
Delegate all document generation to `/ddd:doc-generate`:
|
||||
|
||||
```
|
||||
Invoke /ddd:doc-generate [-y]
|
||||
```
|
||||
|
||||
This generates the complete document tree (Layer 3 → 2 → 1):
|
||||
- `tech-registry/{slug}.md` — component docs from Phase 2 mapping (Layer 3)
|
||||
- `feature-maps/{slug}.md` — feature docs from Phase 3 mapping (Layer 2)
|
||||
- `_index.md`, `README.md`, `ARCHITECTURE.md`, `SCHEMA.md` — index/overview docs (Layer 1)
|
||||
|
||||
See `/ddd:doc-generate` for full details on generation strategy and flags.
|
||||
|
||||
## Phase 6: Coverage Report
|
||||
|
||||
```
|
||||
Index Build Report (spec-first)
|
||||
|
||||
Spec: {session-id}
|
||||
Features: {N} (from {N} Epics)
|
||||
Requirements: {N} (REQ: {n}, NFR: {n})
|
||||
Components: {N} ({orphan} orphans without spec match)
|
||||
ADRs: {N}
|
||||
|
||||
Mapping Coverage:
|
||||
Requirements → Components: {%} ({unmapped} unmapped)
|
||||
Components → Features: {%}
|
||||
Epics → Features: 100%
|
||||
|
||||
Gaps:
|
||||
- {N} requirements have no matching code component
|
||||
- {N} code components are not linked to any requirement
|
||||
```
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Effect |
|
||||
|------|--------|
|
||||
| `-y, --yes` | Skip all interactive prompts |
|
||||
| `-s, --spec <id>` | Use specific spec session |
|
||||
| `--from-scratch` | Delete existing index and rebuild |
|
||||
|
||||
## Integration Points
|
||||
|
||||
- **Input from**: `spec-generator` outputs, codebase, existing `/ddd:scan` index
|
||||
- **Delegates to**: `/ddd:doc-generate` (Phase 5, full document generation)
|
||||
- **Output to**: `ddd:plan`, `ddd:sync`, `ddd:update`
|
||||
- **Upgrades**: Can merge with prior code-first (`/ddd:scan`) index
|
||||
@@ -1,611 +0,0 @@
|
||||
---
|
||||
name: plan
|
||||
description: Document-driven planning pipeline — queries doc-index, explores codebase with doc-aware angles, clarifies ambiguities, and produces unified plan.json + TASK-*.json artifacts with doc_context traceability.
|
||||
argument-hint: "[-y|--yes] [--explore] [--skip-explore] [--skip-clarify] \"task description or feature keyword\""
|
||||
allowed-tools: TodoWrite(*), Agent(*), AskUserQuestion(*), Read(*), Grep(*), Glob(*), Bash(*), Write(*), mcp__ace-tool__search_context(*)
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Skip clarification (Phase 3), auto-select ddd:execute (Phase 5), skip interactive refinement.
|
||||
|
||||
# DDD Plan Command (/ddd:plan)
|
||||
|
||||
## Purpose
|
||||
|
||||
Full planning pipeline for document-driven development. Unlike simple context lookup, this command:
|
||||
1. **Queries** the doc-index for instant context (features, requirements, components, ADRs)
|
||||
2. **Explores** the codebase with doc-index-informed angles (not generic presets)
|
||||
3. **Clarifies** ambiguities from exploration results and doc-index gaps
|
||||
4. **Plans** with unified schema output (plan.json + TASK-*.json with doc_context)
|
||||
5. **Hands off** to ddd:execute or other execution engines
|
||||
|
||||
### Key Differentiation from lite-plan
|
||||
- Phase 1 provides instant context from doc-index (no cold-start exploration)
|
||||
- Exploration angles are doc-index-informed (not generic preset selection)
|
||||
- Tasks carry doc_context for traceability (features → requirements → code)
|
||||
- Architecture decisions (ADRs) automatically surface as constraints
|
||||
|
||||
## Prerequisite
|
||||
|
||||
- `doc-index.json` must exist at `.workflow/.doc-index/doc-index.json`
|
||||
- If not found → suggest running `/ddd:index-build` or `/ddd:scan` first
|
||||
|
||||
## Session Folder
|
||||
|
||||
```
|
||||
.workflow/.doc-index/planning/{task-slug}-{YYYY-MM-DD}/
|
||||
├── exploration-{angle}.json # Per-angle exploration (Phase 2)
|
||||
├── explorations-manifest.json # Exploration index
|
||||
├── plan.json # Plan overview (Phase 4)
|
||||
├── planning-context.md # Legacy context package (Phase 0+1 combined)
|
||||
├── .process/
|
||||
│ └── doc-context-package.json # Bundled doc_context (Phase 1.8)
|
||||
└── .task/
|
||||
├── TASK-001.json
|
||||
└── TASK-002.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Parse Task Intent (enhanced)
|
||||
|
||||
### 0.1 Extract Keywords
|
||||
|
||||
From the user's task description, extract:
|
||||
- **Domain keywords**: feature names, module names, business terms
|
||||
- **Technical keywords**: file paths, class names, function names
|
||||
- **Action type**: feature | bugfix | refactor | optimization | migration
|
||||
|
||||
### 0.2 Glossary Match
|
||||
|
||||
Cross-reference extracted keywords against `doc-index.json.glossary[]`:
|
||||
- Match terms and aliases
|
||||
- Expand user's vocabulary with canonical terms
|
||||
|
||||
### 0.3 Classify Complexity
|
||||
|
||||
Assess task complexity based on:
|
||||
- Number of features potentially affected (from keyword matching)
|
||||
- Whether new components are needed or existing ones modified
|
||||
- Cross-feature impact (single feature vs multiple)
|
||||
|
||||
| Signal | Complexity |
|
||||
|--------|-----------|
|
||||
| Single feature, existing components | Low |
|
||||
| 1-2 features, some new components | Medium |
|
||||
| 3+ features, new architecture needed | High |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Doc-Index Query
|
||||
|
||||
### 1.0 Schema Version Check (TASK-006)
|
||||
|
||||
Before querying doc-index, verify schema compatibility:
|
||||
|
||||
```javascript
|
||||
const docIndex = JSON.parse(Read('.workflow/.doc-index/doc-index.json'));
|
||||
const schemaVersion = docIndex.schema_version || '0.0'; // Default for legacy
|
||||
|
||||
if (schemaVersion !== '1.0') {
|
||||
console.warn(`Schema version mismatch: found ${schemaVersion}, expected 1.0`);
|
||||
console.warn('Consider running schema migration or regenerating doc-index with /ddd:scan');
|
||||
// Continue with degraded functionality - may encounter missing fields
|
||||
}
|
||||
```
|
||||
|
||||
**Graceful degradation**: If version mismatch detected → log warning → continue with caution (some features may not work as expected).
|
||||
|
||||
### 1.1 Feature Search
|
||||
|
||||
```
|
||||
Search doc-index.json.features[] where:
|
||||
- name CONTAINS keyword (fuzzy)
|
||||
- tags INTERSECT keywords
|
||||
- requirementIds link to matching requirements
|
||||
→ Output: matched feature IDs + names
|
||||
```
|
||||
|
||||
### 1.2 Requirement Search
|
||||
|
||||
```
|
||||
Search doc-index.json.requirements[] where:
|
||||
- title CONTAINS keyword
|
||||
- id matches explicit REQ-NNN reference
|
||||
- featureId matches found features
|
||||
→ Output: matched requirement IDs + titles + priorities
|
||||
```
|
||||
|
||||
### 1.3 Component Search
|
||||
|
||||
```
|
||||
Search doc-index.json.technicalComponents[] where:
|
||||
- name CONTAINS keyword
|
||||
- codeLocations[].path CONTAINS file path keyword
|
||||
- codeLocations[].symbols CONTAINS symbol keyword
|
||||
- featureIds INTERSECT found features
|
||||
→ Output: matched component IDs + code locations
|
||||
```
|
||||
|
||||
### 1.4 ADR Search
|
||||
|
||||
```
|
||||
Search doc-index.json.architectureDecisions[] where:
|
||||
- componentIds INTERSECT found components
|
||||
→ Output: matched ADR IDs + titles
|
||||
```
|
||||
|
||||
### 1.5 Action History Search
|
||||
|
||||
```
|
||||
Search doc-index.json.actions[] where:
|
||||
- related to found features or components
|
||||
→ Output: recent actions with descriptions
|
||||
```
|
||||
|
||||
### 1.6 Build Impact Map
|
||||
|
||||
Assemble all found references into a structured impact map:
|
||||
|
||||
```json
|
||||
{
|
||||
"affected_features": ["feat-auth"],
|
||||
"affected_requirements": ["REQ-001", "REQ-002"],
|
||||
"affected_components": ["tech-auth-service", "tech-user-model"],
|
||||
"architecture_constraints": ["ADR-001"],
|
||||
"recent_actions": ["task-123"],
|
||||
"complexity": "Medium"
|
||||
}
|
||||
```
|
||||
|
||||
Save as `planning-context.md` (legacy format for backward compatibility).
|
||||
|
||||
### Phase 1.7: Symbol Query (DeepWiki Bridge)
|
||||
|
||||
If DeepWiki is available (`deepwiki_feature_to_symbol_index` exists in doc-index.json):
|
||||
|
||||
1. Collect all `codeLocations[].path` from matched `technicalComponents[]`
|
||||
2. Query DeepWiki: `POST /api/deepwiki/symbols-for-paths { paths: unique_paths }`
|
||||
3. Build symbol_docs by component, sorted by type priority (class > function > method)
|
||||
4. Populate `doc_context.symbol_docs[]` with Top-5 symbols per component
|
||||
|
||||
**Graceful degradation**: If DeepWiki unavailable → log warning → skip symbol injection → continue flow.
|
||||
|
||||
### Phase 1.8: Persist Doc Context Package
|
||||
|
||||
After building doc_context (including symbol_docs from Phase 1.7), persist it as a reusable context package:
|
||||
|
||||
1. Bundle doc_context into JSON structure:
|
||||
```json
|
||||
{
|
||||
"affected_features": ["feat-auth"],
|
||||
"affected_requirements": ["REQ-001", "REQ-002"],
|
||||
"affected_components": ["tech-auth-service"],
|
||||
"architecture_constraints": ["ADR-001"],
|
||||
"index_path": ".workflow/.doc-index/doc-index.json",
|
||||
"symbol_docs": [...]
|
||||
}
|
||||
```
|
||||
|
||||
2. Write to session folder: `{sessionFolder}/.process/doc-context-package.json`
|
||||
3. Store relative path for task.json population: `../.process/doc-context-package.json`
|
||||
|
||||
**Error handling**: If write fails → log warning → continue without context package (backward compatible).
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Doc-Index-Guided Exploration (NEW)
|
||||
|
||||
Use Phase 1 results to **SELECT exploration angles intelligently**:
|
||||
|
||||
### 2.1 Angle Selection Logic
|
||||
|
||||
| Phase 1 Signal | Add Exploration Angle |
|
||||
|----------------|----------------------|
|
||||
| feat-auth or security-related ADR affected | `security` |
|
||||
| Multiple features crossed (2+) | `integration-points` |
|
||||
| New component needed (no matching tech-*) | `architecture` |
|
||||
| Performance-related requirements | `performance` |
|
||||
| Default (always included) | `patterns` + `dependencies` |
|
||||
|
||||
Select 1-4 angles total. More angles for higher complexity.
|
||||
|
||||
### 2.2 Skip & Trigger Conditions
|
||||
|
||||
| Complexity | Default Behavior | Override |
|
||||
|-----------|-----------------|---------|
|
||||
| **Low** | Auto-skip Phase 2 | `--explore` forces exploration |
|
||||
| **Medium** | Ask user (unless `-y` → skip) | `--explore` forces, `--skip-explore` forces skip |
|
||||
| **High** | Always run | `--skip-explore` forces skip |
|
||||
|
||||
Skip Phase 2 entirely when:
|
||||
- Complexity is Low AND `--explore` not set
|
||||
- OR `--skip-explore` flag is set
|
||||
- OR `-y` flag AND complexity is Medium
|
||||
|
||||
### 2.3 Parallel Exploration
|
||||
|
||||
Launch 1-4 parallel `cli-explore-agent` runs:
|
||||
|
||||
```
|
||||
For each selected angle:
|
||||
Agent(subagent_type="cli-explore-agent", prompt="
|
||||
Explore codebase for: {user task description}
|
||||
Angle: {angle}
|
||||
|
||||
## Doc-Index Context (pre-loaded)
|
||||
Features affected: {feature names + IDs}
|
||||
Components: {component names + code locations}
|
||||
Requirements: {requirement titles}
|
||||
Architecture decisions: {ADR titles + decisions}
|
||||
|
||||
Focus exploration on {angle}-specific concerns.
|
||||
Output: explore-json-schema format.
|
||||
")
|
||||
```
|
||||
|
||||
Each agent receives doc-index context (feature-maps, tech-registry docs) to avoid cold-start.
|
||||
|
||||
### 2.4 Save Exploration Results
|
||||
|
||||
- Each exploration → `exploration-{angle}.json` (explore-json-schema)
|
||||
- Manifest → `explorations-manifest.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"explorations": [
|
||||
{ "angle": "patterns", "path": "exploration-patterns.json", "file_count": 12 },
|
||||
{ "angle": "security", "path": "exploration-security.json", "file_count": 8 }
|
||||
],
|
||||
"total_files_discovered": 18,
|
||||
"timestamp": "ISO8601"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Clarification (NEW)
|
||||
|
||||
### 3.1 Aggregate Clarification Needs
|
||||
|
||||
Collect from three sources:
|
||||
1. **Exploration results**: `clarification_needs[]` from each exploration JSON
|
||||
2. **Doc-index gaps**: unmapped requirements, orphan components, missing feature coverage
|
||||
3. **Conflicting constraints**: contradictory architecture decisions, requirement priority conflicts
|
||||
|
||||
### 3.2 Deduplicate & Batch
|
||||
|
||||
- Merge duplicate/similar questions across exploration angles
|
||||
- Group into rounds (max 4 questions per AskUserQuestion call)
|
||||
- Prioritize: blocking questions first, nice-to-have last
|
||||
|
||||
### 3.3 Skip Conditions
|
||||
|
||||
Skip Phase 3 when:
|
||||
- `-y` flag is set
|
||||
- `--skip-clarify` flag is set
|
||||
- No clarification needs collected from any source
|
||||
- Complexity is Low AND Phase 2 was skipped (no exploration results to aggregate)
|
||||
|
||||
### 3.4 Execute Clarification
|
||||
|
||||
```
|
||||
AskUserQuestion(questions=[
|
||||
{
|
||||
question: "Which authentication strategy should the new endpoint use?",
|
||||
header: "Auth strategy",
|
||||
options: [
|
||||
{ label: "JWT Bearer (Recommended)", description: "Consistent with ADR-001 and existing auth middleware" },
|
||||
{ label: "API Key", description: "Simpler but inconsistent with current architecture" },
|
||||
{ label: "OAuth2", description: "Most flexible but higher implementation cost" }
|
||||
],
|
||||
multiSelect: false
|
||||
}
|
||||
])
|
||||
```
|
||||
|
||||
Feed answers back into Phase 4 as constraints.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Task Planning (NEW — produces plan.json + TASK-*.json)
|
||||
|
||||
### 4.1 Planning Strategy Selection
|
||||
|
||||
| Complexity | Strategy |
|
||||
|-----------|---------|
|
||||
| Low | Direct Claude planning (inline) |
|
||||
| Medium | cli-lite-planning-agent with doc-index context |
|
||||
| High | cli-lite-planning-agent with full exploration + doc-index context |
|
||||
|
||||
### 4.2 Planning Input Assembly
|
||||
|
||||
Combine:
|
||||
- User's original task description
|
||||
- Phase 1 impact map (features, requirements, components, ADRs)
|
||||
- Phase 2 exploration results (if executed)
|
||||
- Phase 3 clarification answers (if collected)
|
||||
- Relevant feature-map and tech-registry doc excerpts
|
||||
|
||||
### 4.3 Execute Planning
|
||||
|
||||
For **Low complexity** (direct):
|
||||
```
|
||||
Generate plan.json + TASK-*.json directly based on assembled context.
|
||||
```
|
||||
|
||||
For **Medium/High complexity**:
|
||||
```
|
||||
Agent(subagent_type="cli-lite-planning-agent", prompt="
|
||||
Task: {user task description}
|
||||
|
||||
## Doc-Index Impact Map
|
||||
{Phase 1 results}
|
||||
|
||||
## Exploration Context
|
||||
{Phase 2 results summary}
|
||||
|
||||
## Clarification Answers
|
||||
{Phase 3 answers}
|
||||
|
||||
## Architecture Constraints
|
||||
{ADR excerpts}
|
||||
|
||||
Generate plan following plan-overview-base-schema.
|
||||
Generate tasks following task-schema.
|
||||
Include doc_context in both plan.json and each TASK-*.json.
|
||||
")
|
||||
```
|
||||
|
||||
### 4.3.1 Populate Task Artifacts (TASK-002)
|
||||
|
||||
After task generation, enrich each TASK-*.json with artifacts[] field:
|
||||
|
||||
1. Load doc-index.json from `.workflow/.doc-index/doc-index.json`
|
||||
2. For each task, extract feature_ids from task.doc_context
|
||||
3. Filter doc-index features/requirements matching task scope:
|
||||
- Match by feature_ids in task.doc_context.feature_ids
|
||||
- Include linked requirements via requirementIds
|
||||
- Include linked components via componentIds
|
||||
4. Populate task.artifacts[] with filtered references:
|
||||
|
||||
```json
|
||||
{
|
||||
"artifacts": [
|
||||
{
|
||||
"type": "feature_spec",
|
||||
"source": "doc-index",
|
||||
"path": ".workflow/.doc-index/feature-maps/auth.md",
|
||||
"feature_id": "feat-auth",
|
||||
"usage": "Reference for authentication requirements"
|
||||
},
|
||||
{
|
||||
"type": "requirement",
|
||||
"source": "doc-index",
|
||||
"path": ".workflow/.doc-index/doc-index.json#requirements[0]",
|
||||
"feature_id": "feat-auth",
|
||||
"requirement_id": "REQ-001",
|
||||
"usage": "Acceptance criteria source"
|
||||
},
|
||||
{
|
||||
"type": "component_doc",
|
||||
"source": "doc-index",
|
||||
"path": ".workflow/.doc-index/tech-registry/auth-service.md",
|
||||
"component_id": "tech-auth-service",
|
||||
"usage": "Implementation reference"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Loading pattern** (following brainstorm pattern from action-planning-agent.md:200-214):
|
||||
- Load doc-index.json once for catalog
|
||||
- Filter by task-relevant feature IDs (1-3 per task)
|
||||
- Only include artifacts directly referenced in task scope
|
||||
- Use relative paths from task file location
|
||||
|
||||
### 4.3.2 Populate Context Package Path (TASK-001)
|
||||
|
||||
Set context_package_path field in each TASK-*.json:
|
||||
|
||||
```json
|
||||
{
|
||||
"context_package_path": "../.process/doc-context-package.json"
|
||||
}
|
||||
```
|
||||
|
||||
Relative path from `.task/TASK-*.json` to `.process/doc-context-package.json`.
|
||||
|
||||
### 4.3.3 Add Navigation Links Block (TASK-003)
|
||||
|
||||
Add links{} navigation block to each TASK-*.json for improved discoverability:
|
||||
|
||||
```json
|
||||
{
|
||||
"links": {
|
||||
"plan": "../plan.json",
|
||||
"doc_index": "../../../doc-index.json",
|
||||
"feature_maps": [
|
||||
"../../../feature-maps/auth.md"
|
||||
],
|
||||
"related_tasks": [
|
||||
"TASK-002.json",
|
||||
"TASK-003.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Path computation**:
|
||||
- `plan`: Relative path from `.task/TASK-*.json` to `plan.json` (sibling of .task/)
|
||||
- `doc_index`: Relative path to `.workflow/.doc-index/doc-index.json`
|
||||
- `feature_maps`: Paths to feature-map docs from task.doc_context.feature_docs
|
||||
- `related_tasks`: Task IDs from task.depends_on or tasks sharing same feature_ids
|
||||
|
||||
**Backward compatibility**: links{} is optional field (task-schema allows additionalProperties).
|
||||
|
||||
### 4.4 Output Schema: plan.json
|
||||
|
||||
Follows `plan-overview-base-schema` with ddd-specific `doc_context` extension:
|
||||
|
||||
```json
|
||||
{
|
||||
"summary": "...",
|
||||
"approach": "...",
|
||||
"task_ids": ["TASK-001", "TASK-002"],
|
||||
"task_count": 2,
|
||||
"complexity": "Medium",
|
||||
"doc_context": {
|
||||
"affected_features": ["feat-auth"],
|
||||
"affected_requirements": ["REQ-001", "REQ-002"],
|
||||
"affected_components": ["tech-auth-service"],
|
||||
"architecture_constraints": ["ADR-001"],
|
||||
"index_path": ".workflow/.doc-index/doc-index.json",
|
||||
"symbol_docs": [
|
||||
{
|
||||
"symbol_urn": "deepwiki:symbol:<path>#L<start>-L<end>",
|
||||
"name": "SymbolName",
|
||||
"type": "class|function|method",
|
||||
"doc_summary": "Generated documentation summary...",
|
||||
"source_path": "src/path/to/file.ts",
|
||||
"doc_path": ".deepwiki/file.md",
|
||||
"freshness": "fresh|stale|unknown"
|
||||
}
|
||||
]
|
||||
},
|
||||
"_metadata": {
|
||||
"timestamp": "ISO8601",
|
||||
"source": "cli-lite-planning-agent",
|
||||
"plan_type": "feature",
|
||||
"schema_version": "2.0",
|
||||
"exploration_angles": ["patterns", "security"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 Output Schema: TASK-*.json
|
||||
|
||||
Follows `task-schema` with ddd-specific `doc_context` extension:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "TASK-001",
|
||||
"title": "Add rate limiting middleware",
|
||||
"description": "...",
|
||||
"depends_on": [],
|
||||
"convergence": {
|
||||
"criteria": ["Rate limiter middleware exists and is registered", "Tests pass"],
|
||||
"verification": "npm test -- --grep rate-limit",
|
||||
"definition_of_done": "API endpoints enforce rate limits per ADR-001 specifications"
|
||||
},
|
||||
"doc_context": {
|
||||
"feature_ids": ["feat-auth"],
|
||||
"requirement_ids": ["REQ-001"],
|
||||
"component_ids": ["tech-auth-service"],
|
||||
"adr_ids": ["ADR-001"],
|
||||
"feature_docs": ["feature-maps/auth.md"],
|
||||
"component_docs": ["tech-registry/auth-service.md"],
|
||||
"symbol_docs": [
|
||||
{
|
||||
"symbol_urn": "deepwiki:symbol:<path>#L<start>-L<end>",
|
||||
"name": "SymbolName",
|
||||
"type": "class|function|method",
|
||||
"doc_summary": "Generated documentation summary...",
|
||||
"source_path": "src/path/to/file.ts",
|
||||
"doc_path": ".deepwiki/file.md",
|
||||
"freshness": "fresh|stale|unknown"
|
||||
}
|
||||
]
|
||||
},
|
||||
"files": [...],
|
||||
"implementation": [...]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.6 Enrichment Rules
|
||||
|
||||
Each task is enriched with:
|
||||
- `feature_ids`, `requirement_ids`, `component_ids`, `adr_ids` — traced from Phase 1
|
||||
- Relevant feature-map and tech-registry doc paths
|
||||
- Requirement acceptance criteria as convergence criteria source
|
||||
- ADR decisions as implementation constraints
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Confirmation & Handoff Selection
|
||||
|
||||
### 5.1 Display Plan Summary
|
||||
|
||||
Show:
|
||||
- Plan overview (summary, approach, complexity)
|
||||
- Task list with dependencies
|
||||
- Doc-index impact: which features/requirements/components will be affected
|
||||
- Estimated scope
|
||||
|
||||
### 5.2 Handoff Options
|
||||
|
||||
| Option | Description | When |
|
||||
|--------|-------------|------|
|
||||
| **ddd:execute** | Document-aware execution (recommended) | Default for ddd workflow |
|
||||
| **lite-execute** | Standard execution (no doc awareness) | When doc traceability not needed |
|
||||
| **direct** | Output context, manual work | User prefers manual coding |
|
||||
| **stop** | Planning only, no execution | Research/analysis tasks |
|
||||
|
||||
### 5.3 Auto-Selection
|
||||
|
||||
With `-y`: auto-select `ddd:execute`.
|
||||
|
||||
Without `-y`: present options via AskUserQuestion.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Handoff
|
||||
|
||||
### 6.1 Build Execution Context
|
||||
|
||||
Build `executionContext` compatible with lite-execute format:
|
||||
|
||||
```json
|
||||
{
|
||||
"plan_path": ".workflow/.doc-index/planning/{slug}/plan.json",
|
||||
"task_dir": ".workflow/.doc-index/planning/{slug}/.task/",
|
||||
"doc_index_path": ".workflow/.doc-index/doc-index.json",
|
||||
"exploration_manifest": ".workflow/.doc-index/planning/{slug}/explorations-manifest.json",
|
||||
"original_input": "user's task description"
|
||||
}
|
||||
```
|
||||
|
||||
### 6.2 Invoke Selected Engine
|
||||
|
||||
| Selection | Action |
|
||||
|-----------|--------|
|
||||
| `ddd:execute` | Invoke `/ddd:execute --in-memory` with executionContext |
|
||||
| `lite-execute` | Invoke `/workflow:lite-execute` with plan.json path |
|
||||
| `direct` | Display context package + file list for manual work |
|
||||
| `stop` | Output plan summary, end here |
|
||||
|
||||
---
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Effect |
|
||||
|------|--------|
|
||||
| `-y, --yes` | Skip clarification, auto-select ddd:execute |
|
||||
| `--explore` | Force Phase 2 exploration even for Low complexity |
|
||||
| `--skip-explore` | Skip Phase 2 (doc-index-guided exploration) |
|
||||
| `--skip-clarify` | Skip Phase 3 (clarification) only |
|
||||
|
||||
## Output
|
||||
|
||||
- **Primary**: plan.json + TASK-*.json in session folder
|
||||
- **Secondary**: planning-context.md (legacy format)
|
||||
- **Exploration**: exploration-{angle}.json files (if Phase 2 ran)
|
||||
- **Console**: Plan summary with doc-index impact
|
||||
|
||||
## Integration Points
|
||||
|
||||
- **Input from**: `doc-index.json` (built by `/ddd:index-build` or `/ddd:scan`)
|
||||
- **Output to**: `/ddd:execute`, `/workflow:lite-execute`, `/ddd:sync` post-task
|
||||
- **Schemas**: `plan-overview-ddd-schema.json` (plan output), `task-schema.json` + `task-ddd-extension-schema.json` (task output), `explore-json-schema.json`
|
||||
- **Triggers**: Before any development task in ddd workflow
|
||||
@@ -1,365 +0,0 @@
|
||||
---
|
||||
name: scan
|
||||
description: Scan existing codebase to build document index without specs. Analyzes code structure, infers features, discovers components, and reverse-engineers project knowledge graph.
|
||||
argument-hint: "[-y|--yes] [--from-scratch] [--scope <dir>] \"optional project description\""
|
||||
allowed-tools: TodoWrite(*), Agent(*), AskUserQuestion(*), Read(*), Grep(*), Glob(*), Bash(*), Edit(*), Write(*), mcp__ace-tool__search_context(*)
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Auto-confirm feature groupings, component naming, skip interactive review.
|
||||
|
||||
# DDD Scan Command (/ddd:scan)
|
||||
|
||||
## Purpose
|
||||
|
||||
For **existing projects without specifications**: analyze codebase to construct the document index by reverse-engineering project structure. This is the code-first entry point — no spec-generator required.
|
||||
|
||||
```
|
||||
Codebase → Components → Features (inferred) → Requirements (inferred) → doc-index.json
|
||||
```
|
||||
|
||||
## When to Use
|
||||
|
||||
- Existing project, no spec-generator outputs
|
||||
- Want to start using doc-driven workflow on a legacy codebase
|
||||
- Quick project mapping for onboarding or audit
|
||||
|
||||
## Prerequisite
|
||||
|
||||
- A codebase must exist (src/, lib/, app/, or similar source directories)
|
||||
- Git repository recommended (for action history seeding)
|
||||
|
||||
## Storage Location
|
||||
|
||||
```
|
||||
.workflow/.doc-index/
|
||||
├── doc-index.json ← Central index (primary output)
|
||||
├── feature-maps/ ← Inferred feature documentation
|
||||
│ ├── _index.md
|
||||
│ └── {feature-slug}.md
|
||||
├── tech-registry/ ← Discovered component documentation
|
||||
│ ├── _index.md
|
||||
│ └── {component-slug}.md
|
||||
└── action-logs/ ← Git history seeds
|
||||
├── _index.md
|
||||
└── {act-hash}.md
|
||||
```
|
||||
|
||||
## Phase 1: Project Structure Analysis
|
||||
|
||||
### 1.1 Framework & Stack Detection
|
||||
|
||||
```bash
|
||||
ccw cli -p "PURPOSE: Analyze project structure, tech stack, and architecture for documentation indexing.
|
||||
TASK:
|
||||
• Detect language/framework from manifest files (package.json, go.mod, Cargo.toml, requirements.txt, etc.)
|
||||
• Map directory structure: source dirs, test dirs, config dirs, entry points
|
||||
• Identify architectural pattern: monolith, microservices, monorepo, library, CLI tool
|
||||
• Detect key dependencies and their roles (ORM, HTTP framework, auth library, etc.)
|
||||
• List all major source directories with brief purpose description
|
||||
MODE: analysis
|
||||
CONTEXT: @**/*
|
||||
EXPECTED: JSON with: {
|
||||
project_name, language, framework, architecture_pattern,
|
||||
source_dirs: [{ path, purpose, file_count }],
|
||||
dependencies: [{ name, role }],
|
||||
entry_points: [{ path, description }]
|
||||
}
|
||||
CONSTRAINTS: Prioritize source directories | Ignore node_modules, dist, build, vendor" --tool gemini --mode analysis
|
||||
```
|
||||
|
||||
### 1.2 Merge with project-tech.json
|
||||
|
||||
If `.workflow/project-tech.json` exists, merge to reduce redundant analysis.
|
||||
|
||||
## Phase 2: Component Discovery
|
||||
|
||||
### 2.1 Deep Module Scan
|
||||
|
||||
```bash
|
||||
ccw cli -p "PURPOSE: Discover all significant code components/modules for documentation indexing.
|
||||
TASK:
|
||||
• For each source directory, identify distinct modules/components
|
||||
• For each component extract:
|
||||
- Name (class name, module name, or logical group)
|
||||
- Type: service | controller | model | util | hook | route | config | middleware | component
|
||||
- File paths (primary file + related files)
|
||||
- Exported symbols (public API: classes, functions, types, constants)
|
||||
- Internal dependencies: what other modules it imports from within the project
|
||||
- Responsibility: one-line description of what it does
|
||||
• Group small utility files under parent module when they share domain
|
||||
MODE: analysis
|
||||
CONTEXT: @{source_dirs from Phase 1}
|
||||
EXPECTED: JSON array: [{ name, type, files, symbols, depends_on, responsibility }]
|
||||
CONSTRAINTS: Focus on business logic | Min threshold: components with 2+ exports or clear domain purpose | Group utilities under parent domain" --tool gemini --mode analysis
|
||||
```
|
||||
|
||||
### 2.2 Generate Component IDs
|
||||
|
||||
For each discovered component:
|
||||
- ID: `tech-{kebab-case-name}` (e.g., `tech-auth-service`, `tech-user-model`)
|
||||
- Validate uniqueness, append counter on collision
|
||||
|
||||
### 2.3 Build Dependency Graph
|
||||
|
||||
From `depends_on` fields, construct internal dependency edges:
|
||||
```
|
||||
tech-auth-service → tech-user-model
|
||||
tech-auth-service → tech-jwt-util
|
||||
tech-order-controller → tech-auth-service
|
||||
```
|
||||
|
||||
## Phase 3: Feature Inference
|
||||
|
||||
**Key step: group components into logical features without formal specs.**
|
||||
|
||||
### 3.1 Inference Strategy (priority order)
|
||||
|
||||
```
|
||||
Strategy 1 — Directory grouping:
|
||||
src/auth/** → feat-auth
|
||||
src/orders/** → feat-orders
|
||||
src/payments/** → feat-payments
|
||||
|
||||
Strategy 2 — Route/endpoint grouping (web apps):
|
||||
/api/users/* → feat-user-management
|
||||
/api/orders/* → feat-order-management
|
||||
|
||||
Strategy 3 — Dependency clustering:
|
||||
Components that heavily import each other → same feature
|
||||
|
||||
Strategy 4 — Domain keyword extraction:
|
||||
Class names + file names → domain terms → feature names
|
||||
```
|
||||
|
||||
### 3.2 Gemini Feature Synthesis
|
||||
|
||||
```bash
|
||||
ccw cli -p "PURPOSE: Infer high-level features from discovered code components. This project has no formal specification.
|
||||
TASK:
|
||||
Given these discovered components:
|
||||
{component list from Phase 2: names, types, files, responsibilities, dependencies}
|
||||
|
||||
• Group them into logical features (3-10 features for a typical project)
|
||||
• For each feature:
|
||||
- name: human-readable (Chinese OK)
|
||||
- component_ids: which components belong
|
||||
- description: what the feature does (inferred from code)
|
||||
- inferred_requirements: what this feature needs to accomplish (1-3 per feature)
|
||||
- status: 'implemented' (code complete) or 'partial' (incomplete patterns)
|
||||
- tags: search keywords
|
||||
• Identify cross-cutting concerns (logging, auth middleware, error handling) as separate features
|
||||
MODE: analysis
|
||||
CONTEXT: {component list JSON}
|
||||
EXPECTED: JSON: { features: [{ name, description, component_ids, inferred_requirements: [{ id, title }], status, tags }] }
|
||||
CONSTRAINTS: Every component must belong to at least 1 feature | Prefer fewer broad features over many narrow ones" --tool gemini --mode analysis
|
||||
```
|
||||
|
||||
### 3.3 Interactive Feature Review (unless -y)
|
||||
|
||||
Present inferred features to user:
|
||||
- Allow renaming, merging, splitting
|
||||
- Allow reassigning components between features
|
||||
- Confirm final feature list
|
||||
|
||||
## Phase 4: Implicit Requirement & Architecture Extraction
|
||||
|
||||
### 4.1 Inferred Requirements
|
||||
|
||||
For each feature, generate lightweight requirement entries from its components:
|
||||
|
||||
```
|
||||
Feature: feat-auth (User Authentication)
|
||||
→ IREQ-001: "Users can log in with email and password" (from LoginController)
|
||||
→ IREQ-002: "JWT tokens for session management" (from AuthMiddleware + jwt dep)
|
||||
→ IREQ-003: "Password reset via email" (from PasswordResetService)
|
||||
```
|
||||
|
||||
**ID Convention**: `IREQ-NNN` — distinguishes inferred from formal `REQ-NNN`.
|
||||
|
||||
### 4.2 Inferred Architecture Decisions
|
||||
|
||||
Detect patterns from code + dependencies:
|
||||
|
||||
```
|
||||
Express.js + JWT middleware → IADR-001: "REST API with JWT authentication"
|
||||
Prisma ORM + PostgreSQL → IADR-002: "PostgreSQL via Prisma ORM"
|
||||
React + Redux → IADR-003: "React frontend with Redux state"
|
||||
```
|
||||
|
||||
**ID Convention**: `IADR-NNN` — distinguishes inferred from formal `ADR-NNN`.
|
||||
|
||||
### 4.3 Glossary Generation
|
||||
|
||||
Extract domain terms from:
|
||||
- Class/function names (CamelCase → terms)
|
||||
- Key business terms in comments and strings
|
||||
- Framework-specific terminology
|
||||
|
||||
Write to `.workflow/.doc-index/glossary.json`.
|
||||
|
||||
## Phase 5: Git History Seeds
|
||||
|
||||
```bash
|
||||
git log --oneline --since="3 months ago" --no-merges --format="%H|%s|%ai" | head -30
|
||||
```
|
||||
|
||||
For each significant commit:
|
||||
- Match changed files to discovered components
|
||||
- Create action entry with `type: "historical"`
|
||||
|
||||
## Phase 6: Assemble doc-index.json
|
||||
|
||||
Write the index with code-first markers:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0",
|
||||
"project": "{project-name}",
|
||||
"build_path": "code-first",
|
||||
"spec_session": null,
|
||||
"last_updated": "ISO8601",
|
||||
"glossary": [...],
|
||||
"features": [{
|
||||
"id": "feat-{slug}",
|
||||
"name": "Feature Name",
|
||||
"epicId": null,
|
||||
"status": "implemented|partial",
|
||||
"docPath": "feature-maps/{slug}.md",
|
||||
"requirementIds": ["IREQ-NNN"],
|
||||
"tags": ["tag"]
|
||||
}],
|
||||
"requirements": [{
|
||||
"id": "IREQ-NNN",
|
||||
"title": "Inferred requirement",
|
||||
"source": "inferred",
|
||||
"priority": "inferred",
|
||||
"sourcePath": null,
|
||||
"techComponentIds": ["tech-{slug}"],
|
||||
"featureId": "feat-{slug}"
|
||||
}],
|
||||
"technicalComponents": [{
|
||||
"id": "tech-{slug}",
|
||||
"name": "ComponentName",
|
||||
"type": "service|controller|model|...",
|
||||
"responsibility": "One-line description",
|
||||
"adrId": "IADR-NNN|null",
|
||||
"docPath": "tech-registry/{slug}.md",
|
||||
"codeLocations": [{ "path": "src/...", "symbols": [...] }],
|
||||
"dependsOn": ["tech-{other}"],
|
||||
"featureIds": ["feat-{slug}"],
|
||||
"actionIds": []
|
||||
}],
|
||||
"architectureDecisions": [{
|
||||
"id": "IADR-NNN",
|
||||
"title": "Inferred decision",
|
||||
"source": "inferred",
|
||||
"sourcePath": null,
|
||||
"componentIds": ["tech-{slug}"]
|
||||
}],
|
||||
"actions": [{
|
||||
"id": "act-{short-hash}",
|
||||
"description": "Commit message",
|
||||
"type": "historical",
|
||||
"status": "historical",
|
||||
"affectedComponents": ["tech-{slug}"],
|
||||
"relatedCommit": "full-hash",
|
||||
"timestamp": "ISO8601"
|
||||
}],
|
||||
"freshness": {
|
||||
"thresholds": { "warning": 0.3, "stale": 0.7 },
|
||||
"weights": { "time": 0.1, "churn": 0.4, "symbol": 0.5 },
|
||||
"time_decay_k": 0.05,
|
||||
"auto_regenerate": false
|
||||
},
|
||||
"deepwiki_feature_to_symbol_index": {}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Phase 7: Build DeepWiki Feature-to-Symbol Index
|
||||
|
||||
If DeepWiki is available (`.codexlens/deepwiki_index.db` exists):
|
||||
|
||||
1. Collect all `codeLocations[].path` from `technicalComponents[]`
|
||||
2. Query DeepWiki: `POST /api/deepwiki/symbols-for-paths { paths: [...] }`
|
||||
3. Build `deepwiki_feature_to_symbol_index` by traversing:
|
||||
`feature → requirementIds → techComponentIds → codeLocations → symbols`
|
||||
|
||||
```json
|
||||
"deepwiki_feature_to_symbol_index": {
|
||||
"feat-auth": [
|
||||
"deepwiki:symbol:src/auth/jwt.ts#L30-L55",
|
||||
"deepwiki:symbol:src/models/user.ts#L12-L40"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Symbol URN format**: `deepwiki:symbol:<file_path>#L<start>-L<end>`
|
||||
|
||||
**Graceful degradation**: If DeepWiki is unavailable, set `deepwiki_feature_to_symbol_index: {}` and log warning.
|
||||
|
||||
## Phase 8: Generate Documents
|
||||
|
||||
Delegate all document generation to `/ddd:doc-generate`:
|
||||
|
||||
```
|
||||
Invoke /ddd:doc-generate [-y]
|
||||
```
|
||||
|
||||
This generates the complete document tree (Layer 3 → 2 → 1):
|
||||
- `tech-registry/{slug}.md` — component docs (Layer 3)
|
||||
- `feature-maps/{slug}.md` — feature docs (Layer 2)
|
||||
- `_index.md`, `README.md`, `ARCHITECTURE.md`, `SCHEMA.md` — index/overview docs (Layer 1)
|
||||
|
||||
See `/ddd:doc-generate` for full details on generation strategy and flags.
|
||||
|
||||
## Phase 9: Validation & Report
|
||||
|
||||
```
|
||||
Scan Report
|
||||
|
||||
Project: {name} ({language}/{framework})
|
||||
Architecture: {pattern}
|
||||
Source dirs: {N}
|
||||
|
||||
Discovered:
|
||||
Components: {N} ({by type breakdown})
|
||||
Features: {N} (inferred)
|
||||
Requirements: {N} (IREQ, inferred)
|
||||
Architecture Decisions: {N} (IADR, inferred)
|
||||
Historical Actions: {N} (from git)
|
||||
|
||||
Coverage:
|
||||
Components → Features: {%}
|
||||
Dependencies mapped: {%}
|
||||
|
||||
Recommendations:
|
||||
- Run /spec-generator to formalize {N} inferred requirements
|
||||
- {N} components have unclear responsibility — review tech-registry docs
|
||||
- Use /ddd:plan to start planning tasks with this index
|
||||
```
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Effect |
|
||||
|------|--------|
|
||||
| `-y, --yes` | Auto-confirm all decisions |
|
||||
| `--from-scratch` | Delete existing index and rebuild |
|
||||
| `--scope <dir>` | Limit scan to specific directory (e.g., `--scope src/auth`) |
|
||||
|
||||
## Upgrade Path: scan → spec
|
||||
|
||||
When a scanned project later runs `spec-generator` + `/ddd:index-build`:
|
||||
- `/ddd:index-build` detects existing code-first index
|
||||
- Merges: `IREQ-NNN` → `REQ-NNN`, `IADR-NNN` → `ADR-NNN` where content overlaps
|
||||
- Updates `build_path` to `"spec-first"`
|
||||
- Preserves all `tech-*` and `feat-*` entries (updates links only)
|
||||
|
||||
## Integration Points
|
||||
|
||||
- **Input from**: Codebase, git history, `project-tech.json`
|
||||
- **Delegates to**: `/ddd:doc-generate` (Phase 8, full document generation)
|
||||
- **Output to**: `ddd:plan`, `ddd:sync`, `ddd:update`, `ddd:index-build` (upgrade)
|
||||
- **Standalone**: Can be used independently on any project
|
||||
@@ -1,353 +0,0 @@
|
||||
---
|
||||
name: sync
|
||||
description: Post-task synchronization - update document index, generate action log, and refresh feature/component docs after completing a development task.
|
||||
argument-hint: "[-y|--yes] [--dry-run] [--from-manifest <path>] [--task-id <id>] [--commit <hash>] \"task summary\""
|
||||
allowed-tools: TodoWrite(*), Agent(*), AskUserQuestion(*), Read(*), Grep(*), Glob(*), Bash(*), Edit(*), Write(*), mcp__ace-tool__search_context(*)
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Auto-detect changes, auto-update all docs, skip review prompts.
|
||||
|
||||
# DDD Sync Command (/ddd:sync)
|
||||
|
||||
## Purpose
|
||||
|
||||
After completing a development task, synchronize the document index with actual code changes:
|
||||
1. **Analyze** what changed (git diff)
|
||||
2. **Trace** which features/requirements/components are affected
|
||||
3. **Update** index entries (status, code locations, links)
|
||||
4. **Generate** action log entry
|
||||
5. **Refresh** feature-map and tech-registry documents
|
||||
|
||||
## When to Use: sync vs update
|
||||
|
||||
| Scenario | Use |
|
||||
|----------|-----|
|
||||
| Task completed, ready to commit | **ddd:sync** — full post-task reconciliation |
|
||||
| Mid-development, quick impact check | ddd:update |
|
||||
| Pre-commit validation | ddd:update --check-only |
|
||||
| Auto-triggered after ddd:execute | **ddd:sync** (automatic) |
|
||||
| Periodic index refresh during refactoring | ddd:update |
|
||||
|
||||
**Rule of thumb**: `sync` = task boundary (done something), `update` = development pulse (doing something).
|
||||
|
||||
## Prerequisite
|
||||
|
||||
- `doc-index.json` must exist
|
||||
- Git repository with committed or staged changes
|
||||
|
||||
## Phase 0: Consistency Validation
|
||||
|
||||
Before processing changes, verify that `doc-index.json` entries are consistent with actual code state.
|
||||
|
||||
### 0.1 Validate Code Locations
|
||||
|
||||
For each `technicalComponents[].codeLocations[]`:
|
||||
- Verify file exists on disk
|
||||
- If file was deleted/moved → flag for removal or update
|
||||
- If file exists → verify listed `symbols[]` still exist (quick grep/AST check)
|
||||
|
||||
### 0.2 Validate Symbols
|
||||
|
||||
For components with `codeLocations[].symbols[]`:
|
||||
- Check each symbol still exists in the referenced file
|
||||
- Detect new exported symbols not yet tracked
|
||||
- Report: `{N} stale symbols, {N} untracked symbols`
|
||||
|
||||
### 0.3 Validation Report
|
||||
|
||||
```
|
||||
Consistency Check:
|
||||
Components validated: {N}
|
||||
Files verified: {N}
|
||||
Stale references: {N} (files missing or symbols removed)
|
||||
Untracked symbols: {N} (new exports not in index)
|
||||
```
|
||||
|
||||
If stale references found: warn and auto-fix during Phase 3 updates.
|
||||
If `--dry-run`: report only, no fixes.
|
||||
|
||||
## Phase 1: Change Detection
|
||||
|
||||
### 1.0.1 Schema Version Check
|
||||
|
||||
Before processing changes, verify doc-index.json schema compatibility:
|
||||
|
||||
```javascript
|
||||
const docIndex = JSON.parse(Read('.workflow/.doc-index/doc-index.json'));
|
||||
const schemaVersion = docIndex.schema_version || '0.0'; // Default for legacy
|
||||
|
||||
if (schemaVersion !== '1.0') {
|
||||
console.warn(`Schema version mismatch: found ${schemaVersion}, expected 1.0`);
|
||||
console.warn('Consider running schema migration or regenerating doc-index with /ddd:scan');
|
||||
// Continue with degraded functionality - may encounter missing fields
|
||||
}
|
||||
```
|
||||
|
||||
**Graceful degradation**: If version mismatch detected → log warning → continue with caution (some features may not work as expected).
|
||||
|
||||
### 1.0 Data Source Selection
|
||||
|
||||
```
|
||||
IF --from-manifest <path>:
|
||||
Load execution-manifest.json
|
||||
→ files_modified[] provides precise file list + action type + task attribution
|
||||
→ TASK-*.result.json provides symbol-level changes + convergence results
|
||||
→ Skip Phase 1.1/1.2 (already classified by execute)
|
||||
→ Proceed directly to Phase 2 with manifest data
|
||||
ELSE:
|
||||
→ Fall through to Phase 1.1 (git-based discovery)
|
||||
```
|
||||
|
||||
**`--from-manifest` advantages** (used automatically by ddd:execute):
|
||||
- Precise file → task attribution (which task modified which file)
|
||||
- Symbol-level change tracking (not just file-level)
|
||||
- Convergence verification results carried forward to action-log
|
||||
- Survives process interruptions (manifest is persisted to disk)
|
||||
|
||||
### 1.1 Identify Changes (git-based fallback)
|
||||
|
||||
```bash
|
||||
# If --commit provided:
|
||||
git diff --name-only {commit}^..{commit}
|
||||
git diff --stat {commit}^..{commit}
|
||||
|
||||
# If --task-id provided, find related commits:
|
||||
git log --oneline --grep="task-{id}" | head -10
|
||||
|
||||
# Otherwise: changes since last ddd:sync
|
||||
git diff --name-only HEAD~1..HEAD
|
||||
```
|
||||
|
||||
### 1.2 Classify Changes (git-based fallback)
|
||||
|
||||
For each changed file, determine:
|
||||
- **Type**: added | modified | deleted | renamed
|
||||
- **Category**: source | test | config | docs | other
|
||||
- **Symbols affected**: parse diff for changed functions/classes (use Gemini if complex)
|
||||
|
||||
## Phase 2: Impact Tracing (Layer-Based, TASK-004)
|
||||
|
||||
**Strategy**: Trace impact through layers (files → components → features → indexes) following memory-manage pattern.
|
||||
|
||||
### 2.1 Match to Index
|
||||
|
||||
For each changed file path:
|
||||
|
||||
```
|
||||
Search doc-index.json.technicalComponents[].codeLocations[].path
|
||||
→ Find matching component IDs (Layer 3)
|
||||
→ From components, find linked featureIds (Layer 2)
|
||||
→ From features, find linked requirementIds (Layer 2)
|
||||
```
|
||||
|
||||
### 2.2 Discover New Components
|
||||
|
||||
If changed files don't match any existing component:
|
||||
- Flag as potential new component
|
||||
- Ask user if it should be registered (or auto-register with `-y`)
|
||||
|
||||
### 2.3 Build Impact Report
|
||||
|
||||
```markdown
|
||||
## Impact Summary
|
||||
|
||||
### Changed Files (5)
|
||||
- src/services/auth.ts (modified) → tech-auth-service → feat-auth
|
||||
- src/models/user.ts (modified) → tech-user-model → feat-auth
|
||||
- src/routes/login.ts (added) → NEW COMPONENT → feat-auth
|
||||
- src/tests/auth.test.ts (modified) → [test file, skip]
|
||||
- package.json (modified) → [config, skip]
|
||||
|
||||
### Affected Features
|
||||
- feat-auth: User Authentication (2 components modified, 1 new)
|
||||
|
||||
### Affected Requirements
|
||||
- REQ-001: Email login (implementation updated)
|
||||
- REQ-002: JWT token generation (implementation updated)
|
||||
```
|
||||
|
||||
## Phase 2.4: DeepWiki Freshness Check
|
||||
|
||||
If DeepWiki integration is configured (`doc-index.json.freshness` exists):
|
||||
|
||||
### 2.4.1 Identify Modified Files
|
||||
From `execution-manifest.json` or git diff, collect `files_modified[]` with `action == "modified"`.
|
||||
|
||||
### 2.4.2 Check Staleness
|
||||
Query DeepWiki: `POST /api/deepwiki/stale-files { files: [{path, hash}] }`
|
||||
|
||||
For each stale file's symbols, calculate staleness score:
|
||||
```
|
||||
S(symbol) = min(1.0, w_t × T + w_c × C + w_s × M)
|
||||
```
|
||||
Where weights come from `doc-index.json.freshness.weights`.
|
||||
|
||||
### 2.4.3 Score Propagation (max aggregation)
|
||||
```
|
||||
S_file = max(S_symbol_1, S_symbol_2, ...)
|
||||
S_component = max(S_file_1, S_file_2, ...)
|
||||
S_feature = max(S_component_1, S_component_2, ...)
|
||||
```
|
||||
|
||||
### 2.4.4 Status Assignment
|
||||
Using thresholds from `doc-index.json.freshness.thresholds`:
|
||||
- `fresh`: score in [0, warning_threshold)
|
||||
- `warning`: score in [warning_threshold, stale_threshold)
|
||||
- `stale`: score in [stale_threshold, 1.0]
|
||||
|
||||
### 2.4.5 Update Records
|
||||
- Update `deepwiki_symbols.staleness_score` and `deepwiki_files.staleness_score` in DeepWiki SQLite
|
||||
- Update `tech-registry/{slug}.md` YAML frontmatter with freshness block
|
||||
- Update `feature-maps/{slug}.md` YAML frontmatter with freshness block
|
||||
- Update `deepwiki_feature_to_symbol_index` in doc-index.json
|
||||
|
||||
### 2.4.6 Staleness Report
|
||||
Add to action-log:
|
||||
- Number of stale symbols/files/components
|
||||
- Top-5 most stale items with scores
|
||||
- Auto-regeneration candidates (if `auto_regenerate: true` and score >= stale threshold)
|
||||
|
||||
## Phase 3: Update Index
|
||||
|
||||
### 3.0 Dry-Run Gate
|
||||
|
||||
If `--dry-run` is set:
|
||||
- Execute Phase 3 analysis (determine what would change)
|
||||
- Display planned modifications as a preview report
|
||||
- Skip all file writes (Phase 3.1-3.5 and Phase 4)
|
||||
- Output: "Dry-run complete. Run without --dry-run to apply changes."
|
||||
|
||||
### 3.0.1 Backup Index
|
||||
|
||||
Before any modifications, create backup:
|
||||
- Copy `doc-index.json` → `doc-index.json.bak`
|
||||
- On failure: restore from `.bak` and report error
|
||||
- On success: remove `.bak`
|
||||
|
||||
### 3.1 Update Technical Components
|
||||
|
||||
For each affected component in `doc-index.json`:
|
||||
- Update `codeLocations` if file paths or line ranges changed
|
||||
- Update `symbols` if new exports were added
|
||||
- Add new `actionIds` entry
|
||||
- **Auto-update `responsibility`**: If symbols changed (new methods/exports added or removed), re-infer responsibility from current symbols list using Gemini analysis. This prevents stale descriptions (e.g., responsibility still says "登录、注册" after adding logout support)
|
||||
|
||||
### 3.2 Register New Components
|
||||
|
||||
For newly discovered components:
|
||||
- Generate `tech-{slug}` ID
|
||||
- Create entry in `technicalComponents[]`
|
||||
- Link to appropriate features
|
||||
- Generate new `tech-registry/{slug}.md` document
|
||||
|
||||
### 3.3 Update Feature Status
|
||||
|
||||
For each affected feature:
|
||||
- If all requirements now have mapped components → `status: "implemented"`
|
||||
- If some requirements still unmapped → `status: "in-progress"`
|
||||
|
||||
### 3.4 Add Action Entry
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "task-{id}",
|
||||
"description": "{task summary from user}",
|
||||
"type": "feature|bugfix|refactor",
|
||||
"status": "completed",
|
||||
"affectedFeatures": ["feat-auth"],
|
||||
"affectedComponents": ["tech-auth-service", "tech-user-model"],
|
||||
"changedFiles": [
|
||||
{ "path": "src/services/auth.ts", "action": "modified", "task_id": "TASK-001" },
|
||||
{ "path": "src/models/user.ts", "action": "modified", "task_id": "TASK-001" }
|
||||
],
|
||||
"symbolsChanged": ["AuthService.validate", "UserModel.toJSON"],
|
||||
"convergenceResults": {
|
||||
"passed": 2,
|
||||
"total": 2,
|
||||
"details": ["Rate limiter middleware exists", "Config accepts per-route limits"]
|
||||
},
|
||||
"verifyGate": "PASS|WARN|FAIL|skipped",
|
||||
"relatedCommit": "{commit hash}",
|
||||
"manifestPath": "{execution-manifest.json path | null}",
|
||||
"timestamp": "ISO8601"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.5 Update Timestamp
|
||||
|
||||
Set `doc-index.json.last_updated` to current time.
|
||||
|
||||
## Phase 4: Refresh Documents & Action Log
|
||||
|
||||
### 4.1 Delegate Document Refresh to /ddd:doc-refresh
|
||||
|
||||
From Phase 2 impact tracing, collect affected component and feature IDs, then delegate:
|
||||
|
||||
```
|
||||
Invoke /ddd:doc-refresh [-y] --components {affected_component_ids} --features {affected_feature_ids}
|
||||
```
|
||||
|
||||
This handles Layer 3 → 2 → 1 selective document refresh. See `/ddd:doc-refresh` for full details.
|
||||
|
||||
### 4.2 Generate Action Log Entry
|
||||
|
||||
Create `.workflow/.doc-index/action-logs/{task-id}.md`:
|
||||
|
||||
```markdown
|
||||
---
|
||||
id: task-{id}
|
||||
type: feature|bugfix|refactor
|
||||
status: completed
|
||||
features: [feat-auth]
|
||||
components: [tech-auth-service, tech-user-model]
|
||||
commit: {hash}
|
||||
timestamp: ISO8601
|
||||
---
|
||||
|
||||
# Task: {summary}
|
||||
|
||||
## Changes
|
||||
| File | Type | Component |
|
||||
|------|------|-----------|
|
||||
| src/services/auth.ts | modified | tech-auth-service |
|
||||
|
||||
## Impact
|
||||
- Features affected: feat-auth
|
||||
- Requirements addressed: REQ-001, REQ-002
|
||||
|
||||
## Staleness (if DeepWiki freshness enabled)
|
||||
| Item | Type | Score | Status |
|
||||
|------|------|-------|--------|
|
||||
| {symbol/file/component} | {type} | {score} | {fresh/warning/stale} |
|
||||
|
||||
## Notes
|
||||
{any user-provided notes}
|
||||
```
|
||||
|
||||
## Phase 5: Confirmation (unless -y)
|
||||
|
||||
Present update summary to user:
|
||||
- Files updated in doc-index
|
||||
- New documents created
|
||||
- Status changes
|
||||
- Ask for confirmation before writing
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Effect |
|
||||
|------|--------|
|
||||
| `-y, --yes` | Auto-confirm all updates |
|
||||
| `--dry-run` | Preview all changes without modifying any files |
|
||||
| `--from-manifest <path>` | Use execution-manifest.json as data source (auto-set by ddd:execute) |
|
||||
| `--task-id <id>` | Associate with specific task ID |
|
||||
| `--commit <hash>` | Analyze specific commit |
|
||||
|
||||
## Integration Points
|
||||
|
||||
- **Input from**: `execution-manifest.json` (preferred, from ddd:execute) OR Git history (fallback), `doc-index.json`, `/ddd:plan` output
|
||||
- **Delegates to**: `/ddd:doc-refresh` (Phase 4.1, selective document refresh)
|
||||
- **Output to**: Updated `doc-index.json`, feature-maps/, tech-registry/, action-logs/
|
||||
- **Triggers**: After completing any development task
|
||||
- **Data source priority**: `--from-manifest` > `--commit` > `--task-id` > git diff HEAD~1
|
||||
@@ -1,160 +0,0 @@
|
||||
---
|
||||
name: update
|
||||
description: Incremental index update - detect code changes and trace impact to related features/requirements. Lightweight alternative to full sync.
|
||||
argument-hint: "[-y|--yes] [--files <file1,file2,...>] [--staged] [--check-only]"
|
||||
allowed-tools: TodoWrite(*), AskUserQuestion(*), Read(*), Grep(*), Glob(*), Bash(*), Edit(*), Write(*), mcp__ace-tool__search_context(*)
|
||||
---
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Auto-update index without confirmation prompts.
|
||||
|
||||
# DDD Update Command (/ddd:update)
|
||||
|
||||
## Purpose
|
||||
|
||||
Lightweight incremental update: given a set of changed files, trace their impact through the document index and update affected entries. Unlike `/ddd:sync` (full post-task sync), this command focuses on keeping the index fresh during development.
|
||||
|
||||
## When to Use: update vs sync
|
||||
|
||||
| Scenario | Use |
|
||||
|----------|-----|
|
||||
| Quick impact check during development | **ddd:update** |
|
||||
| Preview what sync would change | **ddd:update --check-only** |
|
||||
| Task completed, full reconciliation | ddd:sync |
|
||||
| Register new components + update all docs | ddd:sync |
|
||||
|
||||
**Rule of thumb**: `update` = lightweight pulse (during work), `sync` = full checkpoint (after work).
|
||||
|
||||
## Use Cases
|
||||
|
||||
1. **During development**: Quick check which docs are affected by current changes
|
||||
2. **Pre-commit check**: Ensure index is up-to-date before committing
|
||||
3. **Periodic refresh**: Update stale code locations after refactoring
|
||||
|
||||
## Prerequisite
|
||||
|
||||
- `doc-index.json` must exist at `.workflow/.doc-index/doc-index.json`
|
||||
|
||||
## Phase 1: Identify Changed Files
|
||||
|
||||
### Source Priority
|
||||
|
||||
```
|
||||
1. --files <list> → Explicit file list
|
||||
2. --staged → git diff --cached --name-only
|
||||
3. (default) → git diff --name-only (unstaged changes)
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
List of changed file paths with change type (added/modified/deleted/renamed).
|
||||
|
||||
## Phase 2: Trace Impact
|
||||
|
||||
### 2.1 Forward Lookup (Code → Components → Features)
|
||||
|
||||
For each changed file:
|
||||
|
||||
```
|
||||
doc-index.json.technicalComponents[]
|
||||
.codeLocations[].path MATCH changed_file
|
||||
→ component_ids[]
|
||||
|
||||
doc-index.json.technicalComponents[component_ids]
|
||||
.featureIds[]
|
||||
→ feature_ids[]
|
||||
|
||||
doc-index.json.features[feature_ids]
|
||||
.requirementIds[]
|
||||
→ requirement_ids[]
|
||||
```
|
||||
|
||||
### 2.2 Orphan Detection
|
||||
|
||||
Files not matching any component → flag as:
|
||||
- **Potential new component**: if in src/ directory
|
||||
- **Ignorable**: if in test/, docs/, config/ directories
|
||||
|
||||
### 2.3 Impact Report
|
||||
|
||||
```
|
||||
Impact Analysis for 3 changed files:
|
||||
|
||||
src/services/auth.ts (modified)
|
||||
→ Component: tech-auth-service (AuthService)
|
||||
→ Feature: feat-auth (User Authentication)
|
||||
→ Requirements: REQ-001, REQ-002
|
||||
|
||||
src/middleware/rate-limit.ts (added)
|
||||
→ No matching component (new file)
|
||||
→ Suggested: Register as new component
|
||||
|
||||
src/utils/hash.ts (modified)
|
||||
→ Component: tech-hash-util
|
||||
→ Features: feat-auth, feat-password-reset
|
||||
→ Requirements: REQ-001, REQ-005
|
||||
```
|
||||
|
||||
## Phase 3: Update Index (unless --check-only)
|
||||
|
||||
### 3.1 Update Code Locations
|
||||
|
||||
For matched components:
|
||||
- If file was renamed → update `codeLocations[].path`
|
||||
- If file was deleted → remove code location entry
|
||||
- If symbols changed → update `symbols` list (requires AST or Gemini analysis)
|
||||
|
||||
### 3.2 Register New Components (interactive unless -y)
|
||||
|
||||
For orphan files in src/:
|
||||
- Prompt user for component name and type
|
||||
- Or auto-generate with `-y`: derive name from file path
|
||||
- Create `technicalComponents[]` entry
|
||||
- Ask which feature it belongs to (or auto-link by directory structure)
|
||||
|
||||
### 3.3 Update Timestamps
|
||||
|
||||
- Update `technicalComponents[].docPath` last_updated in corresponding .md
|
||||
- Update `doc-index.json.last_updated`
|
||||
|
||||
## Phase 4: Refresh Documents (if updates were made)
|
||||
|
||||
### 4.1 Delegate to /ddd:doc-refresh
|
||||
|
||||
From Phase 2 impact tracing, collect affected component and feature IDs, then delegate:
|
||||
|
||||
```
|
||||
Invoke /ddd:doc-refresh [-y] --minimal --components {affected_component_ids} --features {affected_feature_ids}
|
||||
```
|
||||
|
||||
The `--minimal` flag ensures only metadata/frontmatter is updated (code locations, timestamps), skipping full content regeneration. This keeps the update lightweight.
|
||||
|
||||
See `/ddd:doc-refresh` for full details.
|
||||
|
||||
### 4.2 Skip If --check-only
|
||||
|
||||
With `--check-only`, skip Phase 3 and Phase 4 entirely — only output the impact report.
|
||||
|
||||
## Flags
|
||||
|
||||
| Flag | Effect |
|
||||
|------|--------|
|
||||
| `-y, --yes` | Auto-confirm updates |
|
||||
| `--files <list>` | Explicit comma-separated file list |
|
||||
| `--staged` | Analyze staged (git cached) files |
|
||||
| `--check-only` | Report impact without modifying index |
|
||||
|
||||
## Output
|
||||
|
||||
- **Console**: Impact report showing affected features/requirements
|
||||
- **Updated**: `doc-index.json` (if not --check-only)
|
||||
- **Updated**: Affected tech-registry/ and feature-maps/ docs
|
||||
|
||||
## Integration Points
|
||||
|
||||
- **Input from**: Git working tree, `doc-index.json`
|
||||
- **Delegates to**: `/ddd:doc-refresh` (Phase 4.1, incremental document refresh with --minimal)
|
||||
- **Output to**: Updated `doc-index.json`, impact report
|
||||
- **Triggers**: During development, pre-commit, or periodic refresh
|
||||
- **Can chain to**: `/ddd:sync` for full post-task synchronization
|
||||
@@ -1,287 +0,0 @@
|
||||
---
|
||||
name: add
|
||||
description: Add IDAW tasks - manual creation or import from ccw issue
|
||||
argument-hint: "[-y|--yes] [--from-issue <id>[,<id>,...]] \"description\" [--type <task_type>] [--priority <1-5>]"
|
||||
allowed-tools: AskUserQuestion(*), Read(*), Bash(*), Write(*), Glob(*)
|
||||
---
|
||||
|
||||
# IDAW Add Command (/idaw:add)
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Skip clarification questions, create task with inferred details.
|
||||
|
||||
## IDAW Task Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "IDAW-001",
|
||||
"title": "string",
|
||||
"description": "string",
|
||||
"status": "pending",
|
||||
"priority": 2,
|
||||
"task_type": null,
|
||||
"skill_chain": null,
|
||||
"context": {
|
||||
"affected_files": [],
|
||||
"acceptance_criteria": [],
|
||||
"constraints": [],
|
||||
"references": []
|
||||
},
|
||||
"source": {
|
||||
"type": "manual|import-issue",
|
||||
"issue_id": null,
|
||||
"issue_snapshot": null
|
||||
},
|
||||
"execution": {
|
||||
"session_id": null,
|
||||
"started_at": null,
|
||||
"completed_at": null,
|
||||
"skill_results": [],
|
||||
"git_commit": null,
|
||||
"error": null
|
||||
},
|
||||
"created_at": "ISO",
|
||||
"updated_at": "ISO"
|
||||
}
|
||||
```
|
||||
|
||||
**Valid task_type values**: `bugfix|bugfix-hotfix|feature|feature-complex|refactor|tdd|test|test-fix|review|docs`
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Parse Arguments
|
||||
|
||||
```javascript
|
||||
const args = $ARGUMENTS;
|
||||
const autoYes = /(-y|--yes)\b/.test(args);
|
||||
const fromIssue = args.match(/--from-issue\s+([\w,-]+)/)?.[1];
|
||||
const typeFlag = args.match(/--type\s+([\w-]+)/)?.[1];
|
||||
const priorityFlag = args.match(/--priority\s+(\d)/)?.[1];
|
||||
|
||||
// Extract description: content inside quotes (preferred), or fallback to stripping flags
|
||||
const quotedMatch = args.match(/(?:^|\s)["']([^"']+)["']/);
|
||||
const description = quotedMatch
|
||||
? quotedMatch[1].trim()
|
||||
: args.replace(/(-y|--yes|--from-issue\s+[\w,-]+|--type\s+[\w-]+|--priority\s+\d)/g, '').trim();
|
||||
```
|
||||
|
||||
### Phase 2: Route — Import or Manual
|
||||
|
||||
```
|
||||
--from-issue present?
|
||||
├─ YES → Import Mode (Phase 3A)
|
||||
└─ NO → Manual Mode (Phase 3B)
|
||||
```
|
||||
|
||||
### Phase 3A: Import Mode (from ccw issue)
|
||||
|
||||
```javascript
|
||||
const issueIds = fromIssue.split(',');
|
||||
|
||||
// Fetch all issues once (outside loop)
|
||||
let issues = [];
|
||||
try {
|
||||
const issueJson = Bash(`ccw issue list --json`);
|
||||
issues = JSON.parse(issueJson).issues || [];
|
||||
} catch (e) {
|
||||
console.log(`Error fetching CCW issues: ${e.message || e}`);
|
||||
console.log('Ensure ccw is installed and issues exist. Use /issue:new to create issues first.');
|
||||
return;
|
||||
}
|
||||
|
||||
for (const issueId of issueIds) {
|
||||
// 1. Find issue data
|
||||
const issue = issues.find(i => i.id === issueId.trim());
|
||||
if (!issue) {
|
||||
console.log(`Warning: Issue ${issueId} not found, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 2. Check duplicate (same issue_id already imported)
|
||||
const existing = Glob('.workflow/.idaw/tasks/IDAW-*.json');
|
||||
for (const f of existing) {
|
||||
const data = JSON.parse(Read(f));
|
||||
if (data.source?.issue_id === issueId.trim()) {
|
||||
console.log(`Warning: Issue ${issueId} already imported as ${data.id}, skipping`);
|
||||
continue; // skip to next issue
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Generate next IDAW ID
|
||||
const nextId = generateNextId();
|
||||
|
||||
// 4. Map issue → IDAW task
|
||||
const task = {
|
||||
id: nextId,
|
||||
title: issue.title,
|
||||
description: issue.context || issue.title,
|
||||
status: 'pending',
|
||||
priority: parseInt(priorityFlag) || issue.priority || 3,
|
||||
task_type: typeFlag || inferTaskType(issue.title, issue.context || ''),
|
||||
skill_chain: null,
|
||||
context: {
|
||||
affected_files: issue.affected_components || [],
|
||||
acceptance_criteria: [],
|
||||
constraints: [],
|
||||
references: issue.source_url ? [issue.source_url] : []
|
||||
},
|
||||
source: {
|
||||
type: 'import-issue',
|
||||
issue_id: issue.id,
|
||||
issue_snapshot: {
|
||||
id: issue.id,
|
||||
title: issue.title,
|
||||
status: issue.status,
|
||||
context: issue.context,
|
||||
priority: issue.priority,
|
||||
created_at: issue.created_at
|
||||
}
|
||||
},
|
||||
execution: {
|
||||
session_id: null,
|
||||
started_at: null,
|
||||
completed_at: null,
|
||||
skill_results: [],
|
||||
git_commit: null,
|
||||
error: null
|
||||
},
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
// 5. Write task file
|
||||
Bash('mkdir -p .workflow/.idaw/tasks');
|
||||
Write(`.workflow/.idaw/tasks/${nextId}.json`, JSON.stringify(task, null, 2));
|
||||
console.log(`Created ${nextId} from issue ${issueId}: ${issue.title}`);
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 3B: Manual Mode
|
||||
|
||||
```javascript
|
||||
// 1. Validate description
|
||||
if (!description && !autoYes) {
|
||||
const answer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Please provide a task description:',
|
||||
header: 'Task',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Provide description', description: 'What needs to be done?' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
// Use custom text from "Other"
|
||||
description = answer.customText || '';
|
||||
}
|
||||
|
||||
if (!description) {
|
||||
console.log('Error: No description provided. Usage: /idaw:add "task description"');
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Generate next IDAW ID
|
||||
const nextId = generateNextId();
|
||||
|
||||
// 3. Build title from first sentence
|
||||
const title = description.split(/[.\n]/)[0].substring(0, 80).trim();
|
||||
|
||||
// 4. Determine task_type
|
||||
const taskType = typeFlag || null; // null → inferred at run time
|
||||
|
||||
// 5. Create task
|
||||
const task = {
|
||||
id: nextId,
|
||||
title: title,
|
||||
description: description,
|
||||
status: 'pending',
|
||||
priority: parseInt(priorityFlag) || 3,
|
||||
task_type: taskType,
|
||||
skill_chain: null,
|
||||
context: {
|
||||
affected_files: [],
|
||||
acceptance_criteria: [],
|
||||
constraints: [],
|
||||
references: []
|
||||
},
|
||||
source: {
|
||||
type: 'manual',
|
||||
issue_id: null,
|
||||
issue_snapshot: null
|
||||
},
|
||||
execution: {
|
||||
session_id: null,
|
||||
started_at: null,
|
||||
completed_at: null,
|
||||
skill_results: [],
|
||||
git_commit: null,
|
||||
error: null
|
||||
},
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
|
||||
Bash('mkdir -p .workflow/.idaw/tasks');
|
||||
Write(`.workflow/.idaw/tasks/${nextId}.json`, JSON.stringify(task, null, 2));
|
||||
console.log(`Created ${nextId}: ${title}`);
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
### ID Generation
|
||||
|
||||
```javascript
|
||||
function generateNextId() {
|
||||
const files = Glob('.workflow/.idaw/tasks/IDAW-*.json') || [];
|
||||
if (files.length === 0) return 'IDAW-001';
|
||||
|
||||
const maxNum = files
|
||||
.map(f => parseInt(f.match(/IDAW-(\d+)/)?.[1] || '0'))
|
||||
.reduce((max, n) => Math.max(max, n), 0);
|
||||
|
||||
return `IDAW-${String(maxNum + 1).padStart(3, '0')}`;
|
||||
}
|
||||
```
|
||||
|
||||
### Task Type Inference (deferred — used at run time if task_type is null)
|
||||
|
||||
```javascript
|
||||
function inferTaskType(title, description) {
|
||||
const text = `${title} ${description}`.toLowerCase();
|
||||
if (/urgent|production|critical/.test(text) && /fix|bug/.test(text)) return 'bugfix-hotfix';
|
||||
if (/refactor|重构|tech.*debt/.test(text)) return 'refactor';
|
||||
if (/tdd|test-driven|test first/.test(text)) return 'tdd';
|
||||
if (/test fail|fix test|failing test/.test(text)) return 'test-fix';
|
||||
if (/generate test|写测试|add test/.test(text)) return 'test';
|
||||
if (/review|code review/.test(text)) return 'review';
|
||||
if (/docs|documentation|readme/.test(text)) return 'docs';
|
||||
if (/fix|bug|error|crash|fail/.test(text)) return 'bugfix';
|
||||
if (/complex|multi-module|architecture/.test(text)) return 'feature-complex';
|
||||
return 'feature';
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# Manual creation
|
||||
/idaw:add "Fix login timeout bug" --type bugfix --priority 2
|
||||
/idaw:add "Add rate limiting to API endpoints" --priority 1
|
||||
/idaw:add "Refactor auth module to use strategy pattern"
|
||||
|
||||
# Import from ccw issue
|
||||
/idaw:add --from-issue ISS-20260128-001
|
||||
/idaw:add --from-issue ISS-20260128-001,ISS-20260128-002 --priority 1
|
||||
|
||||
# Auto mode (skip clarification)
|
||||
/idaw:add -y "Quick fix for typo in header"
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
```
|
||||
Created IDAW-001: Fix login timeout bug
|
||||
Type: bugfix | Priority: 2 | Source: manual
|
||||
→ Next: /idaw:run or /idaw:status
|
||||
```
|
||||
@@ -1,442 +0,0 @@
|
||||
---
|
||||
name: resume
|
||||
description: Resume interrupted IDAW session from last checkpoint
|
||||
argument-hint: "[-y|--yes] [session-id]"
|
||||
allowed-tools: Skill(*), TodoWrite(*), AskUserQuestion(*), Read(*), Write(*), Bash(*), Glob(*)
|
||||
---
|
||||
|
||||
# IDAW Resume Command (/idaw:resume)
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Auto-skip interrupted task, continue with remaining.
|
||||
|
||||
## Skill Chain Mapping
|
||||
|
||||
```javascript
|
||||
const SKILL_CHAIN_MAP = {
|
||||
'bugfix': ['workflow-lite-plan', 'workflow-test-fix'],
|
||||
'bugfix-hotfix': ['workflow-lite-plan'],
|
||||
'feature': ['workflow-lite-plan', 'workflow-test-fix'],
|
||||
'feature-complex': ['workflow-plan', 'workflow-execute', 'workflow-test-fix'],
|
||||
'refactor': ['workflow:refactor-cycle'],
|
||||
'tdd': ['workflow-tdd-plan', 'workflow-execute'],
|
||||
'test': ['workflow-test-fix'],
|
||||
'test-fix': ['workflow-test-fix'],
|
||||
'review': ['review-cycle'],
|
||||
'docs': ['workflow-lite-plan']
|
||||
};
|
||||
```
|
||||
|
||||
## Task Type Inference
|
||||
|
||||
```javascript
|
||||
function inferTaskType(title, description) {
|
||||
const text = `${title} ${description}`.toLowerCase();
|
||||
if (/urgent|production|critical/.test(text) && /fix|bug/.test(text)) return 'bugfix-hotfix';
|
||||
if (/refactor|重构|tech.*debt/.test(text)) return 'refactor';
|
||||
if (/tdd|test-driven|test first/.test(text)) return 'tdd';
|
||||
if (/test fail|fix test|failing test/.test(text)) return 'test-fix';
|
||||
if (/generate test|写测试|add test/.test(text)) return 'test';
|
||||
if (/review|code review/.test(text)) return 'review';
|
||||
if (/docs|documentation|readme/.test(text)) return 'docs';
|
||||
if (/fix|bug|error|crash|fail/.test(text)) return 'bugfix';
|
||||
if (/complex|multi-module|architecture/.test(text)) return 'feature-complex';
|
||||
return 'feature';
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Find Resumable Session
|
||||
|
||||
```javascript
|
||||
const args = $ARGUMENTS;
|
||||
const autoYes = /(-y|--yes)/.test(args);
|
||||
const targetSessionId = args.replace(/(-y|--yes)/g, '').trim();
|
||||
|
||||
let session = null;
|
||||
let sessionDir = null;
|
||||
|
||||
if (targetSessionId) {
|
||||
// Load specific session
|
||||
sessionDir = `.workflow/.idaw/sessions/${targetSessionId}`;
|
||||
try {
|
||||
session = JSON.parse(Read(`${sessionDir}/session.json`));
|
||||
} catch {
|
||||
console.log(`Session "${targetSessionId}" not found.`);
|
||||
console.log('Use /idaw:status to list sessions, or /idaw:run to start a new one.');
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Find most recent running session
|
||||
const sessionFiles = Glob('.workflow/.idaw/sessions/IDA-*/session.json') || [];
|
||||
|
||||
for (const f of sessionFiles) {
|
||||
try {
|
||||
const s = JSON.parse(Read(f));
|
||||
if (s.status === 'running') {
|
||||
session = s;
|
||||
sessionDir = f.replace(/\/session\.json$/, '').replace(/\\session\.json$/, '');
|
||||
break;
|
||||
}
|
||||
} catch {
|
||||
// Skip malformed
|
||||
}
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
console.log('No running sessions found to resume.');
|
||||
console.log('Use /idaw:run to start a new execution.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Resuming session: ${session.session_id}`);
|
||||
```
|
||||
|
||||
### Phase 2: Handle Interrupted Task
|
||||
|
||||
```javascript
|
||||
// Find the task that was in_progress when interrupted
|
||||
let currentTaskId = session.current_task;
|
||||
let currentTask = null;
|
||||
|
||||
if (currentTaskId) {
|
||||
try {
|
||||
currentTask = JSON.parse(Read(`.workflow/.idaw/tasks/${currentTaskId}.json`));
|
||||
} catch {
|
||||
console.log(`Warning: Could not read task ${currentTaskId}`);
|
||||
currentTaskId = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentTask && currentTask.status === 'in_progress') {
|
||||
if (autoYes) {
|
||||
// Auto: skip interrupted task
|
||||
currentTask.status = 'skipped';
|
||||
currentTask.execution.error = 'Skipped on resume (auto mode)';
|
||||
currentTask.execution.completed_at = new Date().toISOString();
|
||||
currentTask.updated_at = new Date().toISOString();
|
||||
Write(`.workflow/.idaw/tasks/${currentTaskId}.json`, JSON.stringify(currentTask, null, 2));
|
||||
session.skipped.push(currentTaskId);
|
||||
console.log(`Skipped interrupted task: ${currentTaskId}`);
|
||||
} else {
|
||||
const answer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: `Task ${currentTaskId} was interrupted: "${currentTask.title}". How to proceed?`,
|
||||
header: 'Resume',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Retry', description: 'Reset to pending, re-execute from beginning' },
|
||||
{ label: 'Skip', description: 'Mark as skipped, move to next task' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
if (answer.answers?.Resume === 'Skip') {
|
||||
currentTask.status = 'skipped';
|
||||
currentTask.execution.error = 'Skipped on resume (user choice)';
|
||||
currentTask.execution.completed_at = new Date().toISOString();
|
||||
currentTask.updated_at = new Date().toISOString();
|
||||
Write(`.workflow/.idaw/tasks/${currentTaskId}.json`, JSON.stringify(currentTask, null, 2));
|
||||
session.skipped.push(currentTaskId);
|
||||
} else {
|
||||
// Retry: reset to pending
|
||||
currentTask.status = 'pending';
|
||||
currentTask.execution.started_at = null;
|
||||
currentTask.execution.completed_at = null;
|
||||
currentTask.execution.skill_results = [];
|
||||
currentTask.execution.error = null;
|
||||
currentTask.updated_at = new Date().toISOString();
|
||||
Write(`.workflow/.idaw/tasks/${currentTaskId}.json`, JSON.stringify(currentTask, null, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 3: Build Remaining Task Queue
|
||||
|
||||
```javascript
|
||||
// Collect remaining tasks (pending, or the retried current task)
|
||||
const allTaskIds = session.tasks;
|
||||
const completedSet = new Set([...session.completed, ...session.failed, ...session.skipped]);
|
||||
|
||||
const remainingTasks = [];
|
||||
for (const taskId of allTaskIds) {
|
||||
if (completedSet.has(taskId)) continue;
|
||||
try {
|
||||
const task = JSON.parse(Read(`.workflow/.idaw/tasks/${taskId}.json`));
|
||||
if (task.status === 'pending') {
|
||||
remainingTasks.push(task);
|
||||
}
|
||||
} catch {
|
||||
console.log(`Warning: Could not read task ${taskId}, skipping`);
|
||||
}
|
||||
}
|
||||
|
||||
if (remainingTasks.length === 0) {
|
||||
console.log('No remaining tasks to execute. Session complete.');
|
||||
session.status = 'completed';
|
||||
session.current_task = null;
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort: priority ASC, then ID ASC
|
||||
remainingTasks.sort((a, b) => {
|
||||
if (a.priority !== b.priority) return a.priority - b.priority;
|
||||
return a.id.localeCompare(b.id);
|
||||
});
|
||||
|
||||
console.log(`Remaining tasks: ${remainingTasks.length}`);
|
||||
|
||||
// Append resume marker to progress.md
|
||||
const progressFile = `${sessionDir}/progress.md`;
|
||||
try {
|
||||
const currentProgress = Read(progressFile);
|
||||
Write(progressFile, currentProgress + `\n---\n**Resumed**: ${new Date().toISOString()}\n\n`);
|
||||
} catch {
|
||||
Write(progressFile, `# IDAW Progress — ${session.session_id}\n\n---\n**Resumed**: ${new Date().toISOString()}\n\n`);
|
||||
}
|
||||
|
||||
// Update TodoWrite
|
||||
TodoWrite({
|
||||
todos: remainingTasks.map((t, i) => ({
|
||||
content: `IDAW:[${i + 1}/${remainingTasks.length}] ${t.title}`,
|
||||
status: i === 0 ? 'in_progress' : 'pending',
|
||||
activeForm: `Executing ${t.title}`
|
||||
}))
|
||||
});
|
||||
```
|
||||
|
||||
### Phase 4-6: Execute Remaining (reuse run.md main loop)
|
||||
|
||||
Execute remaining tasks using the same Phase 4-6 logic from `/idaw:run`:
|
||||
|
||||
```javascript
|
||||
// Phase 4: Main Loop — identical to run.md Phase 4
|
||||
for (let taskIdx = 0; taskIdx < remainingTasks.length; taskIdx++) {
|
||||
const task = remainingTasks[taskIdx];
|
||||
|
||||
// Resolve skill chain
|
||||
const resolvedType = task.task_type || inferTaskType(task.title, task.description);
|
||||
const chain = task.skill_chain || SKILL_CHAIN_MAP[resolvedType] || SKILL_CHAIN_MAP['feature'];
|
||||
|
||||
// Update task → in_progress
|
||||
task.status = 'in_progress';
|
||||
task.task_type = resolvedType;
|
||||
task.execution.started_at = new Date().toISOString();
|
||||
Write(`.workflow/.idaw/tasks/${task.id}.json`, JSON.stringify(task, null, 2));
|
||||
|
||||
session.current_task = task.id;
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
console.log(`\n--- [${taskIdx + 1}/${remainingTasks.length}] ${task.id}: ${task.title} ---`);
|
||||
console.log(`Chain: ${chain.join(' → ')}`);
|
||||
|
||||
// ━━━ Pre-Task CLI Context Analysis (for complex/bugfix tasks) ━━━
|
||||
if (['bugfix', 'bugfix-hotfix', 'feature-complex'].includes(resolvedType)) {
|
||||
console.log(` Pre-analysis: gathering context for ${resolvedType} task...`);
|
||||
const affectedFiles = (task.context?.affected_files || []).join(', ');
|
||||
const preAnalysisPrompt = `PURPOSE: Pre-analyze codebase context for IDAW task before execution.
|
||||
TASK: • Understand current state of: ${affectedFiles || 'files related to: ' + task.title} • Identify dependencies and risk areas • Note existing patterns to follow
|
||||
MODE: analysis
|
||||
CONTEXT: @**/*
|
||||
EXPECTED: Brief context summary (affected modules, dependencies, risk areas) in 3-5 bullet points
|
||||
CONSTRAINTS: Keep concise | Focus on execution-relevant context`;
|
||||
const preAnalysis = Bash(`ccw cli -p '${preAnalysisPrompt.replace(/'/g, "'\\''")}' --tool gemini --mode analysis 2>&1 || echo "Pre-analysis skipped"`);
|
||||
task.execution.skill_results.push({
|
||||
skill: 'cli-pre-analysis',
|
||||
status: 'completed',
|
||||
context_summary: preAnalysis?.substring(0, 500),
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
// Execute skill chain
|
||||
let previousResult = null;
|
||||
let taskFailed = false;
|
||||
|
||||
for (let skillIdx = 0; skillIdx < chain.length; skillIdx++) {
|
||||
const skillName = chain[skillIdx];
|
||||
const skillArgs = assembleSkillArgs(skillName, task, previousResult, autoYes, skillIdx === 0);
|
||||
|
||||
console.log(` [${skillIdx + 1}/${chain.length}] ${skillName}`);
|
||||
|
||||
try {
|
||||
const result = Skill({ skill: skillName, args: skillArgs });
|
||||
previousResult = result;
|
||||
task.execution.skill_results.push({
|
||||
skill: skillName,
|
||||
status: 'completed',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
// ━━━ CLI-Assisted Error Recovery ━━━
|
||||
console.log(` Diagnosing failure: ${skillName}...`);
|
||||
const diagnosisPrompt = `PURPOSE: Diagnose why skill "${skillName}" failed during IDAW task execution.
|
||||
TASK: • Analyze error: ${String(error).substring(0, 300)} • Check affected files: ${(task.context?.affected_files || []).join(', ') || 'unknown'} • Identify root cause • Suggest fix strategy
|
||||
MODE: analysis
|
||||
CONTEXT: @**/* | Memory: IDAW task ${task.id}: ${task.title}
|
||||
EXPECTED: Root cause + actionable fix recommendation (1-2 sentences)
|
||||
CONSTRAINTS: Focus on actionable diagnosis`;
|
||||
const diagnosisResult = Bash(`ccw cli -p '${diagnosisPrompt.replace(/'/g, "'\\''")}' --tool gemini --mode analysis --rule analysis-diagnose-bug-root-cause 2>&1 || echo "CLI diagnosis unavailable"`);
|
||||
|
||||
task.execution.skill_results.push({
|
||||
skill: `cli-diagnosis:${skillName}`,
|
||||
status: 'completed',
|
||||
diagnosis: diagnosisResult?.substring(0, 500),
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Retry with diagnosis context
|
||||
console.log(` Retry with diagnosis: ${skillName}`);
|
||||
try {
|
||||
const retryResult = Skill({ skill: skillName, args: skillArgs });
|
||||
previousResult = retryResult;
|
||||
task.execution.skill_results.push({
|
||||
skill: skillName,
|
||||
status: 'completed-retry-with-diagnosis',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (retryError) {
|
||||
task.execution.skill_results.push({
|
||||
skill: skillName,
|
||||
status: 'failed',
|
||||
error: String(retryError).substring(0, 200),
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
if (autoYes) {
|
||||
taskFailed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const answer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: `${skillName} failed after CLI diagnosis + retry: ${String(retryError).substring(0, 100)}`,
|
||||
header: 'Error',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Skip task', description: 'Mark as failed, continue' },
|
||||
{ label: 'Abort', description: 'Stop run' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
if (answer.answers?.Error === 'Abort') {
|
||||
task.status = 'failed';
|
||||
task.execution.error = String(retryError).substring(0, 200);
|
||||
Write(`.workflow/.idaw/tasks/${task.id}.json`, JSON.stringify(task, null, 2));
|
||||
session.failed.push(task.id);
|
||||
session.status = 'failed';
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
return;
|
||||
}
|
||||
taskFailed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 5: Checkpoint
|
||||
if (taskFailed) {
|
||||
task.status = 'failed';
|
||||
task.execution.error = 'Skill chain failed after retry';
|
||||
task.execution.completed_at = new Date().toISOString();
|
||||
session.failed.push(task.id);
|
||||
} else {
|
||||
// Git commit
|
||||
const commitMsg = `feat(idaw): ${task.title} [${task.id}]`;
|
||||
const diffCheck = Bash('git diff --stat HEAD 2>/dev/null || echo ""');
|
||||
const untrackedCheck = Bash('git ls-files --others --exclude-standard 2>/dev/null || echo ""');
|
||||
|
||||
if (diffCheck?.trim() || untrackedCheck?.trim()) {
|
||||
Bash('git add -A');
|
||||
Bash(`git commit -m "$(cat <<'EOF'\n${commitMsg}\nEOF\n)"`);
|
||||
const commitHash = Bash('git rev-parse --short HEAD 2>/dev/null')?.trim();
|
||||
task.execution.git_commit = commitHash;
|
||||
} else {
|
||||
task.execution.git_commit = 'no-commit';
|
||||
}
|
||||
|
||||
task.status = 'completed';
|
||||
task.execution.completed_at = new Date().toISOString();
|
||||
session.completed.push(task.id);
|
||||
}
|
||||
|
||||
task.updated_at = new Date().toISOString();
|
||||
Write(`.workflow/.idaw/tasks/${task.id}.json`, JSON.stringify(task, null, 2));
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
// Append progress
|
||||
const chain_str = chain.join(' → ');
|
||||
const progressEntry = `## ${task.id} — ${task.title}\n- Status: ${task.status}\n- Chain: ${chain_str}\n- Commit: ${task.execution.git_commit || '-'}\n\n`;
|
||||
const currentProgress = Read(`${sessionDir}/progress.md`);
|
||||
Write(`${sessionDir}/progress.md`, currentProgress + progressEntry);
|
||||
}
|
||||
|
||||
// Phase 6: Report
|
||||
session.status = session.failed.length > 0 && session.completed.length === 0 ? 'failed' : 'completed';
|
||||
session.current_task = null;
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
const summary = `\n---\n## Summary (Resumed)\n- Completed: ${session.completed.length}\n- Failed: ${session.failed.length}\n- Skipped: ${session.skipped.length}\n`;
|
||||
const finalProgress = Read(`${sessionDir}/progress.md`);
|
||||
Write(`${sessionDir}/progress.md`, finalProgress + summary);
|
||||
|
||||
console.log('\n=== IDAW Resume Complete ===');
|
||||
console.log(`Session: ${session.session_id}`);
|
||||
console.log(`Completed: ${session.completed.length} | Failed: ${session.failed.length} | Skipped: ${session.skipped.length}`);
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
### assembleSkillArgs
|
||||
|
||||
```javascript
|
||||
function assembleSkillArgs(skillName, task, previousResult, autoYes, isFirst) {
|
||||
let args = '';
|
||||
|
||||
if (isFirst) {
|
||||
// Sanitize for shell safety
|
||||
const goal = `${task.title}\n${task.description}`
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/\$/g, '\\$')
|
||||
.replace(/`/g, '\\`');
|
||||
args = `"${goal}"`;
|
||||
if (task.task_type === 'bugfix-hotfix') args += ' --hotfix';
|
||||
} else if (previousResult?.session_id) {
|
||||
args = `--session="${previousResult.session_id}"`;
|
||||
}
|
||||
|
||||
if (autoYes && !args.includes('-y') && !args.includes('--yes')) {
|
||||
args = args ? `${args} -y` : '-y';
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# Resume most recent running session (interactive)
|
||||
/idaw:resume
|
||||
|
||||
# Resume specific session
|
||||
/idaw:resume IDA-auth-fix-20260301
|
||||
|
||||
# Resume with auto mode (skip interrupted, continue)
|
||||
/idaw:resume -y
|
||||
|
||||
# Resume specific session with auto mode
|
||||
/idaw:resume -y IDA-auth-fix-20260301
|
||||
```
|
||||
@@ -1,648 +0,0 @@
|
||||
---
|
||||
name: run-coordinate
|
||||
description: IDAW coordinator - execute task skill chains via external CLI with hook callbacks and git checkpoints
|
||||
argument-hint: "[-y|--yes] [--task <id>[,<id>,...]] [--dry-run] [--tool <tool>]"
|
||||
allowed-tools: Agent(*), AskUserQuestion(*), Read(*), Write(*), Bash(*), Glob(*), Grep(*)
|
||||
---
|
||||
|
||||
# IDAW Run Coordinate Command (/idaw:run-coordinate)
|
||||
|
||||
Coordinator variant of `/idaw:run`: external CLI execution with background tasks and hook callbacks.
|
||||
|
||||
**Execution Model**: `ccw cli -p "..." --tool <tool> --mode write` in background → hook callback → next step.
|
||||
|
||||
**vs `/idaw:run`**: Direct `Skill()` calls (blocking, main process) vs `ccw cli` (background, external process).
|
||||
|
||||
## When to Use
|
||||
|
||||
| Scenario | Use |
|
||||
|----------|-----|
|
||||
| Standard IDAW execution (main process) | `/idaw:run` |
|
||||
| External CLI execution (background, hook-driven) | `/idaw:run-coordinate` |
|
||||
| Need `claude` or `gemini` as execution tool | `/idaw:run-coordinate --tool claude` |
|
||||
| Long-running tasks, avoid context window pressure | `/idaw:run-coordinate` |
|
||||
|
||||
## Skill Chain Mapping
|
||||
|
||||
```javascript
|
||||
const SKILL_CHAIN_MAP = {
|
||||
'bugfix': ['workflow-lite-plan', 'workflow-test-fix'],
|
||||
'bugfix-hotfix': ['workflow-lite-plan'],
|
||||
'feature': ['workflow-lite-plan', 'workflow-test-fix'],
|
||||
'feature-complex': ['workflow-plan', 'workflow-execute', 'workflow-test-fix'],
|
||||
'refactor': ['workflow:refactor-cycle'],
|
||||
'tdd': ['workflow-tdd-plan', 'workflow-execute'],
|
||||
'test': ['workflow-test-fix'],
|
||||
'test-fix': ['workflow-test-fix'],
|
||||
'review': ['review-cycle'],
|
||||
'docs': ['workflow-lite-plan']
|
||||
};
|
||||
```
|
||||
|
||||
## Task Type Inference
|
||||
|
||||
```javascript
|
||||
function inferTaskType(title, description) {
|
||||
const text = `${title} ${description}`.toLowerCase();
|
||||
if (/urgent|production|critical/.test(text) && /fix|bug/.test(text)) return 'bugfix-hotfix';
|
||||
if (/refactor|重构|tech.*debt/.test(text)) return 'refactor';
|
||||
if (/tdd|test-driven|test first/.test(text)) return 'tdd';
|
||||
if (/test fail|fix test|failing test/.test(text)) return 'test-fix';
|
||||
if (/generate test|写测试|add test/.test(text)) return 'test';
|
||||
if (/review|code review/.test(text)) return 'review';
|
||||
if (/docs|documentation|readme/.test(text)) return 'docs';
|
||||
if (/fix|bug|error|crash|fail/.test(text)) return 'bugfix';
|
||||
if (/complex|multi-module|architecture/.test(text)) return 'feature-complex';
|
||||
return 'feature';
|
||||
}
|
||||
```
|
||||
|
||||
## 6-Phase Execution (Coordinator Model)
|
||||
|
||||
### Phase 1: Load Tasks
|
||||
|
||||
```javascript
|
||||
const args = $ARGUMENTS;
|
||||
const autoYes = /(-y|--yes)/.test(args);
|
||||
const dryRun = /--dry-run/.test(args);
|
||||
const taskFilter = args.match(/--task\s+([\w,-]+)/)?.[1]?.split(',') || null;
|
||||
const cliTool = args.match(/--tool\s+(\w+)/)?.[1] || 'claude';
|
||||
|
||||
// Load task files
|
||||
const taskFiles = Glob('.workflow/.idaw/tasks/IDAW-*.json') || [];
|
||||
|
||||
if (taskFiles.length === 0) {
|
||||
console.log('No IDAW tasks found. Use /idaw:add to create tasks.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse and filter
|
||||
let tasks = taskFiles.map(f => JSON.parse(Read(f)));
|
||||
|
||||
if (taskFilter) {
|
||||
tasks = tasks.filter(t => taskFilter.includes(t.id));
|
||||
} else {
|
||||
tasks = tasks.filter(t => t.status === 'pending');
|
||||
}
|
||||
|
||||
if (tasks.length === 0) {
|
||||
console.log('No pending tasks to execute. Use /idaw:add to add tasks or --task to specify IDs.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort: priority ASC (1=critical first), then ID ASC
|
||||
tasks.sort((a, b) => {
|
||||
if (a.priority !== b.priority) return a.priority - b.priority;
|
||||
return a.id.localeCompare(b.id);
|
||||
});
|
||||
```
|
||||
|
||||
### Phase 2: Session Setup
|
||||
|
||||
```javascript
|
||||
// Generate session ID: IDA-{slug}-YYYYMMDD
|
||||
const slug = tasks[0].title
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.substring(0, 20)
|
||||
.replace(/-$/, '');
|
||||
const dateStr = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
||||
let sessionId = `IDA-${slug}-${dateStr}`;
|
||||
|
||||
// Check collision
|
||||
const existingSession = Glob(`.workflow/.idaw/sessions/${sessionId}/session.json`);
|
||||
if (existingSession?.length > 0) {
|
||||
sessionId = `${sessionId}-2`;
|
||||
}
|
||||
|
||||
const sessionDir = `.workflow/.idaw/sessions/${sessionId}`;
|
||||
Bash(`mkdir -p "${sessionDir}"`);
|
||||
|
||||
const session = {
|
||||
session_id: sessionId,
|
||||
mode: 'coordinate', // ★ Marks this as coordinator-mode session
|
||||
cli_tool: cliTool,
|
||||
status: 'running',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
tasks: tasks.map(t => t.id),
|
||||
current_task: null,
|
||||
current_skill_index: 0,
|
||||
completed: [],
|
||||
failed: [],
|
||||
skipped: [],
|
||||
prompts_used: []
|
||||
};
|
||||
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
// Initialize progress.md
|
||||
const progressHeader = `# IDAW Progress — ${sessionId} (coordinate mode)\nStarted: ${session.created_at}\nCLI Tool: ${cliTool}\n\n`;
|
||||
Write(`${sessionDir}/progress.md`, progressHeader);
|
||||
```
|
||||
|
||||
### Phase 3: Startup Protocol
|
||||
|
||||
```javascript
|
||||
// Check for existing running sessions
|
||||
const runningSessions = Glob('.workflow/.idaw/sessions/IDA-*/session.json')
|
||||
?.map(f => { try { return JSON.parse(Read(f)); } catch { return null; } })
|
||||
.filter(s => s && s.status === 'running' && s.session_id !== sessionId) || [];
|
||||
|
||||
if (runningSessions.length > 0 && !autoYes) {
|
||||
const answer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: `Found running session: ${runningSessions[0].session_id}. How to proceed?`,
|
||||
header: 'Conflict',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Resume existing', description: 'Use /idaw:resume instead' },
|
||||
{ label: 'Start fresh', description: 'Continue with new session' },
|
||||
{ label: 'Abort', description: 'Cancel this run' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
if (answer.answers?.Conflict === 'Resume existing') {
|
||||
console.log(`Use: /idaw:resume ${runningSessions[0].session_id}`);
|
||||
return;
|
||||
}
|
||||
if (answer.answers?.Conflict === 'Abort') return;
|
||||
}
|
||||
|
||||
// Check git status
|
||||
const gitStatus = Bash('git status --porcelain 2>/dev/null');
|
||||
if (gitStatus?.trim() && !autoYes) {
|
||||
const answer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Working tree has uncommitted changes. How to proceed?',
|
||||
header: 'Git',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Continue', description: 'Proceed with dirty tree' },
|
||||
{ label: 'Stash', description: 'git stash before running' },
|
||||
{ label: 'Abort', description: 'Stop and handle manually' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
if (answer.answers?.Git === 'Stash') Bash('git stash push -m "idaw-pre-run"');
|
||||
if (answer.answers?.Git === 'Abort') return;
|
||||
}
|
||||
|
||||
// Dry run
|
||||
if (dryRun) {
|
||||
console.log(`# Dry Run — ${sessionId} (coordinate mode, tool: ${cliTool})\n`);
|
||||
for (const task of tasks) {
|
||||
const taskType = task.task_type || inferTaskType(task.title, task.description);
|
||||
const chain = task.skill_chain || SKILL_CHAIN_MAP[taskType] || SKILL_CHAIN_MAP['feature'];
|
||||
console.log(`## ${task.id}: ${task.title}`);
|
||||
console.log(` Type: ${taskType} | Priority: ${task.priority}`);
|
||||
console.log(` Chain: ${chain.join(' → ')}`);
|
||||
console.log(` CLI: ccw cli --tool ${cliTool} --mode write\n`);
|
||||
}
|
||||
console.log(`Total: ${tasks.length} tasks`);
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: Launch First Task (then wait for hook)
|
||||
|
||||
```javascript
|
||||
// Start with the first task, first skill
|
||||
const firstTask = tasks[0];
|
||||
const resolvedType = firstTask.task_type || inferTaskType(firstTask.title, firstTask.description);
|
||||
const chain = firstTask.skill_chain || SKILL_CHAIN_MAP[resolvedType] || SKILL_CHAIN_MAP['feature'];
|
||||
|
||||
// Update task → in_progress
|
||||
firstTask.status = 'in_progress';
|
||||
firstTask.task_type = resolvedType;
|
||||
firstTask.execution.started_at = new Date().toISOString();
|
||||
Write(`.workflow/.idaw/tasks/${firstTask.id}.json`, JSON.stringify(firstAgent, null, 2));
|
||||
|
||||
// Update session
|
||||
session.current_task = firstTask.id;
|
||||
session.current_skill_index = 0;
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
// ━━━ Pre-Task CLI Context Analysis (for complex/bugfix tasks) ━━━
|
||||
if (['bugfix', 'bugfix-hotfix', 'feature-complex'].includes(resolvedType)) {
|
||||
console.log(`Pre-analysis: gathering context for ${resolvedType} task...`);
|
||||
const affectedFiles = (firstTask.context?.affected_files || []).join(', ');
|
||||
const preAnalysisPrompt = `PURPOSE: Pre-analyze codebase context for IDAW task.
|
||||
TASK: • Understand current state of: ${affectedFiles || 'files related to: ' + firstTask.title} • Identify dependencies and risk areas
|
||||
MODE: analysis
|
||||
CONTEXT: @**/*
|
||||
EXPECTED: Brief context summary in 3-5 bullet points
|
||||
CONSTRAINTS: Keep concise`;
|
||||
Bash(`ccw cli -p '${preAnalysisPrompt.replace(/'/g, "'\\''")}' --tool gemini --mode analysis 2>&1 || echo "Pre-analysis skipped"`);
|
||||
}
|
||||
|
||||
// Assemble prompt for first skill
|
||||
const skillName = chain[0];
|
||||
const prompt = assembleCliPrompt(skillName, firstAgent, null, autoYes);
|
||||
|
||||
session.prompts_used.push({
|
||||
task_id: firstTask.id,
|
||||
skill_index: 0,
|
||||
skill: skillName,
|
||||
prompt: prompt,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
// Launch via ccw cli in background
|
||||
console.log(`[1/${tasks.length}] ${firstTask.id}: ${firstTask.title}`);
|
||||
console.log(` Chain: ${chain.join(' → ')}`);
|
||||
console.log(` Launching: ${skillName} via ccw cli --tool ${cliTool}`);
|
||||
|
||||
Bash(
|
||||
`ccw cli -p "${escapeForShell(prompt)}" --tool ${cliTool} --mode write`,
|
||||
{ run_in_background: true }
|
||||
);
|
||||
|
||||
// ★ STOP HERE — wait for hook callback
|
||||
// Hook callback will trigger handleStepCompletion() below
|
||||
```
|
||||
|
||||
### Phase 5: Hook Callback Handler (per-step completion)
|
||||
|
||||
```javascript
|
||||
// Called by hook when background CLI completes
|
||||
async function handleStepCompletion(sessionId, cliOutput) {
|
||||
const sessionDir = `.workflow/.idaw/sessions/${sessionId}`;
|
||||
const session = JSON.parse(Read(`${sessionDir}/session.json`));
|
||||
|
||||
const taskId = session.current_task;
|
||||
const task = JSON.parse(Read(`.workflow/.idaw/tasks/${taskId}.json`));
|
||||
|
||||
const resolvedType = task.task_type || inferTaskType(task.title, task.description);
|
||||
const chain = task.skill_chain || SKILL_CHAIN_MAP[resolvedType] || SKILL_CHAIN_MAP['feature'];
|
||||
const skillIdx = session.current_skill_index;
|
||||
const skillName = chain[skillIdx];
|
||||
|
||||
// Parse CLI output for session ID
|
||||
const parsedOutput = parseCliOutput(cliOutput);
|
||||
|
||||
// Record skill result
|
||||
task.execution.skill_results.push({
|
||||
skill: skillName,
|
||||
status: parsedOutput.success ? 'completed' : 'failed',
|
||||
session_id: parsedOutput.sessionId,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// ━━━ Handle failure with CLI diagnosis ━━━
|
||||
if (!parsedOutput.success) {
|
||||
console.log(` ${skillName} failed. Running CLI diagnosis...`);
|
||||
const diagnosisPrompt = `PURPOSE: Diagnose why skill "${skillName}" failed during IDAW task.
|
||||
TASK: • Analyze error output • Check affected files: ${(task.context?.affected_files || []).join(', ') || 'unknown'}
|
||||
MODE: analysis
|
||||
CONTEXT: @**/* | Memory: IDAW task ${task.id}: ${task.title}
|
||||
EXPECTED: Root cause + fix recommendation
|
||||
CONSTRAINTS: Actionable diagnosis`;
|
||||
Bash(`ccw cli -p '${diagnosisPrompt.replace(/'/g, "'\\''")}' --tool gemini --mode analysis --rule analysis-diagnose-bug-root-cause 2>&1 || true`);
|
||||
|
||||
task.execution.skill_results.push({
|
||||
skill: `cli-diagnosis:${skillName}`,
|
||||
status: 'completed',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Retry once
|
||||
console.log(` Retrying: ${skillName}`);
|
||||
const retryPrompt = assembleCliPrompt(skillName, task, parsedOutput, true);
|
||||
session.prompts_used.push({
|
||||
task_id: taskId,
|
||||
skill_index: skillIdx,
|
||||
skill: `${skillName}-retry`,
|
||||
prompt: retryPrompt,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
Write(`.workflow/.idaw/tasks/${taskId}.json`, JSON.stringify(task, null, 2));
|
||||
|
||||
Bash(
|
||||
`ccw cli -p "${escapeForShell(retryPrompt)}" --tool ${session.cli_tool} --mode write`,
|
||||
{ run_in_background: true }
|
||||
);
|
||||
return; // Wait for retry hook
|
||||
}
|
||||
|
||||
// ━━━ Skill succeeded — advance ━━━
|
||||
const nextSkillIdx = skillIdx + 1;
|
||||
|
||||
if (nextSkillIdx < chain.length) {
|
||||
// More skills in this task's chain → launch next skill
|
||||
session.current_skill_index = nextSkillIdx;
|
||||
session.updated_at = new Date().toISOString();
|
||||
|
||||
const nextSkill = chain[nextSkillIdx];
|
||||
const nextPrompt = assembleCliPrompt(nextSkill, task, parsedOutput, true);
|
||||
|
||||
session.prompts_used.push({
|
||||
task_id: taskId,
|
||||
skill_index: nextSkillIdx,
|
||||
skill: nextSkill,
|
||||
prompt: nextPrompt,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
Write(`.workflow/.idaw/tasks/${taskId}.json`, JSON.stringify(task, null, 2));
|
||||
|
||||
console.log(` Next skill: ${nextSkill}`);
|
||||
Bash(
|
||||
`ccw cli -p "${escapeForShell(nextPrompt)}" --tool ${session.cli_tool} --mode write`,
|
||||
{ run_in_background: true }
|
||||
);
|
||||
return; // Wait for next hook
|
||||
}
|
||||
|
||||
// ━━━ Task chain complete — git checkpoint ━━━
|
||||
const commitMsg = `feat(idaw): ${task.title} [${task.id}]`;
|
||||
const diffCheck = Bash('git diff --stat HEAD 2>/dev/null || echo ""');
|
||||
const untrackedCheck = Bash('git ls-files --others --exclude-standard 2>/dev/null || echo ""');
|
||||
|
||||
if (diffCheck?.trim() || untrackedCheck?.trim()) {
|
||||
Bash('git add -A');
|
||||
Bash(`git commit -m "$(cat <<'EOF'\n${commitMsg}\nEOF\n)"`);
|
||||
const commitHash = Bash('git rev-parse --short HEAD 2>/dev/null')?.trim();
|
||||
task.execution.git_commit = commitHash;
|
||||
} else {
|
||||
task.execution.git_commit = 'no-commit';
|
||||
}
|
||||
|
||||
task.status = 'completed';
|
||||
task.execution.completed_at = new Date().toISOString();
|
||||
task.updated_at = new Date().toISOString();
|
||||
Write(`.workflow/.idaw/tasks/${taskId}.json`, JSON.stringify(task, null, 2));
|
||||
|
||||
session.completed.push(taskId);
|
||||
|
||||
// Append progress
|
||||
const progressEntry = `## ${task.id} — ${task.title}\n` +
|
||||
`- Status: completed\n` +
|
||||
`- Type: ${task.task_type}\n` +
|
||||
`- Chain: ${chain.join(' → ')}\n` +
|
||||
`- Commit: ${task.execution.git_commit || '-'}\n` +
|
||||
`- Mode: coordinate (${session.cli_tool})\n\n`;
|
||||
const currentProgress = Read(`${sessionDir}/progress.md`);
|
||||
Write(`${sessionDir}/progress.md`, currentProgress + progressEntry);
|
||||
|
||||
// ━━━ Advance to next task ━━━
|
||||
const allTaskIds = session.tasks;
|
||||
const completedSet = new Set([...session.completed, ...session.failed, ...session.skipped]);
|
||||
const nextTaskId = allTaskIds.find(id => !completedSet.has(id));
|
||||
|
||||
if (nextTaskId) {
|
||||
// Load next task
|
||||
const nextTask = JSON.parse(Read(`.workflow/.idaw/tasks/${nextTaskId}.json`));
|
||||
const nextType = nextTask.task_type || inferTaskType(nextTask.title, nextTask.description);
|
||||
const nextChain = nextTask.skill_chain || SKILL_CHAIN_MAP[nextType] || SKILL_CHAIN_MAP['feature'];
|
||||
|
||||
nextTask.status = 'in_progress';
|
||||
nextTask.task_type = nextType;
|
||||
nextTask.execution.started_at = new Date().toISOString();
|
||||
Write(`.workflow/.idaw/tasks/${nextTaskId}.json`, JSON.stringify(nextAgent, null, 2));
|
||||
|
||||
session.current_task = nextTaskId;
|
||||
session.current_skill_index = 0;
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
// Pre-analysis for complex tasks
|
||||
if (['bugfix', 'bugfix-hotfix', 'feature-complex'].includes(nextType)) {
|
||||
const affectedFiles = (nextTask.context?.affected_files || []).join(', ');
|
||||
Bash(`ccw cli -p 'PURPOSE: Pre-analyze context for ${nextTask.title}. TASK: Check ${affectedFiles || "related files"}. MODE: analysis. EXPECTED: 3-5 bullet points.' --tool gemini --mode analysis 2>&1 || true`);
|
||||
}
|
||||
|
||||
const nextSkillName = nextChain[0];
|
||||
const nextPrompt = assembleCliPrompt(nextSkillName, nextAgent, null, true);
|
||||
|
||||
session.prompts_used.push({
|
||||
task_id: nextTaskId,
|
||||
skill_index: 0,
|
||||
skill: nextSkillName,
|
||||
prompt: nextPrompt,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
const taskNum = session.completed.length + 1;
|
||||
const totalTasks = session.tasks.length;
|
||||
console.log(`\n[${taskNum}/${totalTasks}] ${nextTaskId}: ${nextTask.title}`);
|
||||
console.log(` Chain: ${nextChain.join(' → ')}`);
|
||||
|
||||
Bash(
|
||||
`ccw cli -p "${escapeForShell(nextPrompt)}" --tool ${session.cli_tool} --mode write`,
|
||||
{ run_in_background: true }
|
||||
);
|
||||
return; // Wait for hook
|
||||
}
|
||||
|
||||
// ━━━ All tasks complete — Phase 6: Report ━━━
|
||||
session.status = session.failed.length > 0 && session.completed.length === 0 ? 'failed' : 'completed';
|
||||
session.current_task = null;
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
const summary = `\n---\n## Summary (coordinate mode)\n` +
|
||||
`- CLI Tool: ${session.cli_tool}\n` +
|
||||
`- Completed: ${session.completed.length}\n` +
|
||||
`- Failed: ${session.failed.length}\n` +
|
||||
`- Skipped: ${session.skipped.length}\n` +
|
||||
`- Total: ${session.tasks.length}\n`;
|
||||
const finalProgress = Read(`${sessionDir}/progress.md`);
|
||||
Write(`${sessionDir}/progress.md`, finalProgress + summary);
|
||||
|
||||
console.log('\n=== IDAW Coordinate Complete ===');
|
||||
console.log(`Session: ${sessionId}`);
|
||||
console.log(`Completed: ${session.completed.length}/${session.tasks.length}`);
|
||||
if (session.failed.length > 0) console.log(`Failed: ${session.failed.join(', ')}`);
|
||||
}
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
### assembleCliPrompt
|
||||
|
||||
```javascript
|
||||
function assembleCliPrompt(skillName, task, previousResult, autoYes) {
|
||||
let prompt = '';
|
||||
const yFlag = autoYes ? ' -y' : '';
|
||||
|
||||
// Map skill to command invocation
|
||||
if (skillName === 'workflow-lite-plan') {
|
||||
const goal = sanitize(`${task.title}\n${task.description}`);
|
||||
prompt = `/workflow-lite-plan${yFlag} "${goal}"`;
|
||||
if (task.task_type === 'bugfix') prompt = `/workflow-lite-plan${yFlag} --bugfix "${goal}"`;
|
||||
if (task.task_type === 'bugfix-hotfix') prompt = `/workflow-lite-plan${yFlag} --hotfix "${goal}"`;
|
||||
|
||||
} else if (skillName === 'workflow-plan') {
|
||||
prompt = `/workflow-plan${yFlag} "${sanitize(task.title)}"`;
|
||||
|
||||
} else if (skillName === 'workflow-execute') {
|
||||
if (previousResult?.sessionId) {
|
||||
prompt = `/workflow-execute${yFlag} --resume-session="${previousResult.sessionId}"`;
|
||||
} else {
|
||||
prompt = `/workflow-execute${yFlag}`;
|
||||
}
|
||||
|
||||
} else if (skillName === 'workflow-test-fix') {
|
||||
if (previousResult?.sessionId) {
|
||||
prompt = `/workflow-test-fix${yFlag} "${previousResult.sessionId}"`;
|
||||
} else {
|
||||
prompt = `/workflow-test-fix${yFlag} "${sanitize(task.title)}"`;
|
||||
}
|
||||
|
||||
} else if (skillName === 'workflow-tdd-plan') {
|
||||
prompt = `/workflow-tdd-plan${yFlag} "${sanitize(task.title)}"`;
|
||||
|
||||
} else if (skillName === 'workflow:refactor-cycle') {
|
||||
prompt = `/workflow:refactor-cycle${yFlag} "${sanitize(task.title)}"`;
|
||||
|
||||
} else if (skillName === 'review-cycle') {
|
||||
if (previousResult?.sessionId) {
|
||||
prompt = `/review-cycle${yFlag} --session="${previousResult.sessionId}"`;
|
||||
} else {
|
||||
prompt = `/review-cycle${yFlag}`;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Generic fallback
|
||||
prompt = `/${skillName}${yFlag} "${sanitize(task.title)}"`;
|
||||
}
|
||||
|
||||
// Append task context
|
||||
prompt += `\n\nTask: ${task.title}\nDescription: ${task.description}`;
|
||||
if (task.context?.affected_files?.length > 0) {
|
||||
prompt += `\nAffected files: ${task.context.affected_files.join(', ')}`;
|
||||
}
|
||||
if (task.context?.acceptance_criteria?.length > 0) {
|
||||
prompt += `\nAcceptance criteria: ${task.context.acceptance_criteria.join('; ')}`;
|
||||
}
|
||||
|
||||
return prompt;
|
||||
}
|
||||
```
|
||||
|
||||
### sanitize & escapeForShell
|
||||
|
||||
```javascript
|
||||
function sanitize(text) {
|
||||
return text
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/\$/g, '\\$')
|
||||
.replace(/`/g, '\\`');
|
||||
}
|
||||
|
||||
function escapeForShell(prompt) {
|
||||
return prompt.replace(/'/g, "'\\''");
|
||||
}
|
||||
```
|
||||
|
||||
### parseCliOutput
|
||||
|
||||
```javascript
|
||||
function parseCliOutput(output) {
|
||||
// Extract session ID from CLI output (e.g., WFS-xxx, session-xxx)
|
||||
const sessionMatch = output.match(/(?:session|WFS|Session ID)[:\s]*([\w-]+)/i);
|
||||
const success = !/(?:error|failed|fatal)/i.test(output) || /completed|success/i.test(output);
|
||||
|
||||
return {
|
||||
success,
|
||||
sessionId: sessionMatch?.[1] || null,
|
||||
raw: output?.substring(0, 500)
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## CLI-Assisted Analysis
|
||||
|
||||
Same as `/idaw:run` — integrated at two points:
|
||||
|
||||
### Pre-Task Context Analysis
|
||||
For `bugfix`, `bugfix-hotfix`, `feature-complex` tasks: auto-invoke `ccw cli --tool gemini --mode analysis` before launching skill chain.
|
||||
|
||||
### Error Recovery with CLI Diagnosis
|
||||
When a skill's CLI execution fails: invoke diagnosis → retry once → if still fails, mark failed and advance.
|
||||
|
||||
```
|
||||
Skill CLI fails → CLI diagnosis (gemini) → Retry CLI → Still fails → mark failed → next task
|
||||
```
|
||||
|
||||
## State Flow
|
||||
|
||||
```
|
||||
Phase 4: Launch first skill
|
||||
↓
|
||||
ccw cli --tool claude --mode write (background)
|
||||
↓
|
||||
★ STOP — wait for hook callback
|
||||
↓
|
||||
Phase 5: handleStepCompletion()
|
||||
├─ Skill succeeded + more in chain → launch next skill → STOP
|
||||
├─ Skill succeeded + chain complete → git checkpoint → next task → STOP
|
||||
├─ Skill failed → CLI diagnosis → retry → STOP
|
||||
└─ All tasks done → Phase 6: Report
|
||||
```
|
||||
|
||||
## Session State (session.json)
|
||||
|
||||
```json
|
||||
{
|
||||
"session_id": "IDA-fix-login-20260301",
|
||||
"mode": "coordinate",
|
||||
"cli_tool": "claude",
|
||||
"status": "running|waiting|completed|failed",
|
||||
"created_at": "ISO",
|
||||
"updated_at": "ISO",
|
||||
"tasks": ["IDAW-001", "IDAW-002"],
|
||||
"current_task": "IDAW-001",
|
||||
"current_skill_index": 0,
|
||||
"completed": [],
|
||||
"failed": [],
|
||||
"skipped": [],
|
||||
"prompts_used": [
|
||||
{
|
||||
"task_id": "IDAW-001",
|
||||
"skill_index": 0,
|
||||
"skill": "workflow-lite-plan",
|
||||
"prompt": "/workflow-lite-plan -y \"Fix login timeout\"",
|
||||
"timestamp": "ISO"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Differences from /idaw:run
|
||||
|
||||
| Aspect | /idaw:run | /idaw:run-coordinate |
|
||||
|--------|-----------|---------------------|
|
||||
| Execution | `Skill()` blocking in main process | `ccw cli` background + hook callback |
|
||||
| Context window | Shared (each skill uses main context) | Isolated (each CLI gets fresh context) |
|
||||
| Concurrency | Sequential blocking | Sequential non-blocking (hook-driven) |
|
||||
| State tracking | session.json + task.json | session.json + task.json + prompts_used |
|
||||
| Tool selection | N/A (Skill native) | `--tool claude\|gemini\|qwen` |
|
||||
| Resume | Via `/idaw:resume` (same) | Via `/idaw:resume` (same, detects mode) |
|
||||
| Best for | Short chains, interactive | Long chains, autonomous, context-heavy |
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# Execute all pending tasks via claude CLI
|
||||
/idaw:run-coordinate -y
|
||||
|
||||
# Use specific CLI tool
|
||||
/idaw:run-coordinate -y --tool gemini
|
||||
|
||||
# Execute specific tasks
|
||||
/idaw:run-coordinate --task IDAW-001,IDAW-003 --tool claude
|
||||
|
||||
# Dry run (show plan without executing)
|
||||
/idaw:run-coordinate --dry-run
|
||||
|
||||
# Interactive mode
|
||||
/idaw:run-coordinate
|
||||
```
|
||||
@@ -1,539 +0,0 @@
|
||||
---
|
||||
name: run
|
||||
description: IDAW orchestrator - execute task skill chains serially with git checkpoints
|
||||
argument-hint: "[-y|--yes] [--task <id>[,<id>,...]] [--dry-run]"
|
||||
allowed-tools: Skill(*), TodoWrite(*), AskUserQuestion(*), Read(*), Write(*), Bash(*), Glob(*)
|
||||
---
|
||||
|
||||
# IDAW Run Command (/idaw:run)
|
||||
|
||||
## Auto Mode
|
||||
|
||||
When `--yes` or `-y`: Skip all confirmations, auto-skip on failure, proceed with dirty git.
|
||||
|
||||
## Skill Chain Mapping
|
||||
|
||||
```javascript
|
||||
const SKILL_CHAIN_MAP = {
|
||||
'bugfix': ['workflow-lite-plan', 'workflow-test-fix'],
|
||||
'bugfix-hotfix': ['workflow-lite-plan'],
|
||||
'feature': ['workflow-lite-plan', 'workflow-test-fix'],
|
||||
'feature-complex': ['workflow-plan', 'workflow-execute', 'workflow-test-fix'],
|
||||
'refactor': ['workflow:refactor-cycle'],
|
||||
'tdd': ['workflow-tdd-plan', 'workflow-execute'],
|
||||
'test': ['workflow-test-fix'],
|
||||
'test-fix': ['workflow-test-fix'],
|
||||
'review': ['review-cycle'],
|
||||
'docs': ['workflow-lite-plan']
|
||||
};
|
||||
```
|
||||
|
||||
## Task Type Inference
|
||||
|
||||
```javascript
|
||||
function inferTaskType(title, description) {
|
||||
const text = `${title} ${description}`.toLowerCase();
|
||||
if (/urgent|production|critical/.test(text) && /fix|bug/.test(text)) return 'bugfix-hotfix';
|
||||
if (/refactor|重构|tech.*debt/.test(text)) return 'refactor';
|
||||
if (/tdd|test-driven|test first/.test(text)) return 'tdd';
|
||||
if (/test fail|fix test|failing test/.test(text)) return 'test-fix';
|
||||
if (/generate test|写测试|add test/.test(text)) return 'test';
|
||||
if (/review|code review/.test(text)) return 'review';
|
||||
if (/docs|documentation|readme/.test(text)) return 'docs';
|
||||
if (/fix|bug|error|crash|fail/.test(text)) return 'bugfix';
|
||||
if (/complex|multi-module|architecture/.test(text)) return 'feature-complex';
|
||||
return 'feature';
|
||||
}
|
||||
```
|
||||
|
||||
## 6-Phase Execution
|
||||
|
||||
### Phase 1: Load Tasks
|
||||
|
||||
```javascript
|
||||
const args = $ARGUMENTS;
|
||||
const autoYes = /(-y|--yes)/.test(args);
|
||||
const dryRun = /--dry-run/.test(args);
|
||||
const taskFilter = args.match(/--task\s+([\w,-]+)/)?.[1]?.split(',') || null;
|
||||
|
||||
// Load task files
|
||||
const taskFiles = Glob('.workflow/.idaw/tasks/IDAW-*.json') || [];
|
||||
|
||||
if (taskFiles.length === 0) {
|
||||
console.log('No IDAW tasks found. Use /idaw:add to create tasks.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse and filter
|
||||
let tasks = taskFiles.map(f => JSON.parse(Read(f)));
|
||||
|
||||
if (taskFilter) {
|
||||
tasks = tasks.filter(t => taskFilter.includes(t.id));
|
||||
} else {
|
||||
tasks = tasks.filter(t => t.status === 'pending');
|
||||
}
|
||||
|
||||
if (tasks.length === 0) {
|
||||
console.log('No pending tasks to execute. Use /idaw:add to add tasks or --task to specify IDs.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort: priority ASC (1=critical first), then ID ASC
|
||||
tasks.sort((a, b) => {
|
||||
if (a.priority !== b.priority) return a.priority - b.priority;
|
||||
return a.id.localeCompare(b.id);
|
||||
});
|
||||
```
|
||||
|
||||
### Phase 2: Session Setup
|
||||
|
||||
```javascript
|
||||
// Generate session ID: IDA-{slug}-YYYYMMDD
|
||||
const slug = tasks[0].title
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '-')
|
||||
.substring(0, 20)
|
||||
.replace(/-$/, '');
|
||||
const dateStr = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
||||
let sessionId = `IDA-${slug}-${dateStr}`;
|
||||
|
||||
// Check collision
|
||||
const existingSession = Glob(`.workflow/.idaw/sessions/${sessionId}/session.json`);
|
||||
if (existingSession?.length > 0) {
|
||||
sessionId = `${sessionId}-2`;
|
||||
}
|
||||
|
||||
const sessionDir = `.workflow/.idaw/sessions/${sessionId}`;
|
||||
Bash(`mkdir -p "${sessionDir}"`);
|
||||
|
||||
const session = {
|
||||
session_id: sessionId,
|
||||
status: 'running',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
tasks: tasks.map(t => t.id),
|
||||
current_task: null,
|
||||
completed: [],
|
||||
failed: [],
|
||||
skipped: []
|
||||
};
|
||||
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
// Initialize progress.md
|
||||
const progressHeader = `# IDAW Progress — ${sessionId}\nStarted: ${session.created_at}\n\n`;
|
||||
Write(`${sessionDir}/progress.md`, progressHeader);
|
||||
|
||||
// TodoWrite
|
||||
TodoWrite({
|
||||
todos: tasks.map((t, i) => ({
|
||||
content: `IDAW:[${i + 1}/${tasks.length}] ${t.title}`,
|
||||
status: i === 0 ? 'in_progress' : 'pending',
|
||||
activeForm: `Executing ${t.title}`
|
||||
}))
|
||||
});
|
||||
```
|
||||
|
||||
### Phase 3: Startup Protocol
|
||||
|
||||
```javascript
|
||||
// Check for existing running sessions
|
||||
const runningSessions = Glob('.workflow/.idaw/sessions/IDA-*/session.json')
|
||||
?.map(f => JSON.parse(Read(f)))
|
||||
.filter(s => s.status === 'running' && s.session_id !== sessionId) || [];
|
||||
|
||||
if (runningSessions.length > 0) {
|
||||
if (!autoYes) {
|
||||
const answer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: `Found running session: ${runningSessions[0].session_id}. How to proceed?`,
|
||||
header: 'Conflict',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Resume existing', description: 'Use /idaw:resume instead' },
|
||||
{ label: 'Start fresh', description: 'Continue with new session' },
|
||||
{ label: 'Abort', description: 'Cancel this run' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
if (answer.answers?.Conflict === 'Resume existing') {
|
||||
console.log(`Use: /idaw:resume ${runningSessions[0].session_id}`);
|
||||
return;
|
||||
}
|
||||
if (answer.answers?.Conflict === 'Abort') return;
|
||||
}
|
||||
// autoYes or "Start fresh": proceed
|
||||
}
|
||||
|
||||
// Check git status
|
||||
const gitStatus = Bash('git status --porcelain 2>/dev/null');
|
||||
if (gitStatus?.trim()) {
|
||||
if (!autoYes) {
|
||||
const answer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: 'Working tree has uncommitted changes. How to proceed?',
|
||||
header: 'Git',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Continue', description: 'Proceed with dirty tree' },
|
||||
{ label: 'Stash', description: 'git stash before running' },
|
||||
{ label: 'Abort', description: 'Stop and handle manually' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
if (answer.answers?.Git === 'Stash') {
|
||||
Bash('git stash push -m "idaw-pre-run"');
|
||||
}
|
||||
if (answer.answers?.Git === 'Abort') return;
|
||||
}
|
||||
// autoYes: proceed silently
|
||||
}
|
||||
|
||||
// Dry run: show plan and exit
|
||||
if (dryRun) {
|
||||
console.log(`# Dry Run — ${sessionId}\n`);
|
||||
for (const task of tasks) {
|
||||
const taskType = task.task_type || inferTaskType(task.title, task.description);
|
||||
const chain = task.skill_chain || SKILL_CHAIN_MAP[taskType] || SKILL_CHAIN_MAP['feature'];
|
||||
console.log(`## ${task.id}: ${task.title}`);
|
||||
console.log(` Type: ${taskType} | Priority: ${task.priority}`);
|
||||
console.log(` Chain: ${chain.join(' → ')}\n`);
|
||||
}
|
||||
console.log(`Total: ${tasks.length} tasks`);
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: Main Loop (serial, one task at a time)
|
||||
|
||||
```javascript
|
||||
for (let taskIdx = 0; taskIdx < tasks.length; taskIdx++) {
|
||||
const task = tasks[taskIdx];
|
||||
|
||||
// Skip completed/failed/skipped
|
||||
if (['completed', 'failed', 'skipped'].includes(task.status)) continue;
|
||||
|
||||
// Resolve skill chain
|
||||
const resolvedType = task.task_type || inferTaskType(task.title, task.description);
|
||||
const chain = task.skill_chain || SKILL_CHAIN_MAP[resolvedType] || SKILL_CHAIN_MAP['feature'];
|
||||
|
||||
// Update task status → in_progress
|
||||
task.status = 'in_progress';
|
||||
task.task_type = resolvedType; // persist inferred type
|
||||
task.execution.started_at = new Date().toISOString();
|
||||
Write(`.workflow/.idaw/tasks/${task.id}.json`, JSON.stringify(task, null, 2));
|
||||
|
||||
// Update session
|
||||
session.current_task = task.id;
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
console.log(`\n--- [${taskIdx + 1}/${tasks.length}] ${task.id}: ${task.title} ---`);
|
||||
console.log(`Chain: ${chain.join(' → ')}`);
|
||||
|
||||
// ━━━ Pre-Task CLI Context Analysis (for complex/bugfix tasks) ━━━
|
||||
if (['bugfix', 'bugfix-hotfix', 'feature-complex'].includes(resolvedType)) {
|
||||
console.log(` Pre-analysis: gathering context for ${resolvedType} task...`);
|
||||
const affectedFiles = (task.context?.affected_files || []).join(', ');
|
||||
const preAnalysisPrompt = `PURPOSE: Pre-analyze codebase context for IDAW task before execution.
|
||||
TASK: • Understand current state of: ${affectedFiles || 'files related to: ' + task.title} • Identify dependencies and risk areas • Note existing patterns to follow
|
||||
MODE: analysis
|
||||
CONTEXT: @**/*
|
||||
EXPECTED: Brief context summary (affected modules, dependencies, risk areas) in 3-5 bullet points
|
||||
CONSTRAINTS: Keep concise | Focus on execution-relevant context`;
|
||||
const preAnalysis = Bash(`ccw cli -p '${preAnalysisPrompt.replace(/'/g, "'\\''")}' --tool gemini --mode analysis 2>&1 || echo "Pre-analysis skipped"`);
|
||||
task.execution.skill_results.push({
|
||||
skill: 'cli-pre-analysis',
|
||||
status: 'completed',
|
||||
context_summary: preAnalysis?.substring(0, 500),
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
// Execute each skill in chain
|
||||
let previousResult = null;
|
||||
let taskFailed = false;
|
||||
|
||||
for (let skillIdx = 0; skillIdx < chain.length; skillIdx++) {
|
||||
const skillName = chain[skillIdx];
|
||||
const skillArgs = assembleSkillArgs(skillName, task, previousResult, autoYes, skillIdx === 0);
|
||||
|
||||
console.log(` [${skillIdx + 1}/${chain.length}] ${skillName}`);
|
||||
|
||||
try {
|
||||
const result = Skill({ skill: skillName, args: skillArgs });
|
||||
previousResult = result;
|
||||
task.execution.skill_results.push({
|
||||
skill: skillName,
|
||||
status: 'completed',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
// ━━━ CLI-Assisted Error Recovery ━━━
|
||||
// Step 1: Invoke CLI diagnosis (auto-invoke trigger: self-repair fails)
|
||||
console.log(` Diagnosing failure: ${skillName}...`);
|
||||
const diagnosisPrompt = `PURPOSE: Diagnose why skill "${skillName}" failed during IDAW task execution.
|
||||
TASK: • Analyze error: ${String(error).substring(0, 300)} • Check affected files: ${(task.context?.affected_files || []).join(', ') || 'unknown'} • Identify root cause • Suggest fix strategy
|
||||
MODE: analysis
|
||||
CONTEXT: @**/* | Memory: IDAW task ${task.id}: ${task.title}
|
||||
EXPECTED: Root cause + actionable fix recommendation (1-2 sentences)
|
||||
CONSTRAINTS: Focus on actionable diagnosis`;
|
||||
const diagnosisResult = Bash(`ccw cli -p '${diagnosisPrompt.replace(/'/g, "'\\''")}' --tool gemini --mode analysis --rule analysis-diagnose-bug-root-cause 2>&1 || echo "CLI diagnosis unavailable"`);
|
||||
|
||||
task.execution.skill_results.push({
|
||||
skill: `cli-diagnosis:${skillName}`,
|
||||
status: 'completed',
|
||||
diagnosis: diagnosisResult?.substring(0, 500),
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// Step 2: Retry with diagnosis context
|
||||
console.log(` Retry with diagnosis: ${skillName}`);
|
||||
try {
|
||||
const retryResult = Skill({ skill: skillName, args: skillArgs });
|
||||
previousResult = retryResult;
|
||||
task.execution.skill_results.push({
|
||||
skill: skillName,
|
||||
status: 'completed-retry-with-diagnosis',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (retryError) {
|
||||
// Step 3: Failed after CLI-assisted retry
|
||||
task.execution.skill_results.push({
|
||||
skill: skillName,
|
||||
status: 'failed',
|
||||
error: String(retryError).substring(0, 200),
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
if (autoYes) {
|
||||
taskFailed = true;
|
||||
break;
|
||||
} else {
|
||||
const answer = AskUserQuestion({
|
||||
questions: [{
|
||||
question: `${skillName} failed after CLI diagnosis + retry: ${String(retryError).substring(0, 100)}. How to proceed?`,
|
||||
header: 'Error',
|
||||
multiSelect: false,
|
||||
options: [
|
||||
{ label: 'Skip task', description: 'Mark task as failed, continue to next' },
|
||||
{ label: 'Abort', description: 'Stop entire run' }
|
||||
]
|
||||
}]
|
||||
});
|
||||
if (answer.answers?.Error === 'Abort') {
|
||||
task.status = 'failed';
|
||||
task.execution.error = String(retryError).substring(0, 200);
|
||||
Write(`.workflow/.idaw/tasks/${task.id}.json`, JSON.stringify(task, null, 2));
|
||||
session.failed.push(task.id);
|
||||
session.status = 'failed';
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
return;
|
||||
}
|
||||
taskFailed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 5: Checkpoint (per task) — inline
|
||||
if (taskFailed) {
|
||||
task.status = 'failed';
|
||||
task.execution.error = 'Skill chain failed after retry';
|
||||
task.execution.completed_at = new Date().toISOString();
|
||||
session.failed.push(task.id);
|
||||
} else {
|
||||
// Git commit checkpoint
|
||||
const commitMsg = `feat(idaw): ${task.title} [${task.id}]`;
|
||||
const diffCheck = Bash('git diff --stat HEAD 2>/dev/null || echo ""');
|
||||
const untrackedCheck = Bash('git ls-files --others --exclude-standard 2>/dev/null || echo ""');
|
||||
|
||||
if (diffCheck?.trim() || untrackedCheck?.trim()) {
|
||||
Bash('git add -A');
|
||||
const commitResult = Bash(`git commit -m "$(cat <<'EOF'\n${commitMsg}\nEOF\n)"`);
|
||||
const commitHash = Bash('git rev-parse --short HEAD 2>/dev/null')?.trim();
|
||||
task.execution.git_commit = commitHash;
|
||||
} else {
|
||||
task.execution.git_commit = 'no-commit';
|
||||
}
|
||||
|
||||
task.status = 'completed';
|
||||
task.execution.completed_at = new Date().toISOString();
|
||||
session.completed.push(task.id);
|
||||
}
|
||||
|
||||
// Write task + session state
|
||||
task.updated_at = new Date().toISOString();
|
||||
Write(`.workflow/.idaw/tasks/${task.id}.json`, JSON.stringify(task, null, 2));
|
||||
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
// Append to progress.md
|
||||
const duration = task.execution.started_at && task.execution.completed_at
|
||||
? formatDuration(new Date(task.execution.completed_at) - new Date(task.execution.started_at))
|
||||
: 'unknown';
|
||||
|
||||
const progressEntry = `## ${task.id} — ${task.title}\n` +
|
||||
`- Status: ${task.status}\n` +
|
||||
`- Type: ${task.task_type}\n` +
|
||||
`- Chain: ${chain.join(' → ')}\n` +
|
||||
`- Commit: ${task.execution.git_commit || '-'}\n` +
|
||||
`- Duration: ${duration}\n\n`;
|
||||
|
||||
const currentProgress = Read(`${sessionDir}/progress.md`);
|
||||
Write(`${sessionDir}/progress.md`, currentProgress + progressEntry);
|
||||
|
||||
// Update TodoWrite
|
||||
if (taskIdx + 1 < tasks.length) {
|
||||
TodoWrite({
|
||||
todos: tasks.map((t, i) => ({
|
||||
content: `IDAW:[${i + 1}/${tasks.length}] ${t.title}`,
|
||||
status: i < taskIdx + 1 ? 'completed' : (i === taskIdx + 1 ? 'in_progress' : 'pending'),
|
||||
activeForm: `Executing ${t.title}`
|
||||
}))
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 6: Report
|
||||
|
||||
```javascript
|
||||
session.status = session.failed.length > 0 && session.completed.length === 0 ? 'failed' : 'completed';
|
||||
session.current_task = null;
|
||||
session.updated_at = new Date().toISOString();
|
||||
Write(`${sessionDir}/session.json`, JSON.stringify(session, null, 2));
|
||||
|
||||
// Final progress summary
|
||||
const summary = `\n---\n## Summary\n` +
|
||||
`- Completed: ${session.completed.length}\n` +
|
||||
`- Failed: ${session.failed.length}\n` +
|
||||
`- Skipped: ${session.skipped.length}\n` +
|
||||
`- Total: ${tasks.length}\n`;
|
||||
|
||||
const finalProgress = Read(`${sessionDir}/progress.md`);
|
||||
Write(`${sessionDir}/progress.md`, finalProgress + summary);
|
||||
|
||||
// Display report
|
||||
console.log('\n=== IDAW Run Complete ===');
|
||||
console.log(`Session: ${sessionId}`);
|
||||
console.log(`Completed: ${session.completed.length}/${tasks.length}`);
|
||||
if (session.failed.length > 0) console.log(`Failed: ${session.failed.join(', ')}`);
|
||||
if (session.skipped.length > 0) console.log(`Skipped: ${session.skipped.join(', ')}`);
|
||||
|
||||
// List git commits
|
||||
for (const taskId of session.completed) {
|
||||
const t = JSON.parse(Read(`.workflow/.idaw/tasks/${taskId}.json`));
|
||||
if (t.execution.git_commit && t.execution.git_commit !== 'no-commit') {
|
||||
console.log(` ${t.execution.git_commit} ${t.title}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Helper Functions
|
||||
|
||||
### assembleSkillArgs
|
||||
|
||||
```javascript
|
||||
function assembleSkillArgs(skillName, task, previousResult, autoYes, isFirst) {
|
||||
let args = '';
|
||||
|
||||
if (isFirst) {
|
||||
// First skill: pass task goal — sanitize for shell safety
|
||||
const goal = `${task.title}\n${task.description}`
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/\$/g, '\\$')
|
||||
.replace(/`/g, '\\`');
|
||||
args = `"${goal}"`;
|
||||
|
||||
// bugfix-hotfix: add --hotfix
|
||||
if (task.task_type === 'bugfix-hotfix') {
|
||||
args += ' --hotfix';
|
||||
}
|
||||
} else if (previousResult?.session_id) {
|
||||
// Subsequent skills: chain session
|
||||
args = `--session="${previousResult.session_id}"`;
|
||||
}
|
||||
|
||||
// Propagate -y
|
||||
if (autoYes && !args.includes('-y') && !args.includes('--yes')) {
|
||||
args = args ? `${args} -y` : '-y';
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
```
|
||||
|
||||
### formatDuration
|
||||
|
||||
```javascript
|
||||
function formatDuration(ms) {
|
||||
const seconds = Math.floor(ms / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const remainingSeconds = seconds % 60;
|
||||
if (minutes > 0) return `${minutes}m ${remainingSeconds}s`;
|
||||
return `${seconds}s`;
|
||||
}
|
||||
```
|
||||
|
||||
## CLI-Assisted Analysis
|
||||
|
||||
IDAW integrates `ccw cli` (Gemini) for intelligent analysis at two key points:
|
||||
|
||||
### Pre-Task Context Analysis
|
||||
|
||||
For `bugfix`, `bugfix-hotfix`, and `feature-complex` tasks, IDAW automatically invokes CLI analysis **before** executing the skill chain to gather codebase context:
|
||||
|
||||
```
|
||||
Task starts → CLI pre-analysis (gemini) → Context gathered → Skill chain executes
|
||||
```
|
||||
|
||||
- Identifies dependencies and risk areas
|
||||
- Notes existing patterns to follow
|
||||
- Results stored in `task.execution.skill_results` as `cli-pre-analysis`
|
||||
|
||||
### Error Recovery with CLI Diagnosis
|
||||
|
||||
When a skill fails, instead of blind retry, IDAW uses CLI-assisted diagnosis:
|
||||
|
||||
```
|
||||
Skill fails → CLI diagnosis (gemini, analysis-diagnose-bug-root-cause)
|
||||
→ Root cause identified → Retry with diagnosis context
|
||||
→ Still fails → Skip (autoYes) or Ask user (interactive)
|
||||
```
|
||||
|
||||
- Uses `--rule analysis-diagnose-bug-root-cause` template
|
||||
- Diagnosis results stored in `task.execution.skill_results` as `cli-diagnosis:{skill}`
|
||||
- Follows CLAUDE.md auto-invoke trigger pattern: "self-repair fails → invoke CLI analysis"
|
||||
|
||||
### Execution Flow (with CLI analysis)
|
||||
|
||||
```
|
||||
Phase 4 Main Loop (per task):
|
||||
├─ [bugfix/complex only] CLI pre-analysis → context summary
|
||||
├─ Skill 1: execute
|
||||
│ ├─ Success → next skill
|
||||
│ └─ Failure → CLI diagnosis → retry → success/fail
|
||||
├─ Skill 2: execute ...
|
||||
└─ Phase 5: git checkpoint
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# Execute all pending tasks
|
||||
/idaw:run -y
|
||||
|
||||
# Execute specific tasks
|
||||
/idaw:run --task IDAW-001,IDAW-003
|
||||
|
||||
# Dry run (show plan without executing)
|
||||
/idaw:run --dry-run
|
||||
|
||||
# Interactive mode (confirm at each step)
|
||||
/idaw:run
|
||||
```
|
||||
@@ -1,182 +0,0 @@
|
||||
---
|
||||
name: status
|
||||
description: View IDAW task and session progress
|
||||
argument-hint: "[session-id]"
|
||||
allowed-tools: Read(*), Glob(*), Bash(*)
|
||||
---
|
||||
|
||||
# IDAW Status Command (/idaw:status)
|
||||
|
||||
## Overview
|
||||
|
||||
Read-only command to view IDAW task queue and execution session progress.
|
||||
|
||||
## Implementation
|
||||
|
||||
### Phase 1: Determine View Mode
|
||||
|
||||
```javascript
|
||||
const sessionId = $ARGUMENTS?.trim();
|
||||
|
||||
if (sessionId) {
|
||||
// Specific session view
|
||||
showSession(sessionId);
|
||||
} else {
|
||||
// Overview: pending tasks + latest session
|
||||
showOverview();
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 2: Show Overview
|
||||
|
||||
```javascript
|
||||
function showOverview() {
|
||||
// 1. Load all tasks
|
||||
const taskFiles = Glob('.workflow/.idaw/tasks/IDAW-*.json') || [];
|
||||
|
||||
if (taskFiles.length === 0) {
|
||||
console.log('No IDAW tasks found. Use /idaw:add to create tasks.');
|
||||
return;
|
||||
}
|
||||
|
||||
const tasks = taskFiles.map(f => JSON.parse(Read(f)));
|
||||
|
||||
// 2. Group by status
|
||||
const byStatus = {
|
||||
pending: tasks.filter(t => t.status === 'pending'),
|
||||
in_progress: tasks.filter(t => t.status === 'in_progress'),
|
||||
completed: tasks.filter(t => t.status === 'completed'),
|
||||
failed: tasks.filter(t => t.status === 'failed'),
|
||||
skipped: tasks.filter(t => t.status === 'skipped')
|
||||
};
|
||||
|
||||
// 3. Display task summary table
|
||||
console.log('# IDAW Tasks\n');
|
||||
console.log('| ID | Title | Type | Priority | Status |');
|
||||
console.log('|----|-------|------|----------|--------|');
|
||||
|
||||
// Sort: priority ASC, then ID ASC
|
||||
const sorted = [...tasks].sort((a, b) => {
|
||||
if (a.priority !== b.priority) return a.priority - b.priority;
|
||||
return a.id.localeCompare(b.id);
|
||||
});
|
||||
|
||||
for (const t of sorted) {
|
||||
const type = t.task_type || '(infer)';
|
||||
console.log(`| ${t.id} | ${t.title.substring(0, 40)} | ${type} | ${t.priority} | ${t.status} |`);
|
||||
}
|
||||
|
||||
console.log(`\nTotal: ${tasks.length} | Pending: ${byStatus.pending.length} | Completed: ${byStatus.completed.length} | Failed: ${byStatus.failed.length}`);
|
||||
|
||||
// 4. Show latest session (if any)
|
||||
const sessionDirs = Glob('.workflow/.idaw/sessions/IDA-*/session.json') || [];
|
||||
if (sessionDirs.length > 0) {
|
||||
// Sort by modification time (newest first) — Glob returns sorted by mtime
|
||||
const latestSessionFile = sessionDirs[0];
|
||||
const session = JSON.parse(Read(latestSessionFile));
|
||||
console.log(`\n## Latest Session: ${session.session_id}`);
|
||||
console.log(`Status: ${session.status} | Tasks: ${session.tasks?.length || 0}`);
|
||||
console.log(`Completed: ${session.completed?.length || 0} | Failed: ${session.failed?.length || 0} | Skipped: ${session.skipped?.length || 0}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 3: Show Specific Session
|
||||
|
||||
```javascript
|
||||
function showSession(sessionId) {
|
||||
const sessionFile = `.workflow/.idaw/sessions/${sessionId}/session.json`;
|
||||
const progressFile = `.workflow/.idaw/sessions/${sessionId}/progress.md`;
|
||||
|
||||
// Try reading session
|
||||
try {
|
||||
const session = JSON.parse(Read(sessionFile));
|
||||
|
||||
console.log(`# IDAW Session: ${session.session_id}\n`);
|
||||
console.log(`Status: ${session.status}`);
|
||||
console.log(`Created: ${session.created_at}`);
|
||||
console.log(`Updated: ${session.updated_at}`);
|
||||
console.log(`Current Task: ${session.current_task || 'none'}\n`);
|
||||
|
||||
// Task detail table
|
||||
console.log('| ID | Title | Status | Commit |');
|
||||
console.log('|----|-------|--------|--------|');
|
||||
|
||||
for (const taskId of session.tasks) {
|
||||
const taskFile = `.workflow/.idaw/tasks/${taskId}.json`;
|
||||
try {
|
||||
const task = JSON.parse(Read(taskFile));
|
||||
const commit = task.execution?.git_commit?.substring(0, 7) || '-';
|
||||
console.log(`| ${task.id} | ${task.title.substring(0, 40)} | ${task.status} | ${commit} |`);
|
||||
} catch {
|
||||
console.log(`| ${taskId} | (file not found) | unknown | - |`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\nCompleted: ${session.completed?.length || 0} | Failed: ${session.failed?.length || 0} | Skipped: ${session.skipped?.length || 0}`);
|
||||
|
||||
// Show progress.md if exists
|
||||
try {
|
||||
const progress = Read(progressFile);
|
||||
console.log('\n---\n');
|
||||
console.log(progress);
|
||||
} catch {
|
||||
// No progress file yet
|
||||
}
|
||||
|
||||
} catch {
|
||||
// Session not found — try listing all sessions
|
||||
console.log(`Session "${sessionId}" not found.\n`);
|
||||
listSessions();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: List All Sessions
|
||||
|
||||
```javascript
|
||||
function listSessions() {
|
||||
const sessionFiles = Glob('.workflow/.idaw/sessions/IDA-*/session.json') || [];
|
||||
|
||||
if (sessionFiles.length === 0) {
|
||||
console.log('No IDAW sessions found. Use /idaw:run to start execution.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('# IDAW Sessions\n');
|
||||
console.log('| Session ID | Status | Tasks | Completed | Failed |');
|
||||
console.log('|------------|--------|-------|-----------|--------|');
|
||||
|
||||
for (const f of sessionFiles) {
|
||||
try {
|
||||
const session = JSON.parse(Read(f));
|
||||
console.log(`| ${session.session_id} | ${session.status} | ${session.tasks?.length || 0} | ${session.completed?.length || 0} | ${session.failed?.length || 0} |`);
|
||||
} catch {
|
||||
// Skip malformed
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\nUse /idaw:status <session-id> for details.');
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
# Show overview (pending tasks + latest session)
|
||||
/idaw:status
|
||||
|
||||
# Show specific session details
|
||||
/idaw:status IDA-auth-fix-20260301
|
||||
|
||||
# Output example:
|
||||
# IDAW Tasks
|
||||
#
|
||||
# | ID | Title | Type | Priority | Status |
|
||||
# |----------|------------------------------------|--------|----------|-----------|
|
||||
# | IDAW-001 | Fix auth token refresh | bugfix | 1 | completed |
|
||||
# | IDAW-002 | Add rate limiting | feature| 2 | pending |
|
||||
# | IDAW-003 | Refactor payment module | refact | 3 | pending |
|
||||
#
|
||||
# Total: 3 | Pending: 2 | Completed: 1 | Failed: 0
|
||||
```
|
||||
Reference in New Issue
Block a user