@driftless-sh/cli 0.1.25 → 0.1.26

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
@@ -445,7 +445,7 @@ var require_util = __commonJS({
445
445
  return path;
446
446
  }
447
447
  exports2.normalize = normalize;
448
- function join3(aRoot, aPath) {
448
+ function join2(aRoot, aPath) {
449
449
  if (aRoot === "") {
450
450
  aRoot = ".";
451
451
  }
@@ -477,7 +477,7 @@ var require_util = __commonJS({
477
477
  }
478
478
  return joined;
479
479
  }
480
- exports2.join = join3;
480
+ exports2.join = join2;
481
481
  exports2.isAbsolute = function(aPath) {
482
482
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
483
483
  };
@@ -650,7 +650,7 @@ var require_util = __commonJS({
650
650
  parsed.path = parsed.path.substring(0, index + 1);
651
651
  }
652
652
  }
653
- sourceURL = join3(urlGenerate(parsed), sourceURL);
653
+ sourceURL = join2(urlGenerate(parsed), sourceURL);
654
654
  }
655
655
  return normalize(sourceURL);
656
656
  }
@@ -23019,8 +23019,8 @@ ${lanes.join("\n")}
23019
23019
  function containsInvalidEscapeFlag(node) {
23020
23020
  return !!((node.templateFlags || 0) & 2048);
23021
23021
  }
23022
- function hasInvalidEscape(template2) {
23023
- return template2 && !!(isNoSubstitutionTemplateLiteral(template2) ? containsInvalidEscapeFlag(template2) : containsInvalidEscapeFlag(template2.head) || some(template2.templateSpans, (span) => containsInvalidEscapeFlag(span.literal)));
23022
+ function hasInvalidEscape(template) {
23023
+ return template && !!(isNoSubstitutionTemplateLiteral(template) ? containsInvalidEscapeFlag(template) : containsInvalidEscapeFlag(template.head) || some(template.templateSpans, (span) => containsInvalidEscapeFlag(span.literal)));
23024
23024
  }
23025
23025
  var doubleQuoteEscapedCharsRegExp = /[\\"\u0000-\u001f\u2028\u2029\u0085]/g;
23026
23026
  var singleQuoteEscapedCharsRegExp = /[\\'\u0000-\u001f\u2028\u2029\u0085]/g;
@@ -30345,7 +30345,7 @@ ${lanes.join("\n")}
30345
30345
  function updateNewExpression(node, expression, typeArguments, argumentsArray) {
30346
30346
  return node.expression !== expression || node.typeArguments !== typeArguments || node.arguments !== argumentsArray ? update(createNewExpression(expression, typeArguments, argumentsArray), node) : node;
30347
30347
  }
30348
- function createTaggedTemplateExpression(tag, typeArguments, template2) {
30348
+ function createTaggedTemplateExpression(tag, typeArguments, template) {
30349
30349
  const node = createBaseNode(
30350
30350
  216
30351
30351
  /* TaggedTemplateExpression */
@@ -30356,7 +30356,7 @@ ${lanes.join("\n")}
30356
30356
  false
30357
30357
  );
30358
30358
  node.typeArguments = asNodeArray(typeArguments);
30359
- node.template = template2;
30359
+ node.template = template;
30360
30360
  node.transformFlags |= propagateChildFlags(node.tag) | propagateChildrenFlags(node.typeArguments) | propagateChildFlags(node.template) | 1024;
30361
30361
  if (node.typeArguments) {
30362
30362
  node.transformFlags |= 1;
@@ -30366,8 +30366,8 @@ ${lanes.join("\n")}
30366
30366
  }
30367
30367
  return node;
30368
30368
  }
30369
- function updateTaggedTemplateExpression(node, tag, typeArguments, template2) {
30370
- return node.tag !== tag || node.typeArguments !== typeArguments || node.template !== template2 ? update(createTaggedTemplateExpression(tag, typeArguments, template2), node) : node;
30369
+ function updateTaggedTemplateExpression(node, tag, typeArguments, template) {
30370
+ return node.tag !== tag || node.typeArguments !== typeArguments || node.template !== template ? update(createTaggedTemplateExpression(tag, typeArguments, template), node) : node;
30371
30371
  }
30372
30372
  function createTypeAssertion(type, expression) {
30373
30373
  const node = createBaseNode(
@@ -73219,14 +73219,14 @@ ${lanes.join("\n")}
73219
73219
  while (i > 0) {
73220
73220
  i--;
73221
73221
  const t = types[i];
73222
- if (t.flags & 128 && some(templates, (template2) => isTypeMatchedByTemplateLiteralOrStringMapping(t, template2))) {
73222
+ if (t.flags & 128 && some(templates, (template) => isTypeMatchedByTemplateLiteralOrStringMapping(t, template))) {
73223
73223
  orderedRemoveItemAt(types, i);
73224
73224
  }
73225
73225
  }
73226
73226
  }
73227
73227
  }
73228
- function isTypeMatchedByTemplateLiteralOrStringMapping(type, template2) {
73229
- return template2.flags & 134217728 ? isTypeMatchedByTemplateLiteralType(type, template2) : isMemberOfStringMapping(type, template2);
73228
+ function isTypeMatchedByTemplateLiteralOrStringMapping(type, template) {
73229
+ return template.flags & 134217728 ? isTypeMatchedByTemplateLiteralType(type, template) : isMemberOfStringMapping(type, template);
73230
73230
  }
73231
73231
  function removeConstrainedTypeVariables(types) {
73232
73232
  const typeVariables = [];
@@ -85395,9 +85395,9 @@ ${lanes.join("\n")}
85395
85395
  const signature = getDecoratorCallSignature(decorator);
85396
85396
  return signature ? getOrCreateTypeFromSignature(signature) : void 0;
85397
85397
  }
85398
- function getContextualTypeForSubstitutionExpression(template2, substitutionExpression) {
85399
- if (template2.parent.kind === 216) {
85400
- return getContextualTypeForArgument(template2.parent, substitutionExpression);
85398
+ function getContextualTypeForSubstitutionExpression(template, substitutionExpression) {
85399
+ if (template.parent.kind === 216) {
85400
+ return getContextualTypeForArgument(template.parent, substitutionExpression);
85401
85401
  }
85402
85402
  return void 0;
85403
85403
  }
@@ -89022,10 +89022,10 @@ ${lanes.join("\n")}
89022
89022
  return [createSyntheticExpression(node, emptyFreshJsxObjectType)];
89023
89023
  }
89024
89024
  if (node.kind === 216) {
89025
- const template2 = node.template;
89026
- const args2 = [createSyntheticExpression(template2, getGlobalTemplateStringsArrayType())];
89027
- if (template2.kind === 229) {
89028
- forEach(template2.templateSpans, (span) => {
89025
+ const template = node.template;
89026
+ const args2 = [createSyntheticExpression(template, getGlobalTemplateStringsArrayType())];
89027
+ if (template.kind === 229) {
89028
+ forEach(template.templateSpans, (span) => {
89029
89029
  args2.push(span.expression);
89030
89030
  });
89031
89031
  }
@@ -107909,18 +107909,18 @@ ${lanes.join("\n")}
107909
107909
  const templateArguments = [void 0];
107910
107910
  const cookedStrings = [];
107911
107911
  const rawStrings = [];
107912
- const template2 = node.template;
107913
- if (level === 0 && !hasInvalidEscape(template2)) {
107912
+ const template = node.template;
107913
+ if (level === 0 && !hasInvalidEscape(template)) {
107914
107914
  return visitEachChild(node, visitor, context);
107915
107915
  }
107916
107916
  const { factory: factory2 } = context;
107917
- if (isNoSubstitutionTemplateLiteral(template2)) {
107918
- cookedStrings.push(createTemplateCooked(factory2, template2));
107919
- rawStrings.push(getRawLiteral(factory2, template2, currentSourceFile));
107917
+ if (isNoSubstitutionTemplateLiteral(template)) {
107918
+ cookedStrings.push(createTemplateCooked(factory2, template));
107919
+ rawStrings.push(getRawLiteral(factory2, template, currentSourceFile));
107920
107920
  } else {
107921
- cookedStrings.push(createTemplateCooked(factory2, template2.head));
107922
- rawStrings.push(getRawLiteral(factory2, template2.head, currentSourceFile));
107923
- for (const templateSpan of template2.templateSpans) {
107921
+ cookedStrings.push(createTemplateCooked(factory2, template.head));
107922
+ rawStrings.push(getRawLiteral(factory2, template.head, currentSourceFile));
107923
+ for (const templateSpan of template.templateSpans) {
107924
107924
  cookedStrings.push(createTemplateCooked(factory2, templateSpan.literal));
107925
107925
  rawStrings.push(getRawLiteral(factory2, templateSpan.literal, currentSourceFile));
107926
107926
  templateArguments.push(Debug.checkDefined(visitNode(templateSpan.expression, visitor, isExpression)));
@@ -107950,8 +107950,8 @@ ${lanes.join("\n")}
107950
107950
  templateArguments
107951
107951
  );
107952
107952
  }
107953
- function createTemplateCooked(factory2, template2) {
107954
- return template2.templateFlags & 26656 ? factory2.createVoidZero() : factory2.createStringLiteral(template2.text);
107953
+ function createTemplateCooked(factory2, template) {
107954
+ return template.templateFlags & 26656 ? factory2.createVoidZero() : factory2.createStringLiteral(template.text);
107955
107955
  }
107956
107956
  function getRawLiteral(factory2, node, currentSourceFile) {
107957
107957
  let text = node.rawText;
@@ -114494,13 +114494,13 @@ ${lanes.join("\n")}
114494
114494
  const boundTag = factory2.createFunctionBindCall(tag, classThis, []);
114495
114495
  setOriginalNode(boundTag, node);
114496
114496
  setTextRange(boundTag, node);
114497
- const template2 = visitNode(node.template, visitor, isTemplateLiteral);
114497
+ const template = visitNode(node.template, visitor, isTemplateLiteral);
114498
114498
  return factory2.updateTaggedTemplateExpression(
114499
114499
  node,
114500
114500
  boundTag,
114501
114501
  /*typeArguments*/
114502
114502
  void 0,
114503
- template2
114503
+ template
114504
114504
  );
114505
114505
  }
114506
114506
  return visitEachChild(node, visitor, context);
@@ -146943,7 +146943,7 @@ ${lanes.join("\n")}
146943
146943
  };
146944
146944
  }
146945
146945
  function createBuildOrUpdateInvalidedProject(state, project, projectPath, projectIndex, config, status, buildOrder) {
146946
- let step = 0;
146946
+ let step2 = 0;
146947
146947
  let program;
146948
146948
  let buildResult;
146949
146949
  return {
@@ -147019,14 +147019,14 @@ ${lanes.join("\n")}
147019
147019
  if (state.options.dry) {
147020
147020
  reportStatus(state, Diagnostics.A_non_dry_build_would_build_project_0, project);
147021
147021
  buildResult = 1;
147022
- step = 2;
147022
+ step2 = 2;
147023
147023
  return;
147024
147024
  }
147025
147025
  if (state.options.verbose) reportStatus(state, Diagnostics.Building_project_0, project);
147026
147026
  if (config.fileNames.length === 0) {
147027
147027
  reportAndStoreErrors(state, projectPath, getConfigFileParsingDiagnostics(config));
147028
147028
  buildResult = 0;
147029
- step = 2;
147029
+ step2 = 2;
147030
147030
  return;
147031
147031
  }
147032
147032
  const { host, compilerHost } = state;
@@ -147052,13 +147052,13 @@ ${lanes.join("\n")}
147052
147052
  );
147053
147053
  state.builderPrograms.set(projectPath, program);
147054
147054
  }
147055
- step++;
147055
+ step2++;
147056
147056
  }
147057
147057
  function emit(writeFileCallback, cancellationToken, customTransformers) {
147058
147058
  var _a, _b, _c;
147059
147059
  Debug.assertIsDefined(program);
147060
147060
  Debug.assert(
147061
- step === 1
147061
+ step2 === 1
147062
147062
  /* Emit */
147063
147063
  );
147064
147064
  const { host, compilerHost } = state;
@@ -147130,13 +147130,13 @@ ${lanes.join("\n")}
147130
147130
  buildResult |= 4;
147131
147131
  }
147132
147132
  afterProgramDone(state, program);
147133
- step = 2;
147133
+ step2 = 2;
147134
147134
  return emitResult;
147135
147135
  }
147136
147136
  function executeSteps(till, cancellationToken, writeFile2, customTransformers) {
147137
- while (step <= till && step < 3) {
147138
- const currentStep = step;
147139
- switch (step) {
147137
+ while (step2 <= till && step2 < 3) {
147138
+ const currentStep = step2;
147139
+ switch (step2) {
147140
147140
  case 0:
147141
147141
  createProgram2();
147142
147142
  break;
@@ -147145,14 +147145,14 @@ ${lanes.join("\n")}
147145
147145
  break;
147146
147146
  case 2:
147147
147147
  queueReferencingProjects(state, project, projectPath, projectIndex, config, buildOrder, Debug.checkDefined(buildResult));
147148
- step++;
147148
+ step2++;
147149
147149
  break;
147150
147150
  // Should never be done
147151
147151
  case 3:
147152
147152
  default:
147153
- assertType(step);
147153
+ assertType(step2);
147154
147154
  }
147155
- Debug.assert(step > currentStep);
147155
+ Debug.assert(step2 > currentStep);
147156
147156
  }
147157
147157
  }
147158
147158
  }
@@ -159531,12 +159531,12 @@ interface Symbol {
159531
159531
  void 0,
159532
159532
  typeParameter.name
159533
159533
  );
159534
- const template2 = factory.createJSDocTemplateTag(
159534
+ const template = factory.createJSDocTemplateTag(
159535
159535
  factory.createIdentifier("template"),
159536
159536
  constraint && cast(constraint, isJSDocTypeExpression),
159537
159537
  [parameter]
159538
159538
  );
159539
- templates.push(template2);
159539
+ templates.push(template);
159540
159540
  });
159541
159541
  const jsDoc = factory.createJSDocComment(
159542
159542
  /*comment*/
@@ -191190,11 +191190,11 @@ ${content}
191190
191190
  return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart);
191191
191191
  }
191192
191192
  function getApplicableSpanForTaggedTemplate(taggedTemplate, sourceFile) {
191193
- const template2 = taggedTemplate.template;
191194
- const applicableSpanStart = template2.getStart();
191195
- let applicableSpanEnd = template2.getEnd();
191196
- if (template2.kind === 229) {
191197
- const lastSpan = last(template2.templateSpans);
191193
+ const template = taggedTemplate.template;
191194
+ const applicableSpanStart = template.getStart();
191195
+ let applicableSpanEnd = template.getEnd();
191196
+ if (template.kind === 229) {
191197
+ const lastSpan = last(template.templateSpans);
191198
191198
  if (lastSpan.literal.getFullWidth() === 0) {
191199
191199
  applicableSpanEnd = skipTrivia(
191200
191200
  sourceFile.text,
@@ -214431,8 +214431,119 @@ function formatError(e) {
214431
214431
  // src/commands/init.ts
214432
214432
  init_git();
214433
214433
  var import_scanner = __toESM(require_dist());
214434
+ var import_node_fs4 = require("node:fs");
214435
+ var import_node_path4 = require("node:path");
214436
+
214437
+ // src/commands/install-skill.ts
214434
214438
  var import_node_fs3 = require("node:fs");
214435
214439
  var import_node_path3 = require("node:path");
214440
+ var SKILL_URL = "https://raw.githubusercontent.com/driftless-gh/driftless-skill/main/skills/driftless/SKILL.md";
214441
+ var AGENTS_BLOCK = `
214442
+ ## Driftless \u2014 Context Integrity
214443
+
214444
+ Before touching files:
214445
+ driftless context push --files "<files you're about to edit>"
214446
+
214447
+ Before pushing:
214448
+ driftless scan --diff
214449
+
214450
+ Save discoveries:
214451
+ driftless context update <slug> --gotchas "..." --decisions "..."
214452
+
214453
+ Full skill \u2192 https://github.com/driftless-gh/driftless-skill
214454
+ `;
214455
+ async function downloadSkill(cwd) {
214456
+ const driftlessDir = (0, import_node_path3.resolve)(cwd, ".driftless");
214457
+ if (!(0, import_node_fs3.existsSync)(driftlessDir)) {
214458
+ (0, import_node_fs3.mkdirSync)(driftlessDir, { recursive: true });
214459
+ }
214460
+ const skillPath = (0, import_node_path3.resolve)(driftlessDir, "skill.md");
214461
+ try {
214462
+ const { default: https } = await import("node:https");
214463
+ const { default: http } = await import("node:http");
214464
+ const url = new URL(SKILL_URL);
214465
+ const client = url.protocol === "https:" ? https : http;
214466
+ await new Promise((ok, fail) => {
214467
+ client.get(SKILL_URL, (res) => {
214468
+ if (res.statusCode !== 200) {
214469
+ fail(new Error(`HTTP ${res.statusCode}`));
214470
+ return;
214471
+ }
214472
+ const chunks = [];
214473
+ res.on("data", (chunk) => chunks.push(chunk));
214474
+ res.on("end", () => {
214475
+ (0, import_node_fs3.writeFileSync)(skillPath, Buffer.concat(chunks), "utf-8");
214476
+ ok();
214477
+ });
214478
+ res.on("error", fail);
214479
+ }).on("error", fail);
214480
+ });
214481
+ } catch {
214482
+ (0, import_node_fs3.writeFileSync)(
214483
+ skillPath,
214484
+ `# Driftless Skill
214485
+
214486
+ See https://github.com/driftless-gh/driftless-skill for the full skill.
214487
+ `,
214488
+ "utf-8"
214489
+ );
214490
+ }
214491
+ }
214492
+ function handleClaudeMd(cwd) {
214493
+ const claudePath = (0, import_node_path3.resolve)(cwd, "CLAUDE.md");
214494
+ const reference = "@.driftless/skill.md";
214495
+ if (!(0, import_node_fs3.existsSync)(claudePath)) {
214496
+ (0, import_node_fs3.writeFileSync)(claudePath, `${reference}
214497
+ `, "utf-8");
214498
+ return "created";
214499
+ }
214500
+ const existing = (0, import_node_fs3.readFileSync)(claudePath, "utf-8");
214501
+ if (existing.includes(reference)) {
214502
+ return "already configured";
214503
+ }
214504
+ const needsNewline = existing.length > 0 && !existing.endsWith("\n");
214505
+ (0, import_node_fs3.writeFileSync)(claudePath, existing + (needsNewline ? "\n" : "") + `
214506
+ ${reference}
214507
+ `, "utf-8");
214508
+ return "updated";
214509
+ }
214510
+ function handleAgentsMd(cwd) {
214511
+ const agentsPath = (0, import_node_path3.resolve)(cwd, "AGENTS.md");
214512
+ if (!(0, import_node_fs3.existsSync)(agentsPath)) {
214513
+ (0, import_node_fs3.writeFileSync)(agentsPath, AGENTS_BLOCK.trimStart(), "utf-8");
214514
+ return "created";
214515
+ }
214516
+ const existing = (0, import_node_fs3.readFileSync)(agentsPath, "utf-8");
214517
+ if (existing.includes("## Driftless")) {
214518
+ return "already configured";
214519
+ }
214520
+ const needsNewline = existing.length > 0 && !existing.endsWith("\n");
214521
+ (0, import_node_fs3.writeFileSync)(agentsPath, existing + (needsNewline ? "\n" : "") + AGENTS_BLOCK, "utf-8");
214522
+ return "updated";
214523
+ }
214524
+ async function runInstallSkill(cwd) {
214525
+ await downloadSkill(cwd);
214526
+ const claudeMd = handleClaudeMd(cwd);
214527
+ const agentsMd = handleAgentsMd(cwd);
214528
+ return { claudeMd, agentsMd };
214529
+ }
214530
+ async function installSkillCommand() {
214531
+ const cwd = process.cwd();
214532
+ const result = await runInstallSkill(cwd);
214533
+ const icon = (s) => s === "created" ? "created \u2713" : s === "updated" ? "updated \u2713" : "already configured \u2713";
214534
+ console.log(` CLAUDE.md ${icon(result.claudeMd)}`);
214535
+ console.log(` AGENTS.md ${icon(result.agentsMd)}`);
214536
+ return result;
214537
+ }
214538
+
214539
+ // src/commands/init.ts
214540
+ function getVersion() {
214541
+ try {
214542
+ return "0.1.26";
214543
+ } catch {
214544
+ return "0.0.0";
214545
+ }
214546
+ }
214436
214547
  function analyzeCodePatterns(components) {
214437
214548
  const pattern = {
214438
214549
  guardNames: [],
@@ -214690,15 +214801,15 @@ function detectExistingDocs(cwd) {
214690
214801
  { path: "README.md", name: "readme", what: "Project overview and setup", pattern: void 0 }
214691
214802
  ];
214692
214803
  for (const candidate of candidates) {
214693
- const fullPath = (0, import_node_path3.resolve)(cwd, candidate.path);
214694
- if ((0, import_node_fs3.existsSync)(fullPath)) {
214804
+ const fullPath = (0, import_node_path4.resolve)(cwd, candidate.path);
214805
+ if ((0, import_node_fs4.existsSync)(fullPath)) {
214695
214806
  docs.push({ path: candidate.path, name: candidate.name, what: candidate.what, pattern: candidate.pattern });
214696
214807
  }
214697
214808
  }
214698
- const docsDir = (0, import_node_path3.resolve)(cwd, "docs");
214699
- if ((0, import_node_fs3.existsSync)(docsDir)) {
214809
+ const docsDir = (0, import_node_path4.resolve)(cwd, "docs");
214810
+ if ((0, import_node_fs4.existsSync)(docsDir)) {
214700
214811
  try {
214701
- const entries = (0, import_node_fs3.readdirSync)(docsDir, { withFileTypes: true });
214812
+ const entries = (0, import_node_fs4.readdirSync)(docsDir, { withFileTypes: true });
214702
214813
  for (const entry of entries) {
214703
214814
  if (entry.isFile() && entry.name.endsWith(".md")) {
214704
214815
  const existingPath = candidates.find((c) => c.path === `docs/${entry.name}`);
@@ -214718,8 +214829,23 @@ function detectExistingDocs(cwd) {
214718
214829
  }
214719
214830
  return docs;
214720
214831
  }
214832
+ var DIVIDER = "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500";
214833
+ var DOT_TOTAL = 36;
214834
+ function step(label, value) {
214835
+ const prefix = ` \u2192 ${label}`;
214836
+ const dots = ".".repeat(Math.max(2, DOT_TOTAL - prefix.length));
214837
+ console.log(`${prefix}${dots} ${value}`);
214838
+ }
214721
214839
  async function initCommand(args) {
214722
- console.log("Driftless \u2014 context integrity for engineering teams\n");
214840
+ const version = getVersion();
214841
+ console.log("D R I F T L E S S");
214842
+ console.log(`v${version}`);
214843
+ console.log("");
214844
+ console.log("AI ships fast. Humans lose context faster.");
214845
+ console.log("Driftless fixes that.");
214846
+ console.log("");
214847
+ console.log(DIVIDER);
214848
+ console.log("");
214723
214849
  if (!isGitRepo()) {
214724
214850
  console.error("Error: not a git repository.");
214725
214851
  process.exit(1);
@@ -214729,8 +214855,6 @@ async function initCommand(args) {
214729
214855
  console.error("Error: no git remote found.");
214730
214856
  process.exit(1);
214731
214857
  }
214732
- console.log(`Repository: ${remote.org}/${remote.repo}`);
214733
- console.log("\nConnecting to Driftless Cloud...");
214734
214858
  let workspace;
214735
214859
  try {
214736
214860
  workspace = await api.get("/me");
@@ -214738,15 +214862,18 @@ async function initCommand(args) {
214738
214862
  console.error("Could not resolve workspace. Check your API key.");
214739
214863
  process.exit(1);
214740
214864
  }
214741
- console.log(` Workspace: ${workspace.slug} \u2713`);
214742
214865
  } catch (err) {
214743
- console.error(` Failed: ${err?.message || "unreachable"}`);
214866
+ console.error(`Failed to connect: ${err?.message || "unreachable"}`);
214744
214867
  process.exit(1);
214745
214868
  }
214746
214869
  const workspaceSlug = workspace.slug;
214747
- console.log("\nScanning codebase locally...");
214870
+ console.log(`Repository: ${remote.org}/${remote.repo}`);
214871
+ console.log(`Workspace: ${workspaceSlug} \u2713`);
214872
+ console.log("");
214748
214873
  const cwd = process.cwd();
214874
+ const scanStart = Date.now();
214749
214875
  const scanResult = await (0, import_scanner.scanRepo)(cwd);
214876
+ const scanMs = Date.now() - scanStart;
214750
214877
  const earlyPatterns = analyzeCodePatterns(scanResult.components);
214751
214878
  const summary = {
214752
214879
  endpoints: scanResult.stats.total_endpoints,
@@ -214773,36 +214900,31 @@ async function initCommand(args) {
214773
214900
  const actualServices = components.filter((c) => c.type === "service").length;
214774
214901
  const actualModules = components.filter((c) => c.type === "module").length;
214775
214902
  const actualGuards = components.filter((c) => c.type === "guard").length;
214776
- const actualControllers = components.filter((c) => c.type === "controller").length;
214777
- const actualDtos = components.filter((c) => c.type === "dto").length;
214778
- console.log(` Framework: ${summary.framework} | Endpoints: ${actualEndpoints} | Services: ${actualServices}`);
214779
- console.log(` Modules: ${actualModules} | Guards: ${actualGuards}`);
214780
- console.log(` Relations: ${relationCount}`);
214781
- if (actualControllers > 0) console.log(` Controllers: ${actualControllers}`);
214782
- if (actualDtos > 0) console.log(` DTOs: ${actualDtos}`);
214783
- if (summary.auth_patterns.length > 0) {
214784
- console.log(` Auth: ${summary.auth_patterns.join(", ")}`);
214785
- }
214786
- if (scanResult.telemetry) {
214787
- const t = scanResult.telemetry;
214788
- console.log(` Scan: ${t.duration_ms}ms, ${t.memory_mb}MB heap, ${t.files_parsed} files`);
214903
+ const frameworkLabel = [summary.framework, "ts"].filter(Boolean).join(", ");
214904
+ step("detecting framework", frameworkLabel);
214905
+ step("extracting components", `${components.length} found`);
214906
+ step("mapping relations", `${relationCount} mapped`);
214907
+ const patternsLabel = `${actualGuards} guard${actualGuards !== 1 ? "s" : ""} \xB7 ${actualEndpoints} endpoints`;
214908
+ step("analyzing patterns", patternsLabel);
214909
+ const telemetry = scanResult.telemetry;
214910
+ if (telemetry) {
214911
+ console.log(` Scan: ${telemetry.duration_ms}ms \xB7 ${telemetry.memory_mb}MB \xB7 ${telemetry.files_parsed} files`);
214912
+ } else {
214913
+ console.log(` Scan: ${scanMs}ms`);
214789
214914
  }
214915
+ console.log("");
214790
214916
  let repo;
214791
214917
  try {
214792
214918
  const repos = await api.get(`/workspaces/${workspaceSlug}/repos`);
214793
214919
  repo = Array.isArray(repos) ? repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo) : null;
214794
214920
  } catch {
214795
214921
  }
214796
- if (repo) {
214797
- console.log(`
214798
- Repo: ${remote.repo} \u2713`);
214799
- } else {
214922
+ if (!repo) {
214800
214923
  try {
214801
214924
  repo = await api.post(`/workspaces/${workspaceSlug}/repos`, {
214802
214925
  github_org: remote.org,
214803
214926
  github_repo: remote.repo
214804
214927
  });
214805
- console.log(` Repo: ${remote.repo} (connected)`);
214806
214928
  } catch (err) {
214807
214929
  console.error(` Failed to connect repo: ${err?.message || "unknown"}`);
214808
214930
  process.exit(1);
@@ -214810,39 +214932,35 @@ async function initCommand(args) {
214810
214932
  }
214811
214933
  try {
214812
214934
  await api.post(`/workspaces/${workspaceSlug}/repos/${repo.id}/baseline`, summary);
214813
- console.log(" Baseline uploaded \u2713");
214814
- } catch (err) {
214815
- console.error(` Upload failed: ${err?.message || "unknown"}`);
214935
+ step("uploading baseline", "\u2713");
214936
+ } catch {
214937
+ step("uploading baseline", "failed (continuing)");
214816
214938
  }
214817
214939
  if (components.length > 0) {
214818
214940
  try {
214819
214941
  await api.post(`/workspaces/${workspaceSlug}/repos/${repo.id}/components`, { components });
214820
- console.log(` Components uploaded (${components.length}, relations ${relationCount}) \u2713`);
214821
- } catch (err) {
214822
- console.error(` Component upload failed: ${err?.message || "unknown"}`);
214942
+ step("uploading components", `${components.length} components \xB7 ${relationCount} relations \u2713`);
214943
+ } catch {
214944
+ step("uploading components", "failed (continuing)");
214823
214945
  }
214824
214946
  }
214825
- console.log("\nAnalyzing code patterns...");
214826
214947
  const patterns = earlyPatterns;
214827
214948
  const smartRules = generateSmartRules(repo.id, patterns);
214828
- console.log(` Detected: ${patterns.guardNames.length} guards, ${patterns.endpointPaths.length} endpoints`);
214829
- if (patterns.hasMultiTenant) console.log(" Multi-tenant pattern detected");
214830
- if (patterns.largeFiles.length > 0) console.log(` Large files: ${patterns.largeFiles.length} (quarantine candidates)`);
214831
- if (patterns.quarantineCandidates.length > 0) console.log(` Legacy files: ${patterns.quarantineCandidates.length}`);
214832
- console.log(`
214833
- Creating ${smartRules.length} architectural rules...`);
214834
214949
  let rulesCreated = 0;
214835
214950
  let rulesSkipped = 0;
214836
214951
  for (const rule of smartRules) {
214837
214952
  try {
214838
- await api.post(`/workspaces/${workspaceSlug}/rules`, { ...rule, auto_suggested: true });
214839
- rulesCreated++;
214953
+ const result = await api.post(`/workspaces/${workspaceSlug}/rules`, { ...rule, auto_suggested: true });
214954
+ if (result?.skipped) {
214955
+ rulesSkipped++;
214956
+ } else {
214957
+ rulesCreated++;
214958
+ }
214840
214959
  } catch {
214841
214960
  rulesSkipped++;
214842
214961
  }
214843
214962
  }
214844
- console.log(` Rules created: ${rulesCreated}, skipped (already exist): ${rulesSkipped}`);
214845
- console.log("\nCreating context watchers from detected modules...");
214963
+ step("creating rules", `${rulesCreated} created \xB7 ${rulesSkipped} skipped \u2713`);
214846
214964
  const smartWatchers = generateSmartWatchers(components);
214847
214965
  let watchersCreated = 0;
214848
214966
  let watchersSkipped = 0;
@@ -214860,19 +214978,17 @@ Creating ${smartRules.length} architectural rules...`);
214860
214978
  created_by: "driftless-init"
214861
214979
  });
214862
214980
  watchersCreated++;
214863
- console.log(` \u2713 ${watcher.name} \u2014 ${watcher.what}`);
214864
214981
  } catch {
214865
214982
  watchersSkipped++;
214866
214983
  }
214867
214984
  }
214868
- console.log(` Watchers created: ${watchersCreated}, skipped (already exist): ${watchersSkipped}`);
214869
- console.log("\nScanning for existing documentation...");
214985
+ step("creating context", `${watchersCreated} watchers \u2713`);
214870
214986
  const existingDocs = detectExistingDocs(cwd);
214871
214987
  let docsAnchored = 0;
214872
214988
  for (const doc of existingDocs) {
214873
214989
  try {
214874
- const fullPath = (0, import_node_path3.resolve)(cwd, doc.path);
214875
- const fileContent = (0, import_node_fs3.readFileSync)(fullPath, "utf-8");
214990
+ const fullPath = (0, import_node_path4.resolve)(cwd, doc.path);
214991
+ const fileContent = (0, import_node_fs4.readFileSync)(fullPath, "utf-8");
214876
214992
  await api.post(`/workspaces/${workspaceSlug}/watchers`, {
214877
214993
  name: doc.name,
214878
214994
  what: doc.what,
@@ -214885,21 +215001,31 @@ Creating ${smartRules.length} architectural rules...`);
214885
215001
  created_by: "driftless-init"
214886
215002
  });
214887
215003
  docsAnchored++;
214888
- console.log(` \u2713 ${doc.name} \u2014 ${doc.path}`);
214889
215004
  } catch {
214890
215005
  }
214891
215006
  }
214892
- console.log(` Docs anchored: ${docsAnchored}`);
214893
- console.log("\n---");
214894
- console.log(`Architecture: ${actualModules} modules, ${actualEndpoints} endpoints, ${actualServices} services`);
214895
- console.log(`Relations: ${relationCount} cross-component dependencies mapped`);
214896
- console.log(`Rules: ${rulesCreated} architectural rules created from code analysis`);
214897
- console.log(`Watchers: ${watchersCreated} context watchers auto-generated`);
214898
- console.log(`Docs: ${docsAnchored} existing documents anchored`);
214899
- console.log(`
214900
- View: https://driftless.icu/ecosystem`);
214901
- console.log(`Test: driftless scan --diff`);
214902
- console.log(`Push: driftless context push --files "apps/api/src/auth/auth.controller.ts"`);
215007
+ step("anchoring docs", `${docsAnchored} docs \u2713`);
215008
+ let skillLine = "skipped";
215009
+ try {
215010
+ const skillResult = await runInstallSkill(cwd);
215011
+ const claudeIcon = skillResult.claudeMd === "created" ? "CLAUDE.md created" : skillResult.claudeMd === "updated" ? "CLAUDE.md updated" : "CLAUDE.md ok";
215012
+ const agentsIcon = skillResult.agentsMd === "created" ? "AGENTS.md created" : skillResult.agentsMd === "updated" ? "AGENTS.md updated" : "AGENTS.md ok";
215013
+ skillLine = `${claudeIcon} \xB7 ${agentsIcon} \u2713`;
215014
+ } catch {
215015
+ }
215016
+ step("installing skill", skillLine);
215017
+ console.log("");
215018
+ console.log("\u2713 repo context bootstrapped");
215019
+ console.log(` \u251C\u2500 watchers ${watchersCreated}`);
215020
+ console.log(` \u251C\u2500 rules ${rulesCreated}`);
215021
+ console.log(` \u251C\u2500 docs ${docsAnchored}`);
215022
+ console.log(` \u251C\u2500 components ${components.length}`);
215023
+ console.log(` \u2514\u2500 relations ${relationCount}`);
215024
+ console.log("");
215025
+ console.log(" dashboard \u2192 driftless.icu/ecosystem");
215026
+ console.log(" next \u2192 driftless context list");
215027
+ console.log("");
215028
+ console.log(DIVIDER);
214903
215029
  }
214904
215030
 
214905
215031
  // src/commands/scan.ts
@@ -215015,8 +215141,8 @@ ${result.violations.length} violation(s) found (${rulesEvaluated} rule(s) evalua
215015
215141
 
215016
215142
  // src/commands/context.ts
215017
215143
  init_git();
215018
- var import_node_fs4 = require("node:fs");
215019
- var import_node_path4 = require("node:path");
215144
+ var import_node_fs5 = require("node:fs");
215145
+ var import_node_path5 = require("node:path");
215020
215146
  function parseArgs(args) {
215021
215147
  const flags = {};
215022
215148
  const positional = [];
@@ -215250,7 +215376,7 @@ async function contextCommand(args) {
215250
215376
  }
215251
215377
  process.exit(0);
215252
215378
  }
215253
- const missingFiles = files.filter((f) => !(0, import_node_fs4.existsSync)((0, import_node_path4.resolve)(process.cwd(), f)));
215379
+ const missingFiles = files.filter((f) => !(0, import_node_fs5.existsSync)((0, import_node_path5.resolve)(process.cwd(), f)));
215254
215380
  if (missingFiles.length > 0 && !isJSON) {
215255
215381
  console.error(`File(s) not found locally: ${missingFiles.join(", ")}. Matching by pattern only.`);
215256
215382
  }
@@ -215329,7 +215455,7 @@ async function contextCommand(args) {
215329
215455
  let fileContent;
215330
215456
  if (flags["file"]) {
215331
215457
  try {
215332
- fileContent = (0, import_node_fs4.readFileSync)(flags["file"], "utf-8");
215458
+ fileContent = (0, import_node_fs5.readFileSync)(flags["file"], "utf-8");
215333
215459
  } catch {
215334
215460
  console.error(`File not found: ${flags["file"]}`);
215335
215461
  process.exit(1);
@@ -215494,7 +215620,7 @@ async function contextCommand(args) {
215494
215620
  let fileContent;
215495
215621
  if (docFlag) {
215496
215622
  try {
215497
- fileContent = (0, import_node_fs4.readFileSync)(docFlag, "utf-8");
215623
+ fileContent = (0, import_node_fs5.readFileSync)(docFlag, "utf-8");
215498
215624
  } catch {
215499
215625
  console.error(`File not found: ${docFlag}`);
215500
215626
  process.exit(1);
@@ -215556,7 +215682,7 @@ async function contextCommand(args) {
215556
215682
  console.error("No files provided.");
215557
215683
  process.exit(1);
215558
215684
  }
215559
- const missingFiles = files.filter((f) => !(0, import_node_fs4.existsSync)((0, import_node_path4.resolve)(process.cwd(), f)));
215685
+ const missingFiles = files.filter((f) => !(0, import_node_fs5.existsSync)((0, import_node_path5.resolve)(process.cwd(), f)));
215560
215686
  if (missingFiles.length > 0 && !isJSON) {
215561
215687
  console.error(`File(s) not found locally: ${missingFiles.join(", ")}. Matching by pattern only.`);
215562
215688
  }
@@ -215601,14 +215727,21 @@ Warnings:`);
215601
215727
  emitJSON2(results);
215602
215728
  } else {
215603
215729
  if (results.length > 0) {
215604
- console.log(`Matched ${results.length} topic${results.length === 1 ? "" : "s"}:`);
215730
+ console.log(`Matched ${results.length} context topic${results.length === 1 ? "" : "s"} for ${files.length} file${files.length === 1 ? "" : "s"}:
215731
+ `);
215605
215732
  for (const r of results) {
215606
- console.log(` ${r.context.topic} via ${r.match_reason}`);
215733
+ try {
215734
+ const fullCtx = await api.get(`/workspaces/${workspaceSlug}/watchers/${r.context.topic}`);
215735
+ renderContextHuman(fullCtx);
215736
+ console.log("");
215737
+ } catch {
215738
+ console.log(`\u258C ${r.context.topic} (${r.match_reason})`);
215739
+ console.log(` ${r.context.summary}`);
215740
+ console.log("");
215741
+ }
215607
215742
  }
215608
- console.log(`
215609
- Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
215610
215743
  } else {
215611
- console.log(`No context topics match these files. No watcher covers the changed paths.`);
215744
+ console.log(`No context topics match these files. No context covers the changed paths.`);
215612
215745
  }
215613
215746
  }
215614
215747
  } catch (e) {
@@ -215619,10 +215752,10 @@ Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
215619
215752
  }
215620
215753
  if (subCommand === "export") {
215621
215754
  const dir = flags["dir"] ?? ".driftless/watchers";
215622
- const absDir = (0, import_node_path4.resolve)(process.cwd(), dir);
215755
+ const absDir = (0, import_node_path5.resolve)(process.cwd(), dir);
215623
215756
  try {
215624
215757
  const summaries = await api.get(`/workspaces/${workspaceSlug}/watchers`);
215625
- (0, import_node_fs4.mkdirSync)(absDir, { recursive: true });
215758
+ (0, import_node_fs5.mkdirSync)(absDir, { recursive: true });
215626
215759
  for (const summary of summaries) {
215627
215760
  const slug = summary.topic;
215628
215761
  let ctx;
@@ -215652,10 +215785,10 @@ Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
215652
215785
  }
215653
215786
  if (ctx.description.ownership) lines.push(`ownership: ${safeStr(ctx.description.ownership)}`);
215654
215787
  const yamlContent = lines.join("\n") + "\n";
215655
- (0, import_node_fs4.writeFileSync)((0, import_node_path4.join)(absDir, `${slug}.yaml`), yamlContent, "utf-8");
215788
+ (0, import_node_fs5.writeFileSync)((0, import_node_path5.join)(absDir, `${slug}.yaml`), yamlContent, "utf-8");
215656
215789
  }
215657
215790
  if (!isJSON) {
215658
- console.log(`Exported ${summaries.length} watcher${summaries.length === 1 ? "" : "s"} \u2192 ${dir}/`);
215791
+ console.log(`Exported ${summaries.length} context topic${summaries.length === 1 ? "" : "s"} \u2192 ${dir}/`);
215659
215792
  } else {
215660
215793
  emitJSON2({ exported: summaries.length, dir });
215661
215794
  }
@@ -215667,14 +215800,14 @@ Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
215667
215800
  }
215668
215801
  if (subCommand === "import") {
215669
215802
  const dir = flags["dir"] ?? ".driftless/watchers";
215670
- const absDir = (0, import_node_path4.resolve)(process.cwd(), dir);
215671
- if (!(0, import_node_fs4.existsSync)(absDir)) {
215803
+ const absDir = (0, import_node_path5.resolve)(process.cwd(), dir);
215804
+ if (!(0, import_node_fs5.existsSync)(absDir)) {
215672
215805
  console.error(`Directory not found: ${dir}`);
215673
215806
  process.exit(1);
215674
215807
  }
215675
215808
  let files;
215676
215809
  try {
215677
- files = (0, import_node_fs4.readdirSync)(absDir).filter((f) => f.endsWith(".yaml"));
215810
+ files = (0, import_node_fs5.readdirSync)(absDir).filter((f) => f.endsWith(".yaml"));
215678
215811
  } catch (e) {
215679
215812
  console.error(`Failed to read directory: ${formatError(e)}`);
215680
215813
  process.exit(1);
@@ -215686,10 +215819,10 @@ Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
215686
215819
  let created = 0;
215687
215820
  let updated = 0;
215688
215821
  for (const file of files) {
215689
- const filePath = (0, import_node_path4.join)(absDir, file);
215822
+ const filePath = (0, import_node_path5.join)(absDir, file);
215690
215823
  let raw;
215691
215824
  try {
215692
- raw = (0, import_node_fs4.readFileSync)(filePath, "utf-8");
215825
+ raw = (0, import_node_fs5.readFileSync)(filePath, "utf-8");
215693
215826
  } catch {
215694
215827
  console.error(` skipped ${file} (read error)`);
215695
215828
  continue;
@@ -215775,8 +215908,8 @@ Run 'driftless help context' for full reference.`);
215775
215908
 
215776
215909
  // src/commands/session.ts
215777
215910
  init_git();
215778
- var import_node_fs5 = require("node:fs");
215779
- var import_node_path5 = require("node:path");
215911
+ var import_node_fs6 = require("node:fs");
215912
+ var import_node_path6 = require("node:path");
215780
215913
  function parseArgs2(args) {
215781
215914
  const flags = {};
215782
215915
  const positional = [];
@@ -215838,7 +215971,7 @@ async function sessionCommand(args) {
215838
215971
  }
215839
215972
  process.exit(0);
215840
215973
  }
215841
- const missingFiles = files.filter((f) => !(0, import_node_fs5.existsSync)((0, import_node_path5.resolve)(process.cwd(), f)));
215974
+ const missingFiles = files.filter((f) => !(0, import_node_fs6.existsSync)((0, import_node_path6.resolve)(process.cwd(), f)));
215842
215975
  if (missingFiles.length > 0 && !isJSON) {
215843
215976
  console.error(`Warning: ${missingFiles.length} file(s) not found locally \u2014 matching by pattern only.`);
215844
215977
  }
@@ -215887,7 +216020,7 @@ async function sessionCommand(args) {
215887
216020
  console.log(`Session started for ${files.length} file(s):
215888
216021
  `);
215889
216022
  for (const f of files) {
215890
- const exists = (0, import_node_fs5.existsSync)((0, import_node_path5.resolve)(process.cwd(), f));
216023
+ const exists = (0, import_node_fs6.existsSync)((0, import_node_path6.resolve)(process.cwd(), f));
215891
216024
  console.log(` ${exists ? "\u2713" : "\u2717"} ${f}`);
215892
216025
  }
215893
216026
  if (missingFiles.length > 0) {
@@ -215956,9 +216089,9 @@ Rules that will be evaluated:`);
215956
216089
  console.error(`Repo '${remote.repo}' not found. Run 'driftless init' first.`);
215957
216090
  process.exit(1);
215958
216091
  }
215959
- const { getUncommittedDiff: getUncommittedDiff3, getStagedDiff: getStagedDiff2, getLastCommitHash: getLastCommitHash2, getAuthorName: getAuthorName2 } = await Promise.resolve().then(() => (init_git(), git_exports));
216092
+ const { getUncommittedDiff: getUncommittedDiff2, getStagedDiff: getStagedDiff2, getLastCommitHash: getLastCommitHash2, getAuthorName: getAuthorName2 } = await Promise.resolve().then(() => (init_git(), git_exports));
215960
216093
  const staged = getStagedDiff2();
215961
- const unstaged = getUncommittedDiff3();
216094
+ const unstaged = getUncommittedDiff2();
215962
216095
  const diff = [staged, unstaged].filter(Boolean).join("\n");
215963
216096
  if (!diff && !isJSON) {
215964
216097
  console.log("No changes to scan. Session clean.");
@@ -216048,149 +216181,6 @@ Examples:
216048
216181
  driftless session finish # scan + context report`);
216049
216182
  }
216050
216183
 
216051
- // src/commands/install-skill.ts
216052
- var import_node_fs6 = require("node:fs");
216053
- var import_node_path6 = require("node:path");
216054
- var template = `# Driftless \u2014 Live Repo Context
216055
-
216056
- Driftless Cloud holds the team's living codebase context (topics, anchors, rules, gotchas).
216057
- This file teaches you how to use it. Treat it as part of the toolchain, not documentation.
216058
-
216059
- ## Agent loop
216060
-
216061
- ### 1. Before editing \u2014 pull context
216062
-
216063
- If you know the topic:
216064
-
216065
- \`\`\`bash
216066
- driftless context get <topic>
216067
- \`\`\`
216068
-
216069
- If you don't know which topic applies, discover it:
216070
-
216071
- \`\`\`bash
216072
- driftless context search "<keyword>"
216073
- driftless context list
216074
- \`\`\`
216075
-
216076
- The \`get\` response is a canonical JSON object:
216077
-
216078
- \`\`\`json
216079
- {
216080
- "topic": "<slug>",
216081
- "summary": "...",
216082
- "description": {
216083
- "what": "...",
216084
- "how": "...",
216085
- "decisions": "...",
216086
- "gotchas": [],
216087
- "ownership": "..."
216088
- },
216089
- "anchors": {
216090
- "files": [],
216091
- "patterns": [],
216092
- "repos": [],
216093
- "docs": []
216094
- },
216095
- "components": [],
216096
- "rules": [],
216097
- "invariants": [],
216098
- "violations": [],
216099
- "events": [],
216100
- "required_checks": [],
216101
- "stale": { "is_stale": false, "reason": null },
216102
- "metadata": { "created_by": "...", "last_updated": "...", "source": "manual|auto|doc|agent" }
216103
- }
216104
- \`\`\`
216105
-
216106
- Always read:
216107
- - \`description.what\` and \`description.how\` \u2014 what you're about to touch
216108
- - \`description.decisions\` \u2014 why it's built this way
216109
- - \`description.gotchas\` \u2014 known traps
216110
- - \`anchors.docs\` \u2014 anchored team docs
216111
- - \`stale.is_stale\` \u2014 if true, the context may be outdated; read \`stale.reason\`
216112
-
216113
- Note: \`rules\`, \`invariants\`, \`violations\`, \`events\`, and \`required_checks\` may be empty
216114
- during PR 1; they get populated in subsequent releases. Other fields are authoritative.
216115
-
216116
- ### 2. During work
216117
-
216118
- Respect what the topic tells you. Follow decisions and gotchas. If anchored docs contradict
216119
- code, the docs are usually intent \u2014 verify before changing.
216120
-
216121
- ### 3. Before finishing \u2014 scan
216122
-
216123
- \`\`\`bash
216124
- driftless scan --diff
216125
- \`\`\`
216126
-
216127
- This evaluates your uncommitted changes against active Cloud rules. Exit code:
216128
- - \`0\` \u2014 clean
216129
- - \`1\` \u2014 violations (read each \`explanation\`, fix, re-scan until clean)
216130
-
216131
- Do not push if scan returns violations.
216132
-
216133
- ### 4. After learning \u2014 write context back
216134
-
216135
- If you discovered something durable about the codebase, write it back so the next session benefits:
216136
-
216137
- Update an existing topic:
216138
-
216139
- \`\`\`bash
216140
- driftless context update <topic> \\
216141
- --gotchas "Setting X to Y breaks Z under condition W"
216142
- \`\`\`
216143
-
216144
- Or add a new topic:
216145
-
216146
- \`\`\`bash
216147
- driftless context add "<name>" \\
216148
- --what "What this piece is" \\
216149
- --how "How it works" \\
216150
- --pattern "src/path/**" \\
216151
- --decisions "Why built this way" \\
216152
- --gotchas "Known traps" \\
216153
- --ownership "@team"
216154
- \`\`\`
216155
-
216156
- Field reference:
216157
- - \`--what\`, \`--how\`, \`--decisions\`, \`--gotchas\`, \`--ownership\`
216158
- - \`--pattern\` (glob) or \`--where\` (explicit path) \u2014 mutually exclusive
216159
- - \`--file <path>\` (on \`add\`) \u2014 anchor an existing markdown doc
216160
-
216161
- ## Output format
216162
-
216163
- All CLI commands output JSON by default for agent consumption. Use \`--human\` for readable text.
216164
-
216165
- ## What Driftless is
216166
-
216167
- Living context infrastructure for coding agents. CLI is the daily interface; the dashboard
216168
- is observability for humans, not where agents work.
216169
-
216170
- ## What Driftless is not
216171
-
216172
- Does not modify code. Does not approve or reject PRs. Does not block merges. Is not CI.
216173
- Is not an AI reviewer.
216174
-
216175
- Context delivery + structural verification only.
216176
- `;
216177
- function installSkillCommand() {
216178
- const cwd = process.cwd();
216179
- const agentsPath = (0, import_node_path6.resolve)(cwd, "AGENTS.md");
216180
- if ((0, import_node_fs6.existsSync)(agentsPath)) {
216181
- const existing = (0, import_node_fs6.readFileSync)(agentsPath, "utf8");
216182
- if (existing.includes("# Driftless")) {
216183
- console.log("Driftless section already present in AGENTS.md.");
216184
- return;
216185
- }
216186
- (0, import_node_fs6.writeFileSync)(agentsPath, existing + "\n---\n\n" + template);
216187
- console.log(`Appended Driftless section to ${agentsPath}`);
216188
- } else {
216189
- (0, import_node_fs6.writeFileSync)(agentsPath, template);
216190
- console.log(`Driftless skill installed at ${agentsPath}`);
216191
- }
216192
- }
216193
-
216194
216184
  // src/commands/login.ts
216195
216185
  var import_node_fs7 = require("node:fs");
216196
216186
  var import_node_path7 = require("node:path");
@@ -216408,7 +216398,7 @@ function pad2(s, n) {
216408
216398
  }
216409
216399
 
216410
216400
  // src/index.ts
216411
- var VERSION = "0.1.25";
216401
+ var VERSION = "0.1.26";
216412
216402
  var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
216413
216403
 
216414
216404
  Install: npm install -g @driftless-sh/cli
@@ -216628,7 +216618,7 @@ async function main() {
216628
216618
  await sessionCommand(args.slice(1));
216629
216619
  break;
216630
216620
  case "install-skill":
216631
- installSkillCommand();
216621
+ await installSkillCommand();
216632
216622
  break;
216633
216623
  case "login":
216634
216624
  loginCommand(args.slice(1));