opencode-codegraph 0.1.22 → 0.1.24

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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.24 - 2026-03-20
4
+
5
+ - surface workflow-state transitions after later bash interactions so async state changes become visible without waiting for explicit status checks
6
+
7
+ ## 0.1.23 - 2026-03-20
8
+
9
+ - surface workflow-state transitions on later interactions so async status changes become visible without waiting for a fresh session
10
+
3
11
  ## 0.1.22 - 2026-03-20
4
12
 
5
13
  - add DuckDB maintenance guidance and `/maintain-db` workflow command for local database compaction
package/README.md CHANGED
@@ -117,9 +117,11 @@ Place in `.opencode/commands/`:
117
117
  | `tool.execute.after` on test commands | Add after-test workflow guidance and detect failed test runs before suggesting the next command |
118
118
  | `tool.execute.after` on `git status` / `git diff` | Add current workflow guidance directly to common git inspection commands |
119
119
  | `tool.execute.after` | Trigger CPG update after git commit and append structured post-commit review summary |
120
+ | generic `tool.execute.after` | Surface workflow-state transitions after other bash commands when the underlying state changes |
120
121
  | `codegraph_review` tool | Returns review results together with current workflow guidance and suggested follow-up command |
121
122
  | `experimental.session.compacting` | Preserve current dogfooding status when OpenCode compacts long sessions |
122
123
  | `command.execute.before` | Inject current dogfooding status into `/review`, `/audit`, `/update`, `/status`, and `/next` |
124
+ | `db_locked` recovery path | Surface lock-holder-aware recovery guidance when DuckDB is blocked by another process |
123
125
  | `permission.ask` | Auto-allow `codegraph_*` tools |
124
126
 
125
127
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-codegraph",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "OpenCode plugin for CodeGraph CPG-powered code analysis",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/index.ts CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  formatPendingReviewTraceSummary,
24
24
  formatReviewTraceSummary,
25
25
  formatReviewTraceUpdateSummary,
26
+ formatWorkflowStateTransition,
26
27
  formatWorkflowGuidanceBlock,
27
28
  getHeadCommit,
28
29
  isGitCommit,
@@ -48,6 +49,32 @@ const codegraphPlugin: Plugin = async (input) => {
48
49
  // Detect project ID from environment or opencode.json
49
50
  const projectId = process.env.CODEGRAPH_PROJECT || ""
50
51
  let pendingReviewCommit: string | null = null
52
+ let lastWorkflowState: string | null = null
53
+
54
+ const workflowFields = (status: Record<string, unknown>) => {
55
+ const workflowState = typeof status.workflow_state === "string" ? status.workflow_state : null
56
+ const nextAction =
57
+ typeof status.recommended_next_action === "string" ? status.recommended_next_action : undefined
58
+ const nextCommand =
59
+ typeof status.recommended_command === "string" ? status.recommended_command : undefined
60
+ return { workflowState, nextAction, nextCommand }
61
+ }
62
+
63
+ const captureWorkflowState = (status: Record<string, unknown>) => {
64
+ const { workflowState } = workflowFields(status)
65
+ if (workflowState) {
66
+ lastWorkflowState = workflowState
67
+ }
68
+ }
69
+
70
+ const workflowTransition = (status: Record<string, unknown>) => {
71
+ const { workflowState, nextAction, nextCommand } = workflowFields(status)
72
+ const message = formatWorkflowStateTransition(lastWorkflowState, workflowState, nextAction, nextCommand)
73
+ if (workflowState) {
74
+ lastWorkflowState = workflowState
75
+ }
76
+ return message
77
+ }
51
78
 
52
79
  const consumePendingReviewTraceUpdate = async (): Promise<string | null> => {
53
80
  if (!pendingReviewCommit) return null
@@ -67,10 +94,12 @@ const codegraphPlugin: Plugin = async (input) => {
67
94
  "experimental.chat.system.transform": async (_inp, output) => {
68
95
  try {
69
96
  const rawStatus = await $`python -m src.cli.import_commands dogfood status --json`.quiet().text()
70
- const statusSummary = formatDogfoodStatusSummary(JSON.parse(rawStatus))
97
+ const status = JSON.parse(rawStatus)
98
+ const statusSummary = formatDogfoodStatusSummary(status)
71
99
  if (statusSummary) {
72
100
  output.system.push(statusSummary)
73
101
  }
102
+ captureWorkflowState(status)
74
103
  } catch {
75
104
  // Dogfooding status unavailable — keep session startup lightweight
76
105
  }
@@ -91,10 +120,12 @@ const codegraphPlugin: Plugin = async (input) => {
91
120
  "experimental.session.compacting": async (_inp, output) => {
92
121
  try {
93
122
  const rawStatus = await $`python -m src.cli.import_commands dogfood status --json`.quiet().text()
94
- const statusSummary = formatDogfoodStatusSummary(JSON.parse(rawStatus))
123
+ const status = JSON.parse(rawStatus)
124
+ const statusSummary = formatDogfoodStatusSummary(status)
95
125
  if (statusSummary) {
96
126
  output.context.push(statusSummary)
97
127
  }
128
+ captureWorkflowState(status)
98
129
  } catch {
99
130
  // Keep compaction resilient if status lookup fails
100
131
  }
@@ -118,7 +149,15 @@ const codegraphPlugin: Plugin = async (input) => {
118
149
  }
119
150
 
120
151
  const rawStatus = await $`python -m src.cli.import_commands dogfood status --json`.quiet().text()
121
- const statusSummary = formatDogfoodStatusSummary(JSON.parse(rawStatus))
152
+ const status = JSON.parse(rawStatus)
153
+ const transition = workflowTransition(status)
154
+ if (transition) {
155
+ output.parts.push({
156
+ type: "text",
157
+ text: transition,
158
+ } as any)
159
+ }
160
+ const statusSummary = formatDogfoodStatusSummary(status)
122
161
  if (statusSummary) {
123
162
  output.parts.push({
124
163
  type: "text",
@@ -152,7 +191,15 @@ const codegraphPlugin: Plugin = async (input) => {
152
191
  if (workflowIntent) {
153
192
  try {
154
193
  const rawStatus = await $`python -m src.cli.import_commands dogfood status --json`.quiet().text()
155
- const statusSummary = formatDogfoodStatusSummary(JSON.parse(rawStatus))
194
+ const status = JSON.parse(rawStatus)
195
+ const transition = workflowTransition(status)
196
+ if (transition) {
197
+ output.parts.push({
198
+ type: "text",
199
+ text: transition,
200
+ } as any)
201
+ }
202
+ const statusSummary = formatDogfoodStatusSummary(status)
156
203
  if (statusSummary) {
157
204
  output.parts.push({
158
205
  type: "text",
@@ -201,17 +248,20 @@ const codegraphPlugin: Plugin = async (input) => {
201
248
  "tool.execute.after": async (inp, output) => {
202
249
  if (inp.tool !== "bash") return
203
250
  const command = typeof inp.args?.command === "string" ? inp.args.command : ""
251
+ let appendedWorkflowGuidance = false
204
252
 
205
253
  if (isTestCommand(command)) {
206
254
  try {
207
255
  const rawStatus = await $`python -m src.cli.import_commands dogfood status --json`.quiet().text()
208
256
  const status = JSON.parse(rawStatus)
257
+ const transition = workflowTransition(status)
209
258
  const testFailed = didTestCommandFail(output.output)
210
259
  const guidance = formatAfterTestGuidance(status, testFailed)
211
260
  if (guidance) {
212
261
  output.title = "CodeGraph: after-test guidance ready"
213
262
  const existingOutput = output.output?.trimEnd() || ""
214
- output.output = existingOutput ? `${existingOutput}\n\n${guidance}` : guidance
263
+ output.output = [existingOutput, transition || "", guidance].filter(Boolean).join("\n\n")
264
+ appendedWorkflowGuidance = true
215
265
  output.metadata = {
216
266
  ...output.metadata,
217
267
  codegraph_after_test_result: testFailed ? "failed" : "passed_or_unknown",
@@ -229,11 +279,13 @@ const codegraphPlugin: Plugin = async (input) => {
229
279
  try {
230
280
  const rawStatus = await $`python -m src.cli.import_commands dogfood status --json`.quiet().text()
231
281
  const status = JSON.parse(rawStatus)
282
+ const transition = workflowTransition(status)
232
283
  const guidance = formatDogfoodStatusSummary(status)
233
284
  if (guidance) {
234
285
  output.title = "CodeGraph: git workflow guidance ready"
235
286
  const existingOutput = output.output?.trimEnd() || ""
236
- output.output = existingOutput ? `${existingOutput}\n\n${guidance}` : guidance
287
+ output.output = [existingOutput, transition || "", guidance].filter(Boolean).join("\n\n")
288
+ appendedWorkflowGuidance = true
237
289
  output.metadata = {
238
290
  ...output.metadata,
239
291
  codegraph_git_workflow_state: status.workflow_state || null,
@@ -246,6 +298,21 @@ const codegraphPlugin: Plugin = async (input) => {
246
298
  }
247
299
  }
248
300
 
301
+ if (!appendedWorkflowGuidance) {
302
+ try {
303
+ const rawStatus = await $`python -m src.cli.import_commands dogfood status --json`.quiet().text()
304
+ const status = JSON.parse(rawStatus)
305
+ const transition = workflowTransition(status)
306
+ if (transition) {
307
+ output.title = "CodeGraph: workflow update detected"
308
+ const existingOutput = output.output?.trimEnd() || ""
309
+ output.output = [existingOutput, transition].filter(Boolean).join("\n\n")
310
+ }
311
+ } catch {
312
+ // Best-effort guidance only
313
+ }
314
+ }
315
+
249
316
  if (!isGitCommit(output.output)) return
250
317
 
251
318
  try {
package/src/util.ts CHANGED
@@ -457,6 +457,31 @@ export function formatWorkflowGuidanceBlock(snapshot: DogfoodStatusSnapshot): st
457
457
  return lines.join("\n")
458
458
  }
459
459
 
460
+ export function formatWorkflowStateTransition(
461
+ previousState: string | null,
462
+ currentState: string | null,
463
+ nextAction: string | undefined,
464
+ nextCommand: string | undefined,
465
+ ): string | null {
466
+ if (!previousState || !currentState || previousState === currentState) {
467
+ return null
468
+ }
469
+
470
+ const lines = [
471
+ "## CodeGraph Workflow Update",
472
+ "",
473
+ `- Workflow state changed: ${previousState} -> ${currentState}`,
474
+ ]
475
+
476
+ if (nextAction) {
477
+ lines.push(`- Next action: ${nextAction}`)
478
+ }
479
+ if (nextCommand) {
480
+ lines.push(`- Suggested command: ${nextCommand}`)
481
+ }
482
+ return lines.join("\n")
483
+ }
484
+
460
485
  export function formatAfterTestGuidance(
461
486
  snapshot: DogfoodStatusSnapshot,
462
487
  testFailed = false,