Add migration helper for Tabnine CLI to opencode - #1
Merged
Conversation
Adds migration_helper/ subtree with: - skills/migrate-from-tabnine-cli/ — interactive wizard skill that discovers Tabnine CLI (or Gemini CLI) configuration on disk, asks the user per category what to migrate, and translates fields into opencode's schema. Includes reference docs for source paths and field-by-field translation rules. - commands/migrate.md — /migrate slash command as an entry point to the wizard. - install.sh — bash installer that copies the skill and command into either ~/.config/opencode/ (default) or ./.opencode/ (--project). Shows a diff and prompts skip/overwrite/rename on collisions. Supports --overwrite to bypass prompts. - README.md — install (script or manual copy) and usage docs. - LICENSE — MIT, subtree-scoped. Root README gets one new section linking to migration_helper/README.md.
Pavel-tabnine
force-pushed
the
feat/migration-helper
branch
2 times, most recently
from
August 2, 2026 11:44
f8d7d34 to
f59fccc
Compare
Review-driven fixes to migrate-from-tabnine-cli, verified against
opencode 1.18.9 and tabnine-cli sources and via end-to-end wizard runs:
- MCP: env -> environment (opencode's key; env was silently ignored),
keep cwd and per-server timeout (both supported), rewrite $VAR/${VAR}
values to {env:VAR} unconditionally, back up opencode.json before merge
- Commands: single folder-mirroring namespacing policy, inline all three
placeholder rewrites ({{args}}, !{...}, @{...}), no-placeholder rule
- Skills: defined gemini-ism body scan, defensive name normalization,
duplicate-name semantics corrected (log-only warning, coin-flip winner)
- Discovery: A2A array-frontmatter bundles skipped up front, built-ins
shown as "(built-in, skipped)", stale enablement entries ignored,
extension-enablement respected
- source-map: settings precedence corrected (system tier wins last)
- Wizard UX: inline subagent/primary explanation, defined per-category
scope override, no migrate-all shortcut
New: migrate-tabnine-context skill + /migrate-context command that
copies TABNINE.md/GEMINI.md context files into AGENTS.md, re-runnable
per repository, merge-or-skip on existing targets.
Installer: installs both skills, survives non-interactive runs
(default-skip when /dev/tty cannot be opened), accurate --overwrite help.
Pavel-tabnine
force-pushed
the
feat/migration-helper
branch
from
August 3, 2026 10:26
f59fccc to
b6f9b13
Compare
… moves
The agent loader globs {agent,agents}/**/*.md, so both spellings load.
The skill called the singular form canonical while the agents docs use
the plural everywhere, which led to a migrated agent being written to
agent/ and then moved to agents/ after the fact.
Pin new agents to agents/ (matching the docs and 'opencode agent
create'), state that both are loaded, and keep an existing singular
folder as-is instead of consolidating. Add two core rules: no
filesystem changes after the Phase 4 summary, and report doc/skill
conflicts to the user instead of silently picking a side.
OPENCODE_CONFIG_DIR (set by the Tabnine wrapper) looked like it redirected the global config root, which caused a false warning that the migration had targeted the wrong directory. Tested by pointing the variable at a scratch dir: 'debug paths' still reports ~/.config/opencode, and marker skills, agents, and mcp entries placed in the override dir are ALSO loaded. It adds a root, it does not replace one. Document that, and that installing into both roots creates non-deterministic duplicates. Add an optional Phase 5 that verifies a migration actually loaded via 'debug paths', 'debug skill', 'agent list', and 'debug agent'. Offered in the summary, never run uninvited, and it cannot write. Add an advisory frontmatter check for unquoted descriptions containing a colon-space: strict YAML rejects them, current opencode builds parse them, so report rather than alarm and only quote with approval. Split the Gemini token grep into broken invocations vs path mentions. The old bare 'gemini ' substring flagged prose like "upstream Gemini CLI release" as a stale tool reference.
…claims Verified against the Tabnine CLI and opencode sources now available locally. tools was documented as "drop, opencode uses permission instead". Tabnine's tools is a YAML list of ALLOWED tool names (evals/concurrency-safety.eval.ts:14), so dropping it granted the migrated agent every tool including bash, write and edit — a privilege escalation created by the migration. Translate it to a deny-by-default permission block instead, with a name map (read_file -> read, run_shell_command -> bash, ...) since the two systems name tools differently. Flag the cases where the mapping unavoidably widens privilege (replace -> edit gains write) and list unmapped names rather than dropping them. MCP timeout defaults differ by two orders of magnitude: Tabnine MCP_DEFAULT_TIMEOUT_MSEC is 600000 (mcp-client.ts:103), opencode defaults to 5000. Omitting the field silently cut slow servers to a 5-second budget, so write it explicitly when the source relied on the default. Corrected a false claim I introduced: opencode does not resolve duplicate skill names "by a coin-flip". It logs duplicate skill name with both locations and the last scanned copy wins — deterministic but scan-order dependent and invisible in the UI. Fixed in three places plus the README. Which of the two config roots is scanned last is still unverified and is now stated as such. Add three core rules covering untrusted input: path validation before normalization (a lowercased ../../evil still escapes), never printing or copying credential values from settings.json, and treating migrated prompt bodies as data rather than instruction. Also default the A2A remote-agent branch to skip-with-warning instead of offering a bare two-option menu.
… budget Adds the OWASP pre-mutation receipt this skill was missing. New Phase 2.5 resolves every selected item to an absolute destination and prints a plan — target root and how it resolved, MCP endpoints being granted, each planned write with a create/backup/overwrite/rename verb, and the permission changes including any that widen privilege — then stops for explicit approval. A category selection is no longer approval to write. Phase 4 now reconciles actuals against that plan and names any unplanned write, so a partial failure leaves a manifest instead of a glob. Doubles as the dry-run mode. Core rule 3 now requires a timestamped backup before overwriting skills, agents and commands, matching what opencode.json already got; overwrite was previously unrecoverable for those. Collision handling was specified three different ways (rule 3 offered skip/overwrite/rename, Phase 2 only warned, Phase 3 merged without asking). Unified: Phase 2 flags, Phase 2.5 asks and records the verb, opencode.json stays the documented exception. QA pass on my own edits, all found by reading the result rather than by the checks: - "see core rule on privilege above" pointed at a rule that does not exist; now points at the tool map and states the risk inline. - The unknown-frontmatter-key claim said such keys "silently do nothing". opencode passes them to the model provider as request options, so a stale key can reach the provider and be rejected. - Phase 5's preamble asserted a restart was required and then explained why it was not. Rewritten: the commands are accurate immediately, the restart is for the user's running session. - The Phase 1 inventory example printed "MCPs (3)" including a built-in, demonstrating the exact confusion the rule below it forbids. - Duplicated Phase 1 lead-in, a stale "in this order" reference to a table that is gone, an orphaned Claude-skills paragraph left inside Phase 5, and a double blank line. - "Worth verifying on disk" replaced with three explicit checks. Moved verification and troubleshooting into references/verification-and-recovery.md with a JIT trigger, since both are only needed on request or on failure. Gave source-map.md a real trigger and deleted the vague "see references/" pointer the rubrics call out by name. Added a Gotchas section consolidating the facts that were buried mid-paragraph. SKILL.md measured with tiktoken cl100k_base rather than estimated: 5,176 tokens before, 4,936 after, against the 5,000 ceiling. 225 lines.
The README and the /migrate command still described the old flow, in which selecting categories led straight to writing. Both now describe the four steps, state that category selection does not authorize a write, and note that asking for the plan alone previews a migration. Also documented, since all three are user-visible: agent tool restrictions are carried across as an opencode permission block and any widened access is reported; MCP timeouts are written explicitly because the two systems default 10 minutes versus 5 seconds; and existing files are backed up before an approved overwrite. Adds a Reference documents section covering the three files under references/, a short section on confirming a migration loaded, and corrects the OPENCODE_CONFIG_DIR note to say the directory is read in addition to ~/.config/opencode rather than instead of it.
migrate-tabnine-context wrote files with no plan, no backup, and none of the input-handling rules the main skill gained, despite appending to an AGENTS.md the user already relies on. Adds a write plan with explicit approval, a timestamped backup before any merge, path validation for the context.fileName setting (it comes from a settings file and becomes a path), a rule that source content is data rather than instruction, and a rule against echoing file contents since a context file may quote private material. The summary now reconciles against the plan. Records one verified difference from the main skill: the global instruction file resolves from opencode's config root only, so ~/.config/opencode/AGENTS.md is the sole global target even when OPENCODE_CONFIG_DIR is set. Skills and agents load from both directories; AGENTS.md does not. /migrate-context and the README updated to match.
The migration skill told the user either config root was a valid target without qualification. That is wrong at the edges: if they target an OPENCODE_CONFIG_DIR, their skills and agents load from there but a global AGENTS.md never will, so /migrate-context cannot follow the same choice. The skill now states the exception and says to raise it at the moment the user picks the override root. The context skill's note was passive. It now actively checks during discovery for an AGENTS.md sitting inside an OPENCODE_CONFIG_DIR, reports it as present but never loaded, and offers to merge its content into ~/.config/opencode/AGENTS.md through the normal plan and backup flow, leaving the original in place.
The skill mapped a Tabnine subdirectory context file to a sibling AGENTS.md and said the two systems differ only in layout. They also differ in when the file loads, which the mapping hid. Verified in both sources. Tabnine loads a subdirectory's context on demand when the agent touches that subtree (loadJitSubdirectoryMemory), so packages/api/TABNINE.md applies even from the repository root. opencode resolves instructions with an upward glob from the session directory and has no on-demand loading, so packages/api/AGENTS.md is inert unless opencode is started inside packages/api. Both agree on the global file and on ancestor directories. Copying remains the default, since it is correct for anyone who opens sessions in that directory, but the plan now states the limitation per file and offers to fold the content into the project-root AGENTS.md instead, noting that this widens scope to the whole repository. The summary repeats which files are directory-scoped. Also: opencode reads CLAUDE.md and CONTEXT.md natively alongside AGENTS.md, so a context.fileName already set to one of those needs no migration. The skill previously only recognised AGENTS.md.
Reverts the CLAUDE.md and CONTEXT.md mention added in 652af83. Those are not Tabnine CLI context files and have no place in a migration from it. The CLAUDE.md claim was also conditionally wrong, since opencode only reads it when disableClaudeCodePrompt is false, so reporting "no migration needed" would have been incorrect for a user who has it disabled. The sources are TABNINE.md, GEMINI.md in legacy Gemini mode, or whatever context.fileName specifies, and the skill now says so explicitly. AGENTS.md keeps its skip rule, since opencode always reads it.
…d ones Two gaps in the nested-file handling added in 652af83. Discovery only searched the working directory and below. Tabnine also walks upward from the session directory to the project root (findUpwardGeminiFiles), so running the wizard from packages/api would silently miss the repository-root context file — usually the most important one. Discovery now searches upward as well, stopping at the repository root or $HOME, and states that ancestor and cwd files migrate cleanly because opencode loads from the session directory upward. Only files below it carry the loading caveat. The downward search can also match many files in a monorepo, which would make the plan unreadable and the per-file prompt unusable. Above roughly ten matches the wizard now groups them by depth with counts and sizes, and offers to accept them as a group, as a group with named exceptions, or individually. Grouping keeps the prompt readable without removing the decision.
The migration source is Tabnine CLI, so the skill no longer branches on the ORIG_GEMINI mode or scans .gemini/ paths. Removed from discovery, from the context filename defaults, from the descriptions of both skills, from both slash commands, from the README, and from source-map.md. A user still on plain Gemini CLI is out of scope. Grep patterns that flag Gemini-specific tooling inside migrated skill bodies are kept — that is a semantic check on the copied content, not a supported migration source. Rewrote references/source-map.md to describe behaviour rather than implementation. All packages/*.ts:line citations are gone. What Tabnine CLI reads, in what order, and from where is stated as fact rather than attributed to a source line the reader does not have. Applied the same rule elsewhere (source-map pointer in SKILL.md, the note about the Tabnine dev-mode env override). README and both slash commands updated to match the narrower scope.
Re-checked two claims the skill makes about opencode's Tabnine plugin, now that its source is on hand. Confirmed: the plugin registers tabnine-context and tabnine-coaching automatically. The names match. Never migrate them as mcp entries. The disabled-in-Tabnine case was under-handled. If mcp-server-enablement disables either, skipping the server does not carry the opt-out over — the plugin re-enables it by default. Fixed by translating the opt-out into plugin options rather than an mcp.<name>.enabled entry (which the plugin does not read). SKILL.md carries a one-liner and points to references/mapping.md, where the full table and an opencode.json plugin tuple example live. Corrected an imprecise auth claim. Old text: "the plugin already provides Tabnine authentication, so the CLI credential files should not be touched." True in outcome, wrong in reason — the plugin uses its own credential path (~/.local/share/opencode/auth.json) and cannot read the Tabnine CLI's credential files. The user must sign in to the plugin separately after migrating. Updated in source-map.md and the README.
Fixes an outright contradiction and removes several duplications surfaced by an independent review. Verified opencode's behaviour against its own binary before editing. Correctness. The skill claimed unknown agent frontmatter keys either "reach the provider API and be rejected" (SKILL.md) or "silently do nothing" (mapping.md). Neither is right. Verified by loading a probe agent: unknown keys are collected into `options` and forwarded to the model provider; most providers ignore what they don't recognise, some reject the request. Both files now say that. Same probe confirmed that a Tabnine `tools:` list triggers ConfigInvalidError, so translating it into `permission` is not optional — the file otherwise fails to load. Structure. "Phase 2.5" was an iteration scar. Renumbered 2.5 -> 3, 3 -> 4, 4 -> 5 across SKILL.md and every internal reference. Phase 5 is now an explicitly numbered sequence rather than an unordered bullet list, and the context skill's Phase 1 gets its missing "step 4" label. verification-and-recovery.md no longer cross-references another file's rule number. Menu defaults. The collision menus in both skills previously offered skip/overwrite/rename or merge/skip with no default. Named defaults: overwrite-with-backup for the main skill, merge-with-backup for the context skill. Path-safety parity. Both skills now cover the same threat model (`..`, path separator, leading `/` or `~`, control character, symlink out of root, check before normalisation). Category list drift. The description, README, /migrate command, and the four-step summary each listed migration categories in a different order and sometimes dropped agents entirely. Canonicalised to "MCP servers, skills, agents, slash commands, and extension contents" everywhere. Dedup. The plugin opt-out table (enableRemoteCodeSearch etc.) lived in both mapping.md and source-map.md; source-map now points to the mapping.md table. The frontmatter sanity check and the agent-folder- preference paragraphs each drop from four sentences to two.
arad3455
approved these changes
Aug 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
migration_helper/subtree that helps users move their Tabnine CLI (or Gemini CLI) configuration into opencode.It contains two opencode skills that run as interactive wizards inside opencode itself, plus slash commands and a small bash installer:
migrate-from-tabnine-cli(/migrate) — migrates MCP servers, skills, subagents, slash commands, and the contents of Tabnine CLI extensions. Run once per target scope (global or project).migrate-tabnine-context(/migrate-context) — migrates context/memory files (TABNINE.md,GEMINI.md, or customcontext.fileNamefiles) into opencode'sAGENTS.md. Re-runnable per repository.What's inside
migration_helper/What it migrates
MCP servers (including field renames opencode requires, e.g.
env→environment, and$VAR→{env:VAR}value rewrites), skills, subagents, slash commands, extension contents, and context files. Fields with no opencode equivalent are dropped with a note in the wizard's summary. Nothing is deleted from the Tabnine CLI installation — both wizards are copy-and-translate flows, and neither overwrites an existing opencode file without asking.Out of scope: OAuth tokens (users re-authenticate remote MCP servers on first use), Tabnine credentials, admin policy fields, hooks, themes, keybindings, and general settings.
Installer behaviour
For every file it wants to place:
[s]kip / [o]verwrite / [r]ename(rename appends.bak-YYYYMMDD-HHMMSS).Manual
cpcommands are documented as a fully supported alternative (recommended path on Windows).Verification
bash -n install.shpasses; installer tested on macOS (system bash 3.2) and Debian: fresh install (6 files), idempotent re-run,--project,--overwrite, interactive skip/overwrite/rename, and non-interactive runs.~/.gemini), and context-file migration (fresh target, merge into existingAGENTS.md, repeat runs).source-map.md,mapping.md) verified against the Tabnine CLI and opencode sources.Root README
One new section,
## Migrate from Tabnine CLI, links tomigration_helper/README.md. Otherwise the root README is unchanged.Licensing
migration_helper/LICENSEscopes this subtree to MIT withCopyright (c) 2026 Tabnine Ltd.. This is a documentation + shell subtree; MIT is the shortest, most permissive, best-known license for that shape. The root repo's proprietary posture (© Tabnine. All rights reserved.) is unchanged — only this subtree is MIT.