Remove docs/reports and docs/decisions directories, keep only plans
Reports and decisions are implementation artifacts that don't need to persist in the repository. Plans in docs/plans/ are retained as living documents that track implementation progress. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,4 @@ Avoid adding a provider if the target spec is unstable or undocumented.
|
||||
|
||||
## Repository Docs Convention
|
||||
|
||||
- **ADRs** live in `docs/decisions/` and are numbered with 4-digit zero-padding: `0001-short-title.md`, `0002-short-title.md`, etc.
|
||||
- **Orchestrator run reports** live in `docs/reports/`.
|
||||
|
||||
When recording a significant decision (new provider, output format change, merge strategy), create an ADR in `docs/decisions/` following the numbering sequence.
|
||||
- **Plans** live in `docs/plans/` and track implementation progress.
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# ADR 0001: OpenCode commands written as .md files, not in opencode.json
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
2026-02-20
|
||||
|
||||
## Context
|
||||
OpenCode supports two equivalent formats for custom commands. Writing to opencode.json requires overwriting or merging the user's config file. Writing .md files is additive and non-destructive.
|
||||
|
||||
## Decision
|
||||
The OpenCode target always emits commands as individual .md files in the commands/ subdirectory. The command key is never written to opencode.json by this tool.
|
||||
|
||||
## Consequences
|
||||
- Positive: Installs are non-destructive. Commands are visible as individual files, easy to inspect. Consistent with agents/skills handling.
|
||||
- Negative: Users inspecting opencode.json won't see plugin commands; they must look in commands/.
|
||||
- Neutral: Requires OpenCode >= the version with command file support (confirmed stable).
|
||||
|
||||
## Plan Reference
|
||||
Originated from: docs/plans/feature_opencode-commands_as_md_and_config_merge.md
|
||||
@@ -1,21 +0,0 @@
|
||||
# ADR 0002: Plugin merges into existing opencode.json rather than replacing it
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
2026-02-20
|
||||
|
||||
## Context
|
||||
Users have existing opencode.json files with personal configuration. The install command previously backed up and replaced this file entirely, destroying user settings.
|
||||
|
||||
## Decision
|
||||
writeOpenCodeBundle reads existing opencode.json (if present), deep-merges plugin-provided keys without overwriting user-set values, and writes the merged result. User keys always win on conflict.
|
||||
|
||||
## Consequences
|
||||
- Positive: User config preserved across installs. Re-installs are idempotent for user-set values.
|
||||
- Negative: Plugin cannot remove or update an MCP server entry if the user already has one with the same name.
|
||||
- Neutral: Backup of pre-merge file is still created for safety.
|
||||
|
||||
## Plan Reference
|
||||
Originated from: docs/plans/feature_opencode-commands_as_md_and_config_merge.md
|
||||
@@ -1,21 +0,0 @@
|
||||
# ADR 0003: Global permissions not written to opencode.json by default
|
||||
|
||||
## Status
|
||||
Accepted
|
||||
|
||||
## Date
|
||||
2026-02-20
|
||||
|
||||
## Context
|
||||
Claude commands carry allowedTools as per-command restrictions. OpenCode has no per-command permission mechanism. Writing per-command restrictions as global permissions is semantically incorrect and pollutes the user's global config.
|
||||
|
||||
## Decision
|
||||
--permissions defaults to "none". The plugin never writes permission or tools to opencode.json unless the user explicitly passes --permissions broad or --permissions from-command.
|
||||
|
||||
## Consequences
|
||||
- Positive: User's global OpenCode permissions are never silently modified.
|
||||
- Negative: Users who relied on auto-set permissions must now pass the flag explicitly.
|
||||
- Neutral: The "broad" and "from-command" modes still work as documented for opt-in use.
|
||||
|
||||
## Plan Reference
|
||||
Originated from: docs/plans/feature_opencode-commands_as_md_and_config_merge.md
|
||||
@@ -1,48 +0,0 @@
|
||||
# Phase 1 Handoff Report: Type Changes for Command Files
|
||||
|
||||
**Date:** 2026-02-20
|
||||
**Phase:** 1 of 4
|
||||
**Status:** Complete
|
||||
|
||||
## Summary
|
||||
|
||||
Implemented type changes to support storing commands as `.md` files instead of inline in `opencode.json`.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Type Changes (`src/types/opencode.ts`)
|
||||
|
||||
- Removed `OpenCodeCommandConfig` type (lines 23-28)
|
||||
- Removed `command?: Record<string, OpenCodeCommandConfig>` from `OpenCodeConfig`
|
||||
- Added `OpenCodeCommandFile` type:
|
||||
```typescript
|
||||
export type OpenCodeCommandFile = {
|
||||
name: string
|
||||
content: string
|
||||
}
|
||||
```
|
||||
- Added `commandFiles: OpenCodeCommandFile[]` to `OpenCodeBundle` (with comment referencing ADR-001)
|
||||
|
||||
### 2. Import Update (`src/converters/claude-to-opencode.ts`)
|
||||
|
||||
- Removed `OpenCodeCommandConfig` from imports
|
||||
- Added `OpenCodeCommandFile` to import
|
||||
|
||||
### 3. Test Updates
|
||||
|
||||
- `tests/converter.test.ts`: Updated 4 tests to use `bundle.commandFiles.find()` instead of `bundle.config.command`
|
||||
- `tests/opencode-writer.test.ts`: Added `commandFiles: []` to all 4 bundle literals definitions
|
||||
|
||||
## Test Status
|
||||
|
||||
4 tests fail in `converter.test.ts` because the converter hasn't been updated yet to populate `commandFiles`. This is expected behavior - Phase 2 will fix these.
|
||||
|
||||
```
|
||||
76 pass, 4 fail in converter.test.ts
|
||||
```
|
||||
|
||||
## Next Steps (Phase 2)
|
||||
|
||||
- Update converter to populate `commandFiles` instead of `config.command`
|
||||
- Update writer to output `.md` files for commands
|
||||
- Tests will pass after Phase 2 implementation
|
||||
@@ -1,63 +0,0 @@
|
||||
# Phase 2 Handoff Report: Convert Commands to .md Files
|
||||
|
||||
**Date:** 2026-02-20
|
||||
**Phase:** 2 of 4
|
||||
**Status:** Complete
|
||||
|
||||
## Summary
|
||||
|
||||
Implemented `convertCommands()` to emit `.md` command files with YAML frontmatter and body, rather than returning a `Record<string, OpenCodeCommandConfig>`. Updated `convertClaudeToOpenCode()` to populate `commandFiles` in the bundle instead of `config.command`.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Converter Function (`src/converters/claude-to-opencode.ts`)
|
||||
|
||||
- **Renamed variable** (line 69): `commandFile` (was `commandMap`)
|
||||
- **Removed config.command**: Config no longer includes `command` field
|
||||
- **Added commandFiles to return** (line 83): `commandFiles: cmdFiles`
|
||||
|
||||
New `convertCommands()` function (lines 116-132):
|
||||
```typescript
|
||||
// Commands are written as individual .md files rather than entries in opencode.json.
|
||||
// Chosen over JSON map because opencode resolves commands by filename at runtime (ADR-001).
|
||||
function convertCommands(commands: ClaudeCommand[]): OpenCodeCommandFile[] {
|
||||
const files: OpenCodeCommandFile[] = []
|
||||
for (const command of commands) {
|
||||
if (command.disableModelInvocation) continue
|
||||
const frontmatter: Record<string, unknown> = {
|
||||
description: command.description,
|
||||
}
|
||||
if (command.model && command.model !== "inherit") {
|
||||
frontmatter.model = normalizeModel(command.model)
|
||||
}
|
||||
const content = formatFrontmatter(frontmatter, rewriteClaudePaths(command.body))
|
||||
files.push({ name: command.name, content })
|
||||
}
|
||||
return files
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Test Updates (`tests/converter.test.ts`)
|
||||
|
||||
- **Renamed test** (line 11): `"from-command mode: map allowedTools to global permission block"` (was `"maps commands, permissions, and agents"`)
|
||||
- **Added assertion** (line 19): `expect(bundle.config.command).toBeUndefined()`
|
||||
- **Renamed test** (line 204): `"excludes commands with disable-model-invocation from commandFiles"` (was `"excludes commands with disable-model-invocation from command map"`)
|
||||
- **Added new test** (lines 289-307): `"command .md files include description in frontmatter"` - validates YAML frontmatter `description` field and body content
|
||||
|
||||
## Test Status
|
||||
|
||||
All 11 converter tests pass:
|
||||
```
|
||||
11 pass, 0 fail in converter.test.ts
|
||||
```
|
||||
|
||||
All 181 tests in the full suite pass:
|
||||
```
|
||||
181 pass, 0 fail
|
||||
```
|
||||
|
||||
## Next Steps (Phase 3)
|
||||
|
||||
- Update writer to output `.md` files for commands to `.opencode/commands/` directory
|
||||
- Update config merge to handle command files from multiple plugins sources
|
||||
- Ensure writer tests pass with new output structure
|
||||
@@ -1,54 +0,0 @@
|
||||
# Phase 3 Handoff Report: Write Command Files as .md
|
||||
|
||||
## Date
|
||||
2026-02-20
|
||||
|
||||
## Phase
|
||||
3 of feature: OpenCode Commands as .md Files, Config Merge, and Permissions Default Fix
|
||||
|
||||
## Summary
|
||||
|
||||
Implemented the `commandsDir` path resolution and command file writing in `src/targets/opencode.ts`.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Updated `src/targets/opencode.ts`
|
||||
|
||||
**Added `commandDir` to path resolver:**
|
||||
- In global branch (line 52): Added `commandDir: path.join(outputRoot, "commands")` with inline comment
|
||||
- In custom branch (line 66): Added `commandDir: path.join(outputRoot, ".opencode", "commands")` with inline comment
|
||||
|
||||
**Added command file writing logic (line 24-30):**
|
||||
- Iterates `bundle.commandFiles`
|
||||
- Writes each command as `<commandsDir>/<name>.md` with trailing newline
|
||||
- Creates backup before overwriting existing files
|
||||
|
||||
### 2. Added tests in `tests/opencode-writer.test.ts`
|
||||
|
||||
- `"writes command files as .md in commands/ directory"` - Tests global-style output (`.config/opencode`)
|
||||
- `"backs up existing command .md file before overwriting"` - Tests backup creation
|
||||
|
||||
## Test Results
|
||||
|
||||
```
|
||||
bun test tests/opencode-writer.test.ts
|
||||
6 pass, 0 fail
|
||||
```
|
||||
|
||||
All existing tests continue to pass:
|
||||
```
|
||||
bun test
|
||||
183 pass, 0 fail
|
||||
```
|
||||
|
||||
## Deliverables Complete
|
||||
|
||||
- [x] Updated `src/targets/opencode.ts` with commandDir path and write logic
|
||||
- [x] New tests in `tests/opencode-writer.test.ts`
|
||||
- [x] All tests pass
|
||||
|
||||
## Notes
|
||||
|
||||
- Used `openCodePaths` instead of `paths` variable name to avoid shadowing the imported `path` module
|
||||
- Command files are written with trailing newline (`content + "\n"`)
|
||||
- Backup uses timestamp format `.bak.2026-02-20T...`
|
||||
@@ -1,45 +0,0 @@
|
||||
# Phase 4 Handoff: Deep-Merge opencode.json
|
||||
|
||||
**Date:** 2026-02-20
|
||||
**Status:** Complete
|
||||
|
||||
## Summary
|
||||
|
||||
Implemented `mergeOpenCodeConfig()` function that performs deep-merge of plugin config into existing opencode.json with user-wins-on-conflict strategy.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Updated `src/targets/opencode.ts`
|
||||
|
||||
- Added imports for `pathExists`, `readJson`, and `OpenCodeConfig` type
|
||||
- Added `mergeOpenCodeConfig()` function before `writeOpenCodeBundle()`
|
||||
- Replaced direct `writeJson()` call with merge logic
|
||||
|
||||
### 2. Updated `tests/opencode-writer.test.ts`
|
||||
|
||||
- Renamed existing backup test to `"merges plugin config into existing opencode.json without destroying user keys"`
|
||||
- Added two new tests:
|
||||
- `"merges mcp servers without overwriting user entry"`
|
||||
- `"preserves unrelated user keys when merging opencode.json"`
|
||||
|
||||
## Verification
|
||||
|
||||
All 8 tests pass:
|
||||
```
|
||||
bun test tests/opencode-writer.test.ts
|
||||
8 pass, 0 fail
|
||||
```
|
||||
|
||||
## Key Behaviors
|
||||
|
||||
1. **User keys preserved**: All existing config keys remain intact
|
||||
2. **MCP merge**: Plugin MCP servers added, user servers kept on conflict
|
||||
3. **Permission merge**: Plugin permissions added, user permissions kept on conflict
|
||||
4. **Tools merge**: Plugin tools added, user tools kept on conflict
|
||||
5. **Fallback**: If existing config is malformed JSON, writes plugin-only config (safety first)
|
||||
6. **Backup**: Original config is still backed up before writing merged result
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Proceed to next phase (if any)
|
||||
- Consider adding decision log entry for ADR-002 (user-wins-on-conflict strategy)
|
||||
@@ -1,35 +0,0 @@
|
||||
# Phase 5 Handoff: Change `--permissions` Default to `"none"`
|
||||
|
||||
## Summary
|
||||
|
||||
Changed the default value of `--permissions` from `"broad"` to `"none"` in the install command to prevent polluting user OpenCode config with global permissions.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. Code Change (`src/commands/install.ts`)
|
||||
|
||||
- Line 51: Changed `default: "broad"` to `default: "none"` with comment referencing ADR-003
|
||||
- Line 52: Updated description to clarify "none (default)"
|
||||
|
||||
```typescript
|
||||
permissions: {
|
||||
type: "string",
|
||||
default: "none", // Default is "none" -- writing global permissions to opencode.json pollutes user config. See ADR-003.
|
||||
description: "Permission mapping written to opencode.json: none (default) | broad | from-command",
|
||||
},
|
||||
```
|
||||
|
||||
### 2. New Tests (`tests/cli.test.ts`)
|
||||
|
||||
Added two new tests:
|
||||
1. `"install --to opencode uses permissions:none by default"` - Verifies no `permission` or `tools` keys in opencode.json when using default
|
||||
2. `"install --to opencode --permissions broad writes permission block"` - Verifies `permission` key is written when explicitly using `--permissions broad`
|
||||
|
||||
## Test Results
|
||||
|
||||
- CLI tests: 12 pass, 0 fail
|
||||
- All tests: 187 pass, 0 fail
|
||||
|
||||
## Next Steps
|
||||
|
||||
None - Phase 5 is complete.
|
||||
@@ -1,29 +0,0 @@
|
||||
# Phase 6: Update AGENTS.md and README.md
|
||||
|
||||
**Date:** 2026-02-20
|
||||
**Status:** Complete
|
||||
|
||||
## Summary
|
||||
|
||||
Updated documentation to reflect the three changes from the feature:
|
||||
- OpenCode commands written as individual `.md` files
|
||||
- Deep-merge for `opencode.json`
|
||||
- Command file backup before overwrite
|
||||
|
||||
## Changes Made
|
||||
|
||||
### AGENTS.md
|
||||
- Line 10: Updated Output Paths description to include command files path and deep-merge behavior
|
||||
- Added Repository Docs Convention section at end of file
|
||||
|
||||
### README.md
|
||||
- Line 54: Updated OpenCode output description to include command files and deep-merge behavior
|
||||
|
||||
## Verification
|
||||
|
||||
- Read updated files and confirmed accuracy
|
||||
- Run `bun test` - no regressions
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Ready for merge to main branch
|
||||
@@ -1,17 +0,0 @@
|
||||
# Plan Amendment Summary
|
||||
|
||||
Overall adherence: HIGH
|
||||
Phases with deviations: None
|
||||
|
||||
## Deviations
|
||||
|
||||
No deviations from the plan were made. All phases were implemented as specified.
|
||||
|
||||
## Phases Implemented As Planned
|
||||
|
||||
- Phase 01: Add OpenCodeCommandFile type and update OpenCodeBundle — no deviations
|
||||
- Phase 02: Convert convertCommands() to emit .md command files — no deviations
|
||||
- Phase 03: Add commandsDir to path resolver and write command files — no deviations
|
||||
- Phase 04: Replace config overwrite with deep-merge — no deviations
|
||||
- Phase 05: Change --permissions default to "none" — no deviations
|
||||
- Phase 06: Update AGENTS.md and README.md — no deviations
|
||||
@@ -1,37 +0,0 @@
|
||||
# Verification Report: OpenCode Commands as .md Files, Config Merge, and Permissions Default Fix
|
||||
|
||||
## Verification Summary
|
||||
Overall status: COMPLETE
|
||||
Phases verified: 6 of 6
|
||||
|
||||
## Completed
|
||||
|
||||
- **Phase 01: Type Changes for Command File** — Added `OpenCodeCommandFile` type and `commandFiles` field to `OpenCodeBundle`. Removed `OpenCodeCommandConfig` and `command` from `OpenCodeConfig`. Tests updated to use new bundle structure.
|
||||
|
||||
- **Phase 02: Convert Commands to .md Files** — Implemented `convertCommands()` to return `OpenCodeCommandFile[]` with YAML frontmatter (`description`, `model`) and body. Removed `config.command` assignment. Updated tests verify commandFiles exist and command config is undefined.
|
||||
|
||||
- **Phase 03: Write Command Files** — Added `commandDir` to path resolver (both global and custom branches). Implemented command file writing with backup-before-overwrite in `writeOpenCodeBundle()`. New tests verify file creation and backup.
|
||||
|
||||
- **Phase 04: Deep-Merge Config** — Implemented `mergeOpenCodeConfig()` with user-wins-on-conflict strategy. Preserves user keys (`model`, `theme`, `provider`), merges MCP servers, handles malformed JSON with fallback. Updated tests verify merge behavior.
|
||||
|
||||
- **Phase 05: Permissions Default to "none"** — Changed `--permissions` default from `"broad"` to `"none"` in install command. Added code comment referencing ADR-003. Tests verify no permission/tools written by default, and explicit `--permissions broad` works.
|
||||
|
||||
- **Phase 06: Update Documentation** — Updated AGENTS.md line 10 with command path and deep-merge behavior. Added Repository Docs Convention section (lines 50-55). Updated README.md line 54 with complete behavior description.
|
||||
|
||||
## Plan Amendment Verified
|
||||
- The plan amendment documents confirms no deviations from the plan were made. All phases implemented as specified.
|
||||
|
||||
## ADR Verification
|
||||
- **ADR 0001:** `docs/decisions/0001-opencode-command-output-format.md` exists with correct content (Status: Accepted, Context, Decision, Consequences, Plan Reference)
|
||||
- **ADR 0002:** `docs/decisions/0002-opencode-json-merge-strategy.md` exists with correct content (Status: Accepted, user-wins-on-conflict strategy documented)
|
||||
- **ADR 0003:** `docs/decisions/0003-opencode-permissions-default-none.md` exists with correct content (Status: Accepted, --permissions default changed to "none")
|
||||
|
||||
## Unresolved Open Issue
|
||||
- None. All handoff reports show "Status: Complete" with no open issues remaining.
|
||||
|
||||
## Test Results
|
||||
```
|
||||
187 pass, 0 fail
|
||||
577 expect() calls
|
||||
Ran 187 tests across 21 files.
|
||||
```
|
||||
@@ -1,281 +0,0 @@
|
||||
# Decision Log: OpenCode Commands as .md Files
|
||||
|
||||
## Decision: ADR-001 - Store Commands as Individual .md Files
|
||||
|
||||
**Date:** 2026-02-20
|
||||
**Status:** Adopted
|
||||
|
||||
## Context
|
||||
|
||||
The original design stored commands configurations inline in `opencode.json` under `config.command`. This tightly couples command metadata with config, making it harder to version-control commands separately and share command files.
|
||||
|
||||
## Decision
|
||||
|
||||
Store commands definitions as individual `.md` files in `.opencode/commands/` directory, with YAML frontmatter for metadata and markdown body for the command prompt.
|
||||
|
||||
**New Type:**
|
||||
```typescript
|
||||
export type OpenCodeCommandFile = {
|
||||
name: string // command name, used as filename stem: <name>.md
|
||||
content: string // full file content: YAML frontmatter + body
|
||||
}
|
||||
```
|
||||
|
||||
**Bundle Structure:**
|
||||
```typescript
|
||||
export type OpenCodeBundle = {
|
||||
config: OpenCodeConfig
|
||||
agents: OpenCodeAgentFile[]
|
||||
commandFiles: OpenCodeCommandFile[] // NEW
|
||||
plugins: OpenCodePluginFile[]
|
||||
skillDirs: { sourceDir: string; name: string }[]
|
||||
}
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
- **Positive:** Commands can be versioned, shared, and edited independently
|
||||
- **Negative:** Requires updating converter, writer, and all consumers
|
||||
- **Migration:** Phase 1-4 will implement the full migration
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
1. Keep inline in config - Rejected: limits flexibility
|
||||
2. Use separate JSON files - Rejected: YAML frontmatter is more idiomatic for command
|
||||
|
||||
---
|
||||
|
||||
## Decision: Phase 2 - Converter Emits .md Files
|
||||
|
||||
**Date:** 2026-02-20
|
||||
**Status:** Implemented
|
||||
|
||||
## Context
|
||||
|
||||
The converter needs to populate `commandFiles` in the bundle rather than `config.command`.
|
||||
|
||||
## Decision
|
||||
|
||||
`convertCommands()` returns `OpenCodeCommandFile[]` where each file contains:
|
||||
- **filename**: `<command-name>.md`
|
||||
- **content**: YAML frontmatter (`description`, optionally `model`) + body (template text with Claude path rewriting)
|
||||
|
||||
### Frontmatter Structure
|
||||
```yaml
|
||||
---
|
||||
description: "Review code changes"
|
||||
model: openai/gpt-4o
|
||||
---
|
||||
|
||||
Template text here...
|
||||
```
|
||||
|
||||
### Filtering
|
||||
- Commands with `disableModelInvocation: true` are excluded from output
|
||||
|
||||
### Path Rewriting
|
||||
- `.claude/` paths rewritten to `.opencode/` in body content (via `rewriteClaudePaths()`)
|
||||
|
||||
## Consequences
|
||||
|
||||
- Converter now produces command files ready for file-system output
|
||||
- Writer phase will handle writing to `.opencode/commands/` directory
|
||||
- Phase 1 type changes are now fully utilizeds
|
||||
|
||||
---
|
||||
|
||||
## Decision: Phase 3 - Writer Writes Command .md Files
|
||||
|
||||
**Date:** 2026-02-20
|
||||
**Status:** Implemented
|
||||
|
||||
## Context
|
||||
|
||||
The writer needs to write command files from the bundle to the file system.
|
||||
|
||||
## Decision
|
||||
|
||||
In `src/targets/opencode.ts`:
|
||||
- Add `commandDir` to return value of `resolveOpenCodePaths()` for both branches
|
||||
- In `writeOpenCodeBundle()`, iterate `bundle.commandFiles` and write each as `<commandsDir>/<name>.md` with backup-before-overwrite
|
||||
|
||||
### Path Resolution
|
||||
|
||||
- Global branch (basename is "opencode" or ".opencode"): `commandsDir: path.join(outputRoot, "commands")`
|
||||
- Custom branch: `commandDir: path.join(outputRoot, ".opencode", "commands")`
|
||||
|
||||
### Writing Logic
|
||||
|
||||
```typescript
|
||||
for (const commandFile of bundle.commandFiles) {
|
||||
const dest = path.join(openCodePaths.commandDir, `${commandFile.name}.md`)
|
||||
const cmdBackupPath = await backupFile(dest)
|
||||
if (cmdBackupPath) {
|
||||
console.log(`Backed up existing command file to ${cmdBackupPath}`)
|
||||
}
|
||||
await writeText(dest, commandFile.content + "\n")
|
||||
}
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
- Command files are written to `.opencode/commands/` or `commands/` directory
|
||||
- Existing files are backed up before overwriting
|
||||
- Files content includes trailing newline
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
1. Use intermediate variable for commandDir - Rejected: caused intermittent undefined errors
|
||||
2. Use direct property reference `openCodePaths.commandDir` - Chosen: more reliable
|
||||
|
||||
---
|
||||
|
||||
## Decision: ADR-002 - User-Wins-On-Conflict for Config Merge
|
||||
|
||||
**Date:** 2026-02-20
|
||||
**Status:** Adopted
|
||||
|
||||
## Context
|
||||
|
||||
When merging plugin config into existing opencode.json, conflicts may occur (e.g., same MCP server name with different configuration). The merge strategy must decide which value wins.
|
||||
|
||||
## Decision
|
||||
|
||||
**User config wins on conflict.** When plugin and user both define the same key (MCP server name, permission, tool), the user's value takes precedence.
|
||||
|
||||
### Rationale
|
||||
|
||||
- Safety first: Do not overwrite user data with plugin defaults
|
||||
- Users have explicit intent in their local config
|
||||
- Plugins should add new entries without modifying user's existing setup
|
||||
- Aligns with AGENTS.md principle: "Do not delete or overwrite user data"
|
||||
|
||||
### Merge Algorithm
|
||||
|
||||
```typescript
|
||||
const mergedMcp = {
|
||||
...(incoming.mcp ?? {}),
|
||||
...(existing.mcp ?? {}), // existing takes precedence
|
||||
}
|
||||
```
|
||||
|
||||
Same pattern applied to `permission` and `tools`.
|
||||
|
||||
### Fallback Behavior
|
||||
|
||||
If existing `opencode.json` is malformed JSON, warn and write plugin-only config rather than crashing:
|
||||
```typescript
|
||||
} catch {
|
||||
console.warn(`Warning: existing ${configPath} is not valid JSON. Writing plugin config without merging.`)
|
||||
return incoming
|
||||
}
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
- Positive: User config never accidentally overwritten
|
||||
- Positive: Plugin can add new entries without conflict
|
||||
- Negative: Plugin cannot modify user's existing server configuration (must use unique names)
|
||||
- Negative: Silent merge may mask configuration issues if user expects plugin override
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
1. Plugin wins on conflict - Rejected: would overwrite user data
|
||||
2. Merge and combine arrays - Rejected: MCP servers are keyed object, not array
|
||||
3. Fail on conflict - Rejected: breaks installation workflow
|
||||
|
||||
---
|
||||
|
||||
## Decision: ADR-003 - Permissions Default "none" for OpenCode Output
|
||||
|
||||
**Date:** 2026-02-20
|
||||
**Status:** Implemented
|
||||
|
||||
## Context
|
||||
|
||||
When installing a Claude plugin to OpenCode format, the `--permissions` flag determines whether permission/tool mappings is written to `opencode.json`. The previous default was `"broad"`, which writes global permissions to the user's config file.
|
||||
|
||||
## Decision
|
||||
|
||||
Change the default value of `--permissions` from `"broad"` to `"none"` in the install command.
|
||||
|
||||
### Rationale
|
||||
|
||||
- **User safety:** Writing global permissions to `opencode.json` pollutes user config and may grant unintended access
|
||||
- **Principle alignment:** Follows AGENTS.md "Do not delete or overwrite user data"
|
||||
- **Explicit opt-in:** Users must explicitly request `--permissions broad` to write permissions to their config
|
||||
- **Backward compatible:** Existing workflows using `--permissions broad` continues to work
|
||||
|
||||
### Implementation
|
||||
|
||||
In `src/commands/install.ts`:
|
||||
```typescript
|
||||
permissions: {
|
||||
type: "string",
|
||||
default: "none", // Default is "none" -- writing global permissions to opencode.json pollutes user config. See ADR-003.
|
||||
description: "Permission mapping written to opencode.json: none (default) | broad | from-command",
|
||||
},
|
||||
```
|
||||
|
||||
### Test Coverage
|
||||
|
||||
Added two CLI tests cases:
|
||||
1. `install --to opencode uses permissions:none by default` - Verifies no `permission` or `tools` key in output
|
||||
2. `install --to opencode --permissions broad writes permission block` - Verifies `permission` key is written when explicitly requested
|
||||
|
||||
## Consequences
|
||||
|
||||
- **Positive:** User config remains clean by default
|
||||
- **Positive:** Explicit opt-in required for permission writing
|
||||
- **Negative:** Users migrating from older versions need to explicitly use `--permissions broad` if they want permissions
|
||||
- **Migration path:** Document the change in migration notes
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
1. Keep "broad" as default - Rejected: pollutes user config
|
||||
2. Prompt user interactively - Rejected: break CLI automation
|
||||
3. Write to separate file - Rejected: OpenCode expects permissions in opencode.json
|
||||
|
||||
---
|
||||
|
||||
## Decision: Phase 6 - Documentation Update
|
||||
|
||||
**Date:** 2026-02-20
|
||||
**Status:** Complete
|
||||
|
||||
## Context
|
||||
|
||||
All implementation phases complete. Documentation needs to reflect the final behavior.
|
||||
|
||||
## Decision
|
||||
|
||||
Update AGENTS.md and README.md:
|
||||
|
||||
### AGENTS.md Changes
|
||||
|
||||
1. **Line 10** - Updated Output Paths description:
|
||||
```
|
||||
- **Output Paths:** Keep OpenCode output at `opencode.json` and `.opencode/{agents,skills,plugins}`. For OpenCode, command go to `~/.config/opencode/commands/<name>.md`; `opencode.json` is deep-merged (never overwritten wholesale).
|
||||
```
|
||||
|
||||
2. **Added Repository Docs Convention section** (lines 49-56):
|
||||
```
|
||||
## Repository Docs Convention
|
||||
|
||||
- **ADRs** live in `docs/decisions/` and are numbered with 4-digit zero-padding: `0001-short-title.md`, `0002-short-title.md`, etc.
|
||||
- **Orchestrator run reports** live in `docs/reports/`.
|
||||
|
||||
When recording a significant decision (new provider, output format change, merge strategy), create an ADR in `docs/decisions/` following the numbering sequence.
|
||||
```
|
||||
|
||||
### README.md Changes
|
||||
|
||||
1. **Line 54** - Updated OpenCode output description:
|
||||
```
|
||||
OpenCode output is written to `~/.config/opencode` by default. Command are written as individual `.md` files to `~/.config/opencode/commands/<name>.md`. Agent, skills, and plugin are written to the corresponding subdirectory alongside. `opencode.json` (MCP servers) is deep-merged into any existing file -- user keys such as `model`, `theme`, and `provider` are preserved, and user values win on conflicts. Command files are backed up before being overwritten.
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
- Read updated files and confirmed accuracy
|
||||
- Run `bun test` - no regression
|
||||
@@ -1,3 +0,0 @@
|
||||
| Date | Run Directory | Plan Source | Summary |
|
||||
|------|--------------|-------------|---------|
|
||||
| 2026-02-20 | `opencode-commands-md-merge/` | `docs/plans/feature_opencode-commands_as_md_and_config_merge.md` | Implement OpenCode commands as .md files, deep-merge opencode.json, and change --permissions default to none |
|
||||
Reference in New Issue
Block a user