opencode-codegraph 0.1.21 → 0.1.23
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 +1 -0
- package/package.json +1 -1
- package/src/index.ts +55 -6
- package/src/util.ts +37 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.23 - 2026-03-20
|
|
4
|
+
|
|
5
|
+
- surface workflow-state transitions on later interactions so async status changes become visible without waiting for a fresh session
|
|
6
|
+
|
|
7
|
+
## 0.1.22 - 2026-03-20
|
|
8
|
+
|
|
9
|
+
- add DuckDB maintenance guidance and `/maintain-db` workflow command for local database compaction
|
|
10
|
+
|
|
3
11
|
## 0.1.21 - 2026-03-20
|
|
4
12
|
|
|
5
13
|
- append workflow guidance to the `codegraph_review` custom tool so tool results stay aligned with current dogfooding state
|
package/README.md
CHANGED
|
@@ -120,6 +120,7 @@ Place in `.opencode/commands/`:
|
|
|
120
120
|
| `codegraph_review` tool | Returns review results together with current workflow guidance and suggested follow-up command |
|
|
121
121
|
| `experimental.session.compacting` | Preserve current dogfooding status when OpenCode compacts long sessions |
|
|
122
122
|
| `command.execute.before` | Inject current dogfooding status into `/review`, `/audit`, `/update`, `/status`, and `/next` |
|
|
123
|
+
| `db_locked` recovery path | Surface lock-holder-aware recovery guidance when DuckDB is blocked by another process |
|
|
123
124
|
| `permission.ask` | Auto-allow `codegraph_*` tools |
|
|
124
125
|
|
|
125
126
|
## 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",
|
|
@@ -206,12 +253,13 @@ const codegraphPlugin: Plugin = async (input) => {
|
|
|
206
253
|
try {
|
|
207
254
|
const rawStatus = await $`python -m src.cli.import_commands dogfood status --json`.quiet().text()
|
|
208
255
|
const status = JSON.parse(rawStatus)
|
|
256
|
+
const transition = workflowTransition(status)
|
|
209
257
|
const testFailed = didTestCommandFail(output.output)
|
|
210
258
|
const guidance = formatAfterTestGuidance(status, testFailed)
|
|
211
259
|
if (guidance) {
|
|
212
260
|
output.title = "CodeGraph: after-test guidance ready"
|
|
213
261
|
const existingOutput = output.output?.trimEnd() || ""
|
|
214
|
-
output.output = existingOutput
|
|
262
|
+
output.output = [existingOutput, transition || "", guidance].filter(Boolean).join("\n\n")
|
|
215
263
|
output.metadata = {
|
|
216
264
|
...output.metadata,
|
|
217
265
|
codegraph_after_test_result: testFailed ? "failed" : "passed_or_unknown",
|
|
@@ -229,11 +277,12 @@ const codegraphPlugin: Plugin = async (input) => {
|
|
|
229
277
|
try {
|
|
230
278
|
const rawStatus = await $`python -m src.cli.import_commands dogfood status --json`.quiet().text()
|
|
231
279
|
const status = JSON.parse(rawStatus)
|
|
280
|
+
const transition = workflowTransition(status)
|
|
232
281
|
const guidance = formatDogfoodStatusSummary(status)
|
|
233
282
|
if (guidance) {
|
|
234
283
|
output.title = "CodeGraph: git workflow guidance ready"
|
|
235
284
|
const existingOutput = output.output?.trimEnd() || ""
|
|
236
|
-
output.output = existingOutput
|
|
285
|
+
output.output = [existingOutput, transition || "", guidance].filter(Boolean).join("\n\n")
|
|
237
286
|
output.metadata = {
|
|
238
287
|
...output.metadata,
|
|
239
288
|
codegraph_git_workflow_state: status.workflow_state || null,
|
package/src/util.ts
CHANGED
|
@@ -33,6 +33,12 @@ export type DogfoodStatusSnapshot = {
|
|
|
33
33
|
unstaged_changes?: boolean
|
|
34
34
|
untracked_files?: boolean
|
|
35
35
|
}
|
|
36
|
+
db_maintenance?: {
|
|
37
|
+
due?: boolean
|
|
38
|
+
reason?: string
|
|
39
|
+
db_size_mb?: number
|
|
40
|
+
recommended_action?: string
|
|
41
|
+
}
|
|
36
42
|
review_trace?: ReviewTraceSnapshot | null
|
|
37
43
|
workflow_state?: string
|
|
38
44
|
recommended_next_action?: string
|
|
@@ -386,6 +392,7 @@ export function formatDogfoodStatusSummary(snapshot: DogfoodStatusSnapshot): str
|
|
|
386
392
|
const branch = git.branch
|
|
387
393
|
const worktreeClean = git.worktree_clean
|
|
388
394
|
const reviewTrace = snapshot.review_trace || null
|
|
395
|
+
const maintenance = snapshot.db_maintenance || null
|
|
389
396
|
const traceStatus = reviewTrace?.status || "missing"
|
|
390
397
|
const workflowState = snapshot.workflow_state
|
|
391
398
|
const nextAction = snapshot.recommended_next_action
|
|
@@ -410,6 +417,11 @@ export function formatDogfoodStatusSummary(snapshot: DogfoodStatusSnapshot): str
|
|
|
410
417
|
if (typeof worktreeClean === "boolean") {
|
|
411
418
|
lines.push(`- Worktree clean: ${worktreeClean}`)
|
|
412
419
|
}
|
|
420
|
+
if (maintenance) {
|
|
421
|
+
lines.push(
|
|
422
|
+
`- DB maintenance: ${maintenance.due ? "due" : "ok"} (${maintenance.reason || "unknown"}${typeof maintenance.db_size_mb === "number" ? `, ${maintenance.db_size_mb} MB` : ""})`,
|
|
423
|
+
)
|
|
424
|
+
}
|
|
413
425
|
lines.push(`- Review trace: ${traceStatus}`)
|
|
414
426
|
if (workflowState) {
|
|
415
427
|
lines.push(`- Workflow state: ${workflowState}`)
|
|
@@ -445,6 +457,31 @@ export function formatWorkflowGuidanceBlock(snapshot: DogfoodStatusSnapshot): st
|
|
|
445
457
|
return lines.join("\n")
|
|
446
458
|
}
|
|
447
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
|
+
|
|
448
485
|
export function formatAfterTestGuidance(
|
|
449
486
|
snapshot: DogfoodStatusSnapshot,
|
|
450
487
|
testFailed = false,
|