@cursor/july 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +16 -5
- package/README.md +8 -9
- package/dist/bin/agent-serve.d.ts +3 -1
- package/dist/bin/agent-serve.d.ts.map +1 -1
- package/dist/bin/agent-serve.js +416 -155
- package/dist/channels/slack/log.d.ts +1 -1
- package/dist/channels/slack/log.d.ts.map +1 -1
- package/dist/channels/slack/log.js +4 -5
- package/dist/channels/slack/setup.js +1 -1
- package/dist/channels/slack/socket-mode.d.ts.map +1 -1
- package/dist/channels/slack/socket-mode.js +10 -9
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.Oje4vhlk.js → app.DF-Yx4OP.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.0XrheBv2.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.H5XZ2zCB.js → VPLocalSearchBox.DSeJSudL.js} +1 -1
- package/dist/docs/assets/chunks/{theme.CTR_TuaE.js → theme.DZVLTXOC.js} +2 -2
- package/dist/docs/assets/{index.md.m81y7TY7.js → index.md.Cylk70gg.js} +1 -1
- package/dist/docs/assets/{quickstart.md.CfU8_uTC.js → quickstart.md.BU6Iwi_9.js} +18 -6
- package/dist/docs/assets/{reference_cli.md.DA730zCu.js → reference_cli.md.Bv6pOxcF.js} +11 -6
- package/dist/docs/assets/{reference_cli.md.DA730zCu.lean.js → reference_cli.md.Bv6pOxcF.lean.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/slack.html +3 -3
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +5 -5
- package/dist/docs/quickstart.html +22 -10
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +14 -9
- package/dist/docs/reference/connections.html +3 -3
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/internal/ab-collector.js +3 -3
- package/dist/internal/ab-fold.js +1 -1
- package/dist/internal/chat-client.d.ts +29 -3
- package/dist/internal/chat-client.d.ts.map +1 -1
- package/dist/internal/chat-client.js +180 -27
- package/dist/internal/cli-ax.d.ts +83 -2
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +626 -52
- package/dist/internal/cli-cursor.d.ts.map +1 -1
- package/dist/internal/cli-cursor.js +8 -36
- package/dist/internal/cli-deploy.d.ts +59 -3
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +316 -38
- package/dist/internal/cli-github.js +13 -13
- package/dist/internal/cursor/account-mcp.js +3 -3
- package/dist/internal/cursor/backend-client.js +1 -1
- package/dist/internal/cursor/credentials.d.ts +14 -3
- package/dist/internal/cursor/credentials.d.ts.map +1 -1
- package/dist/internal/cursor/credentials.js +21 -6
- package/dist/internal/cursor/github-credentials.js +3 -3
- package/dist/internal/cursor-event-relay.js +1 -1
- package/dist/internal/cursor-relay-core.d.ts +1 -1
- package/dist/internal/cursor-relay-core.d.ts.map +1 -1
- package/dist/internal/cursor-relay-core.js +2 -1
- package/dist/internal/cursor-slack-relay.js +1 -1
- package/dist/internal/deploy-client.d.ts +9 -1
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +23 -3
- package/dist/internal/deploy-source.d.ts +35 -0
- package/dist/internal/deploy-source.d.ts.map +1 -0
- package/dist/internal/deploy-source.js +118 -0
- package/dist/internal/discovery.d.ts +11 -0
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +43 -14
- package/dist/internal/distribution.d.ts +7 -0
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +7 -0
- package/dist/internal/docs-site.js +3 -3
- package/dist/internal/eval-run-store.d.ts.map +1 -1
- package/dist/internal/eval-run-store.js +10 -9
- package/dist/internal/eval-runner.d.ts +2 -0
- package/dist/internal/eval-runner.d.ts.map +1 -1
- package/dist/internal/eval-runner.js +2 -0
- package/dist/internal/event-mapper.d.ts +54 -1
- package/dist/internal/event-mapper.d.ts.map +1 -1
- package/dist/internal/event-mapper.js +151 -41
- package/dist/internal/github-fanout.js +2 -2
- package/dist/internal/host-platforms.js +5 -5
- package/dist/internal/init-project.d.ts +88 -1
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +221 -31
- package/dist/internal/install-cursor-skills.d.ts +64 -0
- package/dist/internal/install-cursor-skills.d.ts.map +1 -0
- package/dist/internal/install-cursor-skills.js +274 -0
- package/dist/internal/logs-client.d.ts +60 -0
- package/dist/internal/logs-client.d.ts.map +1 -0
- package/dist/internal/logs-client.js +311 -0
- package/dist/internal/mcp-endpoint.d.ts.map +1 -1
- package/dist/internal/mcp-endpoint.js +1 -1
- package/dist/internal/open-browser.d.ts +5 -0
- package/dist/internal/open-browser.d.ts.map +1 -0
- package/dist/internal/open-browser.js +34 -0
- package/dist/internal/playground/toolchain.d.ts.map +1 -1
- package/dist/internal/playground/toolchain.js +19 -14
- package/dist/internal/playground-proxy.d.ts +69 -0
- package/dist/internal/playground-proxy.d.ts.map +1 -0
- package/dist/internal/playground-proxy.js +468 -0
- package/dist/internal/prompt-context.d.ts +35 -0
- package/dist/internal/prompt-context.d.ts.map +1 -0
- package/dist/internal/prompt-context.js +71 -0
- package/dist/internal/reminder-runner.d.ts.map +1 -1
- package/dist/internal/reminder-runner.js +11 -11
- package/dist/internal/request-headers.d.ts +11 -0
- package/dist/internal/request-headers.d.ts.map +1 -0
- package/dist/internal/request-headers.js +14 -0
- package/dist/internal/resolve-prod-target.d.ts +44 -0
- package/dist/internal/resolve-prod-target.d.ts.map +1 -0
- package/dist/internal/resolve-prod-target.js +134 -0
- package/dist/internal/resolved-connections.js +5 -5
- package/dist/internal/run-client.d.ts +2 -2
- package/dist/internal/run-client.d.ts.map +1 -1
- package/dist/internal/run-client.js +38 -23
- package/dist/internal/schedule-runner.js +1 -1
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +23 -17
- package/dist/internal/session-engine.d.ts +1 -1
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +34 -19
- package/dist/internal/sessions-client.d.ts +34 -0
- package/dist/internal/sessions-client.d.ts.map +1 -0
- package/dist/internal/sessions-client.js +193 -0
- package/dist/internal/storage-coordinator.js +6 -6
- package/dist/internal/stream-progress.d.ts.map +1 -1
- package/dist/internal/stream-progress.js +31 -3
- package/dist/internal/terminal-style.d.ts +22 -0
- package/dist/internal/terminal-style.d.ts.map +1 -0
- package/dist/internal/terminal-style.js +49 -0
- package/dist/internal/trajectory.d.ts +10 -5
- package/dist/internal/trajectory.d.ts.map +1 -1
- package/dist/internal/trajectory.js +82 -10
- package/dist/internal/update-check.d.ts +93 -0
- package/dist/internal/update-check.d.ts.map +1 -0
- package/dist/internal/update-check.js +431 -0
- package/dist/internal/workspace.d.ts +11 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +38 -4
- package/dist/playground/assets/index-Ct_Usdm8.css +1 -0
- package/dist/playground/assets/index-Dsyb-Vaj.js +319 -0
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +29 -2
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +0 -1
- package/docs/quickstart.md +22 -7
- package/docs/reference/cli.md +51 -3
- package/package.json +4 -1
- package/skills/ab/SKILL.md +8 -8
- package/skills/create-agent/SKILL.md +28 -22
- package/skills/debug/SKILL.md +11 -11
- package/skills/evals/SKILL.md +16 -16
- package/skills/framework-map/SKILL.md +6 -6
- package/skills/github/SKILL.md +13 -13
- package/skills/hillclimb/SKILL.md +10 -10
- package/skills/setup-slack/SKILL.md +14 -14
- package/src/bin/agent-serve.ts +451 -191
- package/src/channels/slack/log.ts +5 -5
- package/src/channels/slack/setup.ts +1 -1
- package/src/channels/slack/socket-mode.ts +11 -9
- package/src/internal/ab-collector.ts +3 -3
- package/src/internal/ab-fold.ts +1 -1
- package/src/internal/chat-client.ts +252 -37
- package/src/internal/cli-ax.ts +761 -72
- package/src/internal/cli-cursor.ts +14 -42
- package/src/internal/cli-deploy.ts +428 -49
- package/src/internal/cli-github.ts +13 -15
- package/src/internal/cursor/account-mcp.ts +3 -3
- package/src/internal/cursor/backend-client.ts +1 -1
- package/src/internal/cursor/credentials.ts +26 -7
- package/src/internal/cursor/github-credentials.ts +3 -3
- package/src/internal/cursor-event-relay.ts +1 -1
- package/src/internal/cursor-relay-core.ts +4 -2
- package/src/internal/cursor-slack-relay.ts +1 -1
- package/src/internal/deploy-client.ts +45 -4
- package/src/internal/deploy-source.ts +133 -0
- package/src/internal/discovery.ts +53 -16
- package/src/internal/distribution.ts +8 -0
- package/src/internal/docs-site.ts +3 -3
- package/src/internal/eval-run-store.ts +10 -11
- package/src/internal/eval-runner.ts +5 -0
- package/src/internal/event-mapper.ts +222 -42
- package/src/internal/github-fanout.ts +2 -2
- package/src/internal/host-platforms.ts +5 -5
- package/src/internal/init-project.ts +333 -51
- package/src/internal/install-cursor-skills.ts +327 -0
- package/src/internal/logs-client.ts +442 -0
- package/src/internal/mcp-endpoint.ts +1 -3
- package/src/internal/open-browser.ts +41 -0
- package/src/internal/playground/toolchain.ts +18 -16
- package/src/internal/playground-proxy.ts +576 -0
- package/src/internal/prompt-context.ts +95 -0
- package/src/internal/reminder-runner.ts +11 -17
- package/src/internal/request-headers.ts +23 -0
- package/src/internal/resolve-prod-target.ts +182 -0
- package/src/internal/resolved-connections.ts +5 -5
- package/src/internal/run-client.ts +39 -36
- package/src/internal/schedule-runner.ts +1 -1
- package/src/internal/server.ts +25 -16
- package/src/internal/session-engine.ts +35 -18
- package/src/internal/sessions-client.ts +216 -0
- package/src/internal/storage-coordinator.ts +6 -6
- package/src/internal/stream-progress.ts +36 -2
- package/src/internal/terminal-style.ts +74 -0
- package/src/internal/trajectory.ts +91 -13
- package/src/internal/update-check.ts +552 -0
- package/src/internal/workspace.ts +42 -4
- package/src/types.ts +45 -7
- package/dist/docs/assets/chunks/@localSearchIndexroot.zwQ9RCQ7.js +0 -1
- package/dist/playground/assets/index-B1Qc9h2u.css +0 -1
- package/dist/playground/assets/index-CpDYCj8W.js +0 -79
- /package/dist/docs/assets/{index.md.m81y7TY7.lean.js → index.md.Cylk70gg.lean.js} +0 -0
- /package/dist/docs/assets/{quickstart.md.CfU8_uTC.lean.js → quickstart.md.BU6Iwi_9.lean.js} +0 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offer to install the package's coding-agent skills into the user's
|
|
3
|
+
* personal Cursor skills dir under `~/.cursor/skills/agentkit/<skill>/`.
|
|
4
|
+
*
|
|
5
|
+
* Cursor walks `~/.cursor/skills` recursively, so the `agentkit/` folder is
|
|
6
|
+
* only organizational — identity comes from the leaf folder that holds
|
|
7
|
+
* SKILL.md. Skipped for `--json` / non-TTY hosts.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
access,
|
|
12
|
+
cp,
|
|
13
|
+
mkdir,
|
|
14
|
+
readdir,
|
|
15
|
+
readFile,
|
|
16
|
+
rm,
|
|
17
|
+
writeFile,
|
|
18
|
+
} from "node:fs/promises";
|
|
19
|
+
import { homedir } from "node:os";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import { packageRootDir } from "./distribution.js";
|
|
22
|
+
import { stdoutPalette } from "./terminal-style.js";
|
|
23
|
+
|
|
24
|
+
/** Monorepo-only skill; excluded from the published package. */
|
|
25
|
+
const SKIP_SKILL_DIRS = new Set(["operate"]);
|
|
26
|
+
|
|
27
|
+
/** Category folder under ~/.cursor/skills (Cursor discovers nested skills). */
|
|
28
|
+
export const CURSOR_SKILLS_NAMESPACE = "agentkit";
|
|
29
|
+
|
|
30
|
+
export interface PackageSkill {
|
|
31
|
+
/** Frontmatter `name` (e.g. agentkit-create-agent). */
|
|
32
|
+
name: string;
|
|
33
|
+
/** Leaf install directory under agentkit/ (e.g. create-agent). */
|
|
34
|
+
leafDir: string;
|
|
35
|
+
/** Absolute path to the source skill directory in the package. */
|
|
36
|
+
sourceDir: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function cursorSkillsHome(
|
|
40
|
+
home: string = process.env.HOME ?? homedir()
|
|
41
|
+
): string {
|
|
42
|
+
const resolved = home.trim() === "" ? homedir() : home;
|
|
43
|
+
return join(resolved, ".cursor", "skills");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** `~/.cursor/skills/agentkit` — where package skills are installed. */
|
|
47
|
+
export function agentkitCursorSkillsDir(
|
|
48
|
+
skillsHome: string = cursorSkillsHome()
|
|
49
|
+
): string {
|
|
50
|
+
return join(skillsHome, CURSOR_SKILLS_NAMESPACE);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Absolute install path for one skill: `…/agentkit/<leafDir>`. */
|
|
54
|
+
export function skillInstallPath(
|
|
55
|
+
skill: Pick<PackageSkill, "leafDir">,
|
|
56
|
+
skillsHome: string = cursorSkillsHome()
|
|
57
|
+
): string {
|
|
58
|
+
return join(agentkitCursorSkillsDir(skillsHome), skill.leafDir);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Skills shipped next to the running package. Uses the frontmatter `name`
|
|
63
|
+
* when present, otherwise `agentkit-<dir>`.
|
|
64
|
+
*/
|
|
65
|
+
export async function listShippedPackageSkills(
|
|
66
|
+
packageRoot: string = packageRootDir()
|
|
67
|
+
): Promise<PackageSkill[]> {
|
|
68
|
+
const skillsRoot = join(packageRoot, "skills");
|
|
69
|
+
let entries: Array<{ name: string; isDirectory: () => boolean }>;
|
|
70
|
+
try {
|
|
71
|
+
entries = await readdir(skillsRoot, { withFileTypes: true });
|
|
72
|
+
} catch {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const skills: PackageSkill[] = [];
|
|
77
|
+
for (const entry of entries) {
|
|
78
|
+
if (!entry.isDirectory() || SKIP_SKILL_DIRS.has(entry.name)) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const sourceDir = join(skillsRoot, entry.name);
|
|
82
|
+
const skillMd = join(sourceDir, "SKILL.md");
|
|
83
|
+
try {
|
|
84
|
+
await access(skillMd);
|
|
85
|
+
} catch {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const raw = await readFile(skillMd, "utf8");
|
|
89
|
+
const name = parseSkillName(raw) ?? `agentkit-${entry.name}`;
|
|
90
|
+
skills.push({ name, leafDir: entry.name, sourceDir });
|
|
91
|
+
}
|
|
92
|
+
skills.sort((a, b) => a.leafDir.localeCompare(b.leafDir));
|
|
93
|
+
return skills;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Skills that do not yet have a SKILL.md under ~/.cursor/skills/agentkit/.
|
|
98
|
+
* Install still overwrites every shipped skill when the user confirms — this
|
|
99
|
+
* helper is for messaging (fresh install vs update).
|
|
100
|
+
*/
|
|
101
|
+
export async function findMissingCursorSkills(options?: {
|
|
102
|
+
packageRoot?: string;
|
|
103
|
+
skillsHome?: string;
|
|
104
|
+
}): Promise<PackageSkill[]> {
|
|
105
|
+
const shipped = await listShippedPackageSkills(options?.packageRoot);
|
|
106
|
+
const home = options?.skillsHome ?? cursorSkillsHome();
|
|
107
|
+
const missing: PackageSkill[] = [];
|
|
108
|
+
for (const skill of shipped) {
|
|
109
|
+
try {
|
|
110
|
+
await access(join(skillInstallPath(skill, home), "SKILL.md"));
|
|
111
|
+
} catch {
|
|
112
|
+
missing.push(skill);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return missing;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Copy each skill directory into `skillsHome/agentkit/<leafDir>/`.
|
|
120
|
+
* Always overwrites so a package upgrade refreshes local Cursor skills.
|
|
121
|
+
* Strips monorepo-only frontmatter and removes leaf dirs no longer shipped.
|
|
122
|
+
*/
|
|
123
|
+
export async function installCursorSkills(
|
|
124
|
+
skills: readonly PackageSkill[],
|
|
125
|
+
skillsHome: string = cursorSkillsHome()
|
|
126
|
+
): Promise<string[]> {
|
|
127
|
+
const namespaceDir = agentkitCursorSkillsDir(skillsHome);
|
|
128
|
+
await mkdir(namespaceDir, { recursive: true });
|
|
129
|
+
|
|
130
|
+
const shippedLeaves = new Set(skills.map((skill) => skill.leafDir));
|
|
131
|
+
let existingLeaves: string[] = [];
|
|
132
|
+
try {
|
|
133
|
+
const entries = await readdir(namespaceDir, { withFileTypes: true });
|
|
134
|
+
existingLeaves = entries
|
|
135
|
+
.filter((entry) => entry.isDirectory())
|
|
136
|
+
.map((entry) => entry.name);
|
|
137
|
+
} catch {
|
|
138
|
+
existingLeaves = [];
|
|
139
|
+
}
|
|
140
|
+
for (const leaf of existingLeaves) {
|
|
141
|
+
if (!shippedLeaves.has(leaf)) {
|
|
142
|
+
await rm(join(namespaceDir, leaf), { recursive: true, force: true });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const installed: string[] = [];
|
|
147
|
+
for (const skill of skills) {
|
|
148
|
+
const target = skillInstallPath(skill, skillsHome);
|
|
149
|
+
await cp(skill.sourceDir, target, { recursive: true, force: true });
|
|
150
|
+
const skillMdPath = join(target, "SKILL.md");
|
|
151
|
+
const raw = await readFile(skillMdPath, "utf8");
|
|
152
|
+
const adjusted = prepareSkillMarkdownForCursorInstall(raw);
|
|
153
|
+
if (adjusted !== raw) {
|
|
154
|
+
await writeFile(skillMdPath, adjusted, "utf8");
|
|
155
|
+
}
|
|
156
|
+
installed.push(skill.leafDir);
|
|
157
|
+
}
|
|
158
|
+
return installed;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Drop monorepo `paths` globs and fix package doc links for ~/.cursor installs. */
|
|
162
|
+
export function prepareSkillMarkdownForCursorInstall(content: string): string {
|
|
163
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
|
|
164
|
+
if (match === null) {
|
|
165
|
+
return content;
|
|
166
|
+
}
|
|
167
|
+
const frontmatter = stripPathsFromFrontmatter(match[1] ?? "");
|
|
168
|
+
const body = content.slice(match[0].length);
|
|
169
|
+
const adjustedBody = body.replace(
|
|
170
|
+
/\[`docs\/([^`]+)`\]\(\.\.\/\.\.\/docs\/[^)]+\)/g,
|
|
171
|
+
"docs/$1 in the @cursor/july package"
|
|
172
|
+
);
|
|
173
|
+
return `---\n${frontmatter.trimEnd()}\n---${adjustedBody}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function stripPathsFromFrontmatter(frontmatter: string): string {
|
|
177
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
178
|
+
const out: string[] = [];
|
|
179
|
+
for (let i = 0; i < lines.length; i++) {
|
|
180
|
+
const line = lines[i] ?? "";
|
|
181
|
+
if (line.trimStart().startsWith("paths:")) {
|
|
182
|
+
i++;
|
|
183
|
+
while (i < lines.length && /^[ \t]*-/.test(lines[i] ?? "")) {
|
|
184
|
+
i++;
|
|
185
|
+
}
|
|
186
|
+
i--;
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
out.push(line);
|
|
190
|
+
}
|
|
191
|
+
return out.join("\n");
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface OfferInstallCursorSkillsOptions {
|
|
195
|
+
/** Skip when false (non-TTY / `--json`). */
|
|
196
|
+
interactive?: boolean;
|
|
197
|
+
packageRoot?: string;
|
|
198
|
+
skillsHome?: string;
|
|
199
|
+
/** Injected yes/no for tests. */
|
|
200
|
+
confirm?: (question: string) => Promise<boolean>;
|
|
201
|
+
out?: (text: string) => void;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* On a TTY, offer to install/update every shipped package skill into
|
|
206
|
+
* ~/.cursor/skills/agentkit (always overwriting). No-op when non-interactive
|
|
207
|
+
* or the package ships no skills.
|
|
208
|
+
*/
|
|
209
|
+
export async function offerInstallCursorSkills(
|
|
210
|
+
options: OfferInstallCursorSkillsOptions = {}
|
|
211
|
+
): Promise<{ installed: string[]; skipped: boolean }> {
|
|
212
|
+
const out = options.out ?? ((text) => process.stdout.write(text));
|
|
213
|
+
const interactive =
|
|
214
|
+
options.interactive ??
|
|
215
|
+
(process.stdin.isTTY === true && process.stdout.isTTY === true);
|
|
216
|
+
const shipped = await listShippedPackageSkills(options.packageRoot);
|
|
217
|
+
if (shipped.length === 0) {
|
|
218
|
+
return { installed: [], skipped: true };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const home = options.skillsHome ?? cursorSkillsHome();
|
|
222
|
+
const targetDir = agentkitCursorSkillsDir(home);
|
|
223
|
+
const missing = await findMissingCursorSkills({
|
|
224
|
+
packageRoot: options.packageRoot,
|
|
225
|
+
skillsHome: home,
|
|
226
|
+
});
|
|
227
|
+
const names = shipped.map((skill) => skill.leafDir).join(", ");
|
|
228
|
+
const noun = shipped.length === 1 ? "skill" : "skills";
|
|
229
|
+
const verb = missing.length === shipped.length ? "Install" : "Update";
|
|
230
|
+
const question = `${verb} ${shipped.length} agentkit ${noun} in ${targetDir}?`;
|
|
231
|
+
|
|
232
|
+
if (!interactive) {
|
|
233
|
+
return { installed: [], skipped: true };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const c = stdoutPalette();
|
|
237
|
+
out(`${question}\n`);
|
|
238
|
+
out(c.dim(` ${names}\n`));
|
|
239
|
+
if (missing.length < shipped.length) {
|
|
240
|
+
out(c.dim(" (overwrites existing copies with the package version)\n"));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const confirm =
|
|
244
|
+
options.confirm ?? ((q) => promptYesNo(q, { defaultYes: true, out }));
|
|
245
|
+
const accepted = await confirm(
|
|
246
|
+
missing.length === shipped.length ? "Install now?" : "Update now?"
|
|
247
|
+
);
|
|
248
|
+
if (!accepted) {
|
|
249
|
+
out(c.dim("Skipped skill install.\n\n"));
|
|
250
|
+
return { installed: [], skipped: true };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const installed = await installCursorSkills(shipped, home);
|
|
254
|
+
out(
|
|
255
|
+
`${verb === "Install" ? "Installed" : "Updated"} ${installed.length} ${noun} in ${targetDir}\n\n`
|
|
256
|
+
);
|
|
257
|
+
return { installed, skipped: false };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function parseSkillName(skillMd: string): string | undefined {
|
|
261
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---/.exec(skillMd);
|
|
262
|
+
if (match === null) {
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
const nameLine = /^name:\s*(.+)\s*$/m.exec(match[1] ?? "");
|
|
266
|
+
if (nameLine === null) {
|
|
267
|
+
return undefined;
|
|
268
|
+
}
|
|
269
|
+
const raw = nameLine[1]?.trim();
|
|
270
|
+
if (raw === undefined || raw === "") {
|
|
271
|
+
return undefined;
|
|
272
|
+
}
|
|
273
|
+
// Strip optional quotes around the YAML scalar.
|
|
274
|
+
return raw.replace(/^["']|["']$/g, "");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async function promptYesNo(
|
|
278
|
+
question: string,
|
|
279
|
+
options: {
|
|
280
|
+
defaultYes: boolean;
|
|
281
|
+
out: (text: string) => void;
|
|
282
|
+
}
|
|
283
|
+
): Promise<boolean> {
|
|
284
|
+
const hint = options.defaultYes ? "Y/n" : "y/N";
|
|
285
|
+
options.out(`${question} [${hint}] `);
|
|
286
|
+
process.stdin.resume();
|
|
287
|
+
const line = await readOneLine(process.stdin);
|
|
288
|
+
process.stdin.pause();
|
|
289
|
+
if (line === undefined || line.trim() === "") {
|
|
290
|
+
return options.defaultYes;
|
|
291
|
+
}
|
|
292
|
+
const answer = line.trim().toLowerCase();
|
|
293
|
+
if (answer === "y" || answer === "yes") {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
if (answer === "n" || answer === "no") {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
return options.defaultYes;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function readOneLine(
|
|
303
|
+
stream: NodeJS.ReadableStream
|
|
304
|
+
): Promise<string | undefined> {
|
|
305
|
+
return new Promise((resolve) => {
|
|
306
|
+
let buffer = "";
|
|
307
|
+
const onData = (chunk: string | Buffer): void => {
|
|
308
|
+
buffer += typeof chunk === "string" ? chunk : chunk.toString("utf8");
|
|
309
|
+
const newline = buffer.indexOf("\n");
|
|
310
|
+
if (newline === -1) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
cleanup();
|
|
314
|
+
resolve(buffer.slice(0, newline).replace(/\r$/, ""));
|
|
315
|
+
};
|
|
316
|
+
const onEnd = (): void => {
|
|
317
|
+
cleanup();
|
|
318
|
+
resolve(buffer === "" ? undefined : buffer.replace(/\r$/, ""));
|
|
319
|
+
};
|
|
320
|
+
const cleanup = (): void => {
|
|
321
|
+
stream.off("data", onData);
|
|
322
|
+
stream.off("end", onEnd);
|
|
323
|
+
};
|
|
324
|
+
stream.on("data", onData);
|
|
325
|
+
stream.on("end", onEnd);
|
|
326
|
+
});
|
|
327
|
+
}
|