haansi 0.1.14 → 0.1.16

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 +231 -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.16",
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,129 @@ 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
+ console.log("Haansi CLI \u2014 Browser Login\n");
87
+ const initiateRes = await fetch(`${apiUrl}/api/v1/auth/extension/initiate`, {
88
+ method: "POST",
89
+ headers: { "Content-Type": "application/json" }
90
+ });
91
+ if (!initiateRes.ok) {
92
+ const body = await initiateRes.text().catch(() => "");
93
+ throw new Error(
94
+ `Failed to start auth session (${initiateRes.status}): ${body.slice(0, 200)}`
95
+ );
96
+ }
97
+ const { session_id, auth_url: vsCodeAuthUrl } = await initiateRes.json();
98
+ const frontendOrigin = new URL(vsCodeAuthUrl).origin;
99
+ const authUrl = `${frontendOrigin}/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, 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
+ HAANSI_DIR = (0, import_node_path.join)((0, import_node_os.homedir)(), ".haansi");
184
+ CREDENTIALS_FILE = (0, import_node_path.join)(HAANSI_DIR, "credentials.json");
185
+ POLL_INTERVAL_MS = 2e3;
186
+ TIMEOUT_MS = 5 * 60 * 1e3;
187
+ }
188
+ });
189
+
67
190
  // src/commands/init.ts
68
191
  var init_exports = {};
69
192
  __export(init_exports, {
@@ -93,8 +216,11 @@ async function validateToken(token, apiUrl) {
93
216
  }
94
217
  }
95
218
  async function init() {
96
- const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL;
219
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL2;
97
220
  console.log("Haansi CLI \u2014 Setup\n");
221
+ console.log(
222
+ "Tip: For interactive login, run `haansi auth login` to authenticate via browser.\n"
223
+ );
98
224
  console.log(
99
225
  `You can generate an API token at https://app.haansi.co/settings/tokens
100
226
  `
@@ -110,28 +236,28 @@ async function init() {
110
236
  }
111
237
  console.log("\nValidating token...");
112
238
  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), {
239
+ (0, import_node_fs2.mkdirSync)(HAANSI_DIR2, { recursive: true });
240
+ (0, import_node_fs2.writeFileSync)(CREDENTIALS_FILE2, JSON.stringify({ token }, null, 2), {
115
241
  encoding: "utf-8",
116
242
  mode: 384
117
243
  });
118
244
  console.log(`
119
- Token saved to ${CREDENTIALS_FILE}`);
245
+ Token saved to ${CREDENTIALS_FILE2}`);
120
246
  console.log(
121
247
  "Run `haansi daemon` to start collecting or `haansi setup-mcp` to configure Claude Code."
122
248
  );
123
249
  }
124
- var readline, import_node_fs, import_node_path, import_node_os, DEFAULT_API_URL, HAANSI_DIR, CREDENTIALS_FILE;
250
+ var readline, import_node_fs2, import_node_path2, import_node_os2, DEFAULT_API_URL2, HAANSI_DIR2, CREDENTIALS_FILE2;
125
251
  var init_init = __esm({
126
252
  "src/commands/init.ts"() {
127
253
  "use strict";
128
254
  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");
255
+ import_node_fs2 = require("node:fs");
256
+ import_node_path2 = require("node:path");
257
+ import_node_os2 = require("node:os");
258
+ DEFAULT_API_URL2 = "https://api.haansi.co";
259
+ HAANSI_DIR2 = (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".haansi");
260
+ CREDENTIALS_FILE2 = (0, import_node_path2.join)(HAANSI_DIR2, "credentials.json");
135
261
  }
136
262
  });
137
263
 
@@ -636,13 +762,13 @@ function extractSessionMeta(filePath) {
636
762
  }
637
763
  function resolveToken() {
638
764
  if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
639
- if ((0, import_fs.existsSync)(CREDENTIALS_FILE2)) {
765
+ if ((0, import_fs.existsSync)(CREDENTIALS_FILE3)) {
640
766
  try {
641
- const creds = JSON.parse((0, import_fs.readFileSync)(CREDENTIALS_FILE2, "utf-8"));
767
+ const creds = JSON.parse((0, import_fs.readFileSync)(CREDENTIALS_FILE3, "utf-8"));
642
768
  if (creds.token) return creds.token;
643
769
  } catch {
644
770
  logger.warn("Could not parse credentials file", {
645
- file: CREDENTIALS_FILE2
771
+ file: CREDENTIALS_FILE3
646
772
  });
647
773
  }
648
774
  }
@@ -731,7 +857,7 @@ async function uploadSession(sessionId, content, apiUrl, token, gitInfo) {
731
857
  }
732
858
  }
733
859
  async function collect(options) {
734
- const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL2;
860
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL3;
735
861
  const token = resolveToken();
736
862
  if (!token && !options.dryRun) {
737
863
  throw new Error(
@@ -813,7 +939,7 @@ async function main() {
813
939
  });
814
940
  if (errors > 0) process.exit(1);
815
941
  }
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;
942
+ 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
943
  var init_collector = __esm({
818
944
  "../service-capture/claude-sessions/collector.ts"() {
819
945
  "use strict";
@@ -826,8 +952,8 @@ var init_collector = __esm({
826
952
  init_logger();
827
953
  (0, import_dotenv.config)({ path: (0, import_path.resolve)(__dirname, "../../../.env") });
828
954
  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");
955
+ DEFAULT_API_URL3 = "https://api.haansi.co";
956
+ CREDENTIALS_FILE3 = (0, import_path.join)((0, import_os.homedir)(), ".haansi", "credentials.json");
831
957
  CLAUDE_PROJECTS_DIR = (0, import_path.join)((0, import_os.homedir)(), ".claude", "projects");
832
958
  MIN_LINES = 4;
833
959
  EDGE_SCRUB_PATTERNS = [
@@ -892,17 +1018,17 @@ var require_task = __commonJS({
892
1018
  this._execution = execution;
893
1019
  }
894
1020
  execute(now) {
895
- let exec;
1021
+ let exec2;
896
1022
  try {
897
- exec = this._execution(now);
1023
+ exec2 = this._execution(now);
898
1024
  } catch (error2) {
899
1025
  return this.emit("task-failed", error2);
900
1026
  }
901
- if (exec instanceof Promise) {
902
- return exec.then(() => this.emit("task-finished")).catch((error2) => this.emit("task-failed", error2));
1027
+ if (exec2 instanceof Promise) {
1028
+ return exec2.then(() => this.emit("task-finished")).catch((error2) => this.emit("task-failed", error2));
903
1029
  } else {
904
1030
  this.emit("task-finished");
905
- return exec;
1031
+ return exec2;
906
1032
  }
907
1033
  }
908
1034
  };
@@ -50545,7 +50671,7 @@ var require_view = __commonJS({
50545
50671
  var dirname = path.dirname;
50546
50672
  var basename2 = path.basename;
50547
50673
  var extname = path.extname;
50548
- var join8 = path.join;
50674
+ var join9 = path.join;
50549
50675
  var resolve4 = path.resolve;
50550
50676
  module2.exports = View;
50551
50677
  function View(name, options) {
@@ -50607,12 +50733,12 @@ var require_view = __commonJS({
50607
50733
  };
50608
50734
  View.prototype.resolve = function resolve5(dir, file2) {
50609
50735
  var ext = this.ext;
50610
- var path2 = join8(dir, file2);
50736
+ var path2 = join9(dir, file2);
50611
50737
  var stat = tryStat(path2);
50612
50738
  if (stat && stat.isFile()) {
50613
50739
  return path2;
50614
50740
  }
50615
- path2 = join8(dir, basename2(file2, ext), "index" + ext);
50741
+ path2 = join9(dir, basename2(file2, ext), "index" + ext);
50616
50742
  stat = tryStat(path2);
50617
50743
  if (stat && stat.isFile()) {
50618
50744
  return path2;
@@ -54257,7 +54383,7 @@ var require_send = __commonJS({
54257
54383
  var Stream = require("stream");
54258
54384
  var util2 = require("util");
54259
54385
  var extname = path.extname;
54260
- var join8 = path.join;
54386
+ var join9 = path.join;
54261
54387
  var normalize = path.normalize;
54262
54388
  var resolve4 = path.resolve;
54263
54389
  var sep = path.sep;
@@ -54429,7 +54555,7 @@ var require_send = __commonJS({
54429
54555
  return res;
54430
54556
  }
54431
54557
  parts = path2.split(sep);
54432
- path2 = normalize(join8(root, path2));
54558
+ path2 = normalize(join9(root, path2));
54433
54559
  } else {
54434
54560
  if (UP_PATH_REGEXP.test(path2)) {
54435
54561
  debug('malicious path "%s"', path2);
@@ -54562,7 +54688,7 @@ var require_send = __commonJS({
54562
54688
  if (err) return self.onStatError(err);
54563
54689
  return self.error(404);
54564
54690
  }
54565
- var p = join8(path2, self._index[i]);
54691
+ var p = join9(path2, self._index[i]);
54566
54692
  debug('stat "%s"', p);
54567
54693
  fs.stat(p, function(err2, stat) {
54568
54694
  if (err2) return next(err2);
@@ -56659,9 +56785,9 @@ var init_streamableHttp = __esm({
56659
56785
  var mcp_server_exports = {};
56660
56786
  function resolveToken2() {
56661
56787
  if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
56662
- if ((0, import_fs2.existsSync)(CREDENTIALS_FILE3)) {
56788
+ if ((0, import_fs2.existsSync)(CREDENTIALS_FILE4)) {
56663
56789
  try {
56664
- const creds = JSON.parse((0, import_fs2.readFileSync)(CREDENTIALS_FILE3, "utf-8"));
56790
+ const creds = JSON.parse((0, import_fs2.readFileSync)(CREDENTIALS_FILE4, "utf-8"));
56665
56791
  if (creds.token) return creds.token;
56666
56792
  } catch {
56667
56793
  }
@@ -56745,7 +56871,7 @@ async function main3() {
56745
56871
  await mcpServer.connect(transport);
56746
56872
  }
56747
56873
  }
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;
56874
+ 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
56875
  var init_mcp_server2 = __esm({
56750
56876
  "../service-capture/claude-sessions/mcp-server.ts"() {
56751
56877
  "use strict";
@@ -56759,9 +56885,9 @@ var init_mcp_server2 = __esm({
56759
56885
  init_stdio2();
56760
56886
  init_types2();
56761
56887
  (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;
56888
+ DEFAULT_API_URL4 = "https://api.haansi.co";
56889
+ CREDENTIALS_FILE4 = (0, import_path3.join)((0, import_os2.homedir)(), ".haansi", "credentials.json");
56890
+ API_URL = process.env.HAANSI_API_URL ?? DEFAULT_API_URL4;
56765
56891
  TOKEN = resolveToken2();
56766
56892
  if (!TOKEN) {
56767
56893
  process.exit(1);
@@ -56791,7 +56917,7 @@ var init_mcp_server2 = __esm({
56791
56917
  tools: [
56792
56918
  {
56793
56919
  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.",
56920
+ 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
56921
  inputSchema: {
56796
56922
  type: "object",
56797
56923
  properties: {
@@ -56943,8 +57069,13 @@ var init_mcp_server2 = __esm({
56943
57069
  ]
56944
57070
  };
56945
57071
  }
57072
+ const header = `Found ${data.total} answer(s) from similar questions in your organization:
57073
+
57074
+ `;
56946
57075
  return {
56947
- content: [{ type: "text", text: data.results.join("\n\n---\n\n") }]
57076
+ content: [
57077
+ { type: "text", text: header + data.results.join("\n\n---\n\n") }
57078
+ ]
56948
57079
  };
56949
57080
  }
56950
57081
  case "list_recent_solutions": {
@@ -57056,7 +57187,7 @@ __export(setup_mcp_exports, {
57056
57187
  });
57057
57188
  function resolveHaansiBin() {
57058
57189
  try {
57059
- const binPath = (0, import_node_child_process.execSync)("which haansi", { encoding: "utf-8" }).trim();
57190
+ const binPath = (0, import_node_child_process2.execSync)("which haansi", { encoding: "utf-8" }).trim();
57060
57191
  if (binPath) return binPath;
57061
57192
  } catch {
57062
57193
  }
@@ -57064,9 +57195,9 @@ function resolveHaansiBin() {
57064
57195
  }
57065
57196
  async function setupMcp() {
57066
57197
  let config6 = {};
57067
- if ((0, import_node_fs2.existsSync)(CLAUDE_JSON)) {
57198
+ if ((0, import_node_fs3.existsSync)(CLAUDE_JSON)) {
57068
57199
  try {
57069
- config6 = JSON.parse((0, import_node_fs2.readFileSync)(CLAUDE_JSON, "utf-8"));
57200
+ config6 = JSON.parse((0, import_node_fs3.readFileSync)(CLAUDE_JSON, "utf-8"));
57070
57201
  } catch {
57071
57202
  console.error(
57072
57203
  `Warning: Could not parse ${CLAUDE_JSON} \u2014 will overwrite with new config.`
@@ -57081,22 +57212,22 @@ async function setupMcp() {
57081
57212
  command: haansiBin,
57082
57213
  args: ["mcp-server"]
57083
57214
  };
57084
- (0, import_node_fs2.writeFileSync)(CLAUDE_JSON, JSON.stringify(config6, null, 2) + "\n", "utf-8");
57215
+ (0, import_node_fs3.writeFileSync)(CLAUDE_JSON, JSON.stringify(config6, null, 2) + "\n", "utf-8");
57085
57216
  console.log(`MCP server configured in ${CLAUDE_JSON}`);
57086
57217
  console.log(` command: ${haansiBin} mcp-server`);
57087
57218
  console.log(
57088
57219
  "\nRestart Claude Code to activate the haansi-solutions MCP server."
57089
57220
  );
57090
57221
  }
57091
- var import_node_fs2, import_node_path2, import_node_os2, import_node_child_process, CLAUDE_JSON;
57222
+ var import_node_fs3, import_node_path3, import_node_os3, import_node_child_process2, CLAUDE_JSON;
57092
57223
  var init_setup_mcp = __esm({
57093
57224
  "src/commands/setup-mcp.ts"() {
57094
57225
  "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");
57226
+ import_node_fs3 = require("node:fs");
57227
+ import_node_path3 = require("node:path");
57228
+ import_node_os3 = require("node:os");
57229
+ import_node_child_process2 = require("node:child_process");
57230
+ CLAUDE_JSON = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".claude.json");
57100
57231
  }
57101
57232
  });
57102
57233
 
@@ -57107,7 +57238,7 @@ __export(setup_daemon_exports, {
57107
57238
  });
57108
57239
  function findHaansiBin() {
57109
57240
  try {
57110
- return (0, import_node_child_process2.execSync)("which haansi", { encoding: "utf-8" }).trim();
57241
+ return (0, import_node_child_process3.execSync)("which haansi", { encoding: "utf-8" }).trim();
57111
57242
  } catch {
57112
57243
  throw new Error(
57113
57244
  "Could not find haansi in PATH. Make sure it is installed globally (npm install -g haansi)."
@@ -57146,28 +57277,28 @@ function buildPlist(haansiPath) {
57146
57277
  }
57147
57278
  async function setupDaemon(uninstall) {
57148
57279
  if (uninstall) {
57149
- if (!(0, import_node_fs3.existsSync)(PLIST_PATH)) {
57280
+ if (!(0, import_node_fs4.existsSync)(PLIST_PATH)) {
57150
57281
  console.log("Daemon is not installed.");
57151
57282
  return;
57152
57283
  }
57153
57284
  try {
57154
- (0, import_node_child_process2.spawnSync)("launchctl", ["unload", PLIST_PATH], { stdio: "pipe" });
57285
+ (0, import_node_child_process3.spawnSync)("launchctl", ["unload", PLIST_PATH], { stdio: "pipe" });
57155
57286
  } catch (err) {
57156
57287
  console.error("Failed to unload daemon:", err);
57157
57288
  }
57158
- (0, import_node_fs3.unlinkSync)(PLIST_PATH);
57289
+ (0, import_node_fs4.unlinkSync)(PLIST_PATH);
57159
57290
  console.log("Daemon stopped and removed.");
57160
57291
  return;
57161
57292
  }
57162
57293
  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");
57294
+ (0, import_node_fs4.mkdirSync)((0, import_node_path4.join)((0, import_node_os4.homedir)(), ".haansi"), { recursive: true });
57295
+ (0, import_node_fs4.writeFileSync)(PLIST_PATH, buildPlist(haansiPath), "utf-8");
57165
57296
  try {
57166
- (0, import_node_child_process2.spawnSync)("launchctl", ["unload", PLIST_PATH], { stdio: "pipe" });
57297
+ (0, import_node_child_process3.spawnSync)("launchctl", ["unload", PLIST_PATH], { stdio: "pipe" });
57167
57298
  } catch (err) {
57168
57299
  console.error("Failed to unload previous daemon:", err);
57169
57300
  }
57170
- (0, import_node_child_process2.spawnSync)("launchctl", ["load", PLIST_PATH], { stdio: "inherit" });
57301
+ (0, import_node_child_process3.spawnSync)("launchctl", ["load", PLIST_PATH], { stdio: "inherit" });
57171
57302
  console.log("Daemon installed and started.");
57172
57303
  console.log(`Plist : ${PLIST_PATH}`);
57173
57304
  console.log(`Logs : ${LOG_FILE}`);
@@ -57175,22 +57306,22 @@ async function setupDaemon(uninstall) {
57175
57306
  The daemon will start automatically on every login.`);
57176
57307
  console.log(`To uninstall: haansi setup-daemon --uninstall`);
57177
57308
  }
57178
- var import_node_child_process2, import_node_fs3, import_node_path3, import_node_os3, PLIST_LABEL, PLIST_PATH, LOG_FILE;
57309
+ var import_node_child_process3, import_node_fs4, import_node_path4, import_node_os4, PLIST_LABEL, PLIST_PATH, LOG_FILE;
57179
57310
  var init_setup_daemon = __esm({
57180
57311
  "src/commands/setup-daemon.ts"() {
57181
57312
  "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");
57313
+ import_node_child_process3 = require("node:child_process");
57314
+ import_node_fs4 = require("node:fs");
57315
+ import_node_path4 = require("node:path");
57316
+ import_node_os4 = require("node:os");
57186
57317
  PLIST_LABEL = "co.haansi.daemon";
57187
- PLIST_PATH = (0, import_node_path3.join)(
57188
- (0, import_node_os3.homedir)(),
57318
+ PLIST_PATH = (0, import_node_path4.join)(
57319
+ (0, import_node_os4.homedir)(),
57189
57320
  "Library",
57190
57321
  "LaunchAgents",
57191
57322
  `${PLIST_LABEL}.plist`
57192
57323
  );
57193
- LOG_FILE = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".haansi", "daemon.log");
57324
+ LOG_FILE = (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".haansi", "daemon.log");
57194
57325
  }
57195
57326
  });
57196
57327
 
@@ -57201,9 +57332,9 @@ __export(config_exports, {
57201
57332
  });
57202
57333
  function resolveToken3() {
57203
57334
  if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
57204
- if ((0, import_node_fs4.existsSync)(CREDENTIALS_FILE4)) {
57335
+ if ((0, import_node_fs5.existsSync)(CREDENTIALS_FILE5)) {
57205
57336
  try {
57206
- const creds = JSON.parse((0, import_node_fs4.readFileSync)(CREDENTIALS_FILE4, "utf-8"));
57337
+ const creds = JSON.parse((0, import_node_fs5.readFileSync)(CREDENTIALS_FILE5, "utf-8"));
57207
57338
  if (creds.token) return creds.token;
57208
57339
  } catch (err) {
57209
57340
  console.error("Failed to read credentials file:", err);
@@ -57220,7 +57351,7 @@ async function config5(args) {
57220
57351
  process.exit(1);
57221
57352
  return;
57222
57353
  }
57223
- const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL4;
57354
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL5;
57224
57355
  const subcommand = args[0];
57225
57356
  if (subcommand === "get") {
57226
57357
  const key = args[1];
@@ -57295,15 +57426,15 @@ Supported keys:
57295
57426
  if (subcommand !== void 0) process.exit(1);
57296
57427
  }
57297
57428
  }
57298
- var import_node_fs4, import_node_path4, import_node_os4, DEFAULT_API_URL4, CREDENTIALS_FILE4, VALID_SESSION_MODES;
57429
+ var import_node_fs5, import_node_path5, import_node_os5, DEFAULT_API_URL5, CREDENTIALS_FILE5, VALID_SESSION_MODES;
57299
57430
  var init_config = __esm({
57300
57431
  "src/commands/config.ts"() {
57301
57432
  "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");
57433
+ import_node_fs5 = require("node:fs");
57434
+ import_node_path5 = require("node:path");
57435
+ import_node_os5 = require("node:os");
57436
+ DEFAULT_API_URL5 = "https://api.haansi.co";
57437
+ CREDENTIALS_FILE5 = (0, import_node_path5.join)((0, import_node_os5.homedir)(), ".haansi", "credentials.json");
57307
57438
  VALID_SESSION_MODES = ["private", "org"];
57308
57439
  }
57309
57440
  });
@@ -57317,9 +57448,9 @@ __export(list_exports, {
57317
57448
  });
57318
57449
  function resolveToken4() {
57319
57450
  if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
57320
- if ((0, import_node_fs5.existsSync)(CREDENTIALS_FILE5)) {
57451
+ if ((0, import_node_fs6.existsSync)(CREDENTIALS_FILE6)) {
57321
57452
  try {
57322
- const creds = JSON.parse((0, import_node_fs5.readFileSync)(CREDENTIALS_FILE5, "utf-8"));
57453
+ const creds = JSON.parse((0, import_node_fs6.readFileSync)(CREDENTIALS_FILE6, "utf-8"));
57323
57454
  if (creds.token) return creds.token;
57324
57455
  } catch (err) {
57325
57456
  console.error("Failed to read credentials file:", err);
@@ -57353,7 +57484,7 @@ async function apiPost2(path, body, token, apiUrl) {
57353
57484
  return response.json();
57354
57485
  }
57355
57486
  async function list(options) {
57356
- const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL5;
57487
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL6;
57357
57488
  const token = resolveToken4();
57358
57489
  let data;
57359
57490
  if (options.search) {
@@ -57379,7 +57510,7 @@ async function list(options) {
57379
57510
  ${data.results.length} result(s)`);
57380
57511
  }
57381
57512
  async function searchKnowledge(options) {
57382
- const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL5;
57513
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL6;
57383
57514
  const token = resolveToken4();
57384
57515
  let url2 = `/sessions/knowledge/search?q=${encodeURIComponent(options.query)}&limit=${options.limit}`;
57385
57516
  if (options.artifactType) {
@@ -57396,7 +57527,7 @@ async function searchKnowledge(options) {
57396
57527
  ${data.results.length} result(s)`);
57397
57528
  }
57398
57529
  async function saveKnowledge(options) {
57399
- const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL5;
57530
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL6;
57400
57531
  const token = resolveToken4();
57401
57532
  const body = {
57402
57533
  problem_description: options.problem,
@@ -57412,15 +57543,15 @@ async function saveKnowledge(options) {
57412
57543
  console.log(` Solution: ${data.solution_summary}`);
57413
57544
  console.log("\nThis is now searchable via `haansi search-solutions`.");
57414
57545
  }
57415
- var import_node_fs5, import_node_path5, import_node_os5, DEFAULT_API_URL5, CREDENTIALS_FILE5;
57546
+ var import_node_fs6, import_node_path6, import_node_os6, DEFAULT_API_URL6, CREDENTIALS_FILE6;
57416
57547
  var init_list = __esm({
57417
57548
  "src/commands/list.ts"() {
57418
57549
  "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");
57550
+ import_node_fs6 = require("node:fs");
57551
+ import_node_path6 = require("node:path");
57552
+ import_node_os6 = require("node:os");
57553
+ DEFAULT_API_URL6 = "https://api.haansi.co";
57554
+ CREDENTIALS_FILE6 = (0, import_node_path6.join)((0, import_node_os6.homedir)(), ".haansi", "credentials.json");
57424
57555
  }
57425
57556
  });
57426
57557
 
@@ -57433,6 +57564,20 @@ async function run2() {
57433
57564
  return;
57434
57565
  }
57435
57566
  switch (command) {
57567
+ case "auth": {
57568
+ const subcommand = process.argv[3];
57569
+ if (subcommand === "login") {
57570
+ const { authLogin: authLogin2 } = await Promise.resolve().then(() => (init_auth_login(), auth_login_exports));
57571
+ await authLogin2();
57572
+ } else {
57573
+ console.error(
57574
+ `Unknown auth subcommand: ${subcommand ?? "(none)"}
57575
+ Usage: haansi auth login`
57576
+ );
57577
+ process.exit(1);
57578
+ }
57579
+ break;
57580
+ }
57436
57581
  case "init": {
57437
57582
  const { init: init2 } = await Promise.resolve().then(() => (init_init(), init_exports));
57438
57583
  await init2();
@@ -57559,7 +57704,8 @@ Usage:
57559
57704
  haansi <command> [options]
57560
57705
 
57561
57706
  Commands:
57562
- init Authenticate and save your API token
57707
+ auth login Log in via browser (recommended)
57708
+ init Authenticate with a token (for CI / headless)
57563
57709
  collect Upload new/changed Claude sessions once
57564
57710
  config get [key] Show your preferences (e.g. session_mode)
57565
57711
  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.16",
4
4
  "description": "Haansi CLI - Session collector and MCP server for Claude Code",
5
5
  "bin": {
6
6
  "haansi": "./dist/haansi.js"