opencodekit 0.14.6 → 0.15.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 (95) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +435 -57
  3. package/dist/template/.opencode/.env.example +1 -0
  4. package/dist/template/.opencode/AGENTS.md +13 -24
  5. package/dist/template/.opencode/README.md +8 -119
  6. package/dist/template/.opencode/agent/explore.md +2 -3
  7. package/dist/template/.opencode/agent/general.md +56 -0
  8. package/dist/template/.opencode/agent/plan.md +54 -0
  9. package/dist/template/.opencode/agent/scout.md +15 -5
  10. package/dist/template/.opencode/command/analyze-project.md +2 -2
  11. package/dist/template/.opencode/command/brainstorm.md +1 -1
  12. package/dist/template/.opencode/command/design-audit.md +4 -5
  13. package/dist/template/.opencode/command/design.md +4 -13
  14. package/dist/template/.opencode/command/generate-pattern.md +2 -9
  15. package/dist/template/.opencode/command/implement.md +4 -4
  16. package/dist/template/.opencode/command/init.md +1 -1
  17. package/dist/template/.opencode/command/new-feature.md +2 -3
  18. package/dist/template/.opencode/command/plan.md +1 -1
  19. package/dist/template/.opencode/command/pr.md +0 -1
  20. package/dist/template/.opencode/command/research.md +20 -6
  21. package/dist/template/.opencode/command/restore-image.md +1 -9
  22. package/dist/template/.opencode/command/revert-feature.md +1 -1
  23. package/dist/template/.opencode/command/review-codebase.md +4 -4
  24. package/dist/template/.opencode/command/status.md +1 -2
  25. package/dist/template/.opencode/command/summarize.md +1 -2
  26. package/dist/template/.opencode/command/triage.md +4 -32
  27. package/dist/template/.opencode/dcp.jsonc +68 -68
  28. package/dist/template/.opencode/memory/_templates/README.md +35 -0
  29. package/dist/template/.opencode/memory/_templates/project/architecture.md +60 -0
  30. package/dist/template/.opencode/memory/_templates/project/commands.md +72 -0
  31. package/dist/template/.opencode/memory/_templates/project/conventions.md +68 -0
  32. package/dist/template/.opencode/memory/_templates/project/gotchas.md +41 -0
  33. package/dist/template/.opencode/memory/beads-workflow.md +30 -29
  34. package/dist/template/.opencode/memory/project/architecture.md +31 -50
  35. package/dist/template/.opencode/memory/project/commands.md +41 -22
  36. package/dist/template/.opencode/memory/project/conventions.md +39 -177
  37. package/dist/template/.opencode/memory/project/gotchas.md +21 -177
  38. package/dist/template/.opencode/memory/user.example.md +5 -0
  39. package/dist/template/.opencode/opencode.json +628 -579
  40. package/dist/template/.opencode/package.json +18 -21
  41. package/dist/template/.opencode/plugin/compaction.ts +79 -85
  42. package/dist/template/.opencode/plugin/env-ctx.ts +19 -19
  43. package/dist/template/.opencode/plugin/lib/notify.ts +41 -45
  44. package/dist/template/.opencode/plugin/lsp.ts +197 -200
  45. package/dist/template/.opencode/plugin/memory.ts +14 -112
  46. package/dist/template/.opencode/plugin/package.json +5 -5
  47. package/dist/template/.opencode/plugin/sessions.ts +1 -1
  48. package/dist/template/.opencode/plugin/skill-mcp.ts +486 -521
  49. package/dist/template/.opencode/plugin/truncator.ts +47 -50
  50. package/dist/template/.opencode/plugin/tsconfig.json +14 -14
  51. package/dist/template/.opencode/skill/chrome-devtools/mcp.json +17 -17
  52. package/dist/template/.opencode/skill/condition-based-waiting/SKILL.md +17 -12
  53. package/dist/template/.opencode/skill/condition-based-waiting/example.ts +63 -69
  54. package/dist/template/.opencode/skill/defense-in-depth/SKILL.md +14 -8
  55. package/dist/template/.opencode/skill/dispatching-parallel-agents/SKILL.md +14 -3
  56. package/dist/template/.opencode/skill/playwright/mcp.json +14 -14
  57. package/dist/template/.opencode/skill/receiving-code-review/SKILL.md +21 -8
  58. package/dist/template/.opencode/skill/requesting-code-review/review.md +14 -0
  59. package/dist/template/.opencode/skill/root-cause-tracing/SKILL.md +18 -4
  60. package/dist/template/.opencode/skill/source-code-research/SKILL.md +9 -7
  61. package/dist/template/.opencode/skill/test-driven-development/SKILL.md +49 -32
  62. package/dist/template/.opencode/skill/testing-anti-patterns/SKILL.md +40 -22
  63. package/dist/template/.opencode/skill/testing-skills-with-subagents/SKILL.md +46 -26
  64. package/dist/template/.opencode/skill/tool-priority/SKILL.md +117 -44
  65. package/dist/template/.opencode/skill/v0/SKILL.md +1 -7
  66. package/dist/template/.opencode/skill/verification-before-completion/SKILL.md +27 -19
  67. package/dist/template/.opencode/skill/writing-skills/anthropic-best-practices.md +171 -148
  68. package/dist/template/.opencode/skill/writing-skills/persuasion-principles.md +39 -6
  69. package/dist/template/.opencode/tool/memory-read.ts +44 -56
  70. package/dist/template/.opencode/tool/memory-search.ts +8 -291
  71. package/dist/template/.opencode/tool/memory-update.ts +47 -51
  72. package/dist/template/.opencode/tool/observation.ts +6 -180
  73. package/dist/template/.opencode/tsconfig.json +19 -19
  74. package/package.json +19 -15
  75. package/dist/template/.opencode/.background-tasks.json +0 -114
  76. package/dist/template/.opencode/.ralph-state.json +0 -12
  77. package/dist/template/.opencode/agent/build.md +0 -327
  78. package/dist/template/.opencode/agent/ninja.md +0 -351
  79. package/dist/template/.opencode/agent/planner.md +0 -281
  80. package/dist/template/.opencode/agent/rush.md +0 -223
  81. package/dist/template/.opencode/memory/handoffs/README.md +0 -83
  82. package/dist/template/.opencode/memory/observations/.gitkeep +0 -0
  83. package/dist/template/.opencode/memory/observations/2026-01-09-pattern-ampcode-mcp-json-includetools-pattern.md +0 -42
  84. package/dist/template/.opencode/memory/vector_db/memories.lance/_transactions/0-0d25ba80-ba3b-4209-9046-b45d6093b4da.txn +0 -0
  85. package/dist/template/.opencode/memory/vector_db/memories.lance/_versions/1.manifest +0 -0
  86. package/dist/template/.opencode/memory/vector_db/memories.lance/data/1111100101010101011010004a9ef34df6b29f36a9a53a2892.lance +0 -0
  87. package/dist/template/.opencode/tool/ast-grep.ts +0 -245
  88. package/dist/template/.opencode/tool/background.ts +0 -509
  89. package/dist/template/.opencode/tool/bd-inbox.ts +0 -110
  90. package/dist/template/.opencode/tool/bd-msg.ts +0 -62
  91. package/dist/template/.opencode/tool/bd-release.ts +0 -71
  92. package/dist/template/.opencode/tool/bd-reserve.ts +0 -121
  93. package/dist/template/.opencode/tool/memory-embed.ts +0 -183
  94. package/dist/template/.opencode/tool/memory-index.ts +0 -769
  95. package/dist/template/.opencode/tool/repo-map.ts +0 -451
package/README.md CHANGED
@@ -65,7 +65,7 @@ create test.txt with "Hello OpenCodeKit"
65
65
  ```
66
66
  build (Primary Orchestrator - 70% of work)
67
67
  ├→ @rush (Fast agent for simple tasks)
68
- ├→ @planner (Complex planning ≥3 phases)
68
+ ├→ @plan (Complex planning ≥3 phases)
69
69
  ├→ @review (Code review + security + debugging)
70
70
  ├→ @scout (External research, library docs, GitHub patterns)
71
71
  ├→ @explore (Fast codebase search)
@@ -175,7 +175,7 @@ Need speed over depth?
175
175
  ✓ → @rush (fast agent, same capabilities)
176
176
 
177
177
  Complex task (≥3 phases)?
178
- ✓ → @planner (creates implementation plan)
178
+ ✓ → @plan (creates implementation plan)
179
179
 
180
180
  Need research?
181
181
  ✓ → @scout (library docs + GitHub patterns)
package/dist/index.js CHANGED
@@ -750,21 +750,24 @@ var cac = (name = "") => new CAC(name);
750
750
  // package.json
751
751
  var package_default = {
752
752
  name: "opencodekit",
753
- version: "0.14.6",
753
+ version: "0.15.1",
754
754
  description: "CLI tool for bootstrapping and managing OpenCodeKit projects",
755
- type: "module",
755
+ keywords: ["agents", "cli", "mcp", "opencode", "opencodekit", "template"],
756
+ license: "MIT",
757
+ author: "OpenCodeKit",
756
758
  repository: {
757
759
  type: "git",
758
760
  url: "git+https://github.com/opencodekit/opencodekit-template.git"
759
761
  },
760
- publishConfig: {
761
- access: "public",
762
- registry: "https://registry.npmjs.org"
763
- },
764
762
  bin: {
765
763
  ock: "dist/index.js"
766
764
  },
767
765
  files: ["dist", "README.md"],
766
+ type: "module",
767
+ publishConfig: {
768
+ access: "public",
769
+ registry: "https://registry.npmjs.org"
770
+ },
768
771
  scripts: {
769
772
  dev: "bun run src/index.ts",
770
773
  build: "bun run build.ts && mkdir -p dist/template && rsync -av --exclude=node_modules --exclude=dist --exclude=.git --exclude=coverage --exclude=.next --exclude=.turbo --exclude=logs --exclude=package-lock.json .opencode/ dist/template/.opencode/",
@@ -773,14 +776,10 @@ var package_default = {
773
776
  typecheck: "tsc --noEmit",
774
777
  test: "bun test",
775
778
  "test:watch": "bun test --watch",
776
- lint: "biome check .",
777
- "lint:fix": "biome check --fix ."
778
- },
779
- keywords: ["cli", "opencodekit", "template", "agents", "mcp", "opencode"],
780
- author: "OpenCodeKit",
781
- license: "MIT",
782
- engines: {
783
- bun: ">=1.3.2"
779
+ lint: "oxlint .",
780
+ "lint:fix": "oxlint --fix .",
781
+ format: "oxfmt",
782
+ "format:check": "oxfmt --check"
784
783
  },
785
784
  dependencies: {
786
785
  "@clack/prompts": "^0.7.0",
@@ -792,7 +791,7 @@ var package_default = {
792
791
  "cli-table3": "^0.6.5",
793
792
  ora: "^9.0.0",
794
793
  picocolors: "^1.1.1",
795
- "solid-js": "^1.9.10",
794
+ "solid-js": "1.9.9",
796
795
  zod: "^3.25.76"
797
796
  },
798
797
  devDependencies: {
@@ -800,8 +799,13 @@ var package_default = {
800
799
  "@biomejs/biome": "^1.9.4",
801
800
  "@types/bun": "latest",
802
801
  "@types/node": "^22.19.5",
802
+ oxfmt: "^0.23.0",
803
+ oxlint: "^1.38.0",
803
804
  typescript: "^5.9.3"
804
805
  },
806
+ engines: {
807
+ bun: ">=1.3.2"
808
+ },
805
809
  trustedDependencies: ["@beads/bd"]
806
810
  };
807
811
 
@@ -2762,7 +2766,7 @@ async function editModel(configPath, modelType = "model") {
2762
2766
  }
2763
2767
  f2.success("Fetched models from models.dev");
2764
2768
  const localProviders = config.provider || {};
2765
- const configuredProviderIds = new Set(Object.keys(localProviders));
2769
+ const _configuredProviderIds = new Set(Object.keys(localProviders));
2766
2770
  const allProviders = Object.values(modelsDevData);
2767
2771
  const quickOptions = [];
2768
2772
  quickOptions.push({
@@ -2929,7 +2933,7 @@ async function editAgentModel(configPath) {
2929
2933
  config.agent = {};
2930
2934
  }
2931
2935
  const serverAgents = await getAgentsFromServer();
2932
- const serverData = await getProvidersFromServer();
2936
+ const _serverData = await getProvidersFromServer();
2933
2937
  let agentNames = [];
2934
2938
  const agentInfoMap = new Map;
2935
2939
  if (serverAgents && serverAgents.length > 0) {
@@ -2943,7 +2947,7 @@ async function editAgentModel(configPath) {
2943
2947
  });
2944
2948
  }
2945
2949
  } else {
2946
- agentNames = Object.keys(config.agent).filter((name) => name !== "general" && name !== "plan");
2950
+ agentNames = Object.keys(config.agent);
2947
2951
  for (const name of agentNames) {
2948
2952
  const agent = config.agent[name];
2949
2953
  agentInfoMap.set(name, {
@@ -3763,6 +3767,8 @@ import {
3763
3767
  mkdirSync as mkdirSync3,
3764
3768
  readFileSync as readFileSync4,
3765
3769
  readdirSync as readdirSync3,
3770
+ renameSync,
3771
+ rmSync,
3766
3772
  writeFileSync as writeFileSync4
3767
3773
  } from "node:fs";
3768
3774
  import { homedir, platform } from "node:os";
@@ -3849,6 +3855,139 @@ async function copyOpenCodeOnly(templateRoot, targetDir) {
3849
3855
  await copyDir(opencodeSrc, opencodeDest);
3850
3856
  return true;
3851
3857
  }
3858
+ var MODEL_PRESETS = {
3859
+ free: {
3860
+ model: "opencode/minimax-m2.1-free",
3861
+ agents: {
3862
+ build: "opencode/minimax-m2.1-free",
3863
+ plan: "opencode/minimax-m2.1-free",
3864
+ review: "opencode/minimax-m2.1-free",
3865
+ explore: "opencode/grok-code",
3866
+ general: "opencode/glm-4.7-free",
3867
+ looker: "opencode/gpt-5-nano",
3868
+ vision: "opencode/gpt-5-nano",
3869
+ scout: "opencode/big-pickle"
3870
+ }
3871
+ },
3872
+ recommend: {
3873
+ model: "proxypal/gemini-claude-opus-4-5-thinking",
3874
+ agents: {
3875
+ build: "proxypal/gemini-claude-opus-4-5-thinking",
3876
+ plan: "proxypal/gemini-3-flash-preview",
3877
+ review: "proxypal/gemini-3-pro-preview",
3878
+ explore: "opencode/grok-code",
3879
+ general: "opencode/glm-4.7-free",
3880
+ looker: "proxypal/gemini-3-flash-preview",
3881
+ vision: "proxypal/gemini-3-pro-preview",
3882
+ scout: "proxypal/gemini-claude-sonnet-4-5"
3883
+ }
3884
+ }
3885
+ };
3886
+ function applyModelPreset(targetDir, preset) {
3887
+ const configPath = join4(targetDir, ".opencode", "opencode.json");
3888
+ if (!existsSync4(configPath))
3889
+ return;
3890
+ const config = JSON.parse(readFileSync4(configPath, "utf-8"));
3891
+ const presetConfig = MODEL_PRESETS[preset];
3892
+ config.model = presetConfig.model;
3893
+ if (config.agent) {
3894
+ for (const [agentName, model] of Object.entries(presetConfig.agents)) {
3895
+ if (config.agent[agentName]) {
3896
+ config.agent[agentName].model = model;
3897
+ }
3898
+ }
3899
+ }
3900
+ writeFileSync4(configPath, JSON.stringify(config, null, 2));
3901
+ }
3902
+ var AGENT_DESCRIPTIONS = {
3903
+ build: "Main coding agent (complex tasks)",
3904
+ plan: "Planning and design agent",
3905
+ review: "Code review and debugging",
3906
+ explore: "Fast codebase search",
3907
+ general: "Quick, simple tasks",
3908
+ looker: "Image/PDF extraction (cheap)",
3909
+ vision: "Visual analysis (quality)",
3910
+ scout: "External research/docs",
3911
+ compaction: "Context summarization"
3912
+ };
3913
+ async function promptCustomModels(targetDir) {
3914
+ const configPath = join4(targetDir, ".opencode", "opencode.json");
3915
+ if (!existsSync4(configPath))
3916
+ return;
3917
+ const config = JSON.parse(readFileSync4(configPath, "utf-8"));
3918
+ f2.info(import_picocolors8.default.dim("Enter model IDs (e.g., opencode/grok-code, proxypal/gemini-3-pro-preview)"));
3919
+ f2.info(import_picocolors8.default.dim(`Press Enter to keep current value
3920
+ `));
3921
+ const mainModel = await te({
3922
+ message: "Main session model",
3923
+ placeholder: config.model || "opencode/minimax-m2.1-free",
3924
+ defaultValue: config.model
3925
+ });
3926
+ if (lD(mainModel)) {
3927
+ f2.warn("Cancelled - keeping defaults");
3928
+ return;
3929
+ }
3930
+ if (mainModel) {
3931
+ config.model = mainModel;
3932
+ }
3933
+ const agents = Object.keys(AGENT_DESCRIPTIONS);
3934
+ for (const agent of agents) {
3935
+ if (!config.agent?.[agent])
3936
+ continue;
3937
+ const currentModel = config.agent[agent].model || config.model;
3938
+ const agentModel = await te({
3939
+ message: `${agent} - ${AGENT_DESCRIPTIONS[agent]}`,
3940
+ placeholder: currentModel,
3941
+ defaultValue: currentModel
3942
+ });
3943
+ if (lD(agentModel)) {
3944
+ f2.warn("Cancelled - saving partial config");
3945
+ break;
3946
+ }
3947
+ if (agentModel) {
3948
+ config.agent[agent].model = agentModel;
3949
+ }
3950
+ }
3951
+ writeFileSync4(configPath, JSON.stringify(config, null, 2));
3952
+ }
3953
+ function getAffectedFiles(dir, prefix = "") {
3954
+ if (!existsSync4(dir))
3955
+ return [];
3956
+ const files = [];
3957
+ for (const entry of readdirSync3(dir, { withFileTypes: true })) {
3958
+ if (EXCLUDED_DIRS.includes(entry.name))
3959
+ continue;
3960
+ const path = prefix ? `${prefix}/${entry.name}` : entry.name;
3961
+ if (entry.isDirectory()) {
3962
+ files.push(...getAffectedFiles(join4(dir, entry.name), path));
3963
+ } else {
3964
+ files.push(path);
3965
+ }
3966
+ }
3967
+ return files;
3968
+ }
3969
+ function backupOpenCode(targetDir) {
3970
+ const opencodeDir = join4(targetDir, ".opencode");
3971
+ if (!existsSync4(opencodeDir))
3972
+ return null;
3973
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
3974
+ const backupDir = join4(targetDir, `.opencode.bak-${timestamp}`);
3975
+ renameSync(opencodeDir, backupDir);
3976
+ return backupDir;
3977
+ }
3978
+ function getTemplateFiles(templateRoot) {
3979
+ const opencodeSrc = join4(templateRoot, ".opencode");
3980
+ if (!existsSync4(opencodeSrc))
3981
+ return new Set;
3982
+ return new Set(getAffectedFiles(opencodeSrc));
3983
+ }
3984
+ function findOrphans(targetDir, templateFiles) {
3985
+ const opencodeDir = join4(targetDir, ".opencode");
3986
+ if (!existsSync4(opencodeDir))
3987
+ return [];
3988
+ const existingFiles = getAffectedFiles(opencodeDir);
3989
+ return existingFiles.filter((f3) => !templateFiles.has(f3));
3990
+ }
3852
3991
  async function initCommand(options = {}) {
3853
3992
  if (process.argv.includes("--quiet"))
3854
3993
  return;
@@ -3898,6 +4037,47 @@ for all OpenCode projects on this machine.`, "Global Installation Complete");
3898
4037
  $e("Nothing to do");
3899
4038
  return;
3900
4039
  }
4040
+ if (mode === "already-initialized" && options.force) {
4041
+ const opencodeDir2 = join4(targetDir, ".opencode");
4042
+ const affected = getAffectedFiles(opencodeDir2);
4043
+ if (affected.length > 0 && !options.yes) {
4044
+ f2.warn(`${affected.length} files will be overwritten:`);
4045
+ const preview = affected.slice(0, 10);
4046
+ for (const file of preview) {
4047
+ f2.info(import_picocolors8.default.dim(` .opencode/${file}`));
4048
+ }
4049
+ if (affected.length > 10) {
4050
+ f2.info(import_picocolors8.default.dim(` ... and ${affected.length - 10} more`));
4051
+ }
4052
+ if (!options.backup) {
4053
+ const shouldBackup = await se({
4054
+ message: "Backup existing .opencode before overwriting?",
4055
+ initialValue: true
4056
+ });
4057
+ if (lD(shouldBackup)) {
4058
+ ue("Cancelled");
4059
+ process.exit(0);
4060
+ }
4061
+ if (shouldBackup) {
4062
+ options.backup = true;
4063
+ }
4064
+ }
4065
+ const proceed = await se({
4066
+ message: options.backup ? "Proceed? (existing config will be backed up)" : "Proceed without backup?",
4067
+ initialValue: options.backup
4068
+ });
4069
+ if (lD(proceed) || !proceed) {
4070
+ ue("Cancelled");
4071
+ process.exit(0);
4072
+ }
4073
+ }
4074
+ if (options.backup) {
4075
+ const backupPath = backupOpenCode(targetDir);
4076
+ if (backupPath) {
4077
+ f2.info(`Backed up to ${import_picocolors8.default.cyan(basename2(backupPath))}`);
4078
+ }
4079
+ }
4080
+ }
3901
4081
  const templateRoot = getTemplateRoot();
3902
4082
  if (!templateRoot) {
3903
4083
  f2.error("Template not found. Please reinstall opencodekit.");
@@ -3933,6 +4113,51 @@ for all OpenCode projects on this machine.`, "Global Installation Complete");
3933
4113
  process.exit(1);
3934
4114
  }
3935
4115
  s.stop("Done");
4116
+ if (options.free) {
4117
+ applyModelPreset(targetDir, "free");
4118
+ f2.info("Applied free model preset");
4119
+ } else if (options.recommend) {
4120
+ applyModelPreset(targetDir, "recommend");
4121
+ f2.info("Applied recommended model preset");
4122
+ } else if (options.yes) {
4123
+ applyModelPreset(targetDir, "free");
4124
+ f2.info("Applied free model preset (default)");
4125
+ } else {
4126
+ const preset = await ie({
4127
+ message: "Choose model preset",
4128
+ options: [
4129
+ {
4130
+ value: "free",
4131
+ label: "Free models",
4132
+ hint: "minimax, glm, grok (no API costs)"
4133
+ },
4134
+ {
4135
+ value: "recommend",
4136
+ label: "Recommended models",
4137
+ hint: "claude-opus, gemini-pro (best quality)"
4138
+ },
4139
+ {
4140
+ value: "custom",
4141
+ label: "Custom",
4142
+ hint: "configure each agent individually"
4143
+ },
4144
+ {
4145
+ value: "skip",
4146
+ label: "Skip",
4147
+ hint: "keep template defaults"
4148
+ }
4149
+ ]
4150
+ });
4151
+ if (!lD(preset)) {
4152
+ if (preset === "custom") {
4153
+ await promptCustomModels(targetDir);
4154
+ f2.info("Applied custom model configuration");
4155
+ } else if (preset !== "skip") {
4156
+ applyModelPreset(targetDir, preset);
4157
+ f2.info(`Applied ${preset} model preset`);
4158
+ }
4159
+ }
4160
+ }
3936
4161
  if (options.beads) {
3937
4162
  const beadsDir = join4(targetDir, ".beads");
3938
4163
  if (!existsSync4(beadsDir)) {
@@ -3954,7 +4179,94 @@ version: 1
3954
4179
  f2.info(".beads/ already exists");
3955
4180
  }
3956
4181
  }
3957
- le("cd .opencode && bun install", "Next steps");
4182
+ const opencodeDir = join4(targetDir, ".opencode");
4183
+ if (existsSync4(join4(opencodeDir, "package.json"))) {
4184
+ const installSpinner = de();
4185
+ installSpinner.start("Installing dependencies");
4186
+ try {
4187
+ execSync("bun install", { cwd: opencodeDir, stdio: "ignore" });
4188
+ installSpinner.stop("Dependencies installed");
4189
+ } catch {
4190
+ installSpinner.stop("Failed to install (run manually: cd .opencode && bun install)");
4191
+ }
4192
+ }
4193
+ if (mode === "already-initialized" && options.force && !options.backup) {
4194
+ const templateFiles = getTemplateFiles(templateRoot);
4195
+ const orphans = findOrphans(targetDir, templateFiles);
4196
+ if (orphans.length > 0) {
4197
+ f2.warn(`Found ${orphans.length} orphan files not in template`);
4198
+ if (options.pruneAll) {
4199
+ const pruneSpinner = de();
4200
+ pruneSpinner.start("Removing orphan files");
4201
+ for (const orphan of orphans) {
4202
+ rmSync(join4(opencodeDir, orphan));
4203
+ }
4204
+ pruneSpinner.stop(`Removed ${orphans.length} orphan files`);
4205
+ } else if (options.prune) {
4206
+ const selected = await ae({
4207
+ message: "Select orphan files to delete",
4208
+ options: orphans.map((o2) => ({ value: o2, label: o2 })),
4209
+ required: false
4210
+ });
4211
+ if (!lD(selected) && selected.length > 0) {
4212
+ const pruneSpinner = de();
4213
+ pruneSpinner.start("Deleting files");
4214
+ for (const file of selected) {
4215
+ rmSync(join4(opencodeDir, file));
4216
+ }
4217
+ pruneSpinner.stop(`Deleted ${selected.length} files`);
4218
+ }
4219
+ } else if (!options.yes) {
4220
+ const preview = orphans.slice(0, 5);
4221
+ for (const file of preview) {
4222
+ f2.info(import_picocolors8.default.dim(` .opencode/${file}`));
4223
+ }
4224
+ if (orphans.length > 5) {
4225
+ f2.info(import_picocolors8.default.dim(` ... and ${orphans.length - 5} more`));
4226
+ }
4227
+ const orphanAction = await ie({
4228
+ message: "How to handle orphan files?",
4229
+ options: [
4230
+ { value: "keep", label: "Keep all", hint: "leave orphan files" },
4231
+ {
4232
+ value: "select",
4233
+ label: "Select",
4234
+ hint: "choose which to delete"
4235
+ },
4236
+ {
4237
+ value: "delete",
4238
+ label: "Delete all",
4239
+ hint: "remove all orphans"
4240
+ }
4241
+ ]
4242
+ });
4243
+ if (!lD(orphanAction)) {
4244
+ if (orphanAction === "delete") {
4245
+ const pruneSpinner = de();
4246
+ pruneSpinner.start("Removing orphan files");
4247
+ for (const orphan of orphans) {
4248
+ rmSync(join4(opencodeDir, orphan));
4249
+ }
4250
+ pruneSpinner.stop(`Removed ${orphans.length} orphan files`);
4251
+ } else if (orphanAction === "select") {
4252
+ const selected = await ae({
4253
+ message: "Select orphan files to delete",
4254
+ options: orphans.map((o2) => ({ value: o2, label: o2 })),
4255
+ required: false
4256
+ });
4257
+ if (!lD(selected) && selected.length > 0) {
4258
+ const pruneSpinner = de();
4259
+ pruneSpinner.start("Deleting files");
4260
+ for (const file of selected) {
4261
+ rmSync(join4(opencodeDir, file));
4262
+ }
4263
+ pruneSpinner.stop(`Deleted ${selected.length} files`);
4264
+ }
4265
+ }
4266
+ }
4267
+ }
4268
+ }
4269
+ }
3958
4270
  $e(import_picocolors8.default.green("Ready to code!"));
3959
4271
  }
3960
4272
 
@@ -3964,14 +4276,7 @@ import { basename as basename4, join as join7 } from "node:path";
3964
4276
  var import_picocolors11 = __toESM(require_picocolors(), 1);
3965
4277
 
3966
4278
  // src/commands/skill.ts
3967
- import {
3968
- existsSync as existsSync5,
3969
- mkdirSync as mkdirSync4,
3970
- readFileSync as readFileSync5,
3971
- readdirSync as readdirSync4,
3972
- rmSync,
3973
- writeFileSync as writeFileSync5
3974
- } from "node:fs";
4279
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5, readdirSync as readdirSync4, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
3975
4280
  import { basename as basename3, join as join5 } from "node:path";
3976
4281
  var import_picocolors9 = __toESM(require_picocolors(), 1);
3977
4282
  async function skillCommand(action) {
@@ -4253,7 +4558,7 @@ async function removeSkill(skillDir, skillNameArg) {
4253
4558
  ue("Cancelled");
4254
4559
  return;
4255
4560
  }
4256
- rmSync(skill.path, { recursive: true, force: true });
4561
+ rmSync2(skill.path, { recursive: true, force: true });
4257
4562
  f2.success(`Removed skill "${skill.name}"`);
4258
4563
  }
4259
4564
 
@@ -4264,7 +4569,7 @@ import {
4264
4569
  mkdirSync as mkdirSync5,
4265
4570
  readFileSync as readFileSync6,
4266
4571
  readdirSync as readdirSync5,
4267
- rmSync as rmSync2,
4572
+ rmSync as rmSync3,
4268
4573
  writeFileSync as writeFileSync6
4269
4574
  } from "node:fs";
4270
4575
  import { dirname as dirname2, join as join6 } from "node:path";
@@ -4478,36 +4783,42 @@ async function upgradeCommand(options = {}) {
4478
4783
  });
4479
4784
  if (orphans.length > 0) {
4480
4785
  f2.info(`${import_picocolors10.default.yellow(orphans.length.toString())} files found that are not in the template (orphans).`);
4481
- if (options.prune) {
4786
+ if (options.pruneAll) {
4482
4787
  const s2 = de();
4483
4788
  s2.start("Pruning orphans");
4484
4789
  for (const orphan of orphans) {
4485
- rmSync2(join6(opencodeDir, orphan));
4790
+ rmSync3(join6(opencodeDir, orphan));
4486
4791
  }
4487
4792
  s2.stop(`Removed ${orphans.length} files`);
4488
- } else {
4489
- const shouldPrune = await se({
4490
- message: "Do you want to review and delete them?",
4491
- initialValue: false
4793
+ } else if (options.prune) {
4794
+ const selected = await ae({
4795
+ message: "Select files to delete",
4796
+ options: orphans.map((o2) => ({ value: o2, label: o2 })),
4797
+ required: false
4492
4798
  });
4493
- if (!lD(shouldPrune) && shouldPrune) {
4494
- const selected = await ae({
4495
- message: "Select files to delete",
4496
- options: orphans.map((o2) => ({ value: o2, label: o2 })),
4497
- required: false
4498
- });
4499
- if (!lD(selected) && selected.length > 0) {
4500
- const s2 = de();
4501
- s2.start("Deleting files");
4502
- for (const file of selected) {
4503
- rmSync2(join6(opencodeDir, file));
4504
- }
4505
- s2.stop(`Deleted ${selected.length} files`);
4799
+ if (!lD(selected) && selected.length > 0) {
4800
+ const s2 = de();
4801
+ s2.start("Deleting files");
4802
+ for (const file of selected) {
4803
+ rmSync3(join6(opencodeDir, file));
4506
4804
  }
4805
+ s2.stop(`Deleted ${selected.length} files`);
4507
4806
  }
4807
+ } else {
4808
+ f2.info(`Run with ${import_picocolors10.default.cyan("--prune")} to select files or ${import_picocolors10.default.cyan("--prune-all")} to delete all`);
4809
+ }
4810
+ }
4811
+ if (existsSync6(join6(opencodeDir, "package.json"))) {
4812
+ const installSpinner = de();
4813
+ installSpinner.start("Installing dependencies");
4814
+ try {
4815
+ const { execSync: execSync2 } = await import("node:child_process");
4816
+ execSync2("bun install", { cwd: opencodeDir, stdio: "ignore" });
4817
+ installSpinner.stop("Dependencies installed");
4818
+ } catch {
4819
+ installSpinner.stop("Failed to install (run manually: cd .opencode && bun install)");
4508
4820
  }
4509
4821
  }
4510
- le("cd .opencode && bun install", "Run if dependencies changed");
4511
4822
  $e(import_picocolors10.default.green(`Upgraded to ${versionInfo.latest}`));
4512
4823
  }
4513
4824
 
@@ -4677,7 +4988,7 @@ async function doctorCommand() {
4677
4988
  }
4678
4989
  checks.push(...configChecks);
4679
4990
  displayChecks(configChecks);
4680
- } catch (err) {
4991
+ } catch {
4681
4992
  checks.push({
4682
4993
  name: "Valid JSON syntax",
4683
4994
  ok: false,
@@ -4964,7 +5275,6 @@ async function runBrowser(options) {
4964
5275
  items,
4965
5276
  icon = "●",
4966
5277
  color: itemColor = import_picocolors12.default.cyan,
4967
- onSelect,
4968
5278
  onBack
4969
5279
  } = options;
4970
5280
  if (items.length === 0) {
@@ -5155,7 +5465,24 @@ function padRight2(str, len) {
5155
5465
  return str + " ".repeat(pad);
5156
5466
  }
5157
5467
  function stripAnsi(str) {
5158
- return str.replace(/\x1b\[[0-9;]*m/g, "");
5468
+ const esc = "\x1B";
5469
+ let result = "";
5470
+ let index = 0;
5471
+ while (index < str.length) {
5472
+ const start = str.indexOf(esc, index);
5473
+ if (start === -1) {
5474
+ result += str.slice(index);
5475
+ break;
5476
+ }
5477
+ result += str.slice(index, start);
5478
+ const end = str.indexOf("m", start);
5479
+ if (end === -1) {
5480
+ index = start + 1;
5481
+ continue;
5482
+ }
5483
+ index = end + 1;
5484
+ }
5485
+ return result;
5159
5486
  }
5160
5487
  async function showFileViewer(item) {
5161
5488
  if (!existsSync8(item.path)) {
@@ -5570,7 +5897,24 @@ function padRight3(str, len) {
5570
5897
  return str + " ".repeat(pad);
5571
5898
  }
5572
5899
  function stripAnsi2(str) {
5573
- return str.replace(/\x1b\[[0-9;]*m/g, "");
5900
+ const esc = "\x1B";
5901
+ let result = "";
5902
+ let index = 0;
5903
+ while (index < str.length) {
5904
+ const start = str.indexOf(esc, index);
5905
+ if (start === -1) {
5906
+ result += str.slice(index);
5907
+ break;
5908
+ }
5909
+ result += str.slice(index, start);
5910
+ const end = str.indexOf("m", start);
5911
+ if (end === -1) {
5912
+ index = start + 1;
5913
+ continue;
5914
+ }
5915
+ index = end + 1;
5916
+ }
5917
+ return result;
5574
5918
  }
5575
5919
 
5576
5920
  // src/tui/components/Dashboard.tsx
@@ -5685,7 +6029,24 @@ function padRight4(str, len) {
5685
6029
  return str + " ".repeat(pad);
5686
6030
  }
5687
6031
  function stripAnsi3(str) {
5688
- return str.replace(/\x1b\[[0-9;]*m/g, "");
6032
+ const esc = "\x1B";
6033
+ let result = "";
6034
+ let index = 0;
6035
+ while (index < str.length) {
6036
+ const start = str.indexOf(esc, index);
6037
+ if (start === -1) {
6038
+ result += str.slice(index);
6039
+ break;
6040
+ }
6041
+ result += str.slice(index, start);
6042
+ const end = str.indexOf("m", start);
6043
+ if (end === -1) {
6044
+ index = start + 1;
6045
+ continue;
6046
+ }
6047
+ index = end + 1;
6048
+ }
6049
+ return result;
5689
6050
  }
5690
6051
 
5691
6052
  // src/tui/components/MCPMonitor.ts
@@ -5869,7 +6230,24 @@ function padRight5(str, len) {
5869
6230
  return str + " ".repeat(pad);
5870
6231
  }
5871
6232
  function stripAnsi4(str) {
5872
- return str.replace(/\x1b\[[0-9;]*m/g, "");
6233
+ const esc = "\x1B";
6234
+ let result = "";
6235
+ let index = 0;
6236
+ while (index < str.length) {
6237
+ const start = str.indexOf(esc, index);
6238
+ if (start === -1) {
6239
+ result += str.slice(index);
6240
+ break;
6241
+ }
6242
+ result += str.slice(index, start);
6243
+ const end = str.indexOf("m", start);
6244
+ if (end === -1) {
6245
+ index = start + 1;
6246
+ continue;
6247
+ }
6248
+ index = end + 1;
6249
+ }
6250
+ return result;
5873
6251
  }
5874
6252
 
5875
6253
  // src/tui/hooks/useData.ts
@@ -6045,7 +6423,7 @@ var cli = cac("ock");
6045
6423
  cli.option("--verbose", "Enable verbose logging");
6046
6424
  cli.option("--quiet", "Suppress all output");
6047
6425
  cli.version(`${packageVersion}`);
6048
- 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/)").action(initCommand);
6426
+ 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);
6049
6427
  cli.command("agent [action]", "Manage agents (list, add, view)").action(async (action) => {
6050
6428
  if (!action) {
6051
6429
  console.log(`
@@ -6095,7 +6473,7 @@ cli.command("status", "Show project overview").action(statusCommand);
6095
6473
  cli.command("config [action]", "Edit opencode.json (model, mcp, permission, validate, show)").action(async (action) => {
6096
6474
  await configCommand(action);
6097
6475
  });
6098
- cli.command("upgrade", "Update .opencode/ templates to latest version").option("--force", "Force upgrade even if already up to date").option("--check", "Check for updates without upgrading").action(async (options) => {
6476
+ cli.command("upgrade", "Update .opencode/ templates to latest version").option("--force", "Force upgrade even if already up to date").option("--check", "Check for updates without upgrading").option("--prune", "Manually select orphan files to delete").option("--prune-all", "Auto-delete all orphan files").action(async (options) => {
6099
6477
  await upgradeCommand(options);
6100
6478
  });
6101
6479
  cli.command("completion [shell]", "Generate shell completion script (bash, zsh, fish)").action(async (shell) => {