@stigmer/cli 3.12.3 → 3.12.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.
Files changed (36) hide show
  1. package/commands/list.js +7 -15
  2. package/commands/list.js.map +1 -1
  3. package/package.json +5 -5
  4. package/registry/verb-support.d.ts.map +1 -1
  5. package/registry/verb-support.js +6 -3
  6. package/registry/verb-support.js.map +1 -1
  7. package/resources/list.d.ts.map +1 -1
  8. package/resources/list.js +39 -9
  9. package/resources/list.js.map +1 -1
  10. package/resources/session.d.ts +0 -8
  11. package/resources/session.d.ts.map +1 -1
  12. package/resources/session.js +6 -33
  13. package/resources/session.js.map +1 -1
  14. package/resources/skill.d.ts +0 -10
  15. package/resources/skill.d.ts.map +1 -1
  16. package/resources/skill.js +8 -59
  17. package/resources/skill.js.map +1 -1
  18. package/resources/stream/events.d.ts +1 -1
  19. package/resources/stream/events.d.ts.map +1 -1
  20. package/resources/stream/events.js +8 -6
  21. package/resources/stream/events.js.map +1 -1
  22. package/resources/stream/render-plaintext.d.ts.map +1 -1
  23. package/resources/stream/render-plaintext.js +19 -5
  24. package/resources/stream/render-plaintext.js.map +1 -1
  25. package/src/commands/list.ts +7 -17
  26. package/src/registry/registry.test.ts +50 -8
  27. package/src/registry/verb-support.ts +6 -3
  28. package/src/resources/list.ts +40 -9
  29. package/src/resources/read-completion.integration.test.ts +14 -4
  30. package/src/resources/session.ts +6 -39
  31. package/src/resources/skill.test.ts +5 -77
  32. package/src/resources/skill.ts +8 -67
  33. package/src/resources/stream/events.ts +9 -7
  34. package/src/resources/stream/render-plaintext.test.ts +51 -0
  35. package/src/resources/stream/render-plaintext.ts +24 -6
  36. package/src/resources/session.test.ts +0 -48
@@ -8,10 +8,8 @@
8
8
  import { readdirSync, readFileSync, statSync } from "node:fs";
9
9
  import { join, relative, resolve } from "node:path";
10
10
  import { create } from "@bufbuild/protobuf";
11
- import { Code, ConnectError } from "@connectrpc/connect";
12
11
  import type { Skill } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/api_pb";
13
- import type { PushSkillRequest } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/io_pb";
14
- import { CreateSkillArtifactUploadUrlRequestSchema, PushSkillRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/io_pb";
12
+ import { PushSkillRequestSchema } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/io_pb";
15
13
  import { GitProvenanceSchema } from "@stigmer/protos/ai/stigmer/agentic/skill/v1/status_pb";
16
14
  import { ApiResourceVisibility } from "@stigmer/protos/ai/stigmer/commons/apiresource/enum_pb";
17
15
  import { UpdateVisibilityInputSchema } from "@stigmer/protos/ai/stigmer/commons/apiresource/io_pb";
@@ -322,67 +320,10 @@ export function analyzeDryRun(dir: string, options: IgnoreOptions): DryRunAnalys
322
320
  return { stats, patternSources, sampleIgnored, sampleIncluded };
323
321
  }
324
322
 
325
- /**
326
- * Largest artifact pushed inline in the gRPC request (#675). The server's
327
- * transport cap is 10MB for the WHOLE message, so the artifact leaves 64KB
328
- * of headroom for the request envelope (org, tag, provenance, framing).
329
- * Mirrors the Go SDK's maxInlineArtifactBytes.
330
- */
331
- const MAX_INLINE_ARTIFACT_BYTES = 10 * 1024 * 1024 - 64 * 1024;
332
-
333
- /**
334
- * Push, routing the artifact by size (#675): small artifacts travel inline
335
- * in the request (one round trip, unchanged behavior); larger ones are
336
- * staged over HTTP via createArtifactUploadUrl — a capability URL, so no
337
- * auth header — and pushed by reference. The 100MB skill limit therefore
338
- * no longer collides with the 10MB gRPC message cap.
339
- */
340
- export async function pushRouted(client: Stigmer, request: PushSkillRequest): Promise<Skill> {
341
- if (request.artifact.length <= MAX_INLINE_ARTIFACT_BYTES) {
342
- return client.skill.push(request);
343
- }
344
-
345
- let minted;
346
- try {
347
- minted = await client.skill.createArtifactUploadUrl(
348
- create(CreateSkillArtifactUploadUrlRequestSchema, {
349
- org: request.org,
350
- sizeBytes: BigInt(request.artifact.length),
351
- }),
352
- );
353
- } catch (err) {
354
- if (err instanceof ConnectError && err.code === Code.Unimplemented) {
355
- // Pre-transfer-lane server: an artifact this size physically cannot
356
- // travel inline. Say so instead of surfacing the raw transport error.
357
- throw new UsageError(
358
- `skill artifact is ${formatBytes(request.artifact.length)}, above the ~10MB gRPC message cap, ` +
359
- "and this server does not support the HTTP artifact transfer lane.\n\n" +
360
- "Upgrade stigmer-server to push skills of this size.",
361
- );
362
- }
363
- throw err;
364
- }
365
-
366
- const resp = await fetch(minted.url, {
367
- method: "PUT",
368
- // Buffer keeps undici's BodyInit typing happy where a bare Uint8Array
369
- // view does not (Node-only code path, so Buffer is always available).
370
- body: Buffer.from(request.artifact),
371
- headers: { "content-type": "application/zip" },
372
- });
373
- if (!resp.ok) {
374
- const detail = (await resp.text().catch(() => "")).slice(0, 512).trim();
375
- throw new Error(`skill artifact upload rejected with HTTP ${resp.status}${detail === "" ? "" : `: ${detail}`}`);
376
- }
377
-
378
- // Same request, artifact traveling by reference instead of by value.
379
- const byRef = create(PushSkillRequestSchema, {
380
- ...request,
381
- artifact: new Uint8Array(0),
382
- artifactUploadRef: minted.artifactUploadRef,
383
- });
384
- return client.skill.push(byRef);
385
- }
323
+ // Size-routed push (#675) moved down into the SDK (stigmer#701): the
324
+ // Stigmer client's `skill.push` routes large artifacts over the HTTP
325
+ // transfer lane itself, so the CLI — like every other SDK consumer — just
326
+ // calls push. See sdk/typescript/src/skill.ts (RoutedSkillClient).
386
327
 
387
328
  /** Push a skill from a local directory. Git provenance is auto-detected. */
388
329
  export async function pushSkill(
@@ -410,7 +351,7 @@ export async function pushSkill(
410
351
  message,
411
352
  gitProvenance: provenance,
412
353
  });
413
- const response = await pushRouted(client, request);
354
+ const response = await client.skill.push(request);
414
355
  const applied = await applyDeclaredVisibility(client, response, visibility);
415
356
  return toResult(applied, name, message, stats.totalSize, visibility);
416
357
  }
@@ -452,7 +393,7 @@ export async function pushSkillFromClone(
452
393
  message: params.message,
453
394
  gitProvenance: provenance,
454
395
  });
455
- const response = await pushRouted(client, request);
396
+ const response = await client.skill.push(request);
456
397
  const applied = await applyDeclaredVisibility(client, response, visibility);
457
398
  return toResult(applied, name, params.message, stats.totalSize, visibility);
458
399
  }
@@ -540,7 +481,7 @@ export async function pushSkillFromArchive(
540
481
  });
541
482
  // Size-routed like every other push path (#675): pre-packaged archives are
542
483
  // the release-pipeline lane, exactly where content-heavy skills come from.
543
- const response = await pushRouted(client, request);
484
+ const response = await client.skill.push(request);
544
485
  const applied = await applyDeclaredVisibility(client, response, archive.meta.visibility);
545
486
  return toResult(applied, archive.meta.name, message, archive.totalSize, archive.meta.visibility);
546
487
  }
@@ -1,17 +1,19 @@
1
1
  // The sealed event model for the headless snapshot differ.
2
2
  //
3
- // Mirrors Go's pkg/executiontui.Event family (events.go). The differ projects
4
- // each AgentExecution snapshot into a sequence of these discrete events; the
3
+ // Inherited from the retired Go CLI's pkg/executiontui.Event family (deleted
4
+ // with the TypeScript migration, PR #203). The differ projects each
5
+ // AgentExecution snapshot into a sequence of these discrete events; the
5
6
  // NDJSON and plaintext renderers consume them. The Ink TTY path does NOT use
6
- // this model — it renders from full snapshots via @stigmer/react.
7
+ // this model — it renders from full snapshots via @stigmer/ink.
7
8
  //
8
- // `kind` is the discriminant. The NDJSON renderer maps each kind to Go's exact
9
- // wire taxonomy (run_stream_json.go), so kinds are deliberately 1:1 with the Go
10
- // event types rather than the wire strings.
9
+ // `kind` is the discriminant. The NDJSON renderer preserves the retired Go
10
+ // CLI's exact wire taxonomy (its run_stream_json.go) — that output is a
11
+ // machine contract consumers may parse — so kinds are deliberately 1:1 with
12
+ // the original event types rather than the wire strings.
11
13
 
12
14
  import type { JsonObject } from "@bufbuild/protobuf";
13
15
 
14
- /** A tool call projected for rendering. Mirrors toolrender.ToolCallInfo. */
16
+ /** A tool call projected for rendering. Shape inherited from the retired Go CLI's toolrender.ToolCallInfo. */
15
17
  export interface ToolCallInfo {
16
18
  readonly id: string;
17
19
  readonly name: string;
@@ -39,6 +39,57 @@ describe("PlaintextRenderer", () => {
39
39
  expect(status).toContain("✗ shell: exploded");
40
40
  });
41
41
 
42
+ it("titles shell status lines with the model-authored intent when present (stigmer#276)", () => {
43
+ const { status } = render([
44
+ {
45
+ kind: "toolRunning",
46
+ toolCallId: "t1",
47
+ subAgentId: "",
48
+ toolCall: toolInfo({
49
+ name: "execute",
50
+ status: "running",
51
+ args: { command: "npx vitest run src/parser", description: "Run unit tests for the parser" },
52
+ }),
53
+ },
54
+ {
55
+ kind: "toolCompleted",
56
+ toolCallId: "t1",
57
+ subAgentId: "",
58
+ toolCall: toolInfo({
59
+ name: "execute",
60
+ args: { command: "npx vitest run src/parser", description: "Run unit tests for the parser" },
61
+ }),
62
+ },
63
+ ]);
64
+ expect(status).toContain("⠋ Run unit tests for the parser");
65
+ expect(status).toContain("✓ Run unit tests for the parser");
66
+ expect(status).not.toContain("execute");
67
+ });
68
+
69
+ it("keeps the raw tool name when there is no intent, and never titles non-shell tools from description", () => {
70
+ const { status } = render([
71
+ {
72
+ kind: "toolCompleted",
73
+ toolCallId: "t1",
74
+ subAgentId: "",
75
+ toolCall: toolInfo({ name: "execute", args: { command: "ls" } }),
76
+ },
77
+ {
78
+ kind: "toolCompleted",
79
+ toolCallId: "t2",
80
+ subAgentId: "",
81
+ toolCall: toolInfo({
82
+ name: "task",
83
+ args: { description: "general-purpose research", prompt: "Find the docs" },
84
+ }),
85
+ },
86
+ ]);
87
+ // A task tool's description is the sub-agent subject, never a row title.
88
+ expect(status).toContain("✓ execute");
89
+ expect(status).toContain("✓ task");
90
+ expect(status).not.toContain("general-purpose research");
91
+ });
92
+
42
93
  it("renders an interrupted tool with the neutral glyph, not a checkmark (issue #207)", () => {
43
94
  const { status } = render([
44
95
  { kind: "toolInterrupted", toolCallId: "t1", subAgentId: "", toolCall: toolInfo({ name: "shell", status: "interrupted" }) },
@@ -1,13 +1,14 @@
1
1
  // Plaintext headless renderer for non-TTY output (piped stdout).
2
2
  //
3
- // Mirrors Go's streamAgentPlainText (run_stream_plaintext.go): clean AI text to
3
+ // Inherited from the retired Go CLI's streamAgentPlainText: clean AI text to
4
4
  // the data sink (stdout), terse tool/status lines to the status sink (stderr).
5
5
  // No ANSI, no frames. Approvals are auto-skipped — there is no keyboard here;
6
6
  // use --json for a machine-readable non-TTY stream. Phase/done/stream-error are
7
7
  // not rendered: the driver returns the outcome and the command surfaces it.
8
8
 
9
9
  import { ApprovalAction } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
10
- import type { ApprovalNeededEvent, StreamEvent } from "./events.js";
10
+ import { resolveToolKindByName, shellIntentFromArgs } from "@stigmer/sdk";
11
+ import type { ApprovalNeededEvent, StreamEvent, ToolCallInfo } from "./events.js";
11
12
  import type { HeadlessRenderer } from "./headless.js";
12
13
  import type { LineWriter } from "./render-ndjson.js";
13
14
 
@@ -40,17 +41,17 @@ export class PlaintextRenderer implements HeadlessRenderer {
40
41
  status.write(`\n> ${event.content}\n\n`);
41
42
  return;
42
43
  case "toolRunning":
43
- status.write(` ⠋ ${event.toolCall.name}\n`);
44
+ status.write(` ⠋ ${toolLine(event.toolCall)}\n`);
44
45
  return;
45
46
  case "toolCompleted":
46
47
  status.write(
47
48
  event.toolCall.error !== ""
48
- ? ` ✗ ${event.toolCall.name}: ${truncate(event.toolCall.error, MAX_ERROR_LEN)}\n`
49
- : ` ✓ ${event.toolCall.name}\n`,
49
+ ? ` ✗ ${toolLine(event.toolCall)}: ${truncate(event.toolCall.error, MAX_ERROR_LEN)}\n`
50
+ : ` ✓ ${toolLine(event.toolCall)}\n`,
50
51
  );
51
52
  return;
52
53
  case "toolInterrupted":
53
- status.write(` ⊘ ${event.toolCall.name} (interrupted)\n`);
54
+ status.write(` ⊘ ${toolLine(event.toolCall)} (interrupted)\n`);
54
55
  return;
55
56
  case "approvalNeeded":
56
57
  status.write(` ⚠ Approval needed: ${event.toolName} (auto-skipped in non-TTY mode)\n`);
@@ -82,6 +83,23 @@ export class PlaintextRenderer implements HeadlessRenderer {
82
83
  }
83
84
  }
84
85
 
86
+ /**
87
+ * The status-line label for a tool call: the model-authored intent phrase
88
+ * when the shell call carries one (stigmer#276) — the same convention that
89
+ * titles rows in the web console and the Ink TTY path — otherwise the raw
90
+ * tool name. Kind is resolved by name only: the snapshot projection carries
91
+ * neither `tool_kind` nor `mcp_server_slug`, and MCP tools never carry an
92
+ * intent (the fixture pins them to null), so name-lookup is sufficient here.
93
+ * NDJSON is deliberately untouched — it is the machine contract.
94
+ */
95
+ function toolLine(toolCall: ToolCallInfo): string {
96
+ const intent = shellIntentFromArgs(
97
+ resolveToolKindByName(toolCall.name),
98
+ toolCall.args,
99
+ );
100
+ return intent ?? toolCall.name;
101
+ }
102
+
85
103
  // Trim and ellipsize. Mirrors Go's truncate.
86
104
  function truncate(value: string, maxLen: number): string {
87
105
  const trimmed = value.trim();
@@ -1,48 +0,0 @@
1
- import { create } from "@bufbuild/protobuf";
2
- import { SessionSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/api_pb";
3
- import { SessionListSchema } from "@stigmer/protos/ai/stigmer/agentic/session/v1/io_pb";
4
- import { describe, expect, it } from "vitest";
5
- import { isSessionAlias, renderSessionList } from "./session.js";
6
-
7
- describe("isSessionAlias", () => {
8
- it.each([
9
- ["session", true],
10
- ["sessions", true],
11
- [" Session ", true],
12
- ["execution", false],
13
- ["agent", false],
14
- ])("%j -> %s", (type, expected) => {
15
- expect(isSessionAlias(type)).toBe(expected);
16
- });
17
- });
18
-
19
- describe("renderSessionList", () => {
20
- const list = create(SessionListSchema, {
21
- totalPages: 1,
22
- entries: [
23
- create(SessionSchema, {
24
- metadata: { id: "ses_1" },
25
- spec: { agentInstanceId: "agi_1", subject: "Fix the build" },
26
- }),
27
- ],
28
- });
29
- const result = { schema: SessionListSchema, message: list };
30
-
31
- it("renders the full list envelope as protojson for json", () => {
32
- const json = JSON.parse(renderSessionList(result, "json"));
33
- expect(json.entries[0].metadata.id).toBe("ses_1");
34
- });
35
-
36
- it("renders a table with the session columns", () => {
37
- const table = renderSessionList(result, "table");
38
- expect(table).toContain("SESSION ID");
39
- expect(table).toContain("ses_1");
40
- expect(table).toContain("agi_1");
41
- expect(table).toContain("Fix the build");
42
- });
43
-
44
- it("renders an empty notice for no sessions", () => {
45
- const empty = { schema: SessionListSchema, message: create(SessionListSchema, {}) };
46
- expect(renderSessionList(empty, "table").toLowerCase()).toContain("no sessions");
47
- });
48
- });