@shardflux/cli 0.25.0 → 0.28.1
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/CHANGELOG.md +154 -1
- package/README.md +212 -36
- package/dist/account-commands.d.ts +2 -4
- package/dist/account-commands.js +2 -6
- package/dist/account-pool.d.ts +11 -1
- package/dist/account-pool.js +48 -0
- package/dist/agent-commands.js +4 -3
- package/dist/agent-git-commands.d.ts +1 -2
- package/dist/agent-git-commands.js +2 -21
- package/dist/agent-models.d.ts +31 -1
- package/dist/agent-models.js +27 -4
- package/dist/agent-setup.d.ts +4 -2
- package/dist/agent-setup.js +6 -4
- package/dist/agent-shell.d.ts +2 -0
- package/dist/agent-shell.js +5 -0
- package/dist/args.d.ts +1 -1
- package/dist/args.js +67 -12
- package/dist/board-args.js +8 -3
- package/dist/board-commands.d.ts +7 -3
- package/dist/board-commands.js +22 -12
- package/dist/brain-commands.d.ts +2 -0
- package/dist/brain-commands.js +78 -0
- package/dist/cli-proxy.d.ts +1 -13
- package/dist/cli-proxy.js +2 -27
- package/dist/client-settings.d.ts +2 -1
- package/dist/client-settings.js +14 -2
- package/dist/codex-cloud.d.ts +2 -0
- package/dist/codex-cloud.js +17 -12
- package/dist/commands.js +8 -0
- package/dist/conversation-commands.d.ts +5 -1
- package/dist/conversation-commands.js +58 -6
- package/dist/conversation-terminal.js +2 -2
- package/dist/desktop-linux.d.ts +12 -1
- package/dist/desktop-linux.js +47 -18
- package/dist/device-approvals.js +24 -2
- package/dist/device-bridge.d.ts +24 -4
- package/dist/device-bridge.js +181 -8
- package/dist/device-folders.d.ts +6 -1
- package/dist/device-folders.js +6 -2
- package/dist/device-helper.d.ts +8 -0
- package/dist/device-helper.js +71 -12
- package/dist/device-mode.d.ts +44 -0
- package/dist/device-mode.js +119 -0
- package/dist/drive-commands.d.ts +2 -2
- package/dist/drive-commands.js +1 -1
- package/dist/format.js +3 -0
- package/dist/forwarder-refresh.d.ts +15 -0
- package/dist/forwarder-refresh.js +32 -0
- package/dist/handler-loader.d.ts +3 -0
- package/dist/handler-loader.js +43 -0
- package/dist/handlers.js +4 -2
- package/dist/land.d.ts +10 -5
- package/dist/land.js +56 -9
- package/dist/linux-helper.js +1 -2
- package/dist/mac-app.d.ts +11 -0
- package/dist/mac-app.js +11 -0
- package/dist/machine-setup-guard.d.ts +9 -0
- package/dist/machine-setup-guard.js +22 -0
- package/dist/machine-setup.d.ts +1 -8
- package/dist/machine-setup.js +2 -22
- package/dist/main.js +19 -16
- package/dist/menubar.d.ts +18 -13
- package/dist/menubar.js +60 -50
- package/dist/orchestrator-args.d.ts +7 -0
- package/dist/orchestrator-args.js +64 -0
- package/dist/orchestrator-commands.d.ts +43 -0
- package/dist/orchestrator-commands.js +260 -0
- package/dist/person-scope.d.ts +20 -0
- package/dist/person-scope.js +61 -0
- package/dist/project-hub-seed.d.ts +20 -0
- package/dist/project-hub-seed.js +59 -0
- package/dist/registry.d.ts +1 -1
- package/dist/registry.js +2 -1
- package/dist/router-commands.d.ts +12 -21
- package/dist/router-commands.js +5 -59
- package/dist/startup-housekeeping.d.ts +5 -0
- package/dist/startup-housekeeping.js +30 -0
- package/dist/t3-launch.js +2 -2
- package/dist/t3-provider.js +4 -2
- package/dist/task-land-hub.py +16 -1
- package/dist/this-computer.d.ts +4 -2
- package/dist/this-computer.js +20 -2
- package/dist/thread-on-computer.js +2 -2
- package/dist/timing.js +3 -1
- package/dist/transcribe-commands.js +6 -2
- package/dist/update.js +4 -2
- package/dist/wait-status.d.ts +1 -1
- package/dist/wait-status.js +2 -0
- package/dist/worktree-twin-engine.d.ts +1 -1
- package/dist/worktree-twin-engine.js +10 -7
- package/dist/worktree-twin.d.ts +6 -0
- package/dist/worktree-twin.js +8 -1
- package/package.json +12 -6
package/dist/agent-commands.js
CHANGED
|
@@ -33,7 +33,7 @@ import { guestFirstPrompt, INBOX_MAX_CHARS, submitInitialPrompt, prepareInitialC
|
|
|
33
33
|
import { conversationForOpen, conversationWorkspace, conversationsUnavailable, getConversation, listConversations, resumeConversation } from "./conversations.js";
|
|
34
34
|
import { conversationClient } from "./conversation-commands.js";
|
|
35
35
|
import { conversationTerminal, reopenConversation } from "./conversation-terminal.js";
|
|
36
|
-
import { CHOICE_PENDING, choiceReport, hasChoice, launchChoice, modelChoice, runFlags } from "./agent-models.js";
|
|
36
|
+
import { CHOICE_PENDING, choiceReport, hasChoice, launchChoice, liveModels, modelChoice, runFlags } from "./agent-models.js";
|
|
37
37
|
const active = (session) => session.state === 'running' || session.state === 'starting';
|
|
38
38
|
/** `<slug>-<6 hex of sha256(identity)>`: the same repository gets the same key on every machine. */
|
|
39
39
|
export function disambiguatedSlug(slug, identity) {
|
|
@@ -135,7 +135,7 @@ export async function terminal(ctx, ws, app, agent, project, values, setupHash,
|
|
|
135
135
|
});
|
|
136
136
|
// This command's --model/--effort, else the conversation's own (a relaunch keeps its model). A computer whose guest
|
|
137
137
|
// predates the choice starts the agent with its defaults, and this command says so once.
|
|
138
|
-
const help = runHelp(cell, app, ctx.signal), explicit = modelChoice(values, app);
|
|
138
|
+
const help = runHelp(cell, app, ctx.signal), explicit = modelChoice(values, app, await liveModels(ctx, values, app));
|
|
139
139
|
const { choice, applied } = await launchChoice(explicit, conversation?.choice, !!conversation, help);
|
|
140
140
|
if (hasChoice(explicit) && !applied && !ctx.json)
|
|
141
141
|
ctx.io.stderr.write(`${CHOICE_PENDING}\n`);
|
|
@@ -242,7 +242,8 @@ export async function launchProjectWith(ctx, values, [path = './', explicitApp],
|
|
|
242
242
|
throw new UsageError(`${target} is not an installed desktop target`);
|
|
243
243
|
const app = explicitApp ?? store.app(projectKey, like?.labels.app === 'codex' ? 'codex' : 'claude');
|
|
244
244
|
// A typo in --model or --effort is refused before anything starts; T3 Code reads the same values (0.17.0+).
|
|
245
|
-
const
|
|
245
|
+
const choiceApp = target === 'codex-app' ? 'codex' : app;
|
|
246
|
+
const choice = modelChoice(values, choiceApp, await liveModels(ctx, values, choiceApp));
|
|
246
247
|
if (target === 'codex-app' || (app === 'codex' && !like && values.in === undefined && !linuxTerminalSession(ctx, values)))
|
|
247
248
|
return launchCodexCloud(ctx, values, path);
|
|
248
249
|
if (target === 'claude-desktop' && hasChoice(choice))
|
|
@@ -6,5 +6,4 @@ export declare function rememberGitAgent(ctx: Ctx, ws: Workspace, folder: string
|
|
|
6
6
|
export declare const AGENT_GIT_HANDLERS: Record<string, Handler>;
|
|
7
7
|
/** No Git or network work when no records are due. Active passes are bounded and never infer deletion from an error. */
|
|
8
8
|
export declare function housekeeping(ctx: HousekeepingCtx, now?: () => number, budgetMs?: number): Promise<void>;
|
|
9
|
-
|
|
10
|
-
export declare function startupHousekeeping(ctx: Pick<Ctx, 'io' | 'store' | 'signal'>): Promise<void>;
|
|
9
|
+
export { startupHousekeeping } from './startup-housekeeping.js';
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
2
|
import { promisify } from 'node:util';
|
|
3
|
-
import {
|
|
3
|
+
import { ShardfluxApiError } from '@shardflux/sdk';
|
|
4
4
|
import { out, resolveWorkspace, str } from "./commands.js";
|
|
5
5
|
import { AgentStateStore } from "./agent-state.js";
|
|
6
|
-
import { resolveApiUrl } from "./args.js";
|
|
7
6
|
import { AgentGit, agentName } from "./agent-git.js";
|
|
8
7
|
import { shardRemote } from "./git-remote.js";
|
|
9
8
|
import { projectIdentity } from "./project-identity.js";
|
|
@@ -274,22 +273,4 @@ export async function housekeeping(ctx, now = Date.now, budgetMs = 1500) {
|
|
|
274
273
|
clearTimeout(timer);
|
|
275
274
|
}
|
|
276
275
|
}
|
|
277
|
-
|
|
278
|
-
export async function startupHousekeeping(ctx) {
|
|
279
|
-
try {
|
|
280
|
-
const state = new AgentStateStore(ctx.store.dir).read();
|
|
281
|
-
if (!state.git_repositories || !Object.keys(state.git_repositories).length)
|
|
282
|
-
return;
|
|
283
|
-
const apiUrl = resolveApiUrl(undefined, ctx.io.env.SHARDFLUX_API_URL ?? ctx.io.env.SHARDFLUX_BASE_URL);
|
|
284
|
-
let cloud;
|
|
285
|
-
await housekeeping({ ...ctx, apiUrl, cloud: () => {
|
|
286
|
-
const key = ctx.io.env.SHARDFLUX_API_KEY?.trim() || ctx.store.profile(apiUrl).api_key?.token;
|
|
287
|
-
if (!key)
|
|
288
|
-
throw new UsageError('saved agents need a project key for housekeeping; run shard setup');
|
|
289
|
-
return cloud ??= new Shardflux({ apiKey: key, baseUrl: apiUrl, versionCheck: false });
|
|
290
|
-
} });
|
|
291
|
-
}
|
|
292
|
-
catch (error) {
|
|
293
|
-
ctx.io.stderr.write(`shard housekeeping: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
276
|
+
export { startupHousekeeping } from "./startup-housekeeping.js";
|
package/dist/agent-models.d.ts
CHANGED
|
@@ -30,13 +30,42 @@ export interface AppModels {
|
|
|
30
30
|
export declare const DEFAULT_CHOICE = "default";
|
|
31
31
|
/** Claude Code `--model <alias>` and `--effort <level>`; Codex `-m <model>` and `-c model_reasoning_effort=<level>`. */
|
|
32
32
|
export declare const MODEL_CHOICES: Readonly<Record<ModelApp, AppModels>>;
|
|
33
|
+
/** A model of the active account's live list (`shard agents models`, 0.27.0+): its id and the efforts it takes. */
|
|
34
|
+
export interface LiveModel {
|
|
35
|
+
id: string;
|
|
36
|
+
efforts: string[];
|
|
37
|
+
}
|
|
38
|
+
interface ModelsSource {
|
|
39
|
+
cloud(): {
|
|
40
|
+
agentAccounts: {
|
|
41
|
+
allModels(opts: {
|
|
42
|
+
provider?: ModelApp;
|
|
43
|
+
}): Promise<{
|
|
44
|
+
accounts: Array<{
|
|
45
|
+
active: boolean;
|
|
46
|
+
models: Array<{
|
|
47
|
+
id: string;
|
|
48
|
+
efforts: string[];
|
|
49
|
+
available: boolean;
|
|
50
|
+
}>;
|
|
51
|
+
}>;
|
|
52
|
+
}>;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The active account's available models (0.27.0+), asked only when `--model` is not one of the aliases above: a full
|
|
58
|
+
* model id is accepted when the account's provider lists it (GET /v1/agent-accounts/models). Null when the API cannot
|
|
59
|
+
* say (an API before the route, or a failure): then only the aliases are accepted.
|
|
60
|
+
*/
|
|
61
|
+
export declare function liveModels(ctx: ModelsSource, values: Values, app: ModelApp): Promise<LiveModel[] | null>;
|
|
33
62
|
/** What a thread starts with: absent fields are the agent's own default (no flag). */
|
|
34
63
|
export interface ModelChoice {
|
|
35
64
|
model?: string;
|
|
36
65
|
effort?: string;
|
|
37
66
|
}
|
|
38
67
|
/** `--model` and `--effort` for this app, checked against its list. Throws a usage error for anything else. */
|
|
39
|
-
export declare function modelChoice(values: Values, app: ModelApp): ModelChoice;
|
|
68
|
+
export declare function modelChoice(values: Values, app: ModelApp, live?: readonly LiveModel[] | null): ModelChoice;
|
|
40
69
|
export declare const hasChoice: (c: ModelChoice | undefined) => c is ModelChoice;
|
|
41
70
|
/** The guest's `sf-agent run` flags for a choice (sf-agent maps them to the agent's own flags). */
|
|
42
71
|
export declare function runFlags(c: ModelChoice | undefined): string[];
|
|
@@ -59,3 +88,4 @@ export declare function choiceReport(c: ModelChoice, applied: boolean): {
|
|
|
59
88
|
effort: string;
|
|
60
89
|
applied: boolean;
|
|
61
90
|
};
|
|
91
|
+
export {};
|
package/dist/agent-models.js
CHANGED
|
@@ -25,10 +25,31 @@ export const MODEL_CHOICES = {
|
|
|
25
25
|
default: { model: 'gpt-6.1-sol', effort: null },
|
|
26
26
|
},
|
|
27
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* The active account's available models (0.27.0+), asked only when `--model` is not one of the aliases above: a full
|
|
30
|
+
* model id is accepted when the account's provider lists it (GET /v1/agent-accounts/models). Null when the API cannot
|
|
31
|
+
* say (an API before the route, or a failure): then only the aliases are accepted.
|
|
32
|
+
*/
|
|
33
|
+
export async function liveModels(ctx, values, app) {
|
|
34
|
+
const model = typeof values.model === 'string' ? values.model.trim().toLowerCase() : '';
|
|
35
|
+
if (!model || MODEL_CHOICES[app].models.some(m => m.id === model))
|
|
36
|
+
return null;
|
|
37
|
+
// Another provider's model is refused without a request: Claude ids are claude-…; a Codex id is anything else.
|
|
38
|
+
const claudeId = model.startsWith('claude-') || MODEL_CHOICES.claude.models.some(m => m.id === model);
|
|
39
|
+
if (claudeId !== (app === 'claude'))
|
|
40
|
+
return null;
|
|
41
|
+
try {
|
|
42
|
+
const active = (await ctx.cloud().agentAccounts.allModels({ provider: app })).accounts.find(a => a.active);
|
|
43
|
+
return active ? active.models.filter(m => m.available).map(m => ({ id: m.id, efforts: m.efforts })) : null;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
28
49
|
const list = (ids) => ids.length > 1 ? `${ids.slice(0, -1).join(', ')} or ${ids.at(-1)}` : ids[0];
|
|
29
50
|
const label = (app) => app === 'claude' ? 'Claude' : 'Codex';
|
|
30
51
|
/** `--model` and `--effort` for this app, checked against its list. Throws a usage error for anything else. */
|
|
31
|
-
export function modelChoice(values, app) {
|
|
52
|
+
export function modelChoice(values, app, live = null) {
|
|
32
53
|
const raw = (name) => {
|
|
33
54
|
const value = values[name];
|
|
34
55
|
if (value === undefined)
|
|
@@ -39,9 +60,11 @@ export function modelChoice(values, app) {
|
|
|
39
60
|
};
|
|
40
61
|
const model = raw('model'), effort = raw('effort'), choices = MODEL_CHOICES[app];
|
|
41
62
|
const option = model === undefined ? undefined : choices.models.find(m => m.id === model);
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
63
|
+
const listed = model === undefined || option ? undefined : live?.find(m => m.id === model);
|
|
64
|
+
if (model !== undefined && !option && !listed) {
|
|
65
|
+
throw new UsageError(`${label(app)} models: ${list(choices.models.map(m => m.id))}${live?.length ? `, or one this account has: ${live.map(m => m.id).join(', ')}` : ', or a model id from shard agents models'}.`);
|
|
66
|
+
}
|
|
67
|
+
const allowed = option?.efforts ?? (listed ? [DEFAULT_CHOICE, ...listed.efforts] : choices.efforts.map(e => e.id));
|
|
45
68
|
if (effort !== undefined && !allowed.includes(effort)) {
|
|
46
69
|
throw new UsageError(`${option?.efforts ? option.label : label(app)} effort levels: ${list(allowed)}.`);
|
|
47
70
|
}
|
package/dist/agent-setup.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import type { Ctx } from './commands.js';
|
|
1
2
|
/**
|
|
2
3
|
* Set in a coding agent's shell: Claude Code (CLAUDECODE), Codex (CODEX_SANDBOX, CODEX_THREAD_ID), Cursor's agent
|
|
3
4
|
* (CURSOR_AGENT) and Gemini CLI (GEMINI_CLI). Such a shell may have a terminal (Cursor and Codex can run commands in a
|
|
4
5
|
* PTY), but nobody types into it: a full-screen view or a prompt there waits forever.
|
|
5
6
|
*/
|
|
6
|
-
export
|
|
7
|
-
export declare function codingAgentShell(env: Record<string, string | undefined>): boolean;
|
|
7
|
+
export { AGENT_SHELL_VARIABLES, codingAgentShell } from './agent-shell.js';
|
|
8
8
|
/**
|
|
9
9
|
* How the agent runs the setup (0.20.1+): it does the work, the person only decides. Onboarding testers (2026-10-12)
|
|
10
10
|
* found agents handing them commands, links and codes, writing paragraphs and asking open questions.
|
|
@@ -46,4 +46,6 @@ export interface GuideState {
|
|
|
46
46
|
apiKey: boolean;
|
|
47
47
|
}
|
|
48
48
|
export declare function setupGuide(state: GuideState): SetupGuide;
|
|
49
|
+
/** The setup guide for this computer: whether a session and an API key are saved (no network call). */
|
|
50
|
+
export declare function agentSetupGuide(ctx: Ctx): ReturnType<typeof setupGuide>;
|
|
49
51
|
export declare function setupGuideText(g: SetupGuide): string;
|
package/dist/agent-setup.js
CHANGED
|
@@ -18,10 +18,7 @@ import { legalFields } from "./legal.js";
|
|
|
18
18
|
* (CURSOR_AGENT) and Gemini CLI (GEMINI_CLI). Such a shell may have a terminal (Cursor and Codex can run commands in a
|
|
19
19
|
* PTY), but nobody types into it: a full-screen view or a prompt there waits forever.
|
|
20
20
|
*/
|
|
21
|
-
export
|
|
22
|
-
export function codingAgentShell(env) {
|
|
23
|
-
return AGENT_SHELL_VARIABLES.some((name) => (env[name]?.trim() ?? '') !== '');
|
|
24
|
-
}
|
|
21
|
+
export { AGENT_SHELL_VARIABLES, codingAgentShell } from "./agent-shell.js";
|
|
25
22
|
/**
|
|
26
23
|
* How the agent runs the setup (0.20.1+): it does the work, the person only decides. Onboarding testers (2026-10-12)
|
|
27
24
|
* found agents handing them commands, links and codes, writing paragraphs and asking open questions.
|
|
@@ -137,6 +134,11 @@ function stepText(n, s) {
|
|
|
137
134
|
...(s.note ? [` ${wrap(s.note, ' ', 96)}`] : []),
|
|
138
135
|
].join('\n');
|
|
139
136
|
}
|
|
137
|
+
/** The setup guide for this computer: whether a session and an API key are saved (no network call). */
|
|
138
|
+
export function agentSetupGuide(ctx) {
|
|
139
|
+
const profile = ctx.store.profile(ctx.apiUrl);
|
|
140
|
+
return setupGuide({ signedIn: ctx.sessionSource() !== null, email: profile.session?.user?.email ?? null, apiKey: profile.api_key !== undefined || Boolean(ctx.io.env.SHARDFLUX_API_KEY?.trim()) });
|
|
141
|
+
}
|
|
140
142
|
export function setupGuideText(g) {
|
|
141
143
|
return [
|
|
142
144
|
'Set up Shardflux for your user. How:',
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Identify an agent shell before loading onboarding or command handlers.
|
|
2
|
+
export const AGENT_SHELL_VARIABLES = ['CLAUDECODE', 'CODEX_SANDBOX', 'CODEX_THREAD_ID', 'CURSOR_AGENT', 'GEMINI_CLI'];
|
|
3
|
+
export function codingAgentShell(env) {
|
|
4
|
+
return AGENT_SHELL_VARIABLES.some((name) => (env[name]?.trim() ?? '') !== '');
|
|
5
|
+
}
|
package/dist/args.d.ts
CHANGED
package/dist/args.js
CHANGED
|
@@ -12,9 +12,10 @@
|
|
|
12
12
|
import { parseArgs } from 'node:util';
|
|
13
13
|
import { ACCOUNT_COMMANDS } from "./account-args.js";
|
|
14
14
|
import { BOARD_COMMANDS } from "./board-args.js";
|
|
15
|
+
import { ORCHESTRATOR_COMMANDS } from "./orchestrator-args.js";
|
|
15
16
|
import { UsageError } from "./errors.js";
|
|
16
17
|
import { DEFAULT_CHOICE, MODEL_CHOICES } from "./agent-models.js";
|
|
17
|
-
export const CLI_VERSION = '0.
|
|
18
|
+
export const CLI_VERSION = '0.28.1';
|
|
18
19
|
export const GLOBAL_OPTIONS = {
|
|
19
20
|
'api-url': { type: 'string', value: '<url>', description: 'API base URL (default $SHARDFLUX_API_URL, else https://api.shardflux.dev).' },
|
|
20
21
|
json: { type: 'boolean', description: 'Print machine-readable JSON on stdout (errors as JSON on stderr).' },
|
|
@@ -165,11 +166,12 @@ const KEY_COMMANDS = [
|
|
|
165
166
|
{ path: ['conversations', 'watch'], summary: 'Watch a conversation’s terminal live, read-only', positionals: [{ name: 'id', required: true }], options: {},
|
|
166
167
|
description: 'Shows the terminal the conversation’s agent runs in (its pty_id, terminal conversations) as the agent works: its last 256 KiB, then live output, until the agent’s session exits or you press Ctrl-C. Nothing you type reaches it. Watching never keeps the computer awake while the agent is idle, and ends when the computer sleeps. The output is the terminal’s own bytes, so there is no --json form; shard agents logs <id> --json returns recent output as JSON.',
|
|
167
168
|
examples: ['shard conversations watch conv_a7k2m4q6s3t5v7x2z4b6d3f5h7'] },
|
|
168
|
-
{ path: ['conversations', 'send'], summary: 'Send
|
|
169
|
-
file: { type: 'string', multiple: true, value: '<path>', description: 'Attach a file (repeatable, up to 20 of 64 MiB each): it lands in the conversation’s computer before the message, which lists its path there (0.25.0+).' }
|
|
169
|
+
{ path: ['conversations', 'send'], summary: 'Send a message to a conversation’s agent', positionals: [{ name: 'id', required: true }, { name: 'text', variadic: true, words: true }], options: {
|
|
170
|
+
file: { type: 'string', multiple: true, value: '<path>', description: 'Attach a file (repeatable, up to 20 of 64 MiB each): it lands in the conversation’s computer before the message, which lists its path there (0.25.0+).' },
|
|
171
|
+
wait: { type: 'boolean', description: 'Wait up to 60 s for the agent to have the message and print what happened to it (0.27.0+).' }
|
|
170
172
|
},
|
|
171
|
-
description: '
|
|
172
|
-
examples: ['shard conversations send conv_6qz2dmu4kd3xa7v5l2hgmfnpjy "Fix the failing export test"', 'shard conversations send conv_6qz2dmu4kd3xa7v5l2hgmfnpjy "Why does this page break?" --file screenshot.png --file console.log'] },
|
|
173
|
+
description: 'A working agent takes the message into its running turn; an idle one starts its next turn with it. --wait (0.27.0+) waits up to 60 s and prints which: "Steered into the running turn.", "Started the next turn." or "Not delivered yet."; --json adds the delivery (state, delivered_at, steered). A conversation that ended is restored into a fresh computer first. Attached files keep their names under /home/user/.shardflux/attachments/<conversation id>/; each stored path is printed.',
|
|
174
|
+
examples: ['shard conversations send conv_6qz2dmu4kd3xa7v5l2hgmfnpjy "Fix the failing export test"', 'shard conversations send conv_6qz2dmu4kd3xa7v5l2hgmfnpjy "Use the staging database instead" --wait', 'shard conversations send conv_6qz2dmu4kd3xa7v5l2hgmfnpjy "Why does this page break?" --file screenshot.png --file console.log'] },
|
|
173
175
|
{ path: ['conversations', 'wait'], summary: 'Wait for a result or question', positionals: [{ name: 'id', required: true }], options: {
|
|
174
176
|
until: { type: 'string', value: '<states>', description: 'Comma-separated conversation states.' }, timeout: { type: 'string', value: '<seconds>', description: 'Wait for 0..900 seconds (default 900).' }
|
|
175
177
|
} },
|
|
@@ -201,6 +203,15 @@ const KEY_COMMANDS = [
|
|
|
201
203
|
description: 'merge (0.25.0+) merges the conversation’s branch into the project’s default branch on the project hub (a fast-forward, or a merge commit when it is clean), brings it into your folder on this computer the way finished conversations land (your uncommitted edits stay), checks the commit is in the branch, and only then records it. pr, keep and discard record what was decided.',
|
|
202
204
|
examples: ['shard conversations land conv_k3m5q7s2u4w6y3b5d7f2h4j6ln merge', 'shard conversations land conv_k3m5q7s2u4w6y3b5d7f2h4j6ln discard'] },
|
|
203
205
|
{ path: ['conversations', 'move'], summary: 'Move at the end of the turn', positionals: [{ name: 'id', required: true }, { name: 'to', required: true }], options: {} },
|
|
206
|
+
{ path: ['conversations', 'archive'], summary: 'Archive conversations from your inbox, on every device (0.28.0+)', positionals: [{ name: 'id', required: true, variadic: true, words: true }], options: {},
|
|
207
|
+
description: 'Archives them from your own inbox, the same in the Shardflux app, the CLI and every client you use: each one carries your dismissed_at until you unarchive it. The conversations, their agents and computers carry on, and everyone else keeps their own inbox. Name up to 200 ids at once. Prints "Archived <id> <title>" for each, and "Not found: <id>" on stderr for an id you cannot read (exit 3 when none was found). --json prints {data, skipped}: the conversations with your seen_at and dismissed_at, and the ids not found.',
|
|
208
|
+
examples: ['shard c archive conv_k3m5q7s2u4w6y3b5d7f2h4j6ln', 'shard conversations archive conv_k3m5q7s2u4w6y3b5d7f2h4j6ln conv_a7k2m4q6s3t5v7x2z4b6d3f5h7 --json'] },
|
|
209
|
+
{ path: ['conversations', 'unarchive'], summary: 'Bring archived conversations back to your inbox, on every device (0.28.0+)', positionals: [{ name: 'id', required: true, variadic: true, words: true }], options: {},
|
|
210
|
+
description: 'Brings them back to your own inbox everywhere: their dismissed_at reads null again. Name up to 200 ids at once. Prints "Unarchived <id> <title>" for each, and "Not found: <id>" on stderr for an id you cannot read (exit 3 when none was found). --json prints {data, skipped}.',
|
|
211
|
+
examples: ['shard c unarchive conv_k3m5q7s2u4w6y3b5d7f2h4j6ln'] },
|
|
212
|
+
{ path: ['conversations', 'seen'], summary: 'Mark conversations seen: you opened them now, on every device (0.28.0+)', positionals: [{ name: 'id', required: true, variadic: true, words: true }], options: {},
|
|
213
|
+
description: 'Records that you opened them now, the same on every device: their seen_at becomes now, so a result that finished before then is no longer new to you anywhere. Name up to 200 ids at once. Prints "Marked seen <id> <title>" for each, and "Not found: <id>" on stderr for an id you cannot read (exit 3 when none was found). --json prints {data, skipped}.',
|
|
214
|
+
examples: ['shard c seen conv_k3m5q7s2u4w6y3b5d7f2h4j6ln conv_a7k2m4q6s3t5v7x2z4b6d3f5h7'] },
|
|
204
215
|
{ path: ['conversations', 'delete'], summary: 'Delete a conversation everywhere: its computer, messages, transcript and branch (0.25.0+)', positionals: [{ name: 'id', required: true }], options: {
|
|
205
216
|
yes: { type: 'boolean', description: 'Required: confirm the deletion.' }
|
|
206
217
|
},
|
|
@@ -877,6 +888,12 @@ const KEY_COMMANDS = [
|
|
|
877
888
|
description: `--ask (0.20.1+) asks your user for a secret file’s contents on a page on this computer; the page says Saved once the file is written. ${ASK_EVENTS}`,
|
|
878
889
|
examples: ['shard files write acme/demo /home/user/notes.txt --from notes.txt', 'printf "x = 1\\n" | shard files write acme/agent-7 /home/user/app/config.py --if-revision 4', 'shard files write "$(shard machine open)" /home/user/.npmrc --ask --json'],
|
|
879
890
|
},
|
|
891
|
+
{
|
|
892
|
+
path: ['files', 'delete'],
|
|
893
|
+
summary: 'Delete a file or empty directory in a workspace',
|
|
894
|
+
positionals: [REF, { name: 'path', required: true }],
|
|
895
|
+
options: { recursive: { type: 'boolean', description: 'Delete a directory and its contents (default false).' }, ...IF_REVISION, ...WAKE_TIMING },
|
|
896
|
+
},
|
|
880
897
|
{
|
|
881
898
|
path: ['files', 'ls'],
|
|
882
899
|
summary: 'List a directory in a workspace',
|
|
@@ -1278,6 +1295,17 @@ const AGENT_ACCOUNT_COMMANDS = [
|
|
|
1278
1295
|
examples: ['shard agents accounts use claude:2', 'shard agents accounts use marc@example.com --json'] },
|
|
1279
1296
|
{ path: ['agents', 'accounts', 'remove'], summary: 'Remove an account and its login (0.18.0+)', positionals: [{ name: 'account', required: true }], options: {} },
|
|
1280
1297
|
];
|
|
1298
|
+
/** The models each account can use (0.27.0+); not aliased under `shard models` (the model template). */
|
|
1299
|
+
const AGENT_MODELS_COMMANDS = [
|
|
1300
|
+
{ path: ['agents', 'models'], summary: 'The models each account can use, as Claude and ChatGPT list them for it: efforts, default, what the plan does not include (0.27.0+)', positionals: [], options: {
|
|
1301
|
+
account: { type: 'string', value: '<account>', description: 'This account’s models (its id, claude:<n>, codex:<n>, email or label) instead of the active accounts’.' },
|
|
1302
|
+
provider: ACCOUNT_PROVIDER,
|
|
1303
|
+
all: { type: 'boolean', description: 'Every account, not only the active one of each provider.' },
|
|
1304
|
+
refresh: { type: 'boolean', description: 'Ask the provider again (lists are kept for an hour).' },
|
|
1305
|
+
},
|
|
1306
|
+
description: 'Asked server side with each account’s own sign-in, the way Claude Code and Codex fill their model pickers; never starts a computer. Pass an id to shard agents new --model. --json prints {accounts: [...]} (one account with --account).',
|
|
1307
|
+
examples: ['shard agents models', 'shard agents models --account claude:2', 'shard agents models --provider codex --json'] },
|
|
1308
|
+
];
|
|
1281
1309
|
/** `shard accounts …`: the same commands, as the menu bar and the design name them. */
|
|
1282
1310
|
const ACCOUNT_ALIASES = AGENT_ACCOUNT_COMMANDS.map(c => ({ ...c, path: c.path.slice(1), hidden: true }));
|
|
1283
1311
|
const COMPUTER_OPTION = { type: 'boolean', description: 'Give the new conversation a desktop with a browser its agent drives (computer use).' };
|
|
@@ -1344,11 +1372,14 @@ const AGENT_COMMANDS = [
|
|
|
1344
1372
|
detach: { type: 'boolean', description: 'Start it in the cloud without opening it here.' },
|
|
1345
1373
|
}, examples: ['shard move --last', 'shard move --list', 'shard move --session 3d085e35-aa66-4866-9a53-f09719d6767c --detach --json'] },
|
|
1346
1374
|
{ path: ['device', 'requests'], summary: 'Review local device requests waiting for approval', positionals: [], options: {} },
|
|
1375
|
+
{ path: ['device', 'approvals'], summary: 'How this computer answers your agents: auto (allow everything), read (allow reading, ask before changes) or ask (0.27.0+)',
|
|
1376
|
+
description: 'Without a mode: the current one and what ran without asking. Only your own agents run without asking; a teammate\'s request always asks.',
|
|
1377
|
+
positionals: [{ name: 'mode', required: false }], options: {}, examples: ['shard device approvals auto', 'shard device approvals ask'] },
|
|
1347
1378
|
...['approve', 'deny'].map((action) => ({ path: ['device', action], summary: `${action} a local device request`, positionals: [{ name: 'id', required: true }], options: action === 'approve' ? { always: { type: 'boolean', description: 'Allow matching requests for this project, or for a folder request (0.25.0+) that folder and what is inside it; never for other access outside the conversation folder.' } } : {} })),
|
|
1348
1379
|
{ path: ['conversations', 'start'], summary: 'Start a conversation from the project or on your machine, no folder needed (0.25.0+)', positionals: [], options: {
|
|
1349
1380
|
project: { type: 'string', value: '<id>', description: 'The project (default: the API key’s, or this computer’s selected project).' },
|
|
1350
1381
|
home: { type: 'boolean', description: 'Start on your machine’s home, without a repository: the agent runs in /home/user and starts threads in the project’s repositories.' },
|
|
1351
|
-
repo: { type: 'string', value: '<remote>', description: 'The repository (default: the project’s
|
|
1382
|
+
repo: { type: 'string', value: '<remote>', description: 'The repository (default: the project’s only one; a project with several asks you to name it).' },
|
|
1352
1383
|
branch: { type: 'string', value: '<name>', description: 'Start from this branch’s newest saved point in the project (default main, then master).' },
|
|
1353
1384
|
app: { type: 'string', value: '<claude|codex>', description: 'Agent app (default claude).' },
|
|
1354
1385
|
title: { type: 'string', value: '<text>', description: 'The conversation’s title (default: named by its first message).' },
|
|
@@ -1430,9 +1461,10 @@ const MENUBAR_COMMANDS = [{ path: ['menubar', 'account'], hidden: true, summary:
|
|
|
1430
1461
|
const SETTINGS_COMMANDS = [{
|
|
1431
1462
|
path: ['settings'], summary: 'Show or change this computer’s settings for cloud conversations (0.20.1+)', positionals: [], options: {
|
|
1432
1463
|
'title-prefix': { type: 'string', value: '<text|none|default>', description: 'The marker your cloud conversations’ Claude session titles start with in claude.ai and the Claude app ("☁ Fix login bug · todo-app"): up to 16 characters on one line; none for no marker, default for ☁. New conversations use it.' },
|
|
1464
|
+
'auto-land': { type: 'string', value: '<on|off>', description: 'Bring finished cloud work (conversations and board tasks) into your folders on this computer automatically (0.27.0+, default on).' },
|
|
1433
1465
|
},
|
|
1434
|
-
description: 'Shows the title marker, where new conversations open
|
|
1435
|
-
examples: ['shard settings', 'shard settings --title-prefix SF', 'shard settings --title-prefix none', 'shard settings --title-prefix default --json'],
|
|
1466
|
+
description: 'Shows the title marker, where new conversations open, the terminal app (change those two with "shard agents targets --open-in" and "--terminal") and auto land. The menu bar and desktop apps change the same settings.',
|
|
1467
|
+
examples: ['shard settings', 'shard settings --title-prefix SF', 'shard settings --title-prefix none', 'shard settings --title-prefix default --json', 'shard settings --auto-land off'],
|
|
1436
1468
|
}];
|
|
1437
1469
|
/** The board's input bar from a terminal (0.25.0+): route a line of input, ask a quick question about the work. */
|
|
1438
1470
|
const BOARD_SCOPE = {
|
|
@@ -1443,8 +1475,9 @@ const BOARD_SCOPE_HELP = 'Reads your organization’s projects and open tasks wi
|
|
|
1443
1475
|
const ROUTER_COMMANDS = [
|
|
1444
1476
|
{
|
|
1445
1477
|
path: ['route'],
|
|
1446
|
-
summary: '
|
|
1478
|
+
summary: 'Deprecated: route input like the board’s input bar (use shard orchestrator say) (0.25.0+)',
|
|
1447
1479
|
description: [
|
|
1480
|
+
'Deprecated: shard orchestrator say <words> sends your input to your orchestrator, which routes it, answers it or files its tasks.',
|
|
1448
1481
|
'Decides which project the input belongs to (or your home, for personal work), whether it is a new task, a question or a steer for an open task, and splits input that asks for several tasks into your own words per task, each with a title. "?" or "quick question" at the start makes it a question.',
|
|
1449
1482
|
BOARD_SCOPE_HELP,
|
|
1450
1483
|
'Prints the kind and where it goes, then each part. With --json: the RouterResult (kind, parts with text, title, project_id and home, task_ref, confidence, uncertain, forced, routed_by, latency_ms).',
|
|
@@ -1458,8 +1491,9 @@ const ROUTER_COMMANDS = [
|
|
|
1458
1491
|
},
|
|
1459
1492
|
{
|
|
1460
1493
|
path: ['ask'],
|
|
1461
|
-
summary: '
|
|
1494
|
+
summary: 'Deprecated: ask a quick question about your work (use shard orchestrator say) (0.25.0+)',
|
|
1462
1495
|
description: [
|
|
1496
|
+
'Deprecated: shard orchestrator say --wait <question> asks your orchestrator and prints its answer.',
|
|
1463
1497
|
'Answers from your tasks, their plans and states, sealed reports, the conversations behind them and what their agents last wrote. The answer streams to standard output as it is written; one that needs work ends with "Make it a task?".',
|
|
1464
1498
|
BOARD_SCOPE_HELP,
|
|
1465
1499
|
'With --json: the whole answer as one object (text, needs_work, sources, answered_by, first_token_ms, total_ms).',
|
|
@@ -1518,6 +1552,26 @@ const DRIVE_COMMANDS = [
|
|
|
1518
1552
|
description: `${DRIVE_HELP} After rm the path no longer lists or reads; shard drive get <path> --sha256 <hex> still reads each of its versions.`,
|
|
1519
1553
|
examples: ['shard drive rm tasks/t_ab12/notes/draft.md'] },
|
|
1520
1554
|
];
|
|
1555
|
+
/** Whose brain a brain command reads (0.27.0+): the person's in this organization. */
|
|
1556
|
+
const BRAIN_SCOPE = {
|
|
1557
|
+
org: { type: 'string', value: '<id|slug>', description: 'Organization whose brain it is (default: the saved one, shard orgs use, else your only organization). Slugs need your signed-in session.' },
|
|
1558
|
+
};
|
|
1559
|
+
const BRAIN_HELP = 'Your brain is your archive in an organization, kept by your orchestrator: every answer it publishes is filed as reports/<YYYY-MM-DD>/<folder>/report.md with the answer’s media next to it (image thumbnails under .thumbs/), with its own notes and files and an index.md of everything, newest first. Your orchestrator writes it; you read it from any device, with every computer asleep. Paths are relative to the brain’s root, the names ~/brain shows on your orchestrator’s computer. Uses your signed-in session, or SHARDFLUX_API_KEY when it is set (a key reads the brain of the member who created it).';
|
|
1560
|
+
const BRAIN_COMMANDS = [
|
|
1561
|
+
{ path: ['brain', 'ls'], summary: 'List your brain’s files: size, saved time, type and path (0.27.0+)', positionals: [{ name: 'prefix' }], options: {
|
|
1562
|
+
...BRAIN_SCOPE,
|
|
1563
|
+
all: { type: 'boolean', description: 'Fetch every page.' },
|
|
1564
|
+
limit: { type: 'string', value: '<n>', description: 'Page size (1 to 1000, default 200).' },
|
|
1565
|
+
cursor: { type: 'string', value: '<cursor>', description: 'Continue from a previous page.' },
|
|
1566
|
+
}, description: `${BRAIN_HELP} Lists the current version of each file, by path; a prefix such as reports/2026-10-17/ lists only that folder. --json returns data (each file with its sha256), next_cursor and the brain’s version, which moves whenever anything in it changes.`,
|
|
1567
|
+
examples: ['shard brain ls', 'shard brain ls reports/2026-10-17/', 'shard brain ls --all --json'] },
|
|
1568
|
+
{ path: ['brain', 'get'], summary: 'Read a file from your brain: its bytes to standard output, or to a file with -o (0.27.0+)', positionals: [{ name: 'path', required: true }], options: {
|
|
1569
|
+
...BRAIN_SCOPE,
|
|
1570
|
+
sha256: { type: 'string', value: '<hex>', description: 'Read exactly this version (the current one or an earlier one).' },
|
|
1571
|
+
out: { type: 'string', short: 'o', value: '<file>', description: 'Write the bytes to this file.' },
|
|
1572
|
+
}, description: `${BRAIN_HELP} The bytes are checked against their SHA-256 before they are written. --json without -o returns the file as content_base64 with its sha256, size and content_type.`,
|
|
1573
|
+
examples: ['shard brain get index.md', 'shard brain get reports/2026-10-17/training-progress-ab12cd34/report.md', 'shard brain get reports/2026-10-17/training-progress-ab12cd34/loss.png -o loss.png'] },
|
|
1574
|
+
];
|
|
1521
1575
|
const MODELS_HELP = 'The model template: which agent app (harness), model and effort each role of a task runs with, from the Claude and Codex accounts your organization connected. master plans and coordinates, builder does the work, hard takes the hardest parts, light the small ones, and reviewer reviews with a fresh context. The primary provider runs master, builder and hard; the other one, light and reviewer. A role whose account waits for its next usage window shows until when, and what the other provider would run now.';
|
|
1522
1576
|
const MODEL_COMMANDS = [
|
|
1523
1577
|
{ path: ['models'], summary: 'Show which harness, model and effort each role of a task runs with (0.25.0+)', positionals: [], options: {
|
|
@@ -1532,7 +1586,7 @@ const MODEL_COMMANDS = [
|
|
|
1532
1586
|
}, description: `${MODELS_HELP} The primary is this project’s choice when it has one, else the organization’s, else the provider connected first; clear removes the choice. Prints the template with the new primary.`,
|
|
1533
1587
|
examples: ['shard models primary codex', 'shard models primary claude --org acme', 'shard models primary clear --project web'] },
|
|
1534
1588
|
];
|
|
1535
|
-
export const COMMANDS = [...ACCOUNT_COMMANDS.filter((c) => c.path[0] !== 'templates'), ...AGENT_COMMANDS, ...ROUTER_COMMANDS, ...AGENT_CREDENTIAL_COMMANDS, ...AGENT_ACCOUNT_COMMANDS, ...ACCOUNT_ALIASES, ...CLI_PROXY_COMMANDS, ...MENUBAR_COMMANDS, ...SETTINGS_COMMANDS, ...KEY_COMMANDS, ...BOARD_COMMANDS, ...DRIVE_COMMANDS, ...MODEL_COMMANDS, ...ACCOUNT_COMMANDS.filter((c) => c.path[0] === 'templates')];
|
|
1589
|
+
export const COMMANDS = [...ACCOUNT_COMMANDS.filter((c) => c.path[0] !== 'templates'), ...AGENT_COMMANDS, ...ROUTER_COMMANDS, ...AGENT_CREDENTIAL_COMMANDS, ...AGENT_ACCOUNT_COMMANDS, ...AGENT_MODELS_COMMANDS, ...ACCOUNT_ALIASES, ...CLI_PROXY_COMMANDS, ...MENUBAR_COMMANDS, ...SETTINGS_COMMANDS, ...KEY_COMMANDS, ...ORCHESTRATOR_COMMANDS, ...BOARD_COMMANDS, ...DRIVE_COMMANDS, ...BRAIN_COMMANDS, ...MODEL_COMMANDS, ...ACCOUNT_COMMANDS.filter((c) => c.path[0] === 'templates')];
|
|
1536
1590
|
const GROUPS = {
|
|
1537
1591
|
device: 'Local device approvals',
|
|
1538
1592
|
'cli-proxy': 'Your hosted CLIProxyAPI, reachable at http://127.0.0.1:8317 on this computer and in every VM',
|
|
@@ -1542,6 +1596,7 @@ const GROUPS = {
|
|
|
1542
1596
|
'needs-you': 'Everything your agents wait on, oldest first, and one answer command for each (0.25.0+)',
|
|
1543
1597
|
approvals: 'The approval mode and always-allow rules your agents work under (0.25.0+)',
|
|
1544
1598
|
drive: 'The project drive: files every agent, phone and person of the project reads and writes, each version kept by its SHA-256 (0.25.0+)',
|
|
1599
|
+
brain: 'Your brain: your orchestrator’s answers, reports and notes, read from any device (0.27.0+)',
|
|
1545
1600
|
models: 'The model template: which harness, model and effort each role of a task runs with, and the primary provider (0.25.0+)',
|
|
1546
1601
|
agents: 'Cloud agent sessions and copied credentials',
|
|
1547
1602
|
auth: 'Create an account and sign in (the session is saved in the credentials file); passwords, email, two-factor authentication and sessions',
|
|
@@ -1955,7 +2010,7 @@ export const EXIT_CODE_HELP = [
|
|
|
1955
2010
|
/** The top help's sections: every command's group (first word) is in exactly one (args.test.ts checks). */
|
|
1956
2011
|
export const HELP_SECTIONS = [
|
|
1957
2012
|
{ title: 'Account (a user session: shard signup or shard auth login)', groups: ['signup', 'verify', 'auth', 'setup', 'env', 'orgs', 'projects', 'api-keys', 'members', 'invitations', 'billing', 'audit', 'account'] },
|
|
1958
|
-
{ title: 'Cloud agents (shard ./ starts one; a project API key)', groups: ['conversations', 'board', 'task', 'needs-you', 'approvals', 'route', 'ask', 'transcribe', 'drive', 'models', 'move', 'machine', 'template', 'agents', 'settings', 'accounts', 'device', 'cli-proxy', 'ssh-proxy'] },
|
|
2013
|
+
{ title: 'Cloud agents (shard ./ starts one; a project API key)', groups: ['conversations', 'orchestrator', 'brain', 'board', 'task', 'needs-you', 'approvals', 'route', 'ask', 'transcribe', 'drive', 'models', 'move', 'machine', 'template', 'agents', 'settings', 'accounts', 'device', 'cli-proxy', 'ssh-proxy'] },
|
|
1959
2014
|
{ title: 'Workspaces for your app (a project API key: SHARDFLUX_API_KEY, else the one shard setup saved)', groups: ['login', 'whoami', 'status', 'usage', 'workspaces', 'files', 'operations', 'executions', 'secrets', 'credentials', 'volumes', 'egress'] },
|
|
1960
2015
|
{ title: 'Templates for your app’s workspaces (not for cloud agents; publish and archive use the session, the rest the API key)', groups: ['templates'] },
|
|
1961
2016
|
{ title: 'Other', groups: ['menubar', 'update', 'feedback', 'version'] },
|
package/dist/board-args.js
CHANGED
|
@@ -109,9 +109,11 @@ export const BOARD_COMMANDS = [
|
|
|
109
109
|
positionals: [],
|
|
110
110
|
options: PROJECT_FILTER,
|
|
111
111
|
description: [
|
|
112
|
-
'Everything your agents wait on, across the board: permission requests (perm_…), questions (cq_…, ask_
|
|
113
|
-
'credential requests (cred_…), finished work to land (land_…)
|
|
114
|
-
'
|
|
112
|
+
'Everything your agents wait on, across the board: permission requests (perm_…), questions (cq_…, ask_…, and your',
|
|
113
|
+
'orchestrator’s oq_…), credential requests (cred_…), finished work to land (land_…), Later tasks to keep or clear',
|
|
114
|
+
'(prune_…) and what your orchestrator asked your computer while it was offline (dev_…, "Waiting for your computer:',
|
|
115
|
+
'list ~/code"). Each line is the id, what is asked, and the command, options or branch behind it. Answer with shard',
|
|
116
|
+
'needs-you answer <id>.',
|
|
115
117
|
'--json returns {data}.',
|
|
116
118
|
].join('\n'),
|
|
117
119
|
examples: ['shard needs-you', 'shard needs-you --json'],
|
|
@@ -132,10 +134,13 @@ export const BOARD_COMMANDS = [
|
|
|
132
134
|
' project) or deny; an agent’s question takes --pick',
|
|
133
135
|
' cq_… --text <answer>: the agent gets it as its next message',
|
|
134
136
|
' ask_… --text <answer>: the task’s master gets it',
|
|
137
|
+
' oq_… --text <answer>: the orchestrator thread that asked gets it (0.27.0+)',
|
|
135
138
|
' cred_… deny (the value goes through the credential request, sealed for its computer)',
|
|
136
139
|
' land_… land (as shard task land: the result into your folder and the default branch), keep (the',
|
|
137
140
|
' branch stays) or discard',
|
|
138
141
|
' prune_… --keep <task-id> for each task to keep, keep for all of them, or discard to clear them',
|
|
142
|
+
' dev_… cancel: drops what your orchestrator asked your computer (it runs by itself when your computer is',
|
|
143
|
+
' back) (0.27.0+)',
|
|
139
144
|
'The agent goes on as soon as you answer.',
|
|
140
145
|
].join('\n'),
|
|
141
146
|
examples: ['shard needs-you answer perm_01a1300c-0000-7000-8000-000000000001 always', 'shard needs-you answer ask_rep_66cbevz65m72ij7mbiyrw4xvdy --text "eu-north-1"', 'shard needs-you answer land_conv_id4zts6jxsqraju2pggr55n3tq land'],
|
package/dist/board-commands.d.ts
CHANGED
|
@@ -14,7 +14,10 @@ export declare function taskLabel(t: Pick<Task, 'title' | 'text'>, max?: number)
|
|
|
14
14
|
* what it does now, "working (Fixing the login cookie)"), title or words (a follow-up ↳, a suggestion marked), project.
|
|
15
15
|
*/
|
|
16
16
|
export declare function taskLine(t: Task): string;
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* One item that waits on you: its id, what is asked, then the command, the suggested answers or the branch behind it. A
|
|
19
|
+
* request your orchestrator made to your computer while it was offline reads "Waiting for your computer: list ~/code".
|
|
20
|
+
*/
|
|
18
21
|
export declare function needsLine(n: NeedsYouItem): string;
|
|
19
22
|
/** A sealed report as --follow prints it: id, milestone, title, then its evidence labels. */
|
|
20
23
|
export declare function reportLine(r: TaskReport): string;
|
|
@@ -38,9 +41,10 @@ export declare class BoardFollower {
|
|
|
38
41
|
}
|
|
39
42
|
/** A feed answer as `board --follow --json` prints it: the API's fields (with `board` after a reset). */
|
|
40
43
|
export declare function feedJson(u: BoardUpdate): Record<string, unknown>;
|
|
41
|
-
/** `shard task show`: the task, its plan and conversations, then every report oldest first with its evidence.
|
|
44
|
+
/** `shard task show`: the task, its plan and conversations, then every report oldest first with its evidence. Every
|
|
45
|
+
* title, step and outcome prints whole (0.27.0+), however long the agent wrote it; the terminal wraps it. */
|
|
42
46
|
export declare function taskDetailText(d: TaskDetail): string;
|
|
43
|
-
/** An item's kind from its id (perm_, cq_, ask_, cred_, land_, prune_); undefined for another id. */
|
|
47
|
+
/** An item's kind from its id (perm_, cq_, ask_, oq_, cred_, land_, prune_, dev_); undefined for another id. */
|
|
44
48
|
export declare function itemKind(id: string): NeedsYouItem['kind'] | undefined;
|
|
45
49
|
export interface AnswerInput {
|
|
46
50
|
word?: string | undefined;
|
package/dist/board-commands.js
CHANGED
|
@@ -31,9 +31,12 @@ export function taskLine(t) {
|
|
|
31
31
|
const label = `${t.parent_task_id ? '↳ ' : ''}${taskLabel(t)}${t.suggested ? ' (suggested)' : ''}`;
|
|
32
32
|
return `${t.id}\t${state}\t${label}\t${t.project_id}${t.home ? ' (home)' : ''}`;
|
|
33
33
|
}
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* One item that waits on you: its id, what is asked, then the command, the suggested answers or the branch behind it. A
|
|
36
|
+
* request your orchestrator made to your computer while it was offline reads "Waiting for your computer: list ~/code".
|
|
37
|
+
*/
|
|
35
38
|
export function needsLine(n) {
|
|
36
|
-
const detail = n.permission ? detailLine(n.permission.detail) : n.options?.length ? n.options.join(' / ') : n.land ? n.land.branch : '';
|
|
39
|
+
const detail = n.permission ? detailLine(n.permission.detail) : n.options?.length ? n.options.join(' / ') : n.land ? n.land.branch : n.device ? 'cancel drops it' : '';
|
|
37
40
|
return `${n.id}\t${oneLine(n.ask, 100)}${detail ? `\t${detail}` : ''}`;
|
|
38
41
|
}
|
|
39
42
|
/** A sealed report as --follow prints it: id, milestone, title, then its evidence labels. */
|
|
@@ -112,11 +115,13 @@ const evidenceText = (e) => {
|
|
|
112
115
|
const behind = e.detail ?? e.path;
|
|
113
116
|
return behind ? `${e.label} (${detailLine(behind, 60)})` : e.label;
|
|
114
117
|
};
|
|
115
|
-
/** `shard task show`: the task, its plan and conversations, then every report oldest first with its evidence.
|
|
118
|
+
/** `shard task show`: the task, its plan and conversations, then every report oldest first with its evidence. Every
|
|
119
|
+
* title, step and outcome prints whole (0.27.0+), however long the agent wrote it; the terminal wraps it. */
|
|
116
120
|
export function taskDetailText(d) {
|
|
117
121
|
const t = d.task;
|
|
122
|
+
const whole = (s) => oneLine(s, Infinity);
|
|
118
123
|
const titled = Boolean(t.title?.trim());
|
|
119
|
-
const lines = [titled ?
|
|
124
|
+
const lines = [titled ? whole(t.title) : whole(t.text.split('\n').find((l) => l.trim()) ?? t.text)];
|
|
120
125
|
lines.push([`${t.state}${stateNote(t, 120)}`, t.bucket, `${t.project_id}${t.home ? ' (home)' : ''}`, ...(t.suggested ? ['suggested'] : []), ...(t.deleted_at ? ['deleted'] : t.cleared_at ? ['cleared'] : [])].join('\t'));
|
|
121
126
|
// Your words exactly, when the first line does not already show them whole.
|
|
122
127
|
if (titled || t.text.trim() !== lines[0])
|
|
@@ -124,16 +129,16 @@ export function taskDetailText(d) {
|
|
|
124
129
|
if (t.plan) {
|
|
125
130
|
lines.push('', `Plan: ${t.plan.summary}`);
|
|
126
131
|
for (const s of t.plan.steps)
|
|
127
|
-
lines.push(` ${s.id}\t${s.state}\t${
|
|
132
|
+
lines.push(` ${s.id}\t${s.state}\t${whole(s.text)}${s.now?.trim() ? `\t(${oneLine(s.now, 80)})` : ''}${s.evidence.length ? `\t${s.evidence.join(', ')}` : ''}`);
|
|
128
133
|
}
|
|
129
134
|
if (t.conversations.length)
|
|
130
135
|
lines.push('', `Conversations: ${t.conversations.map((c) => `${c.id} (${c.role}${c.step_id ? ` ${c.step_id}` : ''})`).join(', ')}`);
|
|
131
136
|
if (d.reports.length) {
|
|
132
137
|
lines.push('', 'Reports');
|
|
133
138
|
for (const r of d.reports) {
|
|
134
|
-
lines.push(`${r.id}\t${r.milestone}\t${time(r.sealed_at)}\t${
|
|
139
|
+
lines.push(`${r.id}\t${r.milestone}\t${time(r.sealed_at)}\t${whole(r.title)}`);
|
|
135
140
|
if (r.outcome.trim())
|
|
136
|
-
lines.push(` ${
|
|
141
|
+
lines.push(` ${whole(r.outcome)}`);
|
|
137
142
|
if (r.options?.length)
|
|
138
143
|
lines.push(` Options: ${r.options.join(' / ')}`);
|
|
139
144
|
if (r.evidence.length)
|
|
@@ -161,13 +166,13 @@ function orderOf(values) {
|
|
|
161
166
|
return first ? null : after;
|
|
162
167
|
}
|
|
163
168
|
// ---- needs you -------------------------------------------------------------------------------
|
|
164
|
-
const KINDS = { perm: 'permission', cq: 'question', ask: 'question', cred: 'credential', land: 'land', prune: 'prune' };
|
|
165
|
-
/** An item's kind from its id (perm_, cq_, ask_, cred_, land_, prune_); undefined for another id. */
|
|
169
|
+
const KINDS = { perm: 'permission', cq: 'question', ask: 'question', oq: 'question', cred: 'credential', land: 'land', prune: 'prune', dev: 'device' };
|
|
170
|
+
/** An item's kind from its id (perm_, cq_, ask_, oq_, cred_, land_, prune_, dev_); undefined for another id. */
|
|
166
171
|
export function itemKind(id) {
|
|
167
172
|
return KINDS[id.slice(0, Math.max(0, id.indexOf('_')))];
|
|
168
173
|
}
|
|
169
|
-
/** The answer words: allow, always and deny (or the API's names), land, keep and
|
|
170
|
-
const WORDS = { allow: 'allow_once', allow_once: 'allow_once', always: 'allow_always', allow_always: 'allow_always', deny: 'deny', land: 'land', keep: 'keep', discard: 'discard' };
|
|
174
|
+
/** The answer words: allow, always and deny (or the API's names), land, keep, discard and cancel. */
|
|
175
|
+
const WORDS = { allow: 'allow_once', allow_once: 'allow_once', always: 'allow_always', allow_always: 'allow_always', deny: 'deny', land: 'land', keep: 'keep', discard: 'discard', cancel: 'cancel' };
|
|
171
176
|
/** Whether the answer needs the item itself: the questions of a permission request, every task of a prune prompt. */
|
|
172
177
|
export const answerNeedsItem = (id, a) => a.picks.length > 0 || (itemKind(id) === 'prune' && a.word?.toLowerCase() === 'keep');
|
|
173
178
|
/**
|
|
@@ -214,6 +219,11 @@ export function needsAnswer(id, a, item) {
|
|
|
214
219
|
if (decision !== 'deny')
|
|
215
220
|
throw new UsageError('Provide the value through the credential request, sealed for its computer; deny declines it here.');
|
|
216
221
|
return { decision };
|
|
222
|
+
case 'device':
|
|
223
|
+
only([], 'It runs when your computer is back (the Shardflux app open); cancel drops it.');
|
|
224
|
+
if (decision !== 'cancel')
|
|
225
|
+
throw new UsageError('It runs when your computer is back (the Shardflux app open); cancel drops it.');
|
|
226
|
+
return { decision };
|
|
217
227
|
case 'land':
|
|
218
228
|
only([], 'Choose land, keep or discard.');
|
|
219
229
|
if (decision !== 'land' && decision !== 'keep' && decision !== 'discard')
|
|
@@ -248,7 +258,7 @@ export function needsAnswer(id, a, item) {
|
|
|
248
258
|
}
|
|
249
259
|
}
|
|
250
260
|
}
|
|
251
|
-
const ANSWERED = { allow_once: 'Allowed once', allow_always: 'Always allowed', deny: 'Denied', land: 'Landing', keep: 'Kept', discard: 'Discarded' };
|
|
261
|
+
const ANSWERED = { allow_once: 'Allowed once', allow_always: 'Always allowed', deny: 'Denied', land: 'Landing', keep: 'Kept', discard: 'Discarded', cancel: 'Canceled' };
|
|
252
262
|
/** What an answer did, in one line. */
|
|
253
263
|
export function answeredText(r, a) {
|
|
254
264
|
const item = r.item;
|