@stigmer/plugin-package 3.15.3-dev.20260916211208

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 (173) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +66 -0
  3. package/detect.d.ts +50 -0
  4. package/detect.d.ts.map +1 -0
  5. package/detect.js +164 -0
  6. package/detect.js.map +1 -0
  7. package/dialects/claude.d.ts +30 -0
  8. package/dialects/claude.d.ts.map +1 -0
  9. package/dialects/claude.js +71 -0
  10. package/dialects/claude.js.map +1 -0
  11. package/dialects/codex.d.ts +18 -0
  12. package/dialects/codex.d.ts.map +1 -0
  13. package/dialects/codex.js +19 -0
  14. package/dialects/codex.js.map +1 -0
  15. package/dialects/cursor.d.ts +23 -0
  16. package/dialects/cursor.d.ts.map +1 -0
  17. package/dialects/cursor.js +63 -0
  18. package/dialects/cursor.js.map +1 -0
  19. package/dialects/manifest.d.ts +109 -0
  20. package/dialects/manifest.d.ts.map +1 -0
  21. package/dialects/manifest.js +194 -0
  22. package/dialects/manifest.js.map +1 -0
  23. package/dialects/open.d.ts +18 -0
  24. package/dialects/open.d.ts.map +1 -0
  25. package/dialects/open.js +47 -0
  26. package/dialects/open.js.map +1 -0
  27. package/documents.d.ts +43 -0
  28. package/documents.d.ts.map +1 -0
  29. package/documents.js +111 -0
  30. package/documents.js.map +1 -0
  31. package/files.d.ts +114 -0
  32. package/files.d.ts.map +1 -0
  33. package/files.js +187 -0
  34. package/files.js.map +1 -0
  35. package/frontmatter.d.ts +51 -0
  36. package/frontmatter.d.ts.map +1 -0
  37. package/frontmatter.js +61 -0
  38. package/frontmatter.js.map +1 -0
  39. package/index.d.ts +19 -0
  40. package/index.d.ts.map +1 -0
  41. package/index.js +17 -0
  42. package/index.js.map +1 -0
  43. package/messages.d.ts +39 -0
  44. package/messages.d.ts.map +1 -0
  45. package/messages.js +135 -0
  46. package/messages.js.map +1 -0
  47. package/normalise/ignored.d.ts +24 -0
  48. package/normalise/ignored.d.ts.map +1 -0
  49. package/normalise/ignored.js +68 -0
  50. package/normalise/ignored.js.map +1 -0
  51. package/normalise/mcp-servers.d.ts +47 -0
  52. package/normalise/mcp-servers.d.ts.map +1 -0
  53. package/normalise/mcp-servers.js +397 -0
  54. package/normalise/mcp-servers.js.map +1 -0
  55. package/normalise/overlay.d.ts +27 -0
  56. package/normalise/overlay.d.ts.map +1 -0
  57. package/normalise/overlay.js +67 -0
  58. package/normalise/overlay.js.map +1 -0
  59. package/normalise/skills.d.ts +31 -0
  60. package/normalise/skills.d.ts.map +1 -0
  61. package/normalise/skills.js +116 -0
  62. package/normalise/skills.js.map +1 -0
  63. package/normalise/sub-agents.d.ts +44 -0
  64. package/normalise/sub-agents.d.ts.map +1 -0
  65. package/normalise/sub-agents.js +168 -0
  66. package/normalise/sub-agents.js.map +1 -0
  67. package/normalise/variables.d.ts +27 -0
  68. package/normalise/variables.d.ts.map +1 -0
  69. package/normalise/variables.js +155 -0
  70. package/normalise/variables.js.map +1 -0
  71. package/outcome.d.ts +46 -0
  72. package/outcome.d.ts.map +1 -0
  73. package/outcome.js +21 -0
  74. package/outcome.js.map +1 -0
  75. package/package.json +40 -0
  76. package/placeholders.d.ts +41 -0
  77. package/placeholders.d.ts.map +1 -0
  78. package/placeholders.js +68 -0
  79. package/placeholders.js.map +1 -0
  80. package/read-plugin-package.d.ts +19 -0
  81. package/read-plugin-package.d.ts.map +1 -0
  82. package/read-plugin-package.js +67 -0
  83. package/read-plugin-package.js.map +1 -0
  84. package/src/__test-utils__/directory-files.ts +29 -0
  85. package/src/__test-utils__/read.ts +55 -0
  86. package/src/__tests__/adversarial.test.ts +434 -0
  87. package/src/__tests__/detect.test.ts +133 -0
  88. package/src/__tests__/files.test.ts +119 -0
  89. package/src/__tests__/fixtures/cursor-plugins/NOTICE +22 -0
  90. package/src/__tests__/fixtures/cursor-plugins/advisor/.cursor-plugin/plugin.json +33 -0
  91. package/src/__tests__/fixtures/cursor-plugins/advisor/CHANGELOG.md +8 -0
  92. package/src/__tests__/fixtures/cursor-plugins/advisor/LICENSE +21 -0
  93. package/src/__tests__/fixtures/cursor-plugins/advisor/README.md +87 -0
  94. package/src/__tests__/fixtures/cursor-plugins/advisor/agents/advisor-subagent.md +48 -0
  95. package/src/__tests__/fixtures/cursor-plugins/advisor/assets/avatar.png +0 -0
  96. package/src/__tests__/fixtures/cursor-plugins/advisor/hooks/capture-response.sh +20 -0
  97. package/src/__tests__/fixtures/cursor-plugins/advisor/hooks/hooks.json +27 -0
  98. package/src/__tests__/fixtures/cursor-plugins/advisor/hooks/lib.sh +61 -0
  99. package/src/__tests__/fixtures/cursor-plugins/advisor/hooks/mark-pending.sh +27 -0
  100. package/src/__tests__/fixtures/cursor-plugins/advisor/hooks/record-consult.sh +41 -0
  101. package/src/__tests__/fixtures/cursor-plugins/advisor/hooks/stop-hook.sh +48 -0
  102. package/src/__tests__/fixtures/cursor-plugins/advisor/skills/advisor/SKILL.md +123 -0
  103. package/src/__tests__/fixtures/cursor-plugins/advisor/skills/advisor/references/briefing-template.md +44 -0
  104. package/src/__tests__/fixtures/cursor-plugins/github/.cursor-plugin/plugin.json +45 -0
  105. package/src/__tests__/fixtures/cursor-plugins/github/CHANGELOG.md +9 -0
  106. package/src/__tests__/fixtures/cursor-plugins/github/LICENSE +21 -0
  107. package/src/__tests__/fixtures/cursor-plugins/github/README.md +64 -0
  108. package/src/__tests__/fixtures/cursor-plugins/github/assets/logo.svg +0 -0
  109. package/src/__tests__/fixtures/cursor-plugins/github/mcp.json +11 -0
  110. package/src/__tests__/fixtures/cursor-plugins/playwright/.cursor-plugin/plugin.json +35 -0
  111. package/src/__tests__/fixtures/cursor-plugins/playwright/CHANGELOG.md +8 -0
  112. package/src/__tests__/fixtures/cursor-plugins/playwright/LICENSE +21 -0
  113. package/src/__tests__/fixtures/cursor-plugins/playwright/README.md +46 -0
  114. package/src/__tests__/fixtures/cursor-plugins/playwright/assets/logo.svg +0 -0
  115. package/src/__tests__/fixtures/cursor-plugins/playwright/mcp.json +8 -0
  116. package/src/__tests__/fixtures/cursor-plugins/salesforce/.cursor-plugin/plugin.json +48 -0
  117. package/src/__tests__/fixtures/cursor-plugins/salesforce/CHANGELOG.md +10 -0
  118. package/src/__tests__/fixtures/cursor-plugins/salesforce/LICENSE +21 -0
  119. package/src/__tests__/fixtures/cursor-plugins/salesforce/README.md +95 -0
  120. package/src/__tests__/fixtures/cursor-plugins/salesforce/assets/logo.svg +0 -0
  121. package/src/__tests__/fixtures/cursor-plugins/salesforce/mcp.json +12 -0
  122. package/src/__tests__/fixtures/cursor-plugins/thermos/.cursor-plugin/plugin.json +32 -0
  123. package/src/__tests__/fixtures/cursor-plugins/thermos/CHANGELOG.md +8 -0
  124. package/src/__tests__/fixtures/cursor-plugins/thermos/LICENSE +21 -0
  125. package/src/__tests__/fixtures/cursor-plugins/thermos/README.md +70 -0
  126. package/src/__tests__/fixtures/cursor-plugins/thermos/agents/thermo-nuclear-code-quality-review-subagent.md +23 -0
  127. package/src/__tests__/fixtures/cursor-plugins/thermos/agents/thermo-nuclear-review-subagent.md +28 -0
  128. package/src/__tests__/fixtures/cursor-plugins/thermos/assets/logo.png +0 -0
  129. package/src/__tests__/fixtures/cursor-plugins/thermos/skills/thermo-nuclear-code-quality-review/SKILL.md +192 -0
  130. package/src/__tests__/fixtures/cursor-plugins/thermos/skills/thermo-nuclear-review/SKILL.md +51 -0
  131. package/src/__tests__/fixtures/cursor-plugins/thermos/skills/thermos/SKILL.md +21 -0
  132. package/src/__tests__/fixtures/cursor-plugins/xero/.cursor-plugin/plugin.json +53 -0
  133. package/src/__tests__/fixtures/cursor-plugins/xero/CHANGELOG.md +9 -0
  134. package/src/__tests__/fixtures/cursor-plugins/xero/LICENSE +21 -0
  135. package/src/__tests__/fixtures/cursor-plugins/xero/README.md +79 -0
  136. package/src/__tests__/fixtures/cursor-plugins/xero/assets/logo.png +0 -0
  137. package/src/__tests__/fixtures/cursor-plugins/xero/mcp.json +16 -0
  138. package/src/__tests__/fixtures.test.ts +198 -0
  139. package/src/__tests__/mcp-servers.test.ts +120 -0
  140. package/src/__tests__/overlay-and-ignored.test.ts +65 -0
  141. package/src/__tests__/skills.test.ts +89 -0
  142. package/src/__tests__/sub-agents.test.ts +111 -0
  143. package/src/__tests__/variables.test.ts +94 -0
  144. package/src/detect.ts +189 -0
  145. package/src/dialects/claude.ts +90 -0
  146. package/src/dialects/codex.ts +24 -0
  147. package/src/dialects/cursor.ts +73 -0
  148. package/src/dialects/manifest.ts +237 -0
  149. package/src/dialects/open.ts +48 -0
  150. package/src/documents.ts +145 -0
  151. package/src/files.ts +213 -0
  152. package/src/frontmatter.ts +70 -0
  153. package/src/index.ts +59 -0
  154. package/src/messages.ts +206 -0
  155. package/src/normalise/ignored.ts +70 -0
  156. package/src/normalise/mcp-servers.ts +427 -0
  157. package/src/normalise/overlay.ts +71 -0
  158. package/src/normalise/skills.ts +126 -0
  159. package/src/normalise/sub-agents.ts +184 -0
  160. package/src/normalise/variables.ts +161 -0
  161. package/src/outcome.ts +122 -0
  162. package/src/placeholders.ts +74 -0
  163. package/src/read-plugin-package.ts +74 -0
  164. package/src/testing.ts +258 -0
  165. package/src/types.ts +189 -0
  166. package/testing.d.ts +106 -0
  167. package/testing.d.ts.map +1 -0
  168. package/testing.js +182 -0
  169. package/testing.js.map +1 -0
  170. package/types.d.ts +152 -0
  171. package/types.d.ts.map +1 -0
  172. package/types.js +19 -0
  173. package/types.js.map +1 -0
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Pins the reader contract and the path discipline: lexical containment of
3
+ * listed and declared paths, the sorted in-memory reader, the index's
4
+ * directory queries, BOM stripping, and the two-sided document cap (the
5
+ * declared size before a read, the returned length after, so a reader that
6
+ * under-declares cannot smuggle bytes past the cap).
7
+ */
8
+
9
+ import { describe, expect, it } from "vitest";
10
+
11
+ import {
12
+ decodeUtf8,
13
+ inMemoryPluginFiles,
14
+ isContainedPath,
15
+ PLUGIN_DOCUMENT_LIMITS,
16
+ PluginFileIndex,
17
+ type PluginFiles,
18
+ resolveDeclaredPath,
19
+ } from "../files.js";
20
+ import { readPluginPackage } from "../read-plugin-package.js";
21
+ import { openPlugin } from "../testing.js";
22
+ import { kindsOf, read } from "../__test-utils__/read.js";
23
+
24
+ describe("isContainedPath", () => {
25
+ it("accepts plugin-relative POSIX paths", () => {
26
+ expect(isContainedPath("plugin.json")).toBe(true);
27
+ expect(isContainedPath("skills/a/SKILL.md")).toBe(true);
28
+ expect(isContainedPath(".cursor-plugin/plugin.json")).toBe(true);
29
+ });
30
+
31
+ it("refuses absolute, parent, dot, empty-segment and backslash paths", () => {
32
+ for (const path of ["", "/etc/passwd", "../x", "a/../b", "a/./b", "a//b", "a\\b", "a/"]) {
33
+ expect(isContainedPath(path), path).toBe(false);
34
+ }
35
+ });
36
+ });
37
+
38
+ describe("resolveDeclaredPath", () => {
39
+ it("normalises ./-prefixed directories and files, and the root spellings", () => {
40
+ expect(resolveDeclaredPath("./skills/")).toEqual({ ok: true, path: "skills" });
41
+ expect(resolveDeclaredPath("./agents/reviewer.md")).toEqual({ ok: true, path: "agents/reviewer.md" });
42
+ expect(resolveDeclaredPath(".")).toEqual({ ok: true, path: "" });
43
+ expect(resolveDeclaredPath("./")).toEqual({ ok: true, path: "" });
44
+ });
45
+
46
+ it("refuses a bare, an escaping and a glob path, each with its own kind", () => {
47
+ expect(resolveDeclaredPath("skills/")).toEqual({ ok: false, kind: "path-not-relative" });
48
+ expect(resolveDeclaredPath("./../other")).toEqual({ ok: false, kind: "path-escapes-root" });
49
+ expect(resolveDeclaredPath("./skills/**/*.md")).toEqual({ ok: false, kind: "path-glob-unsupported" });
50
+ });
51
+ });
52
+
53
+ describe("inMemoryPluginFiles and PluginFileIndex", () => {
54
+ const files = inMemoryPluginFiles(
55
+ new Map<string, string>([
56
+ ["skills/b/SKILL.md", "b"],
57
+ ["skills/a/SKILL.md", "a"],
58
+ ["skills/a/scripts/run.sh", "run"],
59
+ ["plugin.json", "{}"],
60
+ ["agents/x.md", "x"],
61
+ ]),
62
+ );
63
+ const index = new PluginFileIndex(files);
64
+
65
+ it("lists entries sorted by path with their byte sizes", () => {
66
+ expect(files.entries.map((e) => e.path)).toEqual([
67
+ "agents/x.md",
68
+ "plugin.json",
69
+ "skills/a/SKILL.md",
70
+ "skills/a/scripts/run.sh",
71
+ "skills/b/SKILL.md",
72
+ ]);
73
+ expect(files.entries.find((e) => e.path === "skills/a/scripts/run.sh")?.size).toBe(3);
74
+ });
75
+
76
+ it("answers directory queries from the sorted list", () => {
77
+ expect(index.childDirectories("")).toEqual(["agents", "skills"]);
78
+ expect(index.childDirectories("skills")).toEqual(["a", "b"]);
79
+ expect(index.childFiles("")).toEqual(["plugin.json"]);
80
+ expect(index.filesUnder("skills/a")).toEqual(["skills/a/SKILL.md", "skills/a/scripts/run.sh"]);
81
+ expect(index.isDirectory("skills/a/scripts")).toBe(true);
82
+ expect(index.isDirectory("nope")).toBe(false);
83
+ });
84
+
85
+ it("throws on a read of an unlisted path (a reader contract violation, not plugin content)", () => {
86
+ expect(() => files.read("missing")).toThrow(/not listed/);
87
+ });
88
+ });
89
+
90
+ describe("decodeUtf8", () => {
91
+ it("strips a leading byte-order mark and nothing else", () => {
92
+ expect(decodeUtf8(new Uint8Array([0xef, 0xbb, 0xbf, 0x7b, 0x7d]))).toBe("{}");
93
+ expect(decodeUtf8(new TextEncoder().encode("a\ufeffb"))).toBe("a\ufeffb");
94
+ });
95
+ });
96
+
97
+ describe("document caps", () => {
98
+ it("refuses a manifest over its declared-size cap before reading it", () => {
99
+ const huge = `{"name":"x","pad":"${"a".repeat(PLUGIN_DOCUMENT_LIMITS.manifest)}"}`;
100
+ const files = openPlugin({ files: { "plugin.json": huge } });
101
+ const outcome = read(files);
102
+ expect(kindsOf(outcome).errors).toEqual(["document-too-large", "manifest-name-missing"]);
103
+ });
104
+
105
+ it("refuses a document whose reader returned more bytes than it declared", () => {
106
+ const manifest = JSON.stringify({ $schema: "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", name: "x" });
107
+ const lying: PluginFiles = {
108
+ entries: [{ path: "plugin.json", size: 1 }],
109
+ read: () => new TextEncoder().encode(manifest.padEnd(PLUGIN_DOCUMENT_LIMITS.manifest + 1, " ")),
110
+ };
111
+ const outcome = readPluginPackage(lying);
112
+ expect(kindsOf(outcome).errors).toEqual(["document-too-large", "manifest-name-missing"]);
113
+ });
114
+
115
+ it("refuses a listed path outside the root", () => {
116
+ const files = openPlugin({ files: { "../escape.txt": "x" } });
117
+ expect(kindsOf(read(files)).errors).toEqual(["path-uncontained"]);
118
+ });
119
+ });
@@ -0,0 +1,22 @@
1
+ Vendored test fixtures from the Cursor plugins repository.
2
+
3
+ Source: https://github.com/cursor/plugins
4
+ Commit: c1c0a32
5
+ License: each plugin directory carries its own LICENSE file (MIT License,
6
+ Copyright Cursor), vendored beside it unchanged.
7
+
8
+ Directories and their paths in the source repository:
9
+
10
+ thermos/ <- thermos/
11
+ advisor/ <- advisor/
12
+ github/ <- third_party/github/
13
+ xero/ <- third_party/xero/
14
+ playwright/ <- third_party/playwright/
15
+ salesforce/ <- third_party/salesforce/
16
+
17
+ Every text file is a byte-for-byte copy of the source at that commit. The
18
+ image files (assets/*.png, assets/*.svg) are zero-byte placeholders of the
19
+ same name: the reader never opens them (a logo is an ignored component), and
20
+ the tests pin only that they are listed and never read. Nothing under this
21
+ directory is formatted, linted or edited; a change here is a re-vendoring at
22
+ a new commit and updates this notice.
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "advisor",
3
+ "displayName": "Advisor",
4
+ "version": "1.0.0",
5
+ "description": "Consult a stronger model at key checkpoints: before major decisions, when stuck on an error, and before declaring a task done. The advisor gets a full briefing plus the conversation transcript, returns guidance, and the main model keeps doing the work.",
6
+ "author": {
7
+ "name": "Cursor",
8
+ "email": "plugins@cursor.com"
9
+ },
10
+ "homepage": "https://github.com/cursor/plugins/tree/main/advisor",
11
+ "repository": "https://github.com/cursor/plugins",
12
+ "license": "MIT",
13
+ "logo": "assets/avatar.png",
14
+ "keywords": [
15
+ "advisor",
16
+ "second-opinion",
17
+ "grok",
18
+ "multi-model",
19
+ "subagents",
20
+ "review",
21
+ "quality"
22
+ ],
23
+ "category": "developer-tools",
24
+ "tags": [
25
+ "agents",
26
+ "quality",
27
+ "review",
28
+ "automation"
29
+ ],
30
+ "skills": "./skills/",
31
+ "agents": "./agents/",
32
+ "hooks": "./hooks/hooks.json"
33
+ }
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ - Initial Advisor plugin release.
6
+ - Skill: `advisor` (`/advisor [model|off|status|ask ...|nudge on|off]`), usable as a Custom Mode.
7
+ - Agent: `advisor-subagent`, a read-only subagent pinned to Grok 4.6 at xhigh effort by default (any model via `/advisor <model>`) that returns a verdict and guidance.
8
+ - Hooks: track edits since the last consult, log each consult to `.cursor/advisor/log.md`, and nudge a pre-completion consult at the end of a turn.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cursor
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.
@@ -0,0 +1,87 @@
1
+ # Advisor
2
+
3
+ Advisor gives Cursor's agent a stronger second model to consult at key points: before a major decision, when it is stuck on an error, and before it declares a task done. The main model keeps doing the work; the advisor reads a full briefing (and the conversation transcript when available), thinks hard, and returns a verdict with concrete guidance. You get higher quality on complex tasks while paying for the strong model only where it matters.
4
+
5
+ The default advisor is the latest Grok (Grok 4.6) at its highest reasoning effort. Because the advisor is a subagent with its own model, it does not have to be the model you are chatting with: any model available to subagents works, so a second opinion can come from a different model family.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ /add-plugin advisor
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```text
16
+ /advisor turn on with the default advisor (Grok 4.6, xhigh effort)
17
+ /advisor composer-2.5 turn on with a different model (any subagent model slug)
18
+ /advisor ask is this migration safe to run twice?
19
+ /advisor status
20
+ /advisor off
21
+ ```
22
+
23
+ Then work as usual. When the agent hits a checkpoint it consults the advisor and reports back in a line or two:
24
+
25
+ ```text
26
+ Advisor (cursor-grok-4.6-xhigh): proceed with changes. The retry wrapper hides the real
27
+ failure; the 401 comes from a stale token cache. Dropped the retry, fixed the cache key.
28
+ ```
29
+
30
+ To keep the skill in context for a whole session, invoke `/advisor` with Option+Enter (macOS) or Alt+Enter (Windows/Linux) to run it as a Custom Mode. The state file works either way.
31
+
32
+ ## How it works
33
+
34
+ ```mermaid
35
+ flowchart LR
36
+ U[User] -->|/advisor| M[Main model]
37
+ M -->|writes| S[.cursor/advisor/state.json]
38
+ M -->|briefing + transcript path| A[advisor-subagent<br/>strong model, read-only]
39
+ A -->|verdict + guidance| M
40
+ H[hooks] -->|track edits, log consults,<br/>nudge before done| M
41
+ ```
42
+
43
+ - **Skill `advisor`** implements the `/advisor` command and the checkpoint protocol: when to consult, how to write the briefing, how to act on the verdict, and how to report it. See [skills/advisor/SKILL.md](skills/advisor/SKILL.md) and the [briefing template](skills/advisor/references/briefing-template.md).
44
+ - **Agent `advisor-subagent`** is a read-only subagent pinned to a strong model (`grok-4.6[effort=xhigh]` by default, overridden per session by `/advisor <model>`). It verifies the briefing against the repository, reads the transcript when a path is available, and answers with `Verdict / Why / Recommendations / Risks / Answers / Confidence`.
45
+ - **Hooks** keep the mode honest without adding chatter: `afterFileEdit` records that files changed since the last consult, `subagentStop` counts each consult and appends the advice to `.cursor/advisor/log.md`, and `stop` posts a one-line `[Advisor]` follow-up when a turn ends with unreviewed edits so the pre-completion consult is not skipped. The nudge fires at most once per batch of edits, stays quiet when the agent ended its turn with a question for you, and can be disabled with `/advisor nudge off`.
46
+
47
+ ## Checkpoints
48
+
49
+ | Checkpoint | Trigger |
50
+ | --- | --- |
51
+ | Major decision | Choosing between approaches; migrations, deletions, public API or config changes, dependency swaps, auth or payment code; requests ambiguous enough to change the work. |
52
+ | Stuck | Same error or failing test after two real attempts; unexplained behavior; about to add a workaround (retry loop, `sleep`, broad `try/except`, skipped test, disabled check). |
53
+ | Before declaring done | Any task that changed logic or touched more than a couple of files. Trivial edits are skipped and said so. |
54
+ | On request | `/advisor ask ...`, or asking what the advisor thinks. |
55
+
56
+ The skill caps this at roughly four consults per task and never consults for routine steps or things the agent can verify itself.
57
+
58
+ ## Models
59
+
60
+ The default is the latest Grok model at its highest reasoning effort (currently `cursor-grok-4.6-xhigh`). `/advisor <model>` accepts any model slug available to subagents; a family name such as `grok fast` or `composer` resolves to that family's latest model at its highest reasoning tier. If Cursor rejects a slug, the skill picks the closest valid one from the error, saves it, and tells you. Team model restrictions and plan limits apply to the advisor like any subagent.
61
+
62
+ ## State
63
+
64
+ Everything lives in `.cursor/advisor/` at the project root and is safe to delete at any time:
65
+
66
+ | File | Purpose |
67
+ | --- | --- |
68
+ | `state.json` | Mode, model, nudge setting, consult count, bound conversation, transcript path. |
69
+ | `log.md` | Every completed consult with its verdict, for later review. |
70
+ | `pending` | Marker: files changed since the last consult. |
71
+ | `last-response.txt` | Tail of the latest reply, used to avoid nudging over a question to you. |
72
+
73
+ Add `.cursor/advisor/` to your `.gitignore` if you do not want it in the repository; the skill never stages it.
74
+
75
+ ## Cost
76
+
77
+ Each consult is one call to the advisor model with a briefing of a few thousand tokens plus whatever the advisor chooses to read. Selective use is the point: a typical feature takes one to three consults. The default advisor, Grok 4.6, draws from the Cursor Models usage pool, so it is the cheapest of the strong options; switch models when you want a different family's perspective.
78
+
79
+ ## Limitations
80
+
81
+ - State is per project, bound to the conversation that enabled it. Running `/advisor` in a second conversation on the same project re-binds the mode there (model and nudge settings carry over; the consult history and advisor context start fresh). Use `/advisor status` to look without re-binding.
82
+ - The transcript path is recorded by hooks, so the advisor only reads the full transcript when hooks run and transcripts are enabled. It always receives the briefing.
83
+ - The end-of-turn nudge relies on the plugin's hooks; where hooks do not run, the skill still performs the pre-completion consult on its own.
84
+
85
+ ## License
86
+
87
+ MIT
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: advisor-subagent
3
+ description: Stronger-model advisor for the Advisor plugin. Consulted by the main agent at key checkpoints (before a major decision, when stuck on an error, before declaring a task done) with a briefing and, when available, the conversation transcript. Read-only. Returns a verdict and concrete guidance, not edits.
4
+ model: grok-4.6[effort=xhigh]
5
+ readonly: true
6
+ ---
7
+
8
+ # Advisor
9
+
10
+ You are the senior engineer a working agent consults at key points. Your prompt is a briefing: the user's request, what has happened so far, verbatim evidence, the current state, and specific questions. It may also name a transcript file. The parent does the work; you supply judgment.
11
+
12
+ ## Method
13
+
14
+ 1. Read the whole briefing before forming a view. Separate evidence (tool output, diffs) from the parent's interpretation of it.
15
+ 2. Verify what matters. You can read the repository and run read-only commands (`git diff`, `git log`, `grep`, viewing files). Open the files the briefing names and read the actual diff rather than the description of it. For a "stuck" checkpoint, read the failing code path yourself before proposing a cause.
16
+ 3. If a transcript path is given and the file exists, use it to recover what the briefing left out: the user's exact words, earlier decisions, tool results that were summarized away. Check the file size first. For a large transcript, read the most recent portion and search for the user's messages instead of reading everything.
17
+ 4. Look for what the parent most likely missed: an assumption it never tested, a simpler approach, a hidden coupling, a production failure mode, part of the request that quietly dropped out of scope, verification that was claimed but not actually run.
18
+ 5. Decide. Prefer one clear recommendation over a menu. If two options are genuinely close, say so and give the tie-breaker.
19
+
20
+ ## Response
21
+
22
+ The parent has to act on this, not read an essay. Stay under about 400 words unless the situation truly needs more.
23
+
24
+ ```text
25
+ Verdict: proceed | proceed with changes | stop
26
+ Why: <two or three sentences>
27
+
28
+ Recommendations:
29
+ 1. <specific action, with file:line or the exact command where relevant>
30
+ 2. ...
31
+
32
+ Risks / verify before done:
33
+ - <what could still be wrong, and how to check it>
34
+
35
+ Answers:
36
+ <numbered, matching the briefing's questions>
37
+
38
+ Confidence: high | medium | low — <what would change your mind>
39
+ ```
40
+
41
+ ## Rules
42
+
43
+ - Do not edit files, run state-changing commands, or do the task yourself. You advise.
44
+ - Be direct. Disagree when the evidence warrants it, including with the parent's stated leaning. Do not pad agreement with caveats.
45
+ - Say what you verified and what you infer. Never present a guess about the codebase as fact.
46
+ - If the briefing lacks something you need, ask for exactly that in a short numbered list and still give your best provisional read. One round only.
47
+ - Do not spawn subagents.
48
+ - When resumed, treat the new message as the next checkpoint of the same task. Reuse what you already know and do not re-verify what has not changed.
@@ -0,0 +1,20 @@
1
+ #!/bin/bash
2
+
3
+ # afterAgentResponse hook for Advisor.
4
+ # Keeps the tail of the latest assistant message so the stop hook can tell
5
+ # whether the agent ended its turn with a question for the user.
6
+ #
7
+ # Input: { "text": "<assistant response text>", ...common }
8
+ # Output: none
9
+
10
+ set -euo pipefail
11
+
12
+ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
13
+
14
+ HOOK_INPUT=$(cat)
15
+
16
+ advisor_require_enabled
17
+ advisor_bind_conversation "$HOOK_INPUT" || exit 0
18
+
19
+ jq -r '.text // empty' <<< "$HOOK_INPUT" | tail -c 400 > "$LAST_RESPONSE_FILE"
20
+ exit 0
@@ -0,0 +1,27 @@
1
+ {
2
+ "version": 1,
3
+ "hooks": {
4
+ "afterFileEdit": [
5
+ {
6
+ "command": "bash \"${CURSOR_PLUGIN_ROOT}/hooks/mark-pending.sh\""
7
+ }
8
+ ],
9
+ "afterAgentResponse": [
10
+ {
11
+ "command": "bash \"${CURSOR_PLUGIN_ROOT}/hooks/capture-response.sh\""
12
+ }
13
+ ],
14
+ "subagentStop": [
15
+ {
16
+ "command": "bash \"${CURSOR_PLUGIN_ROOT}/hooks/record-consult.sh\"",
17
+ "matcher": "^advisor-subagent$"
18
+ }
19
+ ],
20
+ "stop": [
21
+ {
22
+ "command": "bash \"${CURSOR_PLUGIN_ROOT}/hooks/stop-hook.sh\"",
23
+ "loop_limit": 3
24
+ }
25
+ ]
26
+ }
27
+ }
@@ -0,0 +1,61 @@
1
+ #!/bin/bash
2
+
3
+ # Shared helpers for the Advisor hooks. Source this file; do not run it.
4
+ #
5
+ # State lives in $CURSOR_PROJECT_DIR/.cursor/advisor/:
6
+ # state.json written by the advisor skill, kept current here
7
+ # pending marker: files were edited since the last consult
8
+ # last-response.txt tail of the latest assistant message
9
+ # log.md one entry per completed consult
10
+
11
+ ADVISOR_DIR="${CURSOR_PROJECT_DIR:-.}/.cursor/advisor"
12
+ STATE_FILE="$ADVISOR_DIR/state.json"
13
+ PENDING_FILE="$ADVISOR_DIR/pending"
14
+ LAST_RESPONSE_FILE="$ADVISOR_DIR/last-response.txt"
15
+ LOG_FILE="$ADVISOR_DIR/log.md"
16
+
17
+ # Exit quietly unless advisor mode is on and jq is available.
18
+ advisor_require_enabled() {
19
+ command -v jq >/dev/null 2>&1 || exit 0
20
+ [[ -f "$STATE_FILE" ]] || exit 0
21
+ [[ "$(jq -r '.enabled // false' "$STATE_FILE" 2>/dev/null)" == "true" ]] || exit 0
22
+ }
23
+
24
+ # Atomically apply a jq filter to state.json.
25
+ # Usage: advisor_state_update '<filter>' [jq args...]
26
+ advisor_state_update() {
27
+ local filter="$1"
28
+ shift
29
+ local tmp="${STATE_FILE}.tmp.$$"
30
+ if jq "$@" "$filter" "$STATE_FILE" > "$tmp" 2>/dev/null; then
31
+ mv "$tmp" "$STATE_FILE"
32
+ else
33
+ rm -f "$tmp"
34
+ fi
35
+ }
36
+
37
+ # Bind the state to the first conversation that touches it and keep transcript_path
38
+ # current. Returns 1 when the hook input belongs to a different conversation, so
39
+ # callers can stay out of conversations that did not enable the advisor.
40
+ # A fresh bind also drops per-conversation markers so a re-bind cannot inherit
41
+ # the previous conversation's pending edits.
42
+ advisor_bind_conversation() {
43
+ local input="$1"
44
+ local conv bound transcript current
45
+ conv=$(jq -r '.conversation_id // empty' <<< "$input")
46
+ bound=$(jq -r '.conversation_id // empty' "$STATE_FILE")
47
+ if [[ -n "$conv" ]]; then
48
+ if [[ -z "$bound" ]]; then
49
+ advisor_state_update '.conversation_id = $conv' --arg conv "$conv"
50
+ rm -f "$PENDING_FILE" "$LAST_RESPONSE_FILE"
51
+ elif [[ "$bound" != "$conv" ]]; then
52
+ return 1
53
+ fi
54
+ fi
55
+ transcript=$(jq -r '.transcript_path // empty' <<< "$input")
56
+ current=$(jq -r '.transcript_path // empty' "$STATE_FILE")
57
+ if [[ -n "$transcript" && "$transcript" != "$current" ]]; then
58
+ advisor_state_update '.transcript_path = $path' --arg path "$transcript"
59
+ fi
60
+ return 0
61
+ }
@@ -0,0 +1,27 @@
1
+ #!/bin/bash
2
+
3
+ # afterFileEdit hook for Advisor.
4
+ # Remembers that files changed since the last advisor consult, so the stop hook
5
+ # can ask for a pre-completion review if the turn ends without one.
6
+ #
7
+ # Input: { "file_path": "<absolute path>", "edits": [...], ...common }
8
+ # Output: none
9
+
10
+ set -euo pipefail
11
+
12
+ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
13
+
14
+ HOOK_INPUT=$(cat)
15
+
16
+ advisor_require_enabled
17
+ advisor_bind_conversation "$HOOK_INPUT" || exit 0
18
+
19
+ FILE_PATH=$(jq -r '.file_path // empty' <<< "$HOOK_INPUT")
20
+
21
+ # The plugin's own state is not work product.
22
+ case "$FILE_PATH" in
23
+ */.cursor/advisor/*) exit 0 ;;
24
+ esac
25
+
26
+ touch "$PENDING_FILE"
27
+ exit 0
@@ -0,0 +1,41 @@
1
+ #!/bin/bash
2
+
3
+ # subagentStop hook for Advisor (matcher: the `advisor-subagent` subagent).
4
+ # Counts the consult, clears the pending-edits marker, and appends the advice
5
+ # to .cursor/advisor/log.md so the user can review it later.
6
+ #
7
+ # Input: { "subagent_type": "advisor-subagent", "status": "completed"|"error"|"aborted",
8
+ # "description": "...", "summary": "...", ...common }
9
+ # Output: none
10
+
11
+ set -euo pipefail
12
+
13
+ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
14
+
15
+ HOOK_INPUT=$(cat)
16
+
17
+ advisor_require_enabled
18
+ advisor_bind_conversation "$HOOK_INPUT" || exit 0
19
+
20
+ SUBAGENT_TYPE=$(jq -r '.subagent_type // empty' <<< "$HOOK_INPUT")
21
+ STATUS=$(jq -r '.status // empty' <<< "$HOOK_INPUT")
22
+ if [[ "$SUBAGENT_TYPE" != "advisor-subagent" || "$STATUS" != "completed" ]]; then
23
+ exit 0
24
+ fi
25
+
26
+ NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
27
+ advisor_state_update '.consults = ((.consults // 0) + 1) | .last_consult_at = $now' --arg now "$NOW"
28
+ rm -f "$PENDING_FILE"
29
+
30
+ DESCRIPTION=$(jq -r '.description // "Advisor consult"' <<< "$HOOK_INPUT")
31
+ SUMMARY=$(jq -r '.summary // empty' <<< "$HOOK_INPUT" | head -c 6000)
32
+ {
33
+ printf '## %s — %s\n\n' "$NOW" "$DESCRIPTION"
34
+ if [[ -n "$SUMMARY" ]]; then
35
+ printf '%s\n\n' "$SUMMARY"
36
+ else
37
+ printf '_No summary captured._\n\n'
38
+ fi
39
+ } >> "$LOG_FILE"
40
+
41
+ exit 0
@@ -0,0 +1,48 @@
1
+ #!/bin/bash
2
+
3
+ # stop hook for Advisor.
4
+ # If files changed since the last advisor consult and the turn ended without one,
5
+ # ask the agent (once per batch of edits) to run the pre-completion consult.
6
+ #
7
+ # Input: { "status": "completed"|"aborted"|"error", "loop_count": N, ...common }
8
+ # Output: { "followup_message": "<text>" } to continue, or exit 0 with no output
9
+
10
+ set -euo pipefail
11
+
12
+ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
13
+
14
+ HOOK_INPUT=$(cat)
15
+
16
+ advisor_require_enabled
17
+ advisor_bind_conversation "$HOOK_INPUT" || exit 0
18
+
19
+ STATUS=$(jq -r '.status // empty' <<< "$HOOK_INPUT")
20
+ if [[ "$STATUS" != "completed" ]]; then
21
+ exit 0
22
+ fi
23
+
24
+ NUDGE=$(jq -r 'if .nudge == false then "false" else "true" end' "$STATE_FILE")
25
+ if [[ "$NUDGE" != "true" ]]; then
26
+ exit 0
27
+ fi
28
+
29
+ if [[ ! -f "$PENDING_FILE" ]]; then
30
+ exit 0
31
+ fi
32
+
33
+ # The agent stopped to ask the user something. Stay quiet and leave the marker
34
+ # armed so the reminder fires after the user answers and the work resumes.
35
+ if [[ -f "$LAST_RESPONSE_FILE" ]]; then
36
+ LAST_CHAR=$(tr -d '[:space:]' < "$LAST_RESPONSE_FILE" | tail -c 1)
37
+ if [[ "$LAST_CHAR" == "?" ]]; then
38
+ exit 0
39
+ fi
40
+ fi
41
+
42
+ rm -f "$PENDING_FILE"
43
+
44
+ MODEL=$(jq -r '.model // "the configured advisor model"' "$STATE_FILE")
45
+ MESSAGE="[Advisor] Files changed since the last advisor consult and the turn ended without one. If this work is done, or you were about to declare it done, run the pre-completion consult now per the advisor skill: build the briefing, spawn the \`advisor-subagent\` subagent (model: $MODEL), act on the verdict, and report it in one line. If the change was trivial, or you are waiting on the user, say so in one line and stop."
46
+
47
+ jq -n --arg msg "$MESSAGE" '{"followup_message": $msg}'
48
+ exit 0