@driftless-sh/cli 0.1.22 → 0.1.24

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);
@@ -215388,27 +215501,42 @@ async function contextCommand(args) {
215388
215501
  }
215389
215502
  const updates = { last_updated: /* @__PURE__ */ new Date() };
215390
215503
  if (fileContent) updates.file_content = fileContent;
215504
+ if (docFlag) updates.anchored_doc_path = docFlag;
215391
215505
  if (noteFlag) updates.decisions = noteFlag;
215392
215506
  if (filesFlag) updates.where_files = filesFlag.split(",").map((f) => f.trim()).filter(Boolean);
215393
215507
  if (patternFlag) updates.pattern = patternFlag;
215394
215508
  updates._event_detail = `ANCHOR_ADDED: doc=${!!fileContent}, note=${!!noteFlag}, files=${filesFlag || "none"}`;
215509
+ if (flags["dry-run"]) {
215510
+ console.log(`Would anchor topic '${slug}':`);
215511
+ if (docFlag) console.log(` doc: ${docFlag} (${fileContent ? fileContent.length : 0} chars)`);
215512
+ if (noteFlag) console.log(` note: ${noteFlag}`);
215513
+ if (filesFlag) console.log(` files: ${filesFlag}`);
215514
+ if (patternFlag) console.log(` pattern: ${patternFlag}`);
215515
+ if (!isJSON) {
215516
+ console.log("\n(Dry run \u2014 no changes written to Driftless Cloud)");
215517
+ }
215518
+ if (isJSON) {
215519
+ emitJSON2({ dry_run: true, action: "anchor", topic: slug, doc: docFlag, note: noteFlag, files: filesFlag, pattern: patternFlag });
215520
+ }
215521
+ process.exit(0);
215522
+ }
215395
215523
  try {
215396
215524
  const result = await api.patch(
215397
215525
  `/workspaces/${workspaceSlug}/watchers/${slug}`,
215398
215526
  updates
215399
215527
  );
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 {
215528
+ if (isJSON) {
215407
215529
  const sanitized = JSON.parse(JSON.stringify(result, (_key, value) => {
215408
215530
  if (typeof value === "string" && value.length > 500) return value.slice(0, 500) + "\u2026";
215409
215531
  return value;
215410
215532
  }));
215411
- emitJSON(sanitized);
215533
+ emitJSON2(sanitized);
215534
+ } else {
215535
+ console.log(`Topic '${slug}' anchored.`);
215536
+ if (docFlag) console.log(` doc: ${docFlag} (${fileContent ? fileContent.length : 0} chars)`);
215537
+ if (noteFlag) console.log(` note: ${noteFlag}`);
215538
+ if (filesFlag) console.log(` files: ${filesFlag}`);
215539
+ if (patternFlag) console.log(` pattern: ${patternFlag}`);
215412
215540
  }
215413
215541
  } catch (e) {
215414
215542
  console.error(`Anchor failed: ${formatError(e)}`);
@@ -215428,21 +215556,59 @@ async function contextCommand(args) {
215428
215556
  process.exit(1);
215429
215557
  }
215430
215558
  const missingFiles = files.filter((f) => !(0, import_node_fs4.existsSync)((0, import_node_path4.resolve)(process.cwd(), f)));
215431
- if (missingFiles.length > 0) {
215559
+ if (missingFiles.length > 0 && !isJSON) {
215432
215560
  console.error(`File(s) not found locally: ${missingFiles.join(", ")}. Matching by pattern only.`);
215433
215561
  }
215562
+ if (flags["dry-run"]) {
215563
+ try {
215564
+ const results = await api.post(
215565
+ `/workspaces/${workspaceSlug}/watchers/match-files`,
215566
+ { files }
215567
+ );
215568
+ console.log(`Would match ${results.length} topic${results.length === 1 ? "" : "s"}:`);
215569
+ for (const r of results) {
215570
+ console.log(` ${r.context.topic} via ${r.match_reason}`);
215571
+ }
215572
+ if (missingFiles.length > 0) {
215573
+ console.log(`
215574
+ Warnings:`);
215575
+ for (const f of missingFiles) {
215576
+ console.log(` ${f} does not exist locally \u2014 matched by pattern only`);
215577
+ }
215578
+ }
215579
+ if (!isJSON) {
215580
+ console.log("\n(Dry run \u2014 no context delivered)");
215581
+ }
215582
+ if (isJSON) {
215583
+ emitJSON2({ dry_run: true, matched: results.map((r) => ({ topic: r.context.topic, reason: r.match_reason })), missing_files: missingFiles });
215584
+ }
215585
+ } catch (e) {
215586
+ console.error(`Dry run failed: ${formatError(e)}`);
215587
+ process.exit(1);
215588
+ }
215589
+ process.exit(0);
215590
+ }
215434
215591
  try {
215435
215592
  const results = await api.post(
215436
215593
  `/workspaces/${workspaceSlug}/watchers/match-files`,
215437
215594
  { files }
215438
215595
  );
215439
- if (results.length === 0) {
215440
- console.error(`No context topics match these files. No watcher covers the changed paths.`);
215596
+ if (results.length === 0 && !isJSON) {
215597
+ console.log(`No context topics match these files.`);
215441
215598
  }
215442
- if (isHuman) {
215443
- renderMatchFilesHuman(results, files);
215599
+ if (isJSON) {
215600
+ emitJSON2(results);
215444
215601
  } else {
215445
- emitJSON(results);
215602
+ if (results.length > 0) {
215603
+ console.log(`Matched ${results.length} topic${results.length === 1 ? "" : "s"}:`);
215604
+ for (const r of results) {
215605
+ console.log(` ${r.context.topic} via ${r.match_reason}`);
215606
+ }
215607
+ console.log(`
215608
+ Context delivered for ${files.length} file${files.length === 1 ? "" : "s"}.`);
215609
+ } else {
215610
+ console.log(`No context topics match these files. No watcher covers the changed paths.`);
215611
+ }
215446
215612
  }
215447
215613
  } catch (e) {
215448
215614
  console.error(`Push failed: ${formatError(e)}`);
@@ -215455,9 +215621,284 @@ async function contextCommand(args) {
215455
215621
  Run 'driftless help context' for full reference.`);
215456
215622
  }
215457
215623
 
215458
- // src/commands/install-skill.ts
215624
+ // src/commands/session.ts
215625
+ init_git();
215459
215626
  var import_node_fs5 = require("node:fs");
215460
215627
  var import_node_path5 = require("node:path");
215628
+ function parseArgs2(args) {
215629
+ const flags = {};
215630
+ const positional = [];
215631
+ for (let i = 0; i < args.length; i++) {
215632
+ if (args[i].startsWith("--")) {
215633
+ const key = args[i].slice(2);
215634
+ const next = args[i + 1];
215635
+ if (next && !next.startsWith("--")) {
215636
+ flags[key] = next;
215637
+ i++;
215638
+ } else {
215639
+ flags[key] = true;
215640
+ }
215641
+ } else {
215642
+ positional.push(args[i]);
215643
+ }
215644
+ }
215645
+ return { flags, positional };
215646
+ }
215647
+ function emitJSON3(data) {
215648
+ console.log(JSON.stringify(data, null, 2));
215649
+ }
215650
+ async function resolveWorkspaceSlug2() {
215651
+ try {
215652
+ const me = await api.get("/me");
215653
+ if (me?.slug) return me.slug;
215654
+ } catch {
215655
+ }
215656
+ const remote = getGitRemote();
215657
+ if (!remote) {
215658
+ console.error("Error: no git remote found.");
215659
+ process.exit(1);
215660
+ }
215661
+ return remote.org;
215662
+ }
215663
+ async function sessionCommand(args) {
215664
+ if (!isGitRepo()) {
215665
+ console.error("Error: not a git repository.");
215666
+ process.exit(1);
215667
+ }
215668
+ const workspaceSlug = await resolveWorkspaceSlug2();
215669
+ const { flags, positional } = parseArgs2(args);
215670
+ const subCommand = positional[0];
215671
+ const isJSON = !!flags["json"];
215672
+ if (subCommand === "start") {
215673
+ const filesFlag = flags["files"];
215674
+ let files = [];
215675
+ if (filesFlag) {
215676
+ files = filesFlag.split(",").map((f) => f.trim()).filter(Boolean);
215677
+ } else {
215678
+ files = getChangedFilesList();
215679
+ }
215680
+ if (files.length === 0) {
215681
+ if (!isJSON) {
215682
+ console.log("No files specified and no local changes detected.");
215683
+ console.log('Usage: driftless session start --files "src/auth/**"');
215684
+ } else {
215685
+ emitJSON3({ files: [], topics: [], docs: [], rules: [], gotchas: [] });
215686
+ }
215687
+ process.exit(0);
215688
+ }
215689
+ const missingFiles = files.filter((f) => !(0, import_node_fs5.existsSync)((0, import_node_path5.resolve)(process.cwd(), f)));
215690
+ if (missingFiles.length > 0 && !isJSON) {
215691
+ console.error(`Warning: ${missingFiles.length} file(s) not found locally \u2014 matching by pattern only.`);
215692
+ }
215693
+ try {
215694
+ const results = await api.post(
215695
+ `/workspaces/${workspaceSlug}/watchers/match-files`,
215696
+ { files }
215697
+ );
215698
+ const topics = [];
215699
+ const docs = [];
215700
+ const rules = [];
215701
+ const gotchas = [];
215702
+ const allWhat = [];
215703
+ const allHow = [];
215704
+ for (const r of results) {
215705
+ const ctx = r.context;
215706
+ topics.push(ctx.topic);
215707
+ if (ctx.description.what) allWhat.push(ctx.description.what);
215708
+ if (ctx.description.how) allHow.push(ctx.description.how);
215709
+ if (ctx.description.gotchas) gotchas.push(...ctx.description.gotchas);
215710
+ if (ctx.anchors.docs) {
215711
+ for (const d of ctx.anchors.docs) {
215712
+ if (d.path) docs.push(d.path);
215713
+ }
215714
+ }
215715
+ if (ctx.rules) {
215716
+ for (const rule of ctx.rules) {
215717
+ rules.push(rule.name);
215718
+ }
215719
+ }
215720
+ }
215721
+ if (isJSON) {
215722
+ emitJSON3({
215723
+ files,
215724
+ missing_files: missingFiles,
215725
+ topics,
215726
+ docs: [...new Set(docs)],
215727
+ rules,
215728
+ gotchas,
215729
+ context_summary: {
215730
+ what: allWhat,
215731
+ how: allHow
215732
+ }
215733
+ });
215734
+ } else {
215735
+ console.log(`Session started for ${files.length} file(s):
215736
+ `);
215737
+ for (const f of files) {
215738
+ const exists = (0, import_node_fs5.existsSync)((0, import_node_path5.resolve)(process.cwd(), f));
215739
+ console.log(` ${exists ? "\u2713" : "\u2717"} ${f}`);
215740
+ }
215741
+ if (missingFiles.length > 0) {
215742
+ console.log(`
215743
+ Warning: ${missingFiles.length} file(s) not found locally.`);
215744
+ }
215745
+ if (topics.length > 0) {
215746
+ console.log(`
215747
+ Matched ${topics.length} context topic(s):`);
215748
+ for (const r of results) {
215749
+ console.log(` \u258C ${r.context.topic} (${r.match_reason})`);
215750
+ if (r.context.description.what) {
215751
+ console.log(` ${r.context.description.what}`);
215752
+ }
215753
+ }
215754
+ } else {
215755
+ console.log("\nNo context topics match these files.");
215756
+ }
215757
+ if (docs.length > 0) {
215758
+ console.log(`
215759
+ Anchored docs:`);
215760
+ for (const d of [...new Set(docs)]) {
215761
+ console.log(` \u{1F4C4} ${d}`);
215762
+ }
215763
+ }
215764
+ if (gotchas.length > 0) {
215765
+ console.log(`
215766
+ Gotchas:`);
215767
+ for (const g of gotchas) {
215768
+ console.log(` ! ${g}`);
215769
+ }
215770
+ }
215771
+ if (rules.length > 0) {
215772
+ console.log(`
215773
+ Rules that will be evaluated:`);
215774
+ for (const r of rules) {
215775
+ console.log(` \u2713 ${r}`);
215776
+ }
215777
+ }
215778
+ console.log("\nBefore finishing:");
215779
+ console.log(" driftless scan --diff");
215780
+ console.log(" driftless session finish");
215781
+ }
215782
+ } catch (e) {
215783
+ console.error(`Session start failed: ${formatError(e)}`);
215784
+ process.exit(1);
215785
+ }
215786
+ return;
215787
+ }
215788
+ if (subCommand === "finish") {
215789
+ const files = getChangedFilesList();
215790
+ if (files.length === 0 && !isJSON) {
215791
+ console.log("No local changes detected. Session clean.");
215792
+ process.exit(0);
215793
+ }
215794
+ try {
215795
+ const remote = getGitRemote();
215796
+ if (!remote) {
215797
+ console.error("Error: no git remote found.");
215798
+ process.exit(1);
215799
+ }
215800
+ const me = await api.get("/me");
215801
+ const repos = await api.get(`/workspaces/${me.slug}/repos`);
215802
+ const repo = repos.find((r) => r.github_org === remote.org && r.github_repo === remote.repo);
215803
+ if (!repo) {
215804
+ console.error(`Repo '${remote.repo}' not found. Run 'driftless init' first.`);
215805
+ process.exit(1);
215806
+ }
215807
+ const { getUncommittedDiff: getUncommittedDiff3, getStagedDiff: getStagedDiff2, getLastCommitHash: getLastCommitHash2, getAuthorName: getAuthorName2 } = await Promise.resolve().then(() => (init_git(), git_exports));
215808
+ const staged = getStagedDiff2();
215809
+ const unstaged = getUncommittedDiff3();
215810
+ const diff = [staged, unstaged].filter(Boolean).join("\n");
215811
+ if (!diff && !isJSON) {
215812
+ console.log("No changes to scan. Session clean.");
215813
+ process.exit(0);
215814
+ }
215815
+ const scanResult = await api.post("/scan", {
215816
+ workspace_id: me.workspace_id,
215817
+ repo_id: repo.id,
215818
+ diff,
215819
+ commit_hash: getLastCommitHash2(),
215820
+ author: getAuthorName2()
215821
+ });
215822
+ const rulesEvaluated = scanResult.rules_evaluated || 0;
215823
+ const violations = scanResult.violations || [];
215824
+ const contextResults = files.length > 0 ? await api.post(
215825
+ `/workspaces/${me.slug}/watchers/match-files`,
215826
+ { files }
215827
+ ) : [];
215828
+ const staleTopics = contextResults.filter((r) => r.context.stale?.is_stale);
215829
+ if (isJSON) {
215830
+ emitJSON3({
215831
+ files,
215832
+ rules_evaluated: rulesEvaluated,
215833
+ violations,
215834
+ stale_topics: staleTopics.map((r) => ({ topic: r.context.topic, reason: r.context.stale.reason })),
215835
+ context_matched: contextResults.map((r) => r.context.topic)
215836
+ });
215837
+ } else {
215838
+ console.log(`Session finish \u2014 ${files.length} file(s) changed:
215839
+ `);
215840
+ for (const f of files) {
215841
+ console.log(` \u270E ${f}`);
215842
+ }
215843
+ console.log(`
215844
+ ${rulesEvaluated} rule(s) evaluated.`);
215845
+ if (violations.length > 0) {
215846
+ console.log(`
215847
+ ${violations.length} violation(s) found:`);
215848
+ for (const v of violations) {
215849
+ console.log(` \u2717 [${v.severity.toUpperCase()}] ${v.rule_name}`);
215850
+ console.log(` ${v.file_path}:${v.line_number} \u2014 ${v.explanation}`);
215851
+ }
215852
+ console.log("\nFix violations before pushing.");
215853
+ } else {
215854
+ console.log("No violations.");
215855
+ }
215856
+ if (staleTopics.length > 0) {
215857
+ console.log(`
215858
+ \u26A0 ${staleTopics.length} stale context topic(s):`);
215859
+ for (const s of staleTopics) {
215860
+ console.log(` ${s.context.topic} \u2014 ${s.context.stale.reason}`);
215861
+ console.log(` \u2192 driftless context update ${s.context.topic} --what "..." --how "..."`);
215862
+ }
215863
+ }
215864
+ if (contextResults.length > 0) {
215865
+ console.log(`
215866
+ Context touched:`);
215867
+ for (const r of contextResults) {
215868
+ console.log(` \u258C ${r.context.topic}`);
215869
+ }
215870
+ }
215871
+ console.log("\nLearned something? Update context:");
215872
+ console.log(' driftless context update <topic> --gotchas "..."');
215873
+ console.log(' driftless context add <new-topic> --what "..." --pattern "..."');
215874
+ }
215875
+ if (violations.length > 0) {
215876
+ process.exit(1);
215877
+ }
215878
+ } catch (e) {
215879
+ console.error(`Session finish failed: ${formatError(e)}`);
215880
+ process.exit(1);
215881
+ }
215882
+ return;
215883
+ }
215884
+ console.log(`Usage: driftless session <start|finish> [args]
215885
+
215886
+ start \u2014 Show relevant context before editing
215887
+ --files "path1,path2" Files to check (default: local changes)
215888
+ --json Output as JSON
215889
+
215890
+ finish \u2014 Scan changes, check violations, suggest context updates
215891
+ --json Output as JSON
215892
+
215893
+ Examples:
215894
+ driftless session start --files "src/auth/**"
215895
+ driftless session start # uses local changes
215896
+ driftless session finish # scan + context report`);
215897
+ }
215898
+
215899
+ // src/commands/install-skill.ts
215900
+ var import_node_fs6 = require("node:fs");
215901
+ var import_node_path6 = require("node:path");
215461
215902
  var template = `# Driftless \u2014 Live Repo Context
215462
215903
 
215463
215904
  Driftless Cloud holds the team's living codebase context (topics, anchors, rules, gotchas).
@@ -215583,32 +216024,33 @@ Context delivery + structural verification only.
215583
216024
  `;
215584
216025
  function installSkillCommand() {
215585
216026
  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");
216027
+ const agentsPath = (0, import_node_path6.resolve)(cwd, "AGENTS.md");
216028
+ if ((0, import_node_fs6.existsSync)(agentsPath)) {
216029
+ const existing = (0, import_node_fs6.readFileSync)(agentsPath, "utf8");
215589
216030
  if (existing.includes("# Driftless")) {
215590
216031
  console.log("Driftless section already present in AGENTS.md.");
215591
216032
  return;
215592
216033
  }
215593
- (0, import_node_fs5.writeFileSync)(agentsPath, existing + "\n---\n\n" + template);
216034
+ (0, import_node_fs6.writeFileSync)(agentsPath, existing + "\n---\n\n" + template);
215594
216035
  console.log(`Appended Driftless section to ${agentsPath}`);
215595
216036
  } else {
215596
- (0, import_node_fs5.writeFileSync)(agentsPath, template);
216037
+ (0, import_node_fs6.writeFileSync)(agentsPath, template);
215597
216038
  console.log(`Driftless skill installed at ${agentsPath}`);
215598
216039
  }
215599
216040
  }
215600
216041
 
215601
216042
  // src/commands/login.ts
215602
- var import_node_fs6 = require("node:fs");
215603
- var import_node_path6 = require("node:path");
216043
+ var import_node_fs7 = require("node:fs");
216044
+ var import_node_path7 = require("node:path");
215604
216045
  var import_node_readline = require("node:readline");
215605
216046
  var import_node_child_process2 = require("node:child_process");
215606
216047
  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");
216048
+ var CONFIG_DIR = (0, import_node_path7.resolve)((0, import_node_os2.homedir)(), ".driftless");
216049
+ var CONFIG_PATH2 = (0, import_node_path7.resolve)(CONFIG_DIR, "config.json");
215609
216050
  function openBrowser(url) {
215610
216051
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
215611
- (0, import_node_child_process2.exec)(`${cmd} ${url}`);
216052
+ const child = (0, import_node_child_process2.spawn)(cmd, [url], { stdio: "ignore", detached: true });
216053
+ child.unref();
215612
216054
  }
215613
216055
  async function loginCommand(args) {
215614
216056
  const keyIndex = args.indexOf("--key");
@@ -215634,10 +216076,10 @@ async function loginCommand(args) {
215634
216076
  input: process.stdin,
215635
216077
  output: process.stdout
215636
216078
  });
215637
- const apiKey = await new Promise((resolve8) => {
216079
+ const apiKey = await new Promise((resolve9) => {
215638
216080
  rl.question("Paste your API key: ", (answer) => {
215639
216081
  rl.close();
215640
- resolve8(answer.trim());
216082
+ resolve9(answer.trim());
215641
216083
  });
215642
216084
  });
215643
216085
  if (!apiKey.startsWith("drift_")) {
@@ -215650,10 +216092,10 @@ async function loginCommand(args) {
215650
216092
  function saveConfig(apiKey, apiUrl) {
215651
216093
  const url = apiUrl || "https://api.driftless.icu/api/v1";
215652
216094
  try {
215653
- if (!(0, import_node_fs6.existsSync)(CONFIG_DIR)) {
215654
- (0, import_node_fs6.mkdirSync)(CONFIG_DIR, { recursive: true });
216095
+ if (!(0, import_node_fs7.existsSync)(CONFIG_DIR)) {
216096
+ (0, import_node_fs7.mkdirSync)(CONFIG_DIR, { recursive: true });
215655
216097
  }
215656
- (0, import_node_fs6.writeFileSync)(
216098
+ (0, import_node_fs7.writeFileSync)(
215657
216099
  CONFIG_PATH2,
215658
216100
  JSON.stringify({ api_key: apiKey, api_url: url }, null, 2) + "\n"
215659
216101
  );
@@ -215669,8 +216111,9 @@ function saveConfig(apiKey, apiUrl) {
215669
216111
  }
215670
216112
 
215671
216113
  // src/commands/doctor.ts
215672
- var import_node_fs7 = require("node:fs");
215673
- var import_node_path7 = require("node:path");
216114
+ init_git();
216115
+ var import_node_fs8 = require("node:fs");
216116
+ var import_node_path8 = require("node:path");
215674
216117
  async function doctorCommand() {
215675
216118
  const checks = [];
215676
216119
  const apiKey = getApiKey();
@@ -215756,9 +216199,9 @@ async function doctorCommand() {
215756
216199
  } else {
215757
216200
  checks.push({ name: "Baseline", status: "warn", detail: "Skipped (no git remote)" });
215758
216201
  }
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");
216202
+ const agentsPath = (0, import_node_path8.resolve)(process.cwd(), "AGENTS.md");
216203
+ if ((0, import_node_fs8.existsSync)(agentsPath)) {
216204
+ const content = (0, import_node_fs8.readFileSync)(agentsPath, "utf-8");
215762
216205
  if (content.includes("driftless")) {
215763
216206
  checks.push({ name: "AGENTS.md", status: "ok", detail: "Driftless skill installed" });
215764
216207
  } else {
@@ -215813,31 +216256,33 @@ function pad2(s, n) {
215813
216256
  }
215814
216257
 
215815
216258
  // src/index.ts
215816
- var VERSION = "0.1.22";
215817
- var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Context integrity for AI engineering teams
216259
+ var VERSION = "0.1.24";
216260
+ var HELP_TEXT = `Driftless CLI v${VERSION} \u2014 Living repo context for humans and coding agents
215818
216261
 
215819
216262
  Install: npm install -g @driftless-sh/cli
215820
216263
  Docs: https://driftless.icu/docs
215821
216264
  API: https://api.driftless.icu/api/v1
215822
216265
 
215823
216266
  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
216267
+ driftless session start Show context before editing
216268
+ driftless session finish Scan changes + context report
216269
+ driftless context list List all context topics
216270
+ driftless context get <topic> Live view of one topic before editing
216271
+ driftless context get --diff What context matters for my local changes?
216272
+ driftless context search "<query>" Discover topics by keyword
216273
+ driftless scan --diff Check changes against active rules
215830
216274
 
215831
216275
  Setup:
215832
216276
  driftless login --key <api-key> Authenticate
215833
- driftless init Scan repo, generate rules + topics + anchor docs
216277
+ driftless init Scan repo, bootstrap context, suggest rules
215834
216278
  driftless doctor Check environment health
215835
216279
 
215836
216280
  Commands:
215837
216281
  login Authenticate with API key
215838
- init Smart init: scan \u2192 detect patterns \u2192 create rules + topics + anchor docs
216282
+ init Smart init: scan \u2192 detect patterns \u2192 create topics + rules + anchor docs
215839
216283
  scan Evaluate staged + uncommitted changes against rules
215840
216284
  scan --diff Evaluate uncommitted changes only
216285
+ session Agent session: start (context) \u2192 finish (scan + report)
215841
216286
  context Live repo context (topics, search, anchors)
215842
216287
  install-skill Install AGENTS.md into current repo
215843
216288
  doctor Check environment health (auth, API, git, workspace, repo, baseline)
@@ -215859,8 +216304,13 @@ Context subcommands:
215859
216304
  delete <topic> Delete a topic
215860
216305
  push --files "p1,p2" Legacy: match topics by file paths
215861
216306
 
216307
+ Session subcommands:
216308
+ start [--files "p1,p2"] Show relevant context before editing
216309
+ finish Scan changes, check violations, suggest updates
216310
+
215862
216311
  Flags:
215863
- --human Show human-readable output (default is JSON for agent consumption)
216312
+ --json Output as JSON (default is human-readable)
216313
+ --dry-run Preview changes without writing to Driftless Cloud
215864
216314
 
215865
216315
  Auth priority:
215866
216316
  1. DRIFTLESS_API_KEY env var
@@ -215871,21 +216321,22 @@ Environment:
215871
216321
  DRIFTLESS_API_URL API server URL (default: http://localhost:3000/api/v1)
215872
216322
 
215873
216323
  Output format:
215874
- All commands output JSON by default for agent consumption.
215875
- Use --human flag for readable text output.
216324
+ All commands output human-readable text by default.
216325
+ Use --json flag for machine-readable output (agents, CI).
215876
216326
 
215877
216327
  Examples:
216328
+ driftless session start --files "src/auth/**"
216329
+ driftless session finish
216330
+
215878
216331
  driftless context add "b2b-guard" \\
215879
216332
  --what "Guard que protege endpoints B2B" \\
215880
216333
  --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"
216334
+ --pattern "src/shared/guards/**"
215885
216335
 
215886
- driftless context get sdk --human
216336
+ driftless context get sdk
215887
216337
  driftless context search "auth"
215888
216338
  driftless scan --diff
216339
+ driftless context push --files "src/auth/auth.controller.ts" --dry-run
215889
216340
  `;
215890
216341
  function showCommandHelp(cmd) {
215891
216342
  const help = {
@@ -215934,7 +216385,7 @@ Example:
215934
216385
  context: `driftless context <subcommand> [args] [flags]
215935
216386
 
215936
216387
  Live repo context \u2014 the team's shared codebase memory.
215937
- JSON by default for agent consumption. Use --human for readable output.
216388
+ Human-readable by default. Use --json for machine output.
215938
216389
 
215939
216390
  Subcommands:
215940
216391
  list [filters] List context topics
@@ -215956,69 +216407,20 @@ List filters:
215956
216407
  --manual Only manually-created topics
215957
216408
  --repo <id> Only topics scoped to a given repo
215958
216409
 
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
216410
  Flags:
216007
- --human Human-readable output (default is JSON)
216411
+ --json Machine-readable JSON output
216412
+ --dry-run Preview changes without writing to Driftless Cloud
216008
216413
 
216009
216414
  Examples:
216010
- driftless context list --stale --human
216415
+ driftless context list
216416
+ driftless context list --json
216011
216417
  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"
216418
+ driftless context get --diff
216015
216419
  driftless context search "auth"
216016
216420
  driftless context add "sdk" --pattern "src/sdk/**" --what "Public SDK"
216017
216421
  driftless context update sdk --gotchas "Reset token on org switch"
216018
216422
  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"
216423
+ driftless context push --files "src/auth/guard.ts" --dry-run
216022
216424
  `,
216023
216425
  "install-skill": `driftless install-skill
216024
216426
 
@@ -216070,6 +216472,9 @@ async function main() {
216070
216472
  case "context":
216071
216473
  await contextCommand(args.slice(1));
216072
216474
  break;
216475
+ case "session":
216476
+ await sessionCommand(args.slice(1));
216477
+ break;
216073
216478
  case "install-skill":
216074
216479
  installSkillCommand();
216075
216480
  break;