@shardflux/cli 0.15.2 → 0.16.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 (41) hide show
  1. package/CHANGELOG.md +82 -12
  2. package/README.md +50 -3
  3. package/dist/acp-models.js +5 -1
  4. package/dist/agent-commands.d.ts +8 -2
  5. package/dist/agent-commands.js +23 -11
  6. package/dist/agent-target-commands.d.ts +7 -2
  7. package/dist/agent-target-commands.js +12 -7
  8. package/dist/agent-targets.d.ts +3 -0
  9. package/dist/agent-targets.js +4 -1
  10. package/dist/agent-terminal.d.ts +8 -0
  11. package/dist/agent-terminal.js +35 -2
  12. package/dist/args.d.ts +1 -1
  13. package/dist/args.js +26 -3
  14. package/dist/codex-cloud.d.ts +2 -0
  15. package/dist/codex-cloud.js +2 -0
  16. package/dist/command-center-actions.js +9 -0
  17. package/dist/commands.d.ts +22 -0
  18. package/dist/commands.js +54 -14
  19. package/dist/conversation-commands.js +6 -0
  20. package/dist/conversation-terminal.js +10 -1
  21. package/dist/conversations.d.ts +13 -1
  22. package/dist/conversations.js +41 -5
  23. package/dist/device-helper.js +9 -2
  24. package/dist/errors.d.ts +5 -0
  25. package/dist/errors.js +11 -1
  26. package/dist/files-mirror.d.ts +164 -0
  27. package/dist/files-mirror.js +761 -0
  28. package/dist/handlers.js +3 -1
  29. package/dist/main.js +9 -2
  30. package/dist/registry.d.ts +2 -42
  31. package/dist/registry.js +6 -90
  32. package/dist/thread-on-computer.d.ts +35 -0
  33. package/dist/thread-on-computer.js +200 -0
  34. package/dist/wait-status.d.ts +22 -0
  35. package/dist/wait-status.js +59 -0
  36. package/dist/worktree-twin-engine.d.ts +95 -0
  37. package/dist/worktree-twin-engine.js +402 -0
  38. package/dist/worktree-twin-land.py +331 -0
  39. package/dist/worktree-twin.d.ts +296 -0
  40. package/dist/worktree-twin.js +718 -0
  41. package/package.json +3 -3
@@ -0,0 +1,164 @@
1
+ import type { Workspace } from '@shardflux/sdk';
2
+ import type { Ctx } from './commands.js';
3
+ import type { Conversation, LocalConversation } from './conversations.js';
4
+ /**
5
+ * Files across conversations on the Mac (docs/progress/cross-conversation-files.md): a read-only folder per
6
+ * conversation, `~/Shardflux/<repository>/<base branch>/<conversation>/`, holding what that conversation committed
7
+ * relative to its fork point. Git (the project hub) is the only channel; the user's own checkouts are never written.
8
+ * Files above the per-file limit, or past the conversation's total, are placeholders until `shard files pull`.
9
+ */
10
+ export declare const HUB_PATH = "/shardflux/project/git/hub.git";
11
+ export declare const MANIFEST = ".shardflux-manifest.json";
12
+ export declare const PLACEHOLDER = ".placeholder.txt";
13
+ export declare const FILES_CONFIG = ".shardflux/files.yaml";
14
+ export declare const DEFAULT_MAX_FILE: number;
15
+ export declare const DEFAULT_MAX_CONVERSATION: number;
16
+ export type MirrorConversation = Conversation & {
17
+ name?: string;
18
+ join_mode?: string | null;
19
+ base_branch?: string | null;
20
+ repo_remote?: string;
21
+ landed?: {
22
+ how: string;
23
+ ref?: string;
24
+ at?: string;
25
+ } | null;
26
+ };
27
+ export interface ManifestFile {
28
+ path: string;
29
+ oid: string;
30
+ size: number;
31
+ mode: string;
32
+ state: 'present' | 'placeholder' | 'submodule' | 'symlink';
33
+ reason?: 'file_limit' | 'conversation_limit';
34
+ fetched?: boolean;
35
+ }
36
+ export interface Manifest {
37
+ version: 1;
38
+ conversation_id: string;
39
+ project_id: string;
40
+ name: string;
41
+ title: string;
42
+ repository: string;
43
+ branch: string;
44
+ base_branch: string | null;
45
+ commit: string;
46
+ fork_point: string;
47
+ seed?: string;
48
+ max_file_bytes: number;
49
+ max_conversation_bytes: number;
50
+ files: ManifestFile[];
51
+ deleted: string[];
52
+ landed?: {
53
+ how: string;
54
+ ref: string;
55
+ at: string;
56
+ note: string;
57
+ };
58
+ updated_at: string;
59
+ }
60
+ export interface Limits {
61
+ maxFile: number;
62
+ maxConversation: number;
63
+ }
64
+ export interface Change {
65
+ status: string;
66
+ mode: string;
67
+ oid: string;
68
+ path: string;
69
+ }
70
+ export declare function filesRoot(env: Record<string, string | undefined>): string;
71
+ /** A base branch as one path component: slashes and other separators become "-". */
72
+ export declare function branchFolder(branch: string | null | undefined): string;
73
+ /** A human name as one Finder path component. */
74
+ export declare function humanFolder(text: string, fallback: string): string;
75
+ export declare function repositoryName(identity: string): string;
76
+ export declare function parseFilesConfig(text: string | undefined): Limits;
77
+ export declare function parseDiffTree(bytes: Buffer): Change[];
78
+ export declare function safeRelative(path: string): boolean;
79
+ /** Smallest first while the conversation's total stays within its limit; fetched files stay while unchanged. */
80
+ export declare function planFiles(files: ManifestFile[], limits: Limits, prev: Map<string, ManifestFile>): ManifestFile[];
81
+ export declare function humanBytes(n: number): string;
82
+ export declare function placeholderText(m: Manifest, f: ManifestFile): string;
83
+ export interface GitResult {
84
+ code: number;
85
+ stdout: Buffer;
86
+ stderr: string;
87
+ }
88
+ /**
89
+ * Read-only git against the project hub. `blobs` delivers `git cat-file --batch` output for the given ids, any number
90
+ * of files at once. (The hub's upload-pack, reached through the platform Git bridge, serves advertised refs only, so a
91
+ * fetch of single blobs by id is refused; this needs no hub configuration.)
92
+ */
93
+ export interface Hub {
94
+ git(args: string[], stdin?: string): Promise<GitResult>;
95
+ blobs(oids: string[], onData: (chunk: Uint8Array) => void): Promise<void>;
96
+ close(): void;
97
+ }
98
+ /** A hub on this machine (tests). */
99
+ export declare function localHub(path: string): Hub;
100
+ /** The project hub through a ready VM of the project: git runs there, file contents stream back over the same exec API. */
101
+ export declare function cellHub(ws: Workspace, signal: AbortSignal): Hub;
102
+ /** Copy files' contents from the hub into root, in one stream. */
103
+ export declare function copyFromHub(hub: Hub, root: string, files: ManifestFile[]): Promise<void>;
104
+ export interface PlaceholderEntry {
105
+ conversation_id: string;
106
+ conversation: string;
107
+ title: string;
108
+ folder: string;
109
+ path: string;
110
+ size: number;
111
+ local_path: string;
112
+ }
113
+ export declare class FilesMirrorState {
114
+ readonly revisions: Map<string, string>;
115
+ retryAt: number;
116
+ failures: number;
117
+ listedAt: number;
118
+ readonly reported: Set<string>;
119
+ }
120
+ /** The repository a conversation belongs to: its normalized remote; worktrees of an origin-less repository share its Git common directory. */
121
+ export declare function repositoryIdentity(c: MirrorConversation, local?: LocalConversation): {
122
+ id: string;
123
+ name: string;
124
+ };
125
+ export declare class FilesMirror {
126
+ #private;
127
+ readonly root: string;
128
+ readonly ctx: Pick<Ctx, 'io' | 'store' | 'apiUrl' | 'signal'>;
129
+ readonly connect: (projectId: string, all: MirrorConversation[]) => Promise<Hub>;
130
+ constructor(ctx: Pick<Ctx, 'io' | 'store' | 'apiUrl' | 'signal'>, connect: (projectId: string, all: MirrorConversation[]) => Promise<Hub>, root?: string);
131
+ get stateFile(): string;
132
+ get indexFile(): string;
133
+ /** One pass: only conversations whose API record moved touch the hub. */
134
+ sync(all: MirrorConversation[], state?: FilesMirrorState): Promise<void>;
135
+ /** Find a mirrored conversation by id, name or folder name. */
136
+ find(ref: string): {
137
+ rel: string;
138
+ manifest: Manifest;
139
+ };
140
+ /** Download one listed file, whatever its size. */
141
+ pull(ref: string, path: string, all: () => Promise<MirrorConversation[]>): Promise<{
142
+ conversation: string;
143
+ path: string;
144
+ local_path: string;
145
+ bytes: number;
146
+ already: boolean;
147
+ }>;
148
+ }
149
+ /**
150
+ * The seed snapshot (sh; $1 repository, $2 conversation name, $3 hub): HEAD plus the working tree the conversation was
151
+ * seeded with, committed in the conversation's VM with a temporary index (never on this computer), kept per worktree
152
+ * as refs/worktree/shard/seed and in the hub as refs/shard/seed/<name>. Mirrors leave out what it already contains,
153
+ * so the user's own uncommitted edits never show as the conversation's change. Threads on one computer are worktrees
154
+ * of one repository (0.16.0+), so the seed is per worktree; the shared refs/shard/seed of earlier seeds is read, and
155
+ * still written, only in the repository's main worktree.
156
+ */
157
+ export declare const SEED_SNAPSHOT = "set -eu\ncd \"$1\"\nmain=0; [ \"$(git rev-parse --absolute-git-dir)\" = \"$(git rev-parse --path-format=absolute --git-common-dir)\" ] && main=1\nif git rev-parse -q --verify refs/worktree/shard/seed >/dev/null; then seed=$(git rev-parse refs/worktree/shard/seed)\nelif [ \"$main\" = 1 ] && git rev-parse -q --verify refs/shard/seed >/dev/null; then seed=$(git rev-parse refs/shard/seed); else\nidx=$(mktemp); trap 'rm -f \"$idx\"' EXIT\ncp \"$(git rev-parse --path-format=absolute --git-path index)\" \"$idx\" 2>/dev/null || { rm -f \"$idx\"; GIT_INDEX_FILE=\"$idx\" git read-tree HEAD; }\nGIT_INDEX_FILE=\"$idx\" git add -A\ntree=$(GIT_INDEX_FILE=\"$idx\" git write-tree)\nif [ \"$tree\" = \"$(git rev-parse 'HEAD^{tree}')\" ]; then seed=$(git rev-parse HEAD); else\nseed=$(GIT_AUTHOR_NAME=Shardflux GIT_AUTHOR_EMAIL=seed@shardflux.dev GIT_COMMITTER_NAME=Shardflux GIT_COMMITTER_EMAIL=seed@shardflux.dev git commit-tree \"$tree\" -p HEAD -m \"Shardflux: the working tree this conversation started from\"); fi\ngit update-ref refs/worktree/shard/seed \"$seed\"\nif [ \"$main\" = 1 ]; then git update-ref refs/shard/seed \"$seed\"; fi\nfi\nif [ -d \"$3\" ]; then git -c core.hooksPath=/dev/null push -q \"$3\" \"+$seed:refs/shard/seed/$2\" >/dev/null 2>&1 || true; fi\nprintf '%s\n' \"$seed\"\n";
158
+ /** Best effort: without a snapshot the folder also lists the start state's own edits. */
159
+ export declare function recordSeedSnapshot(ws: Pick<Workspace, 'cell'>, c: Pick<Conversation, 'worktree_path' | 'branch'>, signal?: AbortSignal): Promise<string | null>;
160
+ /** A ready VM of the project (never woken), else the project's space reader, woken when needed. */
161
+ export declare function workspaceHub(ctx: Ctx): (projectId: string, all: MirrorConversation[]) => Promise<Hub>;
162
+ export declare function listMirrorConversations(ctx: Ctx): Promise<MirrorConversation[]>;
163
+ /** The device helper's pass: list every few seconds; touch the hub only for conversations whose record moved. */
164
+ export declare function mirrorFilesOnce(ctx: Ctx, state: FilesMirrorState, intervalMs?: number): Promise<void>;