agent-yes 1.197.0 → 1.198.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.
- package/dist/SUPPORTED_CLIS-Bhb4JKPv.js +9 -0
- package/dist/{SUPPORTED_CLIS-BY83eM58.js → SUPPORTED_CLIS-BuScRhc9.js} +2 -2
- package/dist/{agentShare-DWioPObE.js → agentShare-CD1AIPMR.js} +2 -2
- package/dist/cli.js +4 -4
- package/dist/index.js +2 -2
- package/dist/{notifyDaemon-CRf_27kE.js → notifyDaemon-DYoMC4tA.js} +2 -2
- package/dist/{rustBinary-5HCt7_0b.js → rustBinary-DLI26u3O.js} +2 -2
- package/dist/{schedule-CjBcOy-b.js → schedule-DAl2mCc9.js} +4 -4
- package/dist/{serve-C3E8uicL.js → serve-9jP4V1l_.js} +10 -10
- package/dist/{setup-C4SKqGy1.js → setup-B55UN6xV.js} +2 -2
- package/dist/{subcommands-B7UxIJRw.js → subcommands-D7AmlO_6.js} +13 -8
- package/dist/{subcommands-DzWBZ6HE.js → subcommands-lUi22sO5.js} +1 -1
- package/dist/{ts-B64pNQlI.js → ts-D9RFyHqn.js} +2 -2
- package/dist/{versionChecker-DKiSuTSp.js → versionChecker-DQQIK4Og.js} +2 -2
- package/dist/ws-BpHPhcPH.js +399 -0
- package/package.json +1 -1
- package/ts/subcommands.ts +11 -1
- package/ts/ws.spec.ts +435 -0
- package/ts/ws.ts +522 -0
- package/dist/SUPPORTED_CLIS-D4Z--hGm.js +0 -9
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import "./logger-CDIsZ-Pp.js";
|
|
2
|
+
import "./globalPidIndex-CoNr7tS8.js";
|
|
3
|
+
import "./messageLog-CxrKJj77.js";
|
|
4
|
+
import "./configShared-aKTg-sa5.js";
|
|
5
|
+
import { S as listRecords } from "./subcommands-D7AmlO_6.js";
|
|
6
|
+
import "./e2e-jb0Hp43q.js";
|
|
7
|
+
import "./webrtcLink-B7REGtK2.js";
|
|
8
|
+
import "./remotes-Cim0dBU7.js";
|
|
9
|
+
import { i as getProvisionRoot } from "./workspaceConfig-CjaRvTjf.js";
|
|
10
|
+
import { existsSync } from "fs";
|
|
11
|
+
import { lstat, opendir } from "fs/promises";
|
|
12
|
+
import path from "path";
|
|
13
|
+
|
|
14
|
+
//#region ts/ws.ts
|
|
15
|
+
/**
|
|
16
|
+
* `ay ws` — workspace management over the codehost/provision standard layout
|
|
17
|
+
* (`<wsRoot>/<owner>/<repo>/tree/<branch>`, independent clones except forked
|
|
18
|
+
* linked worktrees).
|
|
19
|
+
*
|
|
20
|
+
* v1 surface (read/list/new/fork only — deletion/gc/doctor are deliberately
|
|
21
|
+
* deferred until workspace lifecycle data exists to design them around):
|
|
22
|
+
*
|
|
23
|
+
* ay ws ls [--status] [--json]
|
|
24
|
+
* ay ws status [<spec-or-path>] [--path|--spec] [--json]
|
|
25
|
+
* ay ws new <source> [--create]
|
|
26
|
+
* ay ws fork <branch> [--from <path>] [--wip]
|
|
27
|
+
*
|
|
28
|
+
* `--json` schema (stable, versioned via `schema`):
|
|
29
|
+
* ls: { schema:"ay-ws/v1", wsRoot, workspaces: WsEntry[] }
|
|
30
|
+
* status: { schema:"ay-ws/v1", workspace: WsEntry }
|
|
31
|
+
* WsEntry = { owner, repo, branch, path, agents: { live: number },
|
|
32
|
+
* git?: GitStatus | null, gitError?: string }
|
|
33
|
+
*
|
|
34
|
+
* codehost/provision is a peer package (bun-linked in dev). Like serve.ts's
|
|
35
|
+
* fork path, it is imported lazily so `ay ls` etc. never pay for it and a
|
|
36
|
+
* missing install produces a clear actionable error instead of a module crash.
|
|
37
|
+
*/
|
|
38
|
+
async function loadProvision() {
|
|
39
|
+
try {
|
|
40
|
+
return await import("codehost/provision");
|
|
41
|
+
} catch (e) {
|
|
42
|
+
throw new Error(`'ay ws' needs the 'codehost' package (codehost/provision) — install it (npm i -g codehost) or 'bun link' it for local dev: ${e.message}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const WS_JSON_SCHEMA = "ay-ws/v1";
|
|
46
|
+
const STATUS_CONCURRENCY = 8;
|
|
47
|
+
const MAX_BRANCH_DEPTH = 8;
|
|
48
|
+
/**
|
|
49
|
+
* Path containment that matches how the OS actually compares paths: segment
|
|
50
|
+
* boundaries via path.relative (never a raw startsWith), case-insensitive on
|
|
51
|
+
* Windows (and macOS's default case-insensitive FS is fine with this too —
|
|
52
|
+
* false positives there require two dirs differing only by case).
|
|
53
|
+
*/
|
|
54
|
+
function isPathInside(parent, child) {
|
|
55
|
+
let p = path.resolve(parent);
|
|
56
|
+
let c = path.resolve(child);
|
|
57
|
+
if (process.platform === "win32") {
|
|
58
|
+
p = p.toLowerCase();
|
|
59
|
+
c = c.toLowerCase();
|
|
60
|
+
}
|
|
61
|
+
const rel = path.relative(p, c);
|
|
62
|
+
return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
|
|
63
|
+
}
|
|
64
|
+
/** True when `dir` is a git checkout root (`.git` file = linked worktree, dir = clone). */
|
|
65
|
+
function isCheckoutRoot(dir) {
|
|
66
|
+
return existsSync(path.join(dir, ".git"));
|
|
67
|
+
}
|
|
68
|
+
async function subdirs(dir) {
|
|
69
|
+
const out = [];
|
|
70
|
+
let d;
|
|
71
|
+
try {
|
|
72
|
+
d = await opendir(dir);
|
|
73
|
+
} catch {
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
for await (const ent of d) {
|
|
77
|
+
if (ent.name.startsWith(".")) continue;
|
|
78
|
+
if (ent.isDirectory()) out.push(ent.name);
|
|
79
|
+
else if (ent.isSymbolicLink()) continue;
|
|
80
|
+
}
|
|
81
|
+
return out.sort();
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Walk `<wsRoot>/<owner>/<repo>/tree/**` collecting checkout roots. The branch
|
|
85
|
+
* may contain `/`, so below `tree/` we descend until a `.git` marker is found
|
|
86
|
+
* (a checkout root is never nested inside another checkout in this layout),
|
|
87
|
+
* bounded by MAX_BRANCH_DEPTH.
|
|
88
|
+
*/
|
|
89
|
+
async function walkWorkspaces(wsRoot) {
|
|
90
|
+
const found = [];
|
|
91
|
+
async function walkBranches(dir, owner, repo, segs) {
|
|
92
|
+
if (segs.length > MAX_BRANCH_DEPTH) return;
|
|
93
|
+
for (const name of await subdirs(dir)) {
|
|
94
|
+
const p = path.join(dir, name);
|
|
95
|
+
const branchSegs = [...segs, name];
|
|
96
|
+
if (isCheckoutRoot(p)) found.push({
|
|
97
|
+
owner,
|
|
98
|
+
repo,
|
|
99
|
+
branch: branchSegs.join("/"),
|
|
100
|
+
path: p
|
|
101
|
+
});
|
|
102
|
+
else await walkBranches(p, owner, repo, branchSegs);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
for (const owner of await subdirs(wsRoot)) for (const repo of await subdirs(path.join(wsRoot, owner))) {
|
|
106
|
+
const tree = path.join(wsRoot, owner, repo, "tree");
|
|
107
|
+
try {
|
|
108
|
+
if (!(await lstat(tree)).isDirectory()) continue;
|
|
109
|
+
} catch {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
await walkBranches(tree, owner, repo, []);
|
|
113
|
+
}
|
|
114
|
+
return found;
|
|
115
|
+
}
|
|
116
|
+
/** Live (non-exited) agents whose cwd sits inside `wsPath`. */
|
|
117
|
+
function liveAgentsIn(records, wsPath) {
|
|
118
|
+
return records.filter((r) => r.cwd && isPathInside(wsPath, r.cwd));
|
|
119
|
+
}
|
|
120
|
+
async function mapBounded(items, limit, fn) {
|
|
121
|
+
const out = new Array(items.length);
|
|
122
|
+
let next = 0;
|
|
123
|
+
const workers = Array.from({ length: Math.min(limit, items.length) }, async () => {
|
|
124
|
+
while (next < items.length) {
|
|
125
|
+
const i = next++;
|
|
126
|
+
out[i] = await fn(items[i]);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
await Promise.all(workers);
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Resolve a status/inspect operand deterministically:
|
|
134
|
+
* 1. `--path` forces path, `--spec` forces source-spec parsing
|
|
135
|
+
* 2. an operand that exists on disk is a path
|
|
136
|
+
* 3. otherwise it must parse as a source (owner/repo[@branch|/tree/branch] or URL)
|
|
137
|
+
* Prints the normalized resolution so the user always sees what was addressed.
|
|
138
|
+
*/
|
|
139
|
+
async function resolveOperand(prov, operand, mode, wsRoot) {
|
|
140
|
+
const asPath = () => {
|
|
141
|
+
const dir = path.resolve(operand);
|
|
142
|
+
if (!existsSync(dir)) throw new Error(`path does not exist: ${dir}`);
|
|
143
|
+
if (!isCheckoutRoot(dir)) throw new Error(`not a git checkout root (no .git): ${dir}`);
|
|
144
|
+
return {
|
|
145
|
+
dir,
|
|
146
|
+
spec: null
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
const asSpec = () => {
|
|
150
|
+
const spec = prov.parseSource(operand);
|
|
151
|
+
if (!spec) throw new Error(`cannot parse "${operand}" as a source — expected <owner>/<repo>, <owner>/<repo>@<branch>, <owner>/<repo>/tree/<branch>, or a github URL` + (existsSync(operand) ? "" : " (and it is not an existing path)"));
|
|
152
|
+
const dir = prov.folderFor(spec, wsRoot);
|
|
153
|
+
if (!existsSync(dir)) throw new Error(`workspace not provisioned: ${dir} (ay ws new ${operand})`);
|
|
154
|
+
return {
|
|
155
|
+
dir,
|
|
156
|
+
spec
|
|
157
|
+
};
|
|
158
|
+
};
|
|
159
|
+
if (mode === "path") return asPath();
|
|
160
|
+
if (mode === "spec") return asSpec();
|
|
161
|
+
if (existsSync(path.resolve(operand))) return asPath();
|
|
162
|
+
return asSpec();
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Default source for `ws fork`: the calling agent's registered cwd when run
|
|
166
|
+
* inside an agent (AGENT_YES_PID is the wrapper pid of the enclosing agent),
|
|
167
|
+
* else the current directory.
|
|
168
|
+
*/
|
|
169
|
+
async function defaultForkFrom() {
|
|
170
|
+
const envPid = Number(process.env.AGENT_YES_PID);
|
|
171
|
+
if (envPid > 0) {
|
|
172
|
+
const recs = await listRecords(void 0, {
|
|
173
|
+
all: true,
|
|
174
|
+
active: false,
|
|
175
|
+
json: false,
|
|
176
|
+
latest: false,
|
|
177
|
+
cwdScope: null
|
|
178
|
+
});
|
|
179
|
+
const self = recs.find((r) => r.wrapper_pid === envPid) ?? recs.find((r) => r.pid === envPid);
|
|
180
|
+
if (self?.cwd) return self.cwd;
|
|
181
|
+
}
|
|
182
|
+
return process.cwd();
|
|
183
|
+
}
|
|
184
|
+
function parseFlags(args, known) {
|
|
185
|
+
const flags = {};
|
|
186
|
+
const positional = [];
|
|
187
|
+
for (let i = 0; i < args.length; i++) {
|
|
188
|
+
const a = args[i];
|
|
189
|
+
if (!a.startsWith("--")) {
|
|
190
|
+
positional.push(a);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
const eq = a.indexOf("=");
|
|
194
|
+
const name = eq === -1 ? a.slice(2) : a.slice(2, eq);
|
|
195
|
+
const kind = known[name];
|
|
196
|
+
if (!kind) throw new Error(`unknown flag --${name}`);
|
|
197
|
+
if (kind === "bool") {
|
|
198
|
+
if (eq !== -1) throw new Error(`--${name} takes no value`);
|
|
199
|
+
flags[name] = true;
|
|
200
|
+
} else {
|
|
201
|
+
const v = eq !== -1 ? a.slice(eq + 1) : args[++i];
|
|
202
|
+
if (v === void 0) throw new Error(`--${name} requires a value`);
|
|
203
|
+
flags[name] = v;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
flags,
|
|
208
|
+
positional
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
async function cmdWsLs(args) {
|
|
212
|
+
const { flags, positional } = parseFlags(args, {
|
|
213
|
+
status: "bool",
|
|
214
|
+
json: "bool"
|
|
215
|
+
});
|
|
216
|
+
if (positional.length > 0) throw new Error(`ws ls takes no positional args`);
|
|
217
|
+
const prov = await loadProvision();
|
|
218
|
+
const wsRoot = prov.resolveWsRoot(getProvisionRoot());
|
|
219
|
+
const [bare, records] = await Promise.all([walkWorkspaces(wsRoot), listRecords(void 0, {
|
|
220
|
+
all: false,
|
|
221
|
+
active: false,
|
|
222
|
+
json: false,
|
|
223
|
+
latest: false,
|
|
224
|
+
cwdScope: null
|
|
225
|
+
})]);
|
|
226
|
+
let entries = bare.map((w) => ({
|
|
227
|
+
...w,
|
|
228
|
+
agents: { live: liveAgentsIn(records, w.path).length }
|
|
229
|
+
}));
|
|
230
|
+
if (flags.status) entries = await mapBounded(entries, STATUS_CONCURRENCY, async (e) => {
|
|
231
|
+
try {
|
|
232
|
+
return {
|
|
233
|
+
...e,
|
|
234
|
+
git: await prov.readStatus(e.path)
|
|
235
|
+
};
|
|
236
|
+
} catch (err) {
|
|
237
|
+
return {
|
|
238
|
+
...e,
|
|
239
|
+
git: null,
|
|
240
|
+
gitError: err.message.slice(0, 200)
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
if (flags.json) {
|
|
245
|
+
process.stdout.write(JSON.stringify({
|
|
246
|
+
schema: WS_JSON_SCHEMA,
|
|
247
|
+
wsRoot,
|
|
248
|
+
workspaces: entries
|
|
249
|
+
}, null, 2) + "\n");
|
|
250
|
+
return 0;
|
|
251
|
+
}
|
|
252
|
+
if (entries.length === 0) {
|
|
253
|
+
process.stderr.write(`no workspaces under ${wsRoot} (layout <owner>/<repo>/tree/<branch>)\n`);
|
|
254
|
+
return 0;
|
|
255
|
+
}
|
|
256
|
+
const specOf = (e) => `${e.owner}/${e.repo}@${e.branch}`;
|
|
257
|
+
const specW = Math.max(9, ...entries.map((e) => specOf(e).length));
|
|
258
|
+
const agentsW = 6;
|
|
259
|
+
const header = flags.status ? `${"WORKSPACE".padEnd(specW)} ${"AGENTS".padEnd(agentsW)} GIT\n` : `${"WORKSPACE".padEnd(specW)} ${"AGENTS".padEnd(agentsW)} PATH\n`;
|
|
260
|
+
process.stdout.write(header);
|
|
261
|
+
for (const e of entries) {
|
|
262
|
+
const agents = e.agents.live > 0 ? String(e.agents.live) : "-";
|
|
263
|
+
const tail = flags.status ? gitSummary(e) : e.path;
|
|
264
|
+
process.stdout.write(`${specOf(e).padEnd(specW)} ${agents.padEnd(agentsW)} ${tail}\n`);
|
|
265
|
+
}
|
|
266
|
+
if (entries.some((e) => e.agents.live > 0)) process.stderr.write(`\n ay ls --cwd <path> # the agents inside a workspace\n`);
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
function gitSummary(e) {
|
|
270
|
+
if (e.gitError) return `error: ${e.gitError}`;
|
|
271
|
+
const g = e.git;
|
|
272
|
+
if (!g) return "?";
|
|
273
|
+
const parts = [];
|
|
274
|
+
if (g.dirty) parts.push("dirty");
|
|
275
|
+
if (g.ahead > 0) parts.push(`ahead ${g.ahead}`);
|
|
276
|
+
if (g.behind > 0) parts.push(`behind ${g.behind}`);
|
|
277
|
+
if (!g.hasUpstream) parts.push("no-upstream");
|
|
278
|
+
return parts.length ? parts.join(", ") : "clean";
|
|
279
|
+
}
|
|
280
|
+
async function cmdWsStatus(args) {
|
|
281
|
+
const { flags, positional } = parseFlags(args, {
|
|
282
|
+
json: "bool",
|
|
283
|
+
path: "bool",
|
|
284
|
+
spec: "bool"
|
|
285
|
+
});
|
|
286
|
+
if (flags.path && flags.spec) throw new Error("--path and --spec are mutually exclusive");
|
|
287
|
+
if (positional.length > 1) throw new Error("ws status takes at most one target");
|
|
288
|
+
const prov = await loadProvision();
|
|
289
|
+
const wsRoot = getProvisionRoot();
|
|
290
|
+
const { dir, spec } = await resolveOperand(prov, positional[0] ?? ".", flags.path ? "path" : flags.spec ? "spec" : "auto", wsRoot);
|
|
291
|
+
const git = await prov.readStatus(dir);
|
|
292
|
+
const layoutSpec = spec ?? (() => {
|
|
293
|
+
const root = prov.resolveWsRoot(wsRoot);
|
|
294
|
+
if (!isPathInside(root, dir)) return null;
|
|
295
|
+
const segs = path.relative(root, dir).split(path.sep);
|
|
296
|
+
return segs.length >= 4 && segs[2] === "tree" ? {
|
|
297
|
+
owner: segs[0],
|
|
298
|
+
repo: segs[1],
|
|
299
|
+
branch: segs.slice(3).join("/")
|
|
300
|
+
} : null;
|
|
301
|
+
})();
|
|
302
|
+
const entry = {
|
|
303
|
+
owner: layoutSpec?.owner ?? "",
|
|
304
|
+
repo: layoutSpec?.repo ?? "",
|
|
305
|
+
branch: layoutSpec?.branch ?? git.branch,
|
|
306
|
+
path: dir,
|
|
307
|
+
agents: { live: liveAgentsIn(await listRecords(void 0, {
|
|
308
|
+
all: false,
|
|
309
|
+
active: false,
|
|
310
|
+
json: false,
|
|
311
|
+
latest: false,
|
|
312
|
+
cwdScope: null
|
|
313
|
+
}), dir).length },
|
|
314
|
+
git
|
|
315
|
+
};
|
|
316
|
+
if (flags.json) {
|
|
317
|
+
process.stdout.write(JSON.stringify({
|
|
318
|
+
schema: WS_JSON_SCHEMA,
|
|
319
|
+
workspace: entry
|
|
320
|
+
}, null, 2) + "\n");
|
|
321
|
+
return 0;
|
|
322
|
+
}
|
|
323
|
+
process.stdout.write(`${dir}\n` + (layoutSpec ? ` spec: ${layoutSpec.owner}/${layoutSpec.repo}@${layoutSpec.branch}\n` : "") + ` branch: ${git.branch} @ ${git.head}\n state: ${gitSummary(entry)}\n agents: ${entry.agents.live} live\n`);
|
|
324
|
+
if (entry.agents.live > 0) process.stderr.write(`\n ay ls --cwd ${dir}\n`);
|
|
325
|
+
return 0;
|
|
326
|
+
}
|
|
327
|
+
async function cmdWsNew(args) {
|
|
328
|
+
const { flags, positional } = parseFlags(args, { create: "bool" });
|
|
329
|
+
const source = positional[0];
|
|
330
|
+
if (!source || positional.length > 1) throw new Error("usage: ay ws new <owner>/<repo>[@branch] [--create]");
|
|
331
|
+
const prov = await loadProvision();
|
|
332
|
+
const wsRoot = getProvisionRoot();
|
|
333
|
+
const spec = prov.parseSource(source);
|
|
334
|
+
if (!spec) throw new Error(`cannot parse "${source}" as a source (owner/repo[@branch] or URL)`);
|
|
335
|
+
process.stderr.write(`provisioning ${spec.owner}/${spec.repo}@${spec.branch} …\n`);
|
|
336
|
+
let res = await prov.provision(spec, { wsRoot });
|
|
337
|
+
if (!res.ok && res.reason === "branch-not-found" && flags.create) {
|
|
338
|
+
process.stderr.write(`branch not found on remote — creating it locally (--create)\n`);
|
|
339
|
+
res = await prov.createBranch(spec, { wsRoot });
|
|
340
|
+
}
|
|
341
|
+
if (!res.ok) {
|
|
342
|
+
process.stderr.write(`provision failed (${res.reason ?? "error"}): ${res.error ?? "unknown"}\n` + (res.reason === "branch-not-found" && !flags.create ? ` (branch missing on the remote — re-run with --create to branch off the default)\n` : ""));
|
|
343
|
+
return 1;
|
|
344
|
+
}
|
|
345
|
+
process.stdout.write(`${res.action} ${res.folder}\n`);
|
|
346
|
+
return 0;
|
|
347
|
+
}
|
|
348
|
+
async function cmdWsFork(args) {
|
|
349
|
+
const { flags, positional } = parseFlags(args, {
|
|
350
|
+
from: "value",
|
|
351
|
+
wip: "bool"
|
|
352
|
+
});
|
|
353
|
+
const branch = positional[0];
|
|
354
|
+
if (!branch || positional.length > 1) throw new Error("usage: ay ws fork <new-branch> [--from <path>] [--wip]");
|
|
355
|
+
const prov = await loadProvision();
|
|
356
|
+
const fromCwd = path.resolve(typeof flags.from === "string" ? flags.from : await defaultForkFrom());
|
|
357
|
+
process.stderr.write(`forking ${fromCwd} → branch ${branch}${flags.wip ? " (with WIP)" : ""} …\n`);
|
|
358
|
+
const res = await prov.forkWorktree({
|
|
359
|
+
fromCwd,
|
|
360
|
+
branch,
|
|
361
|
+
wsRoot: getProvisionRoot(),
|
|
362
|
+
wip: !!flags.wip
|
|
363
|
+
});
|
|
364
|
+
if (!res.ok) {
|
|
365
|
+
process.stderr.write(`fork failed: ${res.error ?? "unknown"}\n`);
|
|
366
|
+
return 1;
|
|
367
|
+
}
|
|
368
|
+
process.stdout.write(`${res.action} ${res.folder}\n`);
|
|
369
|
+
process.stderr.write(`\n ay claude --cwd ${res.folder} -- "<prompt>" # work there\n`);
|
|
370
|
+
return 0;
|
|
371
|
+
}
|
|
372
|
+
function wsHelp() {
|
|
373
|
+
process.stdout.write("ay ws - workspaces under <wsRoot>/<owner>/<repo>/tree/<branch>\n\n ay ws ls [--status] [--json] list workspaces (+git state, +live agent count)\n ay ws status [<spec|path>] [--json] one workspace's git state (default: cwd)\n ay ws new <owner>/<repo>[@branch] clone/refresh a workspace (--create: new branch)\n ay ws fork <branch> [--from <path>] sibling worktree off HEAD (--wip: carry changes)\n\n targets: owner/repo, owner/repo@branch, owner/repo/tree/branch, github URL, or a path\n wsRoot: CODEHOST_WS_ROOT > config provisionRoot > ~/ws\n");
|
|
374
|
+
return 0;
|
|
375
|
+
}
|
|
376
|
+
/** `ay ws <sub> …` dispatcher (called from runSubcommand). */
|
|
377
|
+
async function cmdWs(args) {
|
|
378
|
+
const sub = args[0];
|
|
379
|
+
const rest = args.slice(1);
|
|
380
|
+
switch (sub) {
|
|
381
|
+
case "ls":
|
|
382
|
+
case "list": return cmdWsLs(rest);
|
|
383
|
+
case "status": return cmdWsStatus(rest);
|
|
384
|
+
case "new": return cmdWsNew(rest);
|
|
385
|
+
case "fork": return cmdWsFork(rest);
|
|
386
|
+
case void 0:
|
|
387
|
+
case "help":
|
|
388
|
+
case "--help":
|
|
389
|
+
case "-h": return wsHelp();
|
|
390
|
+
default:
|
|
391
|
+
process.stderr.write(`ay ws: unknown subcommand "${sub}"\n\n`);
|
|
392
|
+
wsHelp();
|
|
393
|
+
return 1;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
//#endregion
|
|
398
|
+
export { cmdWs };
|
|
399
|
+
//# sourceMappingURL=ws-BpHPhcPH.js.map
|
package/package.json
CHANGED
package/ts/subcommands.ts
CHANGED
|
@@ -368,7 +368,7 @@ const SUBCOMMANDS = new Set([
|
|
|
368
368
|
// alias like `cy` (= claude-yes = "agent-yes claude") must NOT treat these as
|
|
369
369
|
// subcommands — `cy setup …` should run claude with that text, not manage the
|
|
370
370
|
// host. Kept separate from SUBCOMMANDS so a runner alias falls straight through.
|
|
371
|
-
const MANAGER_SUBCOMMANDS = new Set(["setup"]);
|
|
371
|
+
const MANAGER_SUBCOMMANDS = new Set(["setup", "ws"]);
|
|
372
372
|
|
|
373
373
|
const IDLE_THRESHOLD_MS = 60 * 1000;
|
|
374
374
|
|
|
@@ -475,6 +475,10 @@ export async function runSubcommand(argv: string[]): Promise<number | null> {
|
|
|
475
475
|
const { cmdSetup } = await import("./setup.ts");
|
|
476
476
|
return cmdSetup(rest);
|
|
477
477
|
}
|
|
478
|
+
case "ws": {
|
|
479
|
+
const { cmdWs } = await import("./ws.ts");
|
|
480
|
+
return cmdWs(rest);
|
|
481
|
+
}
|
|
478
482
|
case "schedule": {
|
|
479
483
|
const { cmdSchedule } = await import("./schedule.ts");
|
|
480
484
|
return cmdSchedule(rest);
|
|
@@ -572,6 +576,11 @@ export async function cmdHelp(managerCommands = true): Promise<number> {
|
|
|
572
576
|
const setupLine = managerCommands
|
|
573
577
|
? ` ay setup guided setup: pick a workspace, share to agent-yes.com\n`
|
|
574
578
|
: ``;
|
|
579
|
+
// `ws` is manager-only for the same reason as `setup`.
|
|
580
|
+
const wsLines = managerCommands
|
|
581
|
+
? ` ay ws ls [--status] list <owner>/<repo>/tree/<branch> workspaces\n` +
|
|
582
|
+
` ay ws new <owner>/<repo>[@branch] clone/refresh a workspace (ay ws help for more)\n`
|
|
583
|
+
: ``;
|
|
575
584
|
// Only agents carry AGENT_YES_PID — a human shell never sets it — so this
|
|
576
585
|
// section is skipped entirely (no async work at all) for interactive use.
|
|
577
586
|
const self = process.env.AGENT_YES_PID ? await resolveSender() : null;
|
|
@@ -599,6 +608,7 @@ export async function cmdHelp(managerCommands = true): Promise<number> {
|
|
|
599
608
|
` ay result <keyword> [--wait] pull an agent's structured result envelope\n` +
|
|
600
609
|
` ay result set '<json>' (inside an agent) deposit your result envelope\n` +
|
|
601
610
|
` ay reap kill process groups leaked by dead agents\n` +
|
|
611
|
+
wsLines +
|
|
602
612
|
`\n` +
|
|
603
613
|
`Remote:\n` +
|
|
604
614
|
setupLine +
|