@shardflux/cli 0.19.1 → 0.22.0

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 (115) hide show
  1. package/CHANGELOG.md +278 -1
  2. package/README.md +225 -73
  3. package/dist/account-args.js +7 -2
  4. package/dist/account-commands.js +56 -106
  5. package/dist/account-pool.d.ts +20 -15
  6. package/dist/account-pool.js +51 -97
  7. package/dist/account-scope.d.ts +65 -0
  8. package/dist/account-scope.js +246 -0
  9. package/dist/acp-login.d.ts +4 -3
  10. package/dist/acp-login.js +7 -5
  11. package/dist/acp-models.js +20 -15
  12. package/dist/acp.js +66 -13
  13. package/dist/agent-commands.d.ts +4 -1
  14. package/dist/agent-commands.js +86 -32
  15. package/dist/agent-config-files.d.ts +19 -0
  16. package/dist/agent-config-files.js +70 -1
  17. package/dist/agent-config-return.d.ts +194 -0
  18. package/dist/agent-config-return.js +724 -0
  19. package/dist/agent-config-sync.d.ts +10 -2
  20. package/dist/agent-config-sync.js +45 -51
  21. package/dist/agent-credentials.js +7 -3
  22. package/dist/agent-repository.d.ts +16 -3
  23. package/dist/agent-repository.js +149 -74
  24. package/dist/agent-setup.d.ts +14 -3
  25. package/dist/agent-setup.js +78 -36
  26. package/dist/agent-target-commands.js +24 -13
  27. package/dist/args.d.ts +1 -1
  28. package/dist/args.js +104 -55
  29. package/dist/bin.js +5 -1
  30. package/dist/browser.d.ts +13 -2
  31. package/dist/browser.js +30 -14
  32. package/dist/claude-cloud-login.d.ts +24 -2
  33. package/dist/claude-cloud-login.js +151 -11
  34. package/dist/cli-proxy-forward.d.ts +2 -0
  35. package/dist/cli-proxy-forward.js +9 -2
  36. package/dist/cli-proxy-hub.d.ts +69 -25
  37. package/dist/cli-proxy-hub.js +231 -54
  38. package/dist/cli-proxy.d.ts +70 -1
  39. package/dist/cli-proxy.js +298 -32
  40. package/dist/client-settings.d.ts +39 -0
  41. package/dist/client-settings.js +91 -0
  42. package/dist/codex-cloud.d.ts +5 -0
  43. package/dist/codex-cloud.js +88 -17
  44. package/dist/command-center-actions.js +5 -1
  45. package/dist/commands.js +171 -22
  46. package/dist/conversation-commands.d.ts +5 -0
  47. package/dist/conversation-commands.js +8 -5
  48. package/dist/conversation-terminal.d.ts +10 -2
  49. package/dist/conversation-terminal.js +81 -15
  50. package/dist/conversations.d.ts +77 -1
  51. package/dist/conversations.js +257 -23
  52. package/dist/device-bridge.d.ts +1 -1
  53. package/dist/device-bridge.js +6 -4
  54. package/dist/device-helper.js +52 -4
  55. package/dist/errors.d.ts +34 -1
  56. package/dist/errors.js +56 -5
  57. package/dist/filesystem-commands.d.ts +22 -3
  58. package/dist/filesystem-commands.js +132 -16
  59. package/dist/format.d.ts +2 -1
  60. package/dist/format.js +10 -1
  61. package/dist/handlers.js +4 -1
  62. package/dist/initial-prompt.d.ts +8 -0
  63. package/dist/initial-prompt.js +43 -1
  64. package/dist/large-media.d.ts +14 -0
  65. package/dist/large-media.js +100 -0
  66. package/dist/linux-terminal.d.ts +5 -3
  67. package/dist/linux-terminal.js +6 -20
  68. package/dist/local-sessions.d.ts +26 -2
  69. package/dist/local-sessions.js +113 -24
  70. package/dist/machine-setup.d.ts +190 -0
  71. package/dist/machine-setup.js +849 -0
  72. package/dist/machine-status.d.ts +67 -0
  73. package/dist/machine-status.js +140 -0
  74. package/dist/main.js +23 -13
  75. package/dist/move-worktrees-restore.py +144 -0
  76. package/dist/move-worktrees.d.ts +63 -0
  77. package/dist/move-worktrees.js +201 -0
  78. package/dist/move.d.ts +10 -1
  79. package/dist/move.js +116 -16
  80. package/dist/onboarding.d.ts +11 -7
  81. package/dist/onboarding.js +59 -27
  82. package/dist/prepared-repository.d.ts +202 -0
  83. package/dist/prepared-repository.js +860 -0
  84. package/dist/prepared-repository.py +1306 -0
  85. package/dist/project-preparation.d.ts +48 -0
  86. package/dist/project-preparation.js +304 -0
  87. package/dist/project-sync-manifest.d.ts +4 -0
  88. package/dist/project-sync-manifest.js +5 -1
  89. package/dist/project-sync-transfer.js +1 -1
  90. package/dist/project-sync.d.ts +19 -4
  91. package/dist/project-sync.js +31 -8
  92. package/dist/project-template-setup.d.ts +33 -1
  93. package/dist/project-template-setup.js +59 -2
  94. package/dist/secret-ask.d.ts +72 -0
  95. package/dist/secret-ask.js +351 -0
  96. package/dist/seed-phases.d.ts +13 -3
  97. package/dist/seed-phases.js +21 -3
  98. package/dist/setup-cache.d.ts +7 -0
  99. package/dist/setup-cache.js +32 -0
  100. package/dist/t3-launch.js +1 -4
  101. package/dist/t3-mode.d.ts +8 -2
  102. package/dist/t3-mode.js +19 -4
  103. package/dist/t3-phone.d.ts +4 -2
  104. package/dist/t3-phone.js +12 -4
  105. package/dist/terminal-apps.d.ts +157 -0
  106. package/dist/terminal-apps.js +561 -0
  107. package/dist/terminal-onboarding.d.ts +2 -0
  108. package/dist/terminal-onboarding.js +11 -1
  109. package/dist/this-computer.d.ts +11 -0
  110. package/dist/this-computer.js +36 -0
  111. package/dist/thread-on-computer.js +4 -1
  112. package/dist/update.js +11 -3
  113. package/dist/worktree-twin.d.ts +10 -2
  114. package/dist/worktree-twin.js +27 -9
  115. package/package.json +5 -5
@@ -53,14 +53,22 @@ export declare function applyConfigFiles(cell: CellClient, home: string, files:
53
53
  copied: string[];
54
54
  removed: string[];
55
55
  }>;
56
- export declare function syncAgentConfig(ctx: Ctx, ws: Workspace): Promise<ConfigSyncResult>;
56
+ /**
57
+ * `memoryOnly` (0.20.0+): just the project memory, for an account whose saved machine carries the rest. A conversation
58
+ * VM gets this computer's memory for its project, under the VM project folder's name (Claude keys memory by path).
59
+ */
60
+ export declare function syncAgentConfig(ctx: Ctx, ws: Workspace, opts?: {
61
+ memoryOnly?: boolean;
62
+ }): Promise<ConfigSyncResult>;
57
63
  /**
58
64
  * Copies the laptop's agent config into a new conversation's VM after its agent started (0.19.0+): the first prompt
59
65
  * never waits for it. From an installed shard it runs as its own detached process (`shard agents config-sync`, its
60
66
  * output in agent-config-sync.log of the config directory), so the command that started the conversation reports the
61
67
  * start at once; without a runtime (tests, an embedding) it runs here and the returned promise settles when it ends.
62
68
  */
63
- export declare function syncAgentConfigAfterStart(ctx: Ctx, ws: Workspace): Promise<void> | undefined;
69
+ export declare function syncAgentConfigAfterStart(ctx: Ctx, ws: Workspace, opts?: {
70
+ memoryOnly?: boolean;
71
+ }): Promise<void> | undefined;
64
72
  /** Tunnels are owned by the attachment, and close even if the terminal fails or the user detaches. */
65
73
  export declare function startAgentConfigTunnels(ctx: Ctx, ws: Workspace): Promise<{
66
74
  stop(): Promise<void>;
@@ -7,7 +7,8 @@ import { parse, stringify } from 'smol-toml';
7
7
  import { ShardfluxApiError, tarEnd, tarHeader, tarPadding } from '@shardflux/sdk';
8
8
  import { AgentStateStore } from "./agent-state.js";
9
9
  import { UsageError } from "./errors.js";
10
- import { collectPlugins, collectUserFiles, object, readObject, readOptional, rewritePaths } from "./agent-config-files.js";
10
+ import { collectMemory, collectPlugins, collectUserFiles, object, readObject, readOptional, rewritePaths, vmMemoryPath } from "./agent-config-files.js";
11
+ import { localConversations } from "./conversations.js";
11
12
  import { digest, prepareMcp } from "./agent-config-mcp.js";
12
13
  import { configIssueLines } from "./agent-config-report.js";
13
14
  const missing = (e) => e instanceof ShardfluxApiError && e.status === 404;
@@ -15,7 +16,8 @@ const parseFailure = (e) => e instanceof SyntaxError || (e instanceof Error && /
15
16
  const markerPath = '.config/shardflux/agent-config.json';
16
17
  const safePath = (path) => !path.split('/').some(p => !p || p === '.' || p === '..') &&
17
18
  (/^(?:\.claude\/(?:settings\.json|CLAUDE\.md)|\.codex\/(?:config\.toml|AGENTS\.md))$/.test(path) ||
18
- /^\.claude\/(?:skills|commands|agents|output-styles|plugins)\/.+/.test(path) || /^\.codex\/(?:prompts|skills)\/.+/.test(path));
19
+ /^\.claude\/(?:skills|commands|agents|output-styles|plugins)\/.+/.test(path) || /^\.codex\/(?:prompts|skills)\/.+/.test(path) ||
20
+ /^\.claude\/projects\/[^/]+\/memory\/.+/.test(path));
19
21
  const key = (ctx, id) => `${ctx.apiUrl}:${id}`;
20
22
  export function configManifests(ctx) {
21
23
  const value = object(new AgentStateStore(ctx.store.dir).read().agent_config);
@@ -285,33 +287,51 @@ async function installPlugins(cell, plan, issues, signal) {
285
287
  if (line.startsWith('failed '))
286
288
  issues.push({ item: `plugin:${line.slice(7)}`, reason: 'could not be installed in the VM' });
287
289
  }
288
- export async function syncAgentConfig(ctx, ws) {
290
+ /**
291
+ * `memoryOnly` (0.20.0+): just the project memory, for an account whose saved machine carries the rest. A conversation
292
+ * VM gets this computer's memory for its project, under the VM project folder's name (Claude keys memory by path).
293
+ */
294
+ export async function syncAgentConfig(ctx, ws, opts = {}) {
289
295
  const home = ctx.io.env.HOME || homedir();
290
296
  const store = new AgentStateStore(ctx.store.dir);
291
297
  const previous = configManifests(ctx)[key(ctx, ws.id)];
292
298
  const issues = [];
293
- const files = await collectUserFiles(home, issues);
294
- const codexBytes = await readOptional(join(home, '.codex/config.toml'));
299
+ const files = opts.memoryOnly ? new Map() : await collectUserFiles(home, issues);
300
+ const codexBytes = opts.memoryOnly ? undefined : await readOptional(join(home, '.codex/config.toml'));
295
301
  // One file that does not parse is skipped with a reason; it never stops the rest of the sync.
296
302
  let claude;
297
- try {
298
- claude = await readObject(join(home, '.claude.json'));
299
- }
300
- catch (error) {
301
- if (!parseFailure(error))
302
- throw error;
303
- issues.push({ item: '.claude.json', reason: 'not valid JSON; its MCP servers were not copied' });
303
+ if (!opts.memoryOnly) {
304
+ try {
305
+ claude = await readObject(join(home, '.claude.json'));
306
+ }
307
+ catch (error) {
308
+ if (!parseFailure(error))
309
+ throw error;
310
+ issues.push({ item: '.claude.json', reason: 'not valid JSON; its MCP servers were not copied' });
311
+ }
304
312
  }
305
- const plugins = await collectPlugins(home, issues);
313
+ const plugins = opts.memoryOnly ? { marketplaces: [], plugins: [] } : await collectPlugins(home, issues);
306
314
  const hasPlugins = plugins.marketplaces.length > 0 || plugins.plugins.length > 0;
315
+ const records = Object.values(localConversations(ctx));
316
+ const conversation = records.find(r => r.conversation.id === ws.labels.shard_conversation) ?? records.find(r => r.conversation.workspace_id === ws.id);
317
+ const memory = conversation ? await collectMemory(home, conversation.folder, issues) : undefined;
307
318
  const empty = { workspace_id: ws.id, workspace_key: ws.key, copied: [], removed: [], skipped: issues, mcp: [], synced_at: null };
308
- if (!files.size && !codexBytes && !hasPlugins && !Object.keys(object(claude?.mcpServers)).length && !previous)
319
+ if (!files.size && !memory?.files.size && !codexBytes && !hasPlugins && !Object.keys(object(claude?.mcpServers)).length && !previous)
309
320
  return empty;
310
321
  const cell = ws.cell({ agentLabel: ctx.agentLabel, transitionTimeoutMs: ctx.wakeTimeoutMs });
311
- const run = await cell.exec.run(['sh', '-c', 'printf "%s\\n" "$HOME"'], { signal: ctx.signal, timeoutMs: 5000, maxOutputBytes: 4096 });
312
- const remoteHome = run.stdout.trim();
322
+ // The VM home, and the project folder its Claude keys memory by (the main checkout of the conversation's worktree).
323
+ const run = await cell.exec.run(['sh', '-c', 'printf "%s\\n" "$HOME"; [ -n "$1" ] && git -C "$1" rev-parse --path-format=absolute --git-common-dir 2>/dev/null || true', 'shard-config', conversation?.conversation.worktree_path ?? ''], { signal: ctx.signal, timeoutMs: 5000, maxOutputBytes: 4096 });
324
+ const [remoteHome = '', common = ''] = run.stdout.trim().split('\n').map(line => line.trim());
313
325
  if (run.exitCode !== 0 || !/^\/(?:[a-zA-Z0-9._-]+\/)*[a-zA-Z0-9._-]+$/.test(remoteHome) || remoteHome.split('/').some(p => p === '..' || p === '.'))
314
326
  throw new Error('agent config could not resolve the VM user home');
327
+ if (memory && conversation) {
328
+ const vmRoot = common.endsWith('/.git') ? common.slice(0, -5) : conversation.conversation.worktree_path;
329
+ for (const [path, file] of memory.files) {
330
+ const dest = vmMemoryPath(path, memory.root, vmRoot);
331
+ if (dest)
332
+ files.set(dest, file);
333
+ }
334
+ }
315
335
  // Claude settings stay the user's. sf-agent hooks are in /etc/claude-code/managed-settings.json.
316
336
  const secrets = { ...previous?.secrets, ...object(object(store.read().agent_config_secrets)[key(ctx, ws.id)]) };
317
337
  const commandCache = new Map();
@@ -432,8 +452,10 @@ export async function syncAgentConfig(ctx, ws) {
432
452
  sync_state: 'synced', synced_at, files: applied.files, claude_mcp: Object.keys(claudeMcp), claude_mcp_owned: partial.claude_mcp_owned, secrets, skipped: issues, mcp, tunnels,
433
453
  ...(pluginsHash ? { plugins_hash: pluginsHash } : {}) };
434
454
  manifest.remote_hash = digest(JSON.stringify({ files: applied.files, claude_mcp: claudeMcp, mcp, tunnels }));
435
- if (marker?.workspace_id !== ws.id || marker.manifest_hash !== manifest.remote_hash)
436
- await cell.files.write(`${remoteHome}/${markerPath}`, JSON.stringify({ workspace_id: ws.id, manifest_hash: manifest.remote_hash }) + '\n', { createParents: true, mode: '0600' });
455
+ // The marker names each file's hash (0.20.0+): the guest tells this copy's writes from the agent's own changes.
456
+ if (marker?.workspace_id !== ws.id || marker.manifest_hash !== manifest.remote_hash || !marker.files)
457
+ await cell.files.write(`${remoteHome}/${markerPath}`, JSON.stringify({ workspace_id: ws.id, manifest_hash: manifest.remote_hash,
458
+ files: Object.fromEntries(Object.entries(applied.files).map(([path, file]) => [path, file.hash])) }) + '\n', { createParents: true, mode: '0600' });
437
459
  store.update(doc => { const all = object(doc.agent_config); all[key(ctx, ws.id)] = manifest; doc.agent_config = all; });
438
460
  const result = { ...empty, copied: applied.copied, removed: applied.removed, skipped: issues, mcp, synced_at };
439
461
  if (!ctx.json) {
@@ -451,13 +473,13 @@ export async function syncAgentConfig(ctx, ws) {
451
473
  * output in agent-config-sync.log of the config directory), so the command that started the conversation reports the
452
474
  * start at once; without a runtime (tests, an embedding) it runs here and the returned promise settles when it ends.
453
475
  */
454
- export function syncAgentConfigAfterStart(ctx, ws) {
476
+ export function syncAgentConfigAfterStart(ctx, ws, opts = {}) {
455
477
  if (ctx.runtime?.script) {
456
478
  try {
457
479
  mkdirSync(ctx.store.dir, { recursive: true, mode: 0o700 });
458
480
  const log = openSync(join(ctx.store.dir, 'agent-config-sync.log'), 'a', 0o600);
459
481
  try {
460
- const child = spawn(ctx.runtime.node, [ctx.runtime.script, 'agents', 'config-sync', ws.id], {
482
+ const child = spawn(ctx.runtime.node, [ctx.runtime.script, 'agents', 'config-sync', ws.id, ...(opts.memoryOnly ? ['--memory-only'] : [])], {
461
483
  detached: true, stdio: ['ignore', log, log],
462
484
  env: { ...process.env, ...ctx.io.env, SHARDFLUX_CONFIG_DIR: ctx.store.dir, SHARDFLUX_API_URL: ctx.apiUrl, SHARDFLUX_NO_UPDATE_CHECK: '1' }
463
485
  });
@@ -471,7 +493,7 @@ export function syncAgentConfigAfterStart(ctx, ws) {
471
493
  }
472
494
  catch { /* runs in this process below */ }
473
495
  }
474
- return syncAgentConfig(ctx, ws).then(() => undefined, (error) => {
496
+ return syncAgentConfig(ctx, ws, opts).then(() => undefined, (error) => {
475
497
  ctx.io.stderr.write(`Agent config not copied to ${ws.key}: ${error instanceof Error ? error.message : String(error)}\n`);
476
498
  });
477
499
  }
@@ -501,41 +523,13 @@ export async function startAgentConfigTunnels(ctx, ws) {
501
523
  }
502
524
  export const AGENT_CONFIG_HANDLERS = {
503
525
  /** The detached copy syncAgentConfigAfterStart starts; one line per run in agent-config-sync.log. */
504
- async 'agents config-sync'(ctx, _values, [id]) {
526
+ async 'agents config-sync'(ctx, values, [id]) {
505
527
  const ws = await ctx.cloud().workspaces.get(id);
506
528
  const started = Date.now();
507
- const result = await syncAgentConfig({ ...ctx, json: true }, ws);
529
+ const result = await syncAgentConfig({ ...ctx, json: true }, ws, { memoryOnly: values['memory-only'] === true });
508
530
  ctx.io.stderr.write(`${new Date().toISOString()} ${ws.key}: ${result.copied.length} copied, ${result.removed.length} removed, ${result.skipped.length} skipped in ${Date.now() - started} ms\n`);
509
531
  for (const line of configIssueLines(result.skipped))
510
532
  ctx.io.stderr.write(` ${line}\n`);
511
533
  return 0;
512
534
  },
513
- 'agents config status'(ctx, values) {
514
- const workspace = typeof values.workspace === 'string' ? values.workspace : undefined;
515
- const manifests = Object.values(configManifests(ctx)).filter(m => m.api_url === ctx.apiUrl && (!workspace || m.workspace_id === workspace || m.workspace_key === workspace));
516
- const report = { observation: 'last sync on this machine', workspaces: manifests };
517
- ctx.io.stdout.write(ctx.json ? JSON.stringify(report, null, 2) + '\n' : manifests.length ? manifests.map(m => `${m.workspace_key}: ${Object.keys(m.files).length} files; ${m.sync_state === 'synced' ? `last sync ${m.synced_at}` : 'sync interrupted; run config sync again'}\n` +
518
- m.skipped.map(s => ` skipped ${s.item}: ${s.reason}\n`).join('') +
519
- m.mcp.map(s => ` MCP ${s.app}/${s.name}: ${s.status}${s.reason ? ` (${s.reason})` : ''}\n`).join('')).join('') : 'No agent config has been synced on this machine.\n');
520
- return Promise.resolve(0);
521
- },
522
- async 'agents config sync'(ctx, values) {
523
- const workspace = typeof values.workspace === 'string' ? values.workspace : undefined;
524
- const cloud = ctx.cloud();
525
- const targets = [];
526
- if (workspace) {
527
- const ws = await cloud.workspaces.findByKey(workspace, { includeDeleted: false, signal: ctx.signal });
528
- targets.push(ws ?? await cloud.workspaces.get(workspace));
529
- }
530
- else
531
- for await (const ws of cloud.workspaces.listAll()) {
532
- if (ws.labels.shard_project && ws.mode === 'processful')
533
- targets.push(ws);
534
- }
535
- const results = [];
536
- for (const ws of targets)
537
- results.push(await syncAgentConfig(ctx, ws));
538
- ctx.io.stdout.write(ctx.json ? JSON.stringify({ workspaces: results }, null, 2) + '\n' : results.length ? results.map(r => r.synced_at === null ? `${r.workspace_key}: no local agent config found\n` : `${r.workspace_key}: ${r.copied.length} files copied, ${r.removed.length} removed\n`).join('') : 'No agent workspaces found.\n');
539
- return 0;
540
- },
541
535
  };
@@ -3,7 +3,8 @@ import { homedir } from 'node:os';
3
3
  import { join } from 'node:path';
4
4
  import { json } from "./format.js";
5
5
  import { UsageError } from "./errors.js";
6
- import { loginClaudeInWorkspace, requireClaudeTerminal, cloudClaudeLogin } from "./claude-cloud-login.js";
6
+ import { claudeTerminal, cloudClaudeLogin, loginClaudeInWorkspace } from "./claude-cloud-login.js";
7
+ import { askTimeout } from "./secret-ask.js";
7
8
  import { resolveWorkspace } from "./commands.js";
8
9
  import { projectIdentity, projectLabels } from "./project-identity.js";
9
10
  async function optionalFile(file) {
@@ -74,7 +75,10 @@ export const AGENT_CREDENTIAL_HANDLERS = {
74
75
  output(ctx, existing);
75
76
  return 0;
76
77
  }
77
- requireClaudeTerminal(ctx);
78
+ // In a coding agent's shell, with --json, without a terminal or with --ask (0.20.1+): the claude.ai sign-in and a
79
+ // page for its code open in the browser. On a terminal the sign-in attaches to it.
80
+ const headless = values.ask === true || !claudeTerminal(ctx);
81
+ const timeoutMs = askTimeout(values);
78
82
  const project = projectIdentity('./');
79
83
  let ws;
80
84
  if (typeof values.workspace === 'string')
@@ -85,7 +89,7 @@ export const AGENT_CREDENTIAL_HANDLERS = {
85
89
  const current = await cloud.workspaces.findByKey(key, { includeDeleted: false, signal: ctx.signal });
86
90
  ws = await cloud.workspaces.open({ key, template: 'coding', labels: { ...current?.labels, ...projectLabels(project, 'main', 'claude') }, wait: { signal: ctx.signal, timeoutMs: ctx.wakeTimeoutMs } });
87
91
  }
88
- output(ctx, await loginClaudeInWorkspace(ctx, ws));
92
+ output(ctx, await loginClaudeInWorkspace(ctx, ws, headless ? { headless: { timeoutMs } } : {}));
89
93
  return 0;
90
94
  },
91
95
  'agents credentials sync': async (ctx) => { output(ctx, await syncAgentCredentials(ctx.cloud(), { home: ctx.io.env.HOME ?? ctx.io.env.USERPROFILE, redact: (value) => ctx.addSecret(value) })); return 0; },
@@ -22,12 +22,21 @@ export declare function seedAgentRepository(ctx: Ctx, ws: Workspace, folder: str
22
22
  populate?: boolean;
23
23
  initialFiles?: readonly string[];
24
24
  onInitialFiles?: () => void;
25
+ changes?: Promise<LocalChanges>;
26
+ background?: (started: Promise<unknown>) => void;
27
+ beforeHistory?: Promise<unknown>;
25
28
  }): Promise<void>;
26
29
  /**
27
30
  * Above this (an estimate: the repository's packed and loose objects) the workspace fetches the history from origin as a
28
31
  * blobless partial clone, and receives only the commits origin lacks (0.19.0+). Below it, one small bundle is faster.
29
32
  */
30
33
  export declare const ORIGIN_FIRST_BYTES: number;
34
+ /**
35
+ * A Git step of the seed that writes the workspace repository's index, refs or config: run again, for up to ~5 s,
36
+ * while another Git process in the VM holds the lock (a conversation's agent starts as soon as its repository appears,
37
+ * and its own `git status` takes the index lock). Every step passed here is safe to repeat.
38
+ */
39
+ export declare function runGitStep(cell: Pick<SyncCell, 'exec'>, argv: string[], options: Parameters<SyncCell['exec']['run']>[1] & object, attempts?: number): Promise<Awaited<ReturnType<SyncCell['exec']['run']>>>;
31
40
  /** The repository's object store size, an upper bound of any one branch's history bundle. Reads sizes only. */
32
41
  export declare function historyEstimate(folder: string, signal?: AbortSignal): Promise<number>;
33
42
  export declare const FETCH_ORIGIN: string;
@@ -39,7 +48,10 @@ export declare const CHECKOUT_HEAD: string;
39
48
  * from origin as a blobless partial clone (the workspace's own Git credentials), plus a bundle of the commits origin
40
49
  * lacks. Without a reachable origin the whole bundle is streamed in chunks: no history size refuses (0.19.0+).
41
50
  */
42
- export declare function importProjectHistory(cell: SyncCell, folder: string, root: string, head: string, branch: string, origin: string | null, signal?: AbortSignal, originFirst?: number, report?: (message: string) => void): Promise<{
51
+ export declare function importProjectHistory(cell: SyncCell, folder: string, root: string, head: string, branch: string, origin: string | null, signal?: AbortSignal, originFirst?: number, report?: (message: string) => void, configure?: {
52
+ settings: string[];
53
+ excludes: string;
54
+ }): Promise<{
43
55
  partial: string | null;
44
56
  }>;
45
57
  /**
@@ -47,11 +59,12 @@ export declare function importProjectHistory(cell: SyncCell, folder: string, roo
47
59
  * untracked files Git does not ignore, the .env files we always carry, Git LFS files (their content lives outside Git),
48
60
  * and deletions. Ignored files, nested repositories and submodule contents stay out.
49
61
  */
50
- export declare function localChanges(folder: string, signal?: AbortSignal): Promise<{
62
+ export type LocalChanges = {
51
63
  copy: string[];
52
64
  remove: string[];
53
65
  skipped: {
54
66
  path: string;
55
67
  reason: string;
56
68
  }[];
57
- }>;
69
+ };
70
+ export declare function localChanges(folder: string, signal?: AbortSignal): Promise<LocalChanges>;