haansi 0.1.0 → 0.1.1

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 +174 -8
  2. package/package.json +11 -4
package/dist/haansi.js CHANGED
@@ -97,7 +97,7 @@ var init_init = __esm({
97
97
  import_node_fs = require("node:fs");
98
98
  import_node_path = require("node:path");
99
99
  import_node_os = require("node:os");
100
- DEFAULT_API_URL = "https://api.haansi.com";
100
+ DEFAULT_API_URL = "https://api.haansi.co";
101
101
  HAANSI_DIR = (0, import_node_path.join)((0, import_node_os.homedir)(), ".haansi");
102
102
  CREDENTIALS_FILE = (0, import_node_path.join)(HAANSI_DIR, "credentials.json");
103
103
  }
@@ -1104,7 +1104,7 @@ var init_collector = __esm({
1104
1104
  init_logger();
1105
1105
  (0, import_dotenv2.config)({ path: (0, import_path3.resolve)(__dirname, "../../../.env") });
1106
1106
  logger3 = createLogger("claude-collector");
1107
- DEFAULT_API_URL2 = "https://api.haansi.com";
1107
+ DEFAULT_API_URL2 = "https://api.haansi.co";
1108
1108
  CREDENTIALS_FILE2 = (0, import_path3.join)((0, import_os3.homedir)(), ".haansi", "credentials.json");
1109
1109
  EDGE_SCRUB_PATTERNS = [
1110
1110
  {
@@ -30355,8 +30355,8 @@ var require_dist = __commonJS({
30355
30355
  return ajv;
30356
30356
  }
30357
30357
  const [formats, exportName] = opts.mode === "fast" ? [formats_1.fastFormats, fastName] : [formats_1.fullFormats, fullName];
30358
- const list = opts.formats || formats_1.formatNames;
30359
- addFormats(ajv, list, formats, exportName);
30358
+ const list2 = opts.formats || formats_1.formatNames;
30359
+ addFormats(ajv, list2, formats, exportName);
30360
30360
  if (opts.keywords)
30361
30361
  (0, limit_1.default)(ajv);
30362
30362
  return ajv;
@@ -30368,11 +30368,11 @@ var require_dist = __commonJS({
30368
30368
  throw new Error(`Unknown format "${name}"`);
30369
30369
  return f;
30370
30370
  };
30371
- function addFormats(ajv, list, fs, exportName) {
30371
+ function addFormats(ajv, list2, fs, exportName) {
30372
30372
  var _a2;
30373
30373
  var _b;
30374
30374
  (_a2 = (_b = ajv.opts.code).formats) !== null && _a2 !== void 0 ? _a2 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
30375
- for (const f of list)
30375
+ for (const f of list2)
30376
30376
  ajv.addFormat(f, fs[f]);
30377
30377
  }
30378
30378
  module2.exports = exports2 = formatsPlugin;
@@ -32027,7 +32027,7 @@ var init_mcp_server2 = __esm({
32027
32027
  init_stdio2();
32028
32028
  init_types2();
32029
32029
  (0, import_dotenv4.config)({ path: (0, import_path5.resolve)(__dirname, "../../../.env") });
32030
- DEFAULT_API_URL3 = "https://api.haansi.com";
32030
+ DEFAULT_API_URL3 = "https://api.haansi.co";
32031
32031
  CREDENTIALS_FILE3 = (0, import_path5.join)((0, import_os4.homedir)(), ".haansi", "credentials.json");
32032
32032
  API_URL = process.env.HAANSI_API_URL ?? DEFAULT_API_URL3;
32033
32033
  TOKEN = resolveToken2();
@@ -32175,6 +32175,151 @@ var init_setup_mcp = __esm({
32175
32175
  }
32176
32176
  });
32177
32177
 
32178
+ // src/commands/setup-daemon.ts
32179
+ var setup_daemon_exports = {};
32180
+ __export(setup_daemon_exports, {
32181
+ setupDaemon: () => setupDaemon
32182
+ });
32183
+ function findHaansiBin() {
32184
+ try {
32185
+ return (0, import_node_child_process.execSync)("which haansi", { encoding: "utf-8" }).trim();
32186
+ } catch {
32187
+ throw new Error("Could not find haansi in PATH. Make sure it is installed globally (npm install -g haansi).");
32188
+ }
32189
+ }
32190
+ function buildPlist(haansiPath) {
32191
+ const binDir = haansiPath.replace(/\/[^/]+$/, "");
32192
+ const path = `${binDir}:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin`;
32193
+ return `<?xml version="1.0" encoding="UTF-8"?>
32194
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
32195
+ <plist version="1.0">
32196
+ <dict>
32197
+ <key>Label</key>
32198
+ <string>${PLIST_LABEL}</string>
32199
+ <key>ProgramArguments</key>
32200
+ <array>
32201
+ <string>${haansiPath}</string>
32202
+ <string>daemon</string>
32203
+ </array>
32204
+ <key>EnvironmentVariables</key>
32205
+ <dict>
32206
+ <key>PATH</key>
32207
+ <string>${path}</string>
32208
+ </dict>
32209
+ <key>RunAtLoad</key>
32210
+ <true/>
32211
+ <key>KeepAlive</key>
32212
+ <true/>
32213
+ <key>StandardOutPath</key>
32214
+ <string>${LOG_FILE}</string>
32215
+ <key>StandardErrorPath</key>
32216
+ <string>${LOG_FILE}</string>
32217
+ </dict>
32218
+ </plist>`;
32219
+ }
32220
+ async function setupDaemon(uninstall) {
32221
+ if (uninstall) {
32222
+ if (!(0, import_node_fs3.existsSync)(PLIST_PATH)) {
32223
+ console.log("Daemon is not installed.");
32224
+ return;
32225
+ }
32226
+ try {
32227
+ (0, import_node_child_process.execSync)(`launchctl unload "${PLIST_PATH}"`, { stdio: "pipe" });
32228
+ } catch {
32229
+ }
32230
+ (0, import_node_fs3.unlinkSync)(PLIST_PATH);
32231
+ console.log("Daemon stopped and removed.");
32232
+ return;
32233
+ }
32234
+ const haansiPath = findHaansiBin();
32235
+ (0, import_node_fs3.mkdirSync)((0, import_node_path3.join)((0, import_node_os3.homedir)(), ".haansi"), { recursive: true });
32236
+ (0, import_node_fs3.writeFileSync)(PLIST_PATH, buildPlist(haansiPath), "utf-8");
32237
+ try {
32238
+ (0, import_node_child_process.execSync)(`launchctl unload "${PLIST_PATH}"`, { stdio: "pipe" });
32239
+ } catch {
32240
+ }
32241
+ (0, import_node_child_process.execSync)(`launchctl load "${PLIST_PATH}"`, { stdio: "inherit" });
32242
+ console.log("Daemon installed and started.");
32243
+ console.log(`Plist : ${PLIST_PATH}`);
32244
+ console.log(`Logs : ${LOG_FILE}`);
32245
+ console.log(`
32246
+ The daemon will start automatically on every login.`);
32247
+ console.log(`To uninstall: haansi setup-daemon --uninstall`);
32248
+ }
32249
+ var import_node_child_process, import_node_fs3, import_node_path3, import_node_os3, PLIST_LABEL, PLIST_PATH, LOG_FILE;
32250
+ var init_setup_daemon = __esm({
32251
+ "src/commands/setup-daemon.ts"() {
32252
+ "use strict";
32253
+ import_node_child_process = require("node:child_process");
32254
+ import_node_fs3 = require("node:fs");
32255
+ import_node_path3 = require("node:path");
32256
+ import_node_os3 = require("node:os");
32257
+ PLIST_LABEL = "co.haansi.daemon";
32258
+ PLIST_PATH = (0, import_node_path3.join)((0, import_node_os3.homedir)(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
32259
+ LOG_FILE = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".haansi", "daemon.log");
32260
+ }
32261
+ });
32262
+
32263
+ // src/commands/list.ts
32264
+ var list_exports = {};
32265
+ __export(list_exports, {
32266
+ list: () => list
32267
+ });
32268
+ function resolveToken3() {
32269
+ if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
32270
+ if ((0, import_node_fs4.existsSync)(CREDENTIALS_FILE4)) {
32271
+ try {
32272
+ const creds = JSON.parse((0, import_node_fs4.readFileSync)(CREDENTIALS_FILE4, "utf-8"));
32273
+ if (creds.token) return creds.token;
32274
+ } catch {
32275
+ }
32276
+ }
32277
+ throw new Error("No token found. Run `haansi init` first.");
32278
+ }
32279
+ async function apiGet2(path, token, apiUrl) {
32280
+ const response = await fetch(`${apiUrl}/api/v1${path}`, {
32281
+ headers: { Authorization: `Bearer ${token}` }
32282
+ });
32283
+ if (!response.ok) {
32284
+ const body = await response.text().catch(() => "");
32285
+ throw new Error(`API error ${response.status}: ${body.slice(0, 200)}`);
32286
+ }
32287
+ return response.json();
32288
+ }
32289
+ async function list(options) {
32290
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL4;
32291
+ const token = resolveToken3();
32292
+ let data;
32293
+ if (options.search) {
32294
+ data = await apiGet2(
32295
+ `/sessions/search?q=${encodeURIComponent(options.search)}&top_k=${options.limit}`,
32296
+ token,
32297
+ apiUrl
32298
+ );
32299
+ } else {
32300
+ data = await apiGet2(`/sessions/recent?limit=${options.limit}`, token, apiUrl);
32301
+ }
32302
+ if (!data.results || data.results.length === 0) {
32303
+ console.log("No solutions found. Run `haansi collect` to upload sessions.");
32304
+ return;
32305
+ }
32306
+ const separator = "\n" + "\u2500".repeat(60) + "\n";
32307
+ console.log(data.results.join(separator));
32308
+ console.log(`
32309
+ ${data.results.length} result(s)`);
32310
+ }
32311
+ var import_node_fs4, import_node_path4, import_node_os4, DEFAULT_API_URL4, CREDENTIALS_FILE4;
32312
+ var init_list = __esm({
32313
+ "src/commands/list.ts"() {
32314
+ "use strict";
32315
+ import_node_fs4 = require("node:fs");
32316
+ import_node_path4 = require("node:path");
32317
+ import_node_os4 = require("node:os");
32318
+ DEFAULT_API_URL4 = "https://api.haansi.co";
32319
+ CREDENTIALS_FILE4 = (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".haansi", "credentials.json");
32320
+ }
32321
+ });
32322
+
32178
32323
  // src/index.ts
32179
32324
  var command = process.argv[2];
32180
32325
  async function run2() {
@@ -32210,6 +32355,21 @@ Collector done: ${uploaded} uploaded, ${skipped} unchanged, ${errors} errors`);
32210
32355
  await setupMcp2();
32211
32356
  break;
32212
32357
  }
32358
+ case "setup-daemon": {
32359
+ const { setupDaemon: setupDaemon2 } = await Promise.resolve().then(() => (init_setup_daemon(), setup_daemon_exports));
32360
+ const uninstall = process.argv.includes("--uninstall");
32361
+ await setupDaemon2(uninstall);
32362
+ break;
32363
+ }
32364
+ case "list": {
32365
+ const { list: list2 } = await Promise.resolve().then(() => (init_list(), list_exports));
32366
+ const searchIdx = process.argv.indexOf("--search");
32367
+ const search = searchIdx !== -1 ? process.argv[searchIdx + 1] : void 0;
32368
+ const limitIdx = process.argv.indexOf("--limit");
32369
+ const limit = limitIdx !== -1 ? parseInt(process.argv[limitIdx + 1], 10) : 10;
32370
+ await list2({ search, limit });
32371
+ break;
32372
+ }
32213
32373
  case "help":
32214
32374
  default: {
32215
32375
  printUsage();
@@ -32231,8 +32391,14 @@ Commands:
32231
32391
  daemon Run the collector on a schedule (every 30 min)
32232
32392
  mcp-server Start the Haansi MCP server (used by Claude Code)
32233
32393
  setup-mcp Add haansi-solutions MCP entry to ~/.claude.json
32394
+ list List or search mined solutions
32395
+ setup-daemon Install launchd service to auto-start daemon on login (macOS)
32234
32396
  help Show this help message
32235
32397
 
32398
+ Options for list:
32399
+ --search <query> Semantic search instead of listing recent
32400
+ --limit <n> Number of results (default: 10)
32401
+
32236
32402
  Options for collect:
32237
32403
  --force-all Re-upload all sessions, not just new ones
32238
32404
  --dry-run Scan without uploading
@@ -32240,7 +32406,7 @@ Options for collect:
32240
32406
 
32241
32407
  Environment variables:
32242
32408
  HAANSI_TOKEN API token (or use \`haansi init\` to save it)
32243
- HAANSI_API_URL API base URL (default: https://api.haansi.com)
32409
+ HAANSI_API_URL API base URL (default: https://api.haansi.co)
32244
32410
  CLAUDE_PROJECT_PATH Collect from a specific project path only
32245
32411
  CLAUDE_COLLECT_SCHEDULE Cron expression for daemon (default: */30 * * * *)
32246
32412
  `);
package/package.json CHANGED
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "name": "haansi",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Haansi CLI - Session collector and MCP server for Claude Code",
5
- "bin": { "haansi": "./dist/haansi.js" },
6
- "files": ["dist/", "README.md"],
5
+ "bin": {
6
+ "haansi": "./dist/haansi.js"
7
+ },
8
+ "files": [
9
+ "dist/",
10
+ "README.md"
11
+ ],
7
12
  "scripts": {
8
13
  "build": "node build.mjs",
9
14
  "prepublishOnly": "npm run build"
@@ -13,5 +18,7 @@
13
18
  "typescript": "^5.7.2",
14
19
  "@types/node": "^22.10.1"
15
20
  },
16
- "engines": { "node": ">=18.0.0" }
21
+ "engines": {
22
+ "node": ">=18.0.0"
23
+ }
17
24
  }