@brainervirus/workit-opencode 0.5.6 → 0.6.1
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 +8 -8
- package/src/plugin.ts +59 -23
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit OpenCode plugin (thin wrapper over @brainervirus/workit-core)",
|
|
6
|
-
"license": "MIT",
|
|
7
6
|
"keywords": [
|
|
8
|
-
"
|
|
9
|
-
"workflow",
|
|
7
|
+
"agent",
|
|
10
8
|
"mcp",
|
|
11
|
-
"
|
|
9
|
+
"opencode",
|
|
10
|
+
"workflow"
|
|
12
11
|
],
|
|
13
12
|
"bugs": {
|
|
14
13
|
"url": "https://github.com/BrainerVirus/workit/issues"
|
|
15
14
|
},
|
|
15
|
+
"license": "MIT",
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
18
18
|
"url": "https://github.com/BrainerVirus/workit.git"
|
|
@@ -26,12 +26,12 @@
|
|
|
26
26
|
".": "./src/plugin.ts",
|
|
27
27
|
"./src/*": "./src/*"
|
|
28
28
|
},
|
|
29
|
-
"scripts": {
|
|
30
|
-
"typecheck": "tsc --noEmit"
|
|
31
|
-
},
|
|
32
29
|
"publishConfig": {
|
|
33
30
|
"access": "public"
|
|
34
31
|
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"typecheck": "tsc --noEmit"
|
|
34
|
+
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@brainervirus/workit-core": "^0.4.0",
|
|
37
37
|
"@opencode-ai/plugin": "1.17.7"
|
package/src/plugin.ts
CHANGED
|
@@ -1,12 +1,45 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
4
|
import type { Plugin } from "@opencode-ai/plugin";
|
|
6
5
|
|
|
7
|
-
import { getWorkflowBootstrap
|
|
8
|
-
import {
|
|
9
|
-
|
|
6
|
+
import { getWorkflowBootstrap } from "./bootstrap";
|
|
7
|
+
import {
|
|
8
|
+
REMINDER_TEXT,
|
|
9
|
+
DETECTION_TEXT,
|
|
10
|
+
DOC_DELIVERY_TEXT,
|
|
11
|
+
DOC_RENDER_TEXT,
|
|
12
|
+
SDD_REMINDER_TEXT,
|
|
13
|
+
shouldInjectSddReminder,
|
|
14
|
+
CONFIG_GUARD_TEXT,
|
|
15
|
+
shouldInjectConfigGuard,
|
|
16
|
+
shouldInjectDocRender,
|
|
17
|
+
VERIFICATION_TEXT,
|
|
18
|
+
TDD_TEXT,
|
|
19
|
+
BRAINSTORM_TEXT,
|
|
20
|
+
DEBUG_TEXT,
|
|
21
|
+
REVIEW_RECEPTION_TEXT,
|
|
22
|
+
shouldInjectVerification,
|
|
23
|
+
shouldInjectTdd,
|
|
24
|
+
shouldInjectBrainstorm,
|
|
25
|
+
shouldInjectDebug,
|
|
26
|
+
shouldInjectReviewReception,
|
|
27
|
+
ISSUE_RAIL_TEXT,
|
|
28
|
+
shouldInjectIssueRail,
|
|
29
|
+
} from "@brainervirus/workit-core/src/core/reminder";
|
|
30
|
+
import {
|
|
31
|
+
detectProseChoices,
|
|
32
|
+
detectBacktickDocRefs,
|
|
33
|
+
findActiveSubagentDrivenPlans,
|
|
34
|
+
detectConfigGapError,
|
|
35
|
+
detectRawDocDelivery,
|
|
36
|
+
detectVerificationClaim,
|
|
37
|
+
detectUntestedImplementation,
|
|
38
|
+
detectImplementationWithoutDesign,
|
|
39
|
+
detectFixWithoutRootCause,
|
|
40
|
+
detectBlindReviewAcceptance,
|
|
41
|
+
detectInstructionOption,
|
|
42
|
+
} from "@brainervirus/workit-core/src/core/detector";
|
|
10
43
|
import { createTools } from "@brainervirus/workit-core/src/tools";
|
|
11
44
|
import { adaptPluginHandoffClient } from "@brainervirus/workit-core/src/tools/handoff";
|
|
12
45
|
import { WorkflowStateStore } from "@brainervirus/workit-core/src/state";
|
|
@@ -41,16 +74,14 @@ const worktreeDenials = {
|
|
|
41
74
|
"*git *worktree*": "deny",
|
|
42
75
|
} as const;
|
|
43
76
|
|
|
44
|
-
const withWorktreeDenials = (
|
|
45
|
-
configuredPermission: unknown,
|
|
46
|
-
): MutablePermission => {
|
|
77
|
+
const withWorktreeDenials = (configuredPermission: unknown): MutablePermission => {
|
|
47
78
|
const permission: MutablePermission =
|
|
48
79
|
typeof configuredPermission === "string"
|
|
49
80
|
? { "*": configuredPermission as PermissionDecision }
|
|
50
81
|
: { ...((configuredPermission ?? {}) as MutablePermission) };
|
|
51
82
|
const bash = permission.bash;
|
|
52
83
|
const bashRules: Record<string, PermissionDecision> =
|
|
53
|
-
typeof bash === "string" ? { "*": bash } : { ...
|
|
84
|
+
typeof bash === "string" ? { "*": bash } : { ...bash };
|
|
54
85
|
for (const pattern of Object.keys(worktreeDenials)) delete bashRules[pattern];
|
|
55
86
|
permission.bash = { ...bashRules, ...worktreeDenials };
|
|
56
87
|
return permission;
|
|
@@ -68,10 +99,7 @@ const plugin: Plugin = async ({ client }) => {
|
|
|
68
99
|
for (const [name, description] of Object.entries(descriptions)) {
|
|
69
100
|
config.command[name] = {
|
|
70
101
|
description,
|
|
71
|
-
template: readFileSync(
|
|
72
|
-
path.join(root, "commands", `${name}.md`),
|
|
73
|
-
"utf8",
|
|
74
|
-
).trim(),
|
|
102
|
+
template: readFileSync(path.join(root, "commands", `${name}.md`), "utf8").trim(),
|
|
75
103
|
};
|
|
76
104
|
}
|
|
77
105
|
mutable.skills ??= {};
|
|
@@ -84,14 +112,10 @@ const plugin: Plugin = async ({ client }) => {
|
|
|
84
112
|
}
|
|
85
113
|
}
|
|
86
114
|
// Current OpenCode accepts top-level shorthand before the installed SDK types do.
|
|
87
|
-
config.permission = withWorktreeDenials(
|
|
88
|
-
config.permission,
|
|
89
|
-
) as typeof config.permission;
|
|
115
|
+
config.permission = withWorktreeDenials(config.permission) as typeof config.permission;
|
|
90
116
|
for (const agent of Object.values(config.agent ?? {})) {
|
|
91
117
|
if (!agent) continue;
|
|
92
|
-
agent.permission = withWorktreeDenials(
|
|
93
|
-
agent.permission,
|
|
94
|
-
) as typeof agent.permission;
|
|
118
|
+
agent.permission = withWorktreeDenials(agent.permission) as typeof agent.permission;
|
|
95
119
|
}
|
|
96
120
|
},
|
|
97
121
|
"experimental.session.compacting": async ({ sessionID }, output) => {
|
|
@@ -110,7 +134,8 @@ const plugin: Plugin = async ({ client }) => {
|
|
|
110
134
|
|
|
111
135
|
// First turn only: full bootstrap anchored to the session's first user message
|
|
112
136
|
if (firstUser?.parts.length) {
|
|
113
|
-
const firstAnchor =
|
|
137
|
+
const firstAnchor =
|
|
138
|
+
firstUser.parts.find((part) => part.type === "text") ?? firstUser.parts[0];
|
|
114
139
|
const firstText = firstUser.parts
|
|
115
140
|
.filter((part) => part.type === "text")
|
|
116
141
|
.map((part) => (part as { text?: string }).text ?? "")
|
|
@@ -128,7 +153,8 @@ const plugin: Plugin = async ({ client }) => {
|
|
|
128
153
|
}
|
|
129
154
|
|
|
130
155
|
// Every turn: compact reminder anchored to the CURRENT user message (idempotent)
|
|
131
|
-
const anchor =
|
|
156
|
+
const anchor =
|
|
157
|
+
currentUser.parts.find((part) => part.type === "text") ?? currentUser.parts[0];
|
|
132
158
|
const makePart = (text: string, tag = "r") => ({
|
|
133
159
|
id: `${anchor.id}-${tag}${Date.now()}`,
|
|
134
160
|
sessionID: anchor.sessionID,
|
|
@@ -157,7 +183,11 @@ const plugin: Plugin = async ({ client }) => {
|
|
|
157
183
|
const usedQuestionTool = lastAssistant.parts.some(
|
|
158
184
|
(p) => (p as { tool?: string }).tool === "question",
|
|
159
185
|
);
|
|
160
|
-
if (
|
|
186
|
+
if (
|
|
187
|
+
detectProseChoices(assistantText) &&
|
|
188
|
+
!usedQuestionTool &&
|
|
189
|
+
!currentText.includes("workflow-detection")
|
|
190
|
+
) {
|
|
161
191
|
currentUser.parts.unshift(makePart(DETECTION_TEXT, "d"));
|
|
162
192
|
}
|
|
163
193
|
const docRefs = detectBacktickDocRefs(assistantText);
|
|
@@ -190,7 +220,10 @@ const plugin: Plugin = async ({ client }) => {
|
|
|
190
220
|
currentUser.parts.unshift(makePart(TDD_TEXT, "tdd"));
|
|
191
221
|
}
|
|
192
222
|
// Every turn: brainstorm rail — implementation without a presented design (idempotent)
|
|
193
|
-
if (
|
|
223
|
+
if (
|
|
224
|
+
detectImplementationWithoutDesign(assistantText) &&
|
|
225
|
+
shouldInjectBrainstorm(currentText)
|
|
226
|
+
) {
|
|
194
227
|
currentUser.parts.unshift(makePart(BRAINSTORM_TEXT, "br"));
|
|
195
228
|
}
|
|
196
229
|
// Every turn: debugging rail — fixes without root-cause evidence (idempotent)
|
|
@@ -198,7 +231,10 @@ const plugin: Plugin = async ({ client }) => {
|
|
|
198
231
|
currentUser.parts.unshift(makePart(DEBUG_TEXT, "db"));
|
|
199
232
|
}
|
|
200
233
|
// Every turn: review-reception rail — feedback accepted without verification (idempotent)
|
|
201
|
-
if (
|
|
234
|
+
if (
|
|
235
|
+
detectBlindReviewAcceptance(assistantText) &&
|
|
236
|
+
shouldInjectReviewReception(currentText)
|
|
237
|
+
) {
|
|
202
238
|
currentUser.parts.unshift(makePart(REVIEW_RECEPTION_TEXT, "rc"));
|
|
203
239
|
}
|
|
204
240
|
|