@brainervirus/workit-opencode 0.6.1 → 0.8.0

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.
Files changed (84) hide show
  1. package/README.md +36 -5
  2. package/assets/commands/wk-changelog.md +2 -0
  3. package/assets/commands/wk-commit.md +2 -0
  4. package/assets/commands/wk-docs-refresh.md +2 -0
  5. package/assets/commands/wk-handoff.md +2 -0
  6. package/assets/commands/wk-implement.md +2 -0
  7. package/assets/commands/wk-init.md +2 -0
  8. package/assets/commands/wk-issue-update.md +2 -0
  9. package/assets/commands/wk-meetings.md +2 -0
  10. package/assets/commands/wk-pr.md +2 -0
  11. package/assets/commands/wk-release-notes.md +2 -0
  12. package/assets/commands/wk-status.md +2 -0
  13. package/assets/commands/wk-verify.md +2 -0
  14. package/assets/skills/wk-changelog/SKILL.md +15 -0
  15. package/assets/skills/wk-commit/SKILL.md +16 -0
  16. package/assets/skills/wk-docs-refresh/SKILL.md +15 -0
  17. package/assets/skills/wk-handoff/SKILL.md +17 -0
  18. package/assets/skills/wk-implement/SKILL.md +41 -0
  19. package/assets/skills/wk-init/SKILL.md +31 -0
  20. package/assets/skills/wk-issue-update/SKILL.md +27 -0
  21. package/assets/skills/wk-issue-update/references/youtrack-update-style.md +81 -0
  22. package/assets/skills/wk-meetings/SKILL.md +17 -0
  23. package/assets/skills/wk-pr/SKILL.md +27 -0
  24. package/assets/skills/wk-release-notes/SKILL.md +15 -0
  25. package/assets/skills/wk-status/SKILL.md +16 -0
  26. package/assets/skills/wk-verify/SKILL.md +16 -0
  27. package/assets/templates/execution-contract.md +58 -0
  28. package/assets/templates/greeting.md +1 -0
  29. package/assets/templates/headers.md +3 -0
  30. package/assets/templates/hygiene/.editorconfig +8 -0
  31. package/assets/templates/hygiene/.gitattributes +3 -0
  32. package/assets/templates/hygiene/CHANGELOG.md +14 -0
  33. package/assets/templates/hygiene/CONTRIBUTING.md +3 -0
  34. package/assets/templates/hygiene/LICENSE +21 -0
  35. package/assets/templates/hygiene/README.md +3 -0
  36. package/assets/templates/issue-update.md +6 -0
  37. package/assets/templates/plan-template.md +25 -0
  38. package/assets/templates/spec-template.md +51 -0
  39. package/assets/templates/superpowers-doc-contract.md +70 -0
  40. package/assets/vendor/superpowers/skills/brainstorming/SKILL.md +159 -0
  41. package/assets/vendor/superpowers/skills/brainstorming/scripts/frame-template.html +213 -0
  42. package/assets/vendor/superpowers/skills/brainstorming/scripts/helper.js +167 -0
  43. package/assets/vendor/superpowers/skills/brainstorming/scripts/server.cjs +723 -0
  44. package/assets/vendor/superpowers/skills/brainstorming/spec-document-reviewer-prompt.md +49 -0
  45. package/assets/vendor/superpowers/skills/brainstorming/visual-companion.md +222 -0
  46. package/assets/vendor/superpowers/skills/dispatching-parallel-agents/SKILL.md +185 -0
  47. package/assets/vendor/superpowers/skills/executing-plans/SKILL.md +70 -0
  48. package/assets/vendor/superpowers/skills/finishing-a-development-branch/SKILL.md +241 -0
  49. package/assets/vendor/superpowers/skills/receiving-code-review/SKILL.md +213 -0
  50. package/assets/vendor/superpowers/skills/requesting-code-review/SKILL.md +103 -0
  51. package/assets/vendor/superpowers/skills/requesting-code-review/code-reviewer.md +172 -0
  52. package/assets/vendor/superpowers/skills/subagent-driven-development/SKILL.md +418 -0
  53. package/assets/vendor/superpowers/skills/subagent-driven-development/implementer-prompt.md +139 -0
  54. package/assets/vendor/superpowers/skills/subagent-driven-development/task-reviewer-prompt.md +188 -0
  55. package/assets/vendor/superpowers/skills/systematic-debugging/CREATION-LOG.md +119 -0
  56. package/assets/vendor/superpowers/skills/systematic-debugging/SKILL.md +296 -0
  57. package/assets/vendor/superpowers/skills/systematic-debugging/condition-based-waiting-example.ts +158 -0
  58. package/assets/vendor/superpowers/skills/systematic-debugging/condition-based-waiting.md +115 -0
  59. package/assets/vendor/superpowers/skills/systematic-debugging/defense-in-depth.md +122 -0
  60. package/assets/vendor/superpowers/skills/systematic-debugging/root-cause-tracing.md +169 -0
  61. package/assets/vendor/superpowers/skills/systematic-debugging/test-academic.md +14 -0
  62. package/assets/vendor/superpowers/skills/systematic-debugging/test-pressure-1.md +58 -0
  63. package/assets/vendor/superpowers/skills/systematic-debugging/test-pressure-2.md +68 -0
  64. package/assets/vendor/superpowers/skills/systematic-debugging/test-pressure-3.md +69 -0
  65. package/assets/vendor/superpowers/skills/test-driven-development/SKILL.md +371 -0
  66. package/assets/vendor/superpowers/skills/test-driven-development/testing-anti-patterns.md +299 -0
  67. package/assets/vendor/superpowers/skills/using-git-worktrees/SKILL.md +202 -0
  68. package/assets/vendor/superpowers/skills/using-superpowers/SKILL.md +62 -0
  69. package/assets/vendor/superpowers/skills/using-superpowers/references/antigravity-tools.md +23 -0
  70. package/assets/vendor/superpowers/skills/using-superpowers/references/codex-tools.md +39 -0
  71. package/assets/vendor/superpowers/skills/using-superpowers/references/pi-tools.md +16 -0
  72. package/assets/vendor/superpowers/skills/verification-before-completion/SKILL.md +139 -0
  73. package/assets/vendor/superpowers/skills/writing-plans/SKILL.md +174 -0
  74. package/assets/vendor/superpowers/skills/writing-plans/plan-document-reviewer-prompt.md +49 -0
  75. package/assets/vendor/superpowers/skills/writing-skills/SKILL.md +689 -0
  76. package/assets/vendor/superpowers/skills/writing-skills/anthropic-best-practices.md +1150 -0
  77. package/assets/vendor/superpowers/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
  78. package/assets/vendor/superpowers/skills/writing-skills/graphviz-conventions.dot +172 -0
  79. package/assets/vendor/superpowers/skills/writing-skills/persuasion-principles.md +187 -0
  80. package/assets/vendor/superpowers/skills/writing-skills/testing-skills-with-subagents.md +384 -0
  81. package/dist/plugin.js +21374 -0
  82. package/package.json +12 -6
  83. package/src/bootstrap.ts +0 -65
  84. package/src/plugin.ts +0 -260
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-opencode",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "private": false,
5
5
  "description": "Workit OpenCode plugin (thin wrapper over @brainervirus/workit-core)",
6
6
  "keywords": [
@@ -18,22 +18,28 @@
18
18
  "url": "https://github.com/BrainerVirus/workit.git"
19
19
  },
20
20
  "files": [
21
- "src/"
21
+ "dist/",
22
+ "assets/"
22
23
  ],
23
24
  "type": "module",
24
- "main": "./src/plugin.ts",
25
+ "main": "./dist/plugin.js",
25
26
  "exports": {
26
- ".": "./src/plugin.ts",
27
- "./src/*": "./src/*"
27
+ ".": "./dist/plugin.js"
28
28
  },
29
29
  "publishConfig": {
30
30
  "access": "public"
31
31
  },
32
32
  "scripts": {
33
+ "build": "bun scripts/build.ts",
33
34
  "typecheck": "tsc --noEmit"
34
35
  },
35
36
  "dependencies": {
36
- "@brainervirus/workit-core": "^0.4.0",
37
+ "@brainervirus/workit-core": "^0.8.0"
38
+ },
39
+ "devDependencies": {
37
40
  "@opencode-ai/plugin": "1.17.7"
41
+ },
42
+ "engines": {
43
+ "node": ">=22"
38
44
  }
39
45
  }
package/src/bootstrap.ts DELETED
@@ -1,65 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { createRequire } from "node:module";
3
- import path from "node:path";
4
- import { readConfig } from "@brainervirus/workit-core/src/core/config";
5
- import { compiledOpenCodeSections } from "@brainervirus/workit-core/src/core/rules";
6
-
7
- // templates/ lives in the core package (same resolution as plugin.ts).
8
- const require = createRequire(import.meta.url);
9
- const root = path.dirname(require.resolve("@brainervirus/workit-core/package.json"));
10
- const marker = "<workflow-toolkit-contract>";
11
-
12
- let cached: string | null | undefined;
13
-
14
- export const loadWorkflowBootstrap = (rootDir: string): string | null => {
15
- const contractPath = path.join(rootDir, "templates", "superpowers-doc-contract.md");
16
- try {
17
- return readFileSync(contractPath, "utf8");
18
- } catch {
19
- return null;
20
- }
21
- };
22
-
23
- export const getWorkflowBootstrap = (): string | null => {
24
- if (cached !== undefined) return cached;
25
-
26
- const contract = loadWorkflowBootstrap(root);
27
- if (contract === null) {
28
- cached = null;
29
- return null;
30
- }
31
-
32
- const config = readConfig();
33
- const userSections = compiledOpenCodeSections();
34
- cached = `${marker}
35
- HARD-GATE: Workflow-toolkit contract is already loaded below. Follow it on every turn; it overrides conflicting Superpowers defaults (brainstorming chat options, visual companion, worktrees, SDD paths).
36
-
37
- ## Locale
38
-
39
- Answer the user in \`${config.locale}\` unless a specific template declares otherwise.
40
-
41
- ${contract}
42
-
43
- ## Presentation and visual companion overrides
44
-
45
- - NEVER offer Superpowers visual companion or open a browser tab for layout comparisons.
46
- - For UI wireframes and layout options use \`workflow_present_ascii\` with a JSON spec; show the tool output in a fenced \`text\` block.
47
- - For process or architecture flows use \`workflow_present_flow\`; show the tool output in a fenced \`mermaid\` block.
48
- - NEVER hand-draw ASCII wireframes or mermaid in chat without calling the tool first.
49
- - For bounded user choices use OpenCode native \`question\`; never A/B/C option lists in chat prose.
50
-
51
- ## Post-plan execution choice
52
-
53
- After saving a plan, call \`workflow_docs_validate\` on the spec/plan pair. On failure, stop — do not offer execution.
54
-
55
- On success, use native \`question\` with exactly: Subagent-driven, Inline, Handoff (new session only), Review spec first, Review plan first. Never emit Superpowers “Two execution options” prose. No \`--stay\` in this menu.
56
-
57
- ## Library documentation
58
-
59
- When the user asks about a library, framework, or API reference, prefer live docs (e.g. Context7 MCP \`resolve-library-id\` + \`query-docs\`) over training-data guesses.
60
- ${userSections ? `\n${userSections}\n` : ""}</workflow-toolkit-contract>`;
61
-
62
- return cached;
63
- };
64
-
65
- export const isWorkflowBootstrap = (text: string) => text.includes(marker);
package/src/plugin.ts DELETED
@@ -1,260 +0,0 @@
1
- import { existsSync, readFileSync } from "node:fs";
2
- import { createRequire } from "node:module";
3
- import path from "node:path";
4
- import type { Plugin } from "@opencode-ai/plugin";
5
-
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";
43
- import { createTools } from "@brainervirus/workit-core/src/tools";
44
- import { adaptPluginHandoffClient } from "@brainervirus/workit-core/src/tools/handoff";
45
- import { WorkflowStateStore } from "@brainervirus/workit-core/src/state";
46
-
47
- // Skills/commands/vendor/templates live in the core package. Resolve its root
48
- // through node_modules (workspace symlink in the monorepo, sibling in a
49
- // published install) instead of a relative path off this file.
50
- const require = createRequire(import.meta.url);
51
- const root = path.dirname(require.resolve("@brainervirus/workit-core/package.json"));
52
- const descriptions: Record<string, string> = {
53
- "wk-init": "Initialize workit configuration",
54
- "wk-status": "Show workflow and repository status",
55
- "wk-verify": "Discover and run repository verification",
56
- "wk-commit": "Review and create a guarded commit",
57
- "wk-pr": "Prepare or create a pull/merge request",
58
- "wk-changelog": "Preview and update Keep a Changelog",
59
- "wk-release-notes": "Draft notes for an explicit release range",
60
- "wk-docs-refresh": "Refresh documentation affected by changes",
61
- "wk-handoff": "Continue work in a new seeded OpenCode session",
62
- "wk-implement": "Execute an approved Superpowers plan",
63
- "wk-meetings": "Log confirmed meeting time in YouTrack",
64
- "wk-issue-update": "Post a confirmed YouTrack work update",
65
- };
66
-
67
- type PermissionDecision = "allow" | "ask" | "deny";
68
- type MutablePermission = Record<string, unknown> & {
69
- "*"?: PermissionDecision;
70
- bash?: PermissionDecision | Record<string, PermissionDecision>;
71
- };
72
-
73
- const worktreeDenials = {
74
- "*git *worktree*": "deny",
75
- } as const;
76
-
77
- const withWorktreeDenials = (configuredPermission: unknown): MutablePermission => {
78
- const permission: MutablePermission =
79
- typeof configuredPermission === "string"
80
- ? { "*": configuredPermission as PermissionDecision }
81
- : { ...((configuredPermission ?? {}) as MutablePermission) };
82
- const bash = permission.bash;
83
- const bashRules: Record<string, PermissionDecision> =
84
- typeof bash === "string" ? { "*": bash } : { ...bash };
85
- for (const pattern of Object.keys(worktreeDenials)) delete bashRules[pattern];
86
- permission.bash = { ...bashRules, ...worktreeDenials };
87
- return permission;
88
- };
89
-
90
- const plugin: Plugin = async ({ client }) => {
91
- const state = new WorkflowStateStore();
92
- return {
93
- tool: createTools(adaptPluginHandoffClient(client), state),
94
- config: async (config) => {
95
- const mutable = config as typeof config & {
96
- skills?: { paths?: string[] };
97
- };
98
- config.command ??= {};
99
- for (const [name, description] of Object.entries(descriptions)) {
100
- config.command[name] = {
101
- description,
102
- template: readFileSync(path.join(root, "commands", `${name}.md`), "utf8").trim(),
103
- };
104
- }
105
- mutable.skills ??= {};
106
- mutable.skills.paths ??= [];
107
- const skillPath = path.join(root, "skills");
108
- const vendoredSkillsPath = path.join(root, "vendor", "superpowers", "skills");
109
- for (const p of [skillPath, vendoredSkillsPath]) {
110
- if (existsSync(p) && !mutable.skills.paths.includes(p)) {
111
- mutable.skills.paths.push(p);
112
- }
113
- }
114
- // Current OpenCode accepts top-level shorthand before the installed SDK types do.
115
- config.permission = withWorktreeDenials(config.permission) as typeof config.permission;
116
- for (const agent of Object.values(config.agent ?? {})) {
117
- if (!agent) continue;
118
- agent.permission = withWorktreeDenials(agent.permission) as typeof agent.permission;
119
- }
120
- },
121
- "experimental.session.compacting": async ({ sessionID }, output) => {
122
- const context = state.compactionContext(sessionID);
123
- if (context) output.context.push(context);
124
- },
125
- // ponytail: mirrors Superpowers bootstrap — inject once on the first user turn;
126
- // plus a per-turn reminder and post-hoc prose-choice detection
127
- "experimental.chat.messages.transform": async (_input, output) => {
128
- try {
129
- if (!output.messages.length) return;
130
- const users = output.messages.filter((m) => m.info.role === "user");
131
- const firstUser = users[0];
132
- const currentUser = users[users.length - 1];
133
- if (!currentUser?.parts.length) return;
134
-
135
- // First turn only: full bootstrap anchored to the session's first user message
136
- if (firstUser?.parts.length) {
137
- const firstAnchor =
138
- firstUser.parts.find((part) => part.type === "text") ?? firstUser.parts[0];
139
- const firstText = firstUser.parts
140
- .filter((part) => part.type === "text")
141
- .map((part) => (part as { text?: string }).text ?? "")
142
- .join("\n");
143
- const bootstrap = getWorkflowBootstrap();
144
- if (bootstrap && !firstText.includes("<workflow-toolkit-contract>")) {
145
- firstUser.parts.unshift({
146
- id: firstAnchor.id,
147
- sessionID: firstAnchor.sessionID,
148
- messageID: firstAnchor.messageID,
149
- type: "text" as const,
150
- text: bootstrap,
151
- });
152
- }
153
- }
154
-
155
- // Every turn: compact reminder anchored to the CURRENT user message (idempotent)
156
- const anchor =
157
- currentUser.parts.find((part) => part.type === "text") ?? currentUser.parts[0];
158
- const makePart = (text: string, tag = "r") => ({
159
- id: `${anchor.id}-${tag}${Date.now()}`,
160
- sessionID: anchor.sessionID,
161
- messageID: anchor.messageID,
162
- type: "text" as const,
163
- text,
164
- });
165
- const currentText = currentUser.parts
166
- .filter((part) => part.type === "text")
167
- .map((part) => (part as { text?: string }).text ?? "")
168
- .join("\n");
169
- if (!currentText.includes(REMINDER_TEXT)) {
170
- currentUser.parts.unshift(makePart(REMINDER_TEXT));
171
- }
172
-
173
- // Post-hoc detection: last assistant message (before current user turn) used prose choices?
174
- const beforeCurrent = output.messages.slice(0, output.messages.indexOf(currentUser));
175
- const lastAssistant = [...beforeCurrent].reverse().find((m) => m.info.role === "assistant");
176
- const assistantText = lastAssistant
177
- ? lastAssistant.parts
178
- .filter((p) => p.type === "text")
179
- .map((p) => (p as { text?: string }).text ?? "")
180
- .join("\n")
181
- : "";
182
- if (lastAssistant) {
183
- const usedQuestionTool = lastAssistant.parts.some(
184
- (p) => (p as { tool?: string }).tool === "question",
185
- );
186
- if (
187
- detectProseChoices(assistantText) &&
188
- !usedQuestionTool &&
189
- !currentText.includes("workflow-detection")
190
- ) {
191
- currentUser.parts.unshift(makePart(DETECTION_TEXT, "d"));
192
- }
193
- const docRefs = detectBacktickDocRefs(assistantText);
194
- if (docRefs && !currentText.includes("workflow-doc-delivery")) {
195
- currentUser.parts.unshift(makePart(DOC_DELIVERY_TEXT, "dd"));
196
- }
197
- if (detectRawDocDelivery(assistantText) && shouldInjectDocRender(currentText)) {
198
- currentUser.parts.unshift(makePart(DOC_RENDER_TEXT, "dr"));
199
- }
200
- }
201
-
202
- // Every turn: subagent-driven rail — active approved plans get one reminder (idempotent)
203
- // ponytail: process.cwd() ceiling — sessions launched from another directory scan the wrong docs/ tree
204
- const activePlans = findActiveSubagentDrivenPlans(process.cwd());
205
- if (activePlans.length > 0 && shouldInjectSddReminder(currentText)) {
206
- currentUser.parts.unshift(makePart(SDD_REMINDER_TEXT, "sdd"));
207
- }
208
-
209
- // Every turn: config-gap rail — structured config errors get a three-option question (idempotent)
210
- if (detectConfigGapError(assistantText) && shouldInjectConfigGuard(currentText)) {
211
- currentUser.parts.unshift(makePart(CONFIG_GUARD_TEXT, "cg"));
212
- }
213
-
214
- // Every turn: verification rail — completion claims without fresh check evidence (idempotent)
215
- if (detectVerificationClaim(assistantText) && shouldInjectVerification(currentText)) {
216
- currentUser.parts.unshift(makePart(VERIFICATION_TEXT, "vf"));
217
- }
218
- // Every turn: TDD rail — implementation without failing-test-first evidence (idempotent)
219
- if (detectUntestedImplementation(assistantText) && shouldInjectTdd(currentText)) {
220
- currentUser.parts.unshift(makePart(TDD_TEXT, "tdd"));
221
- }
222
- // Every turn: brainstorm rail — implementation without a presented design (idempotent)
223
- if (
224
- detectImplementationWithoutDesign(assistantText) &&
225
- shouldInjectBrainstorm(currentText)
226
- ) {
227
- currentUser.parts.unshift(makePart(BRAINSTORM_TEXT, "br"));
228
- }
229
- // Every turn: debugging rail — fixes without root-cause evidence (idempotent)
230
- if (detectFixWithoutRootCause(assistantText) && shouldInjectDebug(currentText)) {
231
- currentUser.parts.unshift(makePart(DEBUG_TEXT, "db"));
232
- }
233
- // Every turn: review-reception rail — feedback accepted without verification (idempotent)
234
- if (
235
- detectBlindReviewAcceptance(assistantText) &&
236
- shouldInjectReviewReception(currentText)
237
- ) {
238
- currentUser.parts.unshift(makePart(REVIEW_RECEPTION_TEXT, "rc"));
239
- }
240
-
241
- // Every turn: issue rail — previous assistant asked for free text via a clickable question option (idempotent)
242
- if (lastAssistant) {
243
- const questionParts = lastAssistant.parts.filter(
244
- (p) => (p as { tool?: string }).tool === "question",
245
- );
246
- const hasInstructionOption = questionParts.some((p) =>
247
- detectInstructionOption((p as { state?: { input?: unknown } }).state?.input),
248
- );
249
- if (hasInstructionOption && shouldInjectIssueRail(currentText)) {
250
- currentUser.parts.unshift(makePart(ISSUE_RAIL_TEXT, "ir"));
251
- }
252
- }
253
- } catch {
254
- // never break the session from a hook
255
- }
256
- },
257
- };
258
- };
259
-
260
- export default plugin;