@driftless-sh/cli 0.1.39 → 0.1.42
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/index.js +101 -19
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -172,12 +172,21 @@ function isTransient(err) {
|
|
|
172
172
|
const msg = err instanceof Error ? err.message : String(err);
|
|
173
173
|
return !/HTTP 4\d\d:/.test(msg);
|
|
174
174
|
}
|
|
175
|
+
function defaultsFor(method) {
|
|
176
|
+
const isGet = method === "GET";
|
|
177
|
+
return {
|
|
178
|
+
timeoutMs: isGet ? 15e3 : 2e4,
|
|
179
|
+
retries: isGet ? 1 : 0
|
|
180
|
+
};
|
|
181
|
+
}
|
|
175
182
|
async function request(method, path, body, opts) {
|
|
176
|
-
const
|
|
183
|
+
const d = defaultsFor(method);
|
|
184
|
+
const timeoutMs = opts?.timeoutMs ?? d.timeoutMs;
|
|
185
|
+
const retries = opts?.retries ?? d.retries;
|
|
177
186
|
let lastErr;
|
|
178
187
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
179
188
|
try {
|
|
180
|
-
return await singleRequest(method, path, body,
|
|
189
|
+
return await singleRequest(method, path, body, timeoutMs);
|
|
181
190
|
} catch (e) {
|
|
182
191
|
lastErr = e;
|
|
183
192
|
if (attempt === retries || !isTransient(e)) break;
|
|
@@ -214633,7 +214642,7 @@ async function installSkillCommand() {
|
|
|
214633
214642
|
// src/commands/init.ts
|
|
214634
214643
|
function getVersion() {
|
|
214635
214644
|
try {
|
|
214636
|
-
return "0.1.
|
|
214645
|
+
return "0.1.42";
|
|
214637
214646
|
} catch {
|
|
214638
214647
|
return "0.0.0";
|
|
214639
214648
|
}
|
|
@@ -215315,11 +215324,13 @@ function renderSummaryHuman(items) {
|
|
|
215315
215324
|
const topicWidth = Math.max(...items.map((i) => i.topic.length), 12) + 2;
|
|
215316
215325
|
const badgeWidth = Math.max(...items.map((i) => formatBadges(i.badges).length), 8) + 2;
|
|
215317
215326
|
const statusWidth = Math.max(...items.map((i) => (i.classification?.status || "").length), 8) + 2;
|
|
215327
|
+
const kindWidth = Math.max(...items.map((i) => (i.classification?.kind || "").length), 8) + 2;
|
|
215318
215328
|
for (const item of items) {
|
|
215319
215329
|
const topic = pad(item.topic, topicWidth);
|
|
215320
215330
|
const badges = pad(formatBadges(item.badges), badgeWidth);
|
|
215321
215331
|
const status = pad(item.classification?.status || "", statusWidth);
|
|
215322
|
-
|
|
215332
|
+
const kind = pad(item.classification?.kind || "", kindWidth);
|
|
215333
|
+
console.log(`${topic}${status}${kind}${badges}${item.summary}`);
|
|
215323
215334
|
}
|
|
215324
215335
|
console.log(`
|
|
215325
215336
|
${items.length} topic${items.length === 1 ? "" : "s"}.`);
|
|
@@ -215468,6 +215479,41 @@ async function contextCommand(args) {
|
|
|
215468
215479
|
const { flags, positional } = parseArgs(args);
|
|
215469
215480
|
const subCommand = positional[0];
|
|
215470
215481
|
const isJSON = !!flags["json"];
|
|
215482
|
+
if (subCommand === "doctor") {
|
|
215483
|
+
let audit;
|
|
215484
|
+
try {
|
|
215485
|
+
audit = await api.get(`/workspaces/${workspaceSlug}/watchers/audit`);
|
|
215486
|
+
} catch (e) {
|
|
215487
|
+
console.error(`Failed to audit context: ${formatError(e)}`);
|
|
215488
|
+
process.exit(1);
|
|
215489
|
+
}
|
|
215490
|
+
if (isJSON) {
|
|
215491
|
+
emitJSON2(audit);
|
|
215492
|
+
const blocking2 = audit.orphaned.length + audit.repo_leak.length;
|
|
215493
|
+
process.exit(blocking2 > 0 ? 1 : 0);
|
|
215494
|
+
}
|
|
215495
|
+
const section = (label, items) => {
|
|
215496
|
+
if (items.length === 0) return;
|
|
215497
|
+
console.log(`
|
|
215498
|
+
${label} (${items.length}):`);
|
|
215499
|
+
for (const it of items) {
|
|
215500
|
+
console.log(` ${it.slug}${it.reason ? ` \u2014 ${it.reason}` : ""}`);
|
|
215501
|
+
}
|
|
215502
|
+
};
|
|
215503
|
+
console.log(`Context audit \u2014 ${audit.total} topic${audit.total === 1 ? "" : "s"}`);
|
|
215504
|
+
section("\u26A0 stale (code changed, context not updated)", audit.stale);
|
|
215505
|
+
section("\u2717 orphaned (repo deleted \u2014 hidden from agents)", audit.orphaned);
|
|
215506
|
+
section("\u2022 draft (suggested, never confirmed)", audit.draft);
|
|
215507
|
+
section("\u2022 docs-pending (doc anchored, never synced)", audit.docs_pending);
|
|
215508
|
+
section("\u2717 repo-leak (references unknown repo ids)", audit.repo_leak);
|
|
215509
|
+
const blocking = audit.orphaned.length + audit.repo_leak.length;
|
|
215510
|
+
if (audit.stale.length + audit.draft.length + audit.docs_pending.length + blocking === 0) {
|
|
215511
|
+
console.log("\nAll context is healthy.");
|
|
215512
|
+
} else if (blocking > 0) {
|
|
215513
|
+
console.log("\nResolve orphaned / repo-leak topics \u2014 they indicate deleted-repo fallout.");
|
|
215514
|
+
}
|
|
215515
|
+
process.exit(blocking > 0 ? 1 : 0);
|
|
215516
|
+
}
|
|
215471
215517
|
if (subCommand === "list") {
|
|
215472
215518
|
const query = [];
|
|
215473
215519
|
if (flags["stale"]) query.push("stale=true");
|
|
@@ -216167,12 +216213,14 @@ async function resolveRepo() {
|
|
|
216167
216213
|
const repo = result.repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
|
|
216168
216214
|
if (repo) {
|
|
216169
216215
|
saveWorkspaceToConfig(slug, meWorkspaceId);
|
|
216216
|
+
const source = slug === configSlug ? "config" : slug === meSlug ? "me" : "git-org";
|
|
216170
216217
|
return {
|
|
216171
216218
|
ok: true,
|
|
216172
216219
|
workspaceSlug: slug,
|
|
216173
216220
|
repoId: repo.id,
|
|
216174
216221
|
remote,
|
|
216175
|
-
hasScanBaseline: !!repo.scan_summary
|
|
216222
|
+
hasScanBaseline: !!repo.scan_summary,
|
|
216223
|
+
source
|
|
216176
216224
|
};
|
|
216177
216225
|
}
|
|
216178
216226
|
lastLinkedSlug = slug;
|
|
@@ -216255,21 +216303,26 @@ async function syncCommand(args) {
|
|
|
216255
216303
|
}
|
|
216256
216304
|
process.exit(1);
|
|
216257
216305
|
}
|
|
216258
|
-
const { workspaceSlug, repoId, remote } = resolution;
|
|
216259
|
-
const [eventsRes, staleTopics, violations, suggestedTopics] = await Promise.allSettled([
|
|
216306
|
+
const { workspaceSlug, repoId, remote, source } = resolution;
|
|
216307
|
+
const [eventsRes, staleTopics, violations, suggestedTopics, prActivity] = await Promise.allSettled([
|
|
216260
216308
|
api.get(`/workspaces/${workspaceSlug}/watchers/events?repo_id=${repoId}&limit=20`),
|
|
216261
216309
|
api.get(`/workspaces/${workspaceSlug}/watchers?stale=true&repo=${repoId}`),
|
|
216262
216310
|
api.get(`/workspaces/${workspaceSlug}/violations?repo_id=${repoId}&status=open&limit=20`),
|
|
216263
|
-
api.get(`/workspaces/${workspaceSlug}/watchers?suggested=true&repo=${repoId}`)
|
|
216311
|
+
api.get(`/workspaces/${workspaceSlug}/watchers?suggested=true&repo=${repoId}`),
|
|
216312
|
+
api.get(`/workspaces/${workspaceSlug}/pr-activity?repo_id=${repoId}&limit=5`)
|
|
216264
216313
|
]);
|
|
216265
216314
|
const events = eventsRes.status === "fulfilled" ? eventsRes.value.events ?? [] : [];
|
|
216266
216315
|
const stale = staleTopics.status === "fulfilled" ? staleTopics.value : [];
|
|
216267
216316
|
const rawViolations = violations.status === "fulfilled" ? violations.value : [];
|
|
216268
216317
|
const openViolations = Array.isArray(rawViolations) ? rawViolations : rawViolations.violations ?? rawViolations.items ?? [];
|
|
216269
216318
|
const suggested = suggestedTopics.status === "fulfilled" ? suggestedTopics.value : [];
|
|
216319
|
+
const rawPr = prActivity.status === "fulfilled" ? prActivity.value : [];
|
|
216320
|
+
const prs = Array.isArray(rawPr) ? rawPr : rawPr.items ?? [];
|
|
216270
216321
|
if (isJSON) {
|
|
216271
216322
|
emitJSON3({
|
|
216272
216323
|
repo: `${remote.org}/${remote.repo}`,
|
|
216324
|
+
workspace: workspaceSlug,
|
|
216325
|
+
resolved_via: source,
|
|
216273
216326
|
stale_topics: stale.map((t) => ({ topic: t.topic, reason: t.stale?.reason ?? null })),
|
|
216274
216327
|
recent_events: events.slice(0, 10).map((e) => ({
|
|
216275
216328
|
type: e.event_type,
|
|
@@ -216283,11 +216336,20 @@ async function syncCommand(args) {
|
|
|
216283
216336
|
status: v.status,
|
|
216284
216337
|
author: v.author
|
|
216285
216338
|
})),
|
|
216286
|
-
suggested_pending: suggested.length
|
|
216339
|
+
suggested_pending: suggested.length,
|
|
216340
|
+
pr_activity: prs.slice(0, 5).map((p) => ({
|
|
216341
|
+
pr_number: p.pr_number,
|
|
216342
|
+
title: p.pr_title,
|
|
216343
|
+
author: p.pr_author,
|
|
216344
|
+
risk: p.risk ?? null,
|
|
216345
|
+
topics: p.watcher_slugs ?? [],
|
|
216346
|
+
observed_at: p.observed_at
|
|
216347
|
+
}))
|
|
216287
216348
|
});
|
|
216288
216349
|
process.exit(0);
|
|
216289
216350
|
}
|
|
216290
|
-
console.log(`\u258C ${remote.org}/${remote.repo}
|
|
216351
|
+
console.log(`\u258C ${remote.org}/${remote.repo}`);
|
|
216352
|
+
console.log(` workspace: ${workspaceSlug} (resolved via ${source})
|
|
216291
216353
|
`);
|
|
216292
216354
|
if (stale.length > 0) {
|
|
216293
216355
|
console.log(`\u26A0 ${stale.length} stale topic${stale.length === 1 ? "" : "s"} \u2014 code changed, context not updated:`);
|
|
@@ -216315,12 +216377,21 @@ async function syncCommand(args) {
|
|
|
216315
216377
|
if (openViolations.length > 5) console.log(` ... and ${openViolations.length - 5} more`);
|
|
216316
216378
|
console.log("");
|
|
216317
216379
|
}
|
|
216380
|
+
if (prs.length > 0) {
|
|
216381
|
+
console.log(`PR activity (${prs.length}, via GitHub App \u2014 remote, not local):`);
|
|
216382
|
+
for (const p of prs.slice(0, 5)) {
|
|
216383
|
+
const risk = p.risk ? `[${p.risk}] ` : "";
|
|
216384
|
+
const topics = (p.watcher_slugs ?? []).length ? ` \u2192 ${(p.watcher_slugs ?? []).join(", ")}` : "";
|
|
216385
|
+
console.log(` ${risk}#${p.pr_number} ${String(p.pr_title ?? "").slice(0, 70)}${topics}`);
|
|
216386
|
+
}
|
|
216387
|
+
console.log("");
|
|
216388
|
+
}
|
|
216318
216389
|
if (suggested.length > 0) {
|
|
216319
216390
|
console.log(`${suggested.length} suggested topic${suggested.length === 1 ? "" : "s"} from init \u2014 review and confirm:`);
|
|
216320
216391
|
console.log(` driftless context list --suggested`);
|
|
216321
216392
|
console.log("");
|
|
216322
216393
|
}
|
|
216323
|
-
if (stale.length === 0 && events.length === 0 && openViolations.length === 0 && suggested.length === 0) {
|
|
216394
|
+
if (stale.length === 0 && events.length === 0 && openViolations.length === 0 && suggested.length === 0 && prs.length === 0) {
|
|
216324
216395
|
console.log("Cloud context is up to date. Nothing to sync.");
|
|
216325
216396
|
} else {
|
|
216326
216397
|
console.log("Review stale topics, then update context before touching code.");
|
|
@@ -216449,7 +216520,7 @@ async function doctorCommand() {
|
|
|
216449
216520
|
if (localOrNoKey) {
|
|
216450
216521
|
checks.push({ name: "Workspace", status: "warn", detail: "Skipped (local API or no key)" });
|
|
216451
216522
|
} else if (resolution.ok) {
|
|
216452
|
-
checks.push({ name: "Workspace", status: "ok", detail: resolution.workspaceSlug });
|
|
216523
|
+
checks.push({ name: "Workspace", status: "ok", detail: `${resolution.workspaceSlug} (source: ${resolution.source})` });
|
|
216453
216524
|
} else if (resolution.reason === "not_linked" && resolution.workspaceSlug) {
|
|
216454
216525
|
checks.push({ name: "Workspace", status: "ok", detail: resolution.workspaceSlug });
|
|
216455
216526
|
} else if (resolution.reason === "no_workspace" && resolution.diagnostics) {
|
|
@@ -216492,18 +216563,23 @@ async function doctorCommand() {
|
|
|
216492
216563
|
if (me?.slug) {
|
|
216493
216564
|
try {
|
|
216494
216565
|
const integrations = await api.get(`/workspaces/${me.slug}/integrations`);
|
|
216495
|
-
const ghApp = integrations.find((i) => i.type === "github_app"
|
|
216496
|
-
if (ghApp) {
|
|
216566
|
+
const ghApp = integrations.find((i) => i.type === "github_app");
|
|
216567
|
+
if (ghApp && ghApp.active) {
|
|
216497
216568
|
checks.push({ name: "GitHub App", status: "ok", detail: "Installed and active" });
|
|
216569
|
+
} else if (ghApp && !ghApp.active) {
|
|
216570
|
+
checks.push({ name: "GitHub App", status: "warn", detail: "Installed, activating \u2014 wait ~60s then re-run doctor" });
|
|
216498
216571
|
} else {
|
|
216499
|
-
checks.push({ name: "GitHub App", status: "warn", detail: "Not installed \u2014 driftless.icu/ecosystem \u2192 Settings \u2192 Integrations
|
|
216572
|
+
checks.push({ name: "GitHub App", status: "warn", detail: "Not installed \u2014 driftless.icu/ecosystem \u2192 Settings \u2192 Integrations" });
|
|
216500
216573
|
}
|
|
216501
216574
|
} catch (err) {
|
|
216502
|
-
const
|
|
216575
|
+
const msg = String(err?.message ?? "");
|
|
216576
|
+
const isNotFound = err?.status === 404 || /HTTP 404|404/.test(msg);
|
|
216503
216577
|
if (isNotFound) {
|
|
216504
216578
|
checks.push({ name: "GitHub App", status: "warn", detail: "Not installed \u2014 driftless.icu/ecosystem \u2192 Settings \u2192 Integrations" });
|
|
216579
|
+
} else if (/timed out|Connection failed|ENOTFOUND|ECONNREFUSED/i.test(msg)) {
|
|
216580
|
+
checks.push({ name: "GitHub App", status: "warn", detail: "Could not verify \u2014 API unreachable (network/timeout)" });
|
|
216505
216581
|
} else {
|
|
216506
|
-
checks.push({ name: "GitHub App", status: "warn", detail: "Could not verify \u2014
|
|
216582
|
+
checks.push({ name: "GitHub App", status: "warn", detail: "Could not verify \u2014 API key may lack permission for this workspace" });
|
|
216507
216583
|
}
|
|
216508
216584
|
}
|
|
216509
216585
|
} else {
|
|
@@ -216543,7 +216619,7 @@ function pad2(s, n) {
|
|
|
216543
216619
|
}
|
|
216544
216620
|
|
|
216545
216621
|
// src/index.ts
|
|
216546
|
-
var VERSION = "0.1.
|
|
216622
|
+
var VERSION = "0.1.42";
|
|
216547
216623
|
var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
|
|
216548
216624
|
|
|
216549
216625
|
Install: npm install -g @driftless-sh/cli
|
|
@@ -216588,6 +216664,7 @@ Context subcommands:
|
|
|
216588
216664
|
update <topic> --invariant ".." Append an invariant
|
|
216589
216665
|
update <topic> --check "..." Append a required check
|
|
216590
216666
|
delete <topic> Delete a topic
|
|
216667
|
+
doctor Audit context health (stale/orphaned/draft/docs-pending/repo-leak)
|
|
216591
216668
|
load --files "p1,p2" Match topics by file paths
|
|
216592
216669
|
|
|
216593
216670
|
Flags:
|
|
@@ -216688,6 +216765,7 @@ Subcommands:
|
|
|
216688
216765
|
sync <topic> --note "..." Add a note to a topic
|
|
216689
216766
|
update <topic> [opts] Update topic fields
|
|
216690
216767
|
delete <topic> Delete a topic
|
|
216768
|
+
doctor Audit context health (stale/orphaned/draft/docs-pending/repo-leak)
|
|
216691
216769
|
load --files "p1,p2,..." Match topics for given file paths
|
|
216692
216770
|
|
|
216693
216771
|
List filters:
|
|
@@ -216825,7 +216903,11 @@ async function main() {
|
|
|
216825
216903
|
}
|
|
216826
216904
|
}
|
|
216827
216905
|
main().catch((err) => {
|
|
216828
|
-
|
|
216906
|
+
const msg = err?.message ?? String(err);
|
|
216907
|
+
console.error("Error:", msg);
|
|
216908
|
+
if (/Connection failed|timed out|ENOTFOUND|ECONNREFUSED|Unauthorized|HTTP 401|HTTP 403|resolve workspace/i.test(msg)) {
|
|
216909
|
+
console.error("\nNext: run `driftless doctor` to diagnose auth/connectivity/workspace.");
|
|
216910
|
+
}
|
|
216829
216911
|
process.exit(1);
|
|
216830
216912
|
});
|
|
216831
216913
|
/*! Bundled license information:
|