@driftless-sh/cli 0.1.35 → 0.1.37

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 CHANGED
@@ -217,9 +217,22 @@ var require_identity = __commonJS({
217
217
  return "";
218
218
  }
219
219
  }
220
+ function findPackageJson(startPath) {
221
+ let dir = startPath;
222
+ for (let i = 0; i < 4; i++) {
223
+ const candidate = node_path_1.default.join(dir, "package.json");
224
+ if (node_fs_1.default.existsSync(candidate))
225
+ return candidate;
226
+ const parent = node_path_1.default.dirname(dir);
227
+ if (parent === dir)
228
+ break;
229
+ dir = parent;
230
+ }
231
+ return null;
232
+ }
220
233
  function detectFramework(fileNames, rootPath, cache) {
221
- const pkgPath = node_path_1.default.join(rootPath, "package.json");
222
- const pkgContent = readFileCached(pkgPath, cache);
234
+ const pkgPath = findPackageJson(rootPath);
235
+ const pkgContent = pkgPath ? readFileCached(pkgPath, cache) : "";
223
236
  if (pkgContent) {
224
237
  try {
225
238
  const pkg = JSON.parse(pkgContent);
@@ -213318,15 +213331,16 @@ var require_nestjs_extractor = __commonJS({
213318
213331
  var typescript_1 = __importDefault(require_typescript());
213319
213332
  function discoverFiles(rootPath) {
213320
213333
  const files = [];
213321
- const srcDir = node_path_1.default.join(rootPath, "src");
213334
+ const srcDir = node_fs_1.default.existsSync(node_path_1.default.join(rootPath, "src")) ? node_path_1.default.join(rootPath, "src") : rootPath;
213322
213335
  if (!node_fs_1.default.existsSync(srcDir))
213323
213336
  return files;
213337
+ const SKIP = /* @__PURE__ */ new Set(["node_modules", "dist", ".next", "build", ".turbo", "coverage"]);
213324
213338
  function walk(dir) {
213325
213339
  const entries = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
213326
213340
  for (const entry of entries) {
213327
213341
  const full = node_path_1.default.join(dir, entry.name);
213328
213342
  if (entry.isDirectory()) {
213329
- if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".next" || entry.name === "build" || entry.name === ".turbo" || entry.name === "coverage")
213343
+ if (SKIP.has(entry.name))
213330
213344
  continue;
213331
213345
  walk(full);
213332
213346
  } else if (entry.isFile() && entry.name.endsWith(".ts") && !entry.name.endsWith(".d.ts") && !entry.name.endsWith(".spec.ts") && !entry.name.endsWith(".test.ts")) {
@@ -213590,7 +213604,7 @@ var require_nestjs_extractor = __commonJS({
213590
213604
  function isInSourceDir(filePath) {
213591
213605
  if (filePath.includes("node_modules") || filePath.includes("/dist/"))
213592
213606
  return false;
213593
- return filePath.startsWith("src/") || filePath.includes("/src/");
213607
+ return filePath.startsWith("src/") || filePath.includes("/src/") || !filePath.startsWith("..");
213594
213608
  }
213595
213609
  var HTTP_METHODS = {
213596
213610
  Get: "GET",
@@ -214567,7 +214581,7 @@ async function installSkillCommand() {
214567
214581
  // src/commands/init.ts
214568
214582
  function getVersion() {
214569
214583
  try {
214570
- return "0.1.35";
214584
+ return "0.1.37";
214571
214585
  } catch {
214572
214586
  return "0.0.0";
214573
214587
  }
@@ -214935,6 +214949,12 @@ async function initCommand(args) {
214935
214949
  }
214936
214950
  };
214937
214951
  const components = scanResult.components;
214952
+ if (components.length === 0) {
214953
+ console.warn(`\u26A0 0 components found in ${srcOverride ? `--src ${srcOverride}` : "repo root"}.`);
214954
+ console.warn(" For NestJS monorepos, point --src at the app source root, not the package root.");
214955
+ console.warn(" Example: driftless init --src apps/api/src (not apps/api)");
214956
+ console.warn(" Continuing with baseline upload \u2014 no components or relations will be registered.");
214957
+ }
214938
214958
  const relationCount = components.reduce(
214939
214959
  (sum, component) => sum + (component.relations?.length || 0),
214940
214960
  0
@@ -215536,13 +215556,17 @@ async function contextCommand(args) {
215536
215556
  }
215537
215557
  }
215538
215558
  const pattern = flags["pattern"];
215539
- if (pattern && !isJSON) {
215559
+ if (pattern) {
215540
215560
  if (pattern.includes(",")) {
215541
- console.error(`Warning: pattern "${pattern}" contains a comma. Only one glob is accepted per topic. Create separate topics or remove the comma.`);
215561
+ console.error(`Error: pattern "${pattern}" contains a comma. Only one glob is accepted per topic.`);
215562
+ console.error(" Create separate topics for each path, or use a broader glob.");
215563
+ process.exit(1);
215542
215564
  }
215543
215565
  const localCount = countLocalFilesMatching(pattern);
215544
215566
  if (localCount === 0) {
215545
- console.error(`Warning: pattern "${pattern}" matches 0 files locally. Topic created anyway.`);
215567
+ console.error(`Error: pattern "${pattern}" matches 0 files locally. Topic not created.`);
215568
+ console.error(" Verify the glob against your repo root, or use --where for explicit file paths.");
215569
+ process.exit(1);
215546
215570
  }
215547
215571
  }
215548
215572
  try {
@@ -216015,6 +216039,40 @@ Run 'driftless help context' for full reference.`);
216015
216039
 
216016
216040
  // src/commands/sync.ts
216017
216041
  init_api_client();
216042
+
216043
+ // src/repo-resolver.ts
216044
+ init_api_client();
216045
+ function notLinkedMessage(remote, workspaceSlug) {
216046
+ return `Repo '${remote.org}/${remote.repo}' is not registered in workspace '${workspaceSlug}'. Run \`driftless init\` to register it.`;
216047
+ }
216048
+ async function resolveRepo() {
216049
+ const remote = getGitRemote();
216050
+ if (!remote) return { ok: false, reason: "no_remote" };
216051
+ let workspaceSlug;
216052
+ try {
216053
+ const me = await api.get("/me");
216054
+ workspaceSlug = me?.slug;
216055
+ } catch {
216056
+ }
216057
+ const slug = workspaceSlug ?? remote.org;
216058
+ let repos = null;
216059
+ try {
216060
+ repos = await api.get(`/workspaces/${slug}/repos`);
216061
+ } catch {
216062
+ }
216063
+ if (!repos) return { ok: false, reason: "no_workspace", remote };
216064
+ const repo = repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
216065
+ if (!repo) return { ok: false, reason: "not_linked", workspaceSlug: slug, remote };
216066
+ return {
216067
+ ok: true,
216068
+ workspaceSlug: slug,
216069
+ repoId: repo.id,
216070
+ remote,
216071
+ hasScanBaseline: !!repo.scan_summary
216072
+ };
216073
+ }
216074
+
216075
+ // src/commands/sync.ts
216018
216076
  function parseArgs2(args) {
216019
216077
  const flags = {};
216020
216078
  for (let i = 0; i < args.length; i++) {
@@ -216032,19 +216090,6 @@ function parseArgs2(args) {
216032
216090
  function emitJSON3(data) {
216033
216091
  console.log(JSON.stringify(data, null, 2));
216034
216092
  }
216035
- async function resolveWorkspaceSlug2() {
216036
- try {
216037
- const me = await api.get("/me");
216038
- if (me?.slug) return me.slug;
216039
- } catch {
216040
- }
216041
- const remote = getGitRemote();
216042
- if (!remote) {
216043
- console.error("Error: no git remote found.");
216044
- process.exit(1);
216045
- }
216046
- return remote.org;
216047
- }
216048
216093
  async function syncCommand(args) {
216049
216094
  if (!isGitRepo()) {
216050
216095
  console.error("Error: not a git repository.");
@@ -216052,28 +216097,18 @@ async function syncCommand(args) {
216052
216097
  }
216053
216098
  const { flags } = parseArgs2(args);
216054
216099
  const isJSON = !!flags["json"];
216055
- const remote = getGitRemote();
216056
- if (!remote) {
216057
- console.error("Error: no git remote configured.");
216058
- process.exit(1);
216059
- }
216060
- const workspaceSlug = await resolveWorkspaceSlug2();
216061
- let repoId = null;
216062
- try {
216063
- const repos = await api.get(`/workspaces/${workspaceSlug}/repos`);
216064
- const match = repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
216065
- if (match) repoId = match.id;
216066
- } catch {
216067
- }
216068
- if (!repoId) {
216100
+ const resolution = await resolveRepo();
216101
+ if (!resolution.ok) {
216102
+ 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);
216069
216103
  if (!isJSON) {
216070
- console.log(`\u26A0 Repo '${remote.org}/${remote.repo}' not found in workspace '${workspaceSlug}'.`);
216071
- console.log(" Run `driftless init` to register this repo, or `driftless doctor` to diagnose.");
216104
+ console.log(`\u26A0 ${msg}`);
216105
+ if (resolution.reason !== "no_remote") console.log(" Run `driftless doctor` to diagnose.");
216072
216106
  } else {
216073
- emitJSON3({ error: "repo_not_found", repo: `${remote.org}/${remote.repo}`, workspace: workspaceSlug });
216107
+ emitJSON3({ error: resolution.reason, message: msg });
216074
216108
  }
216075
216109
  process.exit(1);
216076
216110
  }
216111
+ const { workspaceSlug, repoId, remote } = resolution;
216077
216112
  const [eventsRes, staleTopics, violations, suggestedTopics] = await Promise.allSettled([
216078
216113
  api.get(`/workspaces/${workspaceSlug}/watchers/events?repo_id=${repoId}&limit=20`),
216079
216114
  api.get(`/workspaces/${workspaceSlug}/watchers?stale=true&repo=${repoId}`),
@@ -216273,38 +216308,22 @@ async function doctorCommand() {
216273
216308
  } else {
216274
216309
  checks.push({ name: "Workspace", status: "warn", detail: "Skipped (local API or no key)" });
216275
216310
  }
216276
- if (remote) {
216277
- const me = await getMe();
216278
- if (me?.slug) {
216279
- const repos = await getRepos(me.slug);
216280
- const linked = repos?.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
216281
- if (linked) {
216282
- checks.push({ name: "Repo linked", status: "ok", detail: `${remote.org}/${remote.repo}` });
216283
- } else {
216284
- checks.push({ name: "Repo linked", status: "warn", detail: `Not connected. Run \`driftless init\`` });
216285
- }
216286
- } else {
216287
- checks.push({ name: "Repo linked", status: "warn", detail: "Could not verify (workspace unknown)" });
216288
- }
216311
+ const resolution = await resolveRepo();
216312
+ if (resolution.ok) {
216313
+ checks.push({ name: "Repo linked", status: "ok", detail: `${resolution.remote.org}/${resolution.remote.repo}` });
216314
+ checks.push({
216315
+ name: "Baseline",
216316
+ status: resolution.hasScanBaseline ? "ok" : "warn",
216317
+ detail: resolution.hasScanBaseline ? "Scan data present" : "No scan baseline. Run `driftless init`"
216318
+ });
216319
+ } else if (resolution.reason === "not_linked") {
216320
+ checks.push({ name: "Repo linked", status: "warn", detail: notLinkedMessage(resolution.remote, resolution.workspaceSlug) });
216321
+ checks.push({ name: "Baseline", status: "warn", detail: "Could not verify (repo not registered)" });
216322
+ } else if (resolution.reason === "no_workspace") {
216323
+ checks.push({ name: "Repo linked", status: "warn", detail: "Could not verify (workspace unknown)" });
216324
+ checks.push({ name: "Baseline", status: "warn", detail: "Could not verify (workspace unknown)" });
216289
216325
  } else {
216290
216326
  checks.push({ name: "Repo linked", status: "warn", detail: "Skipped (no git remote)" });
216291
- }
216292
- if (remote) {
216293
- const me = await getMe();
216294
- if (me?.slug) {
216295
- const repos = await getRepos(me.slug);
216296
- const linked = repos?.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
216297
- if (linked?.scan_summary) {
216298
- checks.push({ name: "Baseline", status: "ok", detail: "Scan data present" });
216299
- } else if (linked) {
216300
- checks.push({ name: "Baseline", status: "warn", detail: "Repo connected but no scan baseline. Run `driftless init`" });
216301
- } else {
216302
- checks.push({ name: "Baseline", status: "warn", detail: "Could not verify" });
216303
- }
216304
- } else {
216305
- checks.push({ name: "Baseline", status: "warn", detail: "Could not verify" });
216306
- }
216307
- } else {
216308
216327
  checks.push({ name: "Baseline", status: "warn", detail: "Skipped (no git remote)" });
216309
216328
  }
216310
216329
  const agentsPath = (0, import_node_path7.resolve)(process.cwd(), "AGENTS.md");
@@ -216370,7 +216389,7 @@ function pad2(s, n) {
216370
216389
  }
216371
216390
 
216372
216391
  // src/index.ts
216373
- var VERSION = "0.1.35";
216392
+ var VERSION = "0.1.37";
216374
216393
  var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
216375
216394
 
216376
216395
  Install: npm install -g @driftless-sh/cli