@cargo-ai/cli 1.0.78 → 1.0.80

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 (67) hide show
  1. package/README.md +12 -0
  2. package/build/commands/ask/argvFlags.d.ts +18 -0
  3. package/build/commands/ask/argvFlags.d.ts.map +1 -0
  4. package/build/commands/ask/argvFlags.js +46 -0
  5. package/build/commands/ask/artifacts.d.ts +2 -0
  6. package/build/commands/ask/artifacts.d.ts.map +1 -0
  7. package/build/commands/ask/artifacts.js +100 -0
  8. package/build/commands/ask/binding.d.ts +13 -0
  9. package/build/commands/ask/binding.d.ts.map +1 -0
  10. package/build/commands/ask/binding.js +19 -0
  11. package/build/commands/ask/claudeConfig.d.ts +23 -0
  12. package/build/commands/ask/claudeConfig.d.ts.map +1 -0
  13. package/build/commands/ask/claudeConfig.js +33 -0
  14. package/build/commands/ask/completer.d.ts +48 -0
  15. package/build/commands/ask/completer.d.ts.map +1 -0
  16. package/build/commands/ask/completer.js +217 -0
  17. package/build/commands/ask/history.d.ts +32 -0
  18. package/build/commands/ask/history.d.ts.map +1 -0
  19. package/build/commands/ask/history.js +122 -0
  20. package/build/commands/ask/index.d.ts +14 -0
  21. package/build/commands/ask/index.d.ts.map +1 -0
  22. package/build/commands/ask/index.js +111 -0
  23. package/build/commands/ask/localHarness.d.ts +225 -0
  24. package/build/commands/ask/localHarness.d.ts.map +1 -0
  25. package/build/commands/ask/localHarness.js +1074 -0
  26. package/build/commands/ask/messageStream.d.ts +33 -0
  27. package/build/commands/ask/messageStream.d.ts.map +1 -0
  28. package/build/commands/ask/messageStream.js +114 -0
  29. package/build/commands/ask/persist.d.ts +5 -0
  30. package/build/commands/ask/persist.d.ts.map +1 -0
  31. package/build/commands/ask/persist.js +31 -0
  32. package/build/commands/ask/pnpmShim.d.ts +20 -0
  33. package/build/commands/ask/pnpmShim.d.ts.map +1 -0
  34. package/build/commands/ask/pnpmShim.js +39 -0
  35. package/build/commands/ask/prompt.d.ts +20 -0
  36. package/build/commands/ask/prompt.d.ts.map +1 -0
  37. package/build/commands/ask/prompt.js +228 -0
  38. package/build/commands/ask/rewriteChatFlag.d.ts +9 -0
  39. package/build/commands/ask/rewriteChatFlag.d.ts.map +1 -0
  40. package/build/commands/ask/rewriteChatFlag.js +26 -0
  41. package/build/commands/ask/session.d.ts +94 -0
  42. package/build/commands/ask/session.d.ts.map +1 -0
  43. package/build/commands/ask/session.js +965 -0
  44. package/build/commands/ask/slash.d.ts +20 -0
  45. package/build/commands/ask/slash.d.ts.map +1 -0
  46. package/build/commands/ask/slash.js +73 -0
  47. package/build/commands/ask/state.d.ts +9 -0
  48. package/build/commands/ask/state.d.ts.map +1 -0
  49. package/build/commands/ask/state.js +18 -0
  50. package/build/commands/bookDemo.d.ts +14 -0
  51. package/build/commands/bookDemo.d.ts.map +1 -0
  52. package/build/commands/bookDemo.js +100 -0
  53. package/build/commands/runHandler.d.ts +9 -1
  54. package/build/commands/runHandler.d.ts.map +1 -1
  55. package/build/commands/runHandler.js +16 -1
  56. package/build/index.js +56 -18
  57. package/build/paletteLayout.d.ts +6 -1
  58. package/build/paletteLayout.d.ts.map +1 -1
  59. package/build/paletteLayout.js +35 -9
  60. package/build/utils/cdkState.d.ts +17 -4
  61. package/build/utils/cdkState.d.ts.map +1 -1
  62. package/build/utils/cdkState.js +26 -4
  63. package/build/utils/loadCompiledAgent.d.ts.map +1 -1
  64. package/build/utils/loadCompiledAgent.js +51 -11
  65. package/build/utils/prompt.d.ts.map +1 -1
  66. package/build/utils/prompt.js +4 -1
  67. package/package.json +8 -4
@@ -0,0 +1,33 @@
1
+ export type StreamMessageInput = {
2
+ baseUrl: string;
3
+ getAccessToken: (forceRefresh?: boolean) => Promise<string | undefined>;
4
+ workspaceUuid: string | undefined;
5
+ chatUuid: string;
6
+ messageUuid: string;
7
+ parts: {
8
+ type: "text";
9
+ text: string;
10
+ }[];
11
+ agentUuid?: string;
12
+ /**
13
+ * Fired with the assistant message's uuid as soon as the server announces
14
+ * it, so a caller that stops reading can still cancel the message.
15
+ */
16
+ onAssistantMessageUuid?: (messageUuid: string) => void;
17
+ /**
18
+ * Stops reading the stream. The message keeps running server-side, so a
19
+ * caller that aborts should also cancel it by uuid — otherwise the next
20
+ * question in that chat is refused with `lastMessageNotFinished`.
21
+ */
22
+ abortSignal?: AbortSignal;
23
+ };
24
+ /**
25
+ * POST /v1/ai/messages/stream and yield assistant text deltas.
26
+ *
27
+ * The typed API client always buffers JSON, so Ask talks to the SSE endpoint
28
+ * directly — same contract as the web app's DefaultChatTransport.
29
+ */
30
+ export declare function streamMessageTextDeltas(input: StreamMessageInput): AsyncGenerator<string>;
31
+ /** Exported for tests — turns an error JSON body into a short message. */
32
+ export declare function describeStreamFailure(status: number, body: string): string;
33
+ //# sourceMappingURL=messageStream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messageStream.d.ts","sourceRoot":"","sources":["../../../src/commands/ask/messageStream.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,CAAC,YAAY,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACxE,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,sBAAsB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,CAAC;AAEF;;;;;GAKG;AACH,wBAAuB,uBAAuB,CAC5C,KAAK,EAAE,kBAAkB,GACxB,cAAc,CAAC,MAAM,CAAC,CA8FxB;AAED,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAoC1E"}
@@ -0,0 +1,114 @@
1
+ import { parseJsonEventStream, uiMessageChunkSchema } from "ai";
2
+ import { getProxyFetch } from "../../proxy.js";
3
+ /**
4
+ * POST /v1/ai/messages/stream and yield assistant text deltas.
5
+ *
6
+ * The typed API client always buffers JSON, so Ask talks to the SSE endpoint
7
+ * directly — same contract as the web app's DefaultChatTransport.
8
+ */
9
+ export async function* streamMessageTextDeltas(input) {
10
+ const token = await input.getAccessToken();
11
+ if (token === undefined) {
12
+ throw new Error("Not authenticated.");
13
+ }
14
+ const url = `${input.baseUrl.replace(/\/$/, "")}/v1/ai/messages/stream`;
15
+ const proxyFetch = getProxyFetch(url);
16
+ const fetchFn = proxyFetch !== undefined ? proxyFetch : globalThis.fetch;
17
+ const headers = {
18
+ Authorization: `Bearer ${token}`,
19
+ "Content-Type": "application/json",
20
+ Accept: "text/event-stream",
21
+ "x-cargo-origin": "cli",
22
+ };
23
+ if (input.workspaceUuid !== undefined) {
24
+ headers["selected-workspace-uuid"] = input.workspaceUuid;
25
+ }
26
+ const body = {
27
+ chatUuid: input.chatUuid,
28
+ chatTrigger: { type: "app" },
29
+ uuid: input.messageUuid,
30
+ parts: input.parts,
31
+ };
32
+ if (input.agentUuid !== undefined) {
33
+ body["agentUuid"] = input.agentUuid;
34
+ }
35
+ const response = await fetchFn(url, {
36
+ method: "POST",
37
+ headers,
38
+ body: JSON.stringify(body),
39
+ signal: input.abortSignal,
40
+ });
41
+ if (response.ok === false) {
42
+ const text = await response.text();
43
+ throw new Error(describeStreamFailure(response.status, text));
44
+ }
45
+ if (response.body === null) {
46
+ throw new Error("Message stream returned no body.");
47
+ }
48
+ const parsed = parseJsonEventStream({
49
+ stream: response.body,
50
+ schema: uiMessageChunkSchema,
51
+ });
52
+ const reader = parsed.getReader();
53
+ try {
54
+ for (;;) {
55
+ const { done, value } = await reader.read();
56
+ if (done === true) {
57
+ break;
58
+ }
59
+ if (value.success === false) {
60
+ continue;
61
+ }
62
+ const chunk = value.value;
63
+ if (chunk.type === "start" && typeof chunk.messageId === "string") {
64
+ if (input.onAssistantMessageUuid !== undefined) {
65
+ input.onAssistantMessageUuid(chunk.messageId);
66
+ }
67
+ continue;
68
+ }
69
+ if (chunk.type === "text-delta" && typeof chunk.delta === "string") {
70
+ yield chunk.delta;
71
+ continue;
72
+ }
73
+ if (chunk.type === "error") {
74
+ const message = typeof chunk.errorText === "string"
75
+ ? chunk.errorText
76
+ : "Message stream failed.";
77
+ throw new Error(message);
78
+ }
79
+ }
80
+ }
81
+ finally {
82
+ reader.releaseLock();
83
+ }
84
+ }
85
+ /** Exported for tests — turns an error JSON body into a short message. */
86
+ export function describeStreamFailure(status, body) {
87
+ try {
88
+ const parsed = JSON.parse(body);
89
+ if (typeof parsed === "object" &&
90
+ parsed !== null &&
91
+ "error" in parsed &&
92
+ typeof parsed.error === "object" &&
93
+ parsed.error !== null) {
94
+ const error = parsed.error;
95
+ const description = typeof error["description"] === "string"
96
+ ? error["description"]
97
+ : undefined;
98
+ const code = typeof error["code"] === "string" ? error["code"] : undefined;
99
+ if (description !== undefined) {
100
+ return description;
101
+ }
102
+ if (code !== undefined) {
103
+ return `Message stream failed (${String(status)}): ${code}`;
104
+ }
105
+ }
106
+ }
107
+ catch {
108
+ // Fall through to the raw body.
109
+ }
110
+ if (body.length > 0) {
111
+ return `Message stream failed (${String(status)}): ${body}`;
112
+ }
113
+ return `Message stream failed (${String(status)}).`;
114
+ }
@@ -0,0 +1,5 @@
1
+ import { type AskState } from "./state.js";
2
+ export declare function getAskStatePath(): string;
3
+ export declare function loadAskState(): AskState | undefined;
4
+ export declare function saveAskState(state: AskState): boolean;
5
+ //# sourceMappingURL=persist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../../src/commands/ask/persist.ts"],"names":[],"mappings":"AAKA,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEvD,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,YAAY,IAAI,QAAQ,GAAG,SAAS,CAYnD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAUrD"}
@@ -0,0 +1,31 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { getConfigDir } from "@cargo-ai/cdk/cli";
4
+ import { asAskState } from "./state.js";
5
+ export function getAskStatePath() {
6
+ return join(getConfigDir(), "ask.json");
7
+ }
8
+ export function loadAskState() {
9
+ const path = getAskStatePath();
10
+ if (existsSync(path) === false) {
11
+ return undefined;
12
+ }
13
+ try {
14
+ return asAskState(JSON.parse(readFileSync(path, "utf-8")));
15
+ }
16
+ catch {
17
+ return undefined;
18
+ }
19
+ }
20
+ export function saveAskState(state) {
21
+ try {
22
+ mkdirSync(getConfigDir(), { recursive: true });
23
+ writeFileSync(getAskStatePath(), `${JSON.stringify(state, null, 2)}\n`, {
24
+ mode: 0o600,
25
+ });
26
+ return true;
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Claude Code's harness bootstrap always runs `pnpm install` under
3
+ * `.harness-bootstrap/claude-code/`. The recipe needs pnpm 10 (workspace
4
+ * `allowBuilds`) and must not hit a broken Volta shim or an older pnpm that
5
+ * refuses this npm-configured monorepo.
6
+ *
7
+ * Exported for tests.
8
+ */
9
+ export declare const HARNESS_PNPM_PACKAGE = "pnpm@10";
10
+ /** Directory holding the `pnpm` shim used for local Ask harness bootstrap. */
11
+ export declare function getHarnessPnpmShimDir(): string;
12
+ /**
13
+ * Write (or refresh) an executable `pnpm` that delegates to
14
+ * `npm exec --package=pnpm@10`, then prepend that directory to `process.env.PATH`
15
+ * so sandbox bootstrap and later commands see it first.
16
+ */
17
+ export declare function ensureHarnessPnpmOnPath(): string;
18
+ /** Body of the shim script. Exported for tests. */
19
+ export declare function harnessPnpmShimScript(): string;
20
+ //# sourceMappingURL=pnpmShim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pnpmShim.d.ts","sourceRoot":"","sources":["../../../src/commands/ask/pnpmShim.ts"],"names":[],"mappings":"AAKA;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,YAAY,CAAC;AAE9C,8EAA8E;AAC9E,wBAAgB,qBAAqB,IAAI,MAAM,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,IAAI,MAAM,CAchD;AAED,mDAAmD;AACnD,wBAAgB,qBAAqB,IAAI,MAAM,CAI9C"}
@@ -0,0 +1,39 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { delimiter, join } from "node:path";
3
+ import { getConfigDir } from "@cargo-ai/cdk/cli";
4
+ /**
5
+ * Claude Code's harness bootstrap always runs `pnpm install` under
6
+ * `.harness-bootstrap/claude-code/`. The recipe needs pnpm 10 (workspace
7
+ * `allowBuilds`) and must not hit a broken Volta shim or an older pnpm that
8
+ * refuses this npm-configured monorepo.
9
+ *
10
+ * Exported for tests.
11
+ */
12
+ export const HARNESS_PNPM_PACKAGE = "pnpm@10";
13
+ /** Directory holding the `pnpm` shim used for local Ask harness bootstrap. */
14
+ export function getHarnessPnpmShimDir() {
15
+ return join(getConfigDir(), "ask-shims");
16
+ }
17
+ /**
18
+ * Write (or refresh) an executable `pnpm` that delegates to
19
+ * `npm exec --package=pnpm@10`, then prepend that directory to `process.env.PATH`
20
+ * so sandbox bootstrap and later commands see it first.
21
+ */
22
+ export function ensureHarnessPnpmOnPath() {
23
+ const dir = getHarnessPnpmShimDir();
24
+ mkdirSync(dir, { recursive: true });
25
+ const shimPath = join(dir, "pnpm");
26
+ writeFileSync(shimPath, harnessPnpmShimScript(), { mode: 0o755 });
27
+ const current = process.env["PATH"];
28
+ process.env["PATH"] =
29
+ current === undefined || current === ""
30
+ ? dir
31
+ : `${dir}${delimiter}${current}`;
32
+ return dir;
33
+ }
34
+ /** Body of the shim script. Exported for tests. */
35
+ export function harnessPnpmShimScript() {
36
+ return `#!/bin/sh
37
+ exec npm exec --yes --package=${HARNESS_PNPM_PACKAGE} -- pnpm "$@"
38
+ `;
39
+ }
@@ -0,0 +1,20 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ /**
3
+ * Ctrl-C on the Ask line.
4
+ *
5
+ * Raised rather than exiting on the spot so the chat session unwinds through
6
+ * its `finally` and destroys the local harness first. The command turns it
7
+ * back into exit code 130.
8
+ */
9
+ export declare class AskInterrupted extends Error {
10
+ constructor();
11
+ }
12
+ /**
13
+ * Interactive Ask line: `◆ …` with a live suggestion menu under the rail
14
+ * while the line matches a slash command or a path-like token.
15
+ *
16
+ * Enter submits (accepting the highlighted suggestion when the menu is open).
17
+ * Empty Enter leaves. Tab inserts the highlight without submitting.
18
+ */
19
+ export declare function readAskLine(input: NodeJS.ReadableStream, cwd: string): Promise<string | undefined>;
20
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../../src/commands/ask/prompt.ts"],"names":[],"mappings":";AAiBA;;;;;;GAMG;AACH,qBAAa,cAAe,SAAQ,KAAK;;CAKxC;AASD;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,CAAC,cAAc,EAC5B,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAiQ7B"}
@@ -0,0 +1,228 @@
1
+ import * as readline from "node:readline";
2
+ import { colors } from "../runHandler.js";
3
+ import { applyCompletion, ASK_COMPLETION_MENU_LIMIT, listCompletions, } from "./completer.js";
4
+ /**
5
+ * Ctrl-C on the Ask line.
6
+ *
7
+ * Raised rather than exiting on the spot so the chat session unwinds through
8
+ * its `finally` and destroys the local harness first. The command turns it
9
+ * back into exit code 130.
10
+ */
11
+ export class AskInterrupted extends Error {
12
+ constructor() {
13
+ super("ask-interrupted");
14
+ this.name = "AskInterrupted";
15
+ }
16
+ }
17
+ function isTTYStream(stream) {
18
+ return (typeof stream.setRawMode === "function" &&
19
+ stream.isTTY === true);
20
+ }
21
+ /**
22
+ * Interactive Ask line: `◆ …` with a live suggestion menu under the rail
23
+ * while the line matches a slash command or a path-like token.
24
+ *
25
+ * Enter submits (accepting the highlighted suggestion when the menu is open).
26
+ * Empty Enter leaves. Tab inserts the highlight without submitting.
27
+ */
28
+ export async function readAskLine(input, cwd) {
29
+ return new Promise((resolvePromise, rejectPromise) => {
30
+ let line = "";
31
+ let cursor = 0;
32
+ let selected = 0;
33
+ let painted = false;
34
+ let finished = false;
35
+ const wasRaw = isTTYStream(input) ? input.isRaw === true : false;
36
+ if (isTTYStream(input) === true) {
37
+ input.setRawMode(true);
38
+ }
39
+ // A prompt shown mid-turn — the clack select behind a tool approval — closes
40
+ // its readline interface on the way out, and `Interface.close()` pauses the
41
+ // stream it was reading. An explicitly paused stream does not start flowing
42
+ // again just because a listener is added, so without this the Ask line
43
+ // paints after such a turn and then ignores every keystroke.
44
+ input.resume();
45
+ readline.emitKeypressEvents(input);
46
+ const onKeypress = (str, key) => {
47
+ if (finished === true) {
48
+ return;
49
+ }
50
+ if (key.ctrl === true && key.name === "c") {
51
+ // Raw mode turns Ctrl-C into an ordinary keypress rather than a
52
+ // process-group SIGINT, so nothing else tears the harness down.
53
+ // Exiting here would skip the session's `finally` and leave Claude
54
+ // Code and its bridge running — the orphan this branch exists to fix.
55
+ // Unwind instead and let the caller exit once cleanup has run.
56
+ fail(new AskInterrupted());
57
+ return;
58
+ }
59
+ if (key.ctrl === true && key.name === "d") {
60
+ if (line === "") {
61
+ finish(undefined);
62
+ }
63
+ return;
64
+ }
65
+ if (key.name === "return" || key.name === "enter") {
66
+ const matches = visibleMatches();
67
+ if (matches.length > 0) {
68
+ const pick = matches[selected];
69
+ if (pick !== undefined) {
70
+ line = applyCompletion(line, pick);
71
+ cursor = line.length;
72
+ }
73
+ }
74
+ const trimmed = line.trim();
75
+ finish(trimmed === "" ? undefined : trimmed);
76
+ return;
77
+ }
78
+ if (key.name === "tab") {
79
+ const matches = visibleMatches();
80
+ if (matches.length === 0) {
81
+ return;
82
+ }
83
+ const pick = matches[selected];
84
+ if (pick === undefined) {
85
+ return;
86
+ }
87
+ line = applyCompletion(line, pick);
88
+ cursor = line.length;
89
+ selected = 0;
90
+ paint();
91
+ return;
92
+ }
93
+ if (key.name === "up") {
94
+ const matches = visibleMatches();
95
+ if (matches.length === 0) {
96
+ return;
97
+ }
98
+ selected = selected <= 0 ? matches.length - 1 : selected - 1;
99
+ paint();
100
+ return;
101
+ }
102
+ if (key.name === "down") {
103
+ const matches = visibleMatches();
104
+ if (matches.length === 0) {
105
+ return;
106
+ }
107
+ selected = selected >= matches.length - 1 ? 0 : selected + 1;
108
+ paint();
109
+ return;
110
+ }
111
+ if (key.name === "left") {
112
+ if (cursor > 0) {
113
+ cursor -= 1;
114
+ paint();
115
+ }
116
+ return;
117
+ }
118
+ if (key.name === "right") {
119
+ if (cursor < line.length) {
120
+ cursor += 1;
121
+ paint();
122
+ }
123
+ return;
124
+ }
125
+ if (key.name === "backspace") {
126
+ if (cursor === 0) {
127
+ return;
128
+ }
129
+ line = `${line.slice(0, cursor - 1)}${line.slice(cursor)}`;
130
+ cursor -= 1;
131
+ selected = 0;
132
+ paint();
133
+ return;
134
+ }
135
+ if (key.name === "delete") {
136
+ if (cursor >= line.length) {
137
+ return;
138
+ }
139
+ line = `${line.slice(0, cursor)}${line.slice(cursor + 1)}`;
140
+ selected = 0;
141
+ paint();
142
+ return;
143
+ }
144
+ if (str === undefined || str.length === 0) {
145
+ return;
146
+ }
147
+ if (key.ctrl === true || key.meta === true) {
148
+ return;
149
+ }
150
+ const code = str.codePointAt(0);
151
+ if (code === undefined || code < 32 || code === 127) {
152
+ return;
153
+ }
154
+ line = `${line.slice(0, cursor)}${str}${line.slice(cursor)}`;
155
+ cursor += str.length;
156
+ selected = 0;
157
+ paint();
158
+ };
159
+ input.on("keypress", onKeypress);
160
+ paint();
161
+ function visibleMatches() {
162
+ return listCompletions(line, cwd).slice(0, ASK_COMPLETION_MENU_LIMIT);
163
+ }
164
+ function clearFrame() {
165
+ if (painted === false || process.stderr.isTTY !== true) {
166
+ return;
167
+ }
168
+ // Cursor sits on the ◆ line; wipe from there through the menu below.
169
+ process.stderr.write("\r\x1B[J");
170
+ painted = false;
171
+ }
172
+ function paint() {
173
+ const matches = visibleMatches();
174
+ if (selected >= matches.length) {
175
+ selected = matches.length === 0 ? 0 : matches.length - 1;
176
+ }
177
+ clearFrame();
178
+ process.stderr.write(`${colors.cyan("◆")} ${line}`);
179
+ if (matches.length > 0) {
180
+ process.stderr.write("\n");
181
+ for (let index = 0; index < matches.length; index += 1) {
182
+ const hit = matches[index];
183
+ if (hit === undefined) {
184
+ continue;
185
+ }
186
+ const active = index === selected;
187
+ const bullet = active === true ? colors.cyan("●") : colors.dim("○");
188
+ const label = active === true ? colors.cyan(hit) : colors.dim(hit);
189
+ process.stderr.write(`${colors.dim("│")} ${bullet} ${label}`);
190
+ if (index < matches.length - 1) {
191
+ process.stderr.write("\n");
192
+ }
193
+ }
194
+ // Return to the ◆ line so the next clearFrame / keystroke is correct.
195
+ process.stderr.write(`\x1B[${String(matches.length)}A`);
196
+ }
197
+ // Column after `◆ ` (3 visible cells) plus the cursor in the line.
198
+ const column = 3 + cursor + 1;
199
+ process.stderr.write(`\r\x1B[${String(column)}G`);
200
+ painted = true;
201
+ }
202
+ function cleanup() {
203
+ input.off("keypress", onKeypress);
204
+ if (isTTYStream(input) === true) {
205
+ input.setRawMode(wasRaw);
206
+ }
207
+ }
208
+ function finish(value) {
209
+ if (finished === true) {
210
+ return;
211
+ }
212
+ finished = true;
213
+ clearFrame();
214
+ cleanup();
215
+ resolvePromise(value);
216
+ }
217
+ function fail(error) {
218
+ if (finished === true) {
219
+ return;
220
+ }
221
+ finished = true;
222
+ clearFrame();
223
+ cleanup();
224
+ process.stderr.write("\n");
225
+ rejectPromise(error);
226
+ }
227
+ });
228
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `cargo-ai --chat` is an entrance to `ask`, not a commander flag that a
3
+ * subcommand could collide with. Rewrite it away before parse so `ask` owns
4
+ * the rest of the argv.
5
+ *
6
+ * Exported for tests; `index.ts` is the only production caller.
7
+ */
8
+ export declare function rewriteChatFlag(argv: readonly string[]): string[];
9
+ //# sourceMappingURL=rewriteChatFlag.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rewriteChatFlag.d.ts","sourceRoot":"","sources":["../../../src/commands/ask/rewriteChatFlag.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CA0BjE"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `cargo-ai --chat` is an entrance to `ask`, not a commander flag that a
3
+ * subcommand could collide with. Rewrite it away before parse so `ask` owns
4
+ * the rest of the argv.
5
+ *
6
+ * Exported for tests; `index.ts` is the only production caller.
7
+ */
8
+ export function rewriteChatFlag(argv) {
9
+ const prefix = argv.slice(0, 2);
10
+ const rest = argv.slice(2);
11
+ const chatIndex = rest.indexOf("--chat");
12
+ if (chatIndex === -1) {
13
+ return [...argv];
14
+ }
15
+ const firstCommand = rest.findIndex((arg) => arg.startsWith("-") === false);
16
+ if (firstCommand !== -1 &&
17
+ firstCommand < chatIndex &&
18
+ rest[firstCommand] !== "ask") {
19
+ return [...argv];
20
+ }
21
+ const without = rest.filter((arg) => arg !== "--chat");
22
+ if (without[0] === "ask") {
23
+ return [...prefix, ...without];
24
+ }
25
+ return [...prefix, "ask", ...without];
26
+ }
@@ -0,0 +1,94 @@
1
+ import type { Ai } from "@cargo-ai/api";
2
+ import type { Api } from "../../api.js";
3
+ export type AskSessionOptions = {
4
+ api: Api;
5
+ message?: string;
6
+ continueChat: boolean;
7
+ agentUuid?: string;
8
+ chatUuid?: string;
9
+ dir: string;
10
+ };
11
+ type SessionMode = {
12
+ kind: "remote";
13
+ } | {
14
+ kind: "local";
15
+ };
16
+ /**
17
+ * When the proxy token baked into the running harness stops being safe to
18
+ * reuse.
19
+ *
20
+ * The server re-arms the token on every proxy call, so the clock runs from
21
+ * the last turn that reached Anthropic rather than from when it was minted.
22
+ * Anchoring it to mint time instead would tear down a conversation that is
23
+ * still going — a cold start, a replayed history preamble and the loss of
24
+ * Claude Code's in-session memory, for a token that had not expired.
25
+ *
26
+ * Exported for tests.
27
+ */
28
+ export declare function proxyTokenDeadlineMs(input: {
29
+ mintedAtMs: number;
30
+ lastUsedAtMs: number | undefined;
31
+ ttlMs: number;
32
+ }): number;
33
+ /**
34
+ * No agent → local Claude Code on the host. Any agent streams remotely, like
35
+ * the webapp chat: what it runs — a tool loop or a harness against its bound
36
+ * repo — follows from its deployed release, which the API resolves itself.
37
+ *
38
+ * Exported for tests.
39
+ */
40
+ export declare function modeForAgent(agent: Ai.Agent | undefined): SessionMode;
41
+ /**
42
+ * True when the chat is already on the server. A saved `--continue` uuid can
43
+ * point at nothing (Esc before the first turn, wiped local DB, different API),
44
+ * and that must become a create — not chatNotFound on the first message.
45
+ *
46
+ * Careful with the false case: it means "not readable", which covers a chat
47
+ * that was deleted as well as one that never existed. Deletion is soft, so
48
+ * the row keeps the uuid and creating it again fails on the primary key —
49
+ * see where `--continue` resolves its uuid.
50
+ */
51
+ /**
52
+ * The result of trying to take the chat for one local turn. `busy` is an
53
+ * answer rather than a failure — another session is mid-turn — so it does not
54
+ * end the session the way a transport error does.
55
+ */
56
+ type ChatTurnClaim = {
57
+ outcome: "claimed";
58
+ } | {
59
+ outcome: "busy";
60
+ } | {
61
+ outcome: "unavailable";
62
+ error: unknown;
63
+ };
64
+ /**
65
+ * Take the chat for one local turn.
66
+ *
67
+ * Exported for tests.
68
+ */
69
+ export declare function claimChatTurn(api: Api, chatUuid: string, ownerId: string): Promise<ChatTurnClaim>;
70
+ export declare function doesChatExist(api: Api, chatUuid: string): Promise<boolean>;
71
+ /**
72
+ * A path inside the session directory reads better relative to it; anything
73
+ * outside stays absolute so it is obvious the turn left the project.
74
+ *
75
+ * Exported for tests.
76
+ */
77
+ export declare function displayPath(path: string, dir: string): string;
78
+ /**
79
+ * Same tree grammar as the palette:
80
+ * ◇ <your message>
81
+ * 🧱 reply…
82
+ * ◆
83
+ * Empty Enter or /exit leaves to the parent palette.
84
+ */
85
+ export declare function runAskSession(options: AskSessionOptions): Promise<"palette" | "exit">;
86
+ export declare function runAskPrint(input: {
87
+ api: Api;
88
+ message: string;
89
+ agentUuid?: string;
90
+ chatUuid?: string;
91
+ dir: string;
92
+ }): Promise<void>;
93
+ export {};
94
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/commands/ask/session.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAIxC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AA0BxC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,KAAK,WAAW,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAoB1D;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,MAAM,CAKT;AAiBD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,SAAS,GAAG,WAAW,CAErE;AA8ED;;;;;;;;;GASG;AACH;;;;GAIG;AACH,KAAK,aAAa,GACd;IAAE,OAAO,EAAE,SAAS,CAAA;CAAE,GACtB;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GACnB;IAAE,OAAO,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC;AAE/C;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,CAAC,CAkBxB;AAyCD,wBAAsB,aAAa,CACjC,GAAG,EAAE,GAAG,EACR,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAqBlB;AAsSD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAQ7D;AAkGD;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,CA0b7B;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE;IACvC,GAAG,EAAE,GAAG,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,OAAO,CAAC,IAAI,CAAC,CA8HhB"}