@sayansr26/agent-os 0.5.1 → 0.5.2

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/CHANGELOG.md CHANGED
@@ -5,6 +5,34 @@ All notable changes to this project are documented here.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
6
6
  this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.5.1] — 2026-09-15
9
+
10
+ Found by running 0.5.0 against a real repository that already had six
11
+ path-scoped rules and a hand-written `AGENTS.md`. It overwrote the
12
+ `AGENTS.md`. 0.5.0 was never published.
13
+
14
+ ### Added
15
+ - **`init` installs the Claude Code plugin.** When Claude Code is detected it
16
+ runs `claude plugin marketplace add` and `claude plugin install --yes` at
17
+ project scope, so the plugin travels with the repository rather than living
18
+ on one machine. If the `claude` CLI is not on PATH it prints the two slash
19
+ commands instead. `--no-plugin` skips it, and the rules are written either
20
+ way — the compiler never depends on the plugin step succeeding.
21
+
22
+ ### Fixed
23
+ - **`init` scaffolded over projects that already had rules.** It wrote a
24
+ placeholder `AGENTS.md` and an `example.md` rule regardless of what was
25
+ there, and the next `sync` compiled the placeholder on top of the project's
26
+ real `AGENTS.md`. `init` now adopts what it finds — the existing `AGENTS.md`
27
+ and the first rules directory it recognises, `.cursor/rules/*.mdc` converted
28
+ back to `paths:` — and seeds `example.md` only when there was nothing to
29
+ adopt. A tool for stopping rule drift must not cause it.
30
+ - **`sync` now refuses to overwrite a file it did not generate.** Generated
31
+ files carry a banner; anything else at a generated path is the user's own
32
+ work. `sync` names those files, leaves them alone and exits non-zero, with
33
+ `--force` as the explicit opt-out. `AGENTS.md` carries the banner too — it
34
+ previously did not, which is why nothing could tell it apart.
35
+
8
36
  ## [0.5.0] — 2026-09-15
9
37
 
10
38
  `agent-os` becomes a cross-tool CLI. The Claude Code plugin is now one target
@@ -40,18 +68,6 @@ among several rather than the whole product.
40
68
  - Documentation is generic throughout; examples are invented.
41
69
 
42
70
  ### Fixed
43
- - **`init` scaffolded over projects that already had rules.** It wrote a
44
- placeholder `AGENTS.md` and an `example.md` rule regardless of what was
45
- there, and the next `sync` compiled the placeholder on top of the project's
46
- real `AGENTS.md`. `init` now adopts what it finds — the existing `AGENTS.md`
47
- and the first rules directory it recognises, `.cursor/rules/*.mdc` converted
48
- back to `paths:` — and seeds `example.md` only when there was nothing to
49
- adopt. A tool for stopping rule drift must not cause it.
50
- - **`sync` now refuses to overwrite a file it did not generate.** Generated
51
- files carry a banner; anything else at a generated path is the user's own
52
- work. `sync` names those files, leaves them alone and exits non-zero, with
53
- `--force` as the explicit opt-out. `AGENTS.md` carries the banner too — it
54
- previously did not, which is why nothing could tell it apart.
55
71
  - `check` reported every skill file as drifted immediately after a `sync`. Skill
56
72
  files are read as buffers so a skill can ship a binary asset, rule files are
57
73
  generated as strings, and the two were compared with `!==`. Comparison is now
package/README.md CHANGED
@@ -76,6 +76,13 @@ Every command below is `npx @sayansr26/agent-os <command>`. Install it once —
76
76
 
77
77
  `--root <dir>` to target another directory, `--dry-run` to preview.
78
78
 
79
+ **`init` sets up Claude Code completely.** When it detects Claude Code it also
80
+ installs the plugin — the agents, skills, per-agent memory and session hook —
81
+ with `claude plugin marketplace add` and `claude plugin install --yes` at
82
+ project scope, so it travels with the repo. `--no-plugin` skips it. Every other
83
+ detected tool gets its rules in its own schema; the plugin layer is Claude Code
84
+ only because no other tool has anywhere to put it.
85
+
79
86
  **On an existing project, `init` adopts rather than scaffolds.** It takes your
80
87
  current `AGENTS.md` and the first rules directory it recognises as the source,
81
88
  so the first `sync` regenerates what you already had. **`sync` never overwrites
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sayansr26/agent-os",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "One source of truth for AI coding agent config. Write your rules once; compile them to Claude Code, Cursor, Cline, Windsurf, Antigravity, Gemini CLI, OpenCode and Kilo.",
5
5
  "keywords": [
6
6
  "ai",
package/src/cli.mjs CHANGED
@@ -5,6 +5,7 @@ import { join } from "node:path";
5
5
  import { detect, summarise, TOOLS } from "./detect.mjs";
6
6
  import { load, write, readIfExists, matches, BANNER, DIR } from "./source.mjs";
7
7
  import { adopt } from "./adopt.mjs";
8
+ import { installPlugin, MARKETPLACE, MARKETPLACE_NAME, PLUGIN } from "./plugin.mjs";
8
9
  import { compile, TARGETS } from "./targets.mjs";
9
10
 
10
11
  const bold = (s) => `\x1b[1m${s}\x1b[0m`;
@@ -27,6 +28,7 @@ Options
27
28
  --root <dir> project directory (default: cwd)
28
29
  --dry-run print what would change, write nothing
29
30
  --force overwrite files agent-os did not generate (it refuses by default)
31
+ --no-plugin skip installing the Claude Code plugin during init
30
32
  `;
31
33
 
32
34
  function scaffold(root, found) {
@@ -203,7 +205,25 @@ Pick one:
203
205
  return;
204
206
  }
205
207
 
206
- console.log(`\n${dim("Generated files carry a banner. Edit .agent-os/ and re-run sync; never edit them directly.")}\n`);
208
+ console.log(`\n${dim("Generated files carry a banner. Edit .agent-os/ and re-run sync; never edit them directly.")}`);
209
+
210
+ // The rules are done and safe at this point. The Claude Code plugin is the
211
+ // other half — agents, skills, per-agent memory, the session hook — and it
212
+ // installs from the same repo. Only on `init`, never on `sync`.
213
+ if (cmd === "init" && targets.includes("claude-code") && !argv.includes("--no-plugin")) {
214
+ console.log(`\n${bold("Claude Code plugin")} ${dim(`${PLUGIN}@${MARKETPLACE_NAME}`)}\n`);
215
+ const r = installPlugin({ dry });
216
+ for (const line of r.done) console.log(` ${dim(line)}`);
217
+ if (r.ok && r.reason === "installed") {
218
+ console.log(`\n installed at project scope ${dim("— .claude/settings.json, so it travels with the repo")}`);
219
+ console.log(` ${dim("restart Claude Code, or /reload-plugins, then run /agent-os:init")}`);
220
+ } else if (!r.ok) {
221
+ console.log("");
222
+ for (const line of r.hint) console.log(` ${line}`);
223
+ }
224
+ console.log(`\n ${dim("--no-plugin skips this")}`);
225
+ }
226
+ console.log("");
207
227
  return;
208
228
  }
209
229
 
package/src/plugin.mjs ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Install the Claude Code half of agent-os during `init`.
3
+ *
4
+ * The CLI compiles rules for every tool. Claude Code additionally gets the
5
+ * agents, skills, per-agent memory and session hook, which ship as a plugin —
6
+ * so `init` should install it rather than printing two slash commands and
7
+ * hoping. `claude plugin ...` with `--yes` is the documented automation path.
8
+ *
9
+ * Everything here is best effort and reversible: if the `claude` CLI is not on
10
+ * PATH, or a command fails, `init` reports it and carries on. The rules are the
11
+ * part that must not depend on this.
12
+ */
13
+ import { spawnSync } from "node:child_process";
14
+
15
+ export const MARKETPLACE = "sayansr26/agent-os";
16
+ export const MARKETPLACE_NAME = "sayan-plugins";
17
+ export const PLUGIN = "agent-os";
18
+
19
+ const run = (args) =>
20
+ spawnSync("claude", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
21
+
22
+ /** Is the Claude Code CLI usable from here? */
23
+ export function claudeAvailable() {
24
+ const r = spawnSync("claude", ["--version"], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
25
+ return !r.error && r.status === 0;
26
+ }
27
+
28
+ /**
29
+ * Add the marketplace and install the plugin at project scope, so the setup
30
+ * travels with the repository instead of living on one machine.
31
+ */
32
+ export function installPlugin({ dry = false, scope = "project" } = {}) {
33
+ const steps = [
34
+ ["marketplace", ["plugin", "marketplace", "add", MARKETPLACE, "--scope", scope]],
35
+ ["plugin", ["plugin", "install", `${PLUGIN}@${MARKETPLACE_NAME}`, "--scope", scope, "--yes"]],
36
+ ];
37
+
38
+ if (!claudeAvailable())
39
+ return {
40
+ ok: false,
41
+ reason: "no-cli",
42
+ done: [],
43
+ hint: [
44
+ "The `claude` CLI is not on PATH, so the plugin was not installed.",
45
+ "Inside Claude Code, run:",
46
+ ` /plugin marketplace add ${MARKETPLACE}`,
47
+ ` /plugin install ${PLUGIN}@${MARKETPLACE_NAME}`,
48
+ ],
49
+ };
50
+
51
+ if (dry)
52
+ return { ok: true, reason: "dry-run", done: steps.map(([, a]) => `claude ${a.join(" ")}`), hint: [] };
53
+
54
+ const done = [];
55
+ for (const [what, args] of steps) {
56
+ const r = run(args);
57
+ if (r.status !== 0) {
58
+ const err = (r.stderr || r.stdout || "").trim().split("\n").slice(-3).join("\n");
59
+ return {
60
+ ok: false,
61
+ reason: what,
62
+ done,
63
+ hint: [
64
+ `\`claude ${args.join(" ")}\` failed:`,
65
+ ...err.split("\n").map((l) => ` ${l}`),
66
+ "The rules above were still written. Install the plugin by hand inside",
67
+ `Claude Code: /plugin install ${PLUGIN}@${MARKETPLACE_NAME}`,
68
+ ],
69
+ };
70
+ }
71
+ done.push(`claude ${args.join(" ")}`);
72
+ }
73
+ return { ok: true, reason: "installed", done, hint: [] };
74
+ }
package/src/selftest.mjs CHANGED
@@ -77,7 +77,7 @@ try {
77
77
  writeFileSync(join(r2, ".claude/rules/theming.md"),
78
78
  '---\ndescription: Theming\npaths:\n - "src/**/*.tsx"\n---\n\nUse the token set.\n');
79
79
 
80
- const i = cli(["init"], r2);
80
+ const i = cli(["init", "--no-plugin"], r2);
81
81
  ok(i.status === 0, "init exits 0 on a project that already has rules");
82
82
  ok(readFileSync(join(r2, "AGENTS.md"), "utf8") === realAgents ||
83
83
  readFileSync(join(r2, ".agent-os/AGENTS.md"), "utf8") === realAgents,
@@ -85,6 +85,16 @@ try {
85
85
  ok(existsSync(join(r2, ".agent-os/rules/theming.md")), "existing .claude/rules/ are adopted");
86
86
  ok(!existsSync(join(r2, ".agent-os/rules/example.md")), "no example.md when real rules were adopted");
87
87
  ok(!existsSync(join(r2, ".claude/rules/example.md")), "no example.md compiled into the project");
88
+ ok(!i.stdout.includes("Claude Code plugin"), "--no-plugin skips the plugin install");
89
+
90
+ // init offers the plugin when not told otherwise; --dry-run proves the
91
+ // commands without running them against the machine's real config.
92
+ const r4 = mkdtempSync(join(tmpdir(), "agent-os-plug-"));
93
+ const pi = cli(["init", "--dry-run"], r4);
94
+ ok(pi.stdout.includes("Claude Code plugin"), "init sets up the Claude Code plugin by default");
95
+ ok(/marketplace add sayansr26\/agent-os|plugin marketplace add|not on PATH/.test(pi.stdout),
96
+ "init names the marketplace step or says why it could not run it");
97
+ rmSync(r4, { recursive: true, force: true });
88
98
 
89
99
  // A hand-written file at a generated path must survive a sync.
90
100
  const r3 = mkdtempSync(join(tmpdir(), "agent-os-guard-"));