@comfanion/workflow 4.33.1 → 4.35.0
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/package.json
CHANGED
package/src/build-info.json
CHANGED
|
@@ -16,12 +16,12 @@ tools:
|
|
|
16
16
|
glob: true
|
|
17
17
|
grep: true
|
|
18
18
|
list: true
|
|
19
|
-
skill:
|
|
19
|
+
skill: true # No skill loading - just execute
|
|
20
20
|
question: false # No questions - execute or fail
|
|
21
21
|
bash: true # Full bash for tests, builds
|
|
22
22
|
webfetch: false # No web access
|
|
23
|
-
todowrite:
|
|
24
|
-
todoread:
|
|
23
|
+
todowrite: true # Not available for subagents (Claude internal tools)
|
|
24
|
+
todoread: true # Not available for subagents
|
|
25
25
|
lsp: true # Code intelligence
|
|
26
26
|
|
|
27
27
|
# Permissions - fast execution, no prompts
|
|
@@ -48,14 +48,57 @@ permission:
|
|
|
48
48
|
<r>Find and use `**/project-context.md` and `CLAUDE.md` as source of truth</r>
|
|
49
49
|
</rules>
|
|
50
50
|
|
|
51
|
-
<dev-story-workflow hint="When executing dev-story
|
|
51
|
+
<dev-story-workflow hint="When executing /dev-story command" critical="FOLLOW THIS EXACTLY">
|
|
52
|
+
<!-- PHASE 1: SETUP -->
|
|
52
53
|
<step n="1">READ the entire story file BEFORE any implementation</step>
|
|
53
54
|
<step n="2">Load project-context.md and CLAUDE.md if available</step>
|
|
54
|
-
<step n="3">
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
<step n="3">CREATE TODO LIST from story tasks using todowrite:
|
|
56
|
+
- Each task becomes a TODO item
|
|
57
|
+
- Set priority based on task order (first = high)
|
|
58
|
+
- All tasks start as "pending"
|
|
59
|
+
</step>
|
|
60
|
+
<step n="4">Mark story status as "in-progress"</step>
|
|
61
|
+
|
|
62
|
+
<!-- PHASE 2: IMPLEMENTATION LOOP -->
|
|
63
|
+
<step n="5">FOR EACH TASK in order:
|
|
64
|
+
a) Update TODO: mark current task as "in_progress"
|
|
65
|
+
b) Call @coder with specific task instructions:
|
|
66
|
+
- Include task requirements
|
|
67
|
+
- Include acceptance criteria
|
|
68
|
+
- Include relevant file paths
|
|
69
|
+
- Request: test first, then implement
|
|
70
|
+
c) VERIFY @coder result:
|
|
71
|
+
- Check tests exist and pass
|
|
72
|
+
- Check implementation matches AC
|
|
73
|
+
- If failed: retry or HALT
|
|
74
|
+
d) Update TODO: mark task as "completed"
|
|
75
|
+
e) Update story file: mark task [x]
|
|
76
|
+
f) Run test suite - HALT if failures
|
|
77
|
+
</step>
|
|
78
|
+
|
|
79
|
+
<!-- PHASE 3: FINALIZATION -->
|
|
80
|
+
<step n="6">Run FULL test suite - all tests must pass</step>
|
|
81
|
+
<step n="7">Update story file: File List, Change Log, Dev Agent Record</step>
|
|
82
|
+
<step n="8">Clear TODO list (all done)</step>
|
|
83
|
+
<step n="9">Mark story status as "review"</step>
|
|
58
84
|
</dev-story-workflow>
|
|
85
|
+
|
|
86
|
+
<todo-usage hint="How to use TODO for tracking">
|
|
87
|
+
<create>
|
|
88
|
+
todowrite([
|
|
89
|
+
{ id: "story-task-1", content: "Task 1: Create entity", status: "pending", priority: "high" },
|
|
90
|
+
{ id: "story-task-2", content: "Task 2: Add repository", status: "pending", priority: "medium" },
|
|
91
|
+
...
|
|
92
|
+
])
|
|
93
|
+
</create>
|
|
94
|
+
<update-progress>
|
|
95
|
+
todoread() → get current list
|
|
96
|
+
todowrite([...list with task.status = "in_progress"])
|
|
97
|
+
</update-progress>
|
|
98
|
+
<mark-complete>
|
|
99
|
+
todowrite([...list with task.status = "completed"])
|
|
100
|
+
</mark-complete>
|
|
101
|
+
</todo-usage>
|
|
59
102
|
</activation>
|
|
60
103
|
|
|
61
104
|
<persona>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# /dev-story Command
|
|
2
2
|
|
|
3
|
-
Implement a story using red-green-refactor cycle.
|
|
3
|
+
Implement a story using red-green-refactor cycle with TODO tracking.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
@@ -18,16 +18,48 @@ This command invokes the **Dev** agent (Amelia).
|
|
|
18
18
|
|
|
19
19
|
## Process
|
|
20
20
|
|
|
21
|
+
### Phase 1: Setup
|
|
21
22
|
1. Find or load story file
|
|
22
23
|
2. Load project context (CLAUDE.md, project-context.md)
|
|
23
|
-
3.
|
|
24
|
-
4.
|
|
24
|
+
3. **Create TODO list from story tasks** (for progress tracking)
|
|
25
|
+
4. Mark story as `in-progress`
|
|
26
|
+
|
|
27
|
+
### Phase 2: Implementation (for each task)
|
|
28
|
+
5. **Mark task as `in_progress` in TODO**
|
|
29
|
+
6. Delegate to @coder:
|
|
25
30
|
- 🔴 RED: Write failing test
|
|
26
31
|
- 🟢 GREEN: Implement minimal code to pass
|
|
27
32
|
- 🔵 REFACTOR: Improve while keeping tests green
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
33
|
+
7. Verify @coder result (tests pass)
|
|
34
|
+
8. **Mark task as `completed` in TODO**
|
|
35
|
+
9. Mark task `[x]` in story file
|
|
36
|
+
|
|
37
|
+
### Phase 3: Finalization
|
|
38
|
+
10. Run full test suite
|
|
39
|
+
11. Update story file (File List, Change Log, Dev Agent Record)
|
|
40
|
+
12. **Clear TODO** (all tasks done)
|
|
41
|
+
13. Mark story as `review`
|
|
42
|
+
|
|
43
|
+
## TODO Workflow
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
┌─────────────────────────────────────────────────┐
|
|
47
|
+
│ @dev reads story → creates TODO: │
|
|
48
|
+
│ ┌─────────────────────────────────────────┐ │
|
|
49
|
+
│ │ [ ] Task 1: Create User entity │ │
|
|
50
|
+
│ │ [ ] Task 2: Add repository │ │
|
|
51
|
+
│ │ [ ] Task 3: Write integration tests │ │
|
|
52
|
+
│ └─────────────────────────────────────────┘ │
|
|
53
|
+
│ │
|
|
54
|
+
│ For each task: │
|
|
55
|
+
│ 1. @dev marks [→] in_progress in TODO │
|
|
56
|
+
│ 2. @dev calls @coder with task details │
|
|
57
|
+
│ 3. @coder implements (no TODO access) │
|
|
58
|
+
│ 4. @dev verifies result │
|
|
59
|
+
│ 5. @dev marks [✓] completed in TODO │
|
|
60
|
+
│ 6. @dev marks [x] in story file │
|
|
61
|
+
└─────────────────────────────────────────────────┘
|
|
62
|
+
```
|
|
31
63
|
|
|
32
64
|
## Skills Loaded
|
|
33
65
|
|
package/src/opencode/config.yaml
CHANGED
|
@@ -5,13 +5,15 @@ import fs from "fs/promises"
|
|
|
5
5
|
/**
|
|
6
6
|
* File Indexer Plugin
|
|
7
7
|
*
|
|
8
|
-
* Automatically
|
|
8
|
+
* Automatically manages semantic search indexes:
|
|
9
|
+
* - On session start/resume: freshen existing indexes (update stale files)
|
|
10
|
+
* - On file edit: queue file for reindexing (debounced)
|
|
9
11
|
*
|
|
10
12
|
* Configuration in .opencode/config.yaml:
|
|
11
13
|
* vectorizer:
|
|
12
14
|
* enabled: true # Master switch
|
|
13
15
|
* auto_index: true # Enable this plugin
|
|
14
|
-
* debounce_ms:
|
|
16
|
+
* debounce_ms: 100 # Wait time before indexing
|
|
15
17
|
*
|
|
16
18
|
* Debug mode: set DEBUG=file-indexer or DEBUG=* to see logs
|
|
17
19
|
*/
|
|
@@ -22,7 +24,7 @@ const DEBUG = process.env.DEBUG?.includes('file-indexer') || process.env.DEBUG =
|
|
|
22
24
|
const DEFAULT_CONFIG = {
|
|
23
25
|
enabled: true,
|
|
24
26
|
auto_index: true,
|
|
25
|
-
debounce_ms:
|
|
27
|
+
debounce_ms: 1000,
|
|
26
28
|
indexes: {
|
|
27
29
|
code: { enabled: true, extensions: ['.js', '.ts', '.jsx', '.tsx', '.py', '.go', '.rs', '.java', '.kt', '.swift', '.c', '.cpp', '.h', '.hpp', '.cs', '.rb', '.php', '.scala', '.clj'] },
|
|
28
30
|
docs: { enabled: true, extensions: ['.md', '.mdx', '.txt', '.rst', '.adoc'] },
|
|
@@ -108,6 +110,58 @@ async function isVectorizerInstalled(projectRoot: string): Promise<boolean> {
|
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
|
|
113
|
+
async function hasIndex(projectRoot: string, indexName: string): Promise<boolean> {
|
|
114
|
+
try {
|
|
115
|
+
await fs.access(path.join(projectRoot, ".opencode", "vectors", indexName, "hashes.json"))
|
|
116
|
+
return true
|
|
117
|
+
} catch {
|
|
118
|
+
return false
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Ensure index exists and is fresh on session start
|
|
124
|
+
*/
|
|
125
|
+
async function ensureIndexOnSessionStart(projectRoot: string, config: VectorizerConfig): Promise<void> {
|
|
126
|
+
if (!await isVectorizerInstalled(projectRoot)) {
|
|
127
|
+
debug('Session start: vectorizer not installed, skipping')
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const vectorizerModule = path.join(projectRoot, ".opencode", "vectorizer", "index.js")
|
|
133
|
+
const { CodebaseIndexer } = await import(`file://${vectorizerModule}`)
|
|
134
|
+
|
|
135
|
+
// Check each enabled index
|
|
136
|
+
for (const [indexName, indexConfig] of Object.entries(config.indexes)) {
|
|
137
|
+
if (!indexConfig.enabled) continue
|
|
138
|
+
|
|
139
|
+
const indexExists = await hasIndex(projectRoot, indexName)
|
|
140
|
+
|
|
141
|
+
if (!indexExists) {
|
|
142
|
+
// No index - need full indexing (but don't block session, just log)
|
|
143
|
+
debug(`Session start: index "${indexName}" not found, run: npx @comfanion/workflow index --index ${indexName}`)
|
|
144
|
+
continue
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Index exists - freshen it (update stale files)
|
|
148
|
+
debug(`Session start: freshening index "${indexName}"...`)
|
|
149
|
+
const indexer = await new CodebaseIndexer(projectRoot, indexName).init()
|
|
150
|
+
const stats = await indexer.freshen()
|
|
151
|
+
|
|
152
|
+
if (stats.updated > 0 || stats.deleted > 0) {
|
|
153
|
+
debug(`Session start: ${indexName} - updated ${stats.updated}, deleted ${stats.deleted}`)
|
|
154
|
+
} else {
|
|
155
|
+
debug(`Session start: ${indexName} - index is fresh`)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
await indexer.unloadModel()
|
|
159
|
+
}
|
|
160
|
+
} catch (e) {
|
|
161
|
+
debug(`Session start error: ${(e as Error).message}`)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
111
165
|
async function processPendingFiles(projectRoot: string, config: VectorizerConfig): Promise<void> {
|
|
112
166
|
if (pendingFiles.size === 0) return
|
|
113
167
|
|
|
@@ -196,10 +250,21 @@ export const FileIndexerPlugin: Plugin = async ({ directory }) => {
|
|
|
196
250
|
}, config.debounce_ms + 100)
|
|
197
251
|
}
|
|
198
252
|
|
|
253
|
+
// Track if we've already freshened this session
|
|
254
|
+
let sessionFreshened = false
|
|
255
|
+
|
|
199
256
|
return {
|
|
200
257
|
event: async (ctx) => {
|
|
201
258
|
const event = ctx.event
|
|
202
259
|
|
|
260
|
+
// Freshen index on session start/resume
|
|
261
|
+
if ((event.type === "session.started" || event.type === "session.resumed") && !sessionFreshened) {
|
|
262
|
+
sessionFreshened = true
|
|
263
|
+
debug(`${event.type}: checking indexes...`)
|
|
264
|
+
// Run async, don't block
|
|
265
|
+
ensureIndexOnSessionStart(directory, config).catch(e => debug(`Error: ${e.message}`))
|
|
266
|
+
}
|
|
267
|
+
|
|
203
268
|
if (event.type === "file.edited") {
|
|
204
269
|
const props = (event as any).properties || {}
|
|
205
270
|
const filePath = props.file || props.path || props.filePath
|