@cursor/july 0.1.53 → 0.1.55
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 +12 -0
- package/dist/channels/slack/manifest.d.ts +5 -3
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +7 -3
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.DAKcDclS.js → app.CVQxhV85.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BFpWSo6D.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BNIaCSfG.js → VPLocalSearchBox.QAPzJjql.js} +1 -1
- package/dist/docs/assets/chunks/{theme.B47GzJjc.js → theme.CSxTCOFu.js} +2 -2
- package/dist/docs/assets/{reference_cli.md.-V6J7Cl8.js → reference_cli.md.D_CCfG8R.js} +2 -0
- package/dist/docs/assets/templates_triage.md.DWuQ1bZz.js +9 -0
- package/dist/docs/assets/templates_triage.md.DWuQ1bZz.lean.js +1 -0
- 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/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +3 -3
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.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/mcp-oauth.html +3 -3
- package/dist/docs/guides/opentelemetry.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 +3 -3
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/artifacts.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +6 -4
- 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/prompt.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/templates/triage.html +14 -16
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +10 -1
- package/dist/internal/cli-deploy.js +1 -1
- package/dist/internal/cli-slack.d.ts +2 -0
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +35 -18
- package/dist/internal/init-project.d.ts +3 -0
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +77 -45
- package/dist/internal/slack-provision-client.js +1 -1
- package/dist/playground/assets/{index-BHAPd9KT.js → index-0oqXUkrR.js} +44 -44
- package/dist/playground/assets/index-tHCjJxv4.css +1 -0
- package/dist/playground/index.html +2 -2
- package/docs/reference/cli.md +2 -0
- package/docs/templates/triage.md +59 -101
- package/package.json +1 -1
- package/src/channels/slack/manifest.ts +9 -3
- package/src/internal/cli-ax.ts +10 -1
- package/src/internal/cli-deploy.ts +2 -2
- package/src/internal/cli-slack.ts +57 -18
- package/src/internal/init-project.ts +93 -48
- package/src/internal/slack-provision-client.ts +1 -1
- package/templates/triage/README.md +27 -29
- package/templates/triage/agent/agent.ts +1 -0
- package/templates/triage/agent/channels/queue.ts +47 -0
- package/templates/triage/agent/channels/webhook.ts +50 -0
- package/templates/triage/agent/instructions.md +24 -13
- package/templates/triage/agent/lib/actuate.ts +759 -0
- package/templates/triage/agent/lib/parse-queue-event.ts +168 -0
- package/templates/triage/agent/lib/tracker.ts +5 -0
- package/templates/triage/agent/lib/types.ts +52 -0
- package/templates/triage/agent/lib/verify-webhook.ts +81 -0
- package/templates/triage/agent/skills/triage-rubric.md +6 -5
- package/templates/triage/agent/tools/record_triage.ts +24 -5
- package/templates/triage/evals/triage.eval.ts +25 -3
- package/templates/triage/overlays/jira/README.md +21 -0
- package/templates/triage/overlays/jira/agent/lib/tracker.ts +8 -0
- package/templates/triage/overlays/jira/agent/mcp-connections/tracker.ts +7 -0
- package/templates/triage/overlays/linear/README.md +21 -0
- package/templates/triage/overlays/linear/agent/lib/tracker.ts +8 -0
- package/templates/triage/overlays/linear/agent/mcp-connections/tracker.ts +7 -0
- package/templates/triage/package.json +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.C4uxzYEU.js +0 -1
- package/dist/docs/assets/templates_triage.md.CConZt1z.js +0 -11
- package/dist/docs/assets/templates_triage.md.CConZt1z.lean.js +0 -1
- package/dist/playground/assets/index-DpKH0HcT.css +0 -1
- package/templates/triage/agent/channels/intake.ts +0 -20
- package/templates/triage/agent/channels/slack.ts +0 -3
- /package/dist/docs/assets/{reference_cli.md.-V6J7Cl8.lean.js → reference_cli.md.D_CCfG8R.lean.js} +0 -0
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
/>
|
|
9
9
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
10
10
|
<title>agent-serve playground</title>
|
|
11
|
-
<script type="module" crossorigin src="./assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
11
|
+
<script type="module" crossorigin src="./assets/index-0oqXUkrR.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/index-tHCjJxv4.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
package/docs/reference/cli.md
CHANGED
|
@@ -420,6 +420,8 @@ non-zero when the reconstructed trajectory failed.
|
|
|
420
420
|
|
|
421
421
|
```bash
|
|
422
422
|
agent-sdk init ./my-agent # scaffold package.json, tsconfig.json, agent/ + a demo tool
|
|
423
|
+
agent-sdk init ./my-triage --template triage-linear # comment on Linear issues
|
|
424
|
+
agent-sdk init ./my-triage --template triage-jira # comment on Jira issues
|
|
423
425
|
agent-sdk init ./my-agent --json # machine-readable summary for tooling
|
|
424
426
|
agent-sdk init # no directory: print the setup guide
|
|
425
427
|
```
|
package/docs/templates/triage.md
CHANGED
|
@@ -1,147 +1,105 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
3
|
-
description: "Scaffold the
|
|
2
|
+
title: "Triage Linear or Jira issues in place"
|
|
3
|
+
description: "Scaffold the agent, point it at an existing ticket, tune the rubric, run evals, and deploy."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# Triage Linear or Jira issues in place
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
replies with the next step. This page walks the whole arc: scaffold,
|
|
12
|
-
first verdict, customize, prove, wire Slack, deploy.
|
|
8
|
+
This agent comments on tickets you already filed. It classifies the
|
|
9
|
+
issue, writes a `Triage:` comment, sets bug priority, and links a
|
|
10
|
+
duplicate when titles match.
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
against. Nothing phones home to a template registry.
|
|
12
|
+
File the ticket however you already do. This template starts after the
|
|
13
|
+
ticket exists.
|
|
17
14
|
|
|
18
|
-
## Scaffold
|
|
15
|
+
## Scaffold
|
|
19
16
|
|
|
20
17
|
```bash
|
|
21
|
-
npx @cursor/july init ./triage-agent --template triage
|
|
18
|
+
npx @cursor/july init ./triage-agent --template triage-linear
|
|
19
|
+
npx @cursor/july init ./triage-agent --template triage-jira
|
|
22
20
|
```
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
sign-in when the machine has no Cursor credential. Model turns need
|
|
27
|
-
that credential, so sign in now rather than mid-flow:
|
|
22
|
+
Pick the tracker you use. The agent is the same. The overlay is the
|
|
23
|
+
connection.
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
```
|
|
25
|
+
`init` writes the project, installs dependencies, and puts `agent-sdk`
|
|
26
|
+
on PATH.
|
|
32
27
|
|
|
33
|
-
|
|
28
|
+
## Log in
|
|
34
29
|
|
|
35
|
-
|
|
30
|
+
Model turns and Cursor MCP use your Cursor account.
|
|
36
31
|
|
|
37
32
|
```bash
|
|
38
33
|
cd triage-agent
|
|
39
|
-
agent-sdk
|
|
34
|
+
agent-sdk login
|
|
40
35
|
```
|
|
41
36
|
|
|
42
|
-
|
|
37
|
+
The command opens a browser if you have no stored credential.
|
|
38
|
+
|
|
39
|
+
## Connect the tracker
|
|
43
40
|
|
|
44
|
-
|
|
45
|
-
> Retrying does not help.
|
|
41
|
+
In the Cursor dashboard, open MCP and connect Linear or Jira.
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
fewer. The tool call renders inline, so you see the exact kind,
|
|
50
|
-
severity, and next step it recorded.
|
|
43
|
+
The connection stays on the host. The model calls `record_triage`.
|
|
44
|
+
Linear and Jira tools stay off the model's tool list.
|
|
51
45
|
|
|
52
|
-
|
|
53
|
-
channel accepts reports over HTTP:
|
|
46
|
+
## First verdict
|
|
54
47
|
|
|
55
48
|
```bash
|
|
56
|
-
|
|
57
|
-
-H 'content-type: application/json' \
|
|
58
|
-
-d '{"message":"How do I rotate staging API keys?","reporter":"dana"}'
|
|
49
|
+
agent-sdk dev
|
|
59
50
|
```
|
|
60
51
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
## Make the rubric yours
|
|
64
|
-
|
|
65
|
-
`agent/skills/triage-rubric.md` holds the judgment: what counts as a
|
|
66
|
-
bug, the tie-breakers, and the severity ladder. Edit it first. Your
|
|
67
|
-
team's definition of `urgent` belongs there, in plain markdown, and the
|
|
68
|
-
next turn uses it without a restart step.
|
|
52
|
+
Open the playground. Run `/queue/issue` and paste an existing issue.
|
|
69
53
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
shape.
|
|
73
|
-
|
|
74
|
-
## Prove it with evals
|
|
75
|
-
|
|
76
|
-
The template ships three smoke cases, one per kind. Run them after
|
|
77
|
-
every rubric edit:
|
|
54
|
+
Local multi-agent mode mounts this project as `triage-agent`. You can
|
|
55
|
+
also POST:
|
|
78
56
|
|
|
79
57
|
```bash
|
|
80
|
-
|
|
58
|
+
curl -X POST http://127.0.0.1:3000/triage-agent/v1/channels/queue/issue \
|
|
59
|
+
-H 'content-type: application/json' \
|
|
60
|
+
-d '{
|
|
61
|
+
"identifier": "ENG-142",
|
|
62
|
+
"title": "CSV export returns 500",
|
|
63
|
+
"description": "Export has returned 500 since this morning."
|
|
64
|
+
}'
|
|
81
65
|
```
|
|
82
66
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
report style you tightened it for. The eval suite is the regression
|
|
86
|
-
net for prompt changes, so grow it with the rubric.
|
|
87
|
-
|
|
88
|
-
## File verdicts into your tracker
|
|
89
|
-
|
|
90
|
-
`agent/tools/record_triage.ts` defines the verdict shape with a zod
|
|
91
|
-
schema and returns it. Point its `execute` at Linear, GitHub Issues, or
|
|
92
|
-
a webhook to file real tickets. Test the tool without a model turn:
|
|
67
|
+
`POST /v1/channels/queue/issue` accepts
|
|
68
|
+
`{ identifier, title?, description? }` from the playground.
|
|
93
69
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
"summary": "Export returns 500 since this morning.",
|
|
100
|
-
"nextStep": "Check export service logs."
|
|
101
|
-
}'
|
|
102
|
-
```
|
|
70
|
+
Hosted Cursor aliases require `X-Agent-Alias-Token`, which Linear and
|
|
71
|
+
Jira cannot send. For create webhooks, self-host, set
|
|
72
|
+
`LINEAR_WEBHOOK_SECRET` or `JIRA_WEBHOOK_SECRET`, and POST the signed
|
|
73
|
+
body to `/v1/channels/webhook`. The channel ACKs immediately and
|
|
74
|
+
coalesces retries onto the same issue.
|
|
103
75
|
|
|
104
|
-
|
|
105
|
-
|
|
76
|
+
`record_triage` comments on the issue in the continuation key, not
|
|
77
|
+
whatever identifier the model copies out of the ticket body.
|
|
106
78
|
|
|
107
|
-
##
|
|
79
|
+
## Rubric
|
|
108
80
|
|
|
109
|
-
|
|
110
|
-
|
|
81
|
+
Edit `agent/skills/triage-rubric.md` for kinds, tie-breakers, and what
|
|
82
|
+
`urgent` means on your team. The next turn reads the file.
|
|
111
83
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
```
|
|
84
|
+
`agent/instructions.md` is the procedure around the rubric: missing
|
|
85
|
+
details, when to call `record_triage`, how to reply.
|
|
115
86
|
|
|
116
|
-
|
|
117
|
-
`.env.local`. Restart `agent-sdk dev`, invite the bot to a channel, and
|
|
118
|
-
@mention it with a report. The [Slack guide](../guides/slack.md) covers
|
|
119
|
-
channel watching and approvals when you outgrow mentions.
|
|
87
|
+
## Evals
|
|
120
88
|
|
|
121
|
-
|
|
89
|
+
Three smoke cases, one per kind. Run them after you change the rubric.
|
|
122
90
|
|
|
123
91
|
```bash
|
|
124
|
-
agent-sdk
|
|
92
|
+
agent-sdk eval
|
|
125
93
|
```
|
|
126
94
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
deployment secrets:
|
|
95
|
+
Eval sessions skip tracker writes.
|
|
96
|
+
|
|
97
|
+
## Deploy
|
|
131
98
|
|
|
132
99
|
```bash
|
|
133
|
-
agent-sdk secrets set SLACK_BOT_TOKEN SLACK_APP_TOKEN --slug triage-agent
|
|
134
100
|
agent-sdk deploy
|
|
135
101
|
```
|
|
136
102
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
## Where to go next
|
|
142
|
-
|
|
143
|
-
- Add a GitHub intake with the [GitHub guide](../guides/github.md), so
|
|
144
|
-
issue comments triage themselves.
|
|
145
|
-
- Persist verdicts across restarts with [storage](../storage.md).
|
|
146
|
-
- Measure rubric changes on live traffic with
|
|
147
|
-
[A/B metrics](../ab.md).
|
|
103
|
+
Run it from the agent's git checkout. It infers repository, ref, path,
|
|
104
|
+
and slug, builds on Cursor-managed hosting, and prints the URL.
|
|
105
|
+
`agent-sdk deployments` shows status.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cursor/july",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.55",
|
|
4
4
|
"description": "(early alpha) Filesystem-first framework for defining Cursor agents as markdown and TypeScript and serving them over channels with the Cursor SDK.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"repository": {
|
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
import type { SlackManifestOptions } from "./types.js";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Dashboard tab root: Slack's app-settings banner, and the only OAuth
|
|
15
|
+
* redirect URI we register. AAA request-approval (`oauth_authorize_url`)
|
|
16
|
+
* 400s with redirect_uri mismatch unless a URI is registered. This is
|
|
17
|
+
* not an install callback. `apps.managedInstall` still installs.
|
|
17
18
|
*/
|
|
18
19
|
export const SLACK_EXTERNAL_APP_MANAGEMENT_URL =
|
|
19
20
|
"https://cursor.com/dashboard/deployed-agents";
|
|
@@ -42,6 +43,7 @@ export interface SlackAppManifest {
|
|
|
42
43
|
scopes: {
|
|
43
44
|
bot: string[];
|
|
44
45
|
};
|
|
46
|
+
redirect_urls?: string[];
|
|
45
47
|
};
|
|
46
48
|
settings: {
|
|
47
49
|
event_subscriptions: {
|
|
@@ -174,6 +176,10 @@ export function buildSlackManifest(
|
|
|
174
176
|
scopes: {
|
|
175
177
|
bot: botScopes,
|
|
176
178
|
},
|
|
179
|
+
redirect_urls:
|
|
180
|
+
options.managedByCursor === false
|
|
181
|
+
? undefined
|
|
182
|
+
: [SLACK_EXTERNAL_APP_MANAGEMENT_URL],
|
|
177
183
|
},
|
|
178
184
|
settings: {
|
|
179
185
|
event_subscriptions: {
|
package/src/internal/cli-ax.ts
CHANGED
|
@@ -292,6 +292,8 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
|
|
|
292
292
|
return 0;
|
|
293
293
|
}
|
|
294
294
|
let templateDir: string | undefined;
|
|
295
|
+
let overlayDir: string | undefined;
|
|
296
|
+
let templateName: string | undefined;
|
|
295
297
|
if (options.template !== undefined) {
|
|
296
298
|
const template = resolveTemplate(options.template);
|
|
297
299
|
if (!template.ok) {
|
|
@@ -299,6 +301,8 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
|
|
|
299
301
|
return 1;
|
|
300
302
|
}
|
|
301
303
|
templateDir = template.dir;
|
|
304
|
+
overlayDir = template.overlayDir;
|
|
305
|
+
templateName = template.name;
|
|
302
306
|
}
|
|
303
307
|
const cwd = options.cwd ?? process.cwd();
|
|
304
308
|
const dir = resolve(cwd, target);
|
|
@@ -307,7 +311,12 @@ export async function cmdInit(options: AxCliOptions): Promise<number> {
|
|
|
307
311
|
process.stderr.write(`${targetCheck.message}\n`);
|
|
308
312
|
return 1;
|
|
309
313
|
}
|
|
310
|
-
const result = await initProject({
|
|
314
|
+
const result = await initProject({
|
|
315
|
+
dir,
|
|
316
|
+
templateDir,
|
|
317
|
+
overlayDir,
|
|
318
|
+
templateName,
|
|
319
|
+
});
|
|
311
320
|
let signedIn = (await resolveApiKey()) !== undefined;
|
|
312
321
|
|
|
313
322
|
// Machine-readable path: install deps (piped), never block on browser login.
|
|
@@ -223,8 +223,8 @@ export async function resolveDeployAuth(
|
|
|
223
223
|
`${dim(
|
|
224
224
|
`Using team ${teamId}${
|
|
225
225
|
identity.teamName === undefined ? "" : ` (${identity.teamName})`
|
|
226
|
-
}
|
|
227
|
-
)}\n`
|
|
226
|
+
}`
|
|
227
|
+
)}\n${dim(" the account's team — pass --team to override.")}\n`
|
|
228
228
|
);
|
|
229
229
|
}
|
|
230
230
|
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
type SlackWizardMintResult,
|
|
35
35
|
type SlackWizardPollResult,
|
|
36
36
|
} from "./slack-provision-client.js";
|
|
37
|
+
import { stderrPalette, type TerminalPalette } from "./terminal-style.js";
|
|
37
38
|
|
|
38
39
|
export interface SlackCliOptions {
|
|
39
40
|
dir: string;
|
|
@@ -71,6 +72,7 @@ export interface SlackCliOptions {
|
|
|
71
72
|
doctorImpl?: typeof runSlackDoctor;
|
|
72
73
|
out?: (text: string) => void;
|
|
73
74
|
err?: (text: string) => void;
|
|
75
|
+
palette?: TerminalPalette;
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
const SLACK_HELP = `${CLI} slack — Slack channel pack helpers (Socket Mode)
|
|
@@ -198,6 +200,7 @@ interface SlackProvisionContext {
|
|
|
198
200
|
json: boolean;
|
|
199
201
|
out: (text: string) => void;
|
|
200
202
|
err: (text: string) => void;
|
|
203
|
+
dim: (s: string) => string;
|
|
201
204
|
openBrowserImpl: (url: string) => void;
|
|
202
205
|
pollIntervalMs: number;
|
|
203
206
|
pollTimeoutMs: number;
|
|
@@ -373,6 +376,7 @@ async function prepareProvision(
|
|
|
373
376
|
json: options.json === true,
|
|
374
377
|
out,
|
|
375
378
|
err,
|
|
379
|
+
dim: (options.palette ?? stderrPalette()).dim,
|
|
376
380
|
openBrowserImpl: options.openBrowserImpl ?? openBrowser,
|
|
377
381
|
pollIntervalMs:
|
|
378
382
|
options.connectPollIntervalMs ?? DEFAULT_WIZARD_POLL_INTERVAL_MS,
|
|
@@ -381,6 +385,40 @@ async function prepareProvision(
|
|
|
381
385
|
};
|
|
382
386
|
}
|
|
383
387
|
|
|
388
|
+
function writePendingAdminWait(
|
|
389
|
+
ctx: SlackProvisionContext,
|
|
390
|
+
poll: SlackWizardPollResult
|
|
391
|
+
): void {
|
|
392
|
+
const appSuffix = poll.appId === undefined ? "" : ` (${poll.appId})`;
|
|
393
|
+
const requestUrl =
|
|
394
|
+
poll.oauthAuthorizeUrl !== undefined && poll.oauthAuthorizeUrl.length > 0
|
|
395
|
+
? poll.oauthAuthorizeUrl
|
|
396
|
+
: undefined;
|
|
397
|
+
ctx.err(`Waiting for a Slack admin to approve the app${appSuffix}.\n\n`);
|
|
398
|
+
if (requestUrl !== undefined) {
|
|
399
|
+
ctx.err(
|
|
400
|
+
ctx.dim(
|
|
401
|
+
` Slack will notify workspace owners once the request is in their queue.\n` +
|
|
402
|
+
` Open this page to file it or send a reminder:\n\n`
|
|
403
|
+
)
|
|
404
|
+
);
|
|
405
|
+
ctx.err(` ${requestUrl}\n\n`);
|
|
406
|
+
ctx.openBrowserImpl(requestUrl);
|
|
407
|
+
} else {
|
|
408
|
+
ctx.err(
|
|
409
|
+
ctx.dim(
|
|
410
|
+
` Slack will notify workspace owners once the request is in their queue.\n\n`
|
|
411
|
+
)
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
ctx.err(
|
|
415
|
+
ctx.dim(
|
|
416
|
+
` Keep this command running.\n` +
|
|
417
|
+
` After an admin approves, click Retry in the dashboard.\n`
|
|
418
|
+
)
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
|
|
384
422
|
export async function cmdSlackCreate(
|
|
385
423
|
options: SlackCliOptions
|
|
386
424
|
): Promise<number> {
|
|
@@ -412,7 +450,9 @@ export async function cmdSlackCreate(
|
|
|
412
450
|
return 1;
|
|
413
451
|
}
|
|
414
452
|
if (wrote.length > 0 && !ctx.json) {
|
|
415
|
-
ctx.err(
|
|
453
|
+
ctx.err(
|
|
454
|
+
`${ctx.dim(`Scaffolded the Slack channel: ${wrote.join(", ")}`)}\n`
|
|
455
|
+
);
|
|
416
456
|
}
|
|
417
457
|
|
|
418
458
|
const source = await inferDeployGitSource(target.dir);
|
|
@@ -436,10 +476,13 @@ export async function cmdSlackCreate(
|
|
|
436
476
|
return renderSlackApiError(error, ctx.err);
|
|
437
477
|
}
|
|
438
478
|
|
|
479
|
+
const waitMinutes = Math.round(ctx.pollTimeoutMs / 60_000);
|
|
480
|
+
ctx.err(`Finish Slack setup in the browser:\n\n ${minted.url}\n\n`);
|
|
439
481
|
ctx.err(
|
|
440
|
-
|
|
441
|
-
`Sign in as the same Cursor account that ran this command
|
|
442
|
-
|
|
482
|
+
ctx.dim(
|
|
483
|
+
` Sign in as the same Cursor account that ran this command.\n` +
|
|
484
|
+
` The CLI waits here (up to ${waitMinutes} minutes).\n`
|
|
485
|
+
)
|
|
443
486
|
);
|
|
444
487
|
ctx.openBrowserImpl(minted.url);
|
|
445
488
|
|
|
@@ -465,23 +508,16 @@ export async function cmdSlackCreate(
|
|
|
465
508
|
}
|
|
466
509
|
if (poll.status === "pending_admin_approval" && !sawPendingAdmin) {
|
|
467
510
|
sawPendingAdmin = true;
|
|
468
|
-
|
|
469
|
-
ctx.err(
|
|
470
|
-
`Waiting for a Slack admin to approve the app${appSuffix}. Keep this command running.\n` +
|
|
471
|
-
`Managed install does not file the request. Open Slack to submit it; that same page lets you send a reminder to workspace owners.\n`
|
|
472
|
-
);
|
|
473
|
-
if (
|
|
474
|
-
poll.oauthAuthorizeUrl !== undefined &&
|
|
475
|
-
poll.oauthAuthorizeUrl.length > 0
|
|
476
|
-
) {
|
|
477
|
-
ctx.err(` ${poll.oauthAuthorizeUrl}\n`);
|
|
478
|
-
}
|
|
479
|
-
ctx.err("After an admin approves, click Retry in the dashboard.\n");
|
|
511
|
+
writePendingAdminWait(ctx, poll);
|
|
480
512
|
}
|
|
481
513
|
if (Date.now() >= deadline) {
|
|
514
|
+
ctx.err(`Timed out waiting for Slack setup.\n\n`);
|
|
482
515
|
ctx.err(
|
|
483
|
-
|
|
516
|
+
ctx.dim(
|
|
517
|
+
` Finish it in the browser, then re-run \`${CLI} slack create\`.\n\n`
|
|
518
|
+
)
|
|
484
519
|
);
|
|
520
|
+
ctx.err(` ${minted.url}\n`);
|
|
485
521
|
return 1;
|
|
486
522
|
}
|
|
487
523
|
await sleep(ctx.pollIntervalMs);
|
|
@@ -518,7 +554,10 @@ export async function cmdSlackCreate(
|
|
|
518
554
|
}
|
|
519
555
|
if (poll.localEnv.appToken === undefined) {
|
|
520
556
|
ctx.err(
|
|
521
|
-
|
|
557
|
+
ctx.dim(
|
|
558
|
+
`warning: Slack did not return an app-level token, so ${target.envKeys.app} was not written.\n` +
|
|
559
|
+
` Re-run \`${CLI} slack create\` to recover it.\n`
|
|
560
|
+
)
|
|
522
561
|
);
|
|
523
562
|
}
|
|
524
563
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
-
import { existsSync,
|
|
6
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
7
7
|
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
8
8
|
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
9
9
|
import type { ModelSetting } from "../types.js";
|
|
@@ -27,6 +27,8 @@ export interface InitProjectOptions {
|
|
|
27
27
|
/** Model id override written into agent.ts. */
|
|
28
28
|
model?: string;
|
|
29
29
|
templateDir?: string;
|
|
30
|
+
overlayDir?: string;
|
|
31
|
+
templateName?: string;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
export type InitFileAction = "create" | "exist";
|
|
@@ -46,23 +48,21 @@ export interface InitProjectResult {
|
|
|
46
48
|
skipped: string[];
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
const TRIAGE_OVERLAYS: Readonly<Record<string, string>> = {
|
|
52
|
+
"triage-linear": "linear",
|
|
53
|
+
"triage-jira": "jira",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
function canonicalTemplateName(name: string): string {
|
|
57
|
+
return name === "triage" ? "triage-linear" : name;
|
|
58
|
+
}
|
|
59
|
+
|
|
49
60
|
export function availableTemplates(): string[] {
|
|
50
|
-
|
|
51
|
-
try {
|
|
52
|
-
return readdirSync(templatesDir, { withFileTypes: true })
|
|
53
|
-
.filter((entry) => entry.isDirectory())
|
|
54
|
-
.map((entry) => entry.name)
|
|
55
|
-
.sort();
|
|
56
|
-
} catch (error) {
|
|
57
|
-
if (hasErrorCode(error, "ENOENT")) {
|
|
58
|
-
return [];
|
|
59
|
-
}
|
|
60
|
-
throw error;
|
|
61
|
-
}
|
|
61
|
+
return Object.keys(TRIAGE_OVERLAYS).sort();
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
export type TemplateResolution =
|
|
65
|
-
| { ok: true; name: string; dir: string }
|
|
65
|
+
| { ok: true; name: string; dir: string; overlayDir?: string }
|
|
66
66
|
| { ok: false; message: string };
|
|
67
67
|
|
|
68
68
|
export function resolveTemplate(name: string): TemplateResolution {
|
|
@@ -72,21 +72,20 @@ export function resolveTemplate(name: string): TemplateResolution {
|
|
|
72
72
|
message: "template name must match /^[a-z][a-z0-9-]*$/",
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
? "no templates ship with this build"
|
|
80
|
-
: `available: ${available.join(", ")}`;
|
|
75
|
+
const resolved = canonicalTemplateName(name);
|
|
76
|
+
const overlay = TRIAGE_OVERLAYS[resolved];
|
|
77
|
+
if (overlay !== undefined) {
|
|
78
|
+
const dir = join(packageRootDir(), "templates", "triage");
|
|
81
79
|
return {
|
|
82
|
-
ok:
|
|
83
|
-
|
|
80
|
+
ok: true,
|
|
81
|
+
name: resolved,
|
|
82
|
+
dir,
|
|
83
|
+
overlayDir: join(dir, "overlays", overlay),
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
return {
|
|
87
|
-
ok:
|
|
88
|
-
name,
|
|
89
|
-
dir: join(packageRootDir(), "templates", name),
|
|
87
|
+
ok: false,
|
|
88
|
+
message: `unknown template "${name}" (available: ${availableTemplates().join(", ")})`,
|
|
90
89
|
};
|
|
91
90
|
}
|
|
92
91
|
|
|
@@ -121,6 +120,13 @@ export function checkInitTarget(
|
|
|
121
120
|
if (marker === template) {
|
|
122
121
|
return { ok: true };
|
|
123
122
|
}
|
|
123
|
+
if (
|
|
124
|
+
marker !== undefined &&
|
|
125
|
+
template !== undefined &&
|
|
126
|
+
canonicalTemplateName(marker) === canonicalTemplateName(template)
|
|
127
|
+
) {
|
|
128
|
+
return { ok: true };
|
|
129
|
+
}
|
|
124
130
|
if (template === undefined) {
|
|
125
131
|
return {
|
|
126
132
|
ok: false,
|
|
@@ -194,28 +200,31 @@ export async function initProject(
|
|
|
194
200
|
|
|
195
201
|
if (options.templateDir !== undefined) {
|
|
196
202
|
const templateDir = options.templateDir;
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
203
|
+
const overlayDir = options.overlayDir;
|
|
204
|
+
const overlayPaths = overlayDir ? await listTemplateFiles(overlayDir) : [];
|
|
205
|
+
const overlaySet = new Set(overlayPaths);
|
|
206
|
+
const corePaths = (await listTemplateFiles(templateDir)).filter(
|
|
207
|
+
(rel) => !overlaySet.has(rel)
|
|
208
|
+
);
|
|
209
|
+
const templateName = options.templateName ?? basename(templateDir);
|
|
210
|
+
for (const rel of corePaths) {
|
|
211
|
+
await writeTemplateFile({
|
|
212
|
+
write,
|
|
213
|
+
sourceDir: templateDir,
|
|
214
|
+
rel,
|
|
215
|
+
projectName,
|
|
216
|
+
templateName,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
if (overlayDir !== undefined) {
|
|
220
|
+
for (const rel of overlayPaths) {
|
|
221
|
+
await writeTemplateFile({
|
|
222
|
+
write,
|
|
223
|
+
sourceDir: overlayDir,
|
|
224
|
+
rel,
|
|
225
|
+
projectName,
|
|
226
|
+
templateName,
|
|
227
|
+
});
|
|
219
228
|
}
|
|
220
229
|
}
|
|
221
230
|
return {
|
|
@@ -346,6 +355,41 @@ export default memoryHook();
|
|
|
346
355
|
};
|
|
347
356
|
}
|
|
348
357
|
|
|
358
|
+
async function listTemplateFiles(root: string): Promise<string[]> {
|
|
359
|
+
const entries = await readdir(root, {
|
|
360
|
+
recursive: true,
|
|
361
|
+
withFileTypes: true,
|
|
362
|
+
});
|
|
363
|
+
return entries
|
|
364
|
+
.filter((entry) => entry.isFile())
|
|
365
|
+
.map((entry) => relative(root, join(entry.parentPath, entry.name)))
|
|
366
|
+
.filter((rel) => rel.split(/[\\/]/)[0] !== "overlays")
|
|
367
|
+
.sort();
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async function writeTemplateFile(input: {
|
|
371
|
+
write: (rel: string, contents: string | Uint8Array) => Promise<void>;
|
|
372
|
+
sourceDir: string;
|
|
373
|
+
rel: string;
|
|
374
|
+
projectName: string;
|
|
375
|
+
templateName: string;
|
|
376
|
+
}): Promise<void> {
|
|
377
|
+
const source = join(input.sourceDir, input.rel);
|
|
378
|
+
if (input.rel === "package.json") {
|
|
379
|
+
const packageJson: {
|
|
380
|
+
name: string;
|
|
381
|
+
dependencies: Record<string, string>;
|
|
382
|
+
[key: string]: unknown;
|
|
383
|
+
} = JSON.parse(await readFile(source, "utf8"));
|
|
384
|
+
packageJson.name = input.projectName;
|
|
385
|
+
packageJson.dependencies[PACKAGE_NAME] = `^${packageVersion()}`;
|
|
386
|
+
packageJson.cursorAgentTemplate = input.templateName;
|
|
387
|
+
await input.write(input.rel, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
await input.write(input.rel, await readFile(source));
|
|
391
|
+
}
|
|
392
|
+
|
|
349
393
|
export function isEexist(error: unknown): boolean {
|
|
350
394
|
return hasErrorCode(error, "EEXIST");
|
|
351
395
|
}
|
|
@@ -740,7 +784,8 @@ export function printInitGuide(): void {
|
|
|
740
784
|
|
|
741
785
|
Scaffold a project, then open the playground:
|
|
742
786
|
npx ${PACKAGE_NAME} init ./my-agent # installs deps + links agent-sdk onto PATH
|
|
743
|
-
npx ${PACKAGE_NAME} init ./my-triage --template triage #
|
|
787
|
+
npx ${PACKAGE_NAME} init ./my-triage --template triage-linear # comment on Linear issues
|
|
788
|
+
npx ${PACKAGE_NAME} init ./my-triage --template triage-jira # comment on Jira issues
|
|
744
789
|
cd ./my-agent && agent-sdk dev # or: npx ${PACKAGE_NAME} dev
|
|
745
790
|
|
|
746
791
|
Recommended loop once the project exists (from the project directory):
|