tuncss-plan-kit 0.2.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, and OpenCode:
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,8 +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
- | `AGENTS.md` (alone) | Both Codex and OpenCode (they share `AGENTS.md`) |
26
+ | `.agents/` | Antigravity |
27
+ | `AGENTS.md` (alone) | Codex and Antigravity (they share `AGENTS.md`) |
28
28
 
29
29
  If nothing is detected, pass an explicit target:
30
30
 
@@ -44,13 +44,17 @@ Restart your coding agent after install so it picks up the new skills and slash
44
44
  |---|---|---|---|
45
45
  | Claude Code | `.claude/skills/<n>/SKILL.md` | *(none — skills auto-expose as slash)* | `CLAUDE.md` |
46
46
  | Codex CLI | `.agents/skills/<n>/SKILL.md` | `.codex/prompts/<n>.md` | `AGENTS.md` |
47
- | OpenCode | `.opencode/skills/<n>/SKILL.md` | `.opencode/commands/<n>.md` | `AGENTS.md` |
47
+ | Antigravity | `.agents/skills/<n>/SKILL.md` | *(none — skills auto-expose as slash)* | `AGENTS.md` |
48
48
 
49
- Claude Code automatically exposes any skill named `foo` as `/foo`, so the kit doesn't write wrapper command files for it. Codex and OpenCode don't auto-expose, so wrappers are written there to give you the same `/brainstorm`, `/plan-universal`, `/handoff-plan` 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.
50
50
 
51
- With `--global` the same files go to user-wide locations (`~/.claude/`, `~/.agents/`, `~/.codex/`).
51
+ Antigravity and Codex share `.agents/skills/`, so installing both writes those files once — the second platform reports them as already written.
52
52
 
53
- **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.)
53
+ Note: `agy changelog` is Antigravity's own built-in subcommand for release notes. The kit's `/changelog` is a slash command inside the agent session — same word, different place.
54
+
55
+ With `--global` the same files go to user-wide locations (`~/.claude/`, `~/.agents/`, `~/.codex/`, `~/.gemini/config/`).
56
+
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.
54
58
 
55
59
  Project-local is still the default for all three — recommended unless you specifically want the kit available everywhere.
56
60
 
@@ -102,7 +106,7 @@ npx tuncss-plan-kit init [--target=<list>] [--global] [--force]
102
106
 
103
107
  | Flag | Effect |
104
108
  |------|--------|
105
- | `--target=<list>` | Comma-separated. Values: `claude`, `codex`, `opencode`, `all`. Auto-detected if omitted. |
109
+ | `--target=<list>` | Comma-separated. Values: `claude`, `codex`, `antigravity`, `all`. Auto-detected if omitted. |
106
110
  | `--global` | Install to user-wide locations instead of the current project. |
107
111
  | `--force` | Overwrite existing skill/command files without warning. |
108
112
 
@@ -110,6 +114,11 @@ npx tuncss-plan-kit init [--target=<list>] [--global] [--force]
110
114
 
111
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.
112
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
+
113
122
  ## License
114
123
 
115
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,17 +43,24 @@ const PLATFORMS = {
45
43
  }),
46
44
  commandsSrc: "codex",
47
45
  },
48
- opencode: {
49
- label: "opencode",
46
+ // Antigravity reads .agents/ in a workspace and ~/.gemini/config/ globally.
47
+ // One global location serves all three variants (desktop app, agy CLI, IDE).
48
+ // Like Claude Code, it expands skills into slash commands on its own, so no
49
+ // wrapper files are written.
50
+ antigravity: {
51
+ label: "antigravity",
50
52
  project: {
51
- skillsDir: ".opencode/skills",
52
- commandsDir: ".opencode/commands",
53
+ skillsDir: ".agents/skills",
54
+ commandsDir: null,
53
55
  instructionsFile: "AGENTS.md",
54
56
  },
55
- // Global install for OpenCode uses the npm-plugin route, not file-drop.
56
- // See installOpenCodeGlobal().
57
- global: null,
58
- commandsSrc: "opencode",
57
+ global: (home) => ({
58
+ skillsDir: path.join(home, ".gemini", "config", "skills"),
59
+ commandsDir: null,
60
+ instructionsFile: path.join(home, ".gemini", "config", "AGENTS.md"),
61
+ }),
62
+ commandsSrc: null,
63
+ skipCommands: true,
59
64
  },
60
65
  };
61
66
 
@@ -90,17 +95,14 @@ Usage:
90
95
  npx tuncss-plan-kit init [--target=<list>] [--global] [--force]
91
96
 
92
97
  Commands:
93
- init Install the three skills (brainstorm, plan-universal, handoff-plan)
94
- into the target platform(s).
98
+ init Install the four skills (brainstorm, plan-universal, handoff-plan,
99
+ changelog) into the target platform(s).
95
100
 
96
101
  Options:
97
102
  --target Comma-separated platforms to install for. Supported:
98
- claude, codex, opencode, all
103
+ claude, codex, antigravity, all
99
104
  If omitted, auto-detects from the current directory.
100
105
  --global Install to user-wide locations.
101
- For OpenCode this uses the npm-plugin route (installs the package
102
- into ~/.config/opencode/node_modules/ and registers it in
103
- opencode.json's plugin array).
104
106
  --force Overwrite existing skill/command files without warning.
105
107
  (Instruction-file marker blocks are always idempotent.)
106
108
 
@@ -127,12 +129,12 @@ function detectTargets(cwd) {
127
129
  found.add("claude");
128
130
  }
129
131
  if (exists(path.join(cwd, ".codex"))) found.add("codex");
130
- if (exists(path.join(cwd, ".opencode"))) found.add("opencode");
132
+ // Antigravity discovers .agents/ and reads AGENTS.md as rules. Its project
133
+ // paths are a subset of Codex's, so adding it here costs no extra files.
134
+ if (exists(path.join(cwd, ".agents"))) found.add("antigravity");
131
135
  if (exists(path.join(cwd, "AGENTS.md"))) {
132
- if (!found.has("codex") && !found.has("opencode")) {
133
- found.add("codex");
134
- found.add("opencode");
135
- }
136
+ found.add("antigravity");
137
+ if (!found.has("codex")) found.add("codex");
136
138
  }
137
139
  return [...found];
138
140
  }
@@ -233,7 +235,13 @@ function installPlatform({ platform, isGlobal, force, baseRoot, sharedState }) {
233
235
  for (const skill of SKILLS) {
234
236
  const src = path.join(PKG_ROOT, "skills", skill, "SKILL.md");
235
237
  const dest = path.join(paths.skillsDir, skill, "SKILL.md");
238
+ // Codex and Antigravity share .agents/skills; don't copy the same file twice.
239
+ if (sharedState.writtenSkillFiles.has(dest)) {
240
+ lines.push(` · ${relPath(dest, displayBase)} (already written this run)`);
241
+ continue;
242
+ }
236
243
  const r = copyFile(src, dest, force);
244
+ sharedState.writtenSkillFiles.add(dest);
237
245
  lines.push(` ${statusTag(r.status)} ${relPath(r.dest, displayBase)}`);
238
246
  }
239
247
 
@@ -268,105 +276,6 @@ function installPlatform({ platform, isGlobal, force, baseRoot, sharedState }) {
268
276
  return lines;
269
277
  }
270
278
 
271
- function readJsonOrDefault(filePath, fallback) {
272
- if (!exists(filePath)) return fallback;
273
- try {
274
- return JSON.parse(fs.readFileSync(filePath, "utf8"));
275
- } catch {
276
- return fallback;
277
- }
278
- }
279
-
280
- function writeJson(filePath, obj) {
281
- ensureDir(path.dirname(filePath));
282
- fs.writeFileSync(filePath, JSON.stringify(obj, null, 2) + "\n");
283
- }
284
-
285
- function isRunningFromInstalledPackage() {
286
- // If our package root contains /node_modules/, we were installed as a dep
287
- // (e.g. via `npm install -g` or `npx`). Otherwise we're being run from
288
- // source (dev mode).
289
- return PKG_ROOT.split(path.sep).includes("node_modules");
290
- }
291
-
292
- function dependencySpec() {
293
- // When running from a published install, use a version range so npm
294
- // installs from the registry. When running from source (dev), point to
295
- // our absolute path with file: so changes are picked up without publish.
296
- if (isRunningFromInstalledPackage()) {
297
- return `^${readJsonOrDefault(path.join(PKG_ROOT, "package.json"), { version: "0.1.0" }).version || "0.1.0"}`;
298
- }
299
- return "file:" + PKG_ROOT.split(path.sep).join("/");
300
- }
301
-
302
- function installOpenCodeGlobal() {
303
- const home = os.homedir();
304
- const ocDir = path.join(home, ".config", "opencode");
305
- ensureDir(ocDir);
306
- const lines = [];
307
-
308
- // 1. package.json — add or update tuncss-plan-kit dependency
309
- const pkgPath = path.join(ocDir, "package.json");
310
- const pkg = readJsonOrDefault(pkgPath, {});
311
- pkg.dependencies = pkg.dependencies || {};
312
- const spec = dependencySpec();
313
- const prevSpec = pkg.dependencies[PKG_NAME];
314
- pkg.dependencies[PKG_NAME] = spec;
315
- writeJson(pkgPath, pkg);
316
- lines.push(
317
- ` ${prevSpec === spec ? "·" : prevSpec ? "↻" : "✓"} ${relPath(pkgPath, home)} (dep: ${PKG_NAME}@${spec})`
318
- );
319
-
320
- // 2. npm install in ocDir
321
- // Pass the full command as a single string with shell: true to avoid the
322
- // Node DEP0190 warning about unescaped args concatenation. Args are all
323
- // hardcoded here so there's no injection surface.
324
- const npmBin = process.platform === "win32" ? "npm.cmd" : "npm";
325
- const npmRes = spawnSync(`${npmBin} install --silent --no-audit --no-fund`, {
326
- cwd: ocDir,
327
- stdio: "inherit",
328
- shell: true,
329
- });
330
- if (npmRes.status !== 0) {
331
- throw new Error(
332
- `npm install failed in ${ocDir} (exit code ${npmRes.status}). Fix the error above and re-run.`
333
- );
334
- }
335
- lines.push(` ✓ npm install ran in ${relPath(ocDir, home)}`);
336
-
337
- // 3. command wrappers — file-drop into ~/.config/opencode/commands/
338
- // (the plugin handles skills via config injection, but command discovery
339
- // appears to require file-drop)
340
- const cmdDir = path.join(ocDir, "commands");
341
- for (const cmd of COMMANDS) {
342
- const src = path.join(PKG_ROOT, "commands", "opencode", `${cmd}.md`);
343
- const dest = path.join(cmdDir, `${cmd}.md`);
344
- const r = copyFile(src, dest, false);
345
- lines.push(` ${statusTag(r.status)} ${relPath(r.dest, home)}`);
346
- }
347
-
348
- // 4. opencode.json — add plugin reference (idempotent)
349
- const ocJsonPath = path.join(ocDir, "opencode.json");
350
- const ocJson = readJsonOrDefault(ocJsonPath, {
351
- $schema: "https://opencode.ai/config.json",
352
- });
353
- ocJson.plugin = ocJson.plugin || [];
354
- // Normalize to strings only (we don't use the [string, object] tuple form)
355
- const pluginPath = `~/.config/opencode/node_modules/${PKG_NAME}`;
356
- const alreadyHas = ocJson.plugin.some(
357
- (p) => p === pluginPath || (Array.isArray(p) && p[0] === pluginPath)
358
- );
359
- if (!alreadyHas) {
360
- ocJson.plugin.push(pluginPath);
361
- }
362
- writeJson(ocJsonPath, ocJson);
363
- lines.push(
364
- ` ${alreadyHas ? "·" : "✓"} ${relPath(ocJsonPath, home)} (plugin: ${pluginPath})`
365
- );
366
-
367
- return lines;
368
- }
369
-
370
279
  function init(args) {
371
280
  const cwd = process.cwd();
372
281
 
@@ -405,23 +314,19 @@ function init(args) {
405
314
  const sharedState = {
406
315
  instructionsBlock: loadInstructionsBlock(),
407
316
  writtenInstructionFiles: new Set(),
317
+ writtenSkillFiles: new Set(),
408
318
  };
409
319
 
410
320
  for (const platform of targets) {
411
321
  console.log(`[${platform}]`);
412
- if (platform === "opencode" && args.global) {
413
- const lines = installOpenCodeGlobal();
414
- for (const l of lines) console.log(l);
415
- } else {
416
- const lines = installPlatform({
417
- platform,
418
- isGlobal: args.global,
419
- force: args.force,
420
- baseRoot: cwd,
421
- sharedState,
422
- });
423
- for (const l of lines) console.log(l);
424
- }
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);
425
330
  console.log("");
426
331
  }
427
332
 
package/package.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "name": "tuncss-plan-kit",
3
- "version": "0.2.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