@tryarcanist/cli 0.1.175 → 0.1.176
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 +36 -0
- package/dist/index.js +139 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -327,6 +327,42 @@ arcanist sessions usage abc123
|
|
|
327
327
|
arcanist sessions usage abc123 --json
|
|
328
328
|
```
|
|
329
329
|
|
|
330
|
+
### `arcanist repos list`
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
arcanist repos list
|
|
334
|
+
arcanist repos list --json
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
JSON mode returns `{repos, ssoOrgs}`.
|
|
338
|
+
|
|
339
|
+
### `arcanist repos branches [repo]`
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
arcanist repos branches tryarcanist/arcanist
|
|
343
|
+
arcanist repos branches https://github.com/tryarcanist/arcanist --json
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
JSON mode returns `{branches}`.
|
|
347
|
+
|
|
348
|
+
### `arcanist repos skills [repo]`
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
arcanist repos skills tryarcanist/arcanist
|
|
352
|
+
arcanist repos skills https://github.com/tryarcanist/arcanist --json
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
JSON mode returns `{skills}`.
|
|
356
|
+
|
|
357
|
+
### `arcanist models list`
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
arcanist models list
|
|
361
|
+
arcanist models list --json
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
JSON mode returns `{models}`.
|
|
365
|
+
|
|
330
366
|
### `arcanist automations create <repo-url> [prompt]`
|
|
331
367
|
|
|
332
368
|
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.
|
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,
|
|
@@ -4796,7 +4878,7 @@ function parseStopBlocked(err) {
|
|
|
4796
4878
|
|
|
4797
4879
|
// src/commands/test-creds.ts
|
|
4798
4880
|
import { readFileSync as readFileSync4 } from "fs";
|
|
4799
|
-
function
|
|
4881
|
+
function parseRepoArg4(repo) {
|
|
4800
4882
|
const trimmed = repo.trim();
|
|
4801
4883
|
const slashIndex = trimmed.indexOf("/");
|
|
4802
4884
|
if (slashIndex <= 0 || slashIndex === trimmed.length - 1) {
|
|
@@ -4818,7 +4900,7 @@ function readValue(options) {
|
|
|
4818
4900
|
}
|
|
4819
4901
|
async function listTestCredentialsCommand(repo, options, command) {
|
|
4820
4902
|
const { config } = resolveBusinessContext(command, options);
|
|
4821
|
-
const repoArg =
|
|
4903
|
+
const repoArg = parseRepoArg4(repo);
|
|
4822
4904
|
const payload = await apiFetch(config, basePath(options.business, repoArg));
|
|
4823
4905
|
emit(command, options, payload, (listPayload) => {
|
|
4824
4906
|
if (listPayload.credentials.length === 0) {
|
|
@@ -4833,7 +4915,7 @@ async function listTestCredentialsCommand(repo, options, command) {
|
|
|
4833
4915
|
}
|
|
4834
4916
|
async function setTestCredentialCommand(repo, name, options, command) {
|
|
4835
4917
|
const { config } = resolveBusinessContext(command, options);
|
|
4836
|
-
const repoArg =
|
|
4918
|
+
const repoArg = parseRepoArg4(repo);
|
|
4837
4919
|
const value = readValue(options);
|
|
4838
4920
|
const payload = await apiFetch(
|
|
4839
4921
|
config,
|
|
@@ -4852,11 +4934,11 @@ async function deleteTestCredentialCommand(repo, name, options, command) {
|
|
|
4852
4934
|
throw new CliError("user", "`test-creds delete --json` requires --yes.");
|
|
4853
4935
|
}
|
|
4854
4936
|
if (options.yes !== true) {
|
|
4855
|
-
const repoArg2 =
|
|
4937
|
+
const repoArg2 = parseRepoArg4(repo);
|
|
4856
4938
|
await confirmOrThrow(`Delete test credential '${name}' for ${repoArg2.owner}/${repoArg2.name}?`);
|
|
4857
4939
|
}
|
|
4858
4940
|
const { config } = resolveBusinessContext(command, options);
|
|
4859
|
-
const repoArg =
|
|
4941
|
+
const repoArg = parseRepoArg4(repo);
|
|
4860
4942
|
const payload = await apiFetch(
|
|
4861
4943
|
config,
|
|
4862
4944
|
`${basePath(options.business, repoArg)}/${encodeURIComponent(name)}`,
|
|
@@ -5165,6 +5247,36 @@ Examples:
|
|
|
5165
5247
|
arcanist sessions usage <session-id> --json
|
|
5166
5248
|
`
|
|
5167
5249
|
).action((sessionId, options, command) => usageCommand(sessionId, options, command));
|
|
5250
|
+
var repos = program.command("repos").description("Repository discovery commands");
|
|
5251
|
+
repos.command("list").description("List accessible repositories").addHelpText(
|
|
5252
|
+
"after",
|
|
5253
|
+
`
|
|
5254
|
+
JSON:
|
|
5255
|
+
JSON mode returns {repos, ssoOrgs}
|
|
5256
|
+
`
|
|
5257
|
+
).action((options, command) => reposListCommand(options, command));
|
|
5258
|
+
repos.command("branches").description("List repository branches").argument("[repo]", "Repository owner/name; defaults to current git remote").addHelpText(
|
|
5259
|
+
"after",
|
|
5260
|
+
`
|
|
5261
|
+
JSON:
|
|
5262
|
+
JSON mode returns {branches}
|
|
5263
|
+
`
|
|
5264
|
+
).action((repo, options, command) => repoBranchesCommand(repo, options, command));
|
|
5265
|
+
repos.command("skills").description("List repository skills").argument("[repo]", "Repository owner/name; defaults to current git remote").addHelpText(
|
|
5266
|
+
"after",
|
|
5267
|
+
`
|
|
5268
|
+
JSON:
|
|
5269
|
+
JSON mode returns {skills}
|
|
5270
|
+
`
|
|
5271
|
+
).action((repo, options, command) => repoSkillsCommand(repo, options, command));
|
|
5272
|
+
var models = program.command("models").description("Model discovery commands");
|
|
5273
|
+
models.command("list").description("List session-start models").addHelpText(
|
|
5274
|
+
"after",
|
|
5275
|
+
`
|
|
5276
|
+
JSON:
|
|
5277
|
+
JSON mode returns {models}
|
|
5278
|
+
`
|
|
5279
|
+
).action((options, command) => modelsListCommand(options, command));
|
|
5168
5280
|
var automations = program.command("automations").description("Automation commands");
|
|
5169
5281
|
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
5282
|
"--model <model>",
|