@driftless-sh/cli 0.1.34 → 0.1.36
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 +46 -24
- package/dist/index.js +244 -136
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -214567,7 +214567,7 @@ async function installSkillCommand() {
|
|
|
214567
214567
|
// src/commands/init.ts
|
|
214568
214568
|
function getVersion() {
|
|
214569
214569
|
try {
|
|
214570
|
-
return "0.1.
|
|
214570
|
+
return "0.1.36";
|
|
214571
214571
|
} catch {
|
|
214572
214572
|
return "0.0.0";
|
|
214573
214573
|
}
|
|
@@ -214866,6 +214866,15 @@ function step(label, value) {
|
|
|
214866
214866
|
}
|
|
214867
214867
|
async function initCommand(args) {
|
|
214868
214868
|
const version = getVersion();
|
|
214869
|
+
let srcOverride;
|
|
214870
|
+
let suggest = false;
|
|
214871
|
+
for (let i = 0; i < args.length; i++) {
|
|
214872
|
+
if (args[i] === "--src" && i + 1 < args.length && !args[i + 1].startsWith("--")) {
|
|
214873
|
+
srcOverride = args[++i];
|
|
214874
|
+
} else if (args[i] === "--suggest") {
|
|
214875
|
+
suggest = true;
|
|
214876
|
+
}
|
|
214877
|
+
}
|
|
214869
214878
|
console.log("D R I F T L E S S");
|
|
214870
214879
|
console.log(`v${version}`);
|
|
214871
214880
|
console.log("");
|
|
@@ -214901,10 +214910,12 @@ async function initCommand(args) {
|
|
|
214901
214910
|
const workspaceSlug = workspace.slug;
|
|
214902
214911
|
console.log(`Repository: ${remote.org}/${remote.repo}`);
|
|
214903
214912
|
console.log(`Workspace: ${workspaceSlug} \u2713`);
|
|
214913
|
+
if (srcOverride) console.log(`Scan root: ${srcOverride}`);
|
|
214904
214914
|
console.log("");
|
|
214905
214915
|
const cwd = process.cwd();
|
|
214916
|
+
const scanRoot = srcOverride ? (0, import_node_path4.resolve)(cwd, srcOverride) : cwd;
|
|
214906
214917
|
const scanStart = Date.now();
|
|
214907
|
-
const scanResult = await (0, import_scanner.scanRepo)(
|
|
214918
|
+
const scanResult = await (0, import_scanner.scanRepo)(scanRoot);
|
|
214908
214919
|
const scanMs = Date.now() - scanStart;
|
|
214909
214920
|
const earlyPatterns = analyzeCodePatterns(scanResult.components);
|
|
214910
214921
|
const summary = {
|
|
@@ -214924,6 +214935,12 @@ async function initCommand(args) {
|
|
|
214924
214935
|
}
|
|
214925
214936
|
};
|
|
214926
214937
|
const components = scanResult.components;
|
|
214938
|
+
if (components.length === 0) {
|
|
214939
|
+
console.warn(`\u26A0 0 components found in ${srcOverride ? `--src ${srcOverride}` : "repo root"}.`);
|
|
214940
|
+
console.warn(" For NestJS monorepos, point --src at the app source root, not the package root.");
|
|
214941
|
+
console.warn(" Example: driftless init --src apps/api/src (not apps/api)");
|
|
214942
|
+
console.warn(" Continuing with baseline upload \u2014 no components or relations will be registered.");
|
|
214943
|
+
}
|
|
214927
214944
|
const relationCount = components.reduce(
|
|
214928
214945
|
(sum, component) => sum + (component.relations?.length || 0),
|
|
214929
214946
|
0
|
|
@@ -214976,46 +214993,53 @@ async function initCommand(args) {
|
|
|
214976
214993
|
step("uploading components", "failed (continuing)");
|
|
214977
214994
|
}
|
|
214978
214995
|
}
|
|
214979
|
-
const patterns = earlyPatterns;
|
|
214980
|
-
const smartRules = generateSmartRules(repo.id, patterns);
|
|
214981
214996
|
let rulesCreated = 0;
|
|
214982
|
-
|
|
214983
|
-
|
|
214984
|
-
|
|
214985
|
-
|
|
214986
|
-
|
|
214997
|
+
if (suggest) {
|
|
214998
|
+
const smartRules = generateSmartRules(repo.id, earlyPatterns);
|
|
214999
|
+
let rulesSkipped = 0;
|
|
215000
|
+
for (const rule of smartRules) {
|
|
215001
|
+
try {
|
|
215002
|
+
const result = await api.post(`/workspaces/${workspaceSlug}/rules`, { ...rule, auto_suggested: true });
|
|
215003
|
+
if (result?.skipped) rulesSkipped++;
|
|
215004
|
+
else rulesCreated++;
|
|
215005
|
+
} catch {
|
|
214987
215006
|
rulesSkipped++;
|
|
214988
|
-
} else {
|
|
214989
|
-
rulesCreated++;
|
|
214990
215007
|
}
|
|
214991
|
-
} catch {
|
|
214992
|
-
rulesSkipped++;
|
|
214993
215008
|
}
|
|
215009
|
+
step("creating rules", `${rulesCreated} created \xB7 ${rulesSkipped} skipped \u2713`);
|
|
215010
|
+
} else {
|
|
215011
|
+
step("creating rules", "skipped (use --suggest to auto-generate)");
|
|
214994
215012
|
}
|
|
214995
|
-
step("creating rules", `${rulesCreated} created \xB7 ${rulesSkipped} skipped \u2713`);
|
|
214996
|
-
const smartWatchers = generateSmartWatchers(components);
|
|
214997
215013
|
let watchersCreated = 0;
|
|
214998
|
-
|
|
214999
|
-
|
|
215000
|
-
|
|
215001
|
-
|
|
215002
|
-
|
|
215003
|
-
|
|
215004
|
-
|
|
215005
|
-
|
|
215006
|
-
|
|
215007
|
-
|
|
215008
|
-
|
|
215009
|
-
|
|
215010
|
-
|
|
215011
|
-
|
|
215012
|
-
|
|
215013
|
-
|
|
215014
|
-
|
|
215015
|
-
|
|
215014
|
+
if (suggest) {
|
|
215015
|
+
const smartWatchers = generateSmartWatchers(components);
|
|
215016
|
+
let watchersSkipped = 0;
|
|
215017
|
+
for (const watcher of smartWatchers) {
|
|
215018
|
+
try {
|
|
215019
|
+
await api.post(`/workspaces/${workspaceSlug}/watchers`, {
|
|
215020
|
+
name: watcher.name,
|
|
215021
|
+
what: watcher.what,
|
|
215022
|
+
how: watcher.how,
|
|
215023
|
+
pattern: watcher.pattern,
|
|
215024
|
+
decisions: watcher.decisions,
|
|
215025
|
+
gotchas: watcher.gotchas,
|
|
215026
|
+
ownership: watcher.ownership,
|
|
215027
|
+
where_repos: [repo.id],
|
|
215028
|
+
created_by: "driftless-init",
|
|
215029
|
+
suggested: true,
|
|
215030
|
+
origin: "auto",
|
|
215031
|
+
status: "draft",
|
|
215032
|
+
kind: "domain-map"
|
|
215033
|
+
});
|
|
215034
|
+
watchersCreated++;
|
|
215035
|
+
} catch {
|
|
215036
|
+
watchersSkipped++;
|
|
215037
|
+
}
|
|
215016
215038
|
}
|
|
215039
|
+
step("creating context", `${watchersCreated} topics \u2713`);
|
|
215040
|
+
} else {
|
|
215041
|
+
step("creating context", "skipped (use --suggest to auto-generate)");
|
|
215017
215042
|
}
|
|
215018
|
-
step("creating context", `${watchersCreated} watchers \u2713`);
|
|
215019
215043
|
const existingDocs = detectExistingDocs(cwd);
|
|
215020
215044
|
step("detecting docs", `${existingDocs.length} found (not auto-synced)`);
|
|
215021
215045
|
let skillLine = "skipped";
|
|
@@ -215029,8 +215053,8 @@ async function initCommand(args) {
|
|
|
215029
215053
|
step("installing skill", skillLine);
|
|
215030
215054
|
console.log("");
|
|
215031
215055
|
console.log("\u2713 repo context bootstrapped");
|
|
215032
|
-
console.log(` \u251C\u2500
|
|
215033
|
-
console.log(` \u251C\u2500 rules ${rulesCreated}`);
|
|
215056
|
+
console.log(` \u251C\u2500 topics ${suggest ? watchersCreated : "(use --suggest to generate)"}`);
|
|
215057
|
+
console.log(` \u251C\u2500 rules ${suggest ? rulesCreated : "(use --suggest to generate)"}`);
|
|
215034
215058
|
console.log(` \u251C\u2500 docs found ${existingDocs.length} (ask your agent to sync them)`);
|
|
215035
215059
|
console.log(` \u251C\u2500 components ${components.length}`);
|
|
215036
215060
|
console.log(` \u2514\u2500 relations ${relationCount}`);
|
|
@@ -215212,10 +215236,12 @@ function renderSummaryHuman(items) {
|
|
|
215212
215236
|
}
|
|
215213
215237
|
const topicWidth = Math.max(...items.map((i) => i.topic.length), 12) + 2;
|
|
215214
215238
|
const badgeWidth = Math.max(...items.map((i) => formatBadges(i.badges).length), 8) + 2;
|
|
215239
|
+
const statusWidth = Math.max(...items.map((i) => (i.classification?.status || "").length), 8) + 2;
|
|
215215
215240
|
for (const item of items) {
|
|
215216
215241
|
const topic = pad(item.topic, topicWidth);
|
|
215217
215242
|
const badges = pad(formatBadges(item.badges), badgeWidth);
|
|
215218
|
-
|
|
215243
|
+
const status = pad(item.classification?.status || "", statusWidth);
|
|
215244
|
+
console.log(`${topic}${status}${badges}${item.summary}`);
|
|
215219
215245
|
}
|
|
215220
215246
|
console.log(`
|
|
215221
215247
|
${items.length} topic${items.length === 1 ? "" : "s"}.`);
|
|
@@ -215224,11 +215250,19 @@ function renderContextHuman(ctx) {
|
|
|
215224
215250
|
console.log(`\u258C ${ctx.topic}`);
|
|
215225
215251
|
console.log(` ${ctx.summary}`);
|
|
215226
215252
|
if (ctx.version !== void 0) console.log(` version: ${ctx.version}`);
|
|
215253
|
+
if (ctx.classification) {
|
|
215254
|
+
const { origin, status, kind } = ctx.classification;
|
|
215255
|
+
console.log(` origin: ${origin} status: ${status} kind: ${kind}`);
|
|
215256
|
+
}
|
|
215227
215257
|
if (ctx.stale.is_stale) {
|
|
215228
215258
|
console.log(`
|
|
215229
215259
|
\u26A0 STALE: ${ctx.stale.reason || "Context may be outdated"}`);
|
|
215230
215260
|
console.log(` \u2192 driftless context update ${ctx.topic} --what "..." --how "..."`);
|
|
215231
215261
|
}
|
|
215262
|
+
if (ctx.metadata.created_by === "driftless-init") {
|
|
215263
|
+
console.log(`
|
|
215264
|
+
\u24D8 auto-suggested by init \u2014 confirm with \`driftless context update ${ctx.topic} --what "..."\``);
|
|
215265
|
+
}
|
|
215232
215266
|
console.log("");
|
|
215233
215267
|
if (ctx.description.what) console.log(` what: ${ctx.description.what}`);
|
|
215234
215268
|
if (ctx.description.how) console.log(` how: ${ctx.description.how}`);
|
|
@@ -215357,9 +215391,23 @@ async function contextCommand(args) {
|
|
|
215357
215391
|
if (flags["docs"]) query.push("docs=true");
|
|
215358
215392
|
if (flags["auto"]) query.push("auto=true");
|
|
215359
215393
|
if (flags["manual"]) query.push("manual=true");
|
|
215360
|
-
if (flags["repo"]) query.push(`repo=${encodeURIComponent(flags["repo"])}`);
|
|
215361
215394
|
if (flags["suggested"]) query.push("suggested=true");
|
|
215362
215395
|
else query.push("suggested=false");
|
|
215396
|
+
if (flags["status"]) query.push(`status=${encodeURIComponent(flags["status"])}`);
|
|
215397
|
+
if (flags["kind"]) query.push(`kind=${encodeURIComponent(flags["kind"])}`);
|
|
215398
|
+
if (flags["repo"]) {
|
|
215399
|
+
query.push(`repo=${encodeURIComponent(flags["repo"])}`);
|
|
215400
|
+
} else if (flags["auto"]) {
|
|
215401
|
+
const currentRemote = getGitRemote();
|
|
215402
|
+
if (currentRemote) {
|
|
215403
|
+
try {
|
|
215404
|
+
const repos = await api.get(`/workspaces/${workspaceSlug}/repos`);
|
|
215405
|
+
const match = repos.find((r) => r.github_org === currentRemote.org && r.github_repo === currentRemote.repo);
|
|
215406
|
+
if (match) query.push(`repo=${encodeURIComponent(match.id)}`);
|
|
215407
|
+
} catch {
|
|
215408
|
+
}
|
|
215409
|
+
}
|
|
215410
|
+
}
|
|
215363
215411
|
const qs = query.length > 0 ? `?${query.join("&")}` : "";
|
|
215364
215412
|
try {
|
|
215365
215413
|
const summaries = await api.get(`/workspaces/${workspaceSlug}/watchers${qs}`);
|
|
@@ -215494,10 +215542,17 @@ async function contextCommand(args) {
|
|
|
215494
215542
|
}
|
|
215495
215543
|
}
|
|
215496
215544
|
const pattern = flags["pattern"];
|
|
215497
|
-
if (pattern
|
|
215545
|
+
if (pattern) {
|
|
215546
|
+
if (pattern.includes(",")) {
|
|
215547
|
+
console.error(`Error: pattern "${pattern}" contains a comma. Only one glob is accepted per topic.`);
|
|
215548
|
+
console.error(" Create separate topics for each path, or use a broader glob.");
|
|
215549
|
+
process.exit(1);
|
|
215550
|
+
}
|
|
215498
215551
|
const localCount = countLocalFilesMatching(pattern);
|
|
215499
215552
|
if (localCount === 0) {
|
|
215500
|
-
console.error(`
|
|
215553
|
+
console.error(`Error: pattern "${pattern}" matches 0 files locally. Topic not created.`);
|
|
215554
|
+
console.error(" Verify the glob against your repo root, or use --where for explicit file paths.");
|
|
215555
|
+
process.exit(1);
|
|
215501
215556
|
}
|
|
215502
215557
|
}
|
|
215503
215558
|
try {
|
|
@@ -215510,7 +215565,9 @@ async function contextCommand(args) {
|
|
|
215510
215565
|
decisions: flags["decisions"],
|
|
215511
215566
|
gotchas: flags["gotchas"],
|
|
215512
215567
|
ownership: flags["ownership"],
|
|
215513
|
-
file_content: fileContent
|
|
215568
|
+
file_content: fileContent,
|
|
215569
|
+
status: flags["status"],
|
|
215570
|
+
kind: flags["kind"]
|
|
215514
215571
|
});
|
|
215515
215572
|
analyticsEvent("context_created", workspaceSlug, { topic: name, has_pattern: !!pattern, has_what: !!flags["what"] });
|
|
215516
215573
|
if (isJSON) {
|
|
@@ -215541,12 +215598,21 @@ async function contextCommand(args) {
|
|
|
215541
215598
|
if (flags["decisions"]) updates.decisions = flags["decisions"];
|
|
215542
215599
|
if (flags["gotchas"]) updates.gotchas = flags["gotchas"];
|
|
215543
215600
|
if (flags["ownership"]) updates.ownership = flags["ownership"];
|
|
215544
|
-
|
|
215545
|
-
|
|
215546
|
-
const
|
|
215547
|
-
|
|
215548
|
-
|
|
215549
|
-
|
|
215601
|
+
if (flags["status"]) updates.status = flags["status"];
|
|
215602
|
+
const gotchaValues = [];
|
|
215603
|
+
const invariantValues = [];
|
|
215604
|
+
const checkValues = [];
|
|
215605
|
+
for (let i = 0; i < args.length - 1; i++) {
|
|
215606
|
+
const a = args[i];
|
|
215607
|
+
const b = args[i + 1];
|
|
215608
|
+
if (!b || b.startsWith("--")) continue;
|
|
215609
|
+
if (a === "--gotcha" || a === "--gotchas_append") gotchaValues.push(b);
|
|
215610
|
+
if (a === "--invariant") invariantValues.push(b);
|
|
215611
|
+
if (a === "--check") checkValues.push(b);
|
|
215612
|
+
}
|
|
215613
|
+
if (gotchaValues.length > 0) updates._append_gotchas = gotchaValues.join("\n");
|
|
215614
|
+
if (invariantValues.length > 0) updates._append_invariants = invariantValues.join("\n");
|
|
215615
|
+
if (checkValues.length > 0) updates._append_required_checks = checkValues.join("\n");
|
|
215550
215616
|
const enforceFlag = flags["enforce"];
|
|
215551
215617
|
if (enforceFlag && typeof enforceFlag === "string") {
|
|
215552
215618
|
updates._enforce_rule = enforceFlag;
|
|
@@ -215554,16 +215620,18 @@ async function contextCommand(args) {
|
|
|
215554
215620
|
updates._enforce_rule = true;
|
|
215555
215621
|
}
|
|
215556
215622
|
const eventParts = [];
|
|
215557
|
-
if (
|
|
215558
|
-
if (
|
|
215559
|
-
if (
|
|
215623
|
+
if (gotchaValues.length > 0) eventParts.push(`gotcha appended`);
|
|
215624
|
+
if (invariantValues.length > 0) eventParts.push(`invariant appended`);
|
|
215625
|
+
if (checkValues.length > 0) eventParts.push(`check appended`);
|
|
215560
215626
|
if (enforceFlag) eventParts.push(`enforce requested`);
|
|
215561
215627
|
if (eventParts.length > 0) {
|
|
215562
215628
|
updates._event_detail = `CONTEXT_UPDATED: ${eventParts.join(", ")}`;
|
|
215563
215629
|
}
|
|
215564
215630
|
if (Object.keys(updates).length === 0) {
|
|
215565
|
-
console.error("No fields to update.
|
|
215566
|
-
console.error(
|
|
215631
|
+
console.error("No fields to update.");
|
|
215632
|
+
console.error("Scalar: --what, --how, --pattern, --decisions, --gotchas, --ownership");
|
|
215633
|
+
console.error("Append: --gotcha (repeatable), --invariant (repeatable), --check (repeatable)");
|
|
215634
|
+
console.error('Enforce: --enforce "<rule-description>"');
|
|
215567
215635
|
process.exit(1);
|
|
215568
215636
|
}
|
|
215569
215637
|
const currentRemote = getGitRemote();
|
|
@@ -215672,7 +215740,12 @@ async function contextCommand(args) {
|
|
|
215672
215740
|
}
|
|
215673
215741
|
const updates = { last_updated: /* @__PURE__ */ new Date() };
|
|
215674
215742
|
if (fileContent) updates.file_content = fileContent;
|
|
215675
|
-
if (docFlag)
|
|
215743
|
+
if (docFlag) {
|
|
215744
|
+
updates.anchored_doc_path = docFlag;
|
|
215745
|
+
updates.origin = "doc";
|
|
215746
|
+
updates.status = "draft";
|
|
215747
|
+
updates.kind = "docs-pending";
|
|
215748
|
+
}
|
|
215676
215749
|
if (noteFlag) updates.decisions = noteFlag;
|
|
215677
215750
|
if (filesFlag) updates.where_files = filesFlag.split(",").map((f) => f.trim()).filter(Boolean);
|
|
215678
215751
|
if (patternFlag) updates.pattern = patternFlag;
|
|
@@ -215952,6 +216025,39 @@ Run 'driftless help context' for full reference.`);
|
|
|
215952
216025
|
|
|
215953
216026
|
// src/commands/sync.ts
|
|
215954
216027
|
init_api_client();
|
|
216028
|
+
|
|
216029
|
+
// src/repo-resolver.ts
|
|
216030
|
+
init_api_client();
|
|
216031
|
+
function notLinkedMessage(remote, workspaceSlug) {
|
|
216032
|
+
return `Repo '${remote.org}/${remote.repo}' is not registered in workspace '${workspaceSlug}'. Run \`driftless init\` to register it.`;
|
|
216033
|
+
}
|
|
216034
|
+
async function resolveRepo() {
|
|
216035
|
+
const remote = getGitRemote();
|
|
216036
|
+
if (!remote) return { ok: false, reason: "no_remote" };
|
|
216037
|
+
let workspaceSlug;
|
|
216038
|
+
try {
|
|
216039
|
+
const me = await api.get("/me");
|
|
216040
|
+
workspaceSlug = me?.slug;
|
|
216041
|
+
} catch {
|
|
216042
|
+
}
|
|
216043
|
+
if (!workspaceSlug) return { ok: false, reason: "no_workspace", remote };
|
|
216044
|
+
let repo;
|
|
216045
|
+
try {
|
|
216046
|
+
const repos = await api.get(`/workspaces/${workspaceSlug}/repos`);
|
|
216047
|
+
repo = repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
|
|
216048
|
+
} catch {
|
|
216049
|
+
}
|
|
216050
|
+
if (!repo) return { ok: false, reason: "not_linked", workspaceSlug, remote };
|
|
216051
|
+
return {
|
|
216052
|
+
ok: true,
|
|
216053
|
+
workspaceSlug,
|
|
216054
|
+
repoId: repo.id,
|
|
216055
|
+
remote,
|
|
216056
|
+
hasScanBaseline: !!repo.scan_summary
|
|
216057
|
+
};
|
|
216058
|
+
}
|
|
216059
|
+
|
|
216060
|
+
// src/commands/sync.ts
|
|
215955
216061
|
function parseArgs2(args) {
|
|
215956
216062
|
const flags = {};
|
|
215957
216063
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -215969,19 +216075,6 @@ function parseArgs2(args) {
|
|
|
215969
216075
|
function emitJSON3(data) {
|
|
215970
216076
|
console.log(JSON.stringify(data, null, 2));
|
|
215971
216077
|
}
|
|
215972
|
-
async function resolveWorkspaceSlug2() {
|
|
215973
|
-
try {
|
|
215974
|
-
const me = await api.get("/me");
|
|
215975
|
-
if (me?.slug) return me.slug;
|
|
215976
|
-
} catch {
|
|
215977
|
-
}
|
|
215978
|
-
const remote = getGitRemote();
|
|
215979
|
-
if (!remote) {
|
|
215980
|
-
console.error("Error: no git remote found.");
|
|
215981
|
-
process.exit(1);
|
|
215982
|
-
}
|
|
215983
|
-
return remote.org;
|
|
215984
|
-
}
|
|
215985
216078
|
async function syncCommand(args) {
|
|
215986
216079
|
if (!isGitRepo()) {
|
|
215987
216080
|
console.error("Error: not a git repository.");
|
|
@@ -215989,33 +216082,23 @@ async function syncCommand(args) {
|
|
|
215989
216082
|
}
|
|
215990
216083
|
const { flags } = parseArgs2(args);
|
|
215991
216084
|
const isJSON = !!flags["json"];
|
|
215992
|
-
const
|
|
215993
|
-
if (!
|
|
215994
|
-
|
|
215995
|
-
process.exit(1);
|
|
215996
|
-
}
|
|
215997
|
-
const workspaceSlug = await resolveWorkspaceSlug2();
|
|
215998
|
-
let repoId = null;
|
|
215999
|
-
try {
|
|
216000
|
-
const repos = await api.get(`/workspaces/${workspaceSlug}/repos`);
|
|
216001
|
-
const match = repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
|
|
216002
|
-
if (match) repoId = match.id;
|
|
216003
|
-
} catch {
|
|
216004
|
-
}
|
|
216005
|
-
if (!repoId) {
|
|
216085
|
+
const resolution = await resolveRepo();
|
|
216086
|
+
if (!resolution.ok) {
|
|
216087
|
+
const msg = resolution.reason === "no_remote" ? "Error: no git remote configured." : resolution.reason === "no_workspace" ? "Error: could not resolve workspace. Run `driftless doctor` to diagnose." : notLinkedMessage(resolution.remote, resolution.workspaceSlug);
|
|
216006
216088
|
if (!isJSON) {
|
|
216007
|
-
console.log(
|
|
216008
|
-
console.log("Run driftless
|
|
216089
|
+
console.log(`\u26A0 ${msg}`);
|
|
216090
|
+
if (resolution.reason !== "no_remote") console.log(" Run `driftless doctor` to diagnose.");
|
|
216009
216091
|
} else {
|
|
216010
|
-
emitJSON3({ error:
|
|
216092
|
+
emitJSON3({ error: resolution.reason, message: msg });
|
|
216011
216093
|
}
|
|
216012
|
-
process.exit(
|
|
216094
|
+
process.exit(1);
|
|
216013
216095
|
}
|
|
216096
|
+
const { workspaceSlug, repoId, remote } = resolution;
|
|
216014
216097
|
const [eventsRes, staleTopics, violations, suggestedTopics] = await Promise.allSettled([
|
|
216015
216098
|
api.get(`/workspaces/${workspaceSlug}/watchers/events?repo_id=${repoId}&limit=20`),
|
|
216016
216099
|
api.get(`/workspaces/${workspaceSlug}/watchers?stale=true&repo=${repoId}`),
|
|
216017
216100
|
api.get(`/workspaces/${workspaceSlug}/violations?repo_id=${repoId}&status=open&limit=20`),
|
|
216018
|
-
api.get(`/workspaces/${workspaceSlug}/watchers?suggested=true`)
|
|
216101
|
+
api.get(`/workspaces/${workspaceSlug}/watchers?suggested=true&repo=${repoId}`)
|
|
216019
216102
|
]);
|
|
216020
216103
|
const events = eventsRes.status === "fulfilled" ? eventsRes.value.events ?? [] : [];
|
|
216021
216104
|
const stale = staleTopics.status === "fulfilled" ? staleTopics.value : [];
|
|
@@ -216210,38 +216293,22 @@ async function doctorCommand() {
|
|
|
216210
216293
|
} else {
|
|
216211
216294
|
checks.push({ name: "Workspace", status: "warn", detail: "Skipped (local API or no key)" });
|
|
216212
216295
|
}
|
|
216213
|
-
|
|
216214
|
-
|
|
216215
|
-
|
|
216216
|
-
|
|
216217
|
-
|
|
216218
|
-
|
|
216219
|
-
|
|
216220
|
-
|
|
216221
|
-
|
|
216222
|
-
|
|
216223
|
-
|
|
216224
|
-
|
|
216225
|
-
}
|
|
216296
|
+
const resolution = await resolveRepo();
|
|
216297
|
+
if (resolution.ok) {
|
|
216298
|
+
checks.push({ name: "Repo linked", status: "ok", detail: `${resolution.remote.org}/${resolution.remote.repo}` });
|
|
216299
|
+
checks.push({
|
|
216300
|
+
name: "Baseline",
|
|
216301
|
+
status: resolution.hasScanBaseline ? "ok" : "warn",
|
|
216302
|
+
detail: resolution.hasScanBaseline ? "Scan data present" : "No scan baseline. Run `driftless init`"
|
|
216303
|
+
});
|
|
216304
|
+
} else if (resolution.reason === "not_linked") {
|
|
216305
|
+
checks.push({ name: "Repo linked", status: "warn", detail: notLinkedMessage(resolution.remote, resolution.workspaceSlug) });
|
|
216306
|
+
checks.push({ name: "Baseline", status: "warn", detail: "Could not verify (repo not registered)" });
|
|
216307
|
+
} else if (resolution.reason === "no_workspace") {
|
|
216308
|
+
checks.push({ name: "Repo linked", status: "warn", detail: "Could not verify (workspace unknown)" });
|
|
216309
|
+
checks.push({ name: "Baseline", status: "warn", detail: "Could not verify (workspace unknown)" });
|
|
216226
216310
|
} else {
|
|
216227
216311
|
checks.push({ name: "Repo linked", status: "warn", detail: "Skipped (no git remote)" });
|
|
216228
|
-
}
|
|
216229
|
-
if (remote) {
|
|
216230
|
-
const me = await getMe();
|
|
216231
|
-
if (me?.slug) {
|
|
216232
|
-
const repos = await getRepos(me.slug);
|
|
216233
|
-
const linked = repos?.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
|
|
216234
|
-
if (linked?.scan_summary) {
|
|
216235
|
-
checks.push({ name: "Baseline", status: "ok", detail: "Scan data present" });
|
|
216236
|
-
} else if (linked) {
|
|
216237
|
-
checks.push({ name: "Baseline", status: "warn", detail: "Repo connected but no scan baseline. Run `driftless init`" });
|
|
216238
|
-
} else {
|
|
216239
|
-
checks.push({ name: "Baseline", status: "warn", detail: "Could not verify" });
|
|
216240
|
-
}
|
|
216241
|
-
} else {
|
|
216242
|
-
checks.push({ name: "Baseline", status: "warn", detail: "Could not verify" });
|
|
216243
|
-
}
|
|
216244
|
-
} else {
|
|
216245
216312
|
checks.push({ name: "Baseline", status: "warn", detail: "Skipped (no git remote)" });
|
|
216246
216313
|
}
|
|
216247
216314
|
const agentsPath = (0, import_node_path7.resolve)(process.cwd(), "AGENTS.md");
|
|
@@ -216264,13 +216331,18 @@ async function doctorCommand() {
|
|
|
216264
216331
|
if (ghApp) {
|
|
216265
216332
|
checks.push({ name: "GitHub App", status: "ok", detail: "Installed and active" });
|
|
216266
216333
|
} else {
|
|
216267
|
-
checks.push({ name: "GitHub App", status: "warn", detail: "Not installed
|
|
216334
|
+
checks.push({ name: "GitHub App", status: "warn", detail: "Not installed \u2014 driftless.icu/ecosystem \u2192 Settings \u2192 Integrations (if just installed, wait ~60s)" });
|
|
216335
|
+
}
|
|
216336
|
+
} catch (err) {
|
|
216337
|
+
const isNotFound = err?.status === 404 || String(err?.message ?? "").includes("404");
|
|
216338
|
+
if (isNotFound) {
|
|
216339
|
+
checks.push({ name: "GitHub App", status: "warn", detail: "Not installed \u2014 driftless.icu/ecosystem \u2192 Settings \u2192 Integrations" });
|
|
216340
|
+
} else {
|
|
216341
|
+
checks.push({ name: "GitHub App", status: "warn", detail: "Could not verify \u2014 check your connection or API key" });
|
|
216268
216342
|
}
|
|
216269
|
-
} catch {
|
|
216270
|
-
checks.push({ name: "GitHub App", status: "warn", detail: "Could not verify" });
|
|
216271
216343
|
}
|
|
216272
216344
|
} else {
|
|
216273
|
-
checks.push({ name: "GitHub App", status: "warn", detail: "Could not verify" });
|
|
216345
|
+
checks.push({ name: "GitHub App", status: "warn", detail: "Could not verify (workspace unknown)" });
|
|
216274
216346
|
}
|
|
216275
216347
|
} else {
|
|
216276
216348
|
checks.push({ name: "GitHub App", status: "warn", detail: "Skipped (not a git repo)" });
|
|
@@ -216302,7 +216374,7 @@ function pad2(s, n) {
|
|
|
216302
216374
|
}
|
|
216303
216375
|
|
|
216304
216376
|
// src/index.ts
|
|
216305
|
-
var VERSION = "0.1.
|
|
216377
|
+
var VERSION = "0.1.36";
|
|
216306
216378
|
var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
|
|
216307
216379
|
|
|
216308
216380
|
Install: npm install -g @driftless-sh/cli
|
|
@@ -216319,7 +216391,7 @@ Agent loop:
|
|
|
216319
216391
|
|
|
216320
216392
|
Setup:
|
|
216321
216393
|
driftless login --key <api-key> Authenticate
|
|
216322
|
-
driftless init
|
|
216394
|
+
driftless init [--src <path>] Scan repo, bootstrap context (add --suggest for auto topics+rules)
|
|
216323
216395
|
driftless doctor Check environment health
|
|
216324
216396
|
|
|
216325
216397
|
Commands:
|
|
@@ -216370,8 +216442,8 @@ Examples:
|
|
|
216370
216442
|
driftless sync --json # machine-readable output
|
|
216371
216443
|
|
|
216372
216444
|
driftless context add "b2b-guard" \\
|
|
216373
|
-
--what "Guard
|
|
216374
|
-
--how "
|
|
216445
|
+
--what "Guard protecting B2B endpoints" \\
|
|
216446
|
+
--how "Verifies org_id from the auth token" \\
|
|
216375
216447
|
--pattern "src/shared/guards/**"
|
|
216376
216448
|
|
|
216377
216449
|
driftless context get sdk
|
|
@@ -216393,22 +216465,30 @@ Options:
|
|
|
216393
216465
|
Example:
|
|
216394
216466
|
driftless login --key drift_xxx
|
|
216395
216467
|
`,
|
|
216396
|
-
init: `driftless init
|
|
216468
|
+
init: `driftless init [--src <path>] [--suggest]
|
|
216397
216469
|
|
|
216398
216470
|
Smart initialization \u2014 zero friction:
|
|
216399
216471
|
1. Detects framework, system type, auth patterns (Pass 1)
|
|
216400
216472
|
2. AST scan: extracts endpoints, guards, services, modules, relations (Pass 2)
|
|
216401
216473
|
3. Analyzes code patterns: guard enforcement, multi-tenant, large files, legacy
|
|
216402
216474
|
4. Uploads baseline + components + relations to Cloud
|
|
216403
|
-
5.
|
|
216404
|
-
6.
|
|
216405
|
-
|
|
216475
|
+
5. Detects existing docs (not auto-synced \u2014 ask your agent to sync them)
|
|
216476
|
+
6. Installs AGENTS.md skill
|
|
216477
|
+
|
|
216478
|
+
With --suggest (optional):
|
|
216479
|
+
Also auto-generates rules and context topics from detected patterns.
|
|
216480
|
+
|
|
216481
|
+
Options:
|
|
216482
|
+
--src <path> Scan root relative to cwd (monorepo support).
|
|
216483
|
+
Git root stays at cwd; scanner starts from <path>.
|
|
216484
|
+
--suggest Auto-generate rules and context topics. Off by default.
|
|
216406
216485
|
|
|
216407
216486
|
Must be run from a git repository root.
|
|
216408
216487
|
|
|
216409
|
-
|
|
216410
|
-
cd my-nestjs-repo
|
|
216488
|
+
Examples:
|
|
216411
216489
|
driftless init
|
|
216490
|
+
driftless init --src apps/api/src
|
|
216491
|
+
driftless init --src apps/api/src --suggest
|
|
216412
216492
|
`,
|
|
216413
216493
|
scan: `driftless scan [--diff]
|
|
216414
216494
|
|
|
@@ -216505,25 +216585,53 @@ async function main() {
|
|
|
216505
216585
|
const command = args[0];
|
|
216506
216586
|
switch (command) {
|
|
216507
216587
|
case "init":
|
|
216508
|
-
|
|
216588
|
+
if (args[1] === "--help") {
|
|
216589
|
+
showCommandHelp("init");
|
|
216590
|
+
} else {
|
|
216591
|
+
await initCommand(args.slice(1));
|
|
216592
|
+
}
|
|
216509
216593
|
break;
|
|
216510
216594
|
case "scan":
|
|
216511
|
-
|
|
216595
|
+
if (args[1] === "--help") {
|
|
216596
|
+
showCommandHelp("scan");
|
|
216597
|
+
} else {
|
|
216598
|
+
await scanCommand(args.slice(1));
|
|
216599
|
+
}
|
|
216512
216600
|
break;
|
|
216513
216601
|
case "context":
|
|
216514
|
-
|
|
216602
|
+
if (args[1] === "--help") {
|
|
216603
|
+
showCommandHelp("context");
|
|
216604
|
+
} else {
|
|
216605
|
+
await contextCommand(args.slice(1));
|
|
216606
|
+
}
|
|
216515
216607
|
break;
|
|
216516
216608
|
case "sync":
|
|
216517
|
-
|
|
216609
|
+
if (args[1] === "--help") {
|
|
216610
|
+
showCommandHelp("sync");
|
|
216611
|
+
} else {
|
|
216612
|
+
await syncCommand(args.slice(1));
|
|
216613
|
+
}
|
|
216518
216614
|
break;
|
|
216519
216615
|
case "install-skill":
|
|
216520
|
-
|
|
216616
|
+
if (args[1] === "--help") {
|
|
216617
|
+
showCommandHelp("install-skill");
|
|
216618
|
+
} else {
|
|
216619
|
+
await installSkillCommand();
|
|
216620
|
+
}
|
|
216521
216621
|
break;
|
|
216522
216622
|
case "login":
|
|
216523
|
-
|
|
216623
|
+
if (args[1] === "--help") {
|
|
216624
|
+
showCommandHelp("login");
|
|
216625
|
+
} else {
|
|
216626
|
+
loginCommand(args.slice(1));
|
|
216627
|
+
}
|
|
216524
216628
|
break;
|
|
216525
216629
|
case "doctor":
|
|
216526
|
-
|
|
216630
|
+
if (args[1] === "--help") {
|
|
216631
|
+
showCommandHelp("doctor");
|
|
216632
|
+
} else {
|
|
216633
|
+
await doctorCommand();
|
|
216634
|
+
}
|
|
216527
216635
|
break;
|
|
216528
216636
|
case "help":
|
|
216529
216637
|
if (args[1]) {
|