opencode-codegraph 0.1.5 → 0.1.7

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,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.7 - 2026-03-20
4
+
5
+ - add command-oriented next-step guidance (`/status`, `/update`, `/review`) to review-trace and dogfooding status flows
6
+ - unify `/review`, `/audit`, and `/update` around the shared `dogfood status --json` backend
7
+ - keep session-start and post-commit guidance aligned on the same recommended command semantics
8
+
9
+ ## 0.1.6 - 2026-03-20
10
+
11
+ - add explicit pending post-commit summary when durable review trace is not yet available
12
+ - guide developers to `/status` instead of leaving post-commit review state ambiguous
13
+ - document pending-trace behavior in plugin and OpenCode workflow docs
14
+
3
15
  ## 0.1.5 - 2026-03-20
4
16
 
5
17
  - add unified next-action summary to review-trace status output
package/README.md CHANGED
@@ -39,13 +39,30 @@ Plugin injects:
39
39
 
40
40
  If the message also suggests an edit intent (`refactor`, `fix`, `modify`, `update`, etc.), the plugin appends a pre-edit warning block with complexity, fan-out, dead-code, and security hints for the referenced file.
41
41
 
42
- ### System Prompt
43
-
44
- Every conversation includes a project summary with file count, top complexity hotspots, and open security findings.
42
+ ### System Prompt
43
+
44
+ Every conversation includes:
45
+
46
+ - a project summary with file count, top complexity hotspots, and open security findings;
47
+ - a lightweight dogfooding status block when available, including freshness, current `HEAD`, review-trace state, and recommended next action.
48
+ - a recommended command (`/status`, `/update`, or `/review`) when the workflow can point to a deterministic next step.
45
49
 
46
50
  ### Post-Commit Updates
47
51
 
48
- After `git commit`, the plugin triggers incremental CPG re-parsing via GoCPG and syncs the ChromaDB vector store. If durable review-trace artifacts exist for the new `HEAD`, the plugin also appends a short review-trace summary with findings, recommendations, and a single next action to take.
52
+ After `git commit`, the plugin triggers incremental CPG re-parsing via GoCPG and syncs the ChromaDB vector store. If durable review-trace artifacts exist for the new `HEAD`, the plugin also appends a structured post-commit block with:
53
+
54
+ - what changed in the review trace
55
+ - why it matters
56
+ - top recommendations
57
+ - one clear next action
58
+ - one suggested command to run next
59
+
60
+ If the durable review trace is not available yet, the plugin still appends a pending summary telling the developer to check `/status` instead of leaving the post-commit state ambiguous.
61
+
62
+ This means the post-commit UX now has two explicit states:
63
+
64
+ - trace ready -> structured summary with findings, recommendations, and next action
65
+ - trace pending/missing -> structured pending summary with deterministic follow-up
49
66
 
50
67
  ### Custom Tools
51
68
 
@@ -89,7 +106,7 @@ Place in `.opencode/commands/`:
89
106
  | `experimental.chat.system.transform` | Inject project summary into system prompt |
90
107
  | `chat.message` | Add CPG context for mentioned files |
91
108
  | `chat.message` (edit intent) | Add pre-edit warnings for files likely to be modified |
92
- | `tool.execute.after` | Trigger CPG update after git commit and append review-trace summary with next action |
109
+ | `tool.execute.after` | Trigger CPG update after git commit and append structured post-commit review summary |
93
110
  | `permission.ask` | Auto-allow `codegraph_*` tools |
94
111
 
95
112
  ## License
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-codegraph",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
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
@@ -17,12 +17,17 @@ import { CodeGraphAPI } from "./api"
17
17
  import {
18
18
  extractFileRefs,
19
19
  fileNeedsRegistrationCheck,
20
+ formatDogfoodStatusSummary,
21
+ formatPendingReviewTraceSummary,
20
22
  formatReviewTraceSummary,
21
23
  getHeadCommit,
22
24
  isGitCommit,
23
25
  messageSuggestsEditing,
26
+ recommendedCommandFromReviewTrace,
24
27
  recommendedNextActionFromReviewTrace,
25
28
  readReviewTraceSnapshot,
29
+ whatChangedFromReviewTrace,
30
+ whyItMattersFromReviewTrace,
26
31
  } from "./util"
27
32
 
28
33
  const codegraphPlugin: Plugin = async (input) => {
@@ -39,16 +44,26 @@ const codegraphPlugin: Plugin = async (input) => {
39
44
  // -----------------------------------------------------------------
40
45
  // 1. System prompt: inject CPG project summary
41
46
  // -----------------------------------------------------------------
42
- "experimental.chat.system.transform": async (_inp, output) => {
43
- try {
44
- const summary = await api.getProjectSummary(projectId)
45
- if (summary) {
46
- output.system.push(summary)
47
- }
48
- } catch {
49
- // CodeGraph API not available — skip silently
50
- }
51
- },
47
+ "experimental.chat.system.transform": async (_inp, output) => {
48
+ try {
49
+ const rawStatus = await $`python -m src.cli.import_commands dogfood status --json`.quiet().text()
50
+ const statusSummary = formatDogfoodStatusSummary(JSON.parse(rawStatus))
51
+ if (statusSummary) {
52
+ output.system.push(statusSummary)
53
+ }
54
+ } catch {
55
+ // Dogfooding status unavailable — keep session startup lightweight
56
+ }
57
+
58
+ try {
59
+ const summary = await api.getProjectSummary(projectId)
60
+ if (summary) {
61
+ output.system.push(summary)
62
+ }
63
+ } catch {
64
+ // CodeGraph API not available — skip silently
65
+ }
66
+ },
52
67
 
53
68
  // -----------------------------------------------------------------
54
69
  // 2. Chat message: auto-enrich with CPG context for mentioned files
@@ -114,13 +129,35 @@ const codegraphPlugin: Plugin = async (input) => {
114
129
  codegraph_review_trace_recommendations:
115
130
  traceSnapshot.review_recommendations?.slice(0, 3) || [],
116
131
  codegraph_review_trace_next_action: recommendedNextActionFromReviewTrace(traceSnapshot),
132
+ codegraph_review_trace_recommended_command:
133
+ traceSnapshot.recommended_command || recommendedCommandFromReviewTrace(traceSnapshot),
134
+ codegraph_review_trace_what_changed: whatChangedFromReviewTrace(traceSnapshot),
135
+ codegraph_review_trace_why_it_matters: whyItMattersFromReviewTrace(traceSnapshot),
136
+ }
137
+ } else {
138
+ traceSummary = formatPendingReviewTraceSummary(commit)
139
+ output.metadata = {
140
+ ...output.metadata,
141
+ codegraph_review_trace_status: "pending_or_missing",
142
+ codegraph_review_trace_phase: "awaiting_trace",
143
+ codegraph_review_trace_findings: null,
144
+ codegraph_review_trace_recommendations: [],
145
+ codegraph_review_trace_next_action:
146
+ "Wait briefly for review trace completion, then run /status to check the latest result.",
147
+ codegraph_review_trace_recommended_command: "/status",
148
+ codegraph_review_trace_what_changed: [
149
+ `Incremental CPG update was triggered for ${commit.slice(0, 12)}.`,
150
+ "Durable review trace is not available yet or has not been written.",
151
+ ],
152
+ codegraph_review_trace_why_it_matters:
153
+ "Post-commit findings and recommendations may still be pending, so the commit is not fully evaluated yet.",
117
154
  }
118
155
  }
119
156
  }
120
157
 
121
158
  // Notify user via output metadata (visible in OpenCode UI)
122
159
  output.title = traceSummary
123
- ? "CodeGraph: CPG update triggered + review trace found"
160
+ ? "CodeGraph: post-commit review summary ready"
124
161
  : "CodeGraph: CPG update triggered"
125
162
  output.metadata = {
126
163
  ...output.metadata,
package/src/util.ts CHANGED
@@ -14,6 +14,19 @@ export type ReviewTraceSnapshot = {
14
14
  review_severity_counts?: Record<string, number>
15
15
  review_recommendations?: string[]
16
16
  error?: string | null
17
+ recommended_command?: string
18
+ }
19
+
20
+ export type DogfoodStatusSnapshot = {
21
+ cpg_status?: {
22
+ is_fresh?: boolean
23
+ freshness_reason?: string
24
+ commits_behind?: number
25
+ db_exists?: boolean
26
+ }
27
+ head_commit?: string
28
+ review_trace?: ReviewTraceSnapshot | null
29
+ recommended_next_action?: string
17
30
  }
18
31
 
19
32
  /**
@@ -129,18 +142,68 @@ export async function readReviewTraceSnapshot(
129
142
  return null
130
143
  }
131
144
 
145
+ export function formatPendingReviewTraceSummary(commit: string | null): string {
146
+ const commitLabel = commit ? ` for \`${commit.slice(0, 12)}\`` : ""
147
+ return [
148
+ "## CodeGraph Post-Commit Summary",
149
+ "",
150
+ "### What changed",
151
+ "",
152
+ `- Incremental CPG update was triggered${commitLabel}.`,
153
+ "- Durable review trace is not available yet or has not been written.",
154
+ "",
155
+ "### Why it matters",
156
+ "",
157
+ "- Post-commit findings and recommendations may still be pending, so the commit is not fully evaluated yet.",
158
+ "",
159
+ "### What to do now",
160
+ "",
161
+ "- Wait briefly for review trace completion, then run `/status` to check the latest result.",
162
+ "- Suggested command: `/status`",
163
+ ].join("\n")
164
+ }
165
+
132
166
  export function formatReviewTraceSummary(snapshot: ReviewTraceSnapshot): string | null {
133
- const status = snapshot.status || "unknown"
134
- const phase = snapshot.phase || "unknown"
135
- const findingsCount = snapshot.review_findings_count
136
167
  const recommendations = Array.isArray(snapshot.review_recommendations)
137
168
  ? snapshot.review_recommendations.filter(Boolean)
138
169
  : []
170
+ const whatChanged = whatChangedFromReviewTrace(snapshot)
171
+ const whyItMatters = whyItMattersFromReviewTrace(snapshot)
172
+ const nextAction = recommendedNextActionFromReviewTrace(snapshot)
173
+ const nextCommand = recommendedCommandFromReviewTrace(snapshot)
139
174
 
140
- const lines = ["## CodeGraph Review Trace", "", `- Status: ${status}`, `- Phase: ${phase}`]
175
+ const lines = ["## CodeGraph Post-Commit Summary", ""]
141
176
 
142
- if (typeof findingsCount === "number") {
143
- lines.push(`- Findings: ${findingsCount}`)
177
+ if (whatChanged) {
178
+ lines.push("### What changed", "", ...whatChanged.map((item) => `- ${item}`), "")
179
+ }
180
+
181
+ if (whyItMatters) {
182
+ lines.push("### Why it matters", "", `- ${whyItMatters}`, "")
183
+ }
184
+
185
+ if (recommendations.length) {
186
+ lines.push("### Top recommendations", "")
187
+ for (const recommendation of recommendations.slice(0, 3)) {
188
+ lines.push(`- ${recommendation}`)
189
+ }
190
+ lines.push("")
191
+ }
192
+
193
+ lines.push("### What to do now", "", `- ${nextAction}`)
194
+ lines.push(`- Suggested command: \`${nextCommand}\``)
195
+
196
+ return lines.join("\n")
197
+ }
198
+
199
+ export function whatChangedFromReviewTrace(snapshot: ReviewTraceSnapshot): string[] {
200
+ const items: string[] = []
201
+ const status = snapshot.status || "unknown"
202
+ const phase = snapshot.phase || "unknown"
203
+ items.push(`Review trace status is ${status} (phase: ${phase}).`)
204
+
205
+ if (typeof snapshot.review_findings_count === "number") {
206
+ items.push(`CodeGraph reported ${snapshot.review_findings_count} review finding(s).`)
144
207
  }
145
208
 
146
209
  const severityCounts = snapshot.review_severity_counts || {}
@@ -149,26 +212,37 @@ export function formatReviewTraceSummary(snapshot: ReviewTraceSnapshot): string
149
212
  .map(([severity, count]) => `${severity}:${count}`)
150
213
  .join(", ")
151
214
  if (severitySummary) {
152
- lines.push(`- Severity: ${severitySummary}`)
215
+ items.push(`Severity mix: ${severitySummary}.`)
153
216
  }
154
217
 
155
218
  if (snapshot.error) {
156
- lines.push(`- Error: ${snapshot.error}`)
219
+ items.push(`Review trace recorded an error: ${snapshot.error}`)
157
220
  }
158
221
 
159
- if (recommendations.length) {
160
- lines.push("", "### Recommendations")
161
- for (const recommendation of recommendations.slice(0, 3)) {
162
- lines.push(`- ${recommendation}`)
163
- }
164
- }
222
+ return items
223
+ }
165
224
 
166
- const nextAction = recommendedNextActionFromReviewTrace(snapshot)
167
- if (nextAction) {
168
- lines.push("", `**Next action:** ${nextAction}`)
169
- }
225
+ export function whyItMattersFromReviewTrace(snapshot: ReviewTraceSnapshot): string {
226
+ const severityCounts = snapshot.review_severity_counts || {}
227
+ const high = severityCounts.high || 0
228
+ const medium = severityCounts.medium || 0
170
229
 
171
- return lines.length > 2 ? lines.join("\n") : null
230
+ if (snapshot.error) {
231
+ return "The post-commit feedback loop is incomplete, so current review guidance may be missing or stale."
232
+ }
233
+ if ((snapshot.status || "").toLowerCase() === "running") {
234
+ return "The review trace is still running, so findings and recommendations can still change."
235
+ }
236
+ if (high > 0) {
237
+ return "High-severity findings were recorded, so the commit may need immediate follow-up before merge or push."
238
+ }
239
+ if (medium > 0) {
240
+ return "Medium-severity findings were recorded, so follow-up review is recommended before treating the commit as clean."
241
+ }
242
+ if (typeof snapshot.review_findings_count === "number" && snapshot.review_findings_count > 0) {
243
+ return "The review trace found issues worth checking, even if none were marked high severity."
244
+ }
245
+ return "No review findings were recorded, so the commit looks clean from the current trace perspective."
172
246
  }
173
247
 
174
248
  export function recommendedNextActionFromReviewTrace(snapshot: ReviewTraceSnapshot): string {
@@ -196,6 +270,59 @@ export function recommendedNextActionFromReviewTrace(snapshot: ReviewTraceSnapsh
196
270
  return "No review findings recorded; continue with /review or push once the rest of your checks are green."
197
271
  }
198
272
 
273
+ export function recommendedCommandFromReviewTrace(snapshot: ReviewTraceSnapshot): string {
274
+ const status = (snapshot.status || "").toLowerCase()
275
+ const findingsCount = snapshot.review_findings_count
276
+
277
+ if (snapshot.error) {
278
+ return "/review"
279
+ }
280
+ if (status === "running") {
281
+ return "/status"
282
+ }
283
+ if (status && status !== "completed") {
284
+ return "/review"
285
+ }
286
+ if (typeof findingsCount === "number" && findingsCount > 0) {
287
+ return "/review"
288
+ }
289
+ return "/review"
290
+ }
291
+
292
+ export function formatDogfoodStatusSummary(snapshot: DogfoodStatusSnapshot): string | null {
293
+ const cpg = snapshot.cpg_status || {}
294
+ const isFresh = cpg.is_fresh
295
+ const freshnessReason = cpg.freshness_reason || "unknown"
296
+ const commitsBehind = cpg.commits_behind
297
+ const headCommit = snapshot.head_commit ? snapshot.head_commit.slice(0, 12) : null
298
+ const reviewTrace = snapshot.review_trace || null
299
+ const traceStatus = reviewTrace?.status || "missing"
300
+ const nextAction = snapshot.recommended_next_action
301
+ const nextCommand = (snapshot as { recommended_command?: string }).recommended_command
302
+
303
+ if (typeof isFresh === "undefined" && !headCommit && !nextAction) {
304
+ return null
305
+ }
306
+
307
+ const lines = ["## CodeGraph Dogfooding Status", ""]
308
+ if (typeof isFresh !== "undefined") {
309
+ const freshness = isFresh ? "fresh" : "stale"
310
+ const behind = typeof commitsBehind === "number" ? `, commits behind: ${commitsBehind}` : ""
311
+ lines.push(`- CPG freshness: ${freshness} (reason: ${freshnessReason}${behind})`)
312
+ }
313
+ if (headCommit) {
314
+ lines.push(`- HEAD: ${headCommit}`)
315
+ }
316
+ lines.push(`- Review trace: ${traceStatus}`)
317
+ if (nextAction) {
318
+ lines.push(`- Next action: ${nextAction}`)
319
+ }
320
+ if (nextCommand) {
321
+ lines.push(`- Suggested command: ${nextCommand}`)
322
+ }
323
+ return lines.join("\n")
324
+ }
325
+
199
326
  // File extensions recognized as source code
200
327
  const SOURCE_EXTENSIONS = new Set([
201
328
  "py",