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 +8 -0
- package/README.md +2 -0
- package/package.json +1 -1
- package/src/index.ts +73 -6
- package/src/util.ts +25 -0
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
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|