lazyopencode-core 0.0.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.
Files changed (89) hide show
  1. package/ATTRIBUTION.md +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +357 -0
  4. package/dist/agents/councillor.d.ts +1 -0
  5. package/dist/agents/councillor.js +14 -0
  6. package/dist/agents/designer.d.ts +1 -0
  7. package/dist/agents/designer.js +31 -0
  8. package/dist/agents/explorer.d.ts +1 -0
  9. package/dist/agents/explorer.js +15 -0
  10. package/dist/agents/fixer.d.ts +1 -0
  11. package/dist/agents/fixer.js +23 -0
  12. package/dist/agents/index.d.ts +2 -0
  13. package/dist/agents/index.js +55 -0
  14. package/dist/agents/lazy.d.ts +1 -0
  15. package/dist/agents/lazy.js +3 -0
  16. package/dist/agents/librarian.d.ts +1 -0
  17. package/dist/agents/librarian.js +26 -0
  18. package/dist/agents/observer.d.ts +1 -0
  19. package/dist/agents/observer.js +20 -0
  20. package/dist/agents/oracle.d.ts +1 -0
  21. package/dist/agents/oracle.js +30 -0
  22. package/dist/council/council-manager.d.ts +42 -0
  23. package/dist/council/council-manager.js +223 -0
  24. package/dist/council/index.d.ts +2 -0
  25. package/dist/council/index.js +1 -0
  26. package/dist/hooks/apply-patch-rescue.d.ts +7 -0
  27. package/dist/hooks/apply-patch-rescue.js +150 -0
  28. package/dist/hooks/background-job-board.d.ts +92 -0
  29. package/dist/hooks/background-job-board.js +452 -0
  30. package/dist/hooks/chat-params.d.ts +16 -0
  31. package/dist/hooks/chat-params.js +30 -0
  32. package/dist/hooks/deepwork.d.ts +9 -0
  33. package/dist/hooks/deepwork.js +55 -0
  34. package/dist/hooks/error-recovery.d.ts +21 -0
  35. package/dist/hooks/error-recovery.js +216 -0
  36. package/dist/hooks/index.d.ts +3 -0
  37. package/dist/hooks/index.js +61 -0
  38. package/dist/hooks/lazy-command.d.ts +16 -0
  39. package/dist/hooks/lazy-command.js +178 -0
  40. package/dist/hooks/messages-transform.d.ts +40 -0
  41. package/dist/hooks/messages-transform.js +358 -0
  42. package/dist/hooks/permission-guard.d.ts +5 -0
  43. package/dist/hooks/permission-guard.js +38 -0
  44. package/dist/hooks/runtime.d.ts +169 -0
  45. package/dist/hooks/runtime.js +653 -0
  46. package/dist/hooks/session-events.d.ts +16 -0
  47. package/dist/hooks/session-events.js +65 -0
  48. package/dist/hooks/system-transform.d.ts +8 -0
  49. package/dist/hooks/system-transform.js +113 -0
  50. package/dist/hooks/task-session.d.ts +32 -0
  51. package/dist/hooks/task-session.js +177 -0
  52. package/dist/hooks/workflow-classifier.d.ts +17 -0
  53. package/dist/hooks/workflow-classifier.js +170 -0
  54. package/dist/index.d.ts +13 -0
  55. package/dist/index.js +85 -0
  56. package/dist/opencode-control-plane.d.ts +20 -0
  57. package/dist/opencode-control-plane.js +95 -0
  58. package/dist/ponytail.d.ts +1 -0
  59. package/dist/ponytail.js +33 -0
  60. package/dist/skills/index.d.ts +5 -0
  61. package/dist/skills/index.js +10 -0
  62. package/dist/skills/lazy/build/SKILL.md +62 -0
  63. package/dist/skills/lazy/debug/SKILL.md +17 -0
  64. package/dist/skills/lazy/grill/SKILL.md +54 -0
  65. package/dist/skills/lazy/plan/SKILL.md +52 -0
  66. package/dist/skills/lazy/review/SKILL.md +29 -0
  67. package/dist/skills/lazy/security/SKILL.md +29 -0
  68. package/dist/skills/lazy/simplify/SKILL.md +52 -0
  69. package/dist/skills/lazy/specify/SKILL.md +62 -0
  70. package/dist/skills/lazy/worktree/SKILL.md +66 -0
  71. package/dist/tools/cancel-task.d.ts +3 -0
  72. package/dist/tools/cancel-task.js +37 -0
  73. package/dist/tools/council.d.ts +6 -0
  74. package/dist/tools/council.js +41 -0
  75. package/dist/tools/index.d.ts +2 -0
  76. package/dist/tools/index.js +2 -0
  77. package/dist/v2.d.ts +1 -0
  78. package/dist/v2.js +42 -0
  79. package/docs/architecture.md +47 -0
  80. package/docs/council.md +200 -0
  81. package/docs/desktop-distribution.md +36 -0
  82. package/docs/opencode-integration.md +54 -0
  83. package/docs/positioning.md +44 -0
  84. package/docs/product-audit.md +187 -0
  85. package/docs/product-plan.md +56 -0
  86. package/docs/state-machine.md +35 -0
  87. package/docs/user-manual.md +439 -0
  88. package/docs/work-plan.md +190 -0
  89. package/package.json +44 -0
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Error recovery hook (tool.execute.after).
3
+ * Full slim-equivalent pattern set:
4
+ * 1. JSON parse error recovery (8+ patterns)
5
+ * 2. Apply-patch failure with structured guidance
6
+ * 3. Task delegate retry guidance (8 error patterns)
7
+ * 4. Post-file-tool nudge (phase reminder for lazy primary read/write)
8
+ */
9
+ // ---------------------------------------------------------------------------
10
+ // JSON recovery patterns (match slim's 8 patterns)
11
+ // ---------------------------------------------------------------------------
12
+ const JSON_ERROR_PATTERNS = [
13
+ /SyntaxError.*JSON/i,
14
+ /Unexpected token.*JSON/i,
15
+ /Unexpected end of JSON input/i,
16
+ /JSON\.parse/i,
17
+ /Expected.*JSON/i,
18
+ /is not valid JSON/i,
19
+ /Unexpected token ['"].*in JSON/i,
20
+ /malformed JSON/i,
21
+ ];
22
+ const JSON_RECOVERY_EXCLUDED_TOOLS = new Set([
23
+ "bash",
24
+ "read",
25
+ "glob",
26
+ "webfetch",
27
+ ]);
28
+ /**
29
+ * Check if output contains a JSON-related error.
30
+ */
31
+ function hasJsonError(output) {
32
+ // If the output is valid JSON, it's not a JSON parse error
33
+ try {
34
+ JSON.parse(output);
35
+ return false;
36
+ }
37
+ catch { /* not valid JSON, check patterns */ }
38
+ return JSON_ERROR_PATTERNS.some((p) => p.test(output));
39
+ }
40
+ /**
41
+ * Try to extract valid JSON from mixed output.
42
+ * ponytail: greedy regex. Upgrade: AST-based extraction for large outputs.
43
+ */
44
+ function tryFixJsonOutput(output) {
45
+ const match = output.match(/(\{[\s\S]*\}|\[[\s\S]*\])/);
46
+ if (!match)
47
+ return null;
48
+ try {
49
+ JSON.parse(match[1]);
50
+ return match[1];
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ // ---------------------------------------------------------------------------
57
+ // Apply-patch failure patterns
58
+ // ---------------------------------------------------------------------------
59
+ const PATCH_FAILURE_PATTERNS = [
60
+ "failed",
61
+ "no match",
62
+ "not found",
63
+ "error applying",
64
+ "patch failed",
65
+ "hunk failed",
66
+ "rejected",
67
+ ];
68
+ function isPatchFailure(output) {
69
+ return PATCH_FAILURE_PATTERNS.some((p) => output.toLowerCase().includes(p));
70
+ }
71
+ const TASK_RETRY_GUIDANCE = [
72
+ {
73
+ pattern: /run_in_background/,
74
+ guidance: "You used `run_in_background` with a non-`task` tool. Only the `task` tool supports background execution. Remove the parameter or switch to the `task` tool.",
75
+ },
76
+ {
77
+ pattern: /load_skills/,
78
+ guidance: "One or more skills could not be loaded. Check the skill name spelling, verify the skill name matches what opencode has installed, or remove the parameter.",
79
+ },
80
+ {
81
+ pattern: /category or subagent_type/,
82
+ guidance: "You must specify either `category` (general) or `subagent_type` (specialist). Provide exactly one — not both, not neither.",
83
+ },
84
+ {
85
+ pattern: /Must provide either category or subagent_type/,
86
+ guidance: "You omitted a required parameter. Add `subagent_type` with one of the available specialist types listed in the error output.",
87
+ },
88
+ {
89
+ pattern: /Unknown category/,
90
+ guidance: "The `category` name you used is not recognized. Rerun with the correct category name, or switch to `subagent_type` with a valid specialist type. Check the error output for the list of accepted values.",
91
+ },
92
+ {
93
+ pattern: /Unknown agent/,
94
+ guidance: "The `subagent_type` you specified does not exist. Use one from the list shown in the error output. Run the task again with a valid agent type.",
95
+ },
96
+ {
97
+ pattern: /Skills not found/,
98
+ guidance: "One or more skills referenced in `skill_names` were not found. Verify the skill names are correct and match what opencode has installed. Remove or correct the offending names and retry.",
99
+ },
100
+ {
101
+ pattern: /is not allowed\. Allowed agents:/,
102
+ guidance: "You tried to use a subagent that is not permitted. Use only agent types from the allowed list shown in the error output. If you need a specialist, delegate to `@lazy-oracle` for architecture/design advice.",
103
+ },
104
+ ];
105
+ // ---------------------------------------------------------------------------
106
+ // Main hook
107
+ // ---------------------------------------------------------------------------
108
+ export function createErrorRecoveryHook(runtime) {
109
+ return (input, output) => {
110
+ const { tool, sessionID } = input;
111
+ const out = output.output;
112
+ // Guard: avoid double-injection
113
+ if (output.output.includes("[JSON PARSE ERROR"))
114
+ return;
115
+ // --- 1. JSON error recovery ---
116
+ // Skip recovery for tools where JSON errors are expected in output (bash, web responses, etc.)
117
+ const isExcludedTool = JSON_RECOVERY_EXCLUDED_TOOLS.has(tool.toLowerCase());
118
+ if (hasJsonError(out) && !isExcludedTool) {
119
+ const fixed = tryFixJsonOutput(out);
120
+ if (fixed) {
121
+ output.output = `${out}\n\n[JSON PARSE ERROR — IMMEDIATE ACTION REQUIRED]
122
+ The system could not parse your JSON. However, valid JSON was extracted from the output.
123
+ STOP and do this NOW:
124
+ 1. LOOK at the recovered JSON below
125
+ 2. CORRECT your JSON (missing braces, unescaped quotes, trailing commas, etc.)
126
+ 3. RETRY the tool call with valid JSON
127
+
128
+ Recovered JSON:
129
+ \`\`\`json\n${fixed}\n\`\`\`
130
+
131
+ DO NOT repeat the exact same invalid call.`;
132
+ output.metadata = {
133
+ ...output.metadata,
134
+ _recovered: true,
135
+ _recoveryType: "json",
136
+ };
137
+ }
138
+ else {
139
+ // No valid JSON to recover — inject the error reminder
140
+ output.output = `${out}\n\n[JSON PARSE ERROR — IMMEDIATE ACTION REQUIRED]
141
+ You sent invalid JSON arguments. The system could not parse your tool call.
142
+ STOP and do this NOW:
143
+ 1. LOOK at the error message above to see what was expected vs what you sent.
144
+ 2. CORRECT your JSON syntax (missing braces, unescaped quotes, trailing commas, etc).
145
+ 3. RETRY the tool call with valid JSON.
146
+ DO NOT repeat the exact same invalid call.`;
147
+ output.metadata = {
148
+ ...output.metadata,
149
+ _recovered: false,
150
+ _recoveryType: "json",
151
+ };
152
+ }
153
+ }
154
+ // --- 2. Task tool error — retry guidance ---
155
+ if (tool === "task" && isErrorOutput(out)) {
156
+ const guidance = matchRetryGuidance(out);
157
+ if (guidance) {
158
+ output.output = `${output.output}\n\n[HINT: ${guidance}]`;
159
+ output.metadata = {
160
+ ...output.metadata,
161
+ _retryGuidance: true,
162
+ };
163
+ }
164
+ }
165
+ // --- 3. Bash tool JSON error — explicit retry instruction ---
166
+ if (tool === "bash" &&
167
+ (hasJsonError(output.output) || output.output.includes("SyntaxError"))) {
168
+ // Even if JSON was extracted, the model needs the instruction to use it
169
+ const jsonMatch = output.output.match(/(\{[\s\S]*\}|\[[\s\S]*\])/);
170
+ if (jsonMatch) {
171
+ output.output =
172
+ `${output.output}\n\n[IMMEDIATE ACTION: The output above contains JSON. Parse and use:\n\`\`\`json\n${jsonMatch[1]}\n\`\`\`\nIgnore the error message — the JSON is intact.]`;
173
+ output.metadata = {
174
+ ...output.metadata,
175
+ _immediateAction: true,
176
+ };
177
+ }
178
+ }
179
+ // --- 4. Apply-patch failure — detailed guidance ---
180
+ if (tool === "apply_patch" && isPatchFailure(out)) {
181
+ output.output = `${output.output}\n\n[PATCH FAILURE — try these in order:
182
+ 1. Read the target file with Read tool to see current state
183
+ 2. Rewrite the patch with exact whitespace (tabs/spaces) matching the file
184
+ 3. If line numbers shifted, use broader surrounding context lines
185
+ 4. As last resort: use Write tool to edit the file directly]`;
186
+ output.metadata = {
187
+ ...output.metadata,
188
+ _patchGuidance: true,
189
+ };
190
+ }
191
+ // --- 5. Post-file-tool phase reminder (lazy primary only) ---
192
+ if ((tool === "Read" || tool === "Write" || tool === "edit") &&
193
+ isLazyPrimarySession(sessionID, runtime)) {
194
+ output.output =
195
+ `${output.output}\n\n[Scheduler: plan lanes → dispatch → wait for hook-driven completion → reconcile → verify.]`;
196
+ }
197
+ };
198
+ }
199
+ // ---------------------------------------------------------------------------
200
+ // Helpers
201
+ // ---------------------------------------------------------------------------
202
+ function isErrorOutput(output) {
203
+ return /error|fail|exception|crash|abort/i.test(output.slice(0, 500));
204
+ }
205
+ function matchRetryGuidance(output) {
206
+ for (const { pattern, guidance } of TASK_RETRY_GUIDANCE) {
207
+ if (pattern.test(output))
208
+ return guidance;
209
+ }
210
+ return null;
211
+ }
212
+ function isLazyPrimarySession(sessionID, runtime) {
213
+ if (!sessionID || !runtime)
214
+ return false;
215
+ return runtime.sessionAgentMap.get(sessionID) === "lazy";
216
+ }
@@ -0,0 +1,3 @@
1
+ import type { Hooks } from "@opencode-ai/plugin";
2
+ import type { LazyRuntime } from "./runtime.js";
3
+ export declare function createHooks(runtime?: LazyRuntime): Pick<Hooks, "experimental.chat.system.transform" | "experimental.chat.messages.transform" | "experimental.session.compacting" | "experimental.compaction.autocontinue" | "chat.params" | "command.execute.before" | "permission.ask" | "tool.execute.before" | "tool.execute.after" | "event">;
@@ -0,0 +1,61 @@
1
+ import { createSystemTransformHook } from "./system-transform.js";
2
+ import { createMessagesTransformHook } from "./messages-transform.js";
3
+ import { createChatParamsHook } from "./chat-params.js";
4
+ import { createTaskSessionAfterHook, createTaskSessionBeforeHook } from "./task-session.js";
5
+ import { createErrorRecoveryHook } from "./error-recovery.js";
6
+ import { createSessionEventsHook } from "./session-events.js";
7
+ import { jobBoard } from "./background-job-board.js";
8
+ import { createApplyPatchRescueHook } from "./apply-patch-rescue.js";
9
+ import { createLazyCommandHandler } from "./lazy-command.js";
10
+ import { createPermissionGuardHook } from "./permission-guard.js";
11
+ // Compose tool.execute.before: patch rescue before task session manager
12
+ const applyPatchRescue = createApplyPatchRescueHook();
13
+ export function createHooks(runtime) {
14
+ const taskSessionBefore = createTaskSessionBeforeHook(runtime);
15
+ const taskSessionAfter = createTaskSessionAfterHook(runtime);
16
+ const errorRecovery = createErrorRecoveryHook(runtime);
17
+ const commandHandler = runtime ? createLazyCommandHandler(runtime) : undefined;
18
+ const permissionGuard = createPermissionGuardHook(runtime);
19
+ const sessionEvents = createSessionEventsHook((sid) => (runtime?.jobBoard ?? jobBoard).getTerminalUnreconciledJobs(sid).map((j) => j.taskID), runtime);
20
+ return {
21
+ "experimental.chat.system.transform": createSystemTransformHook(runtime),
22
+ "experimental.chat.messages.transform": createMessagesTransformHook(runtime),
23
+ "experimental.session.compacting": async (_input, output) => {
24
+ const stage = runtime?.workflow.stage;
25
+ const decision = runtime?.workflow.lastDecision;
26
+ if (stage && stage !== "idle") {
27
+ output.context.push(`Current workflow stage: ${stage}`);
28
+ }
29
+ if (decision) {
30
+ output.context.push(`Last workflow decision: ${decision.action} (${decision.level}) — ${decision.reason}`);
31
+ }
32
+ const recentEvents = runtime?.workflow.recentEvents.slice(-5);
33
+ if (recentEvents && recentEvents.length > 0) {
34
+ output.context.push(`Recent events: ${recentEvents.map((e) => e.summary).join("; ")}`);
35
+ }
36
+ },
37
+ "experimental.compaction.autocontinue": async (_input, output) => {
38
+ const stage = runtime?.workflow.stage;
39
+ // Disable auto-continue during user-interaction stages
40
+ if (stage === "grill" || stage === "specify" || stage === "plan") {
41
+ output.enabled = false;
42
+ }
43
+ },
44
+ "chat.params": createChatParamsHook(runtime),
45
+ "command.execute.before": async (input, output) => {
46
+ if (commandHandler)
47
+ await commandHandler(input, output);
48
+ },
49
+ "permission.ask": permissionGuard,
50
+ "tool.execute.before": async (input, output) => {
51
+ await taskSessionBefore(input, output);
52
+ await applyPatchRescue(input, output);
53
+ },
54
+ "tool.execute.after": async (input, output) => {
55
+ await taskSessionAfter(input, output);
56
+ await runtime?.recordToolEvidence(input, output);
57
+ await errorRecovery(input, output);
58
+ },
59
+ "event": sessionEvents,
60
+ };
61
+ }
@@ -0,0 +1,16 @@
1
+ import type { Config } from "@opencode-ai/plugin";
2
+ import type { LazyRuntime } from "./runtime.js";
3
+ type CommandOutput = {
4
+ parts: {
5
+ type: string;
6
+ text?: string;
7
+ [key: string]: unknown;
8
+ }[];
9
+ };
10
+ export declare function registerLazyCommands(opencodeConfig: Config, runtime: LazyRuntime): void;
11
+ export declare function createLazyCommandHandler(runtime: LazyRuntime): (input: {
12
+ command: string;
13
+ arguments?: string;
14
+ sessionID?: string;
15
+ }, output: CommandOutput) => Promise<void>;
16
+ export {};
@@ -0,0 +1,178 @@
1
+ import { DEEPWORK_ACTIVATION } from "./deepwork.js";
2
+ import { classifyWorkflow, formatWorkflowDecision, } from "./workflow-classifier.js";
3
+ const MODES = new Set(["off", "coach", "governor", "strict"]);
4
+ export function registerLazyCommands(opencodeConfig, runtime) {
5
+ if (!opencodeConfig.command)
6
+ opencodeConfig.command = {};
7
+ if (runtime.config.commands.lazy && !opencodeConfig.command.lazy) {
8
+ opencodeConfig.command.lazy = {
9
+ template: "Scope-govern a task: start/status/reset/mode/explain/review/simplify/debug/close/doctor/verify/risk/behavior/deepwork",
10
+ description: "Classify, gate, track, and close AI coding work",
11
+ };
12
+ }
13
+ if (runtime.config.commands.deepworkAlias && !opencodeConfig.command.deepwork) {
14
+ opencodeConfig.command.deepwork = {
15
+ template: "Alias for /lazy deepwork <task>",
16
+ description: "Compatibility alias for lazy deepwork mode",
17
+ };
18
+ }
19
+ }
20
+ export function createLazyCommandHandler(runtime) {
21
+ return async (input, output) => {
22
+ if (input.command === "deepwork" && runtime.config.commands.deepworkAlias) {
23
+ writeText(output, handleDeepwork(input.arguments ?? ""));
24
+ return;
25
+ }
26
+ if (input.command !== "lazy")
27
+ return;
28
+ const raw = input.arguments?.trim() ?? "";
29
+ const [subcommand, ...rest] = raw.split(/\s+/);
30
+ const args = rest.join(" ").trim();
31
+ switch (subcommand || "status") {
32
+ case "start":
33
+ await runtime.refreshOpenCodeSnapshot(input.sessionID);
34
+ writeText(output, await handleStart(runtime, args));
35
+ return;
36
+ case "status":
37
+ await runtime.refreshOpenCodeSnapshot(input.sessionID);
38
+ writeText(output, runtime.formatStatus(input.sessionID));
39
+ return;
40
+ case "reset":
41
+ await runtime.reset();
42
+ writeText(output, "Lazy runtime reset. Stage: idle.");
43
+ return;
44
+ case "mode":
45
+ writeText(output, await handleMode(runtime, args));
46
+ return;
47
+ case "explain":
48
+ writeText(output, handleExplain(runtime));
49
+ return;
50
+ case "review":
51
+ runtime.setStage("review");
52
+ await runtime.save();
53
+ writeText(output, "Load `lazy/review`. Review current changes for must-fix bugs first, then deletion opportunities and test gaps.");
54
+ return;
55
+ case "simplify":
56
+ runtime.setStage("simplify");
57
+ await runtime.save();
58
+ writeText(output, "Load `lazy/simplify`. Find what to delete, collapse, or replace with stdlib. One finding per line.");
59
+ return;
60
+ case "debug":
61
+ writeText(output, await handleDebug(runtime, args));
62
+ return;
63
+ case "close":
64
+ await runtime.refreshOpenCodeSnapshot(input.sessionID);
65
+ writeText(output, await handleClose(runtime, input.sessionID));
66
+ return;
67
+ case "doctor":
68
+ writeText(output, runtime.formatDoctorReport());
69
+ await runtime.save();
70
+ return;
71
+ case "verify":
72
+ writeText(output, await handleVerify(runtime, args));
73
+ return;
74
+ case "risk":
75
+ writeText(output, await handleEvidence(runtime, "risk", args, "Remaining risk recorded."));
76
+ return;
77
+ case "behavior":
78
+ writeText(output, await handleEvidence(runtime, "behavior", args, "Changed behavior recorded."));
79
+ return;
80
+ case "deepwork":
81
+ writeText(output, handleDeepwork(args));
82
+ return;
83
+ default:
84
+ writeText(output, "Usage: /lazy start <task> | status | reset | mode <off|coach|governor|strict> | explain | review | simplify | debug <msg> | close | doctor | verify <pass|fail|pending> | risk <text> | behavior <text> | deepwork <task>");
85
+ }
86
+ };
87
+ }
88
+ async function handleStart(runtime, task) {
89
+ if (!task)
90
+ return "Usage: /lazy start <task>";
91
+ const decision = classifyWorkflow({ text: task, mode: runtime.config.mode });
92
+ await runtime.recordDecision(decision);
93
+ runtime.setStage(stageForDecision(decision));
94
+ await runtime.save();
95
+ return [
96
+ "LAZY START",
97
+ `Task: ${task}`,
98
+ formatWorkflowDecision(decision),
99
+ `Stage: ${runtime.workflow.stage}`,
100
+ runtime.formatIsolationAdvice(decision),
101
+ `Next: ${decision.suggestedCommand ?? "proceed"}`,
102
+ ].filter(Boolean).join("\n");
103
+ }
104
+ async function handleMode(runtime, mode) {
105
+ if (!MODES.has(mode)) {
106
+ return `Current mode: ${runtime.config.mode}. Usage: /lazy mode <off|coach|governor|strict>`;
107
+ }
108
+ await runtime.setMode(mode);
109
+ return `Lazy mode set to ${mode}.`;
110
+ }
111
+ function handleExplain(runtime) {
112
+ const decision = runtime.workflow.lastDecision;
113
+ if (!decision)
114
+ return "No lazy decision has been recorded yet.";
115
+ return [
116
+ `Last lazy decision: ${decision.action} ${decision.level}`,
117
+ `Reason: ${decision.reason}`,
118
+ `Required stages: ${decision.requiredStages.join(" -> ") || "none"}`,
119
+ `Bypassed: ${decision.bypassedByUser ? "yes" : "no"}`,
120
+ ].join("\n");
121
+ }
122
+ async function handleDebug(runtime, args) {
123
+ runtime.setStage("debug");
124
+ await runtime.save();
125
+ return [
126
+ "LAZY DEBUG",
127
+ `Context: ${args || "(no additional context)"}`,
128
+ "",
129
+ "Load `lazy/debug`. Systematic diagnosis loop: reproduce → isolate → hypothesize → test → fix.",
130
+ "Available: @lazy-oracle for escalation, context7 for library API checks.",
131
+ ].join("\n");
132
+ }
133
+ function handleDeepwork(task) {
134
+ if (!task.trim()) {
135
+ return "What task should deepwork manage? Run `/lazy deepwork <task description>`.";
136
+ }
137
+ return DEEPWORK_ACTIVATION(task.trim());
138
+ }
139
+ async function handleClose(runtime, sessionID) {
140
+ runtime.setStage("close");
141
+ await runtime.save();
142
+ return [
143
+ "LAZY CLOSE",
144
+ "Close contract: review, simplify, verify, reconcile.",
145
+ "Manual corrections: /lazy behavior <text>, /lazy risk <text>, /lazy verify <pass|fail|pending>.",
146
+ "",
147
+ runtime.formatCloseReport(sessionID),
148
+ ].join("\n");
149
+ }
150
+ async function handleVerify(runtime, result) {
151
+ if (result !== "pass" && result !== "fail" && result !== "pending") {
152
+ return "Usage: /lazy verify <pass|fail|pending>";
153
+ }
154
+ await runtime.recordCloseEvidence("verification", result);
155
+ return `Verification result recorded: ${result}.`;
156
+ }
157
+ async function handleEvidence(runtime, kind, text, ok) {
158
+ if (!text.trim()) {
159
+ return kind === "risk" ? "Usage: /lazy risk <text>" : "Usage: /lazy behavior <text>";
160
+ }
161
+ await runtime.recordCloseEvidence(kind, text.trim());
162
+ return ok;
163
+ }
164
+ function stageForDecision(decision) {
165
+ if (decision.requiredStages.includes("grill"))
166
+ return "grill";
167
+ if (decision.requiredStages.includes("debug"))
168
+ return "debug";
169
+ if (decision.requiredStages.includes("plan"))
170
+ return "plan";
171
+ if (decision.requiredStages.includes("build"))
172
+ return "build";
173
+ return "idle";
174
+ }
175
+ function writeText(output, text) {
176
+ output.parts.length = 0;
177
+ output.parts.push({ type: "text", text });
178
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Context pruning + image redirect + job board + workflow gate via messages.transform.
3
+ *
4
+ * Features:
5
+ * 1. Enhanced pruning: keep system + last N user turns + everything between
6
+ * 2. Image processing (strip images, inject @lazy-observer redirect)
7
+ * 3. Job board injection for lazy primary (terminal unreconciled jobs only)
8
+ * 4. Workflow gate: detect skipped steps, inject STOP message
9
+ * 5. Skill filtering per-agent
10
+ *
11
+ * ponytail: Don't remind — gate. Only inject when a condition is met.
12
+ */
13
+ import type { LazyRuntime } from "./runtime.js";
14
+ interface MessagePart {
15
+ type: string;
16
+ text?: string;
17
+ tool_call?: {
18
+ name: string;
19
+ arguments: string;
20
+ };
21
+ tool_result?: {
22
+ tool_use_id: string;
23
+ content: unknown[];
24
+ };
25
+ }
26
+ interface ChatMessage {
27
+ info: {
28
+ role: string;
29
+ agent?: string;
30
+ };
31
+ parts: MessagePart[];
32
+ }
33
+ interface TransformInput {
34
+ sessionID?: string;
35
+ agent?: string;
36
+ }
37
+ export declare function createMessagesTransformHook(runtime?: LazyRuntime): (input: TransformInput, output: {
38
+ messages: ChatMessage[];
39
+ }) => Promise<void>;
40
+ export {};