@tryarcanist/cli 0.1.175 → 0.1.177
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/README.md +48 -2
- package/dist/index.js +233 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -266,9 +266,12 @@ arcanist sessions list
|
|
|
266
266
|
arcanist sessions list --status idle --limit 20 --json
|
|
267
267
|
arcanist sessions list --search "architect agent" --repo your-org/your-repo
|
|
268
268
|
arcanist sessions list --scope business --cursor <cursor>
|
|
269
|
+
arcanist sessions list --all --json
|
|
269
270
|
```
|
|
270
271
|
|
|
271
|
-
JSON mode returns `{sessions, nextCursor}`.
|
|
272
|
+
JSON mode returns `{sessions, nextCursor}`.
|
|
273
|
+
`--all` follows cursors until completion and returns `nextCursor: null`.
|
|
274
|
+
Search is metadata-only: generated titles and repo metadata.
|
|
272
275
|
|
|
273
276
|
### `arcanist sessions search <query>`
|
|
274
277
|
|
|
@@ -276,9 +279,10 @@ JSON mode returns `{sessions, nextCursor}`. Search is metadata-only: generated t
|
|
|
276
279
|
arcanist sessions search "architect agent"
|
|
277
280
|
arcanist sessions search "mcp debugging" --repo your-org/your-repo --json
|
|
278
281
|
arcanist sessions search "repo access" --status idle --scope business --limit 20 --cursor <cursor>
|
|
282
|
+
arcanist sessions search "repo access" --all --json
|
|
279
283
|
```
|
|
280
284
|
|
|
281
|
-
Uses the same metadata-only index and filters as `sessions list`: `--status`, `--scope`, `--repo`, `--limit`, and `--
|
|
285
|
+
Uses the same metadata-only index and filters as `sessions list`: `--status`, `--scope`, `--repo`, `--limit`, `--cursor`, and `--all`.
|
|
282
286
|
|
|
283
287
|
### `arcanist sessions events <session-id>`
|
|
284
288
|
|
|
@@ -309,8 +313,11 @@ Renders a session transcript from the stored session export.
|
|
|
309
313
|
```bash
|
|
310
314
|
arcanist sessions transcript abc123
|
|
311
315
|
arcanist sessions transcript abc123 --json
|
|
316
|
+
arcanist sessions transcript abc123 --last 20
|
|
312
317
|
```
|
|
313
318
|
|
|
319
|
+
`--last <n>` renders only the last `n` stored transcript events after fetching the session export.
|
|
320
|
+
|
|
314
321
|
### `arcanist sessions watch <session-id>`
|
|
315
322
|
|
|
316
323
|
Watches session activity until the session becomes idle. For machine-readable streaming, prefer `arcanist sessions events --follow --json`.
|
|
@@ -327,6 +334,42 @@ arcanist sessions usage abc123
|
|
|
327
334
|
arcanist sessions usage abc123 --json
|
|
328
335
|
```
|
|
329
336
|
|
|
337
|
+
### `arcanist repos list`
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
arcanist repos list
|
|
341
|
+
arcanist repos list --json
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
JSON mode returns `{repos, ssoOrgs}`.
|
|
345
|
+
|
|
346
|
+
### `arcanist repos branches [repo]`
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
arcanist repos branches tryarcanist/arcanist
|
|
350
|
+
arcanist repos branches https://github.com/tryarcanist/arcanist --json
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
JSON mode returns `{branches}`.
|
|
354
|
+
|
|
355
|
+
### `arcanist repos skills [repo]`
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
arcanist repos skills tryarcanist/arcanist
|
|
359
|
+
arcanist repos skills https://github.com/tryarcanist/arcanist --json
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
JSON mode returns `{skills}`.
|
|
363
|
+
|
|
364
|
+
### `arcanist models list`
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
arcanist models list
|
|
368
|
+
arcanist models list --json
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
JSON mode returns `{models}`.
|
|
372
|
+
|
|
330
373
|
### `arcanist automations create <repo-url> [prompt]`
|
|
331
374
|
|
|
332
375
|
Creates a scheduled automation for a GitHub repo. Create/delete require a write-scoped CLI token; read-scoped tokens can list automations. The server validates repo access, normalizes cron expressions, dedupes retries by repo+cron+prompt, and caps each business at 20 enabled rules.
|
|
@@ -368,8 +411,11 @@ arcanist automations delete rule_123 --yes --json
|
|
|
368
411
|
arcanist tokens list
|
|
369
412
|
arcanist tokens list --limit 10 --json
|
|
370
413
|
arcanist tokens list --cursor <cursor> --json
|
|
414
|
+
arcanist tokens list --all --json
|
|
371
415
|
```
|
|
372
416
|
|
|
417
|
+
`--all` follows cursors until completion and returns `nextCursor: null` in JSON mode.
|
|
418
|
+
|
|
373
419
|
### `arcanist tokens create`
|
|
374
420
|
|
|
375
421
|
Creates a CLI token and prints the plaintext token exactly once.
|
package/dist/index.js
CHANGED
|
@@ -952,9 +952,9 @@ function formatModelLabel(raw, options = {}) {
|
|
|
952
952
|
const providerName = MODEL_PROVIDER_NAMES[selection.providerID];
|
|
953
953
|
return includeProvider ? `${providerName} / ${selection.modelID}` : selection.modelID;
|
|
954
954
|
}
|
|
955
|
-
function buildModelProviderGroups(
|
|
955
|
+
function buildModelProviderGroups(models2) {
|
|
956
956
|
const groups = /* @__PURE__ */ new Map();
|
|
957
|
-
for (const model of
|
|
957
|
+
for (const model of models2) {
|
|
958
958
|
let group = groups.get(model.provider);
|
|
959
959
|
if (!group) {
|
|
960
960
|
group = { id: model.provider, name: MODEL_PROVIDER_NAMES[model.provider], models: [] };
|
|
@@ -3380,6 +3380,31 @@ async function messageCommand(sessionId, promptArg, options = {}, command) {
|
|
|
3380
3380
|
console.log(`Message sent to session ${sessionId}.`);
|
|
3381
3381
|
}
|
|
3382
3382
|
|
|
3383
|
+
// src/commands/models.ts
|
|
3384
|
+
async function modelsListCommand(options = {}, command) {
|
|
3385
|
+
const { config } = resolveBusinessContext(command, options);
|
|
3386
|
+
const groups = await apiFetch(config, "/api/models");
|
|
3387
|
+
const models2 = groups.flatMap(
|
|
3388
|
+
(group) => group.models.map((model) => {
|
|
3389
|
+
const backends = model.backends ?? [group.id];
|
|
3390
|
+
return {
|
|
3391
|
+
...model,
|
|
3392
|
+
backend: group.id,
|
|
3393
|
+
provider: group.id,
|
|
3394
|
+
backends,
|
|
3395
|
+
default: backends.some(
|
|
3396
|
+
(backend) => DEFAULT_SESSION_START_MODEL_ID_BY_BACKEND[backend] === model.id
|
|
3397
|
+
)
|
|
3398
|
+
};
|
|
3399
|
+
})
|
|
3400
|
+
);
|
|
3401
|
+
emit(command, options, { models: models2 }, (payload) => {
|
|
3402
|
+
for (const model of payload.models) {
|
|
3403
|
+
console.log(`${String(model.id)} ${String(model.backend)}${model.default ? " default" : ""}`);
|
|
3404
|
+
}
|
|
3405
|
+
});
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3383
3408
|
// ../../shared/agent/verify-directive.ts
|
|
3384
3409
|
var MAX_TARGET_PR_URL_LENGTH = 500;
|
|
3385
3410
|
function normalizeGithubPullRequestUrl(rawUrl) {
|
|
@@ -3511,6 +3536,63 @@ async function qaCommand(prUrl, options, command) {
|
|
|
3511
3536
|
console.log(`Follow with: arcanist sessions events ${sessionId} --follow --json`);
|
|
3512
3537
|
}
|
|
3513
3538
|
|
|
3539
|
+
// src/commands/repos.ts
|
|
3540
|
+
import { execFileSync } from "child_process";
|
|
3541
|
+
function git(args) {
|
|
3542
|
+
try {
|
|
3543
|
+
return execFileSync("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
3544
|
+
} catch (err) {
|
|
3545
|
+
throw new CliError("user", `git ${args.join(" ")} failed: ${stringifyError(err)}`);
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
function currentRepo() {
|
|
3549
|
+
const parsed = parseGithubRepoFullName(git(["remote", "get-url", "origin"]));
|
|
3550
|
+
if (!parsed) throw new CliError("user", "origin remote must point at a GitHub repository");
|
|
3551
|
+
return { owner: parsed.owner, repo: parsed.repo };
|
|
3552
|
+
}
|
|
3553
|
+
function parseRepoArg2(value) {
|
|
3554
|
+
if (!value) return currentRepo();
|
|
3555
|
+
const parsed = parseGithubRepoFullName(value);
|
|
3556
|
+
if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
|
|
3557
|
+
return { owner: parsed.owner, repo: parsed.repo };
|
|
3558
|
+
}
|
|
3559
|
+
async function reposListCommand(options = {}, command) {
|
|
3560
|
+
const { config } = resolveBusinessContext(command, options);
|
|
3561
|
+
const payload = await apiFetch(
|
|
3562
|
+
config,
|
|
3563
|
+
"/api/repos"
|
|
3564
|
+
);
|
|
3565
|
+
emit(command, options, { repos: payload.repos, ssoOrgs: payload.ssoOrgs }, (data) => {
|
|
3566
|
+
for (const repo of data.repos) {
|
|
3567
|
+
console.log(`${String(repo.fullName ?? repo.name ?? repo.repo ?? "")} ${String(repo.defaultBranch ?? "")}`);
|
|
3568
|
+
}
|
|
3569
|
+
if (data.ssoOrgs.length > 0) console.log(`SSO withheld orgs: ${data.ssoOrgs.length}`);
|
|
3570
|
+
});
|
|
3571
|
+
}
|
|
3572
|
+
async function repoBranchesCommand(repoArg, options = {}, command) {
|
|
3573
|
+
const { config } = resolveBusinessContext(command, options);
|
|
3574
|
+
const repo = parseRepoArg2(repoArg);
|
|
3575
|
+
await apiFetch(config, "/api/repos");
|
|
3576
|
+
const payload = await apiFetch(
|
|
3577
|
+
config,
|
|
3578
|
+
`/api/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}/branches`
|
|
3579
|
+
);
|
|
3580
|
+
emit(command, options, payload, (data) => {
|
|
3581
|
+
for (const branch of data.branches) console.log(String(branch.name ?? branch.branch ?? ""));
|
|
3582
|
+
});
|
|
3583
|
+
}
|
|
3584
|
+
async function repoSkillsCommand(repoArg, options = {}, command) {
|
|
3585
|
+
const { config } = resolveBusinessContext(command, options);
|
|
3586
|
+
const repo = parseRepoArg2(repoArg);
|
|
3587
|
+
const payload = await apiFetch(
|
|
3588
|
+
config,
|
|
3589
|
+
`/api/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}/skills`
|
|
3590
|
+
);
|
|
3591
|
+
emit(command, options, payload, (data) => {
|
|
3592
|
+
for (const skill of data.skills) console.log(`${String(skill.name ?? "")} ${String(skill.description ?? "")}`);
|
|
3593
|
+
});
|
|
3594
|
+
}
|
|
3595
|
+
|
|
3514
3596
|
// src/commands/respond.ts
|
|
3515
3597
|
async function resolveAnswerInput(answerArg, options) {
|
|
3516
3598
|
const shouldReadStdin = options.answerStdin === true || answerArg === "-";
|
|
@@ -3563,7 +3645,7 @@ function parseRespondConflict(err) {
|
|
|
3563
3645
|
}
|
|
3564
3646
|
|
|
3565
3647
|
// src/commands/sandbox.ts
|
|
3566
|
-
import { execFileSync } from "child_process";
|
|
3648
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
3567
3649
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
3568
3650
|
import { dirname as dirname2 } from "path";
|
|
3569
3651
|
|
|
@@ -4099,34 +4181,34 @@ var DEFAULT_MANIFEST_PATH = SANDBOX_LAYER_MANIFEST_PATH;
|
|
|
4099
4181
|
var DEFAULT_POLL_INTERVAL_MS = 2500;
|
|
4100
4182
|
var MIN_POLL_INTERVAL_MS = 250;
|
|
4101
4183
|
var TERMINAL_BUILD_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "canceled"]);
|
|
4102
|
-
function
|
|
4184
|
+
function git2(args) {
|
|
4103
4185
|
try {
|
|
4104
|
-
return
|
|
4186
|
+
return execFileSync2("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
4105
4187
|
} catch (err) {
|
|
4106
4188
|
throw new CliError("user", `git ${args.join(" ")} failed: ${stringifyError(err)}`);
|
|
4107
4189
|
}
|
|
4108
4190
|
}
|
|
4109
|
-
function
|
|
4110
|
-
const remote =
|
|
4191
|
+
function currentRepo2() {
|
|
4192
|
+
const remote = git2(["remote", "get-url", "origin"]);
|
|
4111
4193
|
const parsed = parseGithubRepoFullName(remote);
|
|
4112
4194
|
if (!parsed) throw new CliError("user", "origin remote must point at a GitHub repository");
|
|
4113
4195
|
return { owner: parsed.owner, repo: parsed.repo };
|
|
4114
4196
|
}
|
|
4115
4197
|
function currentRef() {
|
|
4116
|
-
return
|
|
4198
|
+
return git2(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
4117
4199
|
}
|
|
4118
4200
|
function assertCleanAndPushed() {
|
|
4119
|
-
const dirty =
|
|
4201
|
+
const dirty = git2(["status", "--porcelain"]);
|
|
4120
4202
|
if (dirty) throw new CliError("user", "Sandbox layer source must be committed before build.");
|
|
4121
|
-
const upstream =
|
|
4122
|
-
const ahead =
|
|
4203
|
+
const upstream = git2(["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]);
|
|
4204
|
+
const ahead = git2(["rev-list", "--count", `${upstream}..HEAD`]);
|
|
4123
4205
|
if (ahead !== "0")
|
|
4124
4206
|
throw new CliError("user", "Current commit is not pushed; push before building the sandbox layer.");
|
|
4125
4207
|
}
|
|
4126
4208
|
function assertManifestExists(path) {
|
|
4127
4209
|
if (!existsSync2(path)) throw new CliError("user", `Missing sandbox manifest: ${path}`);
|
|
4128
4210
|
}
|
|
4129
|
-
function
|
|
4211
|
+
function parseRepoArg3(value, fallback) {
|
|
4130
4212
|
if (!value) return fallback();
|
|
4131
4213
|
const parsed = parseGithubRepoFullName(value);
|
|
4132
4214
|
if (!parsed) throw new CliError("user", "repo must be owner/name or a GitHub URL");
|
|
@@ -4303,7 +4385,7 @@ function formatValidationPayload(input) {
|
|
|
4303
4385
|
}
|
|
4304
4386
|
function nextBuildCommand(manifestPath) {
|
|
4305
4387
|
try {
|
|
4306
|
-
const repo =
|
|
4388
|
+
const repo = currentRepo2();
|
|
4307
4389
|
const manifestArg = manifestPath === DEFAULT_MANIFEST_PATH ? "" : ` --manifest ${manifestPath}`;
|
|
4308
4390
|
return `arcanist sandbox build ${repoPath(repo)} --wait --follow${manifestArg}`;
|
|
4309
4391
|
} catch {
|
|
@@ -4393,8 +4475,8 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
4393
4475
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
4394
4476
|
assertManifestExists(manifestPath);
|
|
4395
4477
|
if (!options.ref) assertCleanAndPushed();
|
|
4396
|
-
const sourceRepo =
|
|
4397
|
-
const targetRepo = options.targetRepo ?
|
|
4478
|
+
const sourceRepo = parseRepoArg3(sourceRepoArg, currentRepo2);
|
|
4479
|
+
const targetRepo = options.targetRepo ? parseRepoArg3(options.targetRepo, currentRepo2) : null;
|
|
4398
4480
|
const businessId = await resolveBusinessId(config, options);
|
|
4399
4481
|
const ref = options.ref ?? currentRef();
|
|
4400
4482
|
let payload;
|
|
@@ -4445,7 +4527,7 @@ async function sandboxBuildCommand(sourceRepoArg, options = {}, command) {
|
|
|
4445
4527
|
async function sandboxStatusCommand(repoArg, options = {}, command) {
|
|
4446
4528
|
const { config } = resolveBusinessContext(command, options);
|
|
4447
4529
|
const businessId = await resolveBusinessId(config, options);
|
|
4448
|
-
const repo =
|
|
4530
|
+
const repo = parseRepoArg3(repoArg, currentRepo2);
|
|
4449
4531
|
const payload = await apiFetch(
|
|
4450
4532
|
config,
|
|
4451
4533
|
`/api/businesses/${encodeURIComponent(businessId)}/repos/${encodeURIComponent(repo.owner)}/${encodeURIComponent(
|
|
@@ -4457,9 +4539,9 @@ async function sandboxStatusCommand(repoArg, options = {}, command) {
|
|
|
4457
4539
|
async function sandboxHistoryCommand(sourceRepoArg, options = {}, command) {
|
|
4458
4540
|
const { config } = resolveBusinessContext(command, options);
|
|
4459
4541
|
const businessId = await resolveBusinessId(config, options);
|
|
4460
|
-
const sourceRepo =
|
|
4542
|
+
const sourceRepo = parseRepoArg3(sourceRepoArg, currentRepo2);
|
|
4461
4543
|
const params = new URLSearchParams({ sourceRepo: repoPath(sourceRepo) });
|
|
4462
|
-
if (options.targetRepo) params.set("targetRepo", repoPath(
|
|
4544
|
+
if (options.targetRepo) params.set("targetRepo", repoPath(parseRepoArg3(options.targetRepo, currentRepo2)));
|
|
4463
4545
|
if (options.status) params.set("status", options.status);
|
|
4464
4546
|
if (options.limit) {
|
|
4465
4547
|
const limit = Number(options.limit);
|
|
@@ -4560,7 +4642,7 @@ async function sandboxLogsCommand(buildId, options = {}, command) {
|
|
|
4560
4642
|
async function sandboxAssignDefaultCommand(sourceRepoArg, options = {}, command) {
|
|
4561
4643
|
const { config } = resolveBusinessContext(command, options);
|
|
4562
4644
|
const businessId = await resolveBusinessId(config, options);
|
|
4563
|
-
const sourceRepo =
|
|
4645
|
+
const sourceRepo = parseRepoArg3(sourceRepoArg, currentRepo2);
|
|
4564
4646
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
4565
4647
|
const payload = await apiFetch(
|
|
4566
4648
|
config,
|
|
@@ -4577,8 +4659,8 @@ async function sandboxAssignDefaultCommand(sourceRepoArg, options = {}, command)
|
|
|
4577
4659
|
async function sandboxAssignRepoCommand(targetRepoArg, sourceRepoArg, options = {}, command) {
|
|
4578
4660
|
const { config } = resolveBusinessContext(command, options);
|
|
4579
4661
|
const businessId = await resolveBusinessId(config, options);
|
|
4580
|
-
const targetRepo =
|
|
4581
|
-
const sourceRepo =
|
|
4662
|
+
const targetRepo = parseRepoArg3(targetRepoArg, currentRepo2);
|
|
4663
|
+
const sourceRepo = parseRepoArg3(sourceRepoArg, currentRepo2);
|
|
4582
4664
|
const manifestPath = options.manifest ?? DEFAULT_MANIFEST_PATH;
|
|
4583
4665
|
const payload = await apiFetch(
|
|
4584
4666
|
config,
|
|
@@ -4616,7 +4698,7 @@ async function sandboxUnassignRepoCommand(targetRepoArg, options = {}, command)
|
|
|
4616
4698
|
}
|
|
4617
4699
|
const { config } = resolveBusinessContext(command, options);
|
|
4618
4700
|
const businessId = await resolveBusinessId(config, options);
|
|
4619
|
-
const targetRepo =
|
|
4701
|
+
const targetRepo = parseRepoArg3(targetRepoArg, currentRepo2);
|
|
4620
4702
|
if (!options.yes) await confirmOrThrow(`Clear sandbox layer assignment for ${repoPath(targetRepo)}?`);
|
|
4621
4703
|
const payload = await apiFetch(
|
|
4622
4704
|
config,
|
|
@@ -4629,20 +4711,35 @@ async function sandboxUnassignRepoCommand(targetRepoArg, options = {}, command)
|
|
|
4629
4711
|
}
|
|
4630
4712
|
|
|
4631
4713
|
// src/commands/sessions.ts
|
|
4714
|
+
var MAX_ALL_PAGES = 1e3;
|
|
4632
4715
|
async function listSessionsCommand(options, command) {
|
|
4633
4716
|
const runtime = getRuntimeOptions(command, options);
|
|
4634
4717
|
const config = requireConfig(runtime);
|
|
4635
|
-
const
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4718
|
+
const sessions2 = [];
|
|
4719
|
+
let cursor = options.cursor;
|
|
4720
|
+
let nextCursor = null;
|
|
4721
|
+
let pageCount = 0;
|
|
4722
|
+
do {
|
|
4723
|
+
pageCount += 1;
|
|
4724
|
+
if (options.all === true && pageCount > MAX_ALL_PAGES) {
|
|
4725
|
+
throw new CliError("user", `sessions list --all exceeded ${MAX_ALL_PAGES} pages without reaching the end.`);
|
|
4726
|
+
}
|
|
4727
|
+
const query = new URLSearchParams();
|
|
4728
|
+
if (options.status) query.set("status", options.status);
|
|
4729
|
+
if (options.scope) query.set("scope", options.scope);
|
|
4730
|
+
if (options.search) query.set("q", options.search);
|
|
4731
|
+
if (options.repo) query.set("repo", options.repo);
|
|
4732
|
+
if (options.limit) query.set("limit", options.limit);
|
|
4733
|
+
if (cursor) query.set("cursor", cursor);
|
|
4734
|
+
const payload2 = await apiFetch(
|
|
4735
|
+
config,
|
|
4736
|
+
`/api/sessions${query.size ? `?${query.toString()}` : ""}`
|
|
4737
|
+
);
|
|
4738
|
+
sessions2.push(...payload2.sessions);
|
|
4739
|
+
nextCursor = payload2.nextCursor;
|
|
4740
|
+
cursor = nextCursor ?? void 0;
|
|
4741
|
+
} while (options.all === true && nextCursor);
|
|
4742
|
+
const payload = { sessions: sessions2, nextCursor: options.all === true ? null : nextCursor };
|
|
4646
4743
|
if (isJson(command, options)) {
|
|
4647
4744
|
writeJson(payload);
|
|
4648
4745
|
return;
|
|
@@ -4796,7 +4893,7 @@ function parseStopBlocked(err) {
|
|
|
4796
4893
|
|
|
4797
4894
|
// src/commands/test-creds.ts
|
|
4798
4895
|
import { readFileSync as readFileSync4 } from "fs";
|
|
4799
|
-
function
|
|
4896
|
+
function parseRepoArg4(repo) {
|
|
4800
4897
|
const trimmed = repo.trim();
|
|
4801
4898
|
const slashIndex = trimmed.indexOf("/");
|
|
4802
4899
|
if (slashIndex <= 0 || slashIndex === trimmed.length - 1) {
|
|
@@ -4818,7 +4915,7 @@ function readValue(options) {
|
|
|
4818
4915
|
}
|
|
4819
4916
|
async function listTestCredentialsCommand(repo, options, command) {
|
|
4820
4917
|
const { config } = resolveBusinessContext(command, options);
|
|
4821
|
-
const repoArg =
|
|
4918
|
+
const repoArg = parseRepoArg4(repo);
|
|
4822
4919
|
const payload = await apiFetch(config, basePath(options.business, repoArg));
|
|
4823
4920
|
emit(command, options, payload, (listPayload) => {
|
|
4824
4921
|
if (listPayload.credentials.length === 0) {
|
|
@@ -4833,7 +4930,7 @@ async function listTestCredentialsCommand(repo, options, command) {
|
|
|
4833
4930
|
}
|
|
4834
4931
|
async function setTestCredentialCommand(repo, name, options, command) {
|
|
4835
4932
|
const { config } = resolveBusinessContext(command, options);
|
|
4836
|
-
const repoArg =
|
|
4933
|
+
const repoArg = parseRepoArg4(repo);
|
|
4837
4934
|
const value = readValue(options);
|
|
4838
4935
|
const payload = await apiFetch(
|
|
4839
4936
|
config,
|
|
@@ -4852,11 +4949,11 @@ async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
|
4852
4949
|
throw new CliError("user", "`test-creds delete --json` requires --yes.");
|
|
4853
4950
|
}
|
|
4854
4951
|
if (options.yes !== true) {
|
|
4855
|
-
const repoArg2 =
|
|
4952
|
+
const repoArg2 = parseRepoArg4(repo);
|
|
4856
4953
|
await confirmOrThrow(`Delete test credential '${name}' for ${repoArg2.owner}/${repoArg2.name}?`);
|
|
4857
4954
|
}
|
|
4858
4955
|
const { config } = resolveBusinessContext(command, options);
|
|
4859
|
-
const repoArg =
|
|
4956
|
+
const repoArg = parseRepoArg4(repo);
|
|
4860
4957
|
const payload = await apiFetch(
|
|
4861
4958
|
config,
|
|
4862
4959
|
`${basePath(options.business, repoArg)}/${encodeURIComponent(name)}`,
|
|
@@ -4871,15 +4968,30 @@ async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
|
4871
4968
|
}
|
|
4872
4969
|
|
|
4873
4970
|
// src/commands/tokens.ts
|
|
4971
|
+
var MAX_ALL_PAGES2 = 1e3;
|
|
4874
4972
|
async function listTokensCommand(options, command) {
|
|
4875
4973
|
const { config } = resolveBusinessContext(command, options);
|
|
4876
|
-
const
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4974
|
+
const data = [];
|
|
4975
|
+
let cursor = options.cursor;
|
|
4976
|
+
let nextCursor = null;
|
|
4977
|
+
let pageCount = 0;
|
|
4978
|
+
do {
|
|
4979
|
+
pageCount += 1;
|
|
4980
|
+
if (options.all === true && pageCount > MAX_ALL_PAGES2) {
|
|
4981
|
+
throw new CliError("user", `tokens list --all exceeded ${MAX_ALL_PAGES2} pages without reaching the end.`);
|
|
4982
|
+
}
|
|
4983
|
+
const query = new URLSearchParams();
|
|
4984
|
+
if (options.limit) query.set("limit", options.limit);
|
|
4985
|
+
if (cursor) query.set("cursor", cursor);
|
|
4986
|
+
const payload2 = await apiFetch(
|
|
4987
|
+
config,
|
|
4988
|
+
`/api/cli-tokens${query.size ? `?${query.toString()}` : ""}`
|
|
4989
|
+
);
|
|
4990
|
+
data.push(...payload2.data);
|
|
4991
|
+
nextCursor = payload2.nextCursor;
|
|
4992
|
+
cursor = nextCursor ?? void 0;
|
|
4993
|
+
} while (options.all === true && nextCursor);
|
|
4994
|
+
const payload = { data, nextCursor: options.all === true ? null : nextCursor };
|
|
4883
4995
|
emit(command, options, payload, (listPayload) => {
|
|
4884
4996
|
if (listPayload.data.length === 0) {
|
|
4885
4997
|
console.log("No CLI tokens found.");
|
|
@@ -4943,8 +5055,44 @@ async function revokeTokenCommand(tokenId, options, command) {
|
|
|
4943
5055
|
// src/commands/transcript.ts
|
|
4944
5056
|
async function transcriptCommand(sessionId, options, command) {
|
|
4945
5057
|
const { config } = resolveBusinessContext(command, options);
|
|
5058
|
+
const last = parseLast(options.last);
|
|
4946
5059
|
const exportData = await apiFetch(config, `/api/sessions/${sessionId}/export`);
|
|
4947
|
-
|
|
5060
|
+
const payload = last == null ? exportData : sliceTranscript(exportData, last, { includeBoundaryEvent: !isJson(command, options) });
|
|
5061
|
+
emit(command, options, payload, (data) => console.log(renderSessionTranscript(data)));
|
|
5062
|
+
}
|
|
5063
|
+
function parseLast(raw) {
|
|
5064
|
+
if (raw === void 0) return null;
|
|
5065
|
+
const parsed = Number(raw);
|
|
5066
|
+
if (!Number.isInteger(parsed) || parsed <= 0) throw new CliError("user", "--last must be a positive integer.");
|
|
5067
|
+
return parsed;
|
|
5068
|
+
}
|
|
5069
|
+
function sliceTranscript(exportData, last, options) {
|
|
5070
|
+
const start = Math.max(0, exportData.events.length - last);
|
|
5071
|
+
const boundary = findPromptBoundary(exportData.events, start);
|
|
5072
|
+
const tail = exportData.events.slice(start);
|
|
5073
|
+
const events = options.includeBoundaryEvent && boundary !== null && boundary < start ? [exportData.events[boundary], ...tail] : tail;
|
|
5074
|
+
const promptIds = promptIdsForEvents(events);
|
|
5075
|
+
if (boundary !== null) {
|
|
5076
|
+
const boundaryPromptId = getRawSessionEventPromptId(exportData.events[boundary]);
|
|
5077
|
+
if (boundaryPromptId) promptIds.add(boundaryPromptId);
|
|
5078
|
+
}
|
|
5079
|
+
const prompts = exportData.prompts.filter((prompt) => promptIds.has(prompt.id));
|
|
5080
|
+
return { ...exportData, prompts, events };
|
|
5081
|
+
}
|
|
5082
|
+
function findPromptBoundary(events, start) {
|
|
5083
|
+
for (let index = start; index >= 0; index--) {
|
|
5084
|
+
const event = events[index];
|
|
5085
|
+
if (event && getRawSessionEventKind(event) === "prompt_processing") return index;
|
|
5086
|
+
}
|
|
5087
|
+
return null;
|
|
5088
|
+
}
|
|
5089
|
+
function promptIdsForEvents(events) {
|
|
5090
|
+
const promptIds = /* @__PURE__ */ new Set();
|
|
5091
|
+
for (const event of events) {
|
|
5092
|
+
const promptId = getRawSessionEventPromptId(event);
|
|
5093
|
+
if (promptId) promptIds.add(promptId);
|
|
5094
|
+
}
|
|
5095
|
+
return promptIds;
|
|
4948
5096
|
}
|
|
4949
5097
|
|
|
4950
5098
|
// src/index.ts
|
|
@@ -5103,27 +5251,29 @@ JSON:
|
|
|
5103
5251
|
JSON mode returns the raw session payload. Result fields are at .session.prUrl, .session.publishedBranch, and .session.lastBranch.
|
|
5104
5252
|
`
|
|
5105
5253
|
).action((sessionId, options, command) => getSessionCommand(sessionId, options, command));
|
|
5106
|
-
sessions.command("list").description("List sessions").option("--status <status>", "Filter by session status").option("--scope <scope>", "Session scope: mine or business").option("--search <query>", "Search session titles and repo metadata").option("--repo <repo>", "Filter by repo metadata").option("--limit <n>", "Maximum sessions to return").option("--cursor <cursor>", "Pagination cursor").addHelpText(
|
|
5254
|
+
sessions.command("list").description("List sessions").option("--status <status>", "Filter by session status").option("--scope <scope>", "Session scope: mine or business").option("--search <query>", "Search session titles and repo metadata").option("--repo <repo>", "Filter by repo metadata").option("--limit <n>", "Maximum sessions to return").option("--cursor <cursor>", "Pagination cursor").option("--all", "Fetch all pages").addHelpText(
|
|
5107
5255
|
"after",
|
|
5108
5256
|
`
|
|
5109
5257
|
Examples:
|
|
5110
5258
|
arcanist sessions list
|
|
5111
5259
|
arcanist sessions list --status idle --json
|
|
5112
5260
|
arcanist sessions list --search "architect agent" --repo owner/repo
|
|
5261
|
+
arcanist sessions list --all --json
|
|
5113
5262
|
|
|
5114
5263
|
JSON:
|
|
5115
|
-
JSON mode returns {sessions, nextCursor}
|
|
5264
|
+
JSON mode returns {sessions, nextCursor}. --all follows cursors until nextCursor is null.
|
|
5116
5265
|
`
|
|
5117
5266
|
).action((options, command) => listSessionsCommand(options, command));
|
|
5118
|
-
sessions.command("search").description("Search sessions by title and repo metadata").argument("<query>", "Search query").option("--status <status>", "Filter by session status").option("--scope <scope>", "Session scope: mine or business").option("--repo <repo>", "Filter by repo metadata").option("--limit <n>", "Maximum sessions to return").option("--cursor <cursor>", "Pagination cursor").addHelpText(
|
|
5267
|
+
sessions.command("search").description("Search sessions by title and repo metadata").argument("<query>", "Search query").option("--status <status>", "Filter by session status").option("--scope <scope>", "Session scope: mine or business").option("--repo <repo>", "Filter by repo metadata").option("--limit <n>", "Maximum sessions to return").option("--cursor <cursor>", "Pagination cursor").option("--all", "Fetch all pages").addHelpText(
|
|
5119
5268
|
"after",
|
|
5120
5269
|
`
|
|
5121
5270
|
Examples:
|
|
5122
5271
|
arcanist sessions search "architect agent"
|
|
5123
5272
|
arcanist sessions search "mcp debugging" --repo owner/repo --json
|
|
5273
|
+
arcanist sessions search "repo access" --all --json
|
|
5124
5274
|
|
|
5125
5275
|
JSON:
|
|
5126
|
-
JSON mode returns {sessions, nextCursor}
|
|
5276
|
+
JSON mode returns {sessions, nextCursor}. --all follows cursors until nextCursor is null.
|
|
5127
5277
|
`
|
|
5128
5278
|
).action((query, options, command) => searchSessionsCommand(query, options, command));
|
|
5129
5279
|
sessions.command("events").description("Read or follow session replay events").argument("<session-id>", "Session ID").option("--after-sequence <n>", "Return events after this sequence").option("--after <n>", "Alias for --after-sequence").option("--before-sequence <n>", "Return events before this sequence").option("--before <n>", "Alias for --before-sequence").option("--prompt-id <id>", "Filter events by prompt ID").option("--limit <n>", "Maximum events to return").option("--follow", "Follow events until the session is idle").option("--poll-interval <ms>", "Polling interval in milliseconds", String(DEFAULT_WATCH_POLL_INTERVAL_MS)).addHelpText(
|
|
@@ -5141,12 +5291,13 @@ JSON mode without --follow returns one JSON object from /events/history: {events
|
|
|
5141
5291
|
Actionable follow types: pr_created/pr_updated use data.prUrl; question uses data.question; terminal types are prompt_completed, prompt_failed, and session_idle.
|
|
5142
5292
|
`
|
|
5143
5293
|
).action((sessionId, options, command) => sessionEventsCommand(sessionId, options, command));
|
|
5144
|
-
sessions.command("transcript").description("Render a session transcript").argument("<session-id>", "Session ID").addHelpText(
|
|
5294
|
+
sessions.command("transcript").description("Render a session transcript").argument("<session-id>", "Session ID").option("--last <n>", "Render only the last n transcript entries").addHelpText(
|
|
5145
5295
|
"after",
|
|
5146
5296
|
`
|
|
5147
5297
|
Examples:
|
|
5148
5298
|
arcanist sessions transcript <session-id>
|
|
5149
5299
|
arcanist sessions transcript <session-id> --json
|
|
5300
|
+
arcanist sessions transcript <session-id> --last 20
|
|
5150
5301
|
`
|
|
5151
5302
|
).action((sessionId, options, command) => transcriptCommand(sessionId, options, command));
|
|
5152
5303
|
sessions.command("watch").description("Watch session activity until it becomes idle").argument("<session-id>", "Session ID").option("--poll-interval <ms>", "Polling interval in milliseconds", String(DEFAULT_WATCH_POLL_INTERVAL_MS)).addHelpText(
|
|
@@ -5165,6 +5316,36 @@ Examples:
|
|
|
5165
5316
|
arcanist sessions usage <session-id> --json
|
|
5166
5317
|
`
|
|
5167
5318
|
).action((sessionId, options, command) => usageCommand(sessionId, options, command));
|
|
5319
|
+
var repos = program.command("repos").description("Repository discovery commands");
|
|
5320
|
+
repos.command("list").description("List accessible repositories").addHelpText(
|
|
5321
|
+
"after",
|
|
5322
|
+
`
|
|
5323
|
+
JSON:
|
|
5324
|
+
JSON mode returns {repos, ssoOrgs}
|
|
5325
|
+
`
|
|
5326
|
+
).action((options, command) => reposListCommand(options, command));
|
|
5327
|
+
repos.command("branches").description("List repository branches").argument("[repo]", "Repository owner/name; defaults to current git remote").addHelpText(
|
|
5328
|
+
"after",
|
|
5329
|
+
`
|
|
5330
|
+
JSON:
|
|
5331
|
+
JSON mode returns {branches}
|
|
5332
|
+
`
|
|
5333
|
+
).action((repo, options, command) => repoBranchesCommand(repo, options, command));
|
|
5334
|
+
repos.command("skills").description("List repository skills").argument("[repo]", "Repository owner/name; defaults to current git remote").addHelpText(
|
|
5335
|
+
"after",
|
|
5336
|
+
`
|
|
5337
|
+
JSON:
|
|
5338
|
+
JSON mode returns {skills}
|
|
5339
|
+
`
|
|
5340
|
+
).action((repo, options, command) => repoSkillsCommand(repo, options, command));
|
|
5341
|
+
var models = program.command("models").description("Model discovery commands");
|
|
5342
|
+
models.command("list").description("List session-start models").addHelpText(
|
|
5343
|
+
"after",
|
|
5344
|
+
`
|
|
5345
|
+
JSON:
|
|
5346
|
+
JSON mode returns {models}
|
|
5347
|
+
`
|
|
5348
|
+
).action((options, command) => modelsListCommand(options, command));
|
|
5168
5349
|
var automations = program.command("automations").description("Automation commands");
|
|
5169
5350
|
automations.command("create").description("Create a scheduled automation").argument("<repo-url>", "Repository URL").argument("[prompt]", "Prompt to run, or '-' to read stdin").requiredOption("--cron <expr>", "Cron expression").option("--name <name>", "Automation display name").option(
|
|
5170
5351
|
"--model <model>",
|
|
@@ -5236,7 +5417,7 @@ egress.command("add").description("Open or update a PR adding domains to the egr
|
|
|
5236
5417
|
egress.command("sync").description("Apply the merged egress allowlist source file to the runtime business policy").option("--business <id>", "Business ID; defaults to authenticated user's business").action((options, command) => egressSyncCommand(options, command));
|
|
5237
5418
|
egress.command("validate").description("Validate a local egress allowlist source file").argument("[path]", "Path to validate", ".arcanist/egress-allowlist.txt").action((path, options, command) => egressValidateCommand(path, options, command));
|
|
5238
5419
|
var tokens = program.command("tokens").description("CLI token commands");
|
|
5239
|
-
tokens.command("list").description("List CLI tokens").option("--limit <n>", "Maximum tokens to return").option("--cursor <cursor>", "Pagination cursor").action((options, command) => listTokensCommand(options, command));
|
|
5420
|
+
tokens.command("list").description("List CLI tokens").option("--limit <n>", "Maximum tokens to return").option("--cursor <cursor>", "Pagination cursor").option("--all", "Fetch all pages").action((options, command) => listTokensCommand(options, command));
|
|
5240
5421
|
tokens.command("create").description("Create a CLI token and print it once").option("--scope <scope>", "Token scope: read or write", "read").option("--expires-in-days <days>", "Token expiry in days").option("--idempotency-key <uuid>", "Request idempotency key for safe manual retries").addHelpText(
|
|
5241
5422
|
"after",
|
|
5242
5423
|
`
|