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.
- package/ATTRIBUTION.md +38 -0
- package/LICENSE +21 -0
- package/README.md +357 -0
- package/dist/agents/councillor.d.ts +1 -0
- package/dist/agents/councillor.js +14 -0
- package/dist/agents/designer.d.ts +1 -0
- package/dist/agents/designer.js +31 -0
- package/dist/agents/explorer.d.ts +1 -0
- package/dist/agents/explorer.js +15 -0
- package/dist/agents/fixer.d.ts +1 -0
- package/dist/agents/fixer.js +23 -0
- package/dist/agents/index.d.ts +2 -0
- package/dist/agents/index.js +55 -0
- package/dist/agents/lazy.d.ts +1 -0
- package/dist/agents/lazy.js +3 -0
- package/dist/agents/librarian.d.ts +1 -0
- package/dist/agents/librarian.js +26 -0
- package/dist/agents/observer.d.ts +1 -0
- package/dist/agents/observer.js +20 -0
- package/dist/agents/oracle.d.ts +1 -0
- package/dist/agents/oracle.js +30 -0
- package/dist/council/council-manager.d.ts +42 -0
- package/dist/council/council-manager.js +223 -0
- package/dist/council/index.d.ts +2 -0
- package/dist/council/index.js +1 -0
- package/dist/hooks/apply-patch-rescue.d.ts +7 -0
- package/dist/hooks/apply-patch-rescue.js +150 -0
- package/dist/hooks/background-job-board.d.ts +92 -0
- package/dist/hooks/background-job-board.js +452 -0
- package/dist/hooks/chat-params.d.ts +16 -0
- package/dist/hooks/chat-params.js +30 -0
- package/dist/hooks/deepwork.d.ts +9 -0
- package/dist/hooks/deepwork.js +55 -0
- package/dist/hooks/error-recovery.d.ts +21 -0
- package/dist/hooks/error-recovery.js +216 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.js +61 -0
- package/dist/hooks/lazy-command.d.ts +16 -0
- package/dist/hooks/lazy-command.js +178 -0
- package/dist/hooks/messages-transform.d.ts +40 -0
- package/dist/hooks/messages-transform.js +358 -0
- package/dist/hooks/permission-guard.d.ts +5 -0
- package/dist/hooks/permission-guard.js +38 -0
- package/dist/hooks/runtime.d.ts +169 -0
- package/dist/hooks/runtime.js +653 -0
- package/dist/hooks/session-events.d.ts +16 -0
- package/dist/hooks/session-events.js +65 -0
- package/dist/hooks/system-transform.d.ts +8 -0
- package/dist/hooks/system-transform.js +113 -0
- package/dist/hooks/task-session.d.ts +32 -0
- package/dist/hooks/task-session.js +177 -0
- package/dist/hooks/workflow-classifier.d.ts +17 -0
- package/dist/hooks/workflow-classifier.js +170 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +85 -0
- package/dist/opencode-control-plane.d.ts +20 -0
- package/dist/opencode-control-plane.js +95 -0
- package/dist/ponytail.d.ts +1 -0
- package/dist/ponytail.js +33 -0
- package/dist/skills/index.d.ts +5 -0
- package/dist/skills/index.js +10 -0
- package/dist/skills/lazy/build/SKILL.md +62 -0
- package/dist/skills/lazy/debug/SKILL.md +17 -0
- package/dist/skills/lazy/grill/SKILL.md +54 -0
- package/dist/skills/lazy/plan/SKILL.md +52 -0
- package/dist/skills/lazy/review/SKILL.md +29 -0
- package/dist/skills/lazy/security/SKILL.md +29 -0
- package/dist/skills/lazy/simplify/SKILL.md +52 -0
- package/dist/skills/lazy/specify/SKILL.md +62 -0
- package/dist/skills/lazy/worktree/SKILL.md +66 -0
- package/dist/tools/cancel-task.d.ts +3 -0
- package/dist/tools/cancel-task.js +37 -0
- package/dist/tools/council.d.ts +6 -0
- package/dist/tools/council.js +41 -0
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.js +2 -0
- package/dist/v2.d.ts +1 -0
- package/dist/v2.js +42 -0
- package/docs/architecture.md +47 -0
- package/docs/council.md +200 -0
- package/docs/desktop-distribution.md +36 -0
- package/docs/opencode-integration.md +54 -0
- package/docs/positioning.md +44 -0
- package/docs/product-audit.md +187 -0
- package/docs/product-plan.md +56 -0
- package/docs/state-machine.md +35 -0
- package/docs/user-manual.md +439 -0
- package/docs/work-plan.md +190 -0
- 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,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
|
+
}
|
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.
|
package/docs/council.md
ADDED
|
@@ -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.
|