@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@comfanion/workflow",
3
- "version": "4.33.1",
3
+ "version": "4.35.0",
4
4
  "description": "Initialize OpenCode Workflow system for AI-assisted development with semantic code search",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "3.0.0",
3
- "buildDate": "2026-01-24T12:13:18.909Z",
3
+ "buildDate": "2026-01-24T13:22:48.754Z",
4
4
  "files": [
5
5
  "config.yaml",
6
6
  "FLOW.yaml",
@@ -16,12 +16,12 @@ tools:
16
16
  glob: true
17
17
  grep: true
18
18
  list: true
19
- skill: false # No skill loading - just execute
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: false # Subagent - no todo
24
- todoread: false
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 skill">
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">Execute tasks/subtasks IN ORDER as written in story file</step>
55
- <step n="4">For each task: follow red-green-refactor cycle</step>
56
- <step n="5">Mark task [x] ONLY when implementation AND tests are complete</step>
57
- <step n="6">Run full test suite after each task - NEVER proceed with failing tests</step>
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. Mark story as `in-progress`
24
- 4. For each task/subtask:
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
- 5. Mark task complete `[x]` only when tests pass
29
- 6. Update story file (File List, Change Log, Dev Agent Record)
30
- 7. Mark story as `review` when all tasks complete
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
 
@@ -233,7 +233,7 @@ vectorizer:
233
233
  auto_index: true
234
234
 
235
235
  # Debounce time in ms (wait before indexing after file change)
236
- debounce_ms: 2000
236
+ debounce_ms: 1000
237
237
 
238
238
  # Indexes to maintain
239
239
  indexes:
@@ -5,13 +5,15 @@ import fs from "fs/promises"
5
5
  /**
6
6
  * File Indexer Plugin
7
7
  *
8
- * Automatically reindexes changed files for semantic search.
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: 2000 # Wait time before indexing
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: 2000,
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