opencodekit 0.18.5 → 0.18.7

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/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { createRequire } from "node:module";
3
3
  import { cac } from "cac";
4
4
  import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
5
- import { basename, dirname, join, relative, resolve } from "node:path";
5
+ import { basename, dirname, join, relative } from "node:path";
6
6
  import * as p from "@clack/prompts";
7
7
  import color from "picocolors";
8
8
  import { z } from "zod";
@@ -18,11 +18,35 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
18
18
 
19
19
  //#endregion
20
20
  //#region package.json
21
- var version = "0.18.5";
21
+ var version = "0.18.7";
22
22
 
23
23
  //#endregion
24
24
  //#region src/utils/errors.ts
25
25
  /**
26
+ * Resolve the .opencode directory path.
27
+ * Handles two cases:
28
+ * 1. Standard project: cwd has .opencode/ subdirectory
29
+ * 2. Global config dir: cwd IS the opencode config dir (has opencode.json directly)
30
+ * Returns null if neither case applies.
31
+ */
32
+ function resolveOpencodePath() {
33
+ const nested = join(process.cwd(), ".opencode");
34
+ if (existsSync(nested)) return nested;
35
+ if (existsSync(join(process.cwd(), "opencode.json"))) return process.cwd();
36
+ return null;
37
+ }
38
+ /**
39
+ * Resolve opencode path or show "not initialized" error and return null.
40
+ */
41
+ function requireOpencodePath() {
42
+ const resolved = resolveOpencodePath();
43
+ if (!resolved) {
44
+ notInitialized();
45
+ return null;
46
+ }
47
+ return resolved;
48
+ }
49
+ /**
26
50
  * Display a styled error message with optional fix suggestion
27
51
  */
28
52
  function showError(message, fix) {
@@ -79,7 +103,8 @@ const InitOptionsSchema = z.object({
79
103
  yes: z.boolean().optional().default(false),
80
104
  backup: z.boolean().optional().default(false),
81
105
  prune: z.boolean().optional().default(false),
82
- pruneAll: z.boolean().optional().default(false)
106
+ pruneAll: z.boolean().optional().default(false),
107
+ projectOnly: z.boolean().optional().default(false)
83
108
  });
84
109
  const UpgradeOptionsSchema = z.object({
85
110
  force: z.boolean().optional().default(false),
@@ -192,11 +217,8 @@ function parseAction(schema, action) {
192
217
  //#region src/commands/agent.ts
193
218
  async function agentCommand(action) {
194
219
  const validatedAction = parseAction(AgentActionSchema, action);
195
- const opencodePath = join(process.cwd(), ".opencode");
196
- if (!existsSync(opencodePath)) {
197
- notInitialized();
198
- return;
199
- }
220
+ const opencodePath = requireOpencodePath();
221
+ if (!opencodePath) return;
200
222
  const agentPath = join(opencodePath, "agent");
201
223
  switch (validatedAction) {
202
224
  case "list":
@@ -473,11 +495,8 @@ async function removeAgent(agentPath, agentNameArg) {
473
495
  //#region src/commands/command.ts
474
496
  async function commandCommand(action) {
475
497
  const validatedAction = parseAction(CommandActionSchema, action);
476
- const opencodePath = join(process.cwd(), ".opencode");
477
- if (!existsSync(opencodePath)) {
478
- notInitialized();
479
- return;
480
- }
498
+ const opencodePath = requireOpencodePath();
499
+ if (!opencodePath) return;
481
500
  const commandDir = join(opencodePath, "command");
482
501
  switch (validatedAction) {
483
502
  case "list":
@@ -982,12 +1001,9 @@ async function getAgentsFromServer() {
982
1001
  return fetchFromServer("/agent");
983
1002
  }
984
1003
  async function configCommand(action) {
985
- const opencodePath = join(process.cwd(), ".opencode");
1004
+ const opencodePath = requireOpencodePath();
1005
+ if (!opencodePath) return;
986
1006
  const configPath = join(opencodePath, "opencode.json");
987
- if (!existsSync(opencodePath)) {
988
- notInitialized();
989
- return;
990
- }
991
1007
  if (!existsSync(configPath)) {
992
1008
  showError("opencode.json not found", "ock init --force");
993
1009
  return;
@@ -2758,6 +2774,34 @@ const EXCLUDED_FILES = [
2758
2774
  "pnpm-lock.yaml"
2759
2775
  ];
2760
2776
  const PRESERVE_USER_DIRS = ["memory/project", "context"];
2777
+ const SHARED_CONFIG_DIRS = [
2778
+ "agent",
2779
+ "command",
2780
+ "skill",
2781
+ "tool"
2782
+ ];
2783
+ /**
2784
+ * Detect if global config has any of the shared dirs populated.
2785
+ * Returns null if no global config or no shared dirs found.
2786
+ */
2787
+ function detectGlobalConfig() {
2788
+ const globalDir = getGlobalConfigDir();
2789
+ if (!existsSync(globalDir)) return null;
2790
+ const coveredDirs = SHARED_CONFIG_DIRS.filter((d) => {
2791
+ const dirPath = join(globalDir, d);
2792
+ if (!existsSync(dirPath)) return false;
2793
+ try {
2794
+ return readdirSync(dirPath).filter((e) => !e.startsWith(".")).length > 0;
2795
+ } catch {
2796
+ return false;
2797
+ }
2798
+ });
2799
+ if (coveredDirs.length === 0) return null;
2800
+ return {
2801
+ dir: globalDir,
2802
+ coveredDirs
2803
+ };
2804
+ }
2761
2805
  /**
2762
2806
  * Get the global OpenCode config directory based on OS.
2763
2807
  * - macOS/Linux: ~/.config/opencode/ (respects XDG_CONFIG_HOME)
@@ -2801,10 +2845,25 @@ async function copyDir(src, dest) {
2801
2845
  else writeFileSync(destPath, readFileSync(srcPath, "utf-8"));
2802
2846
  }
2803
2847
  }
2804
- async function copyOpenCodeOnly(templateRoot, targetDir) {
2848
+ async function copyOpenCodeOnly(templateRoot, targetDir, skipDirs) {
2805
2849
  const opencodeSrc = join(templateRoot, ".opencode");
2806
2850
  const opencodeDest = join(targetDir, ".opencode");
2807
2851
  if (!existsSync(opencodeSrc)) return false;
2852
+ if (skipDirs && skipDirs.length > 0) {
2853
+ const skipSet = new Set(skipDirs);
2854
+ mkdirSync(opencodeDest, { recursive: true });
2855
+ for (const entry of readdirSync(opencodeSrc, { withFileTypes: true })) {
2856
+ if (EXCLUDED_DIRS.includes(entry.name)) continue;
2857
+ if (!entry.isDirectory() && EXCLUDED_FILES.includes(entry.name)) continue;
2858
+ if (entry.isSymbolicLink()) continue;
2859
+ if (entry.isDirectory() && skipSet.has(entry.name)) continue;
2860
+ const srcPath = join(opencodeSrc, entry.name);
2861
+ const destPath = join(opencodeDest, entry.name);
2862
+ if (entry.isDirectory()) await copyDir(srcPath, destPath);
2863
+ else writeFileSync(destPath, readFileSync(srcPath, "utf-8"));
2864
+ }
2865
+ return true;
2866
+ }
2808
2867
  await copyDir(opencodeSrc, opencodeDest);
2809
2868
  return true;
2810
2869
  }
@@ -3100,18 +3159,36 @@ async function initCommand(rawOptions = {}) {
3100
3159
  }
3101
3160
  projectName = name || projectName;
3102
3161
  }
3162
+ let skipDirs = [];
3163
+ if (!options.global) {
3164
+ const globalConfig = detectGlobalConfig();
3165
+ if (globalConfig && options.projectOnly) {
3166
+ skipDirs = globalConfig.coveredDirs;
3167
+ p.log.info(`Using global config from ${color.cyan(globalConfig.dir)}`);
3168
+ p.log.info(`Skipping: ${skipDirs.map((d) => color.dim(d)).join(", ")}`);
3169
+ } else if (globalConfig && !options.yes) {
3170
+ p.log.info(`Global config found at ${color.cyan(globalConfig.dir)}`);
3171
+ p.log.info(`Available globally: ${globalConfig.coveredDirs.map((d) => color.green(d)).join(", ")}`);
3172
+ const useGlobal = await p.confirm({
3173
+ message: "Skip these (use global config)? Only project-scope files will be created locally.",
3174
+ initialValue: true
3175
+ });
3176
+ if (!p.isCancel(useGlobal) && useGlobal) skipDirs = globalConfig.coveredDirs;
3177
+ } else if (globalConfig && options.yes) p.log.info(`Global config found at ${color.cyan(globalConfig.dir)} — use ${color.bold("--project-only")} to skip shared dirs`);
3178
+ }
3103
3179
  const s = p.spinner();
3104
3180
  if (mode === "scaffold") {
3105
3181
  s.start("Scaffolding project");
3106
3182
  mkdirSync(targetDir, { recursive: true });
3107
3183
  } else if (mode === "add-config") s.start("Adding OpenCodeKit");
3108
3184
  else s.start("Reinitializing");
3109
- if (!await copyOpenCodeOnly(templateRoot, targetDir)) {
3185
+ if (!await copyOpenCodeOnly(templateRoot, targetDir, skipDirs)) {
3110
3186
  s.stop("Failed");
3111
3187
  p.outro(color.red("Template copy failed"));
3112
3188
  process.exit(1);
3113
3189
  }
3114
3190
  s.stop("Done");
3191
+ if (skipDirs.length > 0) p.log.info(`Project-only init: skipped ${skipDirs.map((d) => color.dim(d)).join(", ")} (using global config)`);
3115
3192
  const restoredFileCount = finalizeInstalledFiles(targetDir, getPackageVersion$1(), preservedFiles);
3116
3193
  if (restoredFileCount > 0) p.log.info(`Preserved ${restoredFileCount} user memory files (memory/project/)`);
3117
3194
  if (options.free) {
@@ -3280,11 +3357,8 @@ async function initCommand(rawOptions = {}) {
3280
3357
  //#region src/commands/skill.ts
3281
3358
  async function skillCommand(action) {
3282
3359
  const validatedAction = parseAction(SkillActionSchema, action);
3283
- const opencodePath = join(process.cwd(), ".opencode");
3284
- if (!existsSync(opencodePath)) {
3285
- notInitialized();
3286
- return;
3287
- }
3360
+ const opencodePath = requireOpencodePath();
3361
+ if (!opencodePath) return;
3288
3362
  const skillDir = join(opencodePath, "skill");
3289
3363
  switch (validatedAction) {
3290
3364
  case "list":
@@ -3684,12 +3758,9 @@ function findUpgradeOrphans(installedFiles, templateFiles) {
3684
3758
  async function upgradeCommand(rawOptions = {}) {
3685
3759
  const options = parseOptions(UpgradeOptionsSchema, rawOptions);
3686
3760
  if (process.argv.includes("--quiet")) return;
3687
- const opencodeDir = join(process.cwd(), ".opencode");
3761
+ const opencodeDir = requireOpencodePath();
3762
+ if (!opencodeDir) return;
3688
3763
  const manifest = loadManifest(opencodeDir);
3689
- if (!existsSync(opencodeDir)) {
3690
- notInitialized();
3691
- return;
3692
- }
3693
3764
  p.intro(color.bgCyan(color.black(" Upgrade ")));
3694
3765
  const versionInfo = await checkVersion(opencodeDir);
3695
3766
  console.log();
@@ -4120,11 +4191,8 @@ function displayChecks(checks) {
4120
4191
  async function statusCommand() {
4121
4192
  if (process.argv.includes("--quiet")) return;
4122
4193
  const cwd = process.cwd();
4123
- const opencodeDir = join(cwd, ".opencode");
4124
- if (!existsSync(opencodeDir)) {
4125
- notInitialized();
4126
- return;
4127
- }
4194
+ const opencodeDir = requireOpencodePath();
4195
+ if (!opencodeDir) return;
4128
4196
  const projectName = basename(cwd);
4129
4197
  p.intro(color.bgCyan(color.black(` ${projectName} `)));
4130
4198
  const agentDir = join(opencodeDir, "agent");
@@ -4163,14 +4231,6 @@ async function statusCommand() {
4163
4231
 
4164
4232
  //#endregion
4165
4233
  //#region src/commands/patch.ts
4166
- function getOpencodeDir() {
4167
- const opencodeDir = join(resolve("."), ".opencode");
4168
- if (!existsSync(opencodeDir)) {
4169
- notInitialized();
4170
- return null;
4171
- }
4172
- return opencodeDir;
4173
- }
4174
4234
  function listPatches(opencodeDir) {
4175
4235
  const metadata = loadPatchMetadata(opencodeDir);
4176
4236
  const entries = Object.entries(metadata.patches);
@@ -4399,7 +4459,7 @@ function togglePatch(opencodeDir, disable) {
4399
4459
  }
4400
4460
  }
4401
4461
  async function patchCommand(action) {
4402
- const opencodeDir = getOpencodeDir();
4462
+ const opencodeDir = requireOpencodePath();
4403
4463
  if (!opencodeDir) return;
4404
4464
  const validatedAction = parseAction(PatchActionSchema, action);
4405
4465
  if (!validatedAction) {
@@ -5416,7 +5476,7 @@ function stripAnsi(str) {
5416
5476
  //#region src/tui/hooks/useData.ts
5417
5477
  function loadProjectData() {
5418
5478
  const cwd = process.cwd();
5419
- const opencodeDir = join(cwd, ".opencode");
5479
+ const opencodeDir = resolveOpencodePath() ?? join(cwd, ".opencode");
5420
5480
  const projectName = basename(cwd);
5421
5481
  const agentDir = join(opencodeDir, "agent");
5422
5482
  let agents = [];
@@ -5445,9 +5505,6 @@ function loadProjectData() {
5445
5505
  mcpServers
5446
5506
  };
5447
5507
  }
5448
- function isInitialized() {
5449
- return existsSync(join(process.cwd(), ".opencode"));
5450
- }
5451
5508
 
5452
5509
  //#endregion
5453
5510
  //#region src/tui/index.ts
@@ -5455,10 +5512,7 @@ function isInitialized() {
5455
5512
  * Launch the TUI dashboard with interactive navigation.
5456
5513
  */
5457
5514
  async function launchTUI() {
5458
- if (!isInitialized()) {
5459
- notInitialized();
5460
- return;
5461
- }
5515
+ if (!requireOpencodePath()) return;
5462
5516
  await mainMenu();
5463
5517
  }
5464
5518
  async function mainMenu() {
@@ -5561,7 +5615,7 @@ const cli = cac("ock");
5561
5615
  cli.option("--verbose", "Enable verbose logging");
5562
5616
  cli.option("--quiet", "Suppress all output");
5563
5617
  cli.version(`${packageVersion}`);
5564
- cli.command("init", "Initialize OpenCodeKit in current directory").option("--force", "Reinitialize even if already exists").option("--beads", "Also initialize .beads/ for multi-agent coordination").option("--global", "Install to global OpenCode config (~/.config/opencode/)").option("--free", "Use free models (default)").option("--recommend", "Use recommended premium models").option("-y, --yes", "Skip prompts, use defaults (for CI)").option("--backup", "Backup existing .opencode before overwriting").option("--prune", "Manually select orphan files to delete").option("--prune-all", "Auto-delete all orphan files").action(initCommand);
5618
+ cli.command("init", "Initialize OpenCodeKit in current directory").option("--force", "Reinitialize even if already exists").option("--beads", "Also initialize .beads/ for multi-agent coordination").option("--global", "Install to global OpenCode config (~/.config/opencode/)").option("--free", "Use free models (default)").option("--recommend", "Use recommended premium models").option("-y, --yes", "Skip prompts, use defaults (for CI)").option("--backup", "Backup existing .opencode before overwriting").option("--prune", "Manually select orphan files to delete").option("--prune-all", "Auto-delete all orphan files").option("--project-only", "Only init project-scope files (skip if global config has agents/skills/commands/tools)").action(initCommand);
5565
5619
  cli.command("agent [action]", "Manage agents (list, add, view)").action(async (action) => {
5566
5620
  if (!action) {
5567
5621
  console.log("\nUsage: ock agent <action>\n");
@@ -201,19 +201,21 @@ For major tracked work:
201
201
  | Phase | Target | Action |
202
202
  | ----------------- | ------- | ------------------------------------------ |
203
203
  | Starting work | <50k | Load only essential AGENTS.md + task spec |
204
- | Mid-task | 50-100k | Distill completed reads, keep active files |
205
- | Approaching limit | >100k | Aggressive distill, prune remaining noise |
204
+ | Mid-task | 50-100k | Compress completed phases, keep active files |
205
+ | Approaching limit | >100k | Aggressive compression, sweep stale noise |
206
206
  | Near capacity | >150k | Session restart with handoff |
207
207
 
208
- ### Tools
208
+ ### DCP Commands
209
209
 
210
- - `distill`Extract key info from tool outputs, then remove raw output (preferred)
211
- - `prune`Remove tool outputs entirely (noise only, no preservation)
210
+ - `/dcp context` — Show current context health and pressure
211
+ - `/dcp compress` — Compress completed conversation ranges (primary tool)
212
+ - `/dcp sweep` — Remove stale/noisy content according to DCP rules
213
+ - `/dcp stats` — Inspect pruning/compression activity
212
214
 
213
215
  ### Rules
214
216
 
215
- 1. **Distill at turn START** — not end (you know what's needed)
216
- 2. **Batch operations** — accumulate candidates before acting
217
+ 1. **Compress at phase boundaries** — not during active edits
218
+ 2. **Batch cleanup** — use `/dcp sweep` for stale noise, not ad-hoc deletion
217
219
  3. **Protected content** — AGENTS.md, .opencode/, .beads/, config files
218
220
 
219
221
  ---
@@ -11,6 +11,9 @@ tools:
11
11
  memory-update: false
12
12
  observation: false
13
13
  question: false
14
+ websearch: false
15
+ webfetch: false
16
+ codesearch: false
14
17
  ---
15
18
 
16
19
  You are OpenCode, the best coding agent on the planet.
@@ -19,8 +22,6 @@ You are OpenCode, the best coding agent on the planet.
19
22
 
20
23
  **Purpose**: Read-only codebase cartographer — you map terrain, you don't build on it.
21
24
 
22
- > _"Agency is knowing where the levers are before you pull them."_
23
-
24
25
  ## Identity
25
26
 
26
27
  You are a read-only codebase explorer. You output concise, evidence-backed findings with absolute paths only.
@@ -29,45 +30,41 @@ You are a read-only codebase explorer. You output concise, evidence-backed findi
29
30
 
30
31
  Find relevant files, symbols, and usage paths quickly for the caller.
31
32
 
32
- ## Skills
33
+ ## Tools — Use These for Local Code Search
33
34
 
34
- Always load:
35
+ | Tool | Use For | Example |
36
+ |------|---------|--------|
37
+ | `grep` | Find text/regex patterns in files | `grep(pattern: "PatchEntry", include: "*.ts")` |
38
+ | `glob` | Find files by name/pattern | `glob(pattern: "src/**/*.ts")` |
39
+ | `lsp` (goToDefinition) | Jump to symbol definition | `lsp(operation: "goToDefinition", filePath: "...", line: N, character: N)` |
40
+ | `lsp` (findReferences) | Find all usages of a symbol | `lsp(operation: "findReferences", ...)` |
41
+ | `lsp` (hover) | Get type info and docs | `lsp(operation: "hover", ...)` |
42
+ | `read` | Read file content | `read(filePath: "src/utils/patch.ts")` |
35
43
 
36
- ```typescript
37
- skill({ name: "code-navigation" });
38
- ```
44
+ **NEVER** use `websearch`, `webfetch`, or `codesearch` — those search the internet, not your project.
39
45
 
40
46
  ## Rules
41
47
 
42
48
  - Never modify files — read-only is a hard constraint
43
49
  - Return absolute paths in final output
44
50
  - Cite `file:line` evidence whenever possible
45
- - Prefer semantic lookup (LSP) before broad text search when it improves precision
46
- - Stop when you can answer with concrete evidence or when additional search only repeats confirmed paths
51
+ - **Always start with `grep` or `glob`** to locate files and symbols — do NOT read directories to browse
52
+ - Use LSP for precise navigation after finding candidate locations
53
+ - Stop when you can answer with concrete evidence
47
54
 
48
- ## Before You Explore
55
+ ## Navigation Patterns
49
56
 
50
- - **Be certain**: Only explore what's needed for the task at hand
51
- - **Don't over-explore**: Stop when you have enough evidence to answer
52
- - **Use LSP first**: Start with goToDefinition/findReferences before grep
53
- - **Stay scoped**: Don't explore files outside the task scope
54
- - **Cite evidence**: Every finding needs file:line reference
57
+ 1. **Search first, read second**: `grep` to find where a symbol lives, then `read` only that section
58
+ 2. **Don't re-read**: If you already read a file, reference what you learned don't read it again
59
+ 3. **Follow the chain**: definition usages callers via LSP findReferences
60
+ 4. **Target ≤3 tool calls per symbol**: grep read section done
55
61
 
56
62
  ## Workflow
57
63
 
58
- 1. Discover candidate files with `glob` or `workspaceSymbol`
59
- 2. Validate symbol flow with LSP (`goToDefinition`, `findReferences`)
60
- 3. Use `grep` for targeted pattern checks
61
- 4. Read only relevant sections
62
- 5. Return findings + next steps
63
-
64
- ## Thoroughness Levels
65
-
66
- | Level | Scope | Use When |
67
- | --------------- | ----------------------------- | ------------------------------------------ |
68
- | `quick` | 1-3 files, direct answer | Simple lookups, known symbol names |
69
- | `medium` | 3-6 files, include call paths | Understanding feature flow |
70
- | `very thorough` | Dependency map + edge cases | Complex refactor prep, architecture review |
64
+ 1. `grep` or `glob` to discover candidate files
65
+ 2. `lsp` goToDefinition/findReferences for precise symbol navigation
66
+ 3. `read` only the relevant sections (use offset/limit)
67
+ 4. Return findings with file:line evidence
71
68
 
72
69
  ## Output
73
70
 
@@ -75,13 +75,15 @@ ls .beads/artifacts/$ARGUMENTS/
75
75
 
76
76
  If `plan.md` exists with dependency graph:
77
77
 
78
- 1. **Parse waves** from plan header (Wave 1, Wave 2, etc.)
79
- 2. **Execute Wave 1** (independent tasks) in parallel using `task()` subagents
80
- 3. **Wait for Wave 1 completion** — all tasks pass or report failures
81
- 4. **Execute Wave 2** (depends on Wave 1) in parallel
78
+ 1. **Load skill:** `skill({ name: "executing-plans" })`
79
+ 2. **Parse waves** from dependency graph section
80
+ 3. **Execute wave-by-wave:**
81
+ - Single-task wave execute directly (no subagent overhead)
82
+ - Multi-task wave → dispatch parallel `task({ subagent_type: "general" })` subagents, one per task
83
+ 4. **Review after each wave** — run verification gates, report, wait for feedback
82
84
  5. **Continue** until all waves complete
83
85
 
84
- **Parallel safety:** Only tasks within same wave run in parallel. Tasks in Wave N+1 wait for Wave N.
86
+ **Parallel safety:** Only tasks within same wave run in parallel. Tasks must NOT share files. Tasks in Wave N+1 wait for Wave N.
85
87
 
86
88
  ### Phase 3A: PRD Task Loop (Sequential Fallback)
87
89
 
@@ -1,83 +1,73 @@
1
1
  {
2
- "$schema": "https://raw.githubusercontent.com/Opencode-DCP/opencode-dynamic-context-pruning/master/dcp.schema.json",
3
- "enabled": true,
4
- "debug": false,
5
- // "minimal" shows prune activity without noise; "detailed" shows token counts
6
- "pruneNotification": "detailed",
7
- // "chat" (in-conversation) or "toast" (system notification)
8
- "pruneNotificationType": "toast",
9
- // Commands: /dcp context, /dcp stats, /dcp sweep
10
- "commands": {
11
- "enabled": true,
12
- // Protect these from /dcp sweep
13
- "protectedTools": ["observation", "memory-update", "memory-search"],
14
- },
15
- "turnProtection": {
16
- "enabled": true,
17
- "turns": 4,
18
- },
19
- // Protected file patterns - never auto-prune reads of these files
20
- "protectedFilePatterns": [
21
- "**/.env*",
22
- "**/AGENTS.md",
23
- "**/.opencode/**",
24
- "**/.beads/**",
25
- "**/package.json",
26
- "**/tsconfig.json",
27
- "**/biome.json",
28
- ],
29
- "tools": {
30
- "settings": {
31
- "nudgeEnabled": true,
32
- "nudgeFrequency": 10,
33
- // v2.0.1: percentage support - 80% of model's context (most models = 128k)
34
- "contextLimit": "80%",
35
- // Protect state-modifying and critical workflow tools
36
- // LSP excluded: ephemeral exploration, prune after understanding
37
- "protectedTools": [
38
- "write",
39
- "edit",
40
- "memory-update",
41
- "observation",
42
- "skill",
43
- "skill_mcp",
44
- "task",
45
- "batch",
46
- "todowrite",
47
- "todoread",
48
- ],
49
- },
50
- // v2.0.0: permission model - "allow", "ask", or "deny"
51
- "prune": {
52
- "permission": "allow",
53
- },
54
- "distill": {
55
- "permission": "allow",
56
- "showDistillation": false,
57
- },
58
- "compress": {
59
- // v2.1.1: compress can have boundary matching issues - use ask for safety
60
- "permission": "allow",
61
- "showCompression": false,
62
- },
63
- },
64
- // Auto strategies - TOP LEVEL, not under tools
65
- "strategies": {
66
- // Dedup = zero LLM cost, high impact - always enable
67
- "deduplication": {
68
- "enabled": true,
69
- "protectedTools": [],
70
- },
71
- // Supersede writes = zero cost, removes redundant write inputs after read
72
- // Note: default changed to false in beta, we explicitly enable
73
- "supersedeWrites": {
74
- "enabled": true,
75
- },
76
- // Purge error inputs after N turns
77
- "purgeErrors": {
78
- "enabled": true,
79
- "turns": 4,
80
- "protectedTools": [],
81
- },
82
- },
2
+ "$schema": "https://raw.githubusercontent.com/Opencode-DCP/opencode-dynamic-context-pruning/master/dcp.schema.json",
3
+ "enabled": true,
4
+ "debug": false,
5
+ // "minimal" shows prune activity without noise; "detailed" shows token counts
6
+ "pruneNotification": "detailed",
7
+ // "chat" (in-conversation) or "toast" (system notification)
8
+ "pruneNotificationType": "toast",
9
+ // Commands: /dcp context, /dcp stats, /dcp sweep, /dcp decompress, /dcp recompress
10
+ "commands": {
11
+ "enabled": true,
12
+ // Protect these from /dcp sweep
13
+ "protectedTools": ["observation", "memory-update", "memory-search"]
14
+ },
15
+ "turnProtection": {
16
+ "enabled": true,
17
+ "turns": 4
18
+ },
19
+ // Protected file patterns - never auto-prune reads of these files
20
+ "protectedFilePatterns": [
21
+ "**/.env*",
22
+ "**/AGENTS.md",
23
+ "**/.opencode/**",
24
+ "**/.beads/**",
25
+ "**/package.json",
26
+ "**/tsconfig.json",
27
+ "**/biome.json"
28
+ ],
29
+ "compress": {
30
+ // v2.2.x beta: compress is the primary context management tool
31
+ "permission": "allow",
32
+ "showCompression": false,
33
+ "maxContextLimit": "80%",
34
+ "minContextLimit": 30000,
35
+ "flatSchema": false,
36
+ "protectUserMessages": true,
37
+ "protectedTools": [
38
+ "write",
39
+ "edit",
40
+ "memory-*",
41
+ "observation",
42
+ "skill",
43
+ "skill_mcp",
44
+ "task",
45
+ "batch",
46
+ "todowrite",
47
+ "todoread",
48
+ "tilth_*"
49
+ ]
50
+ },
51
+ // v2.2.5-beta0: experimental subagent support
52
+ "experimental": {
53
+ "allowSubAgents": true
54
+ },
55
+ // Auto strategies - TOP LEVEL, not under tools
56
+ "strategies": {
57
+ // Dedup = zero LLM cost, high impact - always enable
58
+ "deduplication": {
59
+ "enabled": true,
60
+ "protectedTools": []
61
+ },
62
+ // Supersede writes = zero cost, removes redundant write inputs after read
63
+ "supersedeWrites": {
64
+ "enabled": true
65
+ },
66
+ // Purge error inputs after N turns
67
+ "purgeErrors": {
68
+ "enabled": true,
69
+ "turns": 4,
70
+ "protectedTools": []
71
+ }
72
+ }
83
73
  }
@@ -38,10 +38,10 @@ The edit tool (`str_replace`) is the #1 source of failures in LLM coding. Models
38
38
 
39
39
  ### Context Hygiene
40
40
 
41
- - Distill large tool outputs immediately after use
42
- - Prune irrelevant reads before proceeding
41
+ - Compress completed work phases before moving on
42
+ - Use `/dcp sweep` after a closed phase to remove stale noise
43
43
  - Token budget: <50k start → 50-100k mid → >150k restart session
44
- - Subagent outputs can leak tokens — be aggressive about distilling
44
+ - Subagent outputs can leak tokens — compress completed phases and sweep stale subagent noise
45
45
 
46
46
  ## OpenCode Config
47
47
 
Binary file
@@ -151,7 +151,7 @@
151
151
  }
152
152
  },
153
153
  "plugin": [
154
- "@tarquinen/opencode-dcp@latest",
154
+ "@tarquinen/opencode-dcp@beta",
155
155
  "@franlol/opencode-md-table-formatter@0.0.3",
156
156
  "openslimedit@latest"
157
157
  ],
@@ -11,7 +11,7 @@
11
11
  "type-check": "tsc --noEmit"
12
12
  },
13
13
  "dependencies": {
14
- "@opencode-ai/plugin": "1.2.21"
14
+ "@opencode-ai/plugin": "1.2.22"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@types/node": "^25.3.0",