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,66 @@
1
+ ---
2
+ name: lazy/worktree
3
+ description: Manage Git worktrees as isolated coding lanes for parallel or risky work.
4
+ ---
5
+
6
+ Create, manage, and clean up Git worktrees for isolated coding lanes.
7
+
8
+ All worktrees live under `.worktrees/<slug>/`.
9
+
10
+ ## Safety rules
11
+
12
+ Before any Git mutation:
13
+
14
+ - [ ] Confirm current dir is inside a Git repo
15
+ - [ ] Check current branch, base branch, dirty state
16
+ - [ ] Run `git worktree list` to avoid conflicts
17
+ - [ ] Ensure branch name doesn't already exist locally or remote
18
+ - [ ] Ensure `.worktrees/` is gitignored
19
+
20
+ **Always ask user confirmation before:**
21
+
22
+ - `git worktree add` or `remove`
23
+ - Branch creation/deletion
24
+ - Merges, rebases, cherry-picks
25
+ - `git reset --hard`, `git clean`, `git push --force`
26
+
27
+ ## Workflow
28
+
29
+ ### Setup
30
+
31
+ ```bash
32
+ git worktree add -b <branch> .worktrees/<slug> <base>
33
+ ```
34
+
35
+ ### Execute
36
+
37
+ Run sub-agents with `workdir` set to `.worktrees/<slug>`. Do not modify the main checkout for lane work.
38
+
39
+ ### Integrate
40
+
41
+ 1. Run lint + build + tests inside the worktree
42
+ 2. Show diff against base branch
43
+ 3. Ask user confirmation to merge/cherry-pick
44
+
45
+ ### Cleanup
46
+
47
+ ```bash
48
+ git worktree remove .worktrees/<slug>
49
+ ```
50
+
51
+ ### State tracking
52
+
53
+ If `.worktrees/worktrees.json` exists, update it with lane metadata (slug, branch, path, base, purpose, status).
54
+
55
+ ## When to use
56
+
57
+ - Risky refactoring that could break the active environment
58
+ - Parallel tasks requiring context switching
59
+ - Prototyping that may be discarded
60
+ - Complex upgrades
61
+
62
+ ## When NOT to use
63
+
64
+ - Single-file changes or minor bug fixes
65
+ - Documentation updates
66
+ - User didn't ask for it
@@ -0,0 +1,3 @@
1
+ import { type ToolDefinition } from "@opencode-ai/plugin";
2
+ import type { BackgroundJobBoard } from "../hooks/background-job-board.js";
3
+ export declare function createCancelTaskTool(jobBoard: BackgroundJobBoard): ToolDefinition;
@@ -0,0 +1,37 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ export function createCancelTaskTool(jobBoard) {
3
+ return tool({
4
+ description: "Cancel a running background job by task ID. Use when a subagent is no longer needed or a running lane becomes obsolete.",
5
+ args: {
6
+ task_id: tool.schema.string().describe("The task ID of the running job to cancel (e.g. 'lazy-oracle-3' or the session ID shown in the Background Job Board)."),
7
+ reason: tool.schema.string().optional().describe("Optional reason for cancellation (logged for traceability)."),
8
+ },
9
+ execute: async (args, context) => {
10
+ const { task_id, reason } = args;
11
+ await context.ask?.({
12
+ permission: "Cancel a running background job",
13
+ patterns: ["cancel_task"],
14
+ always: [],
15
+ metadata: { task_id, reason },
16
+ });
17
+ const job = jobBoard.findJobByTaskID(task_id) ?? jobBoard.findJobByAlias(task_id);
18
+ if (!job) {
19
+ return {
20
+ output: `No job found with task ID: ${task_id}`,
21
+ metadata: { error: true },
22
+ };
23
+ }
24
+ if (job.state !== "running") {
25
+ return {
26
+ output: `Job ${task_id} is already in state: ${job.state} (not running). No action taken.`,
27
+ metadata: { state: job.state },
28
+ };
29
+ }
30
+ jobBoard.cancelJob(task_id);
31
+ return {
32
+ output: `Cancelled: ${job.alias} (${task_id})${reason ? ` — ${reason}` : ""}`,
33
+ metadata: { task_id, alias: job.alias, state: "cancelled" },
34
+ };
35
+ },
36
+ });
37
+ }
@@ -0,0 +1,6 @@
1
+ import { type ToolDefinition } from "@opencode-ai/plugin";
2
+ import { type RequiredCouncilConfig } from "../council/index.js";
3
+ export declare function createCouncilTool(client: any, getCouncilConfig: () => RequiredCouncilConfig, getEligibility?: () => {
4
+ eligible: boolean;
5
+ reason?: string;
6
+ }): ToolDefinition;
@@ -0,0 +1,41 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { runCouncil } from "../council/index.js";
3
+ export function createCouncilTool(
4
+ // deno-lint-ignore no-explicit-any
5
+ client, getCouncilConfig, getEligibility) {
6
+ return tool({
7
+ description: "Run a multi-LLM council session. Multiple models independently analyze the same question, then return results for synthesis. Use for high-risk decisions, ambiguous bugs, or architectural choices with long-term impact.",
8
+ args: {
9
+ prompt: tool.schema.string().describe("The question or task for council members"),
10
+ preset: tool.schema.string().optional().describe("Council preset name (configured in lazyopencode.council.presets)"),
11
+ },
12
+ execute: async (args, context) => {
13
+ const councilConfig = getCouncilConfig();
14
+ if (!councilConfig.enabled) {
15
+ return { output: "Council error: Council is disabled by config", metadata: { error: true } };
16
+ }
17
+ const eligibility = getEligibility?.() ?? { eligible: true };
18
+ if (!eligibility.eligible) {
19
+ return {
20
+ output: eligibility.reason ??
21
+ 'Council blocked: not eligible for current workflow. Run /lazy start for risk classification, enter debug, or set council.eligibility = "always".',
22
+ metadata: { error: true },
23
+ };
24
+ }
25
+ await context.ask?.({
26
+ permission: "Run a multi-LLM council session",
27
+ patterns: ["council_session"],
28
+ always: [],
29
+ metadata: { preset: args.preset, prompt: args.prompt },
30
+ });
31
+ const result = await runCouncil(args.prompt, client, councilConfig, args.preset, context.sessionID, context.abort);
32
+ if (!result.success) {
33
+ return {
34
+ output: result.formatted || `Council error: ${result.error || "unknown"}`,
35
+ metadata: { error: true },
36
+ };
37
+ }
38
+ return { output: result.formatted };
39
+ },
40
+ });
41
+ }
@@ -0,0 +1,2 @@
1
+ export { createCouncilTool } from "./council.js";
2
+ export { createCancelTaskTool } from "./cancel-task.js";
@@ -0,0 +1,2 @@
1
+ export { createCouncilTool } from "./council.js";
2
+ export { createCancelTaskTool } from "./cancel-task.js";
package/dist/v2.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare const LazyOpenCodeV2Plugin: import("@opencode-ai/plugin/v2/promise").Plugin;
package/dist/v2.js ADDED
@@ -0,0 +1,42 @@
1
+ import { define } from "@opencode-ai/plugin/v2/promise";
2
+ import { createAgents } from "./agents/index.js";
3
+ import { getSkillsDir } from "./skills/index.js";
4
+ export const LazyOpenCodeV2Plugin = define({
5
+ id: "@lazyopencode/core",
6
+ setup(context) {
7
+ const ctx = context;
8
+ ctx.agent?.transform((draft) => {
9
+ for (const [id, defaults] of Object.entries(createAgents())) {
10
+ draft.update?.(id, (agent) => {
11
+ Object.assign(agent, { ...defaults, ...agent });
12
+ });
13
+ }
14
+ draft.default?.("lazy");
15
+ });
16
+ ctx.command?.transform((draft) => {
17
+ draft.update?.("lazy", (command) => {
18
+ Object.assign(command, {
19
+ template: "Scope-govern a task: start/status/reset/mode/explain/review/simplify/debug/close/doctor/verify/risk/behavior/deepwork",
20
+ description: "Classify, gate, track, and close AI coding work",
21
+ ...command,
22
+ });
23
+ });
24
+ draft.update?.("deepwork", (command) => {
25
+ Object.assign(command, {
26
+ template: "Alias for /lazy deepwork <task>",
27
+ description: "Compatibility alias for lazy deepwork mode",
28
+ ...command,
29
+ });
30
+ });
31
+ });
32
+ ctx.skill?.transform((draft) => {
33
+ draft.source?.({ type: "local", path: getSkillsDir() });
34
+ });
35
+ ctx.reference?.transform((draft) => {
36
+ draft.add?.("lazyopencode", {
37
+ type: "local",
38
+ path: getSkillsDir(),
39
+ });
40
+ });
41
+ },
42
+ });
@@ -0,0 +1,47 @@
1
+ # Architecture
2
+
3
+ LazyOpenCode is an OpenCode plugin with a shared runtime.
4
+
5
+ ## Plugin Entry
6
+
7
+ The npm package default export uses OpenCode v2 promise registration for agents,
8
+ commands, skills, and references. The named `LazyOpenCodePluginV1` export keeps
9
+ the legacy hook adapter for chat, message, command, permission, and tool
10
+ governance until v2 exposes equivalent hook surfaces.
11
+
12
+ ## Runtime
13
+
14
+ `LazyRuntime` owns config, scope, job board, workflow trace, OpenCode snapshot,
15
+ close report state, persistence, reset, doctor output, and status formatting.
16
+
17
+ Default persistence writes to `~/.lazyopencode/state/<scopeID>.json`, never to the project repository unless explicitly configured.
18
+
19
+ ## Token Control
20
+
21
+ The messages hook keeps context bounded with `lazyopencode.maxMessages` (default `80`), records message-pruning stats for `/lazy status`, strips image payloads into file references for `@lazy-observer`, filters available skills for the `lazy` primary agent, and injects summarized job-board state instead of raw subagent output.
22
+
23
+ ## Hooks
24
+
25
+ Hooks receive the runtime:
26
+
27
+ - system transform injects Ponytail and lazy scope-governor behavior
28
+ - messages transform injects job board status and workflow gate nudges
29
+ - permission guard keeps destructive commands at ask
30
+ - task hooks track launches, completions, context files, reuse, and depth
31
+ - session events reconcile, clean up, and record 429 fallback state
32
+ - command hook handles `/lazy`
33
+
34
+ ## Classifier
35
+
36
+ The workflow classifier is local and rule-based. It emits a `WorkflowDecision` with level, action, required stages, reason, bypass flag, and suggested command.
37
+
38
+ ## Commands
39
+
40
+ `/lazy start` is the canonical product entry. Other commands are control and
41
+ closure surfaces: status, reset, mode, explain, review, simplify, debug, close,
42
+ doctor, verify, risk, behavior, and deepwork.
43
+
44
+ ## Desktop Distribution
45
+
46
+ LazyOpenCode Desktop should bundle and enable this plugin by default. Desktop is
47
+ a distribution layer, not a duplicate implementation of runtime governance.
@@ -0,0 +1,200 @@
1
+ # Council — Multi-LLM Parallel Analysis
2
+
3
+ The **council** system runs multiple independent LLM agents (councillors) on the same
4
+ question in parallel, then returns results for synthesis. It is invoked by the
5
+ `lazy-oracle` agent via the `council_session` tool.
6
+
7
+ Use council for high-risk decisions, ambiguous bugs, architectural choices with
8
+ long-term impact, or any question where multiple perspectives reduce blind spots.
9
+
10
+ Council is an optional escalation path, not the default workflow. In the default
11
+ `guarded` mode it only runs for high-risk or ambiguous work, or while the workflow
12
+ stage is `debug`.
13
+
14
+ ---
15
+
16
+ ## How It Works
17
+
18
+ ```
19
+ User question
20
+ → lazy-oracle agent decides council_session is needed
21
+ → CouncilManager creates N sessions (one per councillor)
22
+ → Each councillor runs independently, read-only tools
23
+ → Results are collected, formatted, returned
24
+ → lazy-oracle synthesizes a final recommendation
25
+ ```
26
+
27
+ - Each councillor is a separate OpenCode session with the `lazy-councillor` agent
28
+ - Councillors have read-only access (Read, Glob, Grep, list) — no write capability
29
+ - Sessions are parented to the oracle session for traceability
30
+ - Sessions are cleaned up automatically when the council completes
31
+
32
+ ---
33
+
34
+ ## Configuration
35
+
36
+ Council is configured under `lazyopencode.council` in `opencode.json`:
37
+
38
+ ```jsonc
39
+ {
40
+ "lazyopencode": {
41
+ "council": {
42
+ "enabled": true,
43
+ "eligibility": "guarded",
44
+ "default_preset": "code-review",
45
+ "timeout": 180000,
46
+ "execution_mode": "parallel",
47
+ "retries": 2,
48
+ "maxCouncillors": 3,
49
+ "presets": {
50
+ "code-review": {
51
+ "reasoner": {
52
+ "model": "openai/o3",
53
+ "prompt": "寻找逻辑缺陷和边界条件。"
54
+ },
55
+ "critic": {
56
+ "model": "anthropic/claude-opus-4"
57
+ },
58
+ "nitpicker": {
59
+ "model": "openai/gpt-4o-mini",
60
+ "prompt": "只找代码风格和命名问题。"
61
+ }
62
+ },
63
+ "deep-arch": {
64
+ "architect": {
65
+ "model": "openai/o3",
66
+ "prompt": "Evaluate architectural trade-offs. Focus on coupling, cohesion, and extensibility."
67
+ },
68
+ "security": {
69
+ "model": "anthropic/claude-opus-4",
70
+ "prompt": "Identify security vulnerabilities and data flow risks."
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ ### Options
80
+
81
+ | Option | Type | Default | Description |
82
+ |--------|------|---------|-------------|
83
+ | `enabled` | boolean | `true` | Disable council entirely when false |
84
+ | `eligibility` | string | `"guarded"` | `"guarded"` or `"always"` council access |
85
+ | `default_preset` | string | `"default"` | Preset to use when none specified |
86
+ | `timeout` | number | `180000` | Max total council time in ms (3 min) |
87
+ | `execution_mode` | string | `"parallel"` | `"parallel"` or `"serial"` |
88
+ | `retries` | number | `2` | Retries for empty/failed councillor responses |
89
+ | `maxCouncillors` | number | `3` | Hard cap on model calls per council run |
90
+ | `presets` | object | `{}` | Named preset definitions |
91
+
92
+ ### Preset Entry
93
+
94
+ Each entry in a preset has:
95
+
96
+ | Field | Required | Description |
97
+ |-------|----------|-------------|
98
+ | `model` | yes | `"providerID/modelID"` format (e.g. `"openai/gpt-4o"`) |
99
+ | `prompt` | no | Custom system prompt for this councillor |
100
+
101
+ ---
102
+
103
+ ## Execution Modes
104
+
105
+ ### Parallel (default)
106
+
107
+ All councillors start simultaneously. Results are collected via `Promise.race` with
108
+ a global timeout. Use for independent perspectives where councillors don't need
109
+ each other's output.
110
+
111
+ ### Serial
112
+
113
+ Councillors run one after another. The council stops if one exceeds the remaining
114
+ time budget. Use when councillors should build on each other's work (future
115
+ feature) or when rate limits are a concern.
116
+
117
+ ---
118
+
119
+ ## Failure Handling
120
+
121
+ | Scenario | Behavior |
122
+ |----------|----------|
123
+ | Session creation fails | Councillor marked `error`, rest continue |
124
+ | Prompt times out (> `timeout`) | Councillor marked `timeout`, council stops |
125
+ | Empty/truncated response | Retries up to `retries` times, then `error` |
126
+ | All councillors fail | `success: false`, error message in formatted output |
127
+ | Preset not found | Falls back to `default_preset`, warns |
128
+
129
+ ---
130
+
131
+ ## Tool API
132
+
133
+ The `council_session` tool is registered on the `lazy-oracle` agent.
134
+
135
+ ### Arguments
136
+
137
+ | Name | Type | Required | Description |
138
+ |------|------|----------|-------------|
139
+ | `prompt` | string | yes | The question for council members |
140
+ | `preset` | string | no | Which preset to use (defaults to `default_preset`) |
141
+
142
+ ### Return Value
143
+
144
+ Returns a formatted string containing:
145
+
146
+ ```
147
+ # Council Results
148
+
149
+ ## Question
150
+ <original question>
151
+
152
+ ## <councillor name>
153
+ Status: success
154
+ <independent analysis>
155
+
156
+ ## <councillor name>
157
+ Status: error
158
+ Error: <details>
159
+
160
+ ## Synthesis Required
161
+ Review each councillor's response and synthesize a final recommendation.
162
+ ```
163
+
164
+ The oracle agent receives this output and is expected to synthesize a final
165
+ recommendation.
166
+
167
+ ---
168
+
169
+ ## Design Decisions
170
+
171
+ 1. **Separate sessions, not sub-agents**: Each councillor gets its own HTTP session.
172
+ This isolates context and prevents one councillor's output from polluting another.
173
+
174
+ 2. **Read-only councillors**: No write/edit/shell access. Councillors analyze, not
175
+ modify. This is enforced at the agent prompt level.
176
+
177
+ 3. **Lazy config injection**: The tool uses a getter function to read config at
178
+ execution time, not at plugin init time. This ensures `opencode.json` config
179
+ changes are picked up.
180
+
181
+ 4. **Session cleanup in `finally`**: Council sessions are deleted when done,
182
+ regardless of success or failure. A `catch` on the delete prevents cleanup
183
+ failures from propagating.
184
+
185
+ ---
186
+
187
+ ## When to Use (for lazy-oracle)
188
+
189
+ The council is expensive — N councillors consume N model invocations. Use it for:
190
+
191
+ - **Ambiguous bugs** with multiple possible root causes
192
+ - **Architecture decisions** with long-term cost implications
193
+ - **Security reviews** where missing a finding has real impact
194
+ - **Code review** on critical code (auth, payments, data loss paths)
195
+
196
+ Do NOT use for:
197
+
198
+ - Trivial one-line fixes
199
+ - Well-understood refactors
200
+ - Questions answerable by a single Read/Grep
@@ -0,0 +1,36 @@
1
+ # Desktop Distribution
2
+
3
+ LazyOpenCode Desktop is a preinstalled OpenCode Desktop distribution.
4
+
5
+ This is the `0.1.0` stage, not the current core hardening stage. The intended
6
+ relationship is OpenCode-native: upstream OpenCode remains the runtime,
7
+ LazyOpenCode Desktop ships the governed defaults and health surface.
8
+
9
+ ## Strategy
10
+
11
+ The plugin remains the source of truth. Desktop bundles and enables
12
+ `@lazyopencode/core`; it does not duplicate LazyOpenCode governance logic.
13
+
14
+ ## First-Run Defaults
15
+
16
+ Desktop should merge the defaults from
17
+ `apps/lazyopencode-desktop/lazyopencode.default.jsonc` into the user's generated
18
+ OpenCode config:
19
+
20
+ - Add `@lazyopencode/core` to `plugin` if absent.
21
+ - Add `lazyopencode` defaults only where the user has not set values.
22
+ - Preserve provider, auth, model, MCP, session, and project settings.
23
+
24
+ ## Branding
25
+
26
+ - App name: `LazyOpenCode Desktop`
27
+ - Positioning: zero-config governed team runtime for OpenCode
28
+ - Attribution: based on OpenCode; not the official OpenCode project
29
+
30
+ ## Out Of Scope For 0.0.1
31
+
32
+ - Lazy visual dashboard
33
+ - Deep Desktop workflow fork
34
+ - Provider marketplace
35
+ - MCP marketplace
36
+ - Reimplementing plugin runtime behavior in Desktop
@@ -0,0 +1,54 @@
1
+ # OpenCode Integration
2
+
3
+ LazyOpenCode is an OpenCode-native workflow governor. The plugin should work
4
+ with no `lazyopencode` config block; configuration only customizes defaults.
5
+
6
+ OpenCode loads `dist/index.js` from the npm package. The default export uses the
7
+ v2 promise registration surface for agents, commands, skills, and references.
8
+ The named `LazyOpenCodePluginV1` export remains available for legacy hook
9
+ registration and existing tests. The legacy adapter stays enabled by default
10
+ because current governance still depends on chat, message, command, permission,
11
+ and tool hooks.
12
+
13
+ ## Hook Boundaries
14
+
15
+ - `config`: registers lazy agents, skills, commands, and tools without
16
+ overwriting user-owned config.
17
+ - `permission.ask`: asks before destructive commands when `permissionGuard` is
18
+ enabled.
19
+ - `command.execute.before`: implements `/lazy` and `/deepwork`.
20
+ - `tool.execute.before/after`: tracks subagent launches, completions, reuse, and
21
+ error recovery.
22
+ - chat transforms: inject workflow guidance, job-board status, token-control
23
+ pruning, image redirects, and Ponytail behavior.
24
+
25
+ ## Zero Config
26
+
27
+ Outside Desktop, users still install/load the plugin through OpenCode's normal
28
+ plugin mechanism. Once loaded, LazyOpenCode defaults are complete:
29
+
30
+ - `mode: "governor"`
31
+ - `permissionGuard: true`
32
+ - `maxMessages: 80`
33
+ - `workflowGate: true`
34
+ - `council.eligibility: "guarded"`
35
+ - `sdk.mode: "v2"`
36
+ - `sdk.legacyHookAdapter: true`
37
+ - `takeover: "governed"`
38
+ - `opencode.worktreeIsolation: "risky-only"`
39
+ - `closeReport.autoCollect: true`
40
+
41
+ ## Config Merge Contract
42
+
43
+ LazyOpenCode preserves user config:
44
+
45
+ - Existing non-lazy agents are untouched.
46
+ - Existing lazy agent overrides win over plugin defaults.
47
+ - Existing commands are not overwritten.
48
+ - Skills paths are deduplicated.
49
+
50
+ ## Permission Guard
51
+
52
+ `permissionGuard` is intentionally independent from workflow mode. Even
53
+ `mode: "off"` keeps destructive actions at `ask` unless the user explicitly sets
54
+ `permissionGuard: false`.
@@ -0,0 +1,44 @@
1
+ # Positioning
2
+
3
+ LazyOpenCode is a governed team runtime for AI coding in OpenCode.
4
+
5
+ ## Category
6
+
7
+ OpenCode-native governance plugin now; opinionated OpenCode distribution layer
8
+ next.
9
+
10
+ OpenCode remains the runtime. LazyOpenCode provides the default team, lifecycle
11
+ rules, risk gates, budget controls, and closure discipline.
12
+
13
+ ## Audience
14
+
15
+ OpenCode power users who already delegate real coding work to AI and want less drift, less overbuilding, and better closure.
16
+
17
+ ## Enemy
18
+
19
+ - vague scope
20
+ - overbuilt code
21
+ - unreconciled agents
22
+ - unreviewed output
23
+ - speculative abstractions
24
+
25
+ ## North Star
26
+
27
+ Make AI coding boring, small, and correct.
28
+
29
+ ## Differentiation
30
+
31
+ Ponytail gives the philosophy. LazyOpenCode governs execution.
32
+
33
+ Lightweight agent-routing plugins optimize for delegation. LazyOpenCode includes
34
+ delegation, but its product boundary is the full work lifecycle: classify scope,
35
+ gate risky requests, delegate bounded work, guard destructive permissions, track
36
+ background jobs, budget context/council use, and push review/simplify closure.
37
+
38
+ Autonomous coding assistants optimize for longer independent runs, memory, and
39
+ self-improvement loops. LazyOpenCode emphasizes governed OpenCode operation:
40
+ small scope, explicit risk gates, budget visibility, and boring closure.
41
+
42
+ Desktop is a later `0.1.0` task. The current `0.0.1` target is to make
43
+ `@lazyopencode/core` a complete plugin kernel that a Desktop fork can preinstall
44
+ safely.