@driftless-sh/cli 0.1.22 → 0.1.23

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
@@ -6,9 +6,16 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
9
12
  var __commonJS = (cb, mod) => function __require() {
10
13
  return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
14
  };
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
12
19
  var __copyProps = (to, from, except, desc) => {
13
20
  if (from && typeof from === "object" || typeof from === "function") {
14
21
  for (let key of __getOwnPropNames(from))
@@ -26,6 +33,101 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
33
  mod
27
34
  ));
28
35
 
36
+ // src/git.ts
37
+ var git_exports = {};
38
+ __export(git_exports, {
39
+ getAuthorName: () => getAuthorName,
40
+ getChangedFilesFromDiff: () => getChangedFilesFromDiff,
41
+ getChangedFilesList: () => getChangedFilesList,
42
+ getGitRemote: () => getGitRemote,
43
+ getLastCommitHash: () => getLastCommitHash,
44
+ getStagedDiff: () => getStagedDiff,
45
+ getUncommittedDiff: () => getUncommittedDiff,
46
+ isGitRepo: () => isGitRepo
47
+ });
48
+ function getGitRemote() {
49
+ try {
50
+ const url = (0, import_node_child_process.execSync)("git config --get remote.origin.url", {
51
+ encoding: "utf8",
52
+ cwd: process.cwd()
53
+ }).trim();
54
+ const match = url.match(/[:/]([^/]+)\/([^/]+?)(?:\.git)?$/);
55
+ if (match) {
56
+ return { org: match[1], repo: match[2] };
57
+ }
58
+ return null;
59
+ } catch {
60
+ return null;
61
+ }
62
+ }
63
+ function isGitRepo() {
64
+ return (0, import_node_fs2.existsSync)((0, import_node_path2.resolve)(process.cwd(), ".git"));
65
+ }
66
+ function getUncommittedDiff() {
67
+ try {
68
+ return (0, import_node_child_process.execSync)("git diff", { encoding: "utf8", cwd: process.cwd() }).trim();
69
+ } catch {
70
+ return "";
71
+ }
72
+ }
73
+ function getStagedDiff() {
74
+ try {
75
+ return (0, import_node_child_process.execSync)("git diff --staged", { encoding: "utf8", cwd: process.cwd() }).trim();
76
+ } catch {
77
+ return "";
78
+ }
79
+ }
80
+ function getLastCommitHash() {
81
+ try {
82
+ return (0, import_node_child_process.execSync)("git rev-parse HEAD", { encoding: "utf8", cwd: process.cwd() }).trim();
83
+ } catch {
84
+ return "unknown";
85
+ }
86
+ }
87
+ function getChangedFilesFromDiff(diff) {
88
+ const text = diff ?? getUncommittedDiff();
89
+ if (!text) return [];
90
+ const files = /* @__PURE__ */ new Set();
91
+ for (const line of text.split("\n")) {
92
+ if (line.startsWith("+++ b/")) {
93
+ files.add(line.slice(6));
94
+ } else if (line.startsWith("--- a/")) {
95
+ } else if (line.startsWith("diff --git ")) {
96
+ const match = line.match(/diff --git a\/(.+?) b\/(.+)$/);
97
+ if (match) files.add(match[2]);
98
+ }
99
+ }
100
+ return [...files];
101
+ }
102
+ function getChangedFilesList() {
103
+ try {
104
+ const unstaged = (0, import_node_child_process.execSync)("git diff --name-only HEAD", { encoding: "utf8", cwd: process.cwd() }).trim();
105
+ const staged = (0, import_node_child_process.execSync)("git diff --staged --name-only", { encoding: "utf8", cwd: process.cwd() }).trim();
106
+ const files = /* @__PURE__ */ new Set();
107
+ if (unstaged) unstaged.split("\n").forEach((f) => files.add(f));
108
+ if (staged) staged.split("\n").forEach((f) => files.add(f));
109
+ return [...files].filter(Boolean);
110
+ } catch {
111
+ return [];
112
+ }
113
+ }
114
+ function getAuthorName() {
115
+ try {
116
+ return (0, import_node_child_process.execSync)("git config user.name", { encoding: "utf8", cwd: process.cwd() }).trim();
117
+ } catch {
118
+ return "unknown";
119
+ }
120
+ }
121
+ var import_node_child_process, import_node_fs2, import_node_path2;
122
+ var init_git = __esm({
123
+ "src/git.ts"() {
124
+ "use strict";
125
+ import_node_child_process = require("node:child_process");
126
+ import_node_fs2 = require("node:fs");
127
+ import_node_path2 = require("node:path");
128
+ }
129
+ });
130
+
29
131
  // ../../libs/scanner/dist/identity/identity.js
30
132
  var require_identity = __commonJS({
31
133
  "../../libs/scanner/dist/identity/identity.js"(exports2) {
@@ -2436,7 +2538,7 @@ var require_typescript = __commonJS({
2436
2538
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
2437
2539
  var __getOwnPropNames2 = Object.getOwnPropertyNames;
2438
2540
  var __hasOwnProp2 = Object.prototype.hasOwnProperty;
2439
- var __export = (target, all) => {
2541
+ var __export2 = (target, all) => {
2440
2542
  for (var name in all)
2441
2543
  __defProp2(target, name, { get: all[name], enumerable: true });
2442
2544
  };
@@ -2450,7 +2552,7 @@ var require_typescript = __commonJS({
2450
2552
  };
2451
2553
  var __toCommonJS = (mod) => (__copyProps2, mod);
2452
2554
  var typescript_exports = {};
2453
- __export(typescript_exports, {
2555
+ __export2(typescript_exports, {
2454
2556
  ANONYMOUS: () => ANONYMOUS,
2455
2557
  AccessFlags: () => AccessFlags,
2456
2558
  AssertionLevel: () => AssertionLevel,
@@ -7537,7 +7639,7 @@ ${lanes.join("\n")}
7537
7639
  }
7538
7640
  var timestamp = nativePerformanceTime ? () => nativePerformanceTime.now() : Date.now;
7539
7641
  var ts_performance_exports = {};
7540
- __export(ts_performance_exports, {
7642
+ __export2(ts_performance_exports, {
7541
7643
  clearMarks: () => clearMarks,
7542
7644
  clearMeasures: () => clearMeasures,
7543
7645
  createTimer: () => createTimer,
@@ -55840,7 +55942,7 @@ ${lanes.join("\n")}
55840
55942
  }
55841
55943
  }
55842
55944
  var ts_moduleSpecifiers_exports = {};
55843
- __export(ts_moduleSpecifiers_exports, {
55945
+ __export2(ts_moduleSpecifiers_exports, {
55844
55946
  RelativePreference: () => RelativePreference,
55845
55947
  countPathComponents: () => countPathComponents,
55846
55948
  forEachFileNameOfModule: () => forEachFileNameOfModule,
@@ -126140,7 +126242,7 @@ ${lanes.join("\n")}
126140
126242
  }
126141
126243
  }
126142
126244
  function createImportCallExpressionAMD(arg, containsLexicalThis) {
126143
- const resolve8 = factory2.createUniqueName("resolve");
126245
+ const resolve9 = factory2.createUniqueName("resolve");
126144
126246
  const reject = factory2.createUniqueName("reject");
126145
126247
  const parameters = [
126146
126248
  factory2.createParameterDeclaration(
@@ -126149,7 +126251,7 @@ ${lanes.join("\n")}
126149
126251
  /*dotDotDotToken*/
126150
126252
  void 0,
126151
126253
  /*name*/
126152
- resolve8
126254
+ resolve9
126153
126255
  ),
126154
126256
  factory2.createParameterDeclaration(
126155
126257
  /*modifiers*/
@@ -126166,7 +126268,7 @@ ${lanes.join("\n")}
126166
126268
  factory2.createIdentifier("require"),
126167
126269
  /*typeArguments*/
126168
126270
  void 0,
126169
- [factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]), resolve8, reject]
126271
+ [factory2.createArrayLiteralExpression([arg || factory2.createOmittedExpression()]), resolve9, reject]
126170
126272
  )
126171
126273
  )
126172
126274
  ]);
@@ -150640,7 +150742,7 @@ ${lanes.join("\n")}
150640
150742
  }
150641
150743
  }
150642
150744
  var ts_JsTyping_exports = {};
150643
- __export(ts_JsTyping_exports, {
150745
+ __export2(ts_JsTyping_exports, {
150644
150746
  NameValidationResult: () => NameValidationResult,
150645
150747
  discoverTypings: () => discoverTypings,
150646
150748
  isTypingUpToDate: () => isTypingUpToDate,
@@ -157686,7 +157788,7 @@ interface Symbol {
157686
157788
  return options;
157687
157789
  }
157688
157790
  var ts_NavigateTo_exports = {};
157689
- __export(ts_NavigateTo_exports, {
157791
+ __export2(ts_NavigateTo_exports, {
157690
157792
  getNavigateToItems: () => getNavigateToItems
157691
157793
  });
157692
157794
  function getNavigateToItems(sourceFiles, checker, cancellationToken, searchValue, maxResultCount, excludeDtsFiles, excludeLibFiles) {
@@ -157791,7 +157893,7 @@ interface Symbol {
157791
157893
  };
157792
157894
  }
157793
157895
  var ts_NavigationBar_exports = {};
157794
- __export(ts_NavigationBar_exports, {
157896
+ __export2(ts_NavigationBar_exports, {
157795
157897
  getNavigationBarItems: () => getNavigationBarItems,
157796
157898
  getNavigationTree: () => getNavigationTree
157797
157899
  });
@@ -158579,7 +158681,7 @@ interface Symbol {
158579
158681
  return text.replace(/\\?(?:\r?\n|[\r\u2028\u2029])/g, "");
158580
158682
  }
158581
158683
  var ts_refactor_exports = {};
158582
- __export(ts_refactor_exports, {
158684
+ __export2(ts_refactor_exports, {
158583
158685
  addExportsInOldFile: () => addExportsInOldFile,
158584
158686
  addImportsForMovedSymbols: () => addImportsForMovedSymbols,
158585
158687
  addNewFileToTsconfig: () => addNewFileToTsconfig,
@@ -162122,7 +162224,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
162122
162224
  }
162123
162225
  }
162124
162226
  var ts_refactor_extractSymbol_exports = {};
162125
- __export(ts_refactor_extractSymbol_exports, {
162227
+ __export2(ts_refactor_extractSymbol_exports, {
162126
162228
  Messages: () => Messages,
162127
162229
  RangeFacts: () => RangeFacts,
162128
162230
  getRangeToExtract: () => getRangeToExtract2,
@@ -166701,7 +166803,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
166701
166803
  return result;
166702
166804
  }
166703
166805
  var ts_BreakpointResolver_exports = {};
166704
- __export(ts_BreakpointResolver_exports, {
166806
+ __export2(ts_BreakpointResolver_exports, {
166705
166807
  spanInSourceFileAtLocation: () => spanInSourceFileAtLocation
166706
166808
  });
166707
166809
  function spanInSourceFileAtLocation(sourceFile, position) {
@@ -167207,7 +167309,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
167207
167309
  }
167208
167310
  }
167209
167311
  var ts_CallHierarchy_exports = {};
167210
- __export(ts_CallHierarchy_exports, {
167312
+ __export2(ts_CallHierarchy_exports, {
167211
167313
  createCallHierarchyItem: () => createCallHierarchyItem,
167212
167314
  getIncomingCalls: () => getIncomingCalls,
167213
167315
  getOutgoingCalls: () => getOutgoingCalls,
@@ -167656,11 +167758,11 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
167656
167758
  return group(collectCallSites(program, declaration), getCallSiteGroupKey, (entries) => convertCallSiteGroupToOutgoingCall(program, entries));
167657
167759
  }
167658
167760
  var ts_classifier_exports = {};
167659
- __export(ts_classifier_exports, {
167761
+ __export2(ts_classifier_exports, {
167660
167762
  v2020: () => ts_classifier_v2020_exports
167661
167763
  });
167662
167764
  var ts_classifier_v2020_exports = {};
167663
- __export(ts_classifier_v2020_exports, {
167765
+ __export2(ts_classifier_v2020_exports, {
167664
167766
  TokenEncodingConsts: () => TokenEncodingConsts,
167665
167767
  TokenModifier: () => TokenModifier,
167666
167768
  TokenType: () => TokenType,
@@ -167668,7 +167770,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
167668
167770
  getSemanticClassifications: () => getSemanticClassifications2
167669
167771
  });
167670
167772
  var ts_codefix_exports = {};
167671
- __export(ts_codefix_exports, {
167773
+ __export2(ts_codefix_exports, {
167672
167774
  PreserveOptionalFlags: () => PreserveOptionalFlags,
167673
167775
  addNewNodeForMemberSymbol: () => addNewNodeForMemberSymbol,
167674
167776
  codeFixAll: () => codeFixAll,
@@ -179589,7 +179691,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
179589
179691
  }
179590
179692
  }
179591
179693
  var ts_Completions_exports = {};
179592
- __export(ts_Completions_exports, {
179694
+ __export2(ts_Completions_exports, {
179593
179695
  CompletionKind: () => CompletionKind,
179594
179696
  CompletionSource: () => CompletionSource,
179595
179697
  SortText: () => SortText,
@@ -183845,7 +183947,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
183845
183947
  return keyword === "abstract" || keyword === "async" || keyword === "await" || keyword === "declare" || keyword === "module" || keyword === "namespace" || keyword === "type" || keyword === "satisfies" || keyword === "as";
183846
183948
  }
183847
183949
  var ts_Completions_StringCompletions_exports = {};
183848
- __export(ts_Completions_StringCompletions_exports, {
183950
+ __export2(ts_Completions_StringCompletions_exports, {
183849
183951
  getStringLiteralCompletionDetails: () => getStringLiteralCompletionDetails,
183850
183952
  getStringLiteralCompletions: () => getStringLiteralCompletions
183851
183953
  });
@@ -184913,7 +185015,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
184913
185015
  return isCallExpression(node.parent) && firstOrUndefined(node.parent.arguments) === node && isIdentifier(node.parent.expression) && node.parent.expression.escapedText === "require";
184914
185016
  }
184915
185017
  var ts_FindAllReferences_exports = {};
184916
- __export(ts_FindAllReferences_exports, {
185018
+ __export2(ts_FindAllReferences_exports, {
184917
185019
  Core: () => Core,
184918
185020
  DefinitionKind: () => DefinitionKind,
184919
185021
  EntryKind: () => EntryKind,
@@ -187497,7 +187599,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
187497
187599
  }
187498
187600
  })(Core || (Core = {}));
187499
187601
  var ts_GoToDefinition_exports = {};
187500
- __export(ts_GoToDefinition_exports, {
187602
+ __export2(ts_GoToDefinition_exports, {
187501
187603
  createDefinitionInfo: () => createDefinitionInfo,
187502
187604
  getDefinitionAndBoundSpan: () => getDefinitionAndBoundSpan,
187503
187605
  getDefinitionAtPosition: () => getDefinitionAtPosition,
@@ -188069,7 +188171,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
188069
188171
  }
188070
188172
  }
188071
188173
  var ts_InlayHints_exports = {};
188072
- __export(ts_InlayHints_exports, {
188174
+ __export2(ts_InlayHints_exports, {
188073
188175
  provideInlayHints: () => provideInlayHints
188074
188176
  });
188075
188177
  var leadingParameterNameCommentRegexFactory = (name) => {
@@ -188870,7 +188972,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
188870
188972
  }
188871
188973
  }
188872
188974
  var ts_JsDoc_exports = {};
188873
- __export(ts_JsDoc_exports, {
188975
+ __export2(ts_JsDoc_exports, {
188874
188976
  getDocCommentTemplateAtPosition: () => getDocCommentTemplateAtPosition,
188875
188977
  getJSDocParameterNameCompletionDetails: () => getJSDocParameterNameCompletionDetails,
188876
188978
  getJSDocParameterNameCompletions: () => getJSDocParameterNameCompletions,
@@ -189307,7 +189409,7 @@ ${newComment.split("\n").map((c) => ` * ${c}`).join("\n")}
189307
189409
  }
189308
189410
  }
189309
189411
  var ts_MapCode_exports = {};
189310
- __export(ts_MapCode_exports, {
189412
+ __export2(ts_MapCode_exports, {
189311
189413
  mapCode: () => mapCode
189312
189414
  });
189313
189415
  function mapCode(sourceFile, contents, focusLocations, host, formatContext, preferences) {
@@ -189511,7 +189613,7 @@ ${content}
189511
189613
  node.forEachChild(resetNodePositions);
189512
189614
  }
189513
189615
  var ts_OrganizeImports_exports = {};
189514
- __export(ts_OrganizeImports_exports, {
189616
+ __export2(ts_OrganizeImports_exports, {
189515
189617
  compareImportsOrRequireStatements: () => compareImportsOrRequireStatements,
189516
189618
  compareModuleSpecifiers: () => compareModuleSpecifiers2,
189517
189619
  getImportDeclarationInsertionIndex: () => getImportDeclarationInsertionIndex,
@@ -190171,7 +190273,7 @@ ${content}
190171
190273
  return compareModuleSpecifiersWorker(m1, m2, comparer);
190172
190274
  }
190173
190275
  var ts_OutliningElementsCollector_exports = {};
190174
- __export(ts_OutliningElementsCollector_exports, {
190276
+ __export2(ts_OutliningElementsCollector_exports, {
190175
190277
  collectElements: () => collectElements
190176
190278
  });
190177
190279
  function collectElements(sourceFile, cancellationToken) {
@@ -190590,7 +190692,7 @@ ${content}
190590
190692
  return findChildOfKind(body, 19, sourceFile);
190591
190693
  }
190592
190694
  var ts_Rename_exports = {};
190593
- __export(ts_Rename_exports, {
190695
+ __export2(ts_Rename_exports, {
190594
190696
  getRenameInfo: () => getRenameInfo,
190595
190697
  nodeIsEligibleForRename: () => nodeIsEligibleForRename
190596
190698
  });
@@ -190746,7 +190848,7 @@ ${content}
190746
190848
  }
190747
190849
  }
190748
190850
  var ts_SignatureHelp_exports = {};
190749
- __export(ts_SignatureHelp_exports, {
190851
+ __export2(ts_SignatureHelp_exports, {
190750
190852
  getArgumentInfoForCompletions: () => getArgumentInfoForCompletions,
190751
190853
  getSignatureHelpItems: () => getSignatureHelpItems
190752
190854
  });
@@ -191292,7 +191394,7 @@ ${content}
191292
191394
  return { name: typeParameter.symbol.name, documentation: typeParameter.symbol.getDocumentationComment(checker), displayParts, isOptional: false, isRest: false };
191293
191395
  }
191294
191396
  var ts_SmartSelectionRange_exports = {};
191295
- __export(ts_SmartSelectionRange_exports, {
191397
+ __export2(ts_SmartSelectionRange_exports, {
191296
191398
  getSmartSelectionRange: () => getSmartSelectionRange
191297
191399
  });
191298
191400
  function getSmartSelectionRange(pos, sourceFile) {
@@ -191523,7 +191625,7 @@ ${content}
191523
191625
  }
191524
191626
  }
191525
191627
  var ts_SymbolDisplay_exports = {};
191526
- __export(ts_SymbolDisplay_exports, {
191628
+ __export2(ts_SymbolDisplay_exports, {
191527
191629
  getSymbolDisplayPartsDocumentationAndSymbolKind: () => getSymbolDisplayPartsDocumentationAndSymbolKind,
191528
191630
  getSymbolKind: () => getSymbolKind,
191529
191631
  getSymbolModifiers: () => getSymbolModifiers
@@ -192491,7 +192593,7 @@ ${content}
192491
192593
  });
192492
192594
  }
192493
192595
  var ts_textChanges_exports = {};
192494
- __export(ts_textChanges_exports, {
192596
+ __export2(ts_textChanges_exports, {
192495
192597
  ChangeTracker: () => ChangeTracker,
192496
192598
  LeadingTriviaOption: () => LeadingTriviaOption,
192497
192599
  TrailingTriviaOption: () => TrailingTriviaOption,
@@ -193908,7 +194010,7 @@ ${options.prefix}` : "\n" : options.prefix
193908
194010
  });
193909
194011
  }
193910
194012
  var ts_formatting_exports = {};
193911
- __export(ts_formatting_exports, {
194013
+ __export2(ts_formatting_exports, {
193912
194014
  FormattingContext: () => FormattingContext,
193913
194015
  FormattingRequestKind: () => FormattingRequestKind,
193914
194016
  RuleAction: () => RuleAction,
@@ -197808,7 +197910,7 @@ ${options.prefix}` : "\n" : options.prefix
197808
197910
  }
197809
197911
  })(SmartIndenter || (SmartIndenter = {}));
197810
197912
  var ts_preparePasteEdits_exports = {};
197811
- __export(ts_preparePasteEdits_exports, {
197913
+ __export2(ts_preparePasteEdits_exports, {
197812
197914
  preparePasteEdits: () => preparePasteEdits
197813
197915
  });
197814
197916
  function preparePasteEdits(sourceFile, copiedFromRange, checker) {
@@ -197846,7 +197948,7 @@ ${options.prefix}` : "\n" : options.prefix
197846
197948
  return shouldProvidePasteEdits;
197847
197949
  }
197848
197950
  var ts_PasteEdits_exports = {};
197849
- __export(ts_PasteEdits_exports, {
197951
+ __export2(ts_PasteEdits_exports, {
197850
197952
  pasteEditsProvider: () => pasteEditsProvider
197851
197953
  });
197852
197954
  var fixId55 = "providePostPasteEdits";
@@ -197953,7 +198055,7 @@ ${options.prefix}` : "\n" : options.prefix
197953
198055
  };
197954
198056
  }
197955
198057
  var ts_exports2 = {};
197956
- __export(ts_exports2, {
198058
+ __export2(ts_exports2, {
197957
198059
  ANONYMOUS: () => ANONYMOUS,
197958
198060
  AccessFlags: () => AccessFlags,
197959
198061
  AssertionLevel: () => AssertionLevel,
@@ -200304,7 +200406,7 @@ ${options.prefix}` : "\n" : options.prefix
200304
200406
  };
200305
200407
  }
200306
200408
  var ts_server_exports3 = {};
200307
- __export(ts_server_exports3, {
200409
+ __export2(ts_server_exports3, {
200308
200410
  ActionInvalidate: () => ActionInvalidate,
200309
200411
  ActionPackageInstalled: () => ActionPackageInstalled,
200310
200412
  ActionSet: () => ActionSet,
@@ -200402,7 +200504,7 @@ ${options.prefix}` : "\n" : options.prefix
200402
200504
  updateProjectIfDirty: () => updateProjectIfDirty
200403
200505
  });
200404
200506
  var ts_server_typingsInstaller_exports = {};
200405
- __export(ts_server_typingsInstaller_exports, {
200507
+ __export2(ts_server_typingsInstaller_exports, {
200406
200508
  TypingsInstaller: () => TypingsInstaller,
200407
200509
  getNpmCommandForInstallation: () => getNpmCommandForInstallation,
200408
200510
  installNpmPackages: () => installNpmPackages,
@@ -200969,7 +201071,7 @@ ${options.prefix}` : "\n" : options.prefix
200969
201071
  return base === "tsconfig.json" || base === "jsconfig.json" ? base : void 0;
200970
201072
  }
200971
201073
  var ts_server_protocol_exports = {};
200972
- __export(ts_server_protocol_exports, {
201074
+ __export2(ts_server_protocol_exports, {
200973
201075
  ClassificationType: () => ClassificationType,
200974
201076
  CommandTypes: () => CommandTypes,
200975
201077
  CompletionTriggerKind: () => CompletionTriggerKind,
@@ -212884,8 +212986,8 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
212884
212986
  installPackage(options) {
212885
212987
  this.packageInstallId++;
212886
212988
  const request2 = { kind: "installPackage", ...options, id: this.packageInstallId };
212887
- const promise = new Promise((resolve8, reject) => {
212888
- (this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map())).set(this.packageInstallId, { resolve: resolve8, reject });
212989
+ const promise = new Promise((resolve9, reject) => {
212990
+ (this.packageInstalledPromise ?? (this.packageInstalledPromise = /* @__PURE__ */ new Map())).set(this.packageInstallId, { resolve: resolve9, reject });
212889
212991
  });
212890
212992
  this.installer.send(request2);
212891
212993
  return promise;
@@ -213043,7 +213145,7 @@ Additional information: BADCLIENT: Bad error code, ${badCode} not found in range
213043
213145
  _TypingsInstallerAdapter.requestDelayMillis = 100;
213044
213146
  var TypingsInstallerAdapter = _TypingsInstallerAdapter;
213045
213147
  var ts_server_exports4 = {};
213046
- __export(ts_server_exports4, {
213148
+ __export2(ts_server_exports4, {
213047
213149
  ActionInvalidate: () => ActionInvalidate,
213048
213150
  ActionPackageInstalled: () => ActionPackageInstalled,
213049
213151
  ActionSet: () => ActionSet,
@@ -214245,11 +214347,16 @@ function getBaseUrl() {
214245
214347
  }
214246
214348
  function parseError(e) {
214247
214349
  const msg = e.message;
214248
- const jsonMatch = msg.match(/\{[^}]*"message"[^}]*\}/);
214350
+ const jsonMatch = msg.match(/\{[\s\S]*\}/);
214249
214351
  if (jsonMatch) {
214250
214352
  try {
214251
214353
  const parsed = JSON.parse(jsonMatch[0]);
214252
- if (parsed.message) return parsed.message;
214354
+ const parts = [];
214355
+ if (parsed.message) parts.push(parsed.message);
214356
+ if (parsed.request_id) parts.push(`request_id: ${parsed.request_id}`);
214357
+ if (parsed.retryable !== void 0) parts.push(`retryable: ${parsed.retryable ? "yes" : "no"}`);
214358
+ if (parsed.endpoint) parts.push(`endpoint: ${parsed.endpoint}`);
214359
+ if (parts.length > 0) return parts.join(" | ");
214253
214360
  } catch {
214254
214361
  }
214255
214362
  }
@@ -214266,7 +214373,7 @@ function parseError(e) {
214266
214373
  return msg;
214267
214374
  }
214268
214375
  function request(method, path, body) {
214269
- return new Promise((resolve8, reject) => {
214376
+ return new Promise((resolve9, reject) => {
214270
214377
  const baseUrl = getBaseUrl();
214271
214378
  const fullUrl = `${baseUrl}${path}`;
214272
214379
  const url = new URL(fullUrl);
@@ -214292,9 +214399,9 @@ function request(method, path, body) {
214292
214399
  return;
214293
214400
  }
214294
214401
  try {
214295
- resolve8(JSON.parse(data));
214402
+ resolve9(JSON.parse(data));
214296
214403
  } catch {
214297
- resolve8(data);
214404
+ resolve9(data);
214298
214405
  }
214299
214406
  });
214300
214407
  }
@@ -214321,70 +214428,8 @@ function formatError(e) {
214321
214428
  return parseError(e);
214322
214429
  }
214323
214430
 
214324
- // src/git.ts
214325
- var import_node_child_process = require("node:child_process");
214326
- var import_node_fs2 = require("node:fs");
214327
- var import_node_path2 = require("node:path");
214328
- function getGitRemote() {
214329
- try {
214330
- const url = (0, import_node_child_process.execSync)("git config --get remote.origin.url", {
214331
- encoding: "utf8",
214332
- cwd: process.cwd()
214333
- }).trim();
214334
- const match = url.match(/[:/]([^/]+)\/([^/]+?)(?:\.git)?$/);
214335
- if (match) {
214336
- return { org: match[1], repo: match[2] };
214337
- }
214338
- return null;
214339
- } catch {
214340
- return null;
214341
- }
214342
- }
214343
- function isGitRepo() {
214344
- return (0, import_node_fs2.existsSync)((0, import_node_path2.resolve)(process.cwd(), ".git"));
214345
- }
214346
- function getUncommittedDiff() {
214347
- try {
214348
- return (0, import_node_child_process.execSync)("git diff", { encoding: "utf8", cwd: process.cwd() }).trim();
214349
- } catch {
214350
- return "";
214351
- }
214352
- }
214353
- function getStagedDiff() {
214354
- try {
214355
- return (0, import_node_child_process.execSync)("git diff --staged", { encoding: "utf8", cwd: process.cwd() }).trim();
214356
- } catch {
214357
- return "";
214358
- }
214359
- }
214360
- function getLastCommitHash() {
214361
- try {
214362
- return (0, import_node_child_process.execSync)("git rev-parse HEAD", { encoding: "utf8", cwd: process.cwd() }).trim();
214363
- } catch {
214364
- return "unknown";
214365
- }
214366
- }
214367
- function getChangedFilesList() {
214368
- try {
214369
- const unstaged = (0, import_node_child_process.execSync)("git diff --name-only HEAD", { encoding: "utf8", cwd: process.cwd() }).trim();
214370
- const staged = (0, import_node_child_process.execSync)("git diff --staged --name-only", { encoding: "utf8", cwd: process.cwd() }).trim();
214371
- const files = /* @__PURE__ */ new Set();
214372
- if (unstaged) unstaged.split("\n").forEach((f) => files.add(f));
214373
- if (staged) staged.split("\n").forEach((f) => files.add(f));
214374
- return [...files].filter(Boolean);
214375
- } catch {
214376
- return [];
214377
- }
214378
- }
214379
- function getAuthorName() {
214380
- try {
214381
- return (0, import_node_child_process.execSync)("git config user.name", { encoding: "utf8", cwd: process.cwd() }).trim();
214382
- } catch {
214383
- return "unknown";
214384
- }
214385
- }
214386
-
214387
214431
  // src/commands/init.ts
214432
+ init_git();
214388
214433
  var import_scanner = __toESM(require_dist());
214389
214434
  var import_node_fs3 = require("node:fs");
214390
214435
  var import_node_path3 = require("node:path");
@@ -214858,6 +214903,10 @@ View: https://driftless.icu/ecosystem`);
214858
214903
  }
214859
214904
 
214860
214905
  // src/commands/scan.ts
214906
+ init_git();
214907
+ function emitJSON(data) {
214908
+ console.log(JSON.stringify(data, null, 2));
214909
+ }
214861
214910
  async function scanCommand(args) {
214862
214911
  if (!isGitRepo()) {
214863
214912
  console.error("Error: not a git repository.");
@@ -214868,6 +214917,7 @@ async function scanCommand(args) {
214868
214917
  console.error("Error: no git remote found.");
214869
214918
  process.exit(1);
214870
214919
  }
214920
+ const isJSON = args.includes("--json");
214871
214921
  let workspace;
214872
214922
  try {
214873
214923
  workspace = await api.get("/me");
@@ -214896,7 +214946,8 @@ async function scanCommand(args) {
214896
214946
  diff = getUncommittedDiff();
214897
214947
  source = "uncommitted changes";
214898
214948
  if (!diff) {
214899
- console.log("No uncommitted changes. Clean.");
214949
+ if (isJSON) emitJSON({ status: "clean", files: [], rules_evaluated: 0 });
214950
+ else console.log("No uncommitted changes. Clean.");
214900
214951
  process.exit(0);
214901
214952
  }
214902
214953
  changedFiles = diff.split("\n").filter((line) => line.startsWith("+++ b/") || line.startsWith("--- a/")).map((line) => line.replace(/^\+\+\+ b\//, "").replace(/^--- a\//, "")).filter((f, i, arr) => arr.indexOf(f) === i);
@@ -214906,15 +214957,18 @@ async function scanCommand(args) {
214906
214957
  diff = [staged, unstaged].filter(Boolean).join("\n");
214907
214958
  source = staged && unstaged ? "staged + unstaged" : staged ? "staged" : "unstaged";
214908
214959
  if (!diff) {
214909
- console.log("No changes to scan. Clean.");
214960
+ if (isJSON) emitJSON({ status: "clean", files: [], rules_evaluated: 0 });
214961
+ else console.log("No changes to scan. Clean.");
214910
214962
  process.exit(0);
214911
214963
  }
214912
214964
  }
214913
214965
  const commitHash = getLastCommitHash();
214914
214966
  const author = getAuthorName();
214915
- console.log(`Scanning ${source}...`);
214916
- if (changedFiles.length > 0) {
214917
- console.log(` Files: ${changedFiles.join(", ")}`);
214967
+ if (!isJSON) {
214968
+ console.log(`Scanning ${source}...`);
214969
+ if (changedFiles.length > 0) {
214970
+ console.log(` Files: ${changedFiles.join(", ")}`);
214971
+ }
214918
214972
  }
214919
214973
  const result = await api.post("/scan", {
214920
214974
  workspace_id: workspace.workspace_id,
@@ -214925,29 +214979,42 @@ async function scanCommand(args) {
214925
214979
  });
214926
214980
  const rulesEvaluated = result.rules_evaluated || 0;
214927
214981
  if (result.status === "clean") {
214928
- console.log(`Clean \u2014 ${rulesEvaluated} rule(s) evaluated, no violations.`);
214982
+ if (isJSON) {
214983
+ emitJSON({ status: "clean", files: changedFiles, rules_evaluated: rulesEvaluated, violations: [] });
214984
+ } else {
214985
+ console.log(`Clean \u2014 ${rulesEvaluated} rule(s) evaluated, no violations.`);
214986
+ }
214929
214987
  process.exit(0);
214930
214988
  }
214931
214989
  if (!result.violations || result.violations.length === 0) {
214932
- console.log(`Clean \u2014 ${rulesEvaluated} rule(s) evaluated, no violations.`);
214990
+ if (isJSON) {
214991
+ emitJSON({ status: "clean", files: changedFiles, rules_evaluated: rulesEvaluated, violations: [] });
214992
+ } else {
214993
+ console.log(`Clean \u2014 ${rulesEvaluated} rule(s) evaluated, no violations.`);
214994
+ }
214933
214995
  process.exit(0);
214934
214996
  }
214935
- console.log(`
214997
+ if (isJSON) {
214998
+ emitJSON({ status: "violated", files: changedFiles, rules_evaluated: rulesEvaluated, violations: result.violations });
214999
+ } else {
215000
+ console.log(`
214936
215001
  ${result.violations.length} violation(s) found (${rulesEvaluated} rule(s) evaluated):
214937
215002
  `);
214938
- for (const v of result.violations) {
214939
- const severityColor = v.severity === "critical" || v.severity === "high" ? "\x1B[31m" : "\x1B[33m";
214940
- console.log(` ${severityColor}[${v.severity.toUpperCase()}]\x1B[0m ${v.rule_name}`);
214941
- console.log(` ${v.explanation}`);
214942
- console.log(` File: ${v.file_path}:${v.line_number}`);
214943
- console.log(` Code: ${v.diff_snippet}`);
214944
- console.log();
215003
+ for (const v of result.violations) {
215004
+ const severityColor = v.severity === "critical" || v.severity === "high" ? "\x1B[31m" : "\x1B[33m";
215005
+ console.log(` ${severityColor}[${v.severity.toUpperCase()}]\x1B[0m ${v.rule_name}`);
215006
+ console.log(` ${v.explanation}`);
215007
+ console.log(` File: ${v.file_path}:${v.line_number}`);
215008
+ console.log(` Code: ${v.diff_snippet}`);
215009
+ console.log();
215010
+ }
215011
+ console.log("Fix these violations before pushing.");
214945
215012
  }
214946
- console.log("Fix these violations before pushing.");
214947
215013
  process.exit(1);
214948
215014
  }
214949
215015
 
214950
215016
  // src/commands/context.ts
215017
+ init_git();
214951
215018
  var import_node_fs4 = require("node:fs");
214952
215019
  var import_node_path4 = require("node:path");
214953
215020
  function parseArgs(args) {
@@ -214969,16 +215036,27 @@ function parseArgs(args) {
214969
215036
  }
214970
215037
  return { flags, positional };
214971
215038
  }
214972
- function emitJSON(data) {
215039
+ function emitJSON2(data) {
214973
215040
  console.log(JSON.stringify(data, null, 2));
214974
215041
  }
214975
215042
  function pad(s, n) {
214976
215043
  if (s.length >= n) return s;
214977
215044
  return s + " ".repeat(n - s.length);
214978
215045
  }
215046
+ function formatBadges(badges) {
215047
+ const display = [];
215048
+ for (const b of badges) {
215049
+ if (b === "manual" || b === "fresh") continue;
215050
+ display.push(`[${b}]`);
215051
+ }
215052
+ return display.join(" ");
215053
+ }
214979
215054
  function renderSummaryHuman(items) {
214980
215055
  if (items.length === 0) {
214981
- console.log("No context topics found.");
215056
+ console.log("No context topics yet.");
215057
+ console.log("\nNext:");
215058
+ console.log(' driftless context add "auth-boundaries" --what "..." --how "..." --pattern "src/auth/**"');
215059
+ console.log(" driftless init # auto-generate topics from your codebase");
214982
215060
  return;
214983
215061
  }
214984
215062
  const topicWidth = Math.max(...items.map((i) => i.topic.length), 12) + 2;
@@ -214991,14 +215069,6 @@ function renderSummaryHuman(items) {
214991
215069
  console.log(`
214992
215070
  ${items.length} topic${items.length === 1 ? "" : "s"}.`);
214993
215071
  }
214994
- function formatBadges(badges) {
214995
- const display = [];
214996
- for (const b of badges) {
214997
- if (b === "manual" || b === "fresh") continue;
214998
- display.push(`[${b}]`);
214999
- }
215000
- return display.join(" ");
215001
- }
215002
215072
  function renderContextHuman(ctx) {
215003
215073
  console.log(`\u258C ${ctx.topic}`);
215004
215074
  console.log(` ${ctx.summary}`);
@@ -215028,9 +215098,7 @@ function renderContextHuman(ctx) {
215028
215098
  console.log(`
215029
215099
  docs (${ctx.anchors.docs.length})`);
215030
215100
  for (const d of ctx.anchors.docs) {
215031
- const preview = d.content.split("\n").slice(0, 3).join("\n ");
215032
- console.log(` ${d.path || "(anchored)"}
215033
- ${preview}${d.content.split("\n").length > 3 ? "\n ..." : ""}`);
215101
+ console.log(` ${d.path || "(anchored)"}`);
215034
215102
  }
215035
215103
  }
215036
215104
  if (ctx.components.length > 0) {
@@ -215084,7 +215152,7 @@ function renderMatchFilesHuman(results, files) {
215084
215152
  console.log(`No matching context for ${files.length} changed file${files.length === 1 ? "" : "s"}.`);
215085
215153
  return;
215086
215154
  }
215087
- console.log(`${results.length} topic${results.length === 1 ? "" : "s"} matched for ${files.length} file${files.length === 1 ? "" : "s"}:
215155
+ console.log(`Matched ${results.length} topic${results.length === 1 ? "" : "s"} for ${files.length} file${files.length === 1 ? "" : "s"}:
215088
215156
  `);
215089
215157
  for (const r of results) {
215090
215158
  const ctx = r.context;
@@ -215100,6 +215168,15 @@ function renderMatchFilesHuman(results, files) {
215100
215168
  console.log("");
215101
215169
  }
215102
215170
  }
215171
+ function countLocalFilesMatching(pattern) {
215172
+ try {
215173
+ const { globSync } = require("node:fs");
215174
+ const matches = globSync(pattern, { cwd: process.cwd() });
215175
+ return matches.length;
215176
+ } catch {
215177
+ return 0;
215178
+ }
215179
+ }
215103
215180
  async function resolveWorkspaceSlug() {
215104
215181
  try {
215105
215182
  const me = await api.get("/me");
@@ -215121,7 +215198,7 @@ async function contextCommand(args) {
215121
215198
  const workspaceSlug = await resolveWorkspaceSlug();
215122
215199
  const { flags, positional } = parseArgs(args);
215123
215200
  const subCommand = positional[0];
215124
- const isHuman = !!flags["human"];
215201
+ const isJSON = !!flags["json"];
215125
215202
  if (subCommand === "list") {
215126
215203
  const query = [];
215127
215204
  if (flags["stale"]) query.push("stale=true");
@@ -215132,13 +215209,16 @@ async function contextCommand(args) {
215132
215209
  const qs = query.length > 0 ? `?${query.join("&")}` : "";
215133
215210
  try {
215134
215211
  const summaries = await api.get(`/workspaces/${workspaceSlug}/watchers${qs}`);
215135
- if (summaries.length === 0) {
215136
- console.error("No context topics yet. Run `driftless init` to auto-generate watchers from your codebase.");
215137
- }
215138
- if (isHuman) {
215139
- renderSummaryHuman(summaries);
215212
+ if (summaries.length === 0 && !isJSON) {
215213
+ console.log("No context topics yet.");
215214
+ console.log("\nNext:");
215215
+ console.log(' driftless context add "auth-boundaries" --what "..." --how "..." --pattern "src/auth/**"');
215216
+ console.log(" driftless init # auto-generate topics from your codebase");
215217
+ }
215218
+ if (isJSON) {
215219
+ emitJSON2(summaries);
215140
215220
  } else {
215141
- emitJSON(summaries);
215221
+ renderSummaryHuman(summaries);
215142
215222
  }
215143
215223
  } catch (e) {
215144
215224
  console.error(`List failed: ${formatError(e)}`);
@@ -215162,25 +215242,29 @@ async function contextCommand(args) {
215162
215242
  files = getChangedFilesList();
215163
215243
  }
215164
215244
  if (files.length === 0) {
215165
- if (isHuman) {
215245
+ if (!isJSON) {
215166
215246
  console.log("No local changes detected.");
215167
215247
  } else {
215168
- emitJSON([]);
215248
+ emitJSON2([]);
215169
215249
  }
215170
215250
  process.exit(0);
215171
215251
  }
215252
+ const missingFiles = files.filter((f) => !(0, import_node_fs4.existsSync)((0, import_node_path4.resolve)(process.cwd(), f)));
215253
+ if (missingFiles.length > 0 && !isJSON) {
215254
+ console.error(`File(s) not found locally: ${missingFiles.join(", ")}. Matching by pattern only.`);
215255
+ }
215172
215256
  try {
215173
215257
  const results = await api.post(
215174
215258
  `/workspaces/${workspaceSlug}/watchers/match-files`,
215175
215259
  { files }
215176
215260
  );
215177
- if (results.length === 0) {
215178
- console.error(`No context topics match these files. No watcher covers the changed paths.`);
215261
+ if (results.length === 0 && !isJSON) {
215262
+ console.log(`No context topics match these files.`);
215179
215263
  }
215180
- if (isHuman) {
215181
- renderMatchFilesHuman(results, files);
215264
+ if (isJSON) {
215265
+ emitJSON2(results);
215182
215266
  } else {
215183
- emitJSON(results);
215267
+ renderMatchFilesHuman(results, files);
215184
215268
  }
215185
215269
  } catch (e) {
215186
215270
  console.error(`Match failed: ${formatError(e)}`);
@@ -215189,21 +215273,21 @@ async function contextCommand(args) {
215189
215273
  return;
215190
215274
  }
215191
215275
  if (!slug) {
215192
- console.error("Usage: driftless context get <topic> [--human]");
215193
- console.error(' driftless context get --files "path1,path2" [--human]');
215194
- console.error(" driftless context get --diff [--human]");
215276
+ console.error("Usage: driftless context get <topic>");
215277
+ console.error(' driftless context get --files "path1,path2"');
215278
+ console.error(" driftless context get --diff");
215195
215279
  process.exit(1);
215196
215280
  }
215197
215281
  try {
215198
215282
  const ctx = await api.get(`/workspaces/${workspaceSlug}/watchers/${slug}`);
215199
- if (isHuman) {
215200
- renderContextHuman(ctx);
215201
- } else {
215283
+ if (isJSON) {
215202
215284
  const sanitized = JSON.parse(JSON.stringify(ctx, (_key, value) => {
215203
215285
  if (typeof value === "string" && value.length > 500) return value.slice(0, 500) + "\u2026";
215204
215286
  return value;
215205
215287
  }));
215206
- emitJSON(sanitized);
215288
+ emitJSON2(sanitized);
215289
+ } else {
215290
+ renderContextHuman(ctx);
215207
215291
  }
215208
215292
  } catch (e) {
215209
215293
  console.error(`Topic '${slug}' not found.`);
@@ -215214,20 +215298,20 @@ async function contextCommand(args) {
215214
215298
  if (subCommand === "search") {
215215
215299
  const query = positional.slice(1).join(" ");
215216
215300
  if (!query) {
215217
- console.error('Usage: driftless context search "<query>" [--human]');
215301
+ console.error('Usage: driftless context search "<query>"');
215218
215302
  process.exit(1);
215219
215303
  }
215220
215304
  try {
215221
215305
  const results = await api.get(
215222
215306
  `/workspaces/${workspaceSlug}/watchers/search?q=${encodeURIComponent(query)}`
215223
215307
  );
215224
- if (results.length === 0) {
215225
- console.error(`No context topics matching "${query}".`);
215308
+ if (results.length === 0 && !isJSON) {
215309
+ console.log(`No context topics matching "${query}".`);
215226
215310
  }
215227
- if (isHuman) {
215228
- renderSummaryHuman(results);
215311
+ if (isJSON) {
215312
+ emitJSON2(results);
215229
215313
  } else {
215230
- emitJSON(results);
215314
+ renderSummaryHuman(results);
215231
215315
  }
215232
215316
  } catch (e) {
215233
215317
  console.error(`Search failed: ${formatError(e)}`);
@@ -215244,32 +215328,41 @@ async function contextCommand(args) {
215244
215328
  let fileContent;
215245
215329
  if (flags["file"]) {
215246
215330
  try {
215247
- const fs = await import("node:fs");
215248
- fileContent = fs.readFileSync(flags["file"], "utf-8");
215331
+ fileContent = (0, import_node_fs4.readFileSync)(flags["file"], "utf-8");
215249
215332
  } catch {
215250
215333
  console.error(`File not found: ${flags["file"]}`);
215251
215334
  process.exit(1);
215252
215335
  }
215253
215336
  }
215337
+ const pattern = flags["pattern"];
215338
+ if (pattern && !isJSON) {
215339
+ const localCount = countLocalFilesMatching(pattern);
215340
+ if (localCount === 0) {
215341
+ console.error(`Warning: pattern "${pattern}" matches 0 files locally. Topic created anyway.`);
215342
+ }
215343
+ }
215254
215344
  try {
215255
215345
  const result = await api.post(`/workspaces/${workspaceSlug}/watchers`, {
215256
215346
  name,
215257
215347
  what: flags["what"],
215258
215348
  how: flags["how"],
215259
215349
  where_files: flags["where"] ? [flags["where"]] : void 0,
215260
- pattern: flags["pattern"],
215350
+ pattern,
215261
215351
  decisions: flags["decisions"],
215262
215352
  gotchas: flags["gotchas"],
215263
215353
  ownership: flags["ownership"],
215264
215354
  file_content: fileContent
215265
215355
  });
215266
- if (isHuman) {
215267
- console.log(`Topic '${name}' created.`);
215356
+ if (isJSON) {
215357
+ emitJSON2(result);
215268
215358
  } else {
215269
- emitJSON(result);
215359
+ console.log(`Topic '${name}' created.`);
215360
+ if (pattern) console.log(` pattern: ${pattern}`);
215361
+ if (flags["what"]) console.log(` what: ${flags["what"]}`);
215362
+ if (flags["how"]) console.log(` how: ${flags["how"]}`);
215270
215363
  }
215271
215364
  } catch (e) {
215272
- console.error(`Failed to create topic: ${e.message}`);
215365
+ console.error(`Failed to create topic: ${formatError(e)}`);
215273
215366
  process.exit(1);
215274
215367
  }
215275
215368
  return;
@@ -215289,17 +215382,11 @@ async function contextCommand(args) {
215289
215382
  if (flags["gotchas"]) updates.gotchas = flags["gotchas"];
215290
215383
  if (flags["ownership"]) updates.ownership = flags["ownership"];
215291
215384
  const gotchaFlag = flags["gotcha"] || flags["gotchas_append"];
215292
- if (gotchaFlag) {
215293
- updates._append_gotchas = gotchaFlag;
215294
- }
215385
+ if (gotchaFlag) updates._append_gotchas = gotchaFlag;
215295
215386
  const invariantFlag = flags["invariant"];
215296
- if (invariantFlag) {
215297
- updates._append_invariants = invariantFlag;
215298
- }
215387
+ if (invariantFlag) updates._append_invariants = invariantFlag;
215299
215388
  const checkFlag = flags["check"];
215300
- if (checkFlag) {
215301
- updates._append_required_checks = checkFlag;
215302
- }
215389
+ if (checkFlag) updates._append_required_checks = checkFlag;
215303
215390
  const enforceFlag = flags["enforce"];
215304
215391
  if (enforceFlag && typeof enforceFlag === "string") {
215305
215392
  updates._enforce_rule = enforceFlag;
@@ -215319,12 +215406,28 @@ async function contextCommand(args) {
215319
215406
  console.error('PR 3: --gotcha, --invariant, --check, --enforce "<rule-description>"');
215320
215407
  process.exit(1);
215321
215408
  }
215409
+ if (flags["dry-run"]) {
215410
+ console.log(`Would update topic '${slug}':`);
215411
+ for (const [key, value] of Object.entries(updates)) {
215412
+ if (key.startsWith("_")) continue;
215413
+ console.log(` ${key}: ${value}`);
215414
+ }
215415
+ if (!isJSON) {
215416
+ console.log("\n(Dry run \u2014 no changes written to Driftless Cloud)");
215417
+ }
215418
+ if (isJSON) {
215419
+ emitJSON2({ dry_run: true, topic: slug, updates });
215420
+ }
215421
+ process.exit(0);
215422
+ }
215322
215423
  try {
215323
215424
  const result = await api.patch(
215324
215425
  `/workspaces/${workspaceSlug}/watchers/${slug}`,
215325
215426
  updates
215326
215427
  );
215327
- if (isHuman) {
215428
+ if (isJSON) {
215429
+ emitJSON2(result);
215430
+ } else {
215328
215431
  renderContextHuman(result);
215329
215432
  if (result.enforcement) {
215330
215433
  if (result.enforcement.created) {
@@ -215336,11 +215439,9 @@ async function contextCommand(args) {
215336
215439
  \u26A0 enforce: ${result.enforcement.reason}`);
215337
215440
  }
215338
215441
  }
215339
- } else {
215340
- emitJSON(result);
215341
215442
  }
215342
215443
  } catch (e) {
215343
- console.error(`Failed to update topic: ${e.message}`);
215444
+ console.error(`Failed to update topic: ${formatError(e)}`);
215344
215445
  process.exit(1);
215345
215446
  }
215346
215447
  return;
@@ -215351,12 +215452,25 @@ async function contextCommand(args) {
215351
215452
  console.error("Usage: driftless context delete <topic>");
215352
215453
  process.exit(1);
215353
215454
  }
215455
+ if (flags["dry-run"]) {
215456
+ console.log(`Would delete topic '${slug}'.`);
215457
+ if (!isJSON) {
215458
+ console.log("(Dry run \u2014 no changes written to Driftless Cloud)");
215459
+ }
215460
+ if (isJSON) {
215461
+ emitJSON2({ dry_run: true, action: "delete", topic: slug });
215462
+ }
215463
+ process.exit(0);
215464
+ }
215354
215465
  try {
215355
215466
  await api.delete(`/workspaces/${workspaceSlug}/watchers/${slug}`);
215356
- if (isHuman) console.log(`Topic '${slug}' deleted.`);
215357
- else emitJSON({ status: "deleted", topic: slug });
215467
+ if (isJSON) {
215468
+ emitJSON2({ status: "deleted", topic: slug });
215469
+ } else {
215470
+ console.log(`Topic '${slug}' deleted.`);
215471
+ }
215358
215472
  } catch (e) {
215359
- console.error(`Failed to delete topic: ${e.message}`);
215473
+ console.error(`Failed to delete topic: ${formatError(e)}`);
215360
215474
  process.exit(1);
215361
215475
  }
215362
215476
  return;
@@ -215379,8 +215493,7 @@ async function contextCommand(args) {
215379
215493
  let fileContent;
215380
215494
  if (docFlag) {
215381
215495
  try {
215382
- const fs = await import("node:fs");
215383
- fileContent = fs.readFileSync(docFlag, "utf-8");
215496
+ fileContent = (0, import_node_fs4.readFileSync)(docFlag, "utf-8");
215384
215497
  } catch {
215385
215498
  console.error(`File not found: ${docFlag}`);
215386
215499
  process.exit(1);
@@ -215392,23 +215505,37 @@ async function contextCommand(args) {
215392
215505
  if (filesFlag) updates.where_files = filesFlag.split(",").map((f) => f.trim()).filter(Boolean);
215393
215506
  if (patternFlag) updates.pattern = patternFlag;
215394
215507
  updates._event_detail = `ANCHOR_ADDED: doc=${!!fileContent}, note=${!!noteFlag}, files=${filesFlag || "none"}`;
215508
+ if (flags["dry-run"]) {
215509
+ console.log(`Would anchor topic '${slug}':`);
215510
+ if (docFlag) console.log(` doc: ${docFlag} (${fileContent ? fileContent.length : 0} chars)`);
215511
+ if (noteFlag) console.log(` note: ${noteFlag}`);
215512
+ if (filesFlag) console.log(` files: ${filesFlag}`);
215513
+ if (patternFlag) console.log(` pattern: ${patternFlag}`);
215514
+ if (!isJSON) {
215515
+ console.log("\n(Dry run \u2014 no changes written to Driftless Cloud)");
215516
+ }
215517
+ if (isJSON) {
215518
+ emitJSON2({ dry_run: true, action: "anchor", topic: slug, doc: docFlag, note: noteFlag, files: filesFlag, pattern: patternFlag });
215519
+ }
215520
+ process.exit(0);
215521
+ }
215395
215522
  try {
215396
215523
  const result = await api.patch(
215397
215524
  `/workspaces/${workspaceSlug}/watchers/${slug}`,
215398
215525
  updates
215399
215526
  );
215400
- if (isHuman) {
215401
- console.log(`Topic '${slug}' anchored.`);
215402
- if (fileContent) console.log(` doc: ${docFlag} (${fileContent.length} chars)`);
215403
- if (noteFlag) console.log(` note: ${noteFlag}`);
215404
- if (filesFlag) console.log(` files: ${filesFlag}`);
215405
- if (patternFlag) console.log(` pattern: ${patternFlag}`);
215406
- } else {
215527
+ if (isJSON) {
215407
215528
  const sanitized = JSON.parse(JSON.stringify(result, (_key, value) => {
215408
215529
  if (typeof value === "string" && value.length > 500) return value.slice(0, 500) + "\u2026";
215409
215530
  return value;
215410
215531
  }));
215411
- emitJSON(sanitized);
215532
+ emitJSON2(sanitized);
215533
+ } else {
215534
+ console.log(`Topic '${slug}' anchored.`);
215535
+ if (docFlag) console.log(` doc: ${docFlag} (${fileContent ? fileContent.length : 0} chars)`);
215536
+ if (noteFlag) console.log(` note: ${noteFlag}`);
215537
+ if (filesFlag) console.log(` files: ${filesFlag}`);
215538
+ if (patternFlag) console.log(` pattern: ${patternFlag}`);
215412
215539
  }
215413
215540
  } catch (e) {
215414
215541
  console.error(`Anchor failed: ${formatError(e)}`);
@@ -215428,21 +215555,59 @@ async function contextCommand(args) {
215428
215555
  process.exit(1);
215429
215556
  }
215430
215557
  const missingFiles = files.filter((f) => !(0, import_node_fs4.existsSync)((0, import_node_path4.resolve)(process.cwd(), f)));
215431
- if (missingFiles.length > 0) {
215558
+ if (missingFiles.length > 0 && !isJSON) {
215432
215559
  console.error(`File(s) not found locally: ${missingFiles.join(", ")}. Matching by pattern only.`);
215433
215560
  }
215561
+ if (flags["dry-run"]) {
215562
+ try {
215563
+ const results = await api.post(
215564
+ `/workspaces/${workspaceSlug}/watchers/match-files`,
215565
+ { files }
215566
+ );
215567
+ console.log(`Would match ${results.length} topic${results.length === 1 ? "" : "s"}:`);
215568
+ for (const r of results) {
215569
+ console.log(` ${r.context.topic} via ${r.match_reason}`);
215570
+ }
215571
+ if (missingFiles.length > 0) {
215572
+ console.log(`
215573
+ Warnings:`);
215574
+ for (const f of missingFiles) {
215575
+ console.log(` ${f} does not exist locally \u2014 matched by pattern only`);
215576
+ }
215577
+ }
215578
+ if (!isJSON) {
215579
+ console.log("\n(Dry run \u2014 no context delivered)");
215580
+ }
215581
+ if (isJSON) {
215582
+ emitJSON2({ dry_run: true, matched: results.map((r) => ({ topic: r.context.topic, reason: r.match_reason })), missing_files: missingFiles });
215583
+ }
215584
+ } catch (e) {
215585
+ console.error(`Dry run failed: ${formatError(e)}`);
215586
+ process.exit(1);
215587
+ }
215588
+ process.exit(0);
215589
+ }
215434
215590
  try {
215435
215591
  const results = await api.post(
215436
215592
  `/workspaces/${workspaceSlug}/watchers/match-files`,
215437
215593
  { files }
215438
215594
  );
215439
- if (results.length === 0) {
215440
- console.error(`No context topics match these files. No watcher covers the changed paths.`);
215595
+ if (results.length === 0 && !isJSON) {
215596
+ console.log(`No context topics match these files.`);
215441
215597
  }
215442
- if (isHuman) {
215443
- renderMatchFilesHuman(results, files);
215598
+ if (isJSON) {
215599
+ emitJSON2(results);
215444
215600
  } else {
215445
- emitJSON(results);
215601
+ if (results.length > 0) {
215602
+ console.log(`Matched ${results.length} topic${results.length === 1 ? "" : "s"}:`);
215603
+ for (const r of results) {
215604
+ console.log(` ${r.context.topic} via ${r.match_reason}`);
215605
+ }
215606
+ console.log(`
215607
+ Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
215608
+ } else {
215609
+ console.log(`No context topics match these files. No watcher covers the changed paths.`);
215610
+ }
215446
215611
  }
215447
215612
  } catch (e) {
215448
215613
  console.error(`Push failed: ${formatError(e)}`);
@@ -215455,9 +215620,284 @@ async function contextCommand(args) {
215455
215620
  Run 'driftless help context' for full reference.`);
215456
215621
  }
215457
215622
 
215458
- // src/commands/install-skill.ts
215623
+ // src/commands/session.ts
215624
+ init_git();
215459
215625
  var import_node_fs5 = require("node:fs");
215460
215626
  var import_node_path5 = require("node:path");
215627
+ function parseArgs2(args) {
215628
+ const flags = {};
215629
+ const positional = [];
215630
+ for (let i = 0; i < args.length; i++) {
215631
+ if (args[i].startsWith("--")) {
215632
+ const key = args[i].slice(2);
215633
+ const next = args[i + 1];
215634
+ if (next && !next.startsWith("--")) {
215635
+ flags[key] = next;
215636
+ i++;
215637
+ } else {
215638
+ flags[key] = true;
215639
+ }
215640
+ } else {
215641
+ positional.push(args[i]);
215642
+ }
215643
+ }
215644
+ return { flags, positional };
215645
+ }
215646
+ function emitJSON3(data) {
215647
+ console.log(JSON.stringify(data, null, 2));
215648
+ }
215649
+ async function resolveWorkspaceSlug2() {
215650
+ try {
215651
+ const me = await api.get("/me");
215652
+ if (me?.slug) return me.slug;
215653
+ } catch {
215654
+ }
215655
+ const remote = getGitRemote();
215656
+ if (!remote) {
215657
+ console.error("Error: no git remote found.");
215658
+ process.exit(1);
215659
+ }
215660
+ return remote.org;
215661
+ }
215662
+ async function sessionCommand(args) {
215663
+ if (!isGitRepo()) {
215664
+ console.error("Error: not a git repository.");
215665
+ process.exit(1);
215666
+ }
215667
+ const workspaceSlug = await resolveWorkspaceSlug2();
215668
+ const { flags, positional } = parseArgs2(args);
215669
+ const subCommand = positional[0];
215670
+ const isJSON = !!flags["json"];
215671
+ if (subCommand === "start") {
215672
+ const filesFlag = flags["files"];
215673
+ let files = [];
215674
+ if (filesFlag) {
215675
+ files = filesFlag.split(",").map((f) => f.trim()).filter(Boolean);
215676
+ } else {
215677
+ files = getChangedFilesList();
215678
+ }
215679
+ if (files.length === 0) {
215680
+ if (!isJSON) {
215681
+ console.log("No files specified and no local changes detected.");
215682
+ console.log('Usage: driftless session start --files "src/auth/**"');
215683
+ } else {
215684
+ emitJSON3({ files: [], topics: [], docs: [], rules: [], gotchas: [] });
215685
+ }
215686
+ process.exit(0);
215687
+ }
215688
+ const missingFiles = files.filter((f) => !(0, import_node_fs5.existsSync)((0, import_node_path5.resolve)(process.cwd(), f)));
215689
+ if (missingFiles.length > 0 && !isJSON) {
215690
+ console.error(`Warning: ${missingFiles.length} file(s) not found locally \u2014 matching by pattern only.`);
215691
+ }
215692
+ try {
215693
+ const results = await api.post(
215694
+ `/workspaces/${workspaceSlug}/watchers/match-files`,
215695
+ { files }
215696
+ );
215697
+ const topics = [];
215698
+ const docs = [];
215699
+ const rules = [];
215700
+ const gotchas = [];
215701
+ const allWhat = [];
215702
+ const allHow = [];
215703
+ for (const r of results) {
215704
+ const ctx = r.context;
215705
+ topics.push(ctx.topic);
215706
+ if (ctx.description.what) allWhat.push(ctx.description.what);
215707
+ if (ctx.description.how) allHow.push(ctx.description.how);
215708
+ if (ctx.description.gotchas) gotchas.push(...ctx.description.gotchas);
215709
+ if (ctx.anchors.docs) {
215710
+ for (const d of ctx.anchors.docs) {
215711
+ if (d.path) docs.push(d.path);
215712
+ }
215713
+ }
215714
+ if (ctx.rules) {
215715
+ for (const rule of ctx.rules) {
215716
+ rules.push(rule.name);
215717
+ }
215718
+ }
215719
+ }
215720
+ if (isJSON) {
215721
+ emitJSON3({
215722
+ files,
215723
+ missing_files: missingFiles,
215724
+ topics,
215725
+ docs: [...new Set(docs)],
215726
+ rules,
215727
+ gotchas,
215728
+ context_summary: {
215729
+ what: allWhat,
215730
+ how: allHow
215731
+ }
215732
+ });
215733
+ } else {
215734
+ console.log(`Session started for ${files.length} file(s):
215735
+ `);
215736
+ for (const f of files) {
215737
+ const exists = (0, import_node_fs5.existsSync)((0, import_node_path5.resolve)(process.cwd(), f));
215738
+ console.log(` ${exists ? "\u2713" : "\u2717"} ${f}`);
215739
+ }
215740
+ if (missingFiles.length > 0) {
215741
+ console.log(`
215742
+ Warning: ${missingFiles.length} file(s) not found locally.`);
215743
+ }
215744
+ if (topics.length > 0) {
215745
+ console.log(`
215746
+ Matched ${topics.length} context topic(s):`);
215747
+ for (const r of results) {
215748
+ console.log(` \u258C ${r.context.topic} (${r.match_reason})`);
215749
+ if (r.context.description.what) {
215750
+ console.log(` ${r.context.description.what}`);
215751
+ }
215752
+ }
215753
+ } else {
215754
+ console.log("\nNo context topics match these files.");
215755
+ }
215756
+ if (docs.length > 0) {
215757
+ console.log(`
215758
+ Anchored docs:`);
215759
+ for (const d of [...new Set(docs)]) {
215760
+ console.log(` \u{1F4C4} ${d}`);
215761
+ }
215762
+ }
215763
+ if (gotchas.length > 0) {
215764
+ console.log(`
215765
+ Gotchas:`);
215766
+ for (const g of gotchas) {
215767
+ console.log(` ! ${g}`);
215768
+ }
215769
+ }
215770
+ if (rules.length > 0) {
215771
+ console.log(`
215772
+ Rules that will be evaluated:`);
215773
+ for (const r of rules) {
215774
+ console.log(` \u2713 ${r}`);
215775
+ }
215776
+ }
215777
+ console.log("\nBefore finishing:");
215778
+ console.log(" driftless scan --diff");
215779
+ console.log(" driftless session finish");
215780
+ }
215781
+ } catch (e) {
215782
+ console.error(`Session start failed: ${formatError(e)}`);
215783
+ process.exit(1);
215784
+ }
215785
+ return;
215786
+ }
215787
+ if (subCommand === "finish") {
215788
+ const files = getChangedFilesList();
215789
+ if (files.length === 0 && !isJSON) {
215790
+ console.log("No local changes detected. Session clean.");
215791
+ process.exit(0);
215792
+ }
215793
+ try {
215794
+ const remote = getGitRemote();
215795
+ if (!remote) {
215796
+ console.error("Error: no git remote found.");
215797
+ process.exit(1);
215798
+ }
215799
+ const me = await api.get("/me");
215800
+ const repos = await api.get(`/workspaces/${me.slug}/repos`);
215801
+ const repo = repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
215802
+ if (!repo) {
215803
+ console.error(`Repo '${remote.repo}' not found. Run 'driftless init' first.`);
215804
+ process.exit(1);
215805
+ }
215806
+ const { getUncommittedDiff: getUncommittedDiff3, getStagedDiff: getStagedDiff2, getLastCommitHash: getLastCommitHash2, getAuthorName: getAuthorName2 } = await Promise.resolve().then(() => (init_git(), git_exports));
215807
+ const staged = getStagedDiff2();
215808
+ const unstaged = getUncommittedDiff3();
215809
+ const diff = [staged, unstaged].filter(Boolean).join("\n");
215810
+ if (!diff && !isJSON) {
215811
+ console.log("No changes to scan. Session clean.");
215812
+ process.exit(0);
215813
+ }
215814
+ const scanResult = await api.post("/scan", {
215815
+ workspace_id: me.workspace_id,
215816
+ repo_id: repo.id,
215817
+ diff,
215818
+ commit_hash: getLastCommitHash2(),
215819
+ author: getAuthorName2()
215820
+ });
215821
+ const rulesEvaluated = scanResult.rules_evaluated || 0;
215822
+ const violations = scanResult.violations || [];
215823
+ const contextResults = files.length > 0 ? await api.post(
215824
+ `/workspaces/${me.slug}/watchers/match-files`,
215825
+ { files }
215826
+ ) : [];
215827
+ const staleTopics = contextResults.filter((r) => r.context.stale?.is_stale);
215828
+ if (isJSON) {
215829
+ emitJSON3({
215830
+ files,
215831
+ rules_evaluated: rulesEvaluated,
215832
+ violations,
215833
+ stale_topics: staleTopics.map((r) => ({ topic: r.context.topic, reason: r.context.stale.reason })),
215834
+ context_matched: contextResults.map((r) => r.context.topic)
215835
+ });
215836
+ } else {
215837
+ console.log(`Session finish \u2014 ${files.length} file(s) changed:
215838
+ `);
215839
+ for (const f of files) {
215840
+ console.log(` \u270E ${f}`);
215841
+ }
215842
+ console.log(`
215843
+ ${rulesEvaluated} rule(s) evaluated.`);
215844
+ if (violations.length > 0) {
215845
+ console.log(`
215846
+ ${violations.length} violation(s) found:`);
215847
+ for (const v of violations) {
215848
+ console.log(` \u2717 [${v.severity.toUpperCase()}] ${v.rule_name}`);
215849
+ console.log(` ${v.file_path}:${v.line_number} \u2014 ${v.explanation}`);
215850
+ }
215851
+ console.log("\nFix violations before pushing.");
215852
+ } else {
215853
+ console.log("No violations.");
215854
+ }
215855
+ if (staleTopics.length > 0) {
215856
+ console.log(`
215857
+ \u26A0 ${staleTopics.length} stale context topic(s):`);
215858
+ for (const s of staleTopics) {
215859
+ console.log(` ${s.context.topic} \u2014 ${s.context.stale.reason}`);
215860
+ console.log(` \u2192 driftless context update ${s.context.topic} --what "..." --how "..."`);
215861
+ }
215862
+ }
215863
+ if (contextResults.length > 0) {
215864
+ console.log(`
215865
+ Context touched:`);
215866
+ for (const r of contextResults) {
215867
+ console.log(` \u258C ${r.context.topic}`);
215868
+ }
215869
+ }
215870
+ console.log("\nLearned something? Update context:");
215871
+ console.log(' driftless context update <topic> --gotchas "..."');
215872
+ console.log(' driftless context add <new-topic> --what "..." --pattern "..."');
215873
+ }
215874
+ if (violations.length > 0) {
215875
+ process.exit(1);
215876
+ }
215877
+ } catch (e) {
215878
+ console.error(`Session finish failed: ${formatError(e)}`);
215879
+ process.exit(1);
215880
+ }
215881
+ return;
215882
+ }
215883
+ console.log(`Usage: driftless session <start|finish> [args]
215884
+
215885
+ start \u2014 Show relevant context before editing
215886
+ --files "path1,path2" Files to check (default: local changes)
215887
+ --json Output as JSON
215888
+
215889
+ finish \u2014 Scan changes, check violations, suggest context updates
215890
+ --json Output as JSON
215891
+
215892
+ Examples:
215893
+ driftless session start --files "src/auth/**"
215894
+ driftless session start # uses local changes
215895
+ driftless session finish # scan + context report`);
215896
+ }
215897
+
215898
+ // src/commands/install-skill.ts
215899
+ var import_node_fs6 = require("node:fs");
215900
+ var import_node_path6 = require("node:path");
215461
215901
  var template = `# Driftless \u2014 Live Repo Context
215462
215902
 
215463
215903
  Driftless Cloud holds the team's living codebase context (topics, anchors, rules, gotchas).
@@ -215583,29 +216023,29 @@ Context delivery + structural verification only.
215583
216023
  `;
215584
216024
  function installSkillCommand() {
215585
216025
  const cwd = process.cwd();
215586
- const agentsPath = (0, import_node_path5.resolve)(cwd, "AGENTS.md");
215587
- if ((0, import_node_fs5.existsSync)(agentsPath)) {
215588
- const existing = (0, import_node_fs5.readFileSync)(agentsPath, "utf8");
216026
+ const agentsPath = (0, import_node_path6.resolve)(cwd, "AGENTS.md");
216027
+ if ((0, import_node_fs6.existsSync)(agentsPath)) {
216028
+ const existing = (0, import_node_fs6.readFileSync)(agentsPath, "utf8");
215589
216029
  if (existing.includes("# Driftless")) {
215590
216030
  console.log("Driftless section already present in AGENTS.md.");
215591
216031
  return;
215592
216032
  }
215593
- (0, import_node_fs5.writeFileSync)(agentsPath, existing + "\n---\n\n" + template);
216033
+ (0, import_node_fs6.writeFileSync)(agentsPath, existing + "\n---\n\n" + template);
215594
216034
  console.log(`Appended Driftless section to ${agentsPath}`);
215595
216035
  } else {
215596
- (0, import_node_fs5.writeFileSync)(agentsPath, template);
216036
+ (0, import_node_fs6.writeFileSync)(agentsPath, template);
215597
216037
  console.log(`Driftless skill installed at ${agentsPath}`);
215598
216038
  }
215599
216039
  }
215600
216040
 
215601
216041
  // src/commands/login.ts
215602
- var import_node_fs6 = require("node:fs");
215603
- var import_node_path6 = require("node:path");
216042
+ var import_node_fs7 = require("node:fs");
216043
+ var import_node_path7 = require("node:path");
215604
216044
  var import_node_readline = require("node:readline");
215605
216045
  var import_node_child_process2 = require("node:child_process");
215606
216046
  var import_node_os2 = require("node:os");
215607
- var CONFIG_DIR = (0, import_node_path6.resolve)((0, import_node_os2.homedir)(), ".driftless");
215608
- var CONFIG_PATH2 = (0, import_node_path6.resolve)(CONFIG_DIR, "config.json");
216047
+ var CONFIG_DIR = (0, import_node_path7.resolve)((0, import_node_os2.homedir)(), ".driftless");
216048
+ var CONFIG_PATH2 = (0, import_node_path7.resolve)(CONFIG_DIR, "config.json");
215609
216049
  function openBrowser(url) {
215610
216050
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
215611
216051
  (0, import_node_child_process2.exec)(`${cmd} ${url}`);
@@ -215634,10 +216074,10 @@ async function loginCommand(args) {
215634
216074
  input: process.stdin,
215635
216075
  output: process.stdout
215636
216076
  });
215637
- const apiKey = await new Promise((resolve8) => {
216077
+ const apiKey = await new Promise((resolve9) => {
215638
216078
  rl.question("Paste your API key: ", (answer) => {
215639
216079
  rl.close();
215640
- resolve8(answer.trim());
216080
+ resolve9(answer.trim());
215641
216081
  });
215642
216082
  });
215643
216083
  if (!apiKey.startsWith("drift_")) {
@@ -215650,10 +216090,10 @@ async function loginCommand(args) {
215650
216090
  function saveConfig(apiKey, apiUrl) {
215651
216091
  const url = apiUrl || "https://api.driftless.icu/api/v1";
215652
216092
  try {
215653
- if (!(0, import_node_fs6.existsSync)(CONFIG_DIR)) {
215654
- (0, import_node_fs6.mkdirSync)(CONFIG_DIR, { recursive: true });
216093
+ if (!(0, import_node_fs7.existsSync)(CONFIG_DIR)) {
216094
+ (0, import_node_fs7.mkdirSync)(CONFIG_DIR, { recursive: true });
215655
216095
  }
215656
- (0, import_node_fs6.writeFileSync)(
216096
+ (0, import_node_fs7.writeFileSync)(
215657
216097
  CONFIG_PATH2,
215658
216098
  JSON.stringify({ api_key: apiKey, api_url: url }, null, 2) + "\n"
215659
216099
  );
@@ -215669,8 +216109,9 @@ function saveConfig(apiKey, apiUrl) {
215669
216109
  }
215670
216110
 
215671
216111
  // src/commands/doctor.ts
215672
- var import_node_fs7 = require("node:fs");
215673
- var import_node_path7 = require("node:path");
216112
+ init_git();
216113
+ var import_node_fs8 = require("node:fs");
216114
+ var import_node_path8 = require("node:path");
215674
216115
  async function doctorCommand() {
215675
216116
  const checks = [];
215676
216117
  const apiKey = getApiKey();
@@ -215756,9 +216197,9 @@ async function doctorCommand() {
215756
216197
  } else {
215757
216198
  checks.push({ name: "Baseline", status: "warn", detail: "Skipped (no git remote)" });
215758
216199
  }
215759
- const agentsPath = (0, import_node_path7.resolve)(process.cwd(), "AGENTS.md");
215760
- if ((0, import_node_fs7.existsSync)(agentsPath)) {
215761
- const content = (0, import_node_fs7.readFileSync)(agentsPath, "utf-8");
216200
+ const agentsPath = (0, import_node_path8.resolve)(process.cwd(), "AGENTS.md");
216201
+ if ((0, import_node_fs8.existsSync)(agentsPath)) {
216202
+ const content = (0, import_node_fs8.readFileSync)(agentsPath, "utf-8");
215762
216203
  if (content.includes("driftless")) {
215763
216204
  checks.push({ name: "AGENTS.md", status: "ok", detail: "Driftless skill installed" });
215764
216205
  } else {
@@ -215813,31 +216254,33 @@ function pad2(s, n) {
215813
216254
  }
215814
216255
 
215815
216256
  // src/index.ts
215816
- var VERSION = "0.1.22";
215817
- var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Context integrity for AI engineering teams
216257
+ var VERSION = "0.1.23";
216258
+ var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
215818
216259
 
215819
216260
  Install: npm install -g @driftless-sh/cli
215820
216261
  Docs: https://driftless.icu/docs
215821
216262
  API: https://api.driftless.icu/api/v1
215822
216263
 
215823
216264
  Agent loop:
215824
- driftless context list List all context topics
215825
- driftless context get <topic> Live view of one topic before editing
215826
- driftless context get --diff What context matters for my local changes?
215827
- driftless context get --files "a,b" Same, with explicit file paths
215828
- driftless context search "<query>" Discover topics by keyword
215829
- driftless scan --diff Check changes against active rules
216265
+ driftless session start Show context before editing
216266
+ driftless session finish Scan changes + context report
216267
+ driftless context list List all context topics
216268
+ driftless context get <topic> Live view of one topic before editing
216269
+ driftless context get --diff What context matters for my local changes?
216270
+ driftless context search "<query>" Discover topics by keyword
216271
+ driftless scan --diff Check changes against active rules
215830
216272
 
215831
216273
  Setup:
215832
216274
  driftless login --key <api-key> Authenticate
215833
- driftless init Scan repo, generate rules + topics + anchor docs
216275
+ driftless init Scan repo, bootstrap context, suggest rules
215834
216276
  driftless doctor Check environment health
215835
216277
 
215836
216278
  Commands:
215837
216279
  login Authenticate with API key
215838
- init Smart init: scan \u2192 detect patterns \u2192 create rules + topics + anchor docs
216280
+ init Smart init: scan \u2192 detect patterns \u2192 create topics + rules + anchor docs
215839
216281
  scan Evaluate staged + uncommitted changes against rules
215840
216282
  scan --diff Evaluate uncommitted changes only
216283
+ session Agent session: start (context) \u2192 finish (scan + report)
215841
216284
  context Live repo context (topics, search, anchors)
215842
216285
  install-skill Install AGENTS.md into current repo
215843
216286
  doctor Check environment health (auth, API, git, workspace, repo, baseline)
@@ -215859,8 +216302,13 @@ Context subcommands:
215859
216302
  delete <topic> Delete a topic
215860
216303
  push --files "p1,p2" Legacy: match topics by file paths
215861
216304
 
216305
+ Session subcommands:
216306
+ start [--files "p1,p2"] Show relevant context before editing
216307
+ finish Scan changes, check violations, suggest updates
216308
+
215862
216309
  Flags:
215863
- --human Show human-readable output (default is JSON for agent consumption)
216310
+ --json Output as JSON (default is human-readable)
216311
+ --dry-run Preview changes without writing to Driftless Cloud
215864
216312
 
215865
216313
  Auth priority:
215866
216314
  1. DRIFTLESS_API_KEY env var
@@ -215871,21 +216319,22 @@ Environment:
215871
216319
  DRIFTLESS_API_URL API server URL (default: http://localhost:3000/api/v1)
215872
216320
 
215873
216321
  Output format:
215874
- All commands output JSON by default for agent consumption.
215875
- Use --human flag for readable text output.
216322
+ All commands output human-readable text by default.
216323
+ Use --json flag for machine-readable output (agents, CI).
215876
216324
 
215877
216325
  Examples:
216326
+ driftless session start --files "src/auth/**"
216327
+ driftless session finish
216328
+
215878
216329
  driftless context add "b2b-guard" \\
215879
216330
  --what "Guard que protege endpoints B2B" \\
215880
216331
  --how "Verifica org_id en el token" \\
215881
- --pattern "src/shared/guards/**" \\
215882
- --decisions "Multi-tenant, usamos org_id" \\
215883
- --gotchas "No funciona con tokens de test" \\
215884
- --ownership "@team"
216332
+ --pattern "src/shared/guards/**"
215885
216333
 
215886
- driftless context get sdk --human
216334
+ driftless context get sdk
215887
216335
  driftless context search "auth"
215888
216336
  driftless scan --diff
216337
+ driftless context push --files "src/auth/auth.controller.ts" --dry-run
215889
216338
  `;
215890
216339
  function showCommandHelp(cmd) {
215891
216340
  const help = {
@@ -215934,7 +216383,7 @@ Example:
215934
216383
  context: `driftless context <subcommand> [args] [flags]
215935
216384
 
215936
216385
  Live repo context \u2014 the team's shared codebase memory.
215937
- JSON by default for agent consumption. Use --human for readable output.
216386
+ Human-readable by default. Use --json for machine output.
215938
216387
 
215939
216388
  Subcommands:
215940
216389
  list [filters] List context topics
@@ -215956,69 +216405,20 @@ List filters:
215956
216405
  --manual Only manually-created topics
215957
216406
  --repo <id> Only topics scoped to a given repo
215958
216407
 
215959
- Canonical 'get' shape (JSON):
215960
- {
215961
- "topic": "<slug>",
215962
- "summary": "...",
215963
- "description": { "what", "how", "decisions", "gotchas", "ownership" },
215964
- "anchors": { "files", "patterns", "repos", "docs" },
215965
- "components": [ ... ],
215966
- "rules": [],
215967
- "invariants": ["...", ...],
215968
- "violations": [],
215969
- "events": [],
215970
- "required_checks": ["...", ...],
215971
- "stale": { "is_stale", "reason" },
215972
- "metadata": { "created_by", "last_updated", "source" }
215973
- }
215974
-
215975
- Match-files response (JSON):
215976
- [
215977
- {
215978
- "context": { ... canonical shape ... },
215979
- "history": [ ... watcher events ... ],
215980
- "match_reason": "pattern:src/auth/**"
215981
- }
215982
- ]
215983
-
215984
- Create / update options:
215985
- --what What this piece is
215986
- --how How it works
215987
- --pattern Glob (e.g. src/sdk/**) \u2014 resolves to files dynamically
215988
- --where Explicit file path (alternative to --pattern)
215989
- --decisions Why it was built this way
215990
- --gotchas Known traps and edge cases
215991
- --ownership Who maintains it
215992
- --file <path> Anchor an existing markdown file as a doc (on add)
215993
-
215994
- Anchor options:
215995
- --doc <path> Path to markdown file to anchor as file_content
215996
- --note "..." Short note appended to decisions
215997
- --files "..." Comma-separated file paths to anchor
215998
- --pattern "..." Glob pattern to anchor
215999
-
216000
- Update append options (PR 3):
216001
- --gotcha "..." Append a gotcha to existing list
216002
- --invariant "..." Append an invariant (team rule that must be followed)
216003
- --check "..." Append a required pre-push check
216004
- --enforce "..." Create a structural rule from the invariant
216005
-
216006
216408
  Flags:
216007
- --human Human-readable output (default is JSON)
216409
+ --json Machine-readable JSON output
216410
+ --dry-run Preview changes without writing to Driftless Cloud
216008
216411
 
216009
216412
  Examples:
216010
- driftless context list --stale --human
216413
+ driftless context list
216414
+ driftless context list --json
216011
216415
  driftless context get auth-boundaries
216012
- driftless context get auth-boundaries --human
216013
- driftless context get --diff --human
216014
- driftless context get --files "src/auth/guard.ts,src/auth/service.ts"
216416
+ driftless context get --diff
216015
216417
  driftless context search "auth"
216016
216418
  driftless context add "sdk" --pattern "src/sdk/**" --what "Public SDK"
216017
216419
  driftless context update sdk --gotchas "Reset token on org switch"
216018
216420
  driftless context anchor auth --doc docs/auth.md --files "src/auth/**"
216019
- driftless context anchor auth --note "Use org_id for multi-tenant"
216020
- driftless context update auth --invariant "No direct DB from controllers"
216021
- driftless context update auth --check "Run harness:check before push"
216421
+ driftless context push --files "src/auth/guard.ts" --dry-run
216022
216422
  `,
216023
216423
  "install-skill": `driftless install-skill
216024
216424
 
@@ -216070,6 +216470,9 @@ async function main() {
216070
216470
  case "context":
216071
216471
  await contextCommand(args.slice(1));
216072
216472
  break;
216473
+ case "session":
216474
+ await sessionCommand(args.slice(1));
216475
+ break;
216073
216476
  case "install-skill":
216074
216477
  installSkillCommand();
216075
216478
  break;