tuncss-plan-kit 0.3.0 → 0.4.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # tuncss-plan-kit
2
2
 
3
- Four skills for spec-driven development, installable into Claude Code, Codex CLI, OpenCode, and Antigravity:
3
+ Four skills for spec-driven development, installable into Claude Code, Codex CLI, and Antigravity:
4
4
 
5
5
  - **`/brainstorm`** — turn an idea into an approved spec (`docs/specs/`)
6
6
  - **`/plan-universal`** — turn a spec into an executable plan (`docs/plans/`)
@@ -23,9 +23,8 @@ The installer auto-detects which platform(s) the project uses and writes the rig
23
23
  |---|---|
24
24
  | `.claude/` or `CLAUDE.md` | Claude Code |
25
25
  | `.codex/` | Codex CLI |
26
- | `.opencode/` | OpenCode |
27
26
  | `.agents/` | Antigravity |
28
- | `AGENTS.md` (alone) | Codex, OpenCode, and Antigravity (they share `AGENTS.md`) |
27
+ | `AGENTS.md` (alone) | Codex and Antigravity (they share `AGENTS.md`) |
29
28
 
30
29
  If nothing is detected, pass an explicit target:
31
30
 
@@ -45,10 +44,9 @@ Restart your coding agent after install so it picks up the new skills and slash
45
44
  |---|---|---|---|
46
45
  | Claude Code | `.claude/skills/<n>/SKILL.md` | *(none — skills auto-expose as slash)* | `CLAUDE.md` |
47
46
  | Codex CLI | `.agents/skills/<n>/SKILL.md` | `.codex/prompts/<n>.md` | `AGENTS.md` |
48
- | OpenCode | `.opencode/skills/<n>/SKILL.md` | `.opencode/commands/<n>.md` | `AGENTS.md` |
49
47
  | Antigravity | `.agents/skills/<n>/SKILL.md` | *(none — skills auto-expose as slash)* | `AGENTS.md` |
50
48
 
51
- Claude Code and Antigravity automatically expose any skill named `foo` as `/foo`, so the kit doesn't write wrapper command files for them. Codex and OpenCode don't auto-expose, so wrappers are written there to give you the same `/brainstorm`, `/plan-universal`, `/handoff-plan`, `/changelog` UX everywhere.
49
+ Claude Code and Antigravity automatically expose any skill named `foo` as `/foo`, so the kit doesn't write wrapper command files for them. Codex doesn't auto-expose, so wrappers are written there to give you the same `/brainstorm`, `/plan-universal`, `/handoff-plan`, `/changelog` UX everywhere.
52
50
 
53
51
  Antigravity and Codex share `.agents/skills/`, so installing both writes those files once — the second platform reports them as already written.
54
52
 
@@ -56,11 +54,9 @@ Note: `agy changelog` is Antigravity's own built-in subcommand for release notes
56
54
 
57
55
  With `--global` the same files go to user-wide locations (`~/.claude/`, `~/.agents/`, `~/.codex/`, `~/.gemini/config/`).
58
56
 
59
- **OpenCode `--global` is supported via an npm-plugin route**: the kit installs itself into `~/.config/opencode/node_modules/`, registers itself in `~/.config/opencode/opencode.json`'s `plugin` array, and drops command wrappers into `~/.config/opencode/commands/`. After install, restart OpenCode — skills appear in every project. (For Claude and Codex, `--global` is a plain file copy.)
60
-
61
57
  **Antigravity `--global` is a plain file copy to `~/.gemini/config/`** — a single location the desktop app, the `agy` CLI, and the IDE all read, so one install covers them all.
62
58
 
63
- Project-local is still the default for all four — recommended unless you specifically want the kit available everywhere.
59
+ Project-local is still the default for all three — recommended unless you specifically want the kit available everywhere.
64
60
 
65
61
  ## Workflow
66
62
 
@@ -110,7 +106,7 @@ npx tuncss-plan-kit init [--target=<list>] [--global] [--force]
110
106
 
111
107
  | Flag | Effect |
112
108
  |------|--------|
113
- | `--target=<list>` | Comma-separated. Values: `claude`, `codex`, `opencode`, `antigravity`, `all`. Auto-detected if omitted. |
109
+ | `--target=<list>` | Comma-separated. Values: `claude`, `codex`, `antigravity`, `all`. Auto-detected if omitted. |
114
110
  | `--global` | Install to user-wide locations instead of the current project. |
115
111
  | `--force` | Overwrite existing skill/command files without warning. |
116
112
 
@@ -118,6 +114,11 @@ npx tuncss-plan-kit init [--target=<list>] [--global] [--force]
118
114
 
119
115
  Existing kits ship dozens of agents and skills you'll never use, but every one of them sits in your context and burns tokens each turn. `tuncss-plan-kit` ships four files that cover the only loop most projects need: design → plan → execute (here or elsewhere) → record. That's it.
120
116
 
117
+ ## A note on OpenCode
118
+
119
+ OpenCode was supported through v0.3.0. It was dropped in v0.4.0 because nobody
120
+ on the team uses it any more. If you need it, pin `tuncss-plan-kit@0.3.0`.
121
+
121
122
  ## License
122
123
 
123
124
  MIT
package/bin/cli.js CHANGED
@@ -3,12 +3,10 @@
3
3
  import fs from "fs";
4
4
  import path from "path";
5
5
  import os from "os";
6
- import { spawnSync } from "child_process";
7
6
  import { fileURLToPath } from "url";
8
7
 
9
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
9
  const PKG_ROOT = path.resolve(__dirname, "..");
11
- const PKG_NAME = "tuncss-plan-kit";
12
10
 
13
11
  const SKILLS = ["brainstorm", "plan-universal", "handoff-plan", "changelog"];
14
12
  const COMMANDS = ["brainstorm", "plan-universal", "handoff-plan", "changelog"];
@@ -45,18 +43,6 @@ const PLATFORMS = {
45
43
  }),
46
44
  commandsSrc: "codex",
47
45
  },
48
- opencode: {
49
- label: "opencode",
50
- project: {
51
- skillsDir: ".opencode/skills",
52
- commandsDir: ".opencode/commands",
53
- instructionsFile: "AGENTS.md",
54
- },
55
- // Global install for OpenCode uses the npm-plugin route, not file-drop.
56
- // See installOpenCodeGlobal().
57
- global: null,
58
- commandsSrc: "opencode",
59
- },
60
46
  // Antigravity reads .agents/ in a workspace and ~/.gemini/config/ globally.
61
47
  // One global location serves all three variants (desktop app, agy CLI, IDE).
62
48
  // Like Claude Code, it expands skills into slash commands on its own, so no
@@ -109,17 +95,14 @@ Usage:
109
95
  npx tuncss-plan-kit init [--target=<list>] [--global] [--force]
110
96
 
111
97
  Commands:
112
- init Install the three skills (brainstorm, plan-universal, handoff-plan)
113
- into the target platform(s).
98
+ init Install the four skills (brainstorm, plan-universal, handoff-plan,
99
+ changelog) into the target platform(s).
114
100
 
115
101
  Options:
116
102
  --target Comma-separated platforms to install for. Supported:
117
- claude, codex, opencode, antigravity, all
103
+ claude, codex, antigravity, all
118
104
  If omitted, auto-detects from the current directory.
119
105
  --global Install to user-wide locations.
120
- For OpenCode this uses the npm-plugin route (installs the package
121
- into ~/.config/opencode/node_modules/ and registers it in
122
- opencode.json's plugin array).
123
106
  --force Overwrite existing skill/command files without warning.
124
107
  (Instruction-file marker blocks are always idempotent.)
125
108
 
@@ -146,16 +129,12 @@ function detectTargets(cwd) {
146
129
  found.add("claude");
147
130
  }
148
131
  if (exists(path.join(cwd, ".codex"))) found.add("codex");
149
- if (exists(path.join(cwd, ".opencode"))) found.add("opencode");
150
132
  // Antigravity discovers .agents/ and reads AGENTS.md as rules. Its project
151
133
  // paths are a subset of Codex's, so adding it here costs no extra files.
152
134
  if (exists(path.join(cwd, ".agents"))) found.add("antigravity");
153
135
  if (exists(path.join(cwd, "AGENTS.md"))) {
154
136
  found.add("antigravity");
155
- if (!found.has("codex") && !found.has("opencode")) {
156
- found.add("codex");
157
- found.add("opencode");
158
- }
137
+ if (!found.has("codex")) found.add("codex");
159
138
  }
160
139
  return [...found];
161
140
  }
@@ -297,105 +276,6 @@ function installPlatform({ platform, isGlobal, force, baseRoot, sharedState }) {
297
276
  return lines;
298
277
  }
299
278
 
300
- function readJsonOrDefault(filePath, fallback) {
301
- if (!exists(filePath)) return fallback;
302
- try {
303
- return JSON.parse(fs.readFileSync(filePath, "utf8"));
304
- } catch {
305
- return fallback;
306
- }
307
- }
308
-
309
- function writeJson(filePath, obj) {
310
- ensureDir(path.dirname(filePath));
311
- fs.writeFileSync(filePath, JSON.stringify(obj, null, 2) + "\n");
312
- }
313
-
314
- function isRunningFromInstalledPackage() {
315
- // If our package root contains /node_modules/, we were installed as a dep
316
- // (e.g. via `npm install -g` or `npx`). Otherwise we're being run from
317
- // source (dev mode).
318
- return PKG_ROOT.split(path.sep).includes("node_modules");
319
- }
320
-
321
- function dependencySpec() {
322
- // When running from a published install, use a version range so npm
323
- // installs from the registry. When running from source (dev), point to
324
- // our absolute path with file: so changes are picked up without publish.
325
- if (isRunningFromInstalledPackage()) {
326
- return `^${readJsonOrDefault(path.join(PKG_ROOT, "package.json"), { version: "0.1.0" }).version || "0.1.0"}`;
327
- }
328
- return "file:" + PKG_ROOT.split(path.sep).join("/");
329
- }
330
-
331
- function installOpenCodeGlobal() {
332
- const home = os.homedir();
333
- const ocDir = path.join(home, ".config", "opencode");
334
- ensureDir(ocDir);
335
- const lines = [];
336
-
337
- // 1. package.json — add or update tuncss-plan-kit dependency
338
- const pkgPath = path.join(ocDir, "package.json");
339
- const pkg = readJsonOrDefault(pkgPath, {});
340
- pkg.dependencies = pkg.dependencies || {};
341
- const spec = dependencySpec();
342
- const prevSpec = pkg.dependencies[PKG_NAME];
343
- pkg.dependencies[PKG_NAME] = spec;
344
- writeJson(pkgPath, pkg);
345
- lines.push(
346
- ` ${prevSpec === spec ? "·" : prevSpec ? "↻" : "✓"} ${relPath(pkgPath, home)} (dep: ${PKG_NAME}@${spec})`
347
- );
348
-
349
- // 2. npm install in ocDir
350
- // Pass the full command as a single string with shell: true to avoid the
351
- // Node DEP0190 warning about unescaped args concatenation. Args are all
352
- // hardcoded here so there's no injection surface.
353
- const npmBin = process.platform === "win32" ? "npm.cmd" : "npm";
354
- const npmRes = spawnSync(`${npmBin} install --silent --no-audit --no-fund`, {
355
- cwd: ocDir,
356
- stdio: "inherit",
357
- shell: true,
358
- });
359
- if (npmRes.status !== 0) {
360
- throw new Error(
361
- `npm install failed in ${ocDir} (exit code ${npmRes.status}). Fix the error above and re-run.`
362
- );
363
- }
364
- lines.push(` ✓ npm install ran in ${relPath(ocDir, home)}`);
365
-
366
- // 3. command wrappers — file-drop into ~/.config/opencode/commands/
367
- // (the plugin handles skills via config injection, but command discovery
368
- // appears to require file-drop)
369
- const cmdDir = path.join(ocDir, "commands");
370
- for (const cmd of COMMANDS) {
371
- const src = path.join(PKG_ROOT, "commands", "opencode", `${cmd}.md`);
372
- const dest = path.join(cmdDir, `${cmd}.md`);
373
- const r = copyFile(src, dest, false);
374
- lines.push(` ${statusTag(r.status)} ${relPath(r.dest, home)}`);
375
- }
376
-
377
- // 4. opencode.json — add plugin reference (idempotent)
378
- const ocJsonPath = path.join(ocDir, "opencode.json");
379
- const ocJson = readJsonOrDefault(ocJsonPath, {
380
- $schema: "https://opencode.ai/config.json",
381
- });
382
- ocJson.plugin = ocJson.plugin || [];
383
- // Normalize to strings only (we don't use the [string, object] tuple form)
384
- const pluginPath = `~/.config/opencode/node_modules/${PKG_NAME}`;
385
- const alreadyHas = ocJson.plugin.some(
386
- (p) => p === pluginPath || (Array.isArray(p) && p[0] === pluginPath)
387
- );
388
- if (!alreadyHas) {
389
- ocJson.plugin.push(pluginPath);
390
- }
391
- writeJson(ocJsonPath, ocJson);
392
- lines.push(
393
- ` ${alreadyHas ? "·" : "✓"} ${relPath(ocJsonPath, home)} (plugin: ${pluginPath})`
394
- );
395
-
396
- return lines;
397
- }
398
-
399
279
  function init(args) {
400
280
  const cwd = process.cwd();
401
281
 
@@ -439,19 +319,14 @@ function init(args) {
439
319
 
440
320
  for (const platform of targets) {
441
321
  console.log(`[${platform}]`);
442
- if (platform === "opencode" && args.global) {
443
- const lines = installOpenCodeGlobal();
444
- for (const l of lines) console.log(l);
445
- } else {
446
- const lines = installPlatform({
447
- platform,
448
- isGlobal: args.global,
449
- force: args.force,
450
- baseRoot: cwd,
451
- sharedState,
452
- });
453
- for (const l of lines) console.log(l);
454
- }
322
+ const lines = installPlatform({
323
+ platform,
324
+ isGlobal: args.global,
325
+ force: args.force,
326
+ baseRoot: cwd,
327
+ sharedState,
328
+ });
329
+ for (const l of lines) console.log(l);
455
330
  console.log("");
456
331
  }
457
332
 
package/package.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "name": "tuncss-plan-kit",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
- "main": ".opencode/plugins/tuncss-plan-kit.js",
6
5
  "description": "Four-skill kit for spec-driven development: brainstorm an idea into a spec, turn the spec into an executable plan, hand the plan off to another LLM agent, and record what changed.",
7
6
  "bin": {
8
7
  "tuncss-plan-kit": "bin/cli.js"
@@ -12,7 +11,6 @@
12
11
  "skills/",
13
12
  "commands/",
14
13
  "templates/",
15
- ".opencode/",
16
14
  "README.md"
17
15
  ],
18
16
  "keywords": [
@@ -1,48 +0,0 @@
1
- import path from "path";
2
- import { fileURLToPath } from "url";
3
-
4
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
- const skillsDir = path.resolve(__dirname, "../../skills");
6
-
7
- // Minimal templates: just pass the user's input through. The skill's
8
- // `description` frontmatter is what triggers the model to invoke the skill
9
- // when relevant — we don't force-load it from the wrapper, which would
10
- // dump the full SKILL.md body into the chat in OpenCode's UI.
11
- const WRAPPERS = {
12
- brainstorm: {
13
- description: "Turn an idea into an approved spec",
14
- template: "$ARGUMENTS\n",
15
- },
16
- "plan-universal": {
17
- description: "Turn an approved spec into an executable implementation plan",
18
- template: "$ARGUMENTS\n",
19
- },
20
- "handoff-plan": {
21
- description:
22
- "Generate a paste-ready handoff message for another LLM agent to execute the plan",
23
- template: "$ARGUMENTS\n",
24
- },
25
- changelog: {
26
- description: "Record what changed in docs/CHANGELOG.md",
27
- template: "$ARGUMENTS\n",
28
- },
29
- };
30
-
31
- export const TuncssPlanKitPlugin = async () => ({
32
- config: async (config) => {
33
- config.skills = config.skills || {};
34
- config.skills.paths = config.skills.paths || [];
35
- if (!config.skills.paths.includes(skillsDir)) {
36
- config.skills.paths.push(skillsDir);
37
- }
38
- config.command = config.command || {};
39
- for (const [name, def] of Object.entries(WRAPPERS)) {
40
- if (!config.command[name]) {
41
- config.command[name] = {
42
- template: def.template,
43
- description: def.description,
44
- };
45
- }
46
- }
47
- },
48
- });
@@ -1,5 +0,0 @@
1
- ---
2
- description: Turn an idea into an approved spec
3
- ---
4
-
5
- $ARGUMENTS
@@ -1,5 +0,0 @@
1
- ---
2
- description: Record what changed in docs/CHANGELOG.md
3
- ---
4
-
5
- $ARGUMENTS
@@ -1,5 +0,0 @@
1
- ---
2
- description: Generate a paste-ready handoff message for another LLM agent to execute the plan
3
- ---
4
-
5
- $ARGUMENTS
@@ -1,5 +0,0 @@
1
- ---
2
- description: Turn an approved spec into an executable implementation plan
3
- ---
4
-
5
- $ARGUMENTS