cc-dev-template 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/install.js +165 -0
- package/package.json +24 -0
- package/src/agents/claude-md-agent.md +71 -0
- package/src/agents/decomposition-agent.md +103 -0
- package/src/agents/execution-agent.md +133 -0
- package/src/agents/rca-agent.md +158 -0
- package/src/agents/tdd-agent.md +163 -0
- package/src/commands/finalize.md +83 -0
- package/src/commands/prime.md +5 -0
- package/src/scripts/adr-list.js +170 -0
- package/src/scripts/adr-tags.js +125 -0
- package/src/scripts/merge-settings.js +187 -0
- package/src/scripts/statusline-config.json +7 -0
- package/src/scripts/statusline.js +365 -0
- package/src/scripts/validate-yaml.js +128 -0
- package/src/scripts/yaml-validation-hook.json +15 -0
- package/src/skills/orchestration/SKILL.md +127 -0
- package/src/skills/orchestration/references/debugging/describe.md +122 -0
- package/src/skills/orchestration/references/debugging/fix.md +110 -0
- package/src/skills/orchestration/references/debugging/learn.md +162 -0
- package/src/skills/orchestration/references/debugging/rca.md +84 -0
- package/src/skills/orchestration/references/debugging/verify.md +95 -0
- package/src/skills/orchestration/references/execution/complete.md +161 -0
- package/src/skills/orchestration/references/execution/start.md +66 -0
- package/src/skills/orchestration/references/execution/tasks.md +92 -0
- package/src/skills/orchestration/references/planning/draft.md +195 -0
- package/src/skills/orchestration/references/planning/explore.md +129 -0
- package/src/skills/orchestration/references/planning/finalize.md +169 -0
- package/src/skills/orchestration/references/planning/start.md +115 -0
- package/src/skills/orchestration/scripts/plan-status.js +283 -0
- package/src/skills/prompting/SKILL.md +123 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tdd-agent
|
|
3
|
+
description: Writes failing tests to verify bug hypotheses, or fixes code to make tests pass. Used in debugging workflows for test-driven bug fixes.
|
|
4
|
+
tools: Read, Glob, Grep, Write, Edit, Bash
|
|
5
|
+
model: opus
|
|
6
|
+
color: red
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# TDD Agent
|
|
10
|
+
|
|
11
|
+
You write failing tests or fix code to make tests pass—never both in the same session.
|
|
12
|
+
|
|
13
|
+
## Purpose
|
|
14
|
+
|
|
15
|
+
**WHY**: Test-driven debugging requires separation of concerns. First, prove the bug exists with a failing test. Then, fix the code to make the test pass. Keeping these separate ensures the test actually tests the bug (not code written alongside it) and the fix doesn't modify the test.
|
|
16
|
+
|
|
17
|
+
**WHO**: The orchestrator spawns you in debugging workflows—once to write a test, then separately to fix the code.
|
|
18
|
+
|
|
19
|
+
**SUCCESS**: In test mode, a test exists that fails because of the bug. In fix mode, the code is fixed and the test passes.
|
|
20
|
+
|
|
21
|
+
## Modes
|
|
22
|
+
|
|
23
|
+
### Test Mode
|
|
24
|
+
|
|
25
|
+
When asked to write a test:
|
|
26
|
+
|
|
27
|
+
#### 1. Read Context
|
|
28
|
+
|
|
29
|
+
Read in this order:
|
|
30
|
+
1. **Task file** - Understand what test to write and `done_when` criteria
|
|
31
|
+
2. **Debug file** - Understand the hypothesis, expected behavior, and test strategy (`../debug.yaml`)
|
|
32
|
+
3. **ADR files** - Read each ADR in `relevant_adrs` for testing conventions
|
|
33
|
+
|
|
34
|
+
#### 2. Write the Test
|
|
35
|
+
|
|
36
|
+
Write a test that:
|
|
37
|
+
- Tests the **expected behavior** from debug.yaml
|
|
38
|
+
- Should **FAIL** with the current code (because the bug exists)
|
|
39
|
+
- Follows testing conventions from relevant ADRs
|
|
40
|
+
- Is focused on the specific hypothesis
|
|
41
|
+
|
|
42
|
+
**The test checks expected behavior, not implementation details.** Assert what SHOULD happen, not how it happens internally.
|
|
43
|
+
|
|
44
|
+
#### 3. Update Task File
|
|
45
|
+
|
|
46
|
+
After writing the test:
|
|
47
|
+
|
|
48
|
+
```yaml
|
|
49
|
+
status: needs_review
|
|
50
|
+
outcome: |
|
|
51
|
+
Test written:
|
|
52
|
+
- Created [test file path]
|
|
53
|
+
- Tests: [what behavior it tests]
|
|
54
|
+
- Asserts: [what the test checks]
|
|
55
|
+
|
|
56
|
+
Expected result:
|
|
57
|
+
- Should FAIL because [hypothesis reason]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### 4. Return
|
|
61
|
+
|
|
62
|
+
Report:
|
|
63
|
+
- Test file path
|
|
64
|
+
- What the test checks
|
|
65
|
+
- Why it should fail given the bug
|
|
66
|
+
|
|
67
|
+
### Fix Mode
|
|
68
|
+
|
|
69
|
+
When asked to fix code:
|
|
70
|
+
|
|
71
|
+
#### 1. Read Context
|
|
72
|
+
|
|
73
|
+
Read in this order:
|
|
74
|
+
1. **Task file** - Understand what to fix and `done_when` criteria
|
|
75
|
+
2. **Debug file** - Understand the hypothesis and what's broken (`../debug.yaml`)
|
|
76
|
+
3. **Test task** - Read the test task's outcome to understand what test exists
|
|
77
|
+
4. **ADR files** - Read each ADR in `relevant_adrs` for constraints
|
|
78
|
+
|
|
79
|
+
#### 2. Understand the Test
|
|
80
|
+
|
|
81
|
+
Read the test file. Understand:
|
|
82
|
+
- What behavior it expects
|
|
83
|
+
- Why it's currently failing
|
|
84
|
+
- What the fix needs to achieve
|
|
85
|
+
|
|
86
|
+
#### 3. Fix the Code
|
|
87
|
+
|
|
88
|
+
Modify **only non-test files** to make the test pass.
|
|
89
|
+
|
|
90
|
+
**CRITICAL**: Do not modify test files. The test is the specification. Your job is to make the code meet the specification.
|
|
91
|
+
|
|
92
|
+
If you believe the test is wrong, escalate—don't modify it.
|
|
93
|
+
|
|
94
|
+
#### 4. Verify
|
|
95
|
+
|
|
96
|
+
Run the test to confirm it passes:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
[test command]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
If it still fails, keep fixing. If you're stuck, escalate.
|
|
103
|
+
|
|
104
|
+
#### 5. Update Task File
|
|
105
|
+
|
|
106
|
+
After fixing:
|
|
107
|
+
|
|
108
|
+
```yaml
|
|
109
|
+
status: needs_review
|
|
110
|
+
outcome: |
|
|
111
|
+
Fix applied:
|
|
112
|
+
- Modified [file path]: [what changed]
|
|
113
|
+
- Root cause was: [what was actually wrong]
|
|
114
|
+
|
|
115
|
+
Test result:
|
|
116
|
+
- [test name] now passes
|
|
117
|
+
|
|
118
|
+
Files changed:
|
|
119
|
+
- [list of modified files]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### 6. Return
|
|
123
|
+
|
|
124
|
+
Report:
|
|
125
|
+
- What was fixed
|
|
126
|
+
- Files modified
|
|
127
|
+
- Test result (pass/fail)
|
|
128
|
+
|
|
129
|
+
## Escalation
|
|
130
|
+
|
|
131
|
+
**Stop and escalate if:**
|
|
132
|
+
|
|
133
|
+
- **Test mode**: Can't write a meaningful test for the hypothesis (hypothesis too vague)
|
|
134
|
+
- **Fix mode**: The test seems wrong (tests the wrong thing, has a bug itself)
|
|
135
|
+
- **Fix mode**: Can't fix without modifying test files
|
|
136
|
+
- **Either mode**: ADR conflict prevents the approach
|
|
137
|
+
- **Either mode**: Blocked by missing information
|
|
138
|
+
|
|
139
|
+
**When escalating:**
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
## Escalation
|
|
143
|
+
|
|
144
|
+
**Mode**: [test/fix]
|
|
145
|
+
|
|
146
|
+
**Reason**: [why you can't proceed]
|
|
147
|
+
|
|
148
|
+
**What I found**: [relevant details]
|
|
149
|
+
|
|
150
|
+
**Recommendation**: [what should happen next]
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Principles
|
|
154
|
+
|
|
155
|
+
**One mode per session.** You either write a test OR fix code, never both. This ensures clean separation.
|
|
156
|
+
|
|
157
|
+
**Tests are specifications.** In fix mode, the test defines correct behavior. Don't question it—make the code conform.
|
|
158
|
+
|
|
159
|
+
**Fix mode: code only.** Never touch test files in fix mode. If the test is wrong, escalate.
|
|
160
|
+
|
|
161
|
+
**Test mode: fail is success.** A test that fails (because the bug exists) is exactly what we want. A passing test means the hypothesis is wrong.
|
|
162
|
+
|
|
163
|
+
**Outcome is context.** Write clear outcomes so the next session (or agent) understands what was done.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Capture architectural decisions and tribal knowledge before clearing session context
|
|
3
|
+
allowed-tools: Task, AskUserQuestion
|
|
4
|
+
argument-hint: (no arguments)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Finalize Command
|
|
8
|
+
|
|
9
|
+
Capture architectural decisions and tribal knowledge before ending a session.
|
|
10
|
+
|
|
11
|
+
## Purpose
|
|
12
|
+
|
|
13
|
+
Preserve valuable insights from your session:
|
|
14
|
+
- Architectural decisions → ADRs
|
|
15
|
+
- Tribal knowledge/gotchas → CLAUDE.md updates
|
|
16
|
+
|
|
17
|
+
This ensures nothing is lost when context is cleared.
|
|
18
|
+
|
|
19
|
+
## How It Works
|
|
20
|
+
|
|
21
|
+
You review the conversation to identify architectural decisions and tribal knowledge, present findings to the user for confirmation, then invoke specialized agents to capture them.
|
|
22
|
+
|
|
23
|
+
## What Gets Captured
|
|
24
|
+
|
|
25
|
+
### Architectural Decisions (ADRs)
|
|
26
|
+
- Technical choices with documented alternatives
|
|
27
|
+
- "We decided X because Y" decisions
|
|
28
|
+
- Framework or library selections
|
|
29
|
+
- Design pattern adoptions
|
|
30
|
+
|
|
31
|
+
### Tribal Knowledge (CLAUDE.md)
|
|
32
|
+
- Gotchas discovered during debugging
|
|
33
|
+
- Non-obvious behaviors that caused confusion
|
|
34
|
+
- Patterns established during the session
|
|
35
|
+
- "Always do X" or "Never do Y" lessons
|
|
36
|
+
|
|
37
|
+
### What Doesn't Qualify
|
|
38
|
+
|
|
39
|
+
ADR scope:
|
|
40
|
+
- Bug fixes
|
|
41
|
+
- Implementation details
|
|
42
|
+
- Obvious technical choices
|
|
43
|
+
|
|
44
|
+
CLAUDE.md scope:
|
|
45
|
+
- Standard setup procedures
|
|
46
|
+
- Information already in README or package.json
|
|
47
|
+
- Common knowledge (e.g., "npm install installs packages")
|
|
48
|
+
|
|
49
|
+
## Session Review Process
|
|
50
|
+
|
|
51
|
+
1. Analyze the conversation for architectural decisions and tribal knowledge
|
|
52
|
+
2. **Evaluate against criteria and make your assessment** - apply the ADR and CLAUDE.md criteria below
|
|
53
|
+
3. **Present your findings and act on them** - tell the user what you identified and capture it
|
|
54
|
+
4. If user disagrees with your assessment, adjust accordingly
|
|
55
|
+
5. Report what was documented
|
|
56
|
+
|
|
57
|
+
**ADR criteria** - Worth documenting if:
|
|
58
|
+
- Technical choices with documented alternatives ("We decided X because Y")
|
|
59
|
+
- Framework or library selections with rationale
|
|
60
|
+
- Design pattern adoptions that future work should follow
|
|
61
|
+
|
|
62
|
+
**CLAUDE.md criteria** - Worth documenting if:
|
|
63
|
+
- Non-obvious gotchas discovered during the session
|
|
64
|
+
- "Always do X" or "Never do Y" lessons learned
|
|
65
|
+
- Patterns established that aren't obvious from the code
|
|
66
|
+
|
|
67
|
+
**Present your assessment:**
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
## Session Learnings Assessment
|
|
71
|
+
|
|
72
|
+
**ADRs**: [Either "I identified architectural decisions worth documenting: [list with brief reasons]. Creating ADRs now." OR "No architectural decisions in this session warrant ADRs."]
|
|
73
|
+
|
|
74
|
+
**CLAUDE.md**: [Either "I identified tribal knowledge worth capturing: [list]. Updating CLAUDE.md now." OR "No tribal knowledge discovered that warrants CLAUDE.md updates."]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Then invoke agents to capture what you identified:
|
|
78
|
+
- **adr-agent**: Creates ADR files with proper numbering, schema, and index updates
|
|
79
|
+
- **claude-md-agent**: Adds operational insights to the appropriate CLAUDE.md file
|
|
80
|
+
|
|
81
|
+
## If Nothing Identified
|
|
82
|
+
|
|
83
|
+
If the session contained no architectural decisions or tribal knowledge worth capturing, report that to the user and mark the session ready to clear.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* adr-list.js - List ADRs with optional filtering by tags and status
|
|
5
|
+
*
|
|
6
|
+
* This CLI script scans all YAML ADR files in the project's .claude/adrs/
|
|
7
|
+
* and returns their metadata, with optional filtering capabilities. Designed
|
|
8
|
+
* to support progressive ADR filtering (per ADR-007) by providing descriptions
|
|
9
|
+
* without full content.
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* node ~/.claude/scripts/adr-list.js # All ADRs
|
|
13
|
+
* node ~/.claude/scripts/adr-list.js --tags=architecture # Filter by single tag
|
|
14
|
+
* node ~/.claude/scripts/adr-list.js --tags=agents,planning # Filter by multiple tags (OR logic)
|
|
15
|
+
* node ~/.claude/scripts/adr-list.js --status=Accepted # Filter by status
|
|
16
|
+
* node ~/.claude/scripts/adr-list.js --tags=adrs --status=Accepted # Combine filters
|
|
17
|
+
*
|
|
18
|
+
* Flags:
|
|
19
|
+
* --tags=tag1,tag2 Filter ADRs that have ANY of the specified tags (OR logic)
|
|
20
|
+
* --status=Status Filter ADRs that match the exact status string
|
|
21
|
+
*
|
|
22
|
+
* Output:
|
|
23
|
+
* JSON array to stdout: [{ id, title, description, tags, status, date }]
|
|
24
|
+
*
|
|
25
|
+
* Example output:
|
|
26
|
+
* [
|
|
27
|
+
* {
|
|
28
|
+
* "id": "ADR-001",
|
|
29
|
+
* "title": "Use YAML ADRs for Architectural Decisions",
|
|
30
|
+
* "description": "Establishes YAML-formatted ADRs as the primary mechanism...",
|
|
31
|
+
* "tags": ["architecture", "adrs", "format"],
|
|
32
|
+
* "status": "Accepted",
|
|
33
|
+
* "date": "2025-11-22"
|
|
34
|
+
* }
|
|
35
|
+
* ]
|
|
36
|
+
*
|
|
37
|
+
* Note: This script supplements the adr-agent (per ADR-009), providing
|
|
38
|
+
* quick CLI access to ADR listings without invoking the full agent.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
const fs = require('fs');
|
|
42
|
+
const path = require('path');
|
|
43
|
+
const yaml = require('js-yaml');
|
|
44
|
+
|
|
45
|
+
// ADRs are in the project's .claude/adrs/ directory (relative to CWD)
|
|
46
|
+
const ADRS_DIR = path.join(process.cwd(), '.claude', 'adrs');
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Parse command line arguments for --tags and --status flags
|
|
50
|
+
* @returns {{ tags: string[] | null, status: string | null }}
|
|
51
|
+
*/
|
|
52
|
+
function parseArgs() {
|
|
53
|
+
const args = process.argv.slice(2);
|
|
54
|
+
let tags = null;
|
|
55
|
+
let status = null;
|
|
56
|
+
|
|
57
|
+
for (const arg of args) {
|
|
58
|
+
// Handle --tags=value or --tags value
|
|
59
|
+
if (arg.startsWith('--tags=')) {
|
|
60
|
+
const value = arg.slice('--tags='.length);
|
|
61
|
+
tags = value.split(',').map(t => t.trim()).filter(t => t.length > 0);
|
|
62
|
+
}
|
|
63
|
+
// Handle --status=value or --status value
|
|
64
|
+
else if (arg.startsWith('--status=')) {
|
|
65
|
+
status = arg.slice('--status='.length).trim();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return { tags, status };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Read and parse a YAML ADR file, extracting metadata fields
|
|
74
|
+
* @param {string} filePath - Absolute path to the YAML file
|
|
75
|
+
* @returns {{ id: string, title: string, description: string, tags: string[], status: string, date: string } | null}
|
|
76
|
+
*/
|
|
77
|
+
function parseAdrFile(filePath) {
|
|
78
|
+
try {
|
|
79
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
80
|
+
const data = yaml.load(content);
|
|
81
|
+
|
|
82
|
+
// Extract fields, handling missing optional fields gracefully
|
|
83
|
+
const id = data?.id || path.basename(filePath, '.yaml');
|
|
84
|
+
const title = data?.title || '';
|
|
85
|
+
// Clean up description - remove trailing newlines from YAML multi-line strings
|
|
86
|
+
const description = (data?.description || '').trim();
|
|
87
|
+
const tags = Array.isArray(data?.tags) ? data.tags : [];
|
|
88
|
+
const status = data?.status || '';
|
|
89
|
+
const date = data?.date || '';
|
|
90
|
+
|
|
91
|
+
return { id, title, description, tags, status, date };
|
|
92
|
+
} catch (error) {
|
|
93
|
+
// Log to stderr so it doesn't pollute JSON output
|
|
94
|
+
console.error(`Warning: Failed to parse ${filePath}: ${error.message}`);
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Check if an ADR matches the given tag filter (OR logic)
|
|
101
|
+
* @param {string[]} adrTags - Tags from the ADR
|
|
102
|
+
* @param {string[]} filterTags - Tags to filter by
|
|
103
|
+
* @returns {boolean} - True if ADR has ANY of the filter tags
|
|
104
|
+
*/
|
|
105
|
+
function matchesTags(adrTags, filterTags) {
|
|
106
|
+
if (!filterTags || filterTags.length === 0) {
|
|
107
|
+
return true; // No filter = match all
|
|
108
|
+
}
|
|
109
|
+
// OR logic: return true if ADR has ANY of the filter tags
|
|
110
|
+
return filterTags.some(filterTag => adrTags.includes(filterTag));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Check if an ADR matches the given status filter (exact match)
|
|
115
|
+
* @param {string} adrStatus - Status from the ADR
|
|
116
|
+
* @param {string | null} filterStatus - Status to filter by
|
|
117
|
+
* @returns {boolean} - True if status matches exactly
|
|
118
|
+
*/
|
|
119
|
+
function matchesStatus(adrStatus, filterStatus) {
|
|
120
|
+
if (!filterStatus) {
|
|
121
|
+
return true; // No filter = match all
|
|
122
|
+
}
|
|
123
|
+
return adrStatus === filterStatus;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Scan ADRs directory and collect all ADR metadata with optional filtering
|
|
128
|
+
* @param {{ tags: string[] | null, status: string | null }} filters
|
|
129
|
+
* @returns {Array<{ id: string, title: string, description: string, tags: string[], status: string, date: string }>}
|
|
130
|
+
*/
|
|
131
|
+
function collectAdrs(filters) {
|
|
132
|
+
const adrs = [];
|
|
133
|
+
|
|
134
|
+
// Check if ADRs directory exists
|
|
135
|
+
if (!fs.existsSync(ADRS_DIR)) {
|
|
136
|
+
console.error(`Warning: ADRs directory not found: ${ADRS_DIR}`);
|
|
137
|
+
return adrs;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Read all .yaml files in the ADRs directory
|
|
141
|
+
const files = fs.readdirSync(ADRS_DIR)
|
|
142
|
+
.filter(file => file.endsWith('.yaml'))
|
|
143
|
+
.sort() // Sort alphabetically for consistent ordering
|
|
144
|
+
.map(file => path.join(ADRS_DIR, file));
|
|
145
|
+
|
|
146
|
+
// Process each ADR file
|
|
147
|
+
for (const filePath of files) {
|
|
148
|
+
const adr = parseAdrFile(filePath);
|
|
149
|
+
if (!adr) continue;
|
|
150
|
+
|
|
151
|
+
// Apply filters
|
|
152
|
+
if (!matchesTags(adr.tags, filters.tags)) continue;
|
|
153
|
+
if (!matchesStatus(adr.status, filters.status)) continue;
|
|
154
|
+
|
|
155
|
+
adrs.push(adr);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return adrs;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Main execution
|
|
162
|
+
function main() {
|
|
163
|
+
const filters = parseArgs();
|
|
164
|
+
const adrs = collectAdrs(filters);
|
|
165
|
+
|
|
166
|
+
// Output JSON to stdout
|
|
167
|
+
console.log(JSON.stringify(adrs, null, 2));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
main();
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* adr-tags.js - List all distinct tags across all ADRs with usage counts
|
|
5
|
+
*
|
|
6
|
+
* This CLI script scans all YAML ADR files in the project's .claude/adrs/
|
|
7
|
+
* and extracts tag information, producing a sorted summary of tag usage.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node ~/.claude/scripts/adr-tags.js
|
|
11
|
+
*
|
|
12
|
+
* Output:
|
|
13
|
+
* JSON array to stdout: [{ tag, count, adrs }] sorted by count descending
|
|
14
|
+
*
|
|
15
|
+
* Example output:
|
|
16
|
+
* [
|
|
17
|
+
* { "tag": "architecture", "count": 8, "adrs": ["ADR-001", "ADR-003"] },
|
|
18
|
+
* { "tag": "agents", "count": 4, "adrs": ["ADR-003", "ADR-004"] }
|
|
19
|
+
* ]
|
|
20
|
+
*
|
|
21
|
+
* Note: This script supplements the adr-agent (per ADR-009), providing
|
|
22
|
+
* quick CLI access to tag information without invoking the full agent.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const path = require('path');
|
|
27
|
+
const yaml = require('js-yaml');
|
|
28
|
+
|
|
29
|
+
// ADRs are in the project's .claude/adrs/ directory (relative to CWD)
|
|
30
|
+
const ADRS_DIR = path.join(process.cwd(), '.claude', 'adrs');
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Read and parse a YAML ADR file, extracting id and tags
|
|
34
|
+
* @param {string} filePath - Absolute path to the YAML file
|
|
35
|
+
* @returns {{ id: string, tags: string[] } | null} - Parsed data or null on error
|
|
36
|
+
*/
|
|
37
|
+
function parseAdrFile(filePath) {
|
|
38
|
+
try {
|
|
39
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
40
|
+
const data = yaml.load(content);
|
|
41
|
+
|
|
42
|
+
// Extract id and tags, handling missing fields gracefully
|
|
43
|
+
const id = data?.id || path.basename(filePath, '.yaml');
|
|
44
|
+
const tags = Array.isArray(data?.tags) ? data.tags : [];
|
|
45
|
+
|
|
46
|
+
return { id, tags };
|
|
47
|
+
} catch (error) {
|
|
48
|
+
// Log to stderr so it doesn't pollute JSON output
|
|
49
|
+
console.error(`Warning: Failed to parse ${filePath}: ${error.message}`);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Scan ADRs directory and collect all tag information
|
|
56
|
+
* @returns {Map<string, string[]>} - Map of tag -> array of ADR ids
|
|
57
|
+
*/
|
|
58
|
+
function collectTags() {
|
|
59
|
+
const tagMap = new Map();
|
|
60
|
+
|
|
61
|
+
// Check if ADRs directory exists
|
|
62
|
+
if (!fs.existsSync(ADRS_DIR)) {
|
|
63
|
+
console.error(`Warning: ADRs directory not found: ${ADRS_DIR}`);
|
|
64
|
+
return tagMap;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Read all .yaml files in the ADRs directory
|
|
68
|
+
const files = fs.readdirSync(ADRS_DIR)
|
|
69
|
+
.filter(file => file.endsWith('.yaml'))
|
|
70
|
+
.map(file => path.join(ADRS_DIR, file));
|
|
71
|
+
|
|
72
|
+
// Process each ADR file
|
|
73
|
+
for (const filePath of files) {
|
|
74
|
+
const adr = parseAdrFile(filePath);
|
|
75
|
+
if (!adr) continue;
|
|
76
|
+
|
|
77
|
+
// Add this ADR's id to each of its tags
|
|
78
|
+
for (const tag of adr.tags) {
|
|
79
|
+
if (!tagMap.has(tag)) {
|
|
80
|
+
tagMap.set(tag, []);
|
|
81
|
+
}
|
|
82
|
+
tagMap.get(tag).push(adr.id);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return tagMap;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Format tag data as sorted output array
|
|
91
|
+
* @param {Map<string, string[]>} tagMap - Map of tag -> array of ADR ids
|
|
92
|
+
* @returns {Array<{ tag: string, count: number, adrs: string[] }>}
|
|
93
|
+
*/
|
|
94
|
+
function formatOutput(tagMap) {
|
|
95
|
+
const result = [];
|
|
96
|
+
|
|
97
|
+
for (const [tag, adrs] of tagMap.entries()) {
|
|
98
|
+
result.push({
|
|
99
|
+
tag,
|
|
100
|
+
count: adrs.length,
|
|
101
|
+
adrs: adrs.sort() // Sort ADR ids alphabetically within each tag
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Sort by count descending, then alphabetically by tag name for ties
|
|
106
|
+
result.sort((a, b) => {
|
|
107
|
+
if (b.count !== a.count) {
|
|
108
|
+
return b.count - a.count;
|
|
109
|
+
}
|
|
110
|
+
return a.tag.localeCompare(b.tag);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Main execution
|
|
117
|
+
function main() {
|
|
118
|
+
const tagMap = collectTags();
|
|
119
|
+
const output = formatOutput(tagMap);
|
|
120
|
+
|
|
121
|
+
// Output JSON to stdout
|
|
122
|
+
console.log(JSON.stringify(output, null, 2));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
main();
|