@cursor/july 0.1.42 → 0.1.45
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 +1 -0
- package/dist/bin/agent-serve.js +3 -1
- package/dist/channels/github/api.d.ts +5 -0
- package/dist/channels/github/api.d.ts.map +1 -1
- package/dist/channels/github/api.js +18 -0
- package/dist/channels/github/defaults.d.ts +4 -5
- package/dist/channels/github/defaults.d.ts.map +1 -1
- package/dist/channels/github/defaults.js +27 -1
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +41 -10
- package/dist/channels/github/index.d.ts +1 -0
- package/dist/channels/github/index.d.ts.map +1 -1
- package/dist/channels/github/index.js +1 -0
- package/dist/channels/github/progress.d.ts +54 -0
- package/dist/channels/github/progress.d.ts.map +1 -0
- package/dist/channels/github/progress.js +168 -0
- package/dist/channels/github/state.d.ts +2 -0
- package/dist/channels/github/state.d.ts.map +1 -1
- package/dist/channels/github/state.js +1 -1
- package/dist/channels/github/types.d.ts +16 -4
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.d.ts +4 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +4 -0
- package/dist/channels.d.ts +17 -0
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +31 -0
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.C4-Gwi7g.js → app.fHnNXEJ6.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BMT0xM4I.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DDoLm843.js → VPLocalSearchBox.Cf6ghfFY.js} +1 -1
- package/dist/docs/assets/chunks/{theme.SbjGIiM0.js → theme.B_5-VLe8.js} +2 -2
- package/dist/docs/assets/{deployment.md.BHVjVqRa.js → deployment.md.Cdp4rTE4.js} +1 -1
- package/dist/docs/assets/{guides_github.md.R2QlpR75.js → guides_github.md.DSg0j9qj.js} +29 -3
- package/dist/docs/assets/{guides_github.md.R2QlpR75.lean.js → guides_github.md.DSg0j9qj.lean.js} +1 -1
- package/dist/docs/assets/guides_opentelemetry.md.Csn7ZI25.js +27 -0
- package/dist/docs/assets/guides_opentelemetry.md.Csn7ZI25.lean.js +1 -0
- package/dist/docs/assets/index.md.COeoSkcF.js +5 -0
- package/dist/docs/assets/index.md.COeoSkcF.lean.js +1 -0
- package/dist/docs/assets/{quickstart.md.rIbfahJM.js → quickstart.md.DZxBu44y.js} +18 -49
- package/dist/docs/assets/quickstart.md.DZxBu44y.lean.js +1 -0
- package/dist/docs/assets/{reference_agent-config.md.CRmkoxd6.js → reference_agent-config.md.XRpxctsA.js} +9 -4
- package/dist/docs/assets/{reference_agent-config.md.CRmkoxd6.lean.js → reference_agent-config.md.XRpxctsA.lean.js} +1 -1
- package/dist/docs/assets/{reference_channels.md.BIabFUAI.js → reference_channels.md.MN8iDy8V.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.DqEqQpGn.js → reference_cli.md.BDmjPPAm.js} +2 -2
- package/dist/docs/assets/{reference_hooks.md.B40QAG6m.js → reference_hooks.md.4cbRRIIu.js} +2 -2
- package/dist/docs/assets/{reference_hooks.md.B40QAG6m.lean.js → reference_hooks.md.4cbRRIIu.lean.js} +1 -1
- package/dist/docs/assets/{reference_project-layout.md._XdeMahr.js → reference_project-layout.md.D3MdHM2z.js} +2 -1
- package/dist/docs/assets/{reference_tools.md.lSrsTxYJ.js → reference_tools.md.kJfJAP2m.js} +1 -1
- package/dist/docs/assets/templates_triage.md.CConZt1z.js +11 -0
- package/dist/docs/assets/templates_triage.md.CConZt1z.lean.js +1 -0
- package/dist/docs/building-with-agents.html +5 -5
- package/dist/docs/concepts.html +5 -5
- package/dist/docs/deployment.html +6 -6
- package/dist/docs/evals.html +4 -4
- package/dist/docs/example-agents/approval-buddy.html +4 -4
- package/dist/docs/example-agents/benny.html +4 -4
- package/dist/docs/example-agents/bugbot.html +4 -4
- package/dist/docs/example-agents/codebase-wiki.html +4 -4
- package/dist/docs/example-agents/codeowners-review.html +4 -4
- package/dist/docs/example-agents/concierge.html +4 -4
- package/dist/docs/example-agents/fsd.html +4 -4
- package/dist/docs/example-agents/index.html +5 -5
- package/dist/docs/example-agents/knowledge-base.html +4 -4
- package/dist/docs/example-agents/oncall.html +4 -4
- package/dist/docs/example-agents/security-reviewer.html +4 -4
- package/dist/docs/example-agents/slack-agent.html +4 -4
- package/dist/docs/example-agents/weather-agent.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +5 -5
- package/dist/docs/guides/github.html +33 -7
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +4 -4
- package/dist/docs/guides/opentelemetry.html +51 -0
- package/dist/docs/guides/slack.html +4 -4
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +8 -27
- package/dist/docs/quickstart.html +24 -55
- package/dist/docs/reference/agent-config.html +12 -7
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +6 -6
- package/dist/docs/reference/cli.html +7 -7
- package/dist/docs/reference/connections.html +4 -4
- package/dist/docs/reference/hooks.html +6 -6
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +7 -6
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +6 -6
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/triage.html +35 -0
- package/dist/docs/troubleshooting.html +4 -4
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/internal/cli-ax.d.ts +1 -0
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +20 -5
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +38 -7
- package/dist/internal/deploy-client.d.ts +8 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +5 -2
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +82 -13
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +1 -0
- package/dist/internal/host-otel.d.ts +25 -0
- package/dist/internal/host-otel.d.ts.map +1 -0
- package/dist/internal/host-otel.js +162 -0
- package/dist/internal/init-project.d.ts +24 -0
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +136 -17
- package/dist/internal/otel/attrs.d.ts +7 -0
- package/dist/internal/otel/attrs.d.ts.map +1 -0
- package/dist/internal/otel/attrs.js +13 -0
- package/dist/internal/otel/collector.d.ts +21 -0
- package/dist/internal/otel/collector.d.ts.map +1 -0
- package/dist/internal/otel/collector.js +605 -0
- package/dist/internal/otel/config.d.ts +42 -0
- package/dist/internal/otel/config.d.ts.map +1 -0
- package/dist/internal/otel/config.js +236 -0
- package/dist/internal/otel/http.d.ts +11 -0
- package/dist/internal/otel/http.d.ts.map +1 -0
- package/dist/internal/otel/http.js +97 -0
- package/dist/internal/otel/runtime.d.ts +21 -0
- package/dist/internal/otel/runtime.d.ts.map +1 -0
- package/dist/internal/otel/runtime.js +188 -0
- package/dist/internal/sdk-runner.d.ts +1 -1
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +1 -1
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +26 -1
- package/dist/internal/session-engine.d.ts +15 -2
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +58 -7
- package/dist/memory.d.ts +4 -0
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +4 -0
- package/dist/otel.d.ts +88 -0
- package/dist/otel.d.ts.map +1 -0
- package/dist/otel.js +64 -0
- package/dist/playground/assets/index-CZH9EFvO.css +1 -0
- package/dist/playground/assets/{index-B3HBneZZ.js → index-Dces8BDp.js} +34 -34
- package/dist/playground/index.html +2 -2
- package/dist/types.d.ts +103 -3
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +35 -105
- package/docs/deployment.md +2 -1
- package/docs/guides/github.md +79 -6
- package/docs/guides/opentelemetry.md +214 -0
- package/docs/quickstart.md +89 -227
- package/docs/reference/agent-config.md +52 -0
- package/docs/reference/channels.md +3 -1
- package/docs/reference/cli.md +2 -2
- package/docs/reference/hooks.md +21 -4
- package/docs/reference/project-layout.md +2 -0
- package/docs/reference/tools.md +5 -4
- package/docs/templates/triage.md +147 -0
- package/package.json +24 -2
- package/skills/create-agent/SKILL.md +2 -2
- package/skills/framework-map/SKILL.md +2 -0
- package/skills/otel/SKILL.md +98 -0
- package/src/bin/agent-serve.ts +3 -1
- package/src/channels/github/api.ts +22 -0
- package/src/channels/github/defaults.ts +27 -6
- package/src/channels/github/github-channel.ts +36 -1
- package/src/channels/github/index.ts +1 -0
- package/src/channels/github/progress.ts +243 -0
- package/src/channels/github/state.ts +3 -0
- package/src/channels/github/types.ts +12 -4
- package/src/channels/slack/slack-channel.ts +4 -0
- package/src/channels.ts +38 -0
- package/src/index.ts +7 -0
- package/src/internal/cli-ax.ts +21 -3
- package/src/internal/cli-deploy.ts +58 -3
- package/src/internal/deploy-client.ts +11 -0
- package/src/internal/discovery.ts +108 -4
- package/src/internal/distribution.ts +1 -0
- package/src/internal/host-otel.ts +252 -0
- package/src/internal/init-project.ts +160 -17
- package/src/internal/otel/attrs.ts +18 -0
- package/src/internal/otel/collector.ts +881 -0
- package/src/internal/otel/config.ts +307 -0
- package/src/internal/otel/http.ts +126 -0
- package/src/internal/otel/runtime.ts +271 -0
- package/src/internal/sdk-runner.ts +6 -0
- package/src/internal/server.ts +39 -1
- package/src/internal/session-engine.ts +83 -2
- package/src/memory.ts +4 -0
- package/src/otel.ts +155 -0
- package/src/types.ts +109 -3
- package/templates/triage/README.md +48 -0
- package/templates/triage/agent/agent.ts +14 -0
- package/templates/triage/agent/channels/intake.ts +20 -0
- package/templates/triage/agent/channels/slack.ts +3 -0
- package/templates/triage/agent/hooks/memory.ts +3 -0
- package/templates/triage/agent/instructions.md +27 -0
- package/templates/triage/agent/skills/triage-rubric.md +43 -0
- package/templates/triage/agent/tools/record_triage.ts +23 -0
- package/templates/triage/evals/evals.config.ts +5 -0
- package/templates/triage/evals/triage.eval.ts +43 -0
- package/templates/triage/package.json +18 -0
- package/templates/triage/tsconfig.json +12 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.BlU3A8EB.js +0 -1
- package/dist/docs/assets/index.md.BKpbkCa6.js +0 -24
- package/dist/docs/assets/index.md.BKpbkCa6.lean.js +0 -1
- package/dist/docs/assets/quickstart.md.rIbfahJM.lean.js +0 -1
- package/dist/playground/assets/index-Bqk9nplh.css +0 -1
- /package/dist/docs/assets/{deployment.md.BHVjVqRa.lean.js → deployment.md.Cdp4rTE4.lean.js} +0 -0
- /package/dist/docs/assets/{reference_channels.md.BIabFUAI.lean.js → reference_channels.md.MN8iDy8V.lean.js} +0 -0
- /package/dist/docs/assets/{reference_cli.md.DqEqQpGn.lean.js → reference_cli.md.BDmjPPAm.lean.js} +0 -0
- /package/dist/docs/assets/{reference_project-layout.md._XdeMahr.lean.js → reference_project-layout.md.D3MdHM2z.lean.js} +0 -0
- /package/dist/docs/assets/{reference_tools.md.lSrsTxYJ.lean.js → reference_tools.md.kJfJAP2m.lean.js} +0 -0
|
@@ -20,6 +20,7 @@ import { pathToFileURL } from "node:url";
|
|
|
20
20
|
import { type ABConfigFile, resolveABMaxPlaygroundSessions } from "../ab.js";
|
|
21
21
|
import { resolveArtifactsMax } from "../artifacts.js";
|
|
22
22
|
import { httpChannel } from "../channels.js";
|
|
23
|
+
import type { OtelDefinition } from "../otel.js";
|
|
23
24
|
import type { StorageDefinition } from "../storage.js";
|
|
24
25
|
import {
|
|
25
26
|
type AgentConcurrencyOptions,
|
|
@@ -300,6 +301,7 @@ export async function loadAgentProject(
|
|
|
300
301
|
const abConfig = await loadABConfig(ctx, agentDir);
|
|
301
302
|
const storage = await loadStorage(ctx, agentDir);
|
|
302
303
|
const artifacts = await loadArtifacts(ctx, agentDir);
|
|
304
|
+
const otel = await loadOtel(ctx, agentDir);
|
|
303
305
|
await appendArtifactTool(ctx, agentDir, agent, artifacts);
|
|
304
306
|
|
|
305
307
|
if (agentDir !== rootDir) {
|
|
@@ -319,6 +321,7 @@ export async function loadAgentProject(
|
|
|
319
321
|
abConfig,
|
|
320
322
|
storage,
|
|
321
323
|
artifacts,
|
|
324
|
+
otel,
|
|
322
325
|
diagnostics,
|
|
323
326
|
};
|
|
324
327
|
}
|
|
@@ -373,6 +376,14 @@ async function loadAgent(
|
|
|
373
376
|
message: "builtinTools is not supported on subagents; ignoring.",
|
|
374
377
|
});
|
|
375
378
|
}
|
|
379
|
+
if (config?.tools !== undefined) {
|
|
380
|
+
ctx.diagnostics.push({
|
|
381
|
+
severity: "warning",
|
|
382
|
+
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
383
|
+
message:
|
|
384
|
+
"tools is not supported on subagents (they keep the harness task-tool toolset); ignoring.",
|
|
385
|
+
});
|
|
386
|
+
}
|
|
376
387
|
const artifactsPath = await findModuleFile(agentDir, "artifacts");
|
|
377
388
|
if (artifactsPath !== undefined) {
|
|
378
389
|
ctx.diagnostics.push({
|
|
@@ -412,6 +423,46 @@ async function loadAgent(
|
|
|
412
423
|
const localCwd = resolveAgentCwd(ctx, config?.local?.cwd);
|
|
413
424
|
const localWorkspaceDir = resolveAgentCwd(ctx, config?.local?.workspaceDir);
|
|
414
425
|
const localSandbox = config?.local?.sandbox === true;
|
|
426
|
+
// Built-in tool allowlist (top-level `tools`). `[]` is meaningful (no
|
|
427
|
+
// native built-in tools), so presence is keyed on the field, not on
|
|
428
|
+
// emptiness. "mcp" is always granted alongside a configured allowlist:
|
|
429
|
+
// it is transport, not capability — this kit's own server tools ride the
|
|
430
|
+
// SDK custom-tool MCP path, so omitting it silently breaks
|
|
431
|
+
// `agent/tools/`, and what MCP actually exposes stays controlled by the
|
|
432
|
+
// second layer (authored tools, mcp-connections, the harness cwd's
|
|
433
|
+
// ambient config).
|
|
434
|
+
const authoredAllowlist = config?.tools;
|
|
435
|
+
const allowedTools =
|
|
436
|
+
authoredAllowlist === undefined || authoredAllowlist.includes("mcp")
|
|
437
|
+
? authoredAllowlist
|
|
438
|
+
: [...authoredAllowlist, "mcp" as const];
|
|
439
|
+
// The allowlist states runtime-agnostic intent, but the SDK cannot
|
|
440
|
+
// enforce tool restrictions on cloud agents yet — a cloud-runtime agent
|
|
441
|
+
// that declares one must fail at serve start, never run unrestricted.
|
|
442
|
+
// (Hybrid per-send cloud sessions fail closed in the session engine.)
|
|
443
|
+
if (allowedTools !== undefined && runtime === "cloud") {
|
|
444
|
+
ctx.diagnostics.push({
|
|
445
|
+
severity: "error",
|
|
446
|
+
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
447
|
+
message:
|
|
448
|
+
'tools cannot be enforced on runtime: "cloud" yet (the Cursor SDK rejects tool restrictions on cloud agents). Remove `tools` or run the agent on the local runtime.',
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
// `execution: "agent"` tool scripts run through the shell tool. Shell is
|
|
452
|
+
// a real capability, so it is never injected — but an allowlist that
|
|
453
|
+
// strands authored scripts deserves a loud diagnostic, not silence.
|
|
454
|
+
if (
|
|
455
|
+
allowedTools !== undefined &&
|
|
456
|
+
!allowedTools.includes("shell") &&
|
|
457
|
+
tools.some((tool) => tool.execution === "agent")
|
|
458
|
+
) {
|
|
459
|
+
ctx.diagnostics.push({
|
|
460
|
+
severity: "warning",
|
|
461
|
+
path: displayPath(ctx, join(agentDir, "agent.ts")),
|
|
462
|
+
message:
|
|
463
|
+
'`tools` omits "shell" but this agent has execution: "agent" tools, which run as shell scripts; the model will not be able to run them.',
|
|
464
|
+
});
|
|
465
|
+
}
|
|
415
466
|
if (localCwd !== undefined && localWorkspaceDir !== undefined) {
|
|
416
467
|
ctx.diagnostics.push({
|
|
417
468
|
severity: "warning",
|
|
@@ -452,6 +503,7 @@ async function loadAgent(
|
|
|
452
503
|
runtime,
|
|
453
504
|
cloud,
|
|
454
505
|
local,
|
|
506
|
+
...(allowedTools === undefined ? {} : { allowedTools }),
|
|
455
507
|
hosting,
|
|
456
508
|
concurrency,
|
|
457
509
|
serving,
|
|
@@ -1718,6 +1770,22 @@ async function loadStorage(
|
|
|
1718
1770
|
return definition as StorageDefinition | undefined;
|
|
1719
1771
|
}
|
|
1720
1772
|
|
|
1773
|
+
// ============================================================================
|
|
1774
|
+
// OpenTelemetry (agent/otel.ts)
|
|
1775
|
+
// ============================================================================
|
|
1776
|
+
|
|
1777
|
+
async function loadOtel(
|
|
1778
|
+
ctx: DiscoveryContext,
|
|
1779
|
+
agentDir: string
|
|
1780
|
+
): Promise<OtelDefinition | undefined> {
|
|
1781
|
+
const path = await findModuleFile(agentDir, "otel");
|
|
1782
|
+
if (path === undefined) {
|
|
1783
|
+
return undefined;
|
|
1784
|
+
}
|
|
1785
|
+
const definition = await importDefault(ctx, path, "otel");
|
|
1786
|
+
return definition as OtelDefinition | undefined;
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1721
1789
|
// ============================================================================
|
|
1722
1790
|
// Artifacts (agent/artifacts.ts)
|
|
1723
1791
|
// ============================================================================
|
|
@@ -1973,6 +2041,13 @@ export function projectInfo(project: AgentProject): AgentProjectInfo {
|
|
|
1973
2041
|
agentTool: project.artifacts.agentTool === true,
|
|
1974
2042
|
max: resolveArtifactsMax(project.artifacts),
|
|
1975
2043
|
},
|
|
2044
|
+
otel:
|
|
2045
|
+
project.otel === undefined
|
|
2046
|
+
? undefined
|
|
2047
|
+
: {
|
|
2048
|
+
enabled: true as const,
|
|
2049
|
+
serviceName: project.otel.serviceName,
|
|
2050
|
+
},
|
|
1976
2051
|
diagnostics: project.diagnostics,
|
|
1977
2052
|
};
|
|
1978
2053
|
}
|
|
@@ -1994,6 +2069,7 @@ async function importDefault(
|
|
|
1994
2069
|
| "ab"
|
|
1995
2070
|
| "storage"
|
|
1996
2071
|
| "artifacts"
|
|
2072
|
+
| "otel"
|
|
1997
2073
|
): Promise<unknown> {
|
|
1998
2074
|
let mod: Record<string, unknown>;
|
|
1999
2075
|
try {
|
|
@@ -2008,10 +2084,7 @@ async function importDefault(
|
|
|
2008
2084
|
}
|
|
2009
2085
|
const kind = getDefinitionKind(mod.default);
|
|
2010
2086
|
if (kind !== expectedKind) {
|
|
2011
|
-
const helper =
|
|
2012
|
-
expectedKind === "ab"
|
|
2013
|
-
? "defineAB"
|
|
2014
|
-
: `define${expectedKind[0]?.toUpperCase()}${expectedKind.slice(1)}`;
|
|
2087
|
+
const helper = DEFINE_HELPER_NAME[expectedKind];
|
|
2015
2088
|
ctx.diagnostics.push({
|
|
2016
2089
|
severity: "error",
|
|
2017
2090
|
path: displayPath(ctx, modulePath),
|
|
@@ -2025,6 +2098,37 @@ async function importDefault(
|
|
|
2025
2098
|
return mod.default;
|
|
2026
2099
|
}
|
|
2027
2100
|
|
|
2101
|
+
const DEFINE_HELPER_NAME: Record<
|
|
2102
|
+
| "agent"
|
|
2103
|
+
| "instructions"
|
|
2104
|
+
| "tool"
|
|
2105
|
+
| "skill"
|
|
2106
|
+
| "connection"
|
|
2107
|
+
| "channel"
|
|
2108
|
+
| "http-channel"
|
|
2109
|
+
| "schedule"
|
|
2110
|
+
| "hook"
|
|
2111
|
+
| "ab"
|
|
2112
|
+
| "storage"
|
|
2113
|
+
| "artifacts"
|
|
2114
|
+
| "otel",
|
|
2115
|
+
string
|
|
2116
|
+
> = {
|
|
2117
|
+
agent: "defineAgent",
|
|
2118
|
+
instructions: "defineInstructions",
|
|
2119
|
+
tool: "defineTool",
|
|
2120
|
+
skill: "defineSkill",
|
|
2121
|
+
connection: "defineConnection",
|
|
2122
|
+
channel: "defineChannel",
|
|
2123
|
+
"http-channel": "httpChannel",
|
|
2124
|
+
schedule: "defineSchedule",
|
|
2125
|
+
hook: "defineHook",
|
|
2126
|
+
ab: "defineAB",
|
|
2127
|
+
storage: "defineStorage",
|
|
2128
|
+
artifacts: "defineArtifacts",
|
|
2129
|
+
otel: "defineOtel",
|
|
2130
|
+
};
|
|
2131
|
+
|
|
2028
2132
|
async function warnUnknownSlots(
|
|
2029
2133
|
ctx: DiscoveryContext,
|
|
2030
2134
|
agentDir: string
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Author-facing OpenTelemetry surface for {@link HostContext.otel}.
|
|
3
|
+
*
|
|
4
|
+
* Always present on `ctx.host`. Counters and histograms no-op when no meter
|
|
5
|
+
* is running. `setAttributes` still tags the open session when a collector
|
|
6
|
+
* is attached. `forSession` is engine-only so a tool cannot retarget another
|
|
7
|
+
* conversation's tags.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { Counter, Histogram, Meter } from "@opentelemetry/api";
|
|
11
|
+
import type { HostOtelApi, HostOtelAttributes } from "../types.js";
|
|
12
|
+
import type { AttrValue } from "./otel/attrs.js";
|
|
13
|
+
import { attrs } from "./otel/attrs.js";
|
|
14
|
+
import type { OtelCollector } from "./otel/collector.js";
|
|
15
|
+
|
|
16
|
+
const METRIC_NAME = /^[A-Za-z][A-Za-z0-9_./-]*$/;
|
|
17
|
+
|
|
18
|
+
const RESERVED_METRICS = new Set([
|
|
19
|
+
"cursor.token.usage",
|
|
20
|
+
"cursor.tool.calls",
|
|
21
|
+
"cursor.cost.usage",
|
|
22
|
+
"agent_sdk.session.count",
|
|
23
|
+
"agent_sdk.turn.count",
|
|
24
|
+
"agent_sdk.subagent.count",
|
|
25
|
+
"agent_sdk.http.duration",
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const RESERVED_ATTRIBUTES = new Set([
|
|
29
|
+
"cursor.conversation.id",
|
|
30
|
+
"agent_sdk.agent",
|
|
31
|
+
"agent_sdk.turn_id",
|
|
32
|
+
"agent_sdk.framework",
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
export interface CreateHostOtelApiOptions {
|
|
36
|
+
agentName: string;
|
|
37
|
+
meter?: Meter;
|
|
38
|
+
collector?: OtelCollector;
|
|
39
|
+
/** Session bound for join tags; prefer {@link HostOtelSessionApi.forSession}. */
|
|
40
|
+
sessionId?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Engine-only binding. Not part of the public {@link HostOtelApi}. */
|
|
44
|
+
export interface HostOtelSessionApi extends HostOtelApi {
|
|
45
|
+
forSession(sessionId: string): HostOtelSessionApi;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface CachedInstrument {
|
|
49
|
+
kind: "counter" | "histogram";
|
|
50
|
+
instrument: Counter | Histogram;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface SharedState {
|
|
54
|
+
agentName: string;
|
|
55
|
+
meter: Meter | undefined;
|
|
56
|
+
collector: OtelCollector | undefined;
|
|
57
|
+
instruments: Map<string, CachedInstrument>;
|
|
58
|
+
/** Used when no collector is attached so bound instances still share tags. */
|
|
59
|
+
sessionTags: Map<string, Record<string, AttrValue>>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface BoundState {
|
|
63
|
+
shared: SharedState;
|
|
64
|
+
sessionId: string | undefined;
|
|
65
|
+
localTags: Record<string, AttrValue>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Build the host OpenTelemetry API for one agent process. */
|
|
69
|
+
export function createHostOtelApi(
|
|
70
|
+
options: CreateHostOtelApiOptions
|
|
71
|
+
): HostOtelSessionApi {
|
|
72
|
+
return createBoundHostOtelApi({
|
|
73
|
+
shared: {
|
|
74
|
+
agentName: options.agentName,
|
|
75
|
+
meter: options.meter,
|
|
76
|
+
collector: options.collector,
|
|
77
|
+
instruments: new Map(),
|
|
78
|
+
sessionTags: new Map(),
|
|
79
|
+
},
|
|
80
|
+
sessionId: options.sessionId,
|
|
81
|
+
localTags: {},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function createBoundHostOtelApi(state: BoundState): HostOtelSessionApi {
|
|
86
|
+
const api: HostOtelSessionApi = {
|
|
87
|
+
get enabled(): boolean {
|
|
88
|
+
return state.shared.meter !== undefined;
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
setAttributes(attributes) {
|
|
92
|
+
const cleaned = validateAttributes(attributes);
|
|
93
|
+
if (state.sessionId === undefined) {
|
|
94
|
+
Object.assign(state.localTags, cleaned);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
mergeSessionTags(state, state.sessionId, cleaned);
|
|
98
|
+
state.shared.collector?.setSessionAttributes(state.sessionId, cleaned);
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
increment(
|
|
102
|
+
name: string,
|
|
103
|
+
valueOrAttributes?: number | HostOtelAttributes,
|
|
104
|
+
maybeAttributes?: HostOtelAttributes
|
|
105
|
+
) {
|
|
106
|
+
const value =
|
|
107
|
+
typeof valueOrAttributes === "number" ? valueOrAttributes : 1;
|
|
108
|
+
const attributes =
|
|
109
|
+
typeof valueOrAttributes === "number"
|
|
110
|
+
? maybeAttributes
|
|
111
|
+
: valueOrAttributes;
|
|
112
|
+
counter(state.shared, name)?.add(
|
|
113
|
+
value,
|
|
114
|
+
emitAttributes(state, attributes)
|
|
115
|
+
);
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
record(name, value, attributes) {
|
|
119
|
+
histogram(state.shared, name)?.record(
|
|
120
|
+
value,
|
|
121
|
+
emitAttributes(state, attributes)
|
|
122
|
+
);
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
forSession(sessionId) {
|
|
126
|
+
const trimmed = sessionId.trim();
|
|
127
|
+
if (trimmed === "") {
|
|
128
|
+
throw new Error("host.otel.forSession requires a non-empty session id");
|
|
129
|
+
}
|
|
130
|
+
return createBoundHostOtelApi({
|
|
131
|
+
shared: state.shared,
|
|
132
|
+
sessionId: trimmed,
|
|
133
|
+
localTags: {},
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
return api;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function mergeSessionTags(
|
|
141
|
+
state: BoundState,
|
|
142
|
+
sessionId: string,
|
|
143
|
+
incoming: Record<string, AttrValue>
|
|
144
|
+
): void {
|
|
145
|
+
const bag = state.shared.sessionTags.get(sessionId) ?? {};
|
|
146
|
+
Object.assign(bag, incoming);
|
|
147
|
+
state.shared.sessionTags.set(sessionId, bag);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function emitAttributes(
|
|
151
|
+
state: BoundState,
|
|
152
|
+
callSite: HostOtelAttributes | undefined
|
|
153
|
+
): Record<string, AttrValue> {
|
|
154
|
+
return attrs({
|
|
155
|
+
...sessionBag(state),
|
|
156
|
+
...validateAttributes(callSite),
|
|
157
|
+
"agent_sdk.agent": state.shared.agentName,
|
|
158
|
+
...(state.sessionId === undefined
|
|
159
|
+
? {}
|
|
160
|
+
: { "cursor.conversation.id": state.sessionId }),
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function sessionBag(state: BoundState): Record<string, AttrValue> {
|
|
165
|
+
if (state.sessionId === undefined) {
|
|
166
|
+
return state.localTags;
|
|
167
|
+
}
|
|
168
|
+
const fromCollector = state.shared.collector?.getSessionAttributes(
|
|
169
|
+
state.sessionId
|
|
170
|
+
);
|
|
171
|
+
const fromShared = state.shared.sessionTags.get(state.sessionId) ?? {};
|
|
172
|
+
return {
|
|
173
|
+
...fromShared,
|
|
174
|
+
...(fromCollector ?? {}),
|
|
175
|
+
...state.localTags,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function counter(shared: SharedState, name: string): Counter | undefined {
|
|
180
|
+
validateMetricName(name);
|
|
181
|
+
if (shared.meter === undefined) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
const existing = shared.instruments.get(name);
|
|
185
|
+
if (existing !== undefined) {
|
|
186
|
+
if (existing.kind !== "counter") {
|
|
187
|
+
throw new Error(`host.otel metric "${name}" is already a histogram`);
|
|
188
|
+
}
|
|
189
|
+
return existing.instrument as Counter;
|
|
190
|
+
}
|
|
191
|
+
const instrument = shared.meter.createCounter(name);
|
|
192
|
+
shared.instruments.set(name, { kind: "counter", instrument });
|
|
193
|
+
return instrument;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function histogram(shared: SharedState, name: string): Histogram | undefined {
|
|
197
|
+
validateMetricName(name);
|
|
198
|
+
if (shared.meter === undefined) {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
const existing = shared.instruments.get(name);
|
|
202
|
+
if (existing !== undefined) {
|
|
203
|
+
if (existing.kind !== "histogram") {
|
|
204
|
+
throw new Error(`host.otel metric "${name}" is already a counter`);
|
|
205
|
+
}
|
|
206
|
+
return existing.instrument as Histogram;
|
|
207
|
+
}
|
|
208
|
+
const instrument = shared.meter.createHistogram(name);
|
|
209
|
+
shared.instruments.set(name, { kind: "histogram", instrument });
|
|
210
|
+
return instrument;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function validateMetricName(name: string): void {
|
|
214
|
+
if (!METRIC_NAME.test(name)) {
|
|
215
|
+
throw new Error(
|
|
216
|
+
`host.otel metric name "${name}" must match [A-Za-z][A-Za-z0-9_./-]*`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
if (RESERVED_METRICS.has(name)) {
|
|
220
|
+
throw new Error(`host.otel metric name "${name}" is reserved`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function validateAttributes(
|
|
225
|
+
attributes: HostOtelAttributes | undefined
|
|
226
|
+
): Record<string, AttrValue> {
|
|
227
|
+
if (attributes === undefined) {
|
|
228
|
+
return {};
|
|
229
|
+
}
|
|
230
|
+
const out: Record<string, AttrValue> = {};
|
|
231
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
232
|
+
if (value === undefined) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (RESERVED_ATTRIBUTES.has(key)) {
|
|
236
|
+
throw new Error(
|
|
237
|
+
`host.otel attribute "${key}" is reserved; the runtime always sets it`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
if (
|
|
241
|
+
typeof value !== "string" &&
|
|
242
|
+
typeof value !== "number" &&
|
|
243
|
+
typeof value !== "boolean"
|
|
244
|
+
) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`host.otel attribute "${key}" must be a string, number, or boolean`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
out[key] = value;
|
|
250
|
+
}
|
|
251
|
+
return out;
|
|
252
|
+
}
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
-
import { existsSync } from "node:fs";
|
|
7
|
-
import { mkdir, writeFile } from "node:fs/promises";
|
|
6
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
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";
|
|
10
10
|
import {
|
|
11
11
|
CLI_COMMAND_NAME,
|
|
12
12
|
PACKAGE_NAME,
|
|
13
|
+
packageRootDir,
|
|
13
14
|
packageVersion,
|
|
14
15
|
} from "./distribution.js";
|
|
15
16
|
import {
|
|
@@ -25,6 +26,7 @@ export interface InitProjectOptions {
|
|
|
25
26
|
name?: string;
|
|
26
27
|
/** Model id override written into agent.ts. */
|
|
27
28
|
model?: string;
|
|
29
|
+
templateDir?: string;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
export type InitFileAction = "create" | "exist";
|
|
@@ -44,6 +46,99 @@ export interface InitProjectResult {
|
|
|
44
46
|
skipped: string[];
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
export function availableTemplates(): string[] {
|
|
50
|
+
const templatesDir = join(packageRootDir(), "templates");
|
|
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
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type TemplateResolution =
|
|
65
|
+
| { ok: true; name: string; dir: string }
|
|
66
|
+
| { ok: false; message: string };
|
|
67
|
+
|
|
68
|
+
export function resolveTemplate(name: string): TemplateResolution {
|
|
69
|
+
if (!/^[a-z][a-z0-9-]*$/.test(name)) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
message: "template name must match /^[a-z][a-z0-9-]*$/",
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const available = availableTemplates();
|
|
76
|
+
if (!available.includes(name)) {
|
|
77
|
+
const detail =
|
|
78
|
+
available.length === 0
|
|
79
|
+
? "no templates ship with this build"
|
|
80
|
+
: `available: ${available.join(", ")}`;
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
message: `unknown template "${name}" (${detail})`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
ok: true,
|
|
88
|
+
name,
|
|
89
|
+
dir: join(packageRootDir(), "templates", name),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export type InitTargetCheck = { ok: true } | { ok: false; message: string };
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Re-init must keep the mode the project was scaffolded with: template
|
|
97
|
+
* scaffolds stamp `cursorAgentTemplate` into package.json, and a mismatch
|
|
98
|
+
* here would silently mix default-scaffold files into a template project
|
|
99
|
+
* (or the reverse).
|
|
100
|
+
*/
|
|
101
|
+
export function checkInitTarget(
|
|
102
|
+
rootDir: string,
|
|
103
|
+
template: string | undefined
|
|
104
|
+
): InitTargetCheck {
|
|
105
|
+
const pkgPath = join(rootDir, "package.json");
|
|
106
|
+
if (!existsSync(pkgPath)) {
|
|
107
|
+
return { ok: true };
|
|
108
|
+
}
|
|
109
|
+
let marker: string | undefined;
|
|
110
|
+
try {
|
|
111
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8")) as {
|
|
112
|
+
cursorAgentTemplate?: unknown;
|
|
113
|
+
};
|
|
114
|
+
marker =
|
|
115
|
+
typeof pkg.cursorAgentTemplate === "string"
|
|
116
|
+
? pkg.cursorAgentTemplate
|
|
117
|
+
: undefined;
|
|
118
|
+
} catch {
|
|
119
|
+
marker = undefined;
|
|
120
|
+
}
|
|
121
|
+
if (marker === template) {
|
|
122
|
+
return { ok: true };
|
|
123
|
+
}
|
|
124
|
+
if (template === undefined) {
|
|
125
|
+
return {
|
|
126
|
+
ok: false,
|
|
127
|
+
message: `${rootDir} was scaffolded from the "${marker}" template; re-run with --template ${marker}`,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (marker === undefined) {
|
|
131
|
+
return {
|
|
132
|
+
ok: false,
|
|
133
|
+
message: `${rootDir} already has a package.json without template provenance; re-run init without --template, or scaffold into a fresh directory`,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
ok: false,
|
|
138
|
+
message: `${rootDir} was scaffolded from the "${marker}" template, not "${template}"`,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
47
142
|
const DEFAULT_MODEL: ModelSetting = {
|
|
48
143
|
id: "grok-4.5",
|
|
49
144
|
params: [
|
|
@@ -73,13 +168,15 @@ export async function initProject(
|
|
|
73
168
|
options: InitProjectOptions
|
|
74
169
|
): Promise<InitProjectResult> {
|
|
75
170
|
const rootDir = resolve(options.dir);
|
|
76
|
-
const
|
|
171
|
+
const projectName = basename(rootDir);
|
|
172
|
+
const name = options.name ?? projectName;
|
|
77
173
|
const model = formatModelLiteral(options.model ?? DEFAULT_MODEL);
|
|
78
|
-
const agentDir = join(rootDir, "agent");
|
|
79
|
-
await mkdir(agentDir, { recursive: true });
|
|
80
174
|
|
|
81
175
|
const files: InitFileEntry[] = [];
|
|
82
|
-
const write = async (
|
|
176
|
+
const write = async (
|
|
177
|
+
rel: string,
|
|
178
|
+
contents: string | Uint8Array
|
|
179
|
+
): Promise<void> => {
|
|
83
180
|
const abs = join(rootDir, rel);
|
|
84
181
|
await mkdir(dirname(abs), { recursive: true });
|
|
85
182
|
try {
|
|
@@ -95,6 +192,47 @@ export async function initProject(
|
|
|
95
192
|
}
|
|
96
193
|
};
|
|
97
194
|
|
|
195
|
+
if (options.templateDir !== undefined) {
|
|
196
|
+
const templateDir = options.templateDir;
|
|
197
|
+
const entries = await readdir(templateDir, {
|
|
198
|
+
recursive: true,
|
|
199
|
+
withFileTypes: true,
|
|
200
|
+
});
|
|
201
|
+
const relativePaths = entries
|
|
202
|
+
.filter((entry) => entry.isFile())
|
|
203
|
+
.map((entry) => relative(templateDir, join(entry.parentPath, entry.name)))
|
|
204
|
+
.sort();
|
|
205
|
+
for (const rel of relativePaths) {
|
|
206
|
+
const source = join(templateDir, rel);
|
|
207
|
+
if (rel === "package.json") {
|
|
208
|
+
const packageJson: {
|
|
209
|
+
name: string;
|
|
210
|
+
dependencies: Record<string, string>;
|
|
211
|
+
[key: string]: unknown;
|
|
212
|
+
} = JSON.parse(await readFile(source, "utf8"));
|
|
213
|
+
packageJson.name = projectName;
|
|
214
|
+
packageJson.dependencies[PACKAGE_NAME] = `^${packageVersion()}`;
|
|
215
|
+
packageJson.cursorAgentTemplate = basename(templateDir);
|
|
216
|
+
await write(rel, `${JSON.stringify(packageJson, null, 2)}\n`);
|
|
217
|
+
} else {
|
|
218
|
+
await write(rel, await readFile(source));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return {
|
|
222
|
+
rootDir,
|
|
223
|
+
files,
|
|
224
|
+
created: files
|
|
225
|
+
.filter((entry) => entry.action === "create")
|
|
226
|
+
.map((entry) => entry.path),
|
|
227
|
+
skipped: files
|
|
228
|
+
.filter((entry) => entry.action === "exist")
|
|
229
|
+
.map((entry) => entry.path),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const agentDir = join(rootDir, "agent");
|
|
234
|
+
await mkdir(agentDir, { recursive: true });
|
|
235
|
+
|
|
98
236
|
await write(
|
|
99
237
|
"package.json",
|
|
100
238
|
`${JSON.stringify(
|
|
@@ -209,11 +347,15 @@ export default memoryHook();
|
|
|
209
347
|
}
|
|
210
348
|
|
|
211
349
|
export function isEexist(error: unknown): boolean {
|
|
350
|
+
return hasErrorCode(error, "EEXIST");
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function hasErrorCode(error: unknown, code: string): boolean {
|
|
212
354
|
return (
|
|
213
355
|
error !== null &&
|
|
214
356
|
typeof error === "object" &&
|
|
215
357
|
"code" in error &&
|
|
216
|
-
|
|
358
|
+
error.code === code
|
|
217
359
|
);
|
|
218
360
|
}
|
|
219
361
|
|
|
@@ -557,20 +699,20 @@ function initNextStepEntries(options: InitNextStepsOptions): Array<{
|
|
|
557
699
|
if (cd !== undefined) {
|
|
558
700
|
steps.push({ command: cd });
|
|
559
701
|
}
|
|
560
|
-
if (!options.cliOnPath) {
|
|
561
|
-
steps.push({
|
|
562
|
-
command: 'export PATH="$PWD/node_modules/.bin:$PATH"',
|
|
563
|
-
note: "use the project-local agent-sdk bin",
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
702
|
if (!options.signedIn) {
|
|
703
|
+
const login = options.cliOnPath
|
|
704
|
+
? `${CLI_COMMAND_NAME} login`
|
|
705
|
+
: `npx ${PACKAGE_NAME} login`;
|
|
567
706
|
steps.push({
|
|
568
|
-
command:
|
|
707
|
+
command: login,
|
|
569
708
|
note: "connect your Cursor account (powers model turns)",
|
|
570
709
|
});
|
|
571
710
|
}
|
|
711
|
+
const dev = options.cliOnPath
|
|
712
|
+
? `${CLI_COMMAND_NAME} dev`
|
|
713
|
+
: `npx ${PACKAGE_NAME} dev`;
|
|
572
714
|
steps.push({
|
|
573
|
-
command:
|
|
715
|
+
command: dev,
|
|
574
716
|
note: "start the local playground",
|
|
575
717
|
});
|
|
576
718
|
return steps;
|
|
@@ -597,8 +739,9 @@ export function printInitGuide(): void {
|
|
|
597
739
|
process.stdout.write(`${CLI_COMMAND_NAME} — coding agent setup
|
|
598
740
|
|
|
599
741
|
Scaffold a project, then open the playground:
|
|
600
|
-
npx ${PACKAGE_NAME} init ./my-agent # installs deps +
|
|
601
|
-
|
|
742
|
+
npx ${PACKAGE_NAME} init ./my-agent # installs deps + links agent-sdk onto PATH
|
|
743
|
+
npx ${PACKAGE_NAME} init ./my-triage --template triage # start from a template (agent-sdk templates: triage)
|
|
744
|
+
cd ./my-agent && agent-sdk dev # or: npx ${PACKAGE_NAME} dev
|
|
602
745
|
|
|
603
746
|
Recommended loop once the project exists (from the project directory):
|
|
604
747
|
1. Edit agent/ + tools under agent/tools/
|