@codemcp/workflows-opencode 6.11.1 → 6.13.4
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/README.md +12 -0
- package/dist/index.d.ts +19 -2
- package/dist/index.js +143 -26
- package/package.json +4 -4
- package/resources/agents/architect.yaml +2 -2
- package/resources/agents/business-analyst.yaml +1 -1
- package/resources/agents/developer.yaml +1 -1
- package/resources/templates/skills/POWER.md +1 -1
- package/resources/templates/skills/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -90,6 +90,18 @@ When the env var is set, workflow hooks are skipped and tools throw a clear erro
|
|
|
90
90
|
|
|
91
91
|
**When unset**, workflows are active for all agents (default behavior).
|
|
92
92
|
|
|
93
|
+
### Auto-Compaction
|
|
94
|
+
|
|
95
|
+
When transitioning to a new phase via `proceed_to_phase`, the plugin automatically triggers a session compaction (summarize) to clear prior-phase context from the LLM window. This is enabled by default.
|
|
96
|
+
|
|
97
|
+
Set `WORKFLOW_AUTO_COMPACT=false` to disable this behavior:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
WORKFLOW_AUTO_COMPACT=false npx opencode
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**When unset or any value other than `false`**, compaction runs on every successful phase transition (default behavior).
|
|
104
|
+
|
|
93
105
|
### Per-Agent Behavior
|
|
94
106
|
|
|
95
107
|
- **Agent in filter**: Workflow instructions are injected on every message, tools work normally
|
package/dist/index.d.ts
CHANGED
|
@@ -65,9 +65,26 @@ type ToolDefinition = {
|
|
|
65
65
|
args: z.ZodRawShape;
|
|
66
66
|
execute(args: unknown, context: ToolContext): Promise<string>;
|
|
67
67
|
};
|
|
68
|
+
type SessionCompactedEvent = {
|
|
69
|
+
type: 'session.compacted';
|
|
70
|
+
properties: {
|
|
71
|
+
sessionID: string;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
type SessionIdleEvent = {
|
|
75
|
+
type: 'session.idle';
|
|
76
|
+
properties: {
|
|
77
|
+
sessionID: string;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
type OtherEvent = {
|
|
81
|
+
type: string;
|
|
82
|
+
properties: Record<string, unknown>;
|
|
83
|
+
};
|
|
84
|
+
type BusEvent = SessionCompactedEvent | SessionIdleEvent | OtherEvent;
|
|
68
85
|
interface Hooks {
|
|
69
86
|
event?: (input: {
|
|
70
|
-
event:
|
|
87
|
+
event: BusEvent;
|
|
71
88
|
}) => Promise<void>;
|
|
72
89
|
config?: (input: unknown) => Promise<void>;
|
|
73
90
|
tool?: {
|
|
@@ -217,4 +234,4 @@ declare const _default: {
|
|
|
217
234
|
server: Plugin;
|
|
218
235
|
};
|
|
219
236
|
|
|
220
|
-
export { type Hooks, type Message, type Model, type Part, type Plugin, type PluginInput, type PluginModule, type Project, type ToolContext, type ToolDefinition, type UserMessage, WorkflowsPlugin, _default as default };
|
|
237
|
+
export { type BusEvent, type Hooks, type Message, type Model, type OtherEvent, type Part, type Plugin, type PluginInput, type PluginModule, type Project, type SessionCompactedEvent, type SessionIdleEvent, type ToolContext, type ToolDefinition, type UserMessage, WorkflowsPlugin, _default as default };
|
package/dist/index.js
CHANGED
|
@@ -16902,7 +16902,7 @@ var PlanManager = class {
|
|
|
16902
16902
|
if (workflowName === "custom") {
|
|
16903
16903
|
return void 0;
|
|
16904
16904
|
}
|
|
16905
|
-
return `https://
|
|
16905
|
+
return `https://codemcp.github.io/workflows/workflows/${workflowName}`;
|
|
16906
16906
|
}
|
|
16907
16907
|
/**
|
|
16908
16908
|
* Generate base instructions for the LLM after a workflow is started.
|
|
@@ -18663,9 +18663,13 @@ var InstructionGenerator = class {
|
|
|
18663
18663
|
const { phase, conversationContext, allowedFilePatterns } = context;
|
|
18664
18664
|
const phaseName = capitalizePhase(phase);
|
|
18665
18665
|
let workflowSection = `---
|
|
18666
|
-
|
|
18667
|
-
|
|
18668
|
-
|
|
18666
|
+
### YOU MUST FOLLOW THESE INSTRUCTIONS:
|
|
18667
|
+
|
|
18668
|
+
**IMPORTANT: Read \`${conversationContext.planFilePath}\`** for context.
|
|
18669
|
+
|
|
18670
|
+
**ACTION REQUIRED: Focus on "${phaseName}" tasks** and log decisions in "Key Decisions"
|
|
18671
|
+
|
|
18672
|
+
**CRITICAL: Do NOT use other task/todo tools** - use only the plan file for task tracking`;
|
|
18669
18673
|
if (allowedFilePatterns && allowedFilePatterns.length > 0 && !allowedFilePatterns.includes("**/*") && !allowedFilePatterns.includes("*")) {
|
|
18670
18674
|
workflowSection += `
|
|
18671
18675
|
- Files allowed: \`${allowedFilePatterns.join("`, `")}\``;
|
|
@@ -21453,7 +21457,7 @@ Then retry \`start_development\`.`;
|
|
|
21453
21457
|
if (workflowName === "custom") {
|
|
21454
21458
|
return void 0;
|
|
21455
21459
|
}
|
|
21456
|
-
return `https://
|
|
21460
|
+
return `https://codemcp.github.io/workflows/workflows/${workflowName}`;
|
|
21457
21461
|
}
|
|
21458
21462
|
/**
|
|
21459
21463
|
* Get the current git branch for a project
|
|
@@ -27030,7 +27034,7 @@ var PlanManager2 = class {
|
|
|
27030
27034
|
if (workflowName === "custom") {
|
|
27031
27035
|
return void 0;
|
|
27032
27036
|
}
|
|
27033
|
-
return `https://
|
|
27037
|
+
return `https://codemcp.github.io/workflows/workflows/${workflowName}`;
|
|
27034
27038
|
}
|
|
27035
27039
|
/**
|
|
27036
27040
|
* Generate base instructions for the LLM after a workflow is started.
|
|
@@ -28057,9 +28061,13 @@ var InstructionGenerator2 = class {
|
|
|
28057
28061
|
const { phase, conversationContext, allowedFilePatterns } = context;
|
|
28058
28062
|
const phaseName = capitalizePhase3(phase);
|
|
28059
28063
|
let workflowSection = `---
|
|
28060
|
-
|
|
28061
|
-
|
|
28062
|
-
|
|
28064
|
+
### YOU MUST FOLLOW THESE INSTRUCTIONS:
|
|
28065
|
+
|
|
28066
|
+
**IMPORTANT: Read \`${conversationContext.planFilePath}\`** for context.
|
|
28067
|
+
|
|
28068
|
+
**ACTION REQUIRED: Focus on "${phaseName}" tasks** and log decisions in "Key Decisions"
|
|
28069
|
+
|
|
28070
|
+
**CRITICAL: Do NOT use other task/todo tools** - use only the plan file for task tracking`;
|
|
28063
28071
|
if (allowedFilePatterns && allowedFilePatterns.length > 0 && !allowedFilePatterns.includes("**/*") && !allowedFilePatterns.includes("*")) {
|
|
28064
28072
|
workflowSection += `
|
|
28065
28073
|
- Files allowed: \`${allowedFilePatterns.join("`, `")}\``;
|
|
@@ -28202,17 +28210,25 @@ function createProceedToPhaseTool(getServerContext, setBufferedInstructions, cli
|
|
|
28202
28210
|
plan_file_path: data.plan_file_path,
|
|
28203
28211
|
allowed_file_patterns: data.allowed_file_patterns
|
|
28204
28212
|
});
|
|
28205
|
-
const
|
|
28206
|
-
|
|
28207
|
-
|
|
28208
|
-
|
|
28209
|
-
|
|
28210
|
-
|
|
28211
|
-
|
|
28212
|
-
|
|
28213
|
-
|
|
28214
|
-
|
|
28215
|
-
|
|
28213
|
+
const autoCompact = process.env["WORKFLOW_AUTO_COMPACT"]?.trim()?.toLowerCase();
|
|
28214
|
+
if (autoCompact !== "false") {
|
|
28215
|
+
const model = getModel();
|
|
28216
|
+
client.session.summarize({
|
|
28217
|
+
path: { id: context.sessionID },
|
|
28218
|
+
...model ? { body: model } : {}
|
|
28219
|
+
}).catch(() => {
|
|
28220
|
+
});
|
|
28221
|
+
logger37.info("Triggered compaction after phase transition", {
|
|
28222
|
+
phase: data.phase,
|
|
28223
|
+
sessionID: context.sessionID,
|
|
28224
|
+
hasModel: !!model
|
|
28225
|
+
});
|
|
28226
|
+
} else {
|
|
28227
|
+
logger37.debug("Skipped compaction: WORKFLOW_AUTO_COMPACT=false", {
|
|
28228
|
+
phase: data.phase,
|
|
28229
|
+
sessionID: context.sessionID
|
|
28230
|
+
});
|
|
28231
|
+
}
|
|
28216
28232
|
const lines = [];
|
|
28217
28233
|
lines.push(`Transitioned to: ${data.phase}`);
|
|
28218
28234
|
if (data.transition_reason) {
|
|
@@ -28597,6 +28613,8 @@ var WorkflowsPlugin = async (input) => {
|
|
|
28597
28613
|
let currentSessionId = null;
|
|
28598
28614
|
let lastKnownSessionId = null;
|
|
28599
28615
|
let bufferedInstructions = null;
|
|
28616
|
+
let postCompactionSession = null;
|
|
28617
|
+
let postCompactionMessagePending = false;
|
|
28600
28618
|
let lastKnownModel = null;
|
|
28601
28619
|
function setBufferedInstructions(result) {
|
|
28602
28620
|
bufferedInstructions = {
|
|
@@ -28684,6 +28702,13 @@ var WorkflowsPlugin = async (input) => {
|
|
|
28684
28702
|
if (hookInput.model) {
|
|
28685
28703
|
lastKnownModel = hookInput.model;
|
|
28686
28704
|
}
|
|
28705
|
+
if (postCompactionMessagePending) {
|
|
28706
|
+
postCompactionMessagePending = false;
|
|
28707
|
+
logger37.debug(
|
|
28708
|
+
"chat.message: skipping synthetic part for post-compaction instructions message"
|
|
28709
|
+
);
|
|
28710
|
+
return;
|
|
28711
|
+
}
|
|
28687
28712
|
if (!isAgentEnabled(hookInput.agent)) {
|
|
28688
28713
|
logger37.debug(
|
|
28689
28714
|
"chat.message: Agent not enabled \u2014 injecting tool suppression",
|
|
@@ -28822,8 +28847,10 @@ ACTION REQUIRED: Use transition_phase tool to move to a phase that allows editin
|
|
|
28822
28847
|
},
|
|
28823
28848
|
/**
|
|
28824
28849
|
* Hook 3: experimental.session.compacting
|
|
28825
|
-
* Fires when session is being compacted. We provide
|
|
28826
|
-
*
|
|
28850
|
+
* Fires when session is being compacted. We provide full phase instructions
|
|
28851
|
+
* so the compaction summary is self-sufficient — the AI knows exactly what
|
|
28852
|
+
* to continue even if the chat.message hook doesn't fire for the synthetic
|
|
28853
|
+
* auto-compaction "continue" message.
|
|
28827
28854
|
*/
|
|
28828
28855
|
"experimental.session.compacting": async (hookInput, output) => {
|
|
28829
28856
|
logger37.debug("experimental.session.compacting hook fired", {
|
|
@@ -28834,13 +28861,103 @@ ACTION REQUIRED: Use transition_phase tool to move to a phase that allows editin
|
|
|
28834
28861
|
logger37.debug("No active workflow - skipping compaction guidance");
|
|
28835
28862
|
return;
|
|
28836
28863
|
}
|
|
28864
|
+
let phaseInstructions = null;
|
|
28865
|
+
try {
|
|
28866
|
+
const serverContext = await getServerContext();
|
|
28867
|
+
const handler = new WhatsNextHandler();
|
|
28868
|
+
const handlerResult = await handler.handle({}, serverContext);
|
|
28869
|
+
if (handlerResult.success && handlerResult.data) {
|
|
28870
|
+
phaseInstructions = stripWhatsNextReferences(
|
|
28871
|
+
handlerResult.data.instructions
|
|
28872
|
+
);
|
|
28873
|
+
}
|
|
28874
|
+
} catch (_err) {
|
|
28875
|
+
}
|
|
28837
28876
|
output.context.push(
|
|
28838
28877
|
"Preserve: user intents, key decisions, significant changes and the reasoning why they were made. Remove tool calls, intermediate thoughts, and minor details."
|
|
28839
28878
|
);
|
|
28840
|
-
|
|
28841
|
-
|
|
28842
|
-
|
|
28843
|
-
|
|
28879
|
+
if (phaseInstructions) {
|
|
28880
|
+
output.context.push(
|
|
28881
|
+
`Current workflow phase: ${state.phase}. After compaction, resume with full phase context:
|
|
28882
|
+
|
|
28883
|
+
${phaseInstructions}`
|
|
28884
|
+
);
|
|
28885
|
+
} else {
|
|
28886
|
+
output.context.push(
|
|
28887
|
+
`End summary with: "Continue ${state.phase} phase. ${state.phaseDescription || ""}"`
|
|
28888
|
+
);
|
|
28889
|
+
}
|
|
28890
|
+
logger37.info("Injected compaction guidance", {
|
|
28891
|
+
phase: state.phase,
|
|
28892
|
+
fullInstructions: phaseInstructions !== null
|
|
28893
|
+
});
|
|
28894
|
+
},
|
|
28895
|
+
/**
|
|
28896
|
+
* Hook 4: event
|
|
28897
|
+
* Listens for bus events. When a session compaction completes we record it,
|
|
28898
|
+
* then when the session becomes idle we send a real user message so the
|
|
28899
|
+
* normal chat.message hook fires and injects phase instructions — giving the
|
|
28900
|
+
* AI full workflow context to continue after the compaction.
|
|
28901
|
+
*
|
|
28902
|
+
* We intentionally do NOT suppress the default synthetic "continue" message
|
|
28903
|
+
* (experimental.compaction.autocontinue). It may produce a first generic AI
|
|
28904
|
+
* response, but the idle trigger below ensures a proper phase-aware follow-up.
|
|
28905
|
+
*/
|
|
28906
|
+
event: async ({ event }) => {
|
|
28907
|
+
logger37.debug("event hook fired", { type: event.type });
|
|
28908
|
+
if (event.type === "session.compacted") {
|
|
28909
|
+
postCompactionSession = event.properties.sessionID;
|
|
28910
|
+
logger37.info("session.compacted: pending phase-aware continue", {
|
|
28911
|
+
sessionID: postCompactionSession
|
|
28912
|
+
});
|
|
28913
|
+
return;
|
|
28914
|
+
}
|
|
28915
|
+
if (event.type === "session.idle" && postCompactionSession === event.properties.sessionID) {
|
|
28916
|
+
const sessionID = postCompactionSession;
|
|
28917
|
+
postCompactionSession = null;
|
|
28918
|
+
logger37.info(
|
|
28919
|
+
"session.idle after compaction: sending phase-aware continue",
|
|
28920
|
+
{ sessionID }
|
|
28921
|
+
);
|
|
28922
|
+
await new Promise((resolve6) => setTimeout(resolve6, 500));
|
|
28923
|
+
let promptText = "Continue with the current phase.";
|
|
28924
|
+
let usedPhaseInstructions = false;
|
|
28925
|
+
try {
|
|
28926
|
+
const serverContext = await getServerContext();
|
|
28927
|
+
const handler = new WhatsNextHandler();
|
|
28928
|
+
const handlerResult = await handler.handle({}, serverContext);
|
|
28929
|
+
if (handlerResult.success && handlerResult.data) {
|
|
28930
|
+
const instructions = stripWhatsNextReferences(
|
|
28931
|
+
handlerResult.data.instructions
|
|
28932
|
+
);
|
|
28933
|
+
if (instructions.trim()) {
|
|
28934
|
+
promptText = instructions;
|
|
28935
|
+
usedPhaseInstructions = true;
|
|
28936
|
+
}
|
|
28937
|
+
}
|
|
28938
|
+
} catch (_err) {
|
|
28939
|
+
}
|
|
28940
|
+
if (usedPhaseInstructions) {
|
|
28941
|
+
postCompactionMessagePending = true;
|
|
28942
|
+
}
|
|
28943
|
+
try {
|
|
28944
|
+
const client = input.client;
|
|
28945
|
+
await client.session.promptAsync({
|
|
28946
|
+
path: { id: sessionID },
|
|
28947
|
+
body: {
|
|
28948
|
+
parts: [{ type: "text", text: promptText }]
|
|
28949
|
+
}
|
|
28950
|
+
});
|
|
28951
|
+
logger37.info("session.idle: phase-aware continue sent (async)", {
|
|
28952
|
+
sessionID
|
|
28953
|
+
});
|
|
28954
|
+
} catch (err) {
|
|
28955
|
+
logger37.error("session.idle: failed to send phase-aware continue", {
|
|
28956
|
+
sessionID,
|
|
28957
|
+
error: err instanceof Error ? err.message : String(err)
|
|
28958
|
+
});
|
|
28959
|
+
}
|
|
28960
|
+
}
|
|
28844
28961
|
},
|
|
28845
28962
|
/**
|
|
28846
28963
|
* Custom tools - always registered to allow clear error messages.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemcp/workflows-opencode",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.13.4",
|
|
4
4
|
"description": "OpenCode plugin for structured development workflows",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"rimraf": "^6.0.1",
|
|
20
20
|
"tsup": "^8.0.0",
|
|
21
21
|
"vitest": "4.0.18",
|
|
22
|
-
"@codemcp/workflows-
|
|
23
|
-
"@codemcp/workflows-
|
|
22
|
+
"@codemcp/workflows-core": "6.13.4",
|
|
23
|
+
"@codemcp/workflows-server": "6.13.4"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"@anthropic-ai/sdk": "*"
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"license": "MIT",
|
|
40
40
|
"repository": {
|
|
41
41
|
"type": "git",
|
|
42
|
-
"url": "https://github.com/
|
|
42
|
+
"url": "https://github.com/codemcp/workflows"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsup && pnpm copy-resources",
|
|
@@ -16,7 +16,7 @@ systemPrompt: |
|
|
|
16
16
|
|
|
17
17
|
## Available Tools
|
|
18
18
|
|
|
19
|
-
**Workflow Navigation
|
|
19
|
+
**Workflow Navigation:**
|
|
20
20
|
- `whats_next()`: Call this after EVERY user message to get phase-specific guidance
|
|
21
21
|
- `proceed_to_phase()`: Move to the next phase when your work is complete (only when responsible)
|
|
22
22
|
|
|
@@ -45,7 +45,7 @@ llmSettings:
|
|
|
45
45
|
reasoningEffort: medium
|
|
46
46
|
|
|
47
47
|
mcpServers:
|
|
48
|
-
workflows:
|
|
48
|
+
'workflows':
|
|
49
49
|
type: stdio
|
|
50
50
|
command: npx
|
|
51
51
|
args:
|
|
@@ -16,7 +16,7 @@ systemPrompt: |
|
|
|
16
16
|
|
|
17
17
|
## Available Tools
|
|
18
18
|
|
|
19
|
-
**Workflow Navigation
|
|
19
|
+
**Workflow Navigation**
|
|
20
20
|
- `whats_next()`: Call this after EVERY user message to get phase-specific guidance
|
|
21
21
|
- `proceed_to_phase()`: Move to the next phase when your work is complete (only when responsible)
|
|
22
22
|
|
|
@@ -16,7 +16,7 @@ systemPrompt: |
|
|
|
16
16
|
|
|
17
17
|
## Available Tools
|
|
18
18
|
|
|
19
|
-
**Workflow Navigation
|
|
19
|
+
**Workflow Navigation**
|
|
20
20
|
- `whats_next()`: Call this after EVERY user message to get phase-specific guidance
|
|
21
21
|
- `proceed_to_phase()`: Move to the next phase when your work is complete (only when responsible)
|
|
22
22
|
|