haansi 0.1.14 → 0.1.15

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.
Files changed (2) hide show
  1. package/dist/haansi.js +232 -85
  2. package/package.json +1 -1
package/dist/haansi.js CHANGED
@@ -39,7 +39,7 @@ var require_package = __commonJS({
39
39
  "package.json"(exports2, module2) {
40
40
  module2.exports = {
41
41
  name: "haansi",
42
- version: "0.1.14",
42
+ version: "0.1.15",
43
43
  description: "Haansi CLI - Session collector and MCP server for Claude Code",
44
44
  bin: {
45
45
  haansi: "./dist/haansi.js"
@@ -64,6 +64,130 @@ var require_package = __commonJS({
64
64
  }
65
65
  });
66
66
 
67
+ // src/commands/auth-login.ts
68
+ var auth_login_exports = {};
69
+ __export(auth_login_exports, {
70
+ authLogin: () => authLogin
71
+ });
72
+ function openBrowser(url2) {
73
+ const cmd = (0, import_node_os.platform)() === "darwin" ? "open" : (0, import_node_os.platform)() === "win32" ? "start" : "xdg-open";
74
+ try {
75
+ (0, import_node_child_process.exec)(`${cmd} ${JSON.stringify(url2)}`);
76
+ return true;
77
+ } catch {
78
+ return false;
79
+ }
80
+ }
81
+ function sleep(ms) {
82
+ return new Promise((resolve4) => setTimeout(resolve4, ms));
83
+ }
84
+ async function authLogin() {
85
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL;
86
+ const appUrl = process.env.HAANSI_APP_URL ?? DEFAULT_APP_URL;
87
+ console.log("Haansi CLI \u2014 Browser Login\n");
88
+ const initiateRes = await fetch(`${apiUrl}/api/v1/auth/extension/initiate`, {
89
+ method: "POST",
90
+ headers: { "Content-Type": "application/json" }
91
+ });
92
+ if (!initiateRes.ok) {
93
+ const body = await initiateRes.text().catch(() => "");
94
+ throw new Error(
95
+ `Failed to start auth session (${initiateRes.status}): ${body.slice(0, 200)}`
96
+ );
97
+ }
98
+ const { session_id } = await initiateRes.json();
99
+ const authUrl = `${appUrl}/auth/cli?session_id=${session_id}`;
100
+ const opened = openBrowser(authUrl);
101
+ if (opened) {
102
+ console.log("A browser window has been opened for you to log in.");
103
+ } else {
104
+ console.log("Could not open browser automatically.");
105
+ }
106
+ console.log(`
107
+ If the browser didn't open, visit this URL:
108
+ ${authUrl}
109
+ `);
110
+ console.log("Waiting for approval...");
111
+ const deadline = Date.now() + TIMEOUT_MS;
112
+ let jwt2 = null;
113
+ let orgName = null;
114
+ while (Date.now() < deadline) {
115
+ await sleep(POLL_INTERVAL_MS);
116
+ const pollRes = await fetch(
117
+ `${apiUrl}/api/v1/auth/extension/token?session_id=${encodeURIComponent(session_id)}`
118
+ );
119
+ if (pollRes.status === 410) {
120
+ throw new Error("Session expired or was already used. Please try again.");
121
+ }
122
+ if (!pollRes.ok && pollRes.status !== 200) {
123
+ continue;
124
+ }
125
+ const data = await pollRes.json();
126
+ if (data.status === "pending") {
127
+ process.stdout.write(".");
128
+ continue;
129
+ }
130
+ if (data.token) {
131
+ jwt2 = data.token;
132
+ orgName = data.organization?.name ?? null;
133
+ break;
134
+ }
135
+ }
136
+ console.log("");
137
+ if (!jwt2) {
138
+ throw new Error(
139
+ "Timed out waiting for approval. You can authenticate manually with `haansi init`."
140
+ );
141
+ }
142
+ console.log("Creating CLI token...");
143
+ const tokenRes = await fetch(`${apiUrl}/api/v1/cli-tokens`, {
144
+ method: "POST",
145
+ headers: {
146
+ Authorization: `Bearer ${jwt2}`,
147
+ "Content-Type": "application/json"
148
+ },
149
+ body: JSON.stringify({ name: "CLI (haansi auth login)" })
150
+ });
151
+ if (!tokenRes.ok) {
152
+ const body = await tokenRes.text().catch(() => "");
153
+ throw new Error(
154
+ `Failed to create CLI token (${tokenRes.status}): ${body.slice(0, 200)}`
155
+ );
156
+ }
157
+ const { token: hskToken } = await tokenRes.json();
158
+ (0, import_node_fs.mkdirSync)(HAANSI_DIR, { recursive: true });
159
+ (0, import_node_fs.writeFileSync)(
160
+ CREDENTIALS_FILE,
161
+ JSON.stringify({ token: hskToken }, null, 2),
162
+ {
163
+ encoding: "utf-8",
164
+ mode: 384
165
+ }
166
+ );
167
+ console.log(`
168
+ Authenticated successfully${orgName ? ` (${orgName})` : ""}!`);
169
+ console.log(`Token saved to ${CREDENTIALS_FILE}`);
170
+ console.log(
171
+ "Run `haansi collect` to upload sessions or `haansi setup-mcp` to configure Claude Code."
172
+ );
173
+ }
174
+ var import_node_child_process, import_node_fs, import_node_path, import_node_os, DEFAULT_API_URL, DEFAULT_APP_URL, HAANSI_DIR, CREDENTIALS_FILE, POLL_INTERVAL_MS, TIMEOUT_MS;
175
+ var init_auth_login = __esm({
176
+ "src/commands/auth-login.ts"() {
177
+ "use strict";
178
+ import_node_child_process = require("node:child_process");
179
+ import_node_fs = require("node:fs");
180
+ import_node_path = require("node:path");
181
+ import_node_os = require("node:os");
182
+ DEFAULT_API_URL = "https://api.haansi.co";
183
+ DEFAULT_APP_URL = "https://app.haansi.co";
184
+ HAANSI_DIR = (0, import_node_path.join)((0, import_node_os.homedir)(), ".haansi");
185
+ CREDENTIALS_FILE = (0, import_node_path.join)(HAANSI_DIR, "credentials.json");
186
+ POLL_INTERVAL_MS = 2e3;
187
+ TIMEOUT_MS = 5 * 60 * 1e3;
188
+ }
189
+ });
190
+
67
191
  // src/commands/init.ts
68
192
  var init_exports = {};
69
193
  __export(init_exports, {
@@ -93,8 +217,11 @@ async function validateToken(token, apiUrl) {
93
217
  }
94
218
  }
95
219
  async function init() {
96
- const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL;
220
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL2;
97
221
  console.log("Haansi CLI \u2014 Setup\n");
222
+ console.log(
223
+ "Tip: For interactive login, run `haansi auth login` to authenticate via browser.\n"
224
+ );
98
225
  console.log(
99
226
  `You can generate an API token at https://app.haansi.co/settings/tokens
100
227
  `
@@ -110,28 +237,28 @@ async function init() {
110
237
  }
111
238
  console.log("\nValidating token...");
112
239
  await validateToken(token, apiUrl);
113
- (0, import_node_fs.mkdirSync)(HAANSI_DIR, { recursive: true });
114
- (0, import_node_fs.writeFileSync)(CREDENTIALS_FILE, JSON.stringify({ token }, null, 2), {
240
+ (0, import_node_fs2.mkdirSync)(HAANSI_DIR2, { recursive: true });
241
+ (0, import_node_fs2.writeFileSync)(CREDENTIALS_FILE2, JSON.stringify({ token }, null, 2), {
115
242
  encoding: "utf-8",
116
243
  mode: 384
117
244
  });
118
245
  console.log(`
119
- Token saved to ${CREDENTIALS_FILE}`);
246
+ Token saved to ${CREDENTIALS_FILE2}`);
120
247
  console.log(
121
248
  "Run `haansi daemon` to start collecting or `haansi setup-mcp` to configure Claude Code."
122
249
  );
123
250
  }
124
- var readline, import_node_fs, import_node_path, import_node_os, DEFAULT_API_URL, HAANSI_DIR, CREDENTIALS_FILE;
251
+ var readline, import_node_fs2, import_node_path2, import_node_os2, DEFAULT_API_URL2, HAANSI_DIR2, CREDENTIALS_FILE2;
125
252
  var init_init = __esm({
126
253
  "src/commands/init.ts"() {
127
254
  "use strict";
128
255
  readline = __toESM(require("node:readline"));
129
- import_node_fs = require("node:fs");
130
- import_node_path = require("node:path");
131
- import_node_os = require("node:os");
132
- DEFAULT_API_URL = "https://api.haansi.co";
133
- HAANSI_DIR = (0, import_node_path.join)((0, import_node_os.homedir)(), ".haansi");
134
- CREDENTIALS_FILE = (0, import_node_path.join)(HAANSI_DIR, "credentials.json");
256
+ import_node_fs2 = require("node:fs");
257
+ import_node_path2 = require("node:path");
258
+ import_node_os2 = require("node:os");
259
+ DEFAULT_API_URL2 = "https://api.haansi.co";
260
+ HAANSI_DIR2 = (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".haansi");
261
+ CREDENTIALS_FILE2 = (0, import_node_path2.join)(HAANSI_DIR2, "credentials.json");
135
262
  }
136
263
  });
137
264
 
@@ -636,13 +763,13 @@ function extractSessionMeta(filePath) {
636
763
  }
637
764
  function resolveToken() {
638
765
  if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
639
- if ((0, import_fs.existsSync)(CREDENTIALS_FILE2)) {
766
+ if ((0, import_fs.existsSync)(CREDENTIALS_FILE3)) {
640
767
  try {
641
- const creds = JSON.parse((0, import_fs.readFileSync)(CREDENTIALS_FILE2, "utf-8"));
768
+ const creds = JSON.parse((0, import_fs.readFileSync)(CREDENTIALS_FILE3, "utf-8"));
642
769
  if (creds.token) return creds.token;
643
770
  } catch {
644
771
  logger.warn("Could not parse credentials file", {
645
- file: CREDENTIALS_FILE2
772
+ file: CREDENTIALS_FILE3
646
773
  });
647
774
  }
648
775
  }
@@ -731,7 +858,7 @@ async function uploadSession(sessionId, content, apiUrl, token, gitInfo) {
731
858
  }
732
859
  }
733
860
  async function collect(options) {
734
- const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL2;
861
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL3;
735
862
  const token = resolveToken();
736
863
  if (!token && !options.dryRun) {
737
864
  throw new Error(
@@ -813,7 +940,7 @@ async function main() {
813
940
  });
814
941
  if (errors > 0) process.exit(1);
815
942
  }
816
- var import_dotenv, import_path, import_fs, import_os, import_zlib, import_child_process, logger, DEFAULT_API_URL2, CREDENTIALS_FILE2, CLAUDE_PROJECTS_DIR, MIN_LINES, EDGE_SCRUB_PATTERNS;
943
+ var import_dotenv, import_path, import_fs, import_os, import_zlib, import_child_process, logger, DEFAULT_API_URL3, CREDENTIALS_FILE3, CLAUDE_PROJECTS_DIR, MIN_LINES, EDGE_SCRUB_PATTERNS;
817
944
  var init_collector = __esm({
818
945
  "../service-capture/claude-sessions/collector.ts"() {
819
946
  "use strict";
@@ -826,8 +953,8 @@ var init_collector = __esm({
826
953
  init_logger();
827
954
  (0, import_dotenv.config)({ path: (0, import_path.resolve)(__dirname, "../../../.env") });
828
955
  logger = createLogger("claude-collector");
829
- DEFAULT_API_URL2 = "https://api.haansi.co";
830
- CREDENTIALS_FILE2 = (0, import_path.join)((0, import_os.homedir)(), ".haansi", "credentials.json");
956
+ DEFAULT_API_URL3 = "https://api.haansi.co";
957
+ CREDENTIALS_FILE3 = (0, import_path.join)((0, import_os.homedir)(), ".haansi", "credentials.json");
831
958
  CLAUDE_PROJECTS_DIR = (0, import_path.join)((0, import_os.homedir)(), ".claude", "projects");
832
959
  MIN_LINES = 4;
833
960
  EDGE_SCRUB_PATTERNS = [
@@ -892,17 +1019,17 @@ var require_task = __commonJS({
892
1019
  this._execution = execution;
893
1020
  }
894
1021
  execute(now) {
895
- let exec;
1022
+ let exec2;
896
1023
  try {
897
- exec = this._execution(now);
1024
+ exec2 = this._execution(now);
898
1025
  } catch (error2) {
899
1026
  return this.emit("task-failed", error2);
900
1027
  }
901
- if (exec instanceof Promise) {
902
- return exec.then(() => this.emit("task-finished")).catch((error2) => this.emit("task-failed", error2));
1028
+ if (exec2 instanceof Promise) {
1029
+ return exec2.then(() => this.emit("task-finished")).catch((error2) => this.emit("task-failed", error2));
903
1030
  } else {
904
1031
  this.emit("task-finished");
905
- return exec;
1032
+ return exec2;
906
1033
  }
907
1034
  }
908
1035
  };
@@ -50545,7 +50672,7 @@ var require_view = __commonJS({
50545
50672
  var dirname = path.dirname;
50546
50673
  var basename2 = path.basename;
50547
50674
  var extname = path.extname;
50548
- var join8 = path.join;
50675
+ var join9 = path.join;
50549
50676
  var resolve4 = path.resolve;
50550
50677
  module2.exports = View;
50551
50678
  function View(name, options) {
@@ -50607,12 +50734,12 @@ var require_view = __commonJS({
50607
50734
  };
50608
50735
  View.prototype.resolve = function resolve5(dir, file2) {
50609
50736
  var ext = this.ext;
50610
- var path2 = join8(dir, file2);
50737
+ var path2 = join9(dir, file2);
50611
50738
  var stat = tryStat(path2);
50612
50739
  if (stat && stat.isFile()) {
50613
50740
  return path2;
50614
50741
  }
50615
- path2 = join8(dir, basename2(file2, ext), "index" + ext);
50742
+ path2 = join9(dir, basename2(file2, ext), "index" + ext);
50616
50743
  stat = tryStat(path2);
50617
50744
  if (stat && stat.isFile()) {
50618
50745
  return path2;
@@ -54257,7 +54384,7 @@ var require_send = __commonJS({
54257
54384
  var Stream = require("stream");
54258
54385
  var util2 = require("util");
54259
54386
  var extname = path.extname;
54260
- var join8 = path.join;
54387
+ var join9 = path.join;
54261
54388
  var normalize = path.normalize;
54262
54389
  var resolve4 = path.resolve;
54263
54390
  var sep = path.sep;
@@ -54429,7 +54556,7 @@ var require_send = __commonJS({
54429
54556
  return res;
54430
54557
  }
54431
54558
  parts = path2.split(sep);
54432
- path2 = normalize(join8(root, path2));
54559
+ path2 = normalize(join9(root, path2));
54433
54560
  } else {
54434
54561
  if (UP_PATH_REGEXP.test(path2)) {
54435
54562
  debug('malicious path "%s"', path2);
@@ -54562,7 +54689,7 @@ var require_send = __commonJS({
54562
54689
  if (err) return self.onStatError(err);
54563
54690
  return self.error(404);
54564
54691
  }
54565
- var p = join8(path2, self._index[i]);
54692
+ var p = join9(path2, self._index[i]);
54566
54693
  debug('stat "%s"', p);
54567
54694
  fs.stat(p, function(err2, stat) {
54568
54695
  if (err2) return next(err2);
@@ -56659,9 +56786,9 @@ var init_streamableHttp = __esm({
56659
56786
  var mcp_server_exports = {};
56660
56787
  function resolveToken2() {
56661
56788
  if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
56662
- if ((0, import_fs2.existsSync)(CREDENTIALS_FILE3)) {
56789
+ if ((0, import_fs2.existsSync)(CREDENTIALS_FILE4)) {
56663
56790
  try {
56664
- const creds = JSON.parse((0, import_fs2.readFileSync)(CREDENTIALS_FILE3, "utf-8"));
56791
+ const creds = JSON.parse((0, import_fs2.readFileSync)(CREDENTIALS_FILE4, "utf-8"));
56665
56792
  if (creds.token) return creds.token;
56666
56793
  } catch {
56667
56794
  }
@@ -56745,7 +56872,7 @@ async function main3() {
56745
56872
  await mcpServer.connect(transport);
56746
56873
  }
56747
56874
  }
56748
- var import_dotenv3, import_path3, import_fs2, import_os2, DEFAULT_API_URL3, CREDENTIALS_FILE3, API_URL, TOKEN, contextSchema, isCollecting, lastCollectTime, COLLECT_COOLDOWN_MS, mcpServer, server;
56875
+ var import_dotenv3, import_path3, import_fs2, import_os2, DEFAULT_API_URL4, CREDENTIALS_FILE4, API_URL, TOKEN, contextSchema, isCollecting, lastCollectTime, COLLECT_COOLDOWN_MS, mcpServer, server;
56749
56876
  var init_mcp_server2 = __esm({
56750
56877
  "../service-capture/claude-sessions/mcp-server.ts"() {
56751
56878
  "use strict";
@@ -56759,9 +56886,9 @@ var init_mcp_server2 = __esm({
56759
56886
  init_stdio2();
56760
56887
  init_types2();
56761
56888
  (0, import_dotenv3.config)({ path: (0, import_path3.resolve)(__dirname, "../../../.env") });
56762
- DEFAULT_API_URL3 = "https://api.haansi.co";
56763
- CREDENTIALS_FILE3 = (0, import_path3.join)((0, import_os2.homedir)(), ".haansi", "credentials.json");
56764
- API_URL = process.env.HAANSI_API_URL ?? DEFAULT_API_URL3;
56889
+ DEFAULT_API_URL4 = "https://api.haansi.co";
56890
+ CREDENTIALS_FILE4 = (0, import_path3.join)((0, import_os2.homedir)(), ".haansi", "credentials.json");
56891
+ API_URL = process.env.HAANSI_API_URL ?? DEFAULT_API_URL4;
56765
56892
  TOKEN = resolveToken2();
56766
56893
  if (!TOKEN) {
56767
56894
  process.exit(1);
@@ -56791,7 +56918,7 @@ var init_mcp_server2 = __esm({
56791
56918
  tools: [
56792
56919
  {
56793
56920
  name: "search_solutions",
56794
- description: "Semantic search over past coding solutions mined from developer sessions. Use this to find how bugs were fixed, what patterns were used, and past resolutions to similar problems. Returns results ranked by relevance.",
56921
+ description: "Search previously solved similar questions from your organization's knowledge base. Finds how bugs were fixed, what patterns were used, and past resolutions to similar problems. Results are ranked by a composite of relevance, quality, recency, and verification status.",
56795
56922
  inputSchema: {
56796
56923
  type: "object",
56797
56924
  properties: {
@@ -56943,8 +57070,13 @@ var init_mcp_server2 = __esm({
56943
57070
  ]
56944
57071
  };
56945
57072
  }
57073
+ const header = `Found ${data.total} answer(s) from similar questions in your organization:
57074
+
57075
+ `;
56946
57076
  return {
56947
- content: [{ type: "text", text: data.results.join("\n\n---\n\n") }]
57077
+ content: [
57078
+ { type: "text", text: header + data.results.join("\n\n---\n\n") }
57079
+ ]
56948
57080
  };
56949
57081
  }
56950
57082
  case "list_recent_solutions": {
@@ -57056,7 +57188,7 @@ __export(setup_mcp_exports, {
57056
57188
  });
57057
57189
  function resolveHaansiBin() {
57058
57190
  try {
57059
- const binPath = (0, import_node_child_process.execSync)("which haansi", { encoding: "utf-8" }).trim();
57191
+ const binPath = (0, import_node_child_process2.execSync)("which haansi", { encoding: "utf-8" }).trim();
57060
57192
  if (binPath) return binPath;
57061
57193
  } catch {
57062
57194
  }
@@ -57064,9 +57196,9 @@ function resolveHaansiBin() {
57064
57196
  }
57065
57197
  async function setupMcp() {
57066
57198
  let config6 = {};
57067
- if ((0, import_node_fs2.existsSync)(CLAUDE_JSON)) {
57199
+ if ((0, import_node_fs3.existsSync)(CLAUDE_JSON)) {
57068
57200
  try {
57069
- config6 = JSON.parse((0, import_node_fs2.readFileSync)(CLAUDE_JSON, "utf-8"));
57201
+ config6 = JSON.parse((0, import_node_fs3.readFileSync)(CLAUDE_JSON, "utf-8"));
57070
57202
  } catch {
57071
57203
  console.error(
57072
57204
  `Warning: Could not parse ${CLAUDE_JSON} \u2014 will overwrite with new config.`
@@ -57081,22 +57213,22 @@ async function setupMcp() {
57081
57213
  command: haansiBin,
57082
57214
  args: ["mcp-server"]
57083
57215
  };
57084
- (0, import_node_fs2.writeFileSync)(CLAUDE_JSON, JSON.stringify(config6, null, 2) + "\n", "utf-8");
57216
+ (0, import_node_fs3.writeFileSync)(CLAUDE_JSON, JSON.stringify(config6, null, 2) + "\n", "utf-8");
57085
57217
  console.log(`MCP server configured in ${CLAUDE_JSON}`);
57086
57218
  console.log(` command: ${haansiBin} mcp-server`);
57087
57219
  console.log(
57088
57220
  "\nRestart Claude Code to activate the haansi-solutions MCP server."
57089
57221
  );
57090
57222
  }
57091
- var import_node_fs2, import_node_path2, import_node_os2, import_node_child_process, CLAUDE_JSON;
57223
+ var import_node_fs3, import_node_path3, import_node_os3, import_node_child_process2, CLAUDE_JSON;
57092
57224
  var init_setup_mcp = __esm({
57093
57225
  "src/commands/setup-mcp.ts"() {
57094
57226
  "use strict";
57095
- import_node_fs2 = require("node:fs");
57096
- import_node_path2 = require("node:path");
57097
- import_node_os2 = require("node:os");
57098
- import_node_child_process = require("node:child_process");
57099
- CLAUDE_JSON = (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".claude.json");
57227
+ import_node_fs3 = require("node:fs");
57228
+ import_node_path3 = require("node:path");
57229
+ import_node_os3 = require("node:os");
57230
+ import_node_child_process2 = require("node:child_process");
57231
+ CLAUDE_JSON = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".claude.json");
57100
57232
  }
57101
57233
  });
57102
57234
 
@@ -57107,7 +57239,7 @@ __export(setup_daemon_exports, {
57107
57239
  });
57108
57240
  function findHaansiBin() {
57109
57241
  try {
57110
- return (0, import_node_child_process2.execSync)("which haansi", { encoding: "utf-8" }).trim();
57242
+ return (0, import_node_child_process3.execSync)("which haansi", { encoding: "utf-8" }).trim();
57111
57243
  } catch {
57112
57244
  throw new Error(
57113
57245
  "Could not find haansi in PATH. Make sure it is installed globally (npm install -g haansi)."
@@ -57146,28 +57278,28 @@ function buildPlist(haansiPath) {
57146
57278
  }
57147
57279
  async function setupDaemon(uninstall) {
57148
57280
  if (uninstall) {
57149
- if (!(0, import_node_fs3.existsSync)(PLIST_PATH)) {
57281
+ if (!(0, import_node_fs4.existsSync)(PLIST_PATH)) {
57150
57282
  console.log("Daemon is not installed.");
57151
57283
  return;
57152
57284
  }
57153
57285
  try {
57154
- (0, import_node_child_process2.spawnSync)("launchctl", ["unload", PLIST_PATH], { stdio: "pipe" });
57286
+ (0, import_node_child_process3.spawnSync)("launchctl", ["unload", PLIST_PATH], { stdio: "pipe" });
57155
57287
  } catch (err) {
57156
57288
  console.error("Failed to unload daemon:", err);
57157
57289
  }
57158
- (0, import_node_fs3.unlinkSync)(PLIST_PATH);
57290
+ (0, import_node_fs4.unlinkSync)(PLIST_PATH);
57159
57291
  console.log("Daemon stopped and removed.");
57160
57292
  return;
57161
57293
  }
57162
57294
  const haansiPath = findHaansiBin();
57163
- (0, import_node_fs3.mkdirSync)((0, import_node_path3.join)((0, import_node_os3.homedir)(), ".haansi"), { recursive: true });
57164
- (0, import_node_fs3.writeFileSync)(PLIST_PATH, buildPlist(haansiPath), "utf-8");
57295
+ (0, import_node_fs4.mkdirSync)((0, import_node_path4.join)((0, import_node_os4.homedir)(), ".haansi"), { recursive: true });
57296
+ (0, import_node_fs4.writeFileSync)(PLIST_PATH, buildPlist(haansiPath), "utf-8");
57165
57297
  try {
57166
- (0, import_node_child_process2.spawnSync)("launchctl", ["unload", PLIST_PATH], { stdio: "pipe" });
57298
+ (0, import_node_child_process3.spawnSync)("launchctl", ["unload", PLIST_PATH], { stdio: "pipe" });
57167
57299
  } catch (err) {
57168
57300
  console.error("Failed to unload previous daemon:", err);
57169
57301
  }
57170
- (0, import_node_child_process2.spawnSync)("launchctl", ["load", PLIST_PATH], { stdio: "inherit" });
57302
+ (0, import_node_child_process3.spawnSync)("launchctl", ["load", PLIST_PATH], { stdio: "inherit" });
57171
57303
  console.log("Daemon installed and started.");
57172
57304
  console.log(`Plist : ${PLIST_PATH}`);
57173
57305
  console.log(`Logs : ${LOG_FILE}`);
@@ -57175,22 +57307,22 @@ async function setupDaemon(uninstall) {
57175
57307
  The daemon will start automatically on every login.`);
57176
57308
  console.log(`To uninstall: haansi setup-daemon --uninstall`);
57177
57309
  }
57178
- var import_node_child_process2, import_node_fs3, import_node_path3, import_node_os3, PLIST_LABEL, PLIST_PATH, LOG_FILE;
57310
+ var import_node_child_process3, import_node_fs4, import_node_path4, import_node_os4, PLIST_LABEL, PLIST_PATH, LOG_FILE;
57179
57311
  var init_setup_daemon = __esm({
57180
57312
  "src/commands/setup-daemon.ts"() {
57181
57313
  "use strict";
57182
- import_node_child_process2 = require("node:child_process");
57183
- import_node_fs3 = require("node:fs");
57184
- import_node_path3 = require("node:path");
57185
- import_node_os3 = require("node:os");
57314
+ import_node_child_process3 = require("node:child_process");
57315
+ import_node_fs4 = require("node:fs");
57316
+ import_node_path4 = require("node:path");
57317
+ import_node_os4 = require("node:os");
57186
57318
  PLIST_LABEL = "co.haansi.daemon";
57187
- PLIST_PATH = (0, import_node_path3.join)(
57188
- (0, import_node_os3.homedir)(),
57319
+ PLIST_PATH = (0, import_node_path4.join)(
57320
+ (0, import_node_os4.homedir)(),
57189
57321
  "Library",
57190
57322
  "LaunchAgents",
57191
57323
  `${PLIST_LABEL}.plist`
57192
57324
  );
57193
- LOG_FILE = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".haansi", "daemon.log");
57325
+ LOG_FILE = (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".haansi", "daemon.log");
57194
57326
  }
57195
57327
  });
57196
57328
 
@@ -57201,9 +57333,9 @@ __export(config_exports, {
57201
57333
  });
57202
57334
  function resolveToken3() {
57203
57335
  if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
57204
- if ((0, import_node_fs4.existsSync)(CREDENTIALS_FILE4)) {
57336
+ if ((0, import_node_fs5.existsSync)(CREDENTIALS_FILE5)) {
57205
57337
  try {
57206
- const creds = JSON.parse((0, import_node_fs4.readFileSync)(CREDENTIALS_FILE4, "utf-8"));
57338
+ const creds = JSON.parse((0, import_node_fs5.readFileSync)(CREDENTIALS_FILE5, "utf-8"));
57207
57339
  if (creds.token) return creds.token;
57208
57340
  } catch (err) {
57209
57341
  console.error("Failed to read credentials file:", err);
@@ -57220,7 +57352,7 @@ async function config5(args) {
57220
57352
  process.exit(1);
57221
57353
  return;
57222
57354
  }
57223
- const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL4;
57355
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL5;
57224
57356
  const subcommand = args[0];
57225
57357
  if (subcommand === "get") {
57226
57358
  const key = args[1];
@@ -57295,15 +57427,15 @@ Supported keys:
57295
57427
  if (subcommand !== void 0) process.exit(1);
57296
57428
  }
57297
57429
  }
57298
- var import_node_fs4, import_node_path4, import_node_os4, DEFAULT_API_URL4, CREDENTIALS_FILE4, VALID_SESSION_MODES;
57430
+ var import_node_fs5, import_node_path5, import_node_os5, DEFAULT_API_URL5, CREDENTIALS_FILE5, VALID_SESSION_MODES;
57299
57431
  var init_config = __esm({
57300
57432
  "src/commands/config.ts"() {
57301
57433
  "use strict";
57302
- import_node_fs4 = require("node:fs");
57303
- import_node_path4 = require("node:path");
57304
- import_node_os4 = require("node:os");
57305
- DEFAULT_API_URL4 = "https://api.haansi.co";
57306
- CREDENTIALS_FILE4 = (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".haansi", "credentials.json");
57434
+ import_node_fs5 = require("node:fs");
57435
+ import_node_path5 = require("node:path");
57436
+ import_node_os5 = require("node:os");
57437
+ DEFAULT_API_URL5 = "https://api.haansi.co";
57438
+ CREDENTIALS_FILE5 = (0, import_node_path5.join)((0, import_node_os5.homedir)(), ".haansi", "credentials.json");
57307
57439
  VALID_SESSION_MODES = ["private", "org"];
57308
57440
  }
57309
57441
  });
@@ -57317,9 +57449,9 @@ __export(list_exports, {
57317
57449
  });
57318
57450
  function resolveToken4() {
57319
57451
  if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
57320
- if ((0, import_node_fs5.existsSync)(CREDENTIALS_FILE5)) {
57452
+ if ((0, import_node_fs6.existsSync)(CREDENTIALS_FILE6)) {
57321
57453
  try {
57322
- const creds = JSON.parse((0, import_node_fs5.readFileSync)(CREDENTIALS_FILE5, "utf-8"));
57454
+ const creds = JSON.parse((0, import_node_fs6.readFileSync)(CREDENTIALS_FILE6, "utf-8"));
57323
57455
  if (creds.token) return creds.token;
57324
57456
  } catch (err) {
57325
57457
  console.error("Failed to read credentials file:", err);
@@ -57353,7 +57485,7 @@ async function apiPost2(path, body, token, apiUrl) {
57353
57485
  return response.json();
57354
57486
  }
57355
57487
  async function list(options) {
57356
- const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL5;
57488
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL6;
57357
57489
  const token = resolveToken4();
57358
57490
  let data;
57359
57491
  if (options.search) {
@@ -57379,7 +57511,7 @@ async function list(options) {
57379
57511
  ${data.results.length} result(s)`);
57380
57512
  }
57381
57513
  async function searchKnowledge(options) {
57382
- const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL5;
57514
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL6;
57383
57515
  const token = resolveToken4();
57384
57516
  let url2 = `/sessions/knowledge/search?q=${encodeURIComponent(options.query)}&limit=${options.limit}`;
57385
57517
  if (options.artifactType) {
@@ -57396,7 +57528,7 @@ async function searchKnowledge(options) {
57396
57528
  ${data.results.length} result(s)`);
57397
57529
  }
57398
57530
  async function saveKnowledge(options) {
57399
- const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL5;
57531
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL6;
57400
57532
  const token = resolveToken4();
57401
57533
  const body = {
57402
57534
  problem_description: options.problem,
@@ -57412,15 +57544,15 @@ async function saveKnowledge(options) {
57412
57544
  console.log(` Solution: ${data.solution_summary}`);
57413
57545
  console.log("\nThis is now searchable via `haansi search-solutions`.");
57414
57546
  }
57415
- var import_node_fs5, import_node_path5, import_node_os5, DEFAULT_API_URL5, CREDENTIALS_FILE5;
57547
+ var import_node_fs6, import_node_path6, import_node_os6, DEFAULT_API_URL6, CREDENTIALS_FILE6;
57416
57548
  var init_list = __esm({
57417
57549
  "src/commands/list.ts"() {
57418
57550
  "use strict";
57419
- import_node_fs5 = require("node:fs");
57420
- import_node_path5 = require("node:path");
57421
- import_node_os5 = require("node:os");
57422
- DEFAULT_API_URL5 = "https://api.haansi.co";
57423
- CREDENTIALS_FILE5 = (0, import_node_path5.join)((0, import_node_os5.homedir)(), ".haansi", "credentials.json");
57551
+ import_node_fs6 = require("node:fs");
57552
+ import_node_path6 = require("node:path");
57553
+ import_node_os6 = require("node:os");
57554
+ DEFAULT_API_URL6 = "https://api.haansi.co";
57555
+ CREDENTIALS_FILE6 = (0, import_node_path6.join)((0, import_node_os6.homedir)(), ".haansi", "credentials.json");
57424
57556
  }
57425
57557
  });
57426
57558
 
@@ -57433,6 +57565,20 @@ async function run2() {
57433
57565
  return;
57434
57566
  }
57435
57567
  switch (command) {
57568
+ case "auth": {
57569
+ const subcommand = process.argv[3];
57570
+ if (subcommand === "login") {
57571
+ const { authLogin: authLogin2 } = await Promise.resolve().then(() => (init_auth_login(), auth_login_exports));
57572
+ await authLogin2();
57573
+ } else {
57574
+ console.error(
57575
+ `Unknown auth subcommand: ${subcommand ?? "(none)"}
57576
+ Usage: haansi auth login`
57577
+ );
57578
+ process.exit(1);
57579
+ }
57580
+ break;
57581
+ }
57436
57582
  case "init": {
57437
57583
  const { init: init2 } = await Promise.resolve().then(() => (init_init(), init_exports));
57438
57584
  await init2();
@@ -57559,7 +57705,8 @@ Usage:
57559
57705
  haansi <command> [options]
57560
57706
 
57561
57707
  Commands:
57562
- init Authenticate and save your API token
57708
+ auth login Log in via browser (recommended)
57709
+ init Authenticate with a token (for CI / headless)
57563
57710
  collect Upload new/changed Claude sessions once
57564
57711
  config get [key] Show your preferences (e.g. session_mode)
57565
57712
  config set <key> <val> Update a preference
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "haansi",
3
- "version": "0.1.14",
3
+ "version": "0.1.15",
4
4
  "description": "Haansi CLI - Session collector and MCP server for Claude Code",
5
5
  "bin": {
6
6
  "haansi": "./dist/haansi.js"