@cursor/july 0.1.68 → 0.1.74

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 (252) hide show
  1. package/AGENTS.md +17 -13
  2. package/README.md +23 -11
  3. package/dist/bin/agent-serve.js +10 -7
  4. package/dist/channels/github/api.d.ts +2 -0
  5. package/dist/channels/github/api.d.ts.map +1 -1
  6. package/dist/channels/github/api.js +2 -1
  7. package/dist/channels/github/binding.d.ts +7 -0
  8. package/dist/channels/github/binding.d.ts.map +1 -1
  9. package/dist/channels/github/binding.js +4 -0
  10. package/dist/channels/github/checks.d.ts +99 -0
  11. package/dist/channels/github/checks.d.ts.map +1 -0
  12. package/dist/channels/github/checks.js +108 -0
  13. package/dist/channels/github/cursor-account.d.ts +4 -4
  14. package/dist/channels/github/cursor-account.d.ts.map +1 -1
  15. package/dist/channels/github/cursor-account.js +7 -5
  16. package/dist/channels/github/index.d.ts +1 -0
  17. package/dist/channels/github/index.d.ts.map +1 -1
  18. package/dist/channels/github/index.js +1 -0
  19. package/dist/channels/github/types.d.ts +8 -0
  20. package/dist/channels/github/types.d.ts.map +1 -1
  21. package/dist/channels/slack/manifest.d.ts +2 -2
  22. package/dist/channels/slack/manifest.d.ts.map +1 -1
  23. package/dist/channels/slack/manifest.js +13 -11
  24. package/dist/channels/slack/types.d.ts +2 -2
  25. package/dist/channels/slack/types.d.ts.map +1 -1
  26. package/dist/connections.d.ts +17 -5
  27. package/dist/connections.d.ts.map +1 -1
  28. package/dist/connections.js +17 -5
  29. package/dist/docs/404.html +2 -2
  30. package/dist/docs/ab.html +3 -3
  31. package/dist/docs/assets/{app.C5XUTCw9.js → app.BETd-WqU.js} +1 -1
  32. package/dist/docs/assets/chunks/@localSearchIndexroot.DM5INS71.js +1 -0
  33. package/dist/docs/assets/chunks/{VPLocalSearchBox.CGDTAYHY.js → VPLocalSearchBox.wEaGEmV9.js} +1 -1
  34. package/dist/docs/assets/chunks/{theme.lPixh250.js → theme.DNDicFOP.js} +2 -2
  35. package/dist/docs/assets/{concepts.md.CqOsxbMU.js → concepts.md.BXAm6G-C.js} +1 -1
  36. package/dist/docs/assets/{guides_mcp-oauth.md.C7G7IykG.js → guides_mcp-oauth.md.CzEB6RaG.js} +1 -1
  37. package/dist/docs/assets/{reference_agent-config.md.DHkkvcEZ.js → reference_agent-config.md.BZnEKfEc.js} +3 -4
  38. package/dist/docs/assets/{reference_agent-config.md.DHkkvcEZ.lean.js → reference_agent-config.md.BZnEKfEc.lean.js} +1 -1
  39. package/dist/docs/assets/{reference_cli.md.Cl14Jfo3.js → reference_cli.md.TAaYU8br.js} +3 -3
  40. package/dist/docs/assets/{reference_cli.md.Cl14Jfo3.lean.js → reference_cli.md.TAaYU8br.lean.js} +1 -1
  41. package/dist/docs/assets/{reference_connections.md.Bwswbm6H.js → reference_connections.md.Cu3N-S3Q.js} +18 -11
  42. package/dist/docs/assets/{reference_connections.md.Bwswbm6H.lean.js → reference_connections.md.Cu3N-S3Q.lean.js} +1 -1
  43. package/dist/docs/assets/{reference_sessions.md.DBVFi2Sx.js → reference_sessions.md.B0DdlM-K.js} +1 -1
  44. package/dist/docs/assets/{reference_tools.md.Wmvb7Lob.js → reference_tools.md.DTg_kEsx.js} +1 -1
  45. package/dist/docs/assets/scaffolding-agents.md.CiGsJ1aw.js +1 -0
  46. package/dist/docs/assets/{scaffolding-agents.md.CrWSA0Ko.lean.js → scaffolding-agents.md.CiGsJ1aw.lean.js} +1 -1
  47. package/dist/docs/assets/troubleshooting.md.DQ-hYuZT.js +1 -0
  48. package/dist/docs/building-with-agents.html +3 -3
  49. package/dist/docs/concepts.html +5 -5
  50. package/dist/docs/deployment.html +3 -3
  51. package/dist/docs/evals.html +3 -3
  52. package/dist/docs/example-agents/approval-buddy.html +3 -3
  53. package/dist/docs/example-agents/benny.html +3 -3
  54. package/dist/docs/example-agents/bugbot.html +3 -3
  55. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  56. package/dist/docs/example-agents/codeowners-review.html +3 -3
  57. package/dist/docs/example-agents/concierge.html +3 -3
  58. package/dist/docs/example-agents/fsd.html +3 -3
  59. package/dist/docs/example-agents/index.html +3 -3
  60. package/dist/docs/example-agents/knowledge-base.html +3 -3
  61. package/dist/docs/example-agents/oncall.html +3 -3
  62. package/dist/docs/example-agents/security-reviewer.html +3 -3
  63. package/dist/docs/example-agents/slack-agent.html +3 -3
  64. package/dist/docs/example-agents/weather-agent.html +3 -3
  65. package/dist/docs/guides/agent-to-agent.html +3 -3
  66. package/dist/docs/guides/cloud-runtime.html +3 -3
  67. package/dist/docs/guides/convert-automation.html +3 -3
  68. package/dist/docs/guides/github.html +3 -3
  69. package/dist/docs/guides/human-in-the-loop.html +3 -3
  70. package/dist/docs/guides/mcp-oauth.html +5 -5
  71. package/dist/docs/guides/opentelemetry.html +3 -3
  72. package/dist/docs/guides/slack.html +3 -3
  73. package/dist/docs/guides/webhooks.html +3 -3
  74. package/dist/docs/hashmap.json +1 -1
  75. package/dist/docs/hillclimbing.html +3 -3
  76. package/dist/docs/index.html +3 -3
  77. package/dist/docs/quickstart.html +3 -3
  78. package/dist/docs/reference/agent-config.html +5 -6
  79. package/dist/docs/reference/artifacts.html +3 -3
  80. package/dist/docs/reference/channels.html +3 -3
  81. package/dist/docs/reference/cli.html +6 -6
  82. package/dist/docs/reference/connections.html +20 -13
  83. package/dist/docs/reference/hooks.html +3 -3
  84. package/dist/docs/reference/http-api.html +3 -3
  85. package/dist/docs/reference/instructions.html +3 -3
  86. package/dist/docs/reference/playground.html +3 -3
  87. package/dist/docs/reference/project-layout.html +3 -3
  88. package/dist/docs/reference/prompt.html +3 -3
  89. package/dist/docs/reference/schedules.html +3 -3
  90. package/dist/docs/reference/sessions.html +5 -5
  91. package/dist/docs/reference/skills.html +3 -3
  92. package/dist/docs/reference/subagents.html +3 -3
  93. package/dist/docs/reference/tools.html +5 -5
  94. package/dist/docs/scaffolding-agents.html +5 -5
  95. package/dist/docs/storage.html +3 -3
  96. package/dist/docs/templates/agentic-owners.html +3 -3
  97. package/dist/docs/templates/demo.html +3 -3
  98. package/dist/docs/templates/pr-autofixer.html +3 -3
  99. package/dist/docs/templates/security-reviewer.html +3 -3
  100. package/dist/docs/templates/triage.html +3 -3
  101. package/dist/docs/troubleshooting.html +5 -5
  102. package/dist/internal/advertise-tools.d.ts +11 -6
  103. package/dist/internal/advertise-tools.d.ts.map +1 -1
  104. package/dist/internal/advertise-tools.js +63 -30
  105. package/dist/internal/cli-deploy.d.ts.map +1 -1
  106. package/dist/internal/cli-deploy.js +2 -4
  107. package/dist/internal/cli-skills.d.ts +4 -2
  108. package/dist/internal/cli-skills.d.ts.map +1 -1
  109. package/dist/internal/cli-skills.js +8 -3
  110. package/dist/internal/convert-automation/convert-workflow.d.ts.map +1 -1
  111. package/dist/internal/convert-automation/convert-workflow.js +2 -10
  112. package/dist/internal/cursor/account-mcp.d.ts +29 -3
  113. package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
  114. package/dist/internal/cursor/account-mcp.js +147 -67
  115. package/dist/internal/cursor/backend-client.d.ts +11 -6
  116. package/dist/internal/cursor/backend-client.d.ts.map +1 -1
  117. package/dist/internal/cursor/backend-client.js +32 -10
  118. package/dist/internal/cursor-account-mcp-auth.d.ts +11 -0
  119. package/dist/internal/cursor-account-mcp-auth.d.ts.map +1 -1
  120. package/dist/internal/cursor-account-mcp-auth.js +69 -0
  121. package/dist/internal/deploy-client.d.ts +0 -7
  122. package/dist/internal/deploy-client.d.ts.map +1 -1
  123. package/dist/internal/deploy-client.js +2 -4
  124. package/dist/internal/deploy-manifest.d.ts +15 -24
  125. package/dist/internal/deploy-manifest.d.ts.map +1 -1
  126. package/dist/internal/deploy-manifest.js +38 -27
  127. package/dist/internal/deployment-scm-event-stream.d.ts +4 -1
  128. package/dist/internal/deployment-scm-event-stream.d.ts.map +1 -1
  129. package/dist/internal/deployment-scm-event-stream.js +15 -1
  130. package/dist/internal/discovery.d.ts.map +1 -1
  131. package/dist/internal/discovery.js +42 -10
  132. package/dist/internal/init-project.d.ts.map +1 -1
  133. package/dist/internal/init-project.js +7 -106
  134. package/dist/internal/init-scaffold.d.ts +8 -0
  135. package/dist/internal/init-scaffold.d.ts.map +1 -0
  136. package/dist/internal/init-scaffold.js +168 -0
  137. package/dist/internal/inline-stdio-mcp.d.ts +2 -0
  138. package/dist/internal/inline-stdio-mcp.d.ts.map +1 -1
  139. package/dist/internal/inline-stdio-mcp.js +10 -8
  140. package/dist/internal/install-cursor-skills.d.ts +71 -16
  141. package/dist/internal/install-cursor-skills.d.ts.map +1 -1
  142. package/dist/internal/install-cursor-skills.js +160 -48
  143. package/dist/internal/json-schema.d.ts +7 -0
  144. package/dist/internal/json-schema.d.ts.map +1 -1
  145. package/dist/internal/json-schema.js +18 -3
  146. package/dist/internal/mcp-endpoint.js +7 -7
  147. package/dist/internal/mcp-host.js +4 -2
  148. package/dist/internal/mcp-oauth.d.ts +22 -0
  149. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  150. package/dist/internal/mcp-oauth.js +40 -1
  151. package/dist/internal/package-inline-stdio.d.ts.map +1 -1
  152. package/dist/internal/package-inline-stdio.js +5 -5
  153. package/dist/internal/review-comments.d.ts +97 -0
  154. package/dist/internal/review-comments.d.ts.map +1 -1
  155. package/dist/internal/review-comments.js +151 -0
  156. package/dist/internal/sanitize-mcp-tool-name.d.ts +20 -0
  157. package/dist/internal/sanitize-mcp-tool-name.d.ts.map +1 -0
  158. package/dist/internal/sanitize-mcp-tool-name.js +30 -0
  159. package/dist/internal/sdk-runner.d.ts.map +1 -1
  160. package/dist/internal/sdk-runner.js +6 -2
  161. package/dist/internal/session-engine.d.ts.map +1 -1
  162. package/dist/internal/session-engine.js +4 -6
  163. package/dist/internal/shallow-clone.d.ts +94 -39
  164. package/dist/internal/shallow-clone.d.ts.map +1 -1
  165. package/dist/internal/shallow-clone.js +188 -68
  166. package/dist/internal/terminal-style.d.ts +18 -0
  167. package/dist/internal/terminal-style.d.ts.map +1 -1
  168. package/dist/internal/terminal-style.js +46 -1
  169. package/dist/internal/workspace.d.ts +2 -0
  170. package/dist/internal/workspace.d.ts.map +1 -1
  171. package/dist/internal/workspace.js +5 -0
  172. package/dist/playground/assets/index-D_8I9TM0.js +85 -0
  173. package/dist/playground/assets/index-M3nSMc6_.css +1 -0
  174. package/dist/playground/index.html +2 -2
  175. package/dist/tools.d.ts +7 -0
  176. package/dist/tools.d.ts.map +1 -1
  177. package/dist/types.d.ts +28 -3
  178. package/dist/types.d.ts.map +1 -1
  179. package/docs/concepts.md +4 -4
  180. package/docs/guides/mcp-oauth.md +3 -1
  181. package/docs/reference/agent-config.md +11 -12
  182. package/docs/reference/cli.md +16 -8
  183. package/docs/reference/connections.md +44 -26
  184. package/docs/reference/sessions.md +2 -2
  185. package/docs/reference/tools.md +1 -1
  186. package/docs/scaffolding-agents.md +14 -6
  187. package/docs/troubleshooting.md +3 -2
  188. package/package.json +6 -1
  189. package/scripts/install-cursor-skills.mjs +36 -0
  190. package/skills/ab/SKILL.md +1 -1
  191. package/skills/create-agent/SKILL.md +13 -10
  192. package/skills/debug/SKILL.md +3 -2
  193. package/skills/evals/SKILL.md +1 -1
  194. package/skills/framework-map/SKILL.md +7 -7
  195. package/skills/github/SKILL.md +1 -1
  196. package/skills/hillclimb/SKILL.md +1 -1
  197. package/skills/mcp-auth/SKILL.md +9 -7
  198. package/skills/otel/SKILL.md +1 -1
  199. package/skills/setup-slack/SKILL.md +1 -1
  200. package/src/bin/agent-serve.ts +10 -7
  201. package/src/channels/github/api.ts +6 -1
  202. package/src/channels/github/binding.ts +21 -0
  203. package/src/channels/github/checks.ts +179 -0
  204. package/src/channels/github/cursor-account.ts +9 -6
  205. package/src/channels/github/index.ts +1 -0
  206. package/src/channels/github/types.ts +8 -0
  207. package/src/channels/slack/manifest.ts +8 -10
  208. package/src/channels/slack/types.ts +2 -2
  209. package/src/connections.ts +17 -5
  210. package/src/internal/advertise-tools.ts +72 -23
  211. package/src/internal/cli-deploy.ts +4 -15
  212. package/src/internal/cli-skills.ts +10 -2
  213. package/src/internal/convert-automation/convert-workflow.ts +2 -11
  214. package/src/internal/cursor/account-mcp.ts +208 -73
  215. package/src/internal/cursor/backend-client.ts +45 -11
  216. package/src/internal/cursor-account-mcp-auth.ts +83 -0
  217. package/src/internal/deploy-client.ts +0 -10
  218. package/src/internal/deploy-manifest.ts +33 -39
  219. package/src/internal/deployment-scm-event-stream.ts +17 -1
  220. package/src/internal/discovery.ts +47 -3
  221. package/src/internal/init-project.ts +7 -133
  222. package/src/internal/init-scaffold.ts +176 -0
  223. package/src/internal/inline-stdio-mcp.ts +13 -0
  224. package/src/internal/install-cursor-skills.ts +204 -49
  225. package/src/internal/json-schema.ts +24 -3
  226. package/src/internal/mcp-endpoint.ts +6 -0
  227. package/src/internal/mcp-host.ts +4 -0
  228. package/src/internal/mcp-oauth.ts +55 -1
  229. package/src/internal/package-inline-stdio.ts +5 -1
  230. package/src/internal/review-comments.ts +245 -0
  231. package/src/internal/sanitize-mcp-tool-name.ts +33 -0
  232. package/src/internal/sdk-runner.ts +6 -3
  233. package/src/internal/session-engine.ts +7 -0
  234. package/src/internal/shallow-clone.ts +293 -105
  235. package/src/internal/terminal-style.ts +67 -1
  236. package/src/internal/workspace.ts +6 -0
  237. package/src/tools.ts +7 -0
  238. package/src/types.ts +32 -4
  239. package/templates/agentic-owners/agent/agent.ts +3 -0
  240. package/templates/demo/agent/agent.ts +3 -0
  241. package/templates/security-reviewer/agent/agent.ts +3 -0
  242. package/templates/triage/agent/agent.ts +3 -0
  243. package/dist/docs/assets/chunks/@localSearchIndexroot.DvUR3lv9.js +0 -1
  244. package/dist/docs/assets/scaffolding-agents.md.CrWSA0Ko.js +0 -1
  245. package/dist/docs/assets/troubleshooting.md.CuVztCKs.js +0 -1
  246. package/dist/playground/assets/index-CoL7xCJl.css +0 -1
  247. package/dist/playground/assets/index-yxs1heea.js +0 -85
  248. /package/dist/docs/assets/{concepts.md.CqOsxbMU.lean.js → concepts.md.BXAm6G-C.lean.js} +0 -0
  249. /package/dist/docs/assets/{guides_mcp-oauth.md.C7G7IykG.lean.js → guides_mcp-oauth.md.CzEB6RaG.lean.js} +0 -0
  250. /package/dist/docs/assets/{reference_sessions.md.DBVFi2Sx.lean.js → reference_sessions.md.B0DdlM-K.lean.js} +0 -0
  251. /package/dist/docs/assets/{reference_tools.md.Wmvb7Lob.lean.js → reference_tools.md.DTg_kEsx.lean.js} +0 -0
  252. /package/dist/docs/assets/{troubleshooting.md.CuVztCKs.lean.js → troubleshooting.md.DQ-hYuZT.lean.js} +0 -0
@@ -1,10 +1,17 @@
1
1
  /**
2
2
  * Offer to install the package's coding-agent skills into the user's
3
- * personal Cursor skills dir under `~/.cursor/skills/agentkit/<skill>/`.
3
+ * personal Cursor skills dir under `~/.cursor/skills/agentsdk/<skill>/`.
4
4
  *
5
- * Cursor walks `~/.cursor/skills` recursively, so the `agentkit/` folder is
5
+ * Cursor walks `~/.cursor/skills` recursively, so the `agentsdk/` folder is
6
6
  * only organizational — identity comes from the leaf folder that holds
7
- * SKILL.md. Skipped for `--json` / non-TTY hosts.
7
+ * SKILL.md. Installed copies get `alwaysApply: true` so Cursor injects the
8
+ * skill body as a global rule instead of leaving it as an on-demand catalog
9
+ * entry. Skipped for `--json` / non-TTY hosts.
10
+ *
11
+ * Consumer installs (`npm install` / `npx` of `@cursor/july`) also copy
12
+ * these skills from a postinstall hook. That path never prompts and never
13
+ * fails the package install. The monorepo source checkout is not a consumer
14
+ * install, so `pnpm install` in everysphere does not overwrite `~/.cursor`.
8
15
  */
9
16
 
10
17
  import {
@@ -21,51 +28,86 @@ import {
21
28
  import { homedir } from "node:os";
22
29
  import { join } from "node:path";
23
30
  import { CLI_COMMAND_NAME, packageRootDir } from "./distribution.js";
31
+ import { parseFrontmatter } from "./frontmatter.js";
24
32
  import { stdoutPalette } from "./terminal-style.js";
25
33
 
26
34
  /** Monorepo-only skill; excluded from the published package. */
27
35
  const SKIP_SKILL_DIRS = new Set(["operate"]);
28
36
 
29
37
  /** Category folder under ~/.cursor/skills (Cursor discovers nested skills). */
30
- export const CURSOR_SKILLS_NAMESPACE = "agentkit";
38
+ export const CURSOR_SKILLS_NAMESPACE = "agentsdk";
39
+
40
+ /** Pre-rename install folder. Install deletes this so `/agentkit-*` rows disappear. */
41
+ export const LEGACY_CURSOR_SKILLS_NAMESPACE = "agentkit";
31
42
 
32
43
  /** How long the `init` offer waits for an answer before skipping the install. */
33
44
  export const SKILL_PROMPT_TIMEOUT_MS = 45_000;
34
45
 
46
+ /**
47
+ * Override the resolved `~/.cursor/skills` directory (not the user home).
48
+ * Used by the postinstall hook in tests and publish smoke.
49
+ */
50
+ export const CURSOR_JULY_SKILLS_HOME_ENV = "CURSOR_JULY_SKILLS_HOME";
51
+
52
+ /** Set to `1` to skip the postinstall skill copy. */
53
+ export const CURSOR_JULY_SKIP_SKILL_INSTALL_ENV =
54
+ "CURSOR_JULY_SKIP_SKILL_INSTALL";
55
+
35
56
  export interface PackageSkill {
36
- /** Frontmatter `name` (e.g. agentkit-create-agent). */
57
+ /** Frontmatter `name` (e.g. agentsdk-create-agent). */
37
58
  name: string;
38
- /** Leaf install directory under agentkit/ (e.g. create-agent). */
59
+ /** Leaf install directory under agentsdk/ (e.g. create-agent). */
39
60
  leafDir: string;
40
61
  /** Absolute path to the source skill directory in the package. */
41
62
  sourceDir: string;
42
63
  }
43
64
 
44
- export function cursorSkillsHome(
45
- home: string = process.env.HOME ?? homedir()
46
- ): string {
47
- const resolved = home.trim() === "" ? homedir() : home;
48
- return join(resolved, ".cursor", "skills");
65
+ export function cursorSkillsHome(home?: string): string {
66
+ if (home !== undefined) {
67
+ return join(home.trim() === "" ? homedir() : home, ".cursor", "skills");
68
+ }
69
+ const override = process.env[CURSOR_JULY_SKILLS_HOME_ENV]?.trim();
70
+ if (override !== undefined && override !== "") {
71
+ return override;
72
+ }
73
+ return join(homedir(), ".cursor", "skills");
74
+ }
75
+
76
+ /**
77
+ * True when this package root is an installed copy under `node_modules`
78
+ * (npm, pnpm, yarn, npx, global), not the everysphere source checkout.
79
+ */
80
+ export function isConsumerPackageInstall(packageRoot: string): boolean {
81
+ // Segment check only — split on both separators so a Windows path still
82
+ // matches when this runs (or is tested) on POSIX. Do not rewrite the path.
83
+ return packageRoot.split(/[\\/]/).includes("node_modules");
49
84
  }
50
85
 
51
- /** `~/.cursor/skills/agentkit` — where package skills are installed. */
52
- export function agentkitCursorSkillsDir(
86
+ /** `~/.cursor/skills/agentsdk` — where package skills are installed. */
87
+ export function agentsdkCursorSkillsDir(
53
88
  skillsHome: string = cursorSkillsHome()
54
89
  ): string {
55
90
  return join(skillsHome, CURSOR_SKILLS_NAMESPACE);
56
91
  }
57
92
 
58
- /** Absolute install path for one skill: `…/agentkit/<leafDir>`. */
93
+ /** Pre-rename `~/.cursor/skills/agentkit`. */
94
+ export function legacyAgentkitCursorSkillsDir(
95
+ skillsHome: string = cursorSkillsHome()
96
+ ): string {
97
+ return join(skillsHome, LEGACY_CURSOR_SKILLS_NAMESPACE);
98
+ }
99
+
100
+ /** Absolute install path for one skill: `…/agentsdk/<leafDir>`. */
59
101
  export function skillInstallPath(
60
102
  skill: Pick<PackageSkill, "leafDir">,
61
103
  skillsHome: string = cursorSkillsHome()
62
104
  ): string {
63
- return join(agentkitCursorSkillsDir(skillsHome), skill.leafDir);
105
+ return join(agentsdkCursorSkillsDir(skillsHome), skill.leafDir);
64
106
  }
65
107
 
66
108
  /**
67
109
  * Skills shipped next to the running package. Uses the frontmatter `name`
68
- * when present, otherwise `agentkit-<dir>`.
110
+ * when present, otherwise `agentsdk-<dir>`.
69
111
  */
70
112
  export async function listShippedPackageSkills(
71
113
  packageRoot: string = packageRootDir()
@@ -91,7 +133,9 @@ export async function listShippedPackageSkills(
91
133
  continue;
92
134
  }
93
135
  const raw = await readFile(skillMd, "utf8");
94
- const name = parseSkillName(raw) ?? `agentkit-${entry.name}`;
136
+ const name =
137
+ parseFrontmatter(raw).data.name ||
138
+ `${CURSOR_SKILLS_NAMESPACE}-${entry.name}`;
95
139
  skills.push({ name, leafDir: entry.name, sourceDir });
96
140
  }
97
141
  skills.sort((a, b) => a.leafDir.localeCompare(b.leafDir));
@@ -99,7 +143,7 @@ export async function listShippedPackageSkills(
99
143
  }
100
144
 
101
145
  /**
102
- * Skills that do not yet have a SKILL.md under ~/.cursor/skills/agentkit/.
146
+ * Skills that do not yet have a SKILL.md under ~/.cursor/skills/agentsdk/.
103
147
  * Install still overwrites every shipped skill when the user confirms — this
104
148
  * helper is for messaging (fresh install vs update).
105
149
  */
@@ -125,18 +169,22 @@ export interface SkillInstallResult {
125
169
  installed: string[];
126
170
  /** Leaf dirs deleted because the package no longer ships them. */
127
171
  removed: string[];
172
+ /** True when leftover `~/.cursor/skills/agentkit` was deleted. */
173
+ removedLegacyNamespace: boolean;
128
174
  }
129
175
 
130
176
  /**
131
- * Copy each skill directory into `skillsHome/agentkit/<leafDir>/`.
177
+ * Copy each skill directory into `skillsHome/agentsdk/<leafDir>/`.
132
178
  * Always overwrites so a package upgrade refreshes local Cursor skills.
133
- * Strips monorepo-only frontmatter and removes leaf dirs no longer shipped.
179
+ * Strips monorepo-only frontmatter, sets `alwaysApply: true` so Cursor
180
+ * loads the body into context, and removes leaf dirs no longer shipped.
181
+ * Also deletes leftover `skillsHome/agentkit/` from the previous namespace.
134
182
  */
135
183
  export async function installCursorSkills(
136
184
  skills: readonly PackageSkill[],
137
185
  skillsHome: string = cursorSkillsHome()
138
186
  ): Promise<SkillInstallResult> {
139
- const namespaceDir = agentkitCursorSkillsDir(skillsHome);
187
+ const namespaceDir = agentsdkCursorSkillsDir(skillsHome);
140
188
  await mkdir(namespaceDir, { recursive: true });
141
189
 
142
190
  const removed = await orphanLeaves(namespaceDir, skills);
@@ -161,7 +209,9 @@ export async function installCursorSkills(
161
209
  }
162
210
  installed.push(skill.leafDir);
163
211
  }
164
- return { installed, removed };
212
+ const removedLegacyNamespace =
213
+ await removeLegacyCursorSkillsNamespace(skillsHome);
214
+ return { installed, removed, removedLegacyNamespace };
165
215
  }
166
216
 
167
217
  async function makeTreeWritable(root: string): Promise<void> {
@@ -202,13 +252,43 @@ async function orphanLeaves(
202
252
  .map((entry) => entry.name);
203
253
  }
204
254
 
205
- /** Drop monorepo `paths` globs and fix package doc links for ~/.cursor installs. */
255
+ /** Drop leftover `skillsHome/agentkit` from the previous install namespace. */
256
+ async function removeLegacyCursorSkillsNamespace(
257
+ skillsHome: string
258
+ ): Promise<boolean> {
259
+ const legacyDir = legacyAgentkitCursorSkillsDir(skillsHome);
260
+ try {
261
+ await stat(legacyDir);
262
+ } catch {
263
+ return false;
264
+ }
265
+ await rm(legacyDir, { recursive: true, force: true });
266
+ return true;
267
+ }
268
+
269
+ async function hasLegacyCursorSkillsNamespace(
270
+ skillsHome: string
271
+ ): Promise<boolean> {
272
+ try {
273
+ await stat(legacyAgentkitCursorSkillsDir(skillsHome));
274
+ return true;
275
+ } catch {
276
+ return false;
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Drop monorepo `paths` globs, set `alwaysApply: true` so Cursor injects the
282
+ * body as a global rule, and fix package doc links for ~/.cursor installs.
283
+ */
206
284
  export function prepareSkillMarkdownForCursorInstall(content: string): string {
207
285
  const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
208
286
  if (match === null) {
209
287
  return content;
210
288
  }
211
- const frontmatter = stripPathsFromFrontmatter(match[1] ?? "");
289
+ const frontmatter = withAlwaysApply(
290
+ stripPathsFromFrontmatter(match[1] ?? "")
291
+ );
212
292
  const body = content.slice(match[0].length);
213
293
  const adjustedBody = body.replace(
214
294
  /\[`docs\/([^`]+)`\]\(\.\.\/\.\.\/docs\/[^)]+\)/g,
@@ -235,11 +315,24 @@ function stripPathsFromFrontmatter(frontmatter: string): string {
235
315
  return out.join("\n");
236
316
  }
237
317
 
318
+ /** Cursor loads `alwaysApply: true` skills as global rules, not catalog rows. */
319
+ function withAlwaysApply(frontmatter: string): string {
320
+ const without = frontmatter
321
+ .split(/\r?\n/)
322
+ .filter((line) => !/^\s*alwaysApply\s*:/.test(line))
323
+ .join("\n")
324
+ .replace(/\n+$/, "");
325
+ if (without === "") {
326
+ return "alwaysApply: true";
327
+ }
328
+ return `${without}\nalwaysApply: true`;
329
+ }
330
+
238
331
  export interface SkillInstallPlan {
239
332
  shipped: PackageSkill[];
240
333
  /** Resolved `~/.cursor/skills`, to install into. */
241
334
  skillsHome: string;
242
- /** Install target: `<skillsHome>/agentkit`. */
335
+ /** Install target: `<skillsHome>/agentsdk`. */
243
336
  directory: string;
244
337
  /** Leaf dirs the package ships. */
245
338
  leaves: string[];
@@ -247,6 +340,8 @@ export interface SkillInstallPlan {
247
340
  firstInstall: boolean;
248
341
  /** Installed leaf dirs the package dropped; installing deletes them. */
249
342
  orphans: string[];
343
+ /** True when leftover `skillsHome/agentkit` still exists. */
344
+ hasLegacyNamespace: boolean;
250
345
  }
251
346
 
252
347
  /** What an install would do, for the offer and for `install-skills`. */
@@ -255,19 +350,26 @@ export async function planSkillInstall(options?: {
255
350
  skillsHome?: string;
256
351
  }): Promise<SkillInstallPlan> {
257
352
  const skillsHome = options?.skillsHome ?? cursorSkillsHome();
258
- const directory = agentkitCursorSkillsDir(skillsHome);
353
+ const directory = agentsdkCursorSkillsDir(skillsHome);
259
354
  const shipped = await listShippedPackageSkills(options?.packageRoot);
260
- const missing = await findMissingCursorSkills({
261
- packageRoot: options?.packageRoot,
262
- skillsHome,
263
- });
355
+ const present = await Promise.all(
356
+ shipped.map(async (skill) => {
357
+ try {
358
+ await access(join(skillInstallPath(skill, skillsHome), "SKILL.md"));
359
+ return true;
360
+ } catch {
361
+ return false;
362
+ }
363
+ })
364
+ );
264
365
  return {
265
366
  shipped,
266
367
  skillsHome,
267
368
  directory,
268
369
  leaves: shipped.map((skill) => skill.leafDir),
269
- firstInstall: missing.length === shipped.length,
370
+ firstInstall: present.every((isPresent) => !isPresent),
270
371
  orphans: await orphanLeaves(directory, shipped),
372
+ hasLegacyNamespace: await hasLegacyCursorSkillsNamespace(skillsHome),
271
373
  };
272
374
  }
273
375
 
@@ -282,10 +384,73 @@ export function formatSkillInstall(
282
384
  if (result.removed.length > 0) {
283
385
  lines.push(` removed: ${result.removed.join(", ")}`);
284
386
  }
387
+ if (result.removedLegacyNamespace) {
388
+ lines.push(
389
+ ` removed: ${legacyAgentkitCursorSkillsDir(plan.skillsHome)} (legacy namespace)`
390
+ );
391
+ }
285
392
  lines.push(` location: ${plan.directory}`);
286
393
  return `${lines.join("\n")}\n`;
287
394
  }
288
395
 
396
+ export type PackageInstallSkillSyncResult =
397
+ | { status: "installed"; installed: string[]; removed: string[] }
398
+ | {
399
+ status: "skipped";
400
+ reason: "opt-out" | "not-consumer" | "no-skills" | "error";
401
+ };
402
+
403
+ export interface SyncCursorSkillsOnPackageInstallOptions {
404
+ packageRoot?: string;
405
+ skillsHome?: string;
406
+ env?: NodeJS.ProcessEnv;
407
+ /** Override the node_modules consumer check. */
408
+ consumer?: boolean;
409
+ out?: (text: string) => void;
410
+ }
411
+
412
+ /**
413
+ * Copy shipped coding-agent skills into ~/.cursor/skills/agentsdk during a
414
+ * consumer `npm install` / `npx`. Never throws: a failed copy must not fail
415
+ * the package install. No-op in the monorepo source tree and when
416
+ * `CURSOR_JULY_SKIP_SKILL_INSTALL=1`.
417
+ */
418
+ export async function syncCursorSkillsOnPackageInstall(
419
+ options: SyncCursorSkillsOnPackageInstallOptions = {}
420
+ ): Promise<PackageInstallSkillSyncResult> {
421
+ try {
422
+ const env = options.env ?? process.env;
423
+ if (env[CURSOR_JULY_SKIP_SKILL_INSTALL_ENV] === "1") {
424
+ return { status: "skipped", reason: "opt-out" };
425
+ }
426
+
427
+ const packageRoot = options.packageRoot ?? packageRootDir();
428
+ const consumer = options.consumer ?? isConsumerPackageInstall(packageRoot);
429
+ if (!consumer) {
430
+ return { status: "skipped", reason: "not-consumer" };
431
+ }
432
+
433
+ const plan = await planSkillInstall({
434
+ packageRoot,
435
+ skillsHome: options.skillsHome,
436
+ });
437
+ if (plan.shipped.length === 0) {
438
+ return { status: "skipped", reason: "no-skills" };
439
+ }
440
+
441
+ const result = await installCursorSkills(plan.shipped, plan.skillsHome);
442
+ const out = options.out ?? ((text) => process.stderr.write(text));
443
+ out(formatSkillInstall(plan, result));
444
+ return {
445
+ status: "installed",
446
+ installed: result.installed,
447
+ removed: result.removed,
448
+ };
449
+ } catch {
450
+ return { status: "skipped", reason: "error" };
451
+ }
452
+ }
453
+
289
454
  export interface OfferInstallCursorSkillsOptions {
290
455
  /** Skip when false (non-TTY / `--json`). */
291
456
  interactive?: boolean;
@@ -298,7 +463,7 @@ export interface OfferInstallCursorSkillsOptions {
298
463
 
299
464
  /**
300
465
  * On a TTY, offer to install/update every shipped package skill into
301
- * ~/.cursor/skills/agentkit (always overwriting). No-op when non-interactive
466
+ * ~/.cursor/skills/agentsdk (always overwriting). No-op when non-interactive
302
467
  * or the package ships no skills. An unanswered prompt skips the install
303
468
  * rather than blocking the caller — see `SKILL_PROMPT_TIMEOUT_MS`.
304
469
  */
@@ -334,6 +499,13 @@ export async function offerInstallCursorSkills(
334
499
  if (plan.orphans.length > 0) {
335
500
  out(c.dim(` (deletes ${plan.orphans.join(", ")})\n`));
336
501
  }
502
+ if (plan.hasLegacyNamespace) {
503
+ out(
504
+ c.dim(
505
+ ` (deletes leftover ${legacyAgentkitCursorSkillsDir(plan.skillsHome)})\n`
506
+ )
507
+ );
508
+ }
337
509
 
338
510
  const confirm =
339
511
  options.confirm ??
@@ -365,23 +537,6 @@ export async function offerInstallCursorSkills(
365
537
  return { installed: result.installed, skipped: false };
366
538
  }
367
539
 
368
- function parseSkillName(skillMd: string): string | undefined {
369
- const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(skillMd);
370
- if (match === null) {
371
- return undefined;
372
- }
373
- const nameLine = /^name:\s*(.+)\s*$/m.exec(match[1] ?? "");
374
- if (nameLine === null) {
375
- return undefined;
376
- }
377
- const raw = nameLine[1]?.trim();
378
- if (raw === undefined || raw === "") {
379
- return undefined;
380
- }
381
- // Strip optional quotes around the YAML scalar.
382
- return raw.replace(/^["']|["']$/g, "");
383
- }
384
-
385
540
  export interface PromptYesNoOptions {
386
541
  defaultYes: boolean;
387
542
  out: (text: string) => void;
@@ -29,15 +29,36 @@ export function toolInputJsonSchema(
29
29
  if (inputSchema === undefined) {
30
30
  return ANY_OBJECT_SCHEMA;
31
31
  }
32
- if (isZodSchema(inputSchema)) {
33
- const converted = zodToJsonSchema(inputSchema as z.ZodTypeAny, {
32
+ return authoredSchemaToJsonSchema(inputSchema);
33
+ }
34
+
35
+ /**
36
+ * Convert an authored output schema into the JSON Schema advertised on the
37
+ * tool. Unlike {@link toolInputJsonSchema} there is no fallback: an
38
+ * undeclared output schema stays undeclared (the MCP field is optional), and
39
+ * outputs are never validated against it.
40
+ */
41
+ export function toolOutputJsonSchema(
42
+ outputSchema: z.ZodTypeAny | JsonSchemaObject | undefined
43
+ ): JsonSchemaObject | undefined {
44
+ if (outputSchema === undefined) {
45
+ return undefined;
46
+ }
47
+ return authoredSchemaToJsonSchema(outputSchema);
48
+ }
49
+
50
+ function authoredSchemaToJsonSchema(
51
+ schema: z.ZodTypeAny | JsonSchemaObject
52
+ ): JsonSchemaObject {
53
+ if (isZodSchema(schema)) {
54
+ const converted = zodToJsonSchema(schema as z.ZodTypeAny, {
34
55
  $refStrategy: "none",
35
56
  target: "jsonSchema7",
36
57
  }) as Record<string, unknown>;
37
58
  delete converted.$schema;
38
59
  return converted;
39
60
  }
40
- return inputSchema;
61
+ return schema;
41
62
  }
42
63
 
43
64
  export type ToolInputValidation =
@@ -218,6 +218,9 @@ function buildToolMcpServer(
218
218
  name: tool.name,
219
219
  description: tool.description,
220
220
  inputSchema: tool.inputSchema,
221
+ ...(tool.outputSchema === undefined
222
+ ? {}
223
+ : { outputSchema: tool.outputSchema }),
221
224
  })),
222
225
  })
223
226
  );
@@ -298,6 +301,9 @@ function buildConnectionBridgeMcpServer(
298
301
  ? {}
299
302
  : { description: tool.description }),
300
303
  inputSchema: tool.inputSchema ?? { type: "object" },
304
+ ...(tool.outputSchema === undefined
305
+ ? {}
306
+ : { outputSchema: tool.outputSchema }),
301
307
  })),
302
308
  };
303
309
  });
@@ -217,6 +217,7 @@ function toToolInfo(tool: {
217
217
  name: string;
218
218
  description?: string;
219
219
  inputSchema?: unknown;
220
+ outputSchema?: unknown;
220
221
  }): HostMcpToolInfo {
221
222
  return {
222
223
  name: tool.name,
@@ -226,6 +227,9 @@ function toToolInfo(tool: {
226
227
  ...(tool.inputSchema === undefined
227
228
  ? {}
228
229
  : { inputSchema: tool.inputSchema as JsonObject }),
230
+ ...(tool.outputSchema === undefined
231
+ ? {}
232
+ : { outputSchema: tool.outputSchema as JsonObject }),
229
233
  };
230
234
  }
231
235
 
@@ -259,8 +259,52 @@ type ProviderArgs = {
259
259
  onRedirect?: (url: URL) => void;
260
260
  tokens?: OAuthTokens;
261
261
  clientInformation?: OAuthClientInformationFull;
262
+ /**
263
+ * True when authorize/token requests must NOT carry the RFC 8707
264
+ * `resource` indicator, read off the server's protected-resource
265
+ * metadata (`cursor_omit_resource_indicator`). Entra rejects the
266
+ * indicator with AADSTS9010010, so Microsoft 365 flows through Cursor's
267
+ * REST-as-MCP translator fail pre-auth without this.
268
+ */
269
+ omitResourceIndicator?: boolean;
262
270
  };
263
271
 
272
+ /**
273
+ * Reads the Cursor REST-as-MCP translator's OAuth quirk off the resource's
274
+ * RFC 9728 protected-resource metadata. Deliberately self-contained
275
+ * (`@cursor/july` publishes to public npm with no private workspace
276
+ * dependencies — see `waitForMcpOAuthCallback`): matches the translator
277
+ * path shape on any origin so local translators work, and fails to the
278
+ * default behavior (indicator kept) when the PRM is unreachable.
279
+ */
280
+ export async function fetchOmitResourceIndicatorFromPrm(
281
+ resourceUrl: string,
282
+ fetchImpl: typeof fetch = fetch
283
+ ): Promise<boolean> {
284
+ let url: URL;
285
+ try {
286
+ url = new URL(resourceUrl);
287
+ } catch {
288
+ return false;
289
+ }
290
+ if (!/^\/rest-mcp\/[^/]+\/mcp\/?$/.test(url.pathname)) {
291
+ return false;
292
+ }
293
+ const prmUrl = `${url.origin}/.well-known/oauth-protected-resource${url.pathname.replace(/\/+$/, "")}`;
294
+ try {
295
+ const response = await fetchImpl(prmUrl);
296
+ if (!response.ok) {
297
+ return false;
298
+ }
299
+ const document = (await response.json()) as {
300
+ cursor_omit_resource_indicator?: unknown;
301
+ };
302
+ return document.cursor_omit_resource_indicator === true;
303
+ } catch {
304
+ return false;
305
+ }
306
+ }
307
+
264
308
  export class HostMcpOAuthProvider implements OAuthClientProvider {
265
309
  private tokensValue: OAuthTokens | undefined;
266
310
  private clientInfo: OAuthClientInformationFull | undefined;
@@ -268,11 +312,19 @@ export class HostMcpOAuthProvider implements OAuthClientProvider {
268
312
  private redirect: URL | undefined;
269
313
  private readonly meta: OAuthClientMetadata;
270
314
  private readonly args: ProviderArgs;
315
+ /**
316
+ * Assigned only when the PRM sets `cursor_omit_resource_indicator`; left
317
+ * absent otherwise so the SDK's default resource validation applies.
318
+ */
319
+ readonly validateResourceURL: (() => Promise<URL | undefined>) | undefined;
271
320
 
272
321
  constructor(args: ProviderArgs) {
273
322
  this.args = args;
274
323
  this.tokensValue = args.tokens;
275
324
  this.clientInfo = args.clientInformation;
325
+ if (args.omitResourceIndicator === true) {
326
+ this.validateResourceURL = async () => undefined;
327
+ }
276
328
  this.meta = {
277
329
  redirect_uris: [args.redirectUrl ?? MCP_OAUTH_CALLBACK_URL],
278
330
  token_endpoint_auth_method: "none",
@@ -366,9 +418,10 @@ export async function createHostMcpOAuthProvider(args: {
366
418
  onRedirect?: (url: URL) => void;
367
419
  }): Promise<HostMcpOAuthProvider> {
368
420
  const store = args.store ?? new FileMcpOAuthStore();
369
- const [fileTokens, fileClient] = await Promise.all([
421
+ const [fileTokens, fileClient, omitResourceIndicator] = await Promise.all([
370
422
  store.loadTokens(args.identifier, args.resourceUrl),
371
423
  store.loadClientInformation(args.identifier, args.resourceUrl),
424
+ fetchOmitResourceIndicatorFromPrm(args.resourceUrl),
372
425
  ]);
373
426
  return new HostMcpOAuthProvider({
374
427
  identifier: args.identifier,
@@ -377,6 +430,7 @@ export async function createHostMcpOAuthProvider(args: {
377
430
  tokens: fileTokens ?? tokensFromEnv(args.identifier),
378
431
  clientInformation: fileClient ?? clientIdFromEnv(args.identifier),
379
432
  onRedirect: args.onRedirect,
433
+ omitResourceIndicator,
380
434
  });
381
435
  }
382
436
 
@@ -14,7 +14,7 @@ import type {
14
14
  JsonSchemaObject,
15
15
  } from "../types.js";
16
16
  import type { InlineStdioCatalogEntry } from "./inline-stdio-mcp.js";
17
- import { toolInputJsonSchema } from "./json-schema.js";
17
+ import { toolInputJsonSchema, toolOutputJsonSchema } from "./json-schema.js";
18
18
 
19
19
  const require = createRequire(import.meta.url);
20
20
 
@@ -35,9 +35,13 @@ function jsonSchemaCatalog(
35
35
  ): Record<string, InlineStdioCatalogEntry> {
36
36
  const catalog: Record<string, InlineStdioCatalogEntry> = {};
37
37
  for (const [name, tool] of Object.entries(tools)) {
38
+ const outputSchema = toolOutputJsonSchema(tool.outputSchema);
38
39
  catalog[name] = {
39
40
  description: tool.description,
40
41
  inputSchema: toolInputJsonSchema(tool.inputSchema) as JsonSchemaObject,
42
+ ...(outputSchema === undefined
43
+ ? {}
44
+ : { outputSchema: outputSchema as JsonSchemaObject }),
41
45
  };
42
46
  }
43
47
  return catalog;