@sayansr26/agent-os 0.6.1 → 0.6.3

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.
Files changed (33) hide show
  1. package/.claude-plugin/marketplace.json +13 -1
  2. package/CHANGELOG.md +44 -0
  3. package/README.md +36 -2
  4. package/cursor/hooks/git-guard.mjs +40 -0
  5. package/cursor/skills/agent-os-init/SKILL.md +116 -0
  6. package/package.json +4 -4
  7. package/plugins/agent-os/.claude-plugin/plugin.json +3 -3
  8. package/plugins/agent-os/hooks/pre-edit.mjs +36 -7
  9. package/plugins/agent-os/hooks/session-resume.mjs +29 -15
  10. package/plugins/auto-handoff/.claude-plugin/plugin.json +49 -0
  11. package/plugins/auto-handoff/LICENSE +21 -0
  12. package/plugins/auto-handoff/README.md +131 -0
  13. package/plugins/auto-handoff/UPSTREAM.md +22 -0
  14. package/plugins/auto-handoff/hooks/brief.ts +190 -0
  15. package/plugins/auto-handoff/hooks/config.ts +64 -0
  16. package/plugins/auto-handoff/hooks/hooks.json +1 -0
  17. package/plugins/auto-handoff/hooks/panel.tsx +45 -0
  18. package/plugins/auto-handoff/hooks/register.tsx +615 -0
  19. package/plugins/auto-handoff/hooks/server.ts +32 -0
  20. package/plugins/auto-handoff/hooks/templates.ts +17 -0
  21. package/plugins/auto-handoff/hooks/viewer.ts +173 -0
  22. package/plugins/auto-handoff/statusline/handoff-link.sh +15 -0
  23. package/plugins/auto-handoff/templates/brief.md +34 -0
  24. package/plugins/auto-handoff/templates/instructions.md +10 -0
  25. package/plugins/auto-handoff/tests/auto-handoff.test.tsx +931 -0
  26. package/plugins/auto-handoff/tests/seed-row.test.tsx +58 -0
  27. package/plugins/auto-handoff/tests/viewer.test.ts +111 -0
  28. package/plugins/auto-handoff/tsconfig.json +4 -0
  29. package/src/cli.mjs +57 -12
  30. package/src/cursor.mjs +167 -0
  31. package/src/plugin.mjs +5 -4
  32. package/src/selftest.mjs +65 -1
  33. package/src/targets.mjs +18 -11
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Context engineering and agent tooling for Claude Code.",
9
- "version": "0.5.0"
9
+ "version": "0.5.1"
10
10
  },
11
11
  "plugins": [
12
12
  {
@@ -33,6 +33,18 @@
33
33
  "effort",
34
34
  "mod"
35
35
  ]
36
+ },
37
+ {
38
+ "name": "auto-handoff",
39
+ "source": "./plugins/auto-handoff",
40
+ "description": "Hands a long session off to a fresh one instead of auto-compacting: Haiku writes a structured brief, the mod runs /clear and seeds the new session with it. Vendored from alexknowshtml/claude-auto-handoff (MIT).",
41
+ "category": "productivity",
42
+ "keywords": [
43
+ "context",
44
+ "handoff",
45
+ "compaction",
46
+ "mod"
47
+ ]
36
48
  }
37
49
  ]
38
50
  }
package/CHANGELOG.md CHANGED
@@ -5,6 +5,50 @@ 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.6.3] — 2026-10-31
9
+
10
+ agent-os plugin 0.5.1 (hooks gain a Cursor mode; Claude Code behaviour
11
+ unchanged), auto-route unchanged (0.1.0), auto-handoff unchanged (0.8.6).
12
+
13
+ ### Added
14
+ - **The agent-os layer for Cursor.** With `cursor` as a target, `init` and
15
+ `sync` compile the seven agents to `.cursor/agents/agent-os-<name>.md`, the
16
+ three skills to `/agent-os-init`, `/agent-os-map` and `/agent-os-memory`, the
17
+ session-start and pre-edit hooks plus git write protection to
18
+ `.cursor/hooks/agent-os/` (registered in `.cursor/hooks.json`, merged), and
19
+ the agent contract to an always-on `.cursor/rules/agent-os-agents.mdc`.
20
+ Subagent memory uses `.claude/agent-memory/agent-os-<name>/`, the directories
21
+ Claude Code's plugin agents use. `{ "cursor": { "agentOs": false } }` or
22
+ `{ "cursor": { "gitProtection": false } }` in `.agent-os/config.json` opt out.
23
+ - `session-resume.mjs --cursor` and `pre-edit.mjs --cursor` answer in Cursor's
24
+ hook protocol (`additional_context`; `permission` with an explicit `allow`).
25
+
26
+ ### Fixed
27
+ - **`init` in a project that does not target Claude Code** no longer prints
28
+ Claude Code's SETUP block or ends `fresh → repair` with a to-do list of
29
+ CLAUDE.md, `.claude/settings.json` and plugin items — including one telling
30
+ the user to run `init` again. It now ends with what it compiled for.
31
+
32
+ ## [0.6.2] — 2026-10-22
33
+
34
+ agent-os plugin unchanged (0.5.0), auto-route unchanged (0.1.0). New plugin
35
+ auto-handoff 0.8.6 in the same marketplace.
36
+
37
+ ### Added
38
+ - **auto-handoff, a third plugin in the `sayan-plugins` marketplace**, vendored
39
+ from [alexknowshtml/claude-auto-handoff](https://github.com/alexknowshtml/claude-auto-handoff)
40
+ by Alex Hillman (MIT, commit `8d762ce`). In place of auto-compact, Haiku
41
+ writes a structured brief, the mod runs `/clear`, and the fresh session is
42
+ seeded with a pointer to the brief. `/plugin install auto-handoff@sayan-plugins`.
43
+ The brief viewer server is **off** by default here (upstream serves on the
44
+ Tailscale IP); `plugins/auto-handoff/UPSTREAM.md` lists every local change.
45
+ - **`agent-os init` installs and updates auto-handoff** with the other two;
46
+ `--no-auto-handoff` leaves it out.
47
+
48
+ ### Changed
49
+ - `scripts/validate-plugin.mjs` lets a mod carry a listing `icon.png`, as the
50
+ agent-os plugin now does.
51
+
8
52
  ## [0.6.1] — 2026-10-21
9
53
 
10
54
  agent-os plugin unchanged (0.5.0). New plugin auto-route 0.1.0 in the same
package/README.md CHANGED
@@ -110,14 +110,38 @@ Generated files carry a banner. Edit `.agent-os/`, run `sync`, never edit the ou
110
110
 
111
111
  Being straight about this matters more than the feature list.
112
112
 
113
- **Hooks don't port.** Every tool differs in events *and* control protocol — Cursor returns JSON `permission`, Windsurf uses exit codes, Claude Code uses JSON. There is no honest common denominator, so `agent-os` doesn't invent one.
113
+ **Hooks don't port generically.** Every tool differs in events *and* control protocol — Cursor returns JSON `permission`, Windsurf uses exit codes, Claude Code uses JSON. There is no honest common denominator, so `agent-os` doesn't invent one for your hooks. Its own hooks are written once per tool: Claude Code (the plugin) and Cursor (see below).
114
114
 
115
- **Memory doesn't port.** Only Claude Code (auto memory) and Windsurf have native per-session memory. Kilo deprecated its Memory Bank in favour of `AGENTS.md`; Cline's is a community methodology, not a feature. So the architecture map — the thing that makes *"change the login flow from email to OTP"* run off known structure — works in Claude Code and nowhere else yet.
115
+ **Memory doesn't port generically.** Only Claude Code (auto memory) and Windsurf have native per-session memory. Kilo deprecated its Memory Bank in favour of `AGENTS.md`; Cline's is a community methodology, not a feature. So the architecture map — the thing that makes *"change the login flow from email to OTP"* run off known structure — works in Claude Code and Cursor, whose subagents are told where their memory lives, and nowhere else yet.
116
116
 
117
117
  **Antigravity's rules target is best effort.** Its docs state a rule is "simply a Markdown file" and never show frontmatter; glob activation is configured in the Customizations panel. So `agent-os` writes valid Markdown and states the intended scope in a comment, rather than inventing a `globs:` key that may silently do nothing.
118
118
 
119
119
  ---
120
120
 
121
+ ## Cursor
122
+
123
+ When Cursor is a target, `init` and `sync` also compile the agent-os layer into the places Cursor reads it — the same agents, skills and hooks the Claude Code plugin ships:
124
+
125
+ | Claude Code (plugin) | Cursor (generated) |
126
+ |---|---|
127
+ | agents `agent-os:<name>` | subagents `.cursor/agents/agent-os-<name>.md` |
128
+ | `/agent-os:init`, `/agent-os:map`, `/agent-os:memory` | `/agent-os-init`, `/agent-os-map`, `/agent-os-memory` in `.cursor/skills/` |
129
+ | SessionStart hook | `sessionStart` hook → `additional_context` |
130
+ | PreToolUse guard on generated files + cartographer reminder | `preToolUse` hook on `Write` |
131
+ | git write protection in `permissions.deny` | `beforeShellExecution` hook refusing the same git commands |
132
+ | "Agents in this project" section of `CLAUDE.md` | `.cursor/rules/agent-os-agents.mdc`, always applied |
133
+ | per-agent memory, `.claude/agent-memory/agent-os-<name>/` | the same directories — one map for both tools |
134
+
135
+ Hook scripts live in `.cursor/hooks/agent-os/` and are registered in `.cursor/hooks.json`, merged so your own hooks stay. Everything carries the generated banner, so `check` catches drift and the pre-edit hook blocks hand edits; `sync` after upgrading the package is the Cursor equivalent of `claude plugin update`. Reload the Cursor window after `init`, then run `/agent-os-init` in Agent chat.
136
+
137
+ ```json
138
+ { "targets": ["cursor"], "cursor": { "agentOs": true, "gitProtection": true } }
139
+ ```
140
+
141
+ `agentOs: false` stops generating the layer and removes its hooks from `hooks.json` (delete the `agent-os-*` files yourself); `gitProtection: false` drops only the git hook. What does not carry over: Claude Code's auto memory, task tools (Cursor has its own to-do list), and the auto-route and auto-handoff mods.
142
+
143
+ ---
144
+
121
145
  ## The Claude Code plugin
122
146
 
123
147
  The deeper context-engineering work — the architecture map, seven coordinated agents, per-agent memory, and the hooks that make Claude use them — ships as a Claude Code plugin in this repo:
@@ -162,6 +186,16 @@ claude plugin test plugins/auto-route # tests/route.test.ts
162
186
 
163
187
  Once the engine loads it, it writes `.claude-plugin/types/` (git-ignored) for the editor; `tsc -p plugins/auto-route` type-checks it after that. Bump `version` in its `plugin.json` when shipping a change so installs update.
164
188
 
189
+ ### auto-handoff
190
+
191
+ A third plugin, vendored from [alexknowshtml/claude-auto-handoff](https://github.com/alexknowshtml/claude-auto-handoff) by Alex Hillman (MIT). Instead of auto-compacting at the context limit, it has Haiku write a structured brief, runs `/clear`, and seeds the fresh session with a pointer to the brief, so work carries on with a clean context. Settings (threshold, templates, ignored files) are rows in `/config`.
192
+
193
+ ```
194
+ /plugin install auto-handoff@sayan-plugins
195
+ ```
196
+
197
+ `agent-os init` installs it too; `--no-auto-handoff` leaves it out. One change from upstream: the brief viewer server is **off** by default here (upstream serves pages on your Tailscale IP); set `viewer` in `/config` to turn it on. `plugins/auto-handoff/UPSTREAM.md` records the upstream commit, the local changes and how to sync. Develop it the same way as auto-route: `claude --plugin-dir plugins/auto-handoff`, `claude plugin validate|test plugins/auto-handoff`.
198
+
165
199
  ---
166
200
 
167
201
  ## Design rules
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Cursor beforeShellExecution hook — git write protection.
4
+ *
5
+ * Claude Code gets this as `permissions.deny` rules in .claude/settings.json.
6
+ * Cursor has no deny list, so the same rules (GIT_DENY, generated next to this
7
+ * file from settings.mjs) are enforced here: any git command that changes the
8
+ * repository is denied, read-only inspection is allowed.
9
+ *
10
+ * The hook is registered with a `git` matcher, so it only sees commands that
11
+ * mention git. Cursor reads a missing or invalid response as a block, so every
12
+ * exit answers explicitly.
13
+ */
14
+ import { readFileSync } from "node:fs";
15
+ import { GIT_DENY } from "../lib/git-deny.mjs";
16
+
17
+ const answer = (o) => { process.stdout.write(JSON.stringify(o)); process.exit(0); };
18
+
19
+ // "Bash(git push *)" -> "git push" plus anything after a space;
20
+ // "Bash(git -C*)" -> anything starting "git -C".
21
+ const RULES = GIT_DENY.map((r) => r.replace(/^Bash\(/, "").replace(/\)$/, "")).map((r) =>
22
+ r.endsWith(" *") ? { prefix: r.slice(0, -2), spaced: true } : { prefix: r.replace(/\*$/, ""), spaced: false });
23
+
24
+ const denied = (segment) => {
25
+ const cmd = segment.trim().replace(/\s+/g, " ").replace(/^(?:\w+=\S*\s+)+/, "");
26
+ return RULES.find((r) => r.spaced ? cmd === r.prefix || cmd.startsWith(`${r.prefix} `) : cmd.startsWith(r.prefix));
27
+ };
28
+
29
+ let command = "";
30
+ try { command = String(JSON.parse(readFileSync(0, "utf8") || "{}").command ?? ""); } catch { answer({ permission: "allow" }); }
31
+
32
+ // Check every command in a chain: `cd x && git push` is still a push.
33
+ for (const segment of command.split(/&&|\|\||[;|\n]|\$\(|`/)) {
34
+ const hit = denied(segment);
35
+ if (!hit) continue;
36
+ const text = `agent-os blocks git commands that change the repository (${hit.prefix}${hit.spaced ? " …" : "…"}). ` +
37
+ "Read-only git (status, log, diff, show, blame) is allowed. Ask the user to run this one themselves.";
38
+ answer({ permission: "deny", user_message: text, agent_message: text });
39
+ }
40
+ answer({ permission: "allow" });
@@ -0,0 +1,116 @@
1
+ ---
2
+ name: agent-os-init
3
+ description: Set up or repair this project's agent-os context layer in Cursor. Checks the generated rules, subagents and hooks against .agent-os/, inspects agent memory, then establishes, maps, migrates or maintains the layer. Use for "set up agent-os", "agent-os init", "initialise this project", "bootstrap my context setup", "migrate off memory-bank or .cursorrules", "my AGENTS.md is too big", or when starting work in a repo with no setup.
4
+ ---
5
+
6
+ # agent-os for Cursor — set up or repair
7
+
8
+ The Cursor counterpart of Claude Code's `/agent-os:init`. Same layout, same
9
+ agents, same memory — only the file names differ.
10
+
11
+ | Tier | Lives in | Startup cost | Holds |
12
+ |---|---|---|---|
13
+ | Always-loaded | `AGENTS.md` (from `.agent-os/AGENTS.md`), ≤200 lines | small, fixed | Only what is true in *every* session |
14
+ | On-demand | `.cursor/rules/*.mdc` with `globs:` (from `.agent-os/rules/*.md` with `paths:`) | **zero** | Durable facts scoped to some files |
15
+ | Explored | `.claude/agent-memory/agent-os-feature-cartographer/` | **zero** (subagent context) | How each feature is actually built |
16
+ | Task state | the project's own vault / handoff file | zero (read selectively) | What is in flight right now |
17
+
18
+ Agent memory stays under `.claude/agent-memory/` on purpose: Claude Code's
19
+ agent-os agents use the same directories, so a team on both tools shares one map.
20
+
21
+ **Storing a fact and loading a fact are different acts.** Anything that makes
22
+ them the same act — a handbook `AGENTS.md`, a memory bank read at every startup,
23
+ an always-apply rule that only matters for one directory — is the bug.
24
+
25
+ ## Arguments
26
+
27
+ | Argument | Do |
28
+ |---|---|
29
+ | *(none)* | The full pass below |
30
+ | `audit` | Steps 1 and 3 only. Change nothing. |
31
+
32
+ `/agent-os-map` builds the architecture map; `/agent-os-memory` inspects and
33
+ repairs what the project remembers.
34
+
35
+ ## Step 1 — Read the state
36
+
37
+ Three commands. Do not rediscover this with a dozen file reads — they are
38
+ deterministic:
39
+
40
+ ```bash
41
+ npx -y @sayansr26/agent-os detect
42
+ npx -y @sayansr26/agent-os check
43
+ npx -y @sayansr26/agent-os memory
44
+ ```
45
+
46
+ `check` lists every generated file — rules, `AGENTS.md`, the `agent-os-*`
47
+ subagents and skills, the hook scripts and `.cursor/hooks.json` — as `ok`,
48
+ `MISSING` or `DRIFTED`. `memory` lists every agent's memory, which features are
49
+ mapped, and a health list.
50
+
51
+ **Show the output to the user before you change anything.**
52
+
53
+ ## Step 2 — Fresh setup or repair?
54
+
55
+ | State | Do |
56
+ |---|---|
57
+ | no `.agent-os/` | `npx -y @sayansr26/agent-os init`, then carry on with Step 3 |
58
+ | `check` reports `MISSING` or `DRIFTED` | `npx -y @sayansr26/agent-os sync` — it rewrites only what it generated |
59
+ | `sync` names files it did not generate | stop and ask: move their content into `.agent-os/` (preferred), or `--force` after a commit |
60
+ | everything `ok` | nothing to repair |
61
+
62
+ Never edit a generated file by hand. They carry a banner, the pre-edit hook
63
+ blocks the edit, and the next `sync` would revert it. Edit `.agent-os/` and sync.
64
+
65
+ ## Step 3 — Decide what this run is for
66
+
67
+ Judge from what Step 1 printed and a look at the source tree:
68
+
69
+ | Mode | Evidence | Do |
70
+ |---|---|---|
71
+ | `TOO-EARLY` | Barely any source (under ~5 files) | **Build nothing.** A layer over an empty project is invented conventions. |
72
+ | `ESTABLISH` | `.agent-os/AGENTS.md` is still the placeholder, or the only rule is `example.md` | `references/establishing.md` — build the layer *from the code* |
73
+ | `MAP` | Layer real, but `memory` shows no `_architecture.md` | `references/establishing.md`, "Map the architecture" — or `/agent-os-map` |
74
+ | `MIGRATE` | `.cursorrules`, `memory-bank/`, `.serena/memories/`, or an always-apply rule that only concerns part of the tree | fix it; `references/migrating.md` for legacy stores |
75
+ | `MAINTAIN` | Real layer, architecture mapped | report and stop |
76
+
77
+ `references/changing-a-feature.md` is the workflow that pays all this back —
78
+ point the user at it once the layer exists.
79
+
80
+ The references were written for Claude Code. Read them through this table:
81
+
82
+ | They say | In Cursor |
83
+ |---|---|
84
+ | `CLAUDE.md` | `.agent-os/AGENTS.md` (compiled to `AGENTS.md`). Draft it yourself from the code — there is no `/init` to run first. |
85
+ | `.claude/rules/*.md` | `.agent-os/rules/*.md`, then `sync` (compiled to `.cursor/rules/*.mdc`) |
86
+ | `agent-os:<name>` | the `agent-os-<name>` subagent |
87
+ | `/agent-os:init`, `/agent-os:map`, `/agent-os:memory` | `/agent-os-init`, `/agent-os-map`, `/agent-os-memory` |
88
+ | `node "${CLAUDE_PLUGIN_ROOT}/…/<script>.mjs"` | `npx -y @sayansr26/agent-os <script>` |
89
+ | "Wire the agents into CLAUDE.md" (Step 4b) | already done: `.cursor/rules/agent-os-agents.mdc` is always applied — skip it |
90
+ | the settings pass, `permissions.deny`, task tools | see "Git write protection" below; Cursor has its own to-do list |
91
+
92
+ ## Step 4 — Verify by re-running
93
+
94
+ Run `check` and `memory` again and show before and after: generated files all
95
+ `ok`, features mapped, health findings. Do not declare success on vibes — the
96
+ commands produce the numbers, so quote them.
97
+
98
+ ## Git write protection
99
+
100
+ Claude Code gets git write protection as `permissions.deny` rules. Cursor has no
101
+ deny list, so `sync` registers a `beforeShellExecution` hook in
102
+ `.cursor/hooks.json` (`.cursor/hooks/agent-os/hooks/git-guard.mjs`) that refuses
103
+ the same commands: every git command that changes the repository. Read-only git
104
+ stays available. `references/git-permissions.md` explains each rule.
105
+
106
+ If the user wants it off: `{ "cursor": { "gitProtection": false } }` in
107
+ `.agent-os/config.json`, then `sync`. Say what that gives up.
108
+
109
+ ## What this skill will not do
110
+
111
+ - It will not write facts it has not verified in the repo. An invented rule is
112
+ worse than a missing one, because it will be trusted.
113
+ - It will not delete a legacy store before its content has landed in `.agent-os/`.
114
+ - It will not hand-edit generated files, or another agent's memory.
115
+ - It will not add a memory MCP server. The common failure is that nothing was
116
+ *written*, which a graph does not fix and the cartographer's memory does.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sayansr26/agent-os",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
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",
@@ -34,6 +34,7 @@
34
34
  "bin",
35
35
  "src",
36
36
  "plugins",
37
+ "cursor",
37
38
  ".claude-plugin",
38
39
  "README.md",
39
40
  "LICENSE",
@@ -43,7 +44,6 @@
43
44
  "access": "public"
44
45
  },
45
46
  "scripts": {
46
- "test": "node scripts/validate-plugin.mjs && node src/selftest.mjs",
47
- "prepublishOnly": "npm test"
47
+ "test": "node scripts/validate-plugin.mjs && node src/selftest.mjs"
48
48
  }
49
- }
49
+ }
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "agent-os",
3
3
  "description": "A context-engineered agent system for Claude Code. Ships a durable per-project memory layer, a cartographer that maps your codebase once and remembers it, and a coordinated agent set that reads your project's rules rather than hardcoding them.",
4
- "version": "0.5.0",
4
+ "version": "0.5.1",
5
5
  "author": {
6
6
  "name": "Sayan Choudhury",
7
7
  "email": "sayan.choudhury.in@gmail.com"
8
8
  },
9
9
  "homepage": "https://github.com/sayansr26/agent-os",
10
10
  "repository": "https://github.com/sayansr26/agent-os",
11
- "privacyPolicyUrl": "https://s3techpulse.com/privacy-policy",
12
- "supportUrl": "https://s3techpulse.com/support",
11
+ "privacyPolicyUrl": "https://github.com/sayansr26/agent-os/blob/master/PRIVACY.md",
12
+ "supportUrl": "https://github.com/sayansr26/agent-os/issues",
13
13
  "license": "MIT",
14
14
  "keywords": [
15
15
  "context-engineering",
@@ -10,11 +10,16 @@
10
10
  * Deny, and name the source to edit instead.
11
11
  * 2. Cartographer reminder (advisory, never blocks). The first edit to an
12
12
  * existing feature with no cartographer map adds a note telling Claude to
13
- * ask agent-os:feature-cartographer first. Once per feature per session.
13
+ * ask agent-os:feature-cartographer first (agent-os-feature-cartographer
14
+ * in Cursor). Once per feature per session.
14
15
  *
15
16
  * Stays silent for new (untracked) features, paths outside a feature
16
17
  * directory, and anything it cannot work out. Turn the reminder off with
17
18
  * `{ "claude": { "cartographerReminder": false } }` in `.agent-os/config.json`.
19
+ *
20
+ * `--cursor` is the same hook as Cursor's preToolUse on Write. Cursor reads a
21
+ * missing or empty response as a block, so in that mode every exit answers
22
+ * explicitly: `allow` (with the reminder as `agent_message`) or `deny`.
18
23
  */
19
24
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
20
25
  import { tmpdir } from "node:os";
@@ -24,17 +29,33 @@ import { claudeConfig, featureOf, findMap, mapFiles, tracked } from "../lib/feat
24
29
  // Must stay a substring of BANNER in src/source.mjs.
25
30
  const BANNER = "agent-os: generated from .agent-os/";
26
31
 
27
- const quiet = () => process.exit(0);
32
+ const CURSOR = process.argv.includes("--cursor");
33
+ const agent = (name) => `agent-os${CURSOR ? "-" : ":"}${name}`;
34
+
35
+ const quiet = () => {
36
+ if (CURSOR) process.stdout.write(JSON.stringify({ permission: "allow" }));
37
+ process.exit(0);
38
+ };
28
39
  const emit = (hookSpecificOutput) => {
29
- process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", ...hookSpecificOutput } }));
40
+ if (CURSOR) {
41
+ const deny = hookSpecificOutput.permissionDecision === "deny";
42
+ const text = deny ? hookSpecificOutput.permissionDecisionReason : hookSpecificOutput.additionalContext;
43
+ process.stdout.write(JSON.stringify(deny
44
+ ? { permission: "deny", user_message: text, agent_message: text }
45
+ : { permission: "allow", agent_message: text }));
46
+ } else
47
+ process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", ...hookSpecificOutput } }));
30
48
  process.exit(0);
31
49
  };
32
50
 
33
51
  let payload;
34
52
  try { payload = JSON.parse(readFileSync(0, "utf8") || "{}"); } catch { quiet(); }
35
53
 
36
- const root = process.env.CLAUDE_PROJECT_DIR || payload?.cwd || process.cwd();
37
- const raw = String(payload?.tool_input?.file_path ?? "");
54
+ // Claude Code may run inside Cursor's terminal, so CURSOR_PROJECT_DIR counts only in --cursor mode.
55
+ const root = (CURSOR && process.env.CURSOR_PROJECT_DIR) || process.env.CLAUDE_PROJECT_DIR || payload?.cwd || process.cwd();
56
+ // Cursor does not document its Write tool's input, so take the usual names.
57
+ const input = payload?.tool_input || {};
58
+ const raw = String(input.file_path ?? input.path ?? input.target_file ?? input.filePath ?? "");
38
59
  if (!raw) quiet();
39
60
  const abs = isAbsolute(raw) ? raw : join(root, raw);
40
61
  const rel = relative(root, abs).replace(/\\/g, "/");
@@ -44,6 +65,14 @@ if (rel.startsWith("..")) quiet();
44
65
  try {
45
66
  if (existsSync(join(root, ".agent-os")) && existsSync(abs) && readFileSync(abs, "utf8").includes(BANNER)) {
46
67
  let source = ".agent-os/";
68
+ // Cursor's agent-os layer is compiled from the package, not from .agent-os/.
69
+ if (/^\.cursor\/(?:agents\/agent-os-|skills\/agent-os-|hooks\/agent-os\/|rules\/agent-os-agents\.mdc$)/.test(rel))
70
+ emit({
71
+ permissionDecision: "deny",
72
+ permissionDecisionReason:
73
+ `${rel} is part of the agent-os layer for Cursor and the next sync would revert this edit. ` +
74
+ `To customise it, set \`{ "cursor": { "agentOs": false } }\` in .agent-os/config.json and keep your own copy.`,
75
+ });
47
76
  const rule = rel.match(/^\.(?:claude|clinerules|cursor|windsurf|kilocode|gemini|agents?)\/(?:rules\/)?(.+?)\.(?:md|mdc)$/);
48
77
  if (rel === "AGENTS.md") source = ".agent-os/AGENTS.md";
49
78
  else if (rule && existsSync(join(root, ".agent-os/rules", `${basename(rule[1])}.md`)))
@@ -67,7 +96,7 @@ try {
67
96
 
68
97
  const dir = join(tmpdir(), "agent-os-reminders");
69
98
  mkdirSync(dir, { recursive: true });
70
- const session = String(payload?.session_id ?? "nosession").replace(/[^\w-]/g, "");
99
+ const session = String(payload?.session_id ?? payload?.conversation_id ?? "nosession").replace(/[^\w-]/g, "");
71
100
  const marker = join(dir, `${session}--${feature.dir.replace(/[^\w-]/g, "_")}`);
72
101
  if (existsSync(marker)) quiet();
73
102
  writeFileSync(marker, "");
@@ -75,7 +104,7 @@ try {
75
104
  emit({
76
105
  additionalContext:
77
106
  `agent-os (advisory): ${feature.dir}/ is an existing feature with no cartographer map. ` +
78
- `Before changing it further, ask agent-os:feature-cartographer how "${feature.name}" is built — ` +
107
+ `Before changing it further, ask ${agent("feature-cartographer")} how "${feature.name}" is built — ` +
79
108
  `it answers and files the map for every later session. This applies in plan mode too ` +
80
109
  `(ask it read-only; it writes the map after plan mode ends). Shown once per feature per session.`,
81
110
  });
@@ -16,6 +16,9 @@
16
16
  * - The snapshot is hard-capped (MAX_LINES); the contract is fixed-size.
17
17
  * - Fails silent: any error exits 0, so a broken hook never blocks a session.
18
18
  * - Read-only. Runs git for reading only; never writes, never mutates.
19
+ *
20
+ * `--cursor` is the same hook as Cursor's sessionStart: agent names in Cursor's
21
+ * `agent-os-<name>` form, and the text wrapped as `{ additional_context }`.
19
22
  */
20
23
 
21
24
  import { execSync } from "node:child_process";
@@ -24,7 +27,13 @@ import { join } from "node:path";
24
27
  import { coverage } from "../lib/features.mjs";
25
28
 
26
29
  const MAX_LINES = 40;
27
- const cwd = process.env.CLAUDE_PROJECT_DIR || process.cwd();
30
+ const CURSOR = process.argv.includes("--cursor");
31
+ // Claude Code may run inside Cursor's terminal, so CURSOR_PROJECT_DIR counts only in --cursor mode.
32
+ const cwd = (CURSOR && process.env.CURSOR_PROJECT_DIR) || process.env.CLAUDE_PROJECT_DIR || process.cwd();
33
+ // Claude Code namespaces plugin agents and skills with `:`; Cursor names allow
34
+ // only letters and hyphens, so its generated copies use `-`.
35
+ const agent = (name) => `agent-os${CURSOR ? "-" : ":"}${name}`;
36
+ const skill = (name) => `/agent-os${CURSOR ? "-" : ":"}${name}`;
28
37
  const out = [];
29
38
 
30
39
  const sh = (cmd) => {
@@ -135,13 +144,13 @@ function snapshot() {
135
144
 
136
145
  // The agents and what triggers each. `subagent_type` is the namespaced name.
137
146
  const AGENTS = [
138
- ["agent-os:feature-cartographer", "BEFORE changing any existing feature — ask how it is built (files, state, API, blast radius). It answers from its map, or maps the feature and files the map."],
139
- ["agent-os:architect", "before a new subsystem, a cross-module change, or a data model others will depend on."],
140
- ["agent-os:builder", "to write the code once the shape is settled; it enforces the project's rules while writing."],
141
- ["agent-os:tester", "after building, to verify the change actually works."],
142
- ["agent-os:reviewer", "before calling any change done or opening a PR."],
143
- ["agent-os:documenter", "once work is verified, to update the changelog, task state and docs the change invalidated."],
144
- ["agent-os:orchestrator", "for work spanning several of the above, or that cannot be stated in one sentence."],
147
+ ["feature-cartographer", "BEFORE changing any existing feature — ask how it is built (files, state, API, blast radius). It answers from its map, or maps the feature and files the map."],
148
+ ["architect", "before a new subsystem, a cross-module change, or a data model others will depend on."],
149
+ ["builder", "to write the code once the shape is settled; it enforces the project's rules while writing."],
150
+ ["tester", "after building, to verify the change actually works."],
151
+ ["reviewer", "before calling any change done or opening a PR."],
152
+ ["documenter", "once work is verified, to update the changelog, task state and docs the change invalidated."],
153
+ ["orchestrator", "for work spanning several of the above, or that cannot be stated in one sentence."],
145
154
  ];
146
155
 
147
156
  function contract() {
@@ -153,15 +162,17 @@ function contract() {
153
162
  return [
154
163
  "## agent-os",
155
164
  "",
156
- "The agent-os plugin is installed but this project is not set up. If the user starts feature work, suggest `/agent-os:init` once.",
165
+ `agent-os is installed but this project is not set up. If the user starts feature work, suggest \`${skill("init")}\` once.`,
157
166
  ];
158
167
 
159
168
  const lines = [
160
169
  "## agent-os is active in this project",
161
170
  "",
162
- "These agents are how work is done here — use them without being asked (Agent tool, `subagent_type` as shown):",
171
+ CURSOR
172
+ ? "These subagents are how work is done here — delegate to them without being asked, by the names shown:"
173
+ : "These agents are how work is done here — use them without being asked (Agent tool, `subagent_type` as shown):",
163
174
  ];
164
- for (const [name, when] of AGENTS) lines.push(`- \`${name}\` — ${when}`);
175
+ for (const [name, when] of AGENTS) lines.push(`- \`${agent(name)}\` — ${when}`);
165
176
 
166
177
  const cov = coverage(cwd);
167
178
  lines.push("");
@@ -173,12 +184,14 @@ function contract() {
173
184
  } else if (cov.dir) {
174
185
  lines.push(`Cartographer maps are indexed in \`${cov.dir.slice(cwd.length + 1)}/MEMORY.md\`.`);
175
186
  }
176
- if (!cov.architecture) lines.push("No architecture map yet — `/agent-os:map` builds it.");
187
+ if (!cov.architecture) lines.push(`No architecture map yet — \`${skill("map")}\` builds it.`);
177
188
 
178
189
  lines.push("");
179
- lines.push("Plan mode: its \"Explore agents only\" phase does not replace the cartographer. Ask the cartographer read-only during planning (it will not write), and let it file its map once plan mode ends.");
190
+ lines.push(CURSOR
191
+ ? "Plan mode: ask the cartographer read-only during planning (it will not write), and let it file its map once plan mode ends."
192
+ : "Plan mode: its \"Explore agents only\" phase does not replace the cartographer. Ask the cartographer read-only during planning (it will not write), and let it file its map once plan mode ends.");
180
193
  if (existsSync(join(cwd, ".agent-os/rules")))
181
- lines.push("Rules: `.claude/rules/*` and other tool copies are generated. Edit `.agent-os/rules/`, then run `npx @sayansr26/agent-os sync`.");
194
+ lines.push(`Rules: \`${CURSOR ? ".cursor/rules/*" : ".claude/rules/*"}\` and other tool copies are generated. Edit \`.agent-os/rules/\`, then run \`npx @sayansr26/agent-os sync\`.`);
182
195
  return lines;
183
196
  }
184
197
 
@@ -186,7 +199,8 @@ try {
186
199
  const blocks = [];
187
200
  try { const s = snapshot(); if (s.length) blocks.push(s.join("\n")); } catch {}
188
201
  try { blocks.push(contract().join("\n")); } catch {}
189
- if (blocks.length) process.stdout.write(blocks.join("\n\n") + "\n");
202
+ if (CURSOR) process.stdout.write(JSON.stringify(blocks.length ? { additional_context: blocks.join("\n\n") } : {}));
203
+ else if (blocks.length) process.stdout.write(blocks.join("\n\n") + "\n");
190
204
  } catch {
191
205
  // Never let a resume hook break a session.
192
206
  }
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "auto-handoff",
3
+ "version": "0.8.6",
4
+ "description": "Hands a Claude Code session off to a fresh one before the context fills: a Haiku-written brief, /clear, and a seed that points the new session at the brief.",
5
+ "author": {
6
+ "name": "Alex Hillman",
7
+ "url": "https://github.com/alexknowshtml"
8
+ },
9
+ "repository": "https://github.com/alexknowshtml/claude-auto-handoff",
10
+ "license": "MIT",
11
+ "userConfig": {
12
+ "threshold": {
13
+ "type": "number",
14
+ "title": "Handoff threshold (tokens)",
15
+ "description": "Context size that triggers a handoff. AUTO_HANDOFF_TOKENS overrides it for a test run. A seeded session hands off no sooner than 40k past its starting size; the panel above the prompt says so when this is set lower.",
16
+ "default": 160000
17
+ },
18
+ "maxConsecutiveHandoffs": {
19
+ "type": "number",
20
+ "title": "Handoffs in a row without a message",
21
+ "description": "Handoffs allowed before you type a prompt. Past this the mod pauses until you send one.",
22
+ "default": 2
23
+ },
24
+ "briefTemplate": {
25
+ "type": "string",
26
+ "title": "Brief template file",
27
+ "description": "Markdown file that tells Haiku which sections to write. Created with the defaults on first run; edit it to change the brief.",
28
+ "default": "~/.claude/auto-handoff/brief.md"
29
+ },
30
+ "instructionsTemplate": {
31
+ "type": "string",
32
+ "title": "Instructions template file",
33
+ "description": "Markdown file that tells the fresh session how to read the brief. Created with the defaults on first run.",
34
+ "default": "~/.claude/auto-handoff/instructions.md"
35
+ },
36
+ "ignoreFiles": {
37
+ "type": "string",
38
+ "title": "Ignored files (regex)",
39
+ "description": "Edited files matching this pattern are left out of the brief, such as auto-synced state or caches. Leave blank for none.",
40
+ "default": ""
41
+ },
42
+ "viewer": {
43
+ "type": "string",
44
+ "title": "Viewer address",
45
+ "description": "Where the mod serves a readable page for each brief, as host:port, e.g. 127.0.0.1:3846. \"tailscale\" as the host means this machine's Tailscale IP, so only your tailnet can open it; without Tailscale it serves on 127.0.0.1. Blank (the default here) runs no server; the link is then the local file.",
46
+ "default": ""
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alex Hillman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.