haansi 0.1.6 → 0.1.8

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 +191 -38
  2. package/package.json +1 -1
package/dist/haansi.js CHANGED
@@ -34,6 +34,36 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
34
34
  ));
35
35
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
36
36
 
37
+ // package.json
38
+ var require_package = __commonJS({
39
+ "package.json"(exports2, module2) {
40
+ module2.exports = {
41
+ name: "haansi",
42
+ version: "0.1.8",
43
+ description: "Haansi CLI - Session collector and MCP server for Claude Code",
44
+ bin: {
45
+ haansi: "./dist/haansi.js"
46
+ },
47
+ files: [
48
+ "dist/",
49
+ "README.md"
50
+ ],
51
+ scripts: {
52
+ build: "node build.mjs",
53
+ prepublishOnly: "npm run build"
54
+ },
55
+ devDependencies: {
56
+ esbuild: "^0.27.0",
57
+ typescript: "^5.7.2",
58
+ "@types/node": "^22.10.1"
59
+ },
60
+ engines: {
61
+ node: ">=18.0.0"
62
+ }
63
+ };
64
+ }
65
+ });
66
+
37
67
  // src/commands/init.ts
38
68
  var init_exports = {};
39
69
  __export(init_exports, {
@@ -106,7 +136,7 @@ var init_init = __esm({
106
136
  });
107
137
 
108
138
  // ../../node_modules/dotenv/package.json
109
- var require_package = __commonJS({
139
+ var require_package2 = __commonJS({
110
140
  "../../node_modules/dotenv/package.json"(exports2, module2) {
111
141
  module2.exports = {
112
142
  name: "dotenv",
@@ -180,7 +210,7 @@ var require_main = __commonJS({
180
210
  var path = require("path");
181
211
  var os = require("os");
182
212
  var crypto6 = require("crypto");
183
- var packageJson = require_package();
213
+ var packageJson = require_package2();
184
214
  var version3 = packageJson.version;
185
215
  var LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;
186
216
  function parse4(src) {
@@ -486,12 +516,12 @@ var init_logger = __esm({
486
516
  }
487
517
  debug(message, context) {
488
518
  if (this.level <= 0 /* DEBUG */) {
489
- console.debug(this.formatMessage("DEBUG", message, context));
519
+ console.error(this.formatMessage("DEBUG", message, context));
490
520
  }
491
521
  }
492
522
  info(message, context) {
493
523
  if (this.level <= 1 /* INFO */) {
494
- console.log(this.formatMessage("INFO", message, context));
524
+ console.error(this.formatMessage("INFO", message, context));
495
525
  }
496
526
  }
497
527
  warn(message, context) {
@@ -56869,6 +56899,14 @@ var setup_mcp_exports = {};
56869
56899
  __export(setup_mcp_exports, {
56870
56900
  setupMcp: () => setupMcp
56871
56901
  });
56902
+ function resolveHaansiBin() {
56903
+ try {
56904
+ const binPath = (0, import_node_child_process.execSync)("which haansi", { encoding: "utf-8" }).trim();
56905
+ if (binPath) return binPath;
56906
+ } catch {
56907
+ }
56908
+ return "haansi";
56909
+ }
56872
56910
  async function setupMcp() {
56873
56911
  let config6 = {};
56874
56912
  if ((0, import_node_fs2.existsSync)(CLAUDE_JSON)) {
@@ -56883,23 +56921,26 @@ async function setupMcp() {
56883
56921
  if (!config6.mcpServers) {
56884
56922
  config6.mcpServers = {};
56885
56923
  }
56924
+ const haansiBin = resolveHaansiBin();
56886
56925
  config6.mcpServers["haansi-solutions"] = {
56887
- command: "npx",
56888
- args: ["haansi", "mcp-server"]
56926
+ command: haansiBin,
56927
+ args: ["mcp-server"]
56889
56928
  };
56890
56929
  (0, import_node_fs2.writeFileSync)(CLAUDE_JSON, JSON.stringify(config6, null, 2) + "\n", "utf-8");
56891
56930
  console.log(`MCP server configured in ${CLAUDE_JSON}`);
56931
+ console.log(` command: ${haansiBin} mcp-server`);
56892
56932
  console.log(
56893
- "Restart Claude Code to activate the haansi-solutions MCP server."
56933
+ "\nRestart Claude Code to activate the haansi-solutions MCP server."
56894
56934
  );
56895
56935
  }
56896
- var import_node_fs2, import_node_path2, import_node_os2, CLAUDE_JSON;
56936
+ var import_node_fs2, import_node_path2, import_node_os2, import_node_child_process, CLAUDE_JSON;
56897
56937
  var init_setup_mcp = __esm({
56898
56938
  "src/commands/setup-mcp.ts"() {
56899
56939
  "use strict";
56900
56940
  import_node_fs2 = require("node:fs");
56901
56941
  import_node_path2 = require("node:path");
56902
56942
  import_node_os2 = require("node:os");
56943
+ import_node_child_process = require("node:child_process");
56903
56944
  CLAUDE_JSON = (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".claude.json");
56904
56945
  }
56905
56946
  });
@@ -56911,7 +56952,7 @@ __export(setup_daemon_exports, {
56911
56952
  });
56912
56953
  function findHaansiBin() {
56913
56954
  try {
56914
- return (0, import_node_child_process.execSync)("which haansi", { encoding: "utf-8" }).trim();
56955
+ return (0, import_node_child_process2.execSync)("which haansi", { encoding: "utf-8" }).trim();
56915
56956
  } catch {
56916
56957
  throw new Error(
56917
56958
  "Could not find haansi in PATH. Make sure it is installed globally (npm install -g haansi)."
@@ -56955,7 +56996,7 @@ async function setupDaemon(uninstall) {
56955
56996
  return;
56956
56997
  }
56957
56998
  try {
56958
- (0, import_node_child_process.execSync)(`launchctl unload "${PLIST_PATH}"`, { stdio: "pipe" });
56999
+ (0, import_node_child_process2.execSync)(`launchctl unload "${PLIST_PATH}"`, { stdio: "pipe" });
56959
57000
  } catch {
56960
57001
  }
56961
57002
  (0, import_node_fs3.unlinkSync)(PLIST_PATH);
@@ -56966,10 +57007,10 @@ async function setupDaemon(uninstall) {
56966
57007
  (0, import_node_fs3.mkdirSync)((0, import_node_path3.join)((0, import_node_os3.homedir)(), ".haansi"), { recursive: true });
56967
57008
  (0, import_node_fs3.writeFileSync)(PLIST_PATH, buildPlist(haansiPath), "utf-8");
56968
57009
  try {
56969
- (0, import_node_child_process.execSync)(`launchctl unload "${PLIST_PATH}"`, { stdio: "pipe" });
57010
+ (0, import_node_child_process2.execSync)(`launchctl unload "${PLIST_PATH}"`, { stdio: "pipe" });
56970
57011
  } catch {
56971
57012
  }
56972
- (0, import_node_child_process.execSync)(`launchctl load "${PLIST_PATH}"`, { stdio: "inherit" });
57013
+ (0, import_node_child_process2.execSync)(`launchctl load "${PLIST_PATH}"`, { stdio: "inherit" });
56973
57014
  console.log("Daemon installed and started.");
56974
57015
  console.log(`Plist : ${PLIST_PATH}`);
56975
57016
  console.log(`Logs : ${LOG_FILE}`);
@@ -56977,11 +57018,11 @@ async function setupDaemon(uninstall) {
56977
57018
  The daemon will start automatically on every login.`);
56978
57019
  console.log(`To uninstall: haansi setup-daemon --uninstall`);
56979
57020
  }
56980
- var import_node_child_process, import_node_fs3, import_node_path3, import_node_os3, PLIST_LABEL, PLIST_PATH, LOG_FILE;
57021
+ var import_node_child_process2, import_node_fs3, import_node_path3, import_node_os3, PLIST_LABEL, PLIST_PATH, LOG_FILE;
56981
57022
  var init_setup_daemon = __esm({
56982
57023
  "src/commands/setup-daemon.ts"() {
56983
57024
  "use strict";
56984
- import_node_child_process = require("node:child_process");
57025
+ import_node_child_process2 = require("node:child_process");
56985
57026
  import_node_fs3 = require("node:fs");
56986
57027
  import_node_path3 = require("node:path");
56987
57028
  import_node_os3 = require("node:os");
@@ -57112,7 +57153,9 @@ var init_config = __esm({
57112
57153
  // src/commands/list.ts
57113
57154
  var list_exports = {};
57114
57155
  __export(list_exports, {
57115
- list: () => list
57156
+ list: () => list,
57157
+ saveKnowledge: () => saveKnowledge,
57158
+ searchKnowledge: () => searchKnowledge
57116
57159
  });
57117
57160
  function resolveToken4() {
57118
57161
  if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
@@ -57135,6 +57178,21 @@ async function apiGet3(path, token, apiUrl) {
57135
57178
  }
57136
57179
  return response.json();
57137
57180
  }
57181
+ async function apiPost2(path, body, token, apiUrl) {
57182
+ const response = await fetch(`${apiUrl}/api/v1${path}`, {
57183
+ method: "POST",
57184
+ headers: {
57185
+ Authorization: `Bearer ${token}`,
57186
+ "Content-Type": "application/json"
57187
+ },
57188
+ body: JSON.stringify(body)
57189
+ });
57190
+ if (!response.ok) {
57191
+ const text = await response.text().catch(() => "");
57192
+ throw new Error(`API error ${response.status}: ${text.slice(0, 200)}`);
57193
+ }
57194
+ return response.json();
57195
+ }
57138
57196
  async function list(options) {
57139
57197
  const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL5;
57140
57198
  const token = resolveToken4();
@@ -57161,6 +57219,41 @@ async function list(options) {
57161
57219
  console.log(`
57162
57220
  ${data.results.length} result(s)`);
57163
57221
  }
57222
+ async function searchKnowledge(options) {
57223
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL5;
57224
+ const token = resolveToken4();
57225
+ let url2 = `/sessions/knowledge/search?q=${encodeURIComponent(options.query)}&limit=${options.limit}`;
57226
+ if (options.artifactType) {
57227
+ url2 += `&artifact_type=${encodeURIComponent(options.artifactType)}`;
57228
+ }
57229
+ const data = await apiGet3(url2, token, apiUrl);
57230
+ if (!data.results || data.results.length === 0) {
57231
+ console.log("No knowledge artifacts found matching your query.");
57232
+ return;
57233
+ }
57234
+ const separator = "\n" + "\u2500".repeat(60) + "\n";
57235
+ console.log(data.results.join(separator));
57236
+ console.log(`
57237
+ ${data.results.length} result(s)`);
57238
+ }
57239
+ async function saveKnowledge(options) {
57240
+ const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL5;
57241
+ const token = resolveToken4();
57242
+ const body = {
57243
+ problem_description: options.problem,
57244
+ solution_summary: options.solution,
57245
+ source_tool: "haansi-cli"
57246
+ };
57247
+ if (options.sessionType) body.session_type = options.sessionType;
57248
+ if (options.complexity) body.complexity = options.complexity;
57249
+ if (options.tags) body.tags = options.tags;
57250
+ const data = await apiPost2("/sessions/knowledge", body, token, apiUrl);
57251
+ console.log("Knowledge saved successfully.");
57252
+ console.log(` ID: ${data.id}`);
57253
+ console.log(` Problem: ${data.problem_description}`);
57254
+ console.log(` Solution: ${data.solution_summary}`);
57255
+ console.log("\nThis is now searchable via `haansi search-solutions`.");
57256
+ }
57164
57257
  var import_node_fs5, import_node_path5, import_node_os5, DEFAULT_API_URL5, CREDENTIALS_FILE5;
57165
57258
  var init_list = __esm({
57166
57259
  "src/commands/list.ts"() {
@@ -57174,8 +57267,13 @@ var init_list = __esm({
57174
57267
  });
57175
57268
 
57176
57269
  // src/index.ts
57270
+ var { version: VERSION } = require_package();
57177
57271
  var command = process.argv[2];
57178
57272
  async function run2() {
57273
+ if (command === "--version" || command === "-v") {
57274
+ console.log(VERSION);
57275
+ return;
57276
+ }
57179
57277
  switch (command) {
57180
57278
  case "init": {
57181
57279
  const { init: init2 } = await Promise.resolve().then(() => (init_init(), init_exports));
@@ -57226,11 +57324,13 @@ Collector done: ${uploaded} uploaded, ${skipped} unchanged, ${errors} errors`
57226
57324
  await config6(process.argv.slice(3));
57227
57325
  break;
57228
57326
  }
57327
+ // ── Solution & Knowledge commands (consistent with MCP tool names) ──
57328
+ case "search-solutions":
57229
57329
  case "search": {
57230
57330
  const { list: list2 } = await Promise.resolve().then(() => (init_list(), list_exports));
57231
57331
  const query = process.argv[3];
57232
57332
  if (!query || query.startsWith("--")) {
57233
- console.error("Usage: haansi search <query> [--limit <n>]");
57333
+ console.error("Usage: haansi search-solutions <query> [--limit <n>]");
57234
57334
  process.exit(1);
57235
57335
  }
57236
57336
  const limitIdx = process.argv.indexOf("--limit");
@@ -57238,6 +57338,7 @@ Collector done: ${uploaded} uploaded, ${skipped} unchanged, ${errors} errors`
57238
57338
  await list2({ search: query, limit });
57239
57339
  break;
57240
57340
  }
57341
+ case "list-solutions":
57241
57342
  case "list": {
57242
57343
  const { list: list2 } = await Promise.resolve().then(() => (init_list(), list_exports));
57243
57344
  const searchIdx = process.argv.indexOf("--search");
@@ -57247,6 +57348,43 @@ Collector done: ${uploaded} uploaded, ${skipped} unchanged, ${errors} errors`
57247
57348
  await list2({ search, limit });
57248
57349
  break;
57249
57350
  }
57351
+ case "search-knowledge": {
57352
+ const { searchKnowledge: searchKnowledge2 } = await Promise.resolve().then(() => (init_list(), list_exports));
57353
+ const query = process.argv[3];
57354
+ if (!query || query.startsWith("--")) {
57355
+ console.error(
57356
+ "Usage: haansi search-knowledge <query> [--type <artifact_type>] [--limit <n>]"
57357
+ );
57358
+ process.exit(1);
57359
+ }
57360
+ const typeIdx = process.argv.indexOf("--type");
57361
+ const artifactType = typeIdx !== -1 ? process.argv[typeIdx + 1] : void 0;
57362
+ const limitIdx = process.argv.indexOf("--limit");
57363
+ const limit = limitIdx !== -1 ? parseInt(process.argv[limitIdx + 1], 10) : 10;
57364
+ await searchKnowledge2({ query, artifactType, limit });
57365
+ break;
57366
+ }
57367
+ case "save-knowledge": {
57368
+ const { saveKnowledge: saveKnowledge2 } = await Promise.resolve().then(() => (init_list(), list_exports));
57369
+ const problemIdx = process.argv.indexOf("--problem");
57370
+ const solutionIdx = process.argv.indexOf("--solution");
57371
+ if (problemIdx === -1 || solutionIdx === -1) {
57372
+ console.error(
57373
+ 'Usage: haansi save-knowledge --problem "description" --solution "summary" [--type <session_type>] [--complexity <simple|moderate|complex>] [--tags tag1,tag2]'
57374
+ );
57375
+ process.exit(1);
57376
+ }
57377
+ const problem = process.argv[problemIdx + 1];
57378
+ const solution = process.argv[solutionIdx + 1];
57379
+ const typeIdx = process.argv.indexOf("--type");
57380
+ const sessionType = typeIdx !== -1 ? process.argv[typeIdx + 1] : void 0;
57381
+ const complexityIdx = process.argv.indexOf("--complexity");
57382
+ const complexity = complexityIdx !== -1 ? process.argv[complexityIdx + 1] : void 0;
57383
+ const tagsIdx = process.argv.indexOf("--tags");
57384
+ const tags = tagsIdx !== -1 ? process.argv[tagsIdx + 1].split(",") : void 0;
57385
+ await saveKnowledge2({ problem, solution, sessionType, complexity, tags });
57386
+ break;
57387
+ }
57250
57388
  case "help":
57251
57389
  default: {
57252
57390
  printUsage();
@@ -57257,37 +57395,52 @@ Collector done: ${uploaded} uploaded, ${skipped} unchanged, ${errors} errors`
57257
57395
  }
57258
57396
  function printUsage() {
57259
57397
  console.log(`
57260
- Haansi CLI \u2014 Session collector and MCP server for Claude Code
57398
+ Haansi CLI v${VERSION} \u2014 Session collector and MCP server for Claude Code
57261
57399
 
57262
57400
  Usage:
57263
57401
  haansi <command> [options]
57264
57402
 
57265
57403
  Commands:
57266
- init Authenticate and save your API token
57267
- collect Upload new/changed Claude sessions once
57268
- config get [key] Show your preferences (e.g. session_mode)
57269
- config set <key> <val> Update a preference
57270
- search <query> Semantic search over mined solutions
57271
- list List recent mined solutions
57272
- daemon Run the collector on a schedule (every 30 min)
57273
- mcp-server Start the Haansi MCP server (used by Claude Code)
57274
- setup-mcp Add haansi-solutions MCP entry to ~/.claude.json
57275
- setup-daemon Install launchd service to auto-start daemon on login (macOS)
57276
- help Show this help message
57277
-
57278
- Options for search/list:
57279
- --limit <n> Number of results (default: 5 for search, 10 for list)
57404
+ init Authenticate and save your API token
57405
+ collect Upload new/changed Claude sessions once
57406
+ config get [key] Show your preferences (e.g. session_mode)
57407
+ config set <key> <val> Update a preference
57408
+
57409
+ search-solutions <query> Semantic search over mined solutions
57410
+ list-solutions List recent mined solutions
57411
+ search-knowledge <query> Search knowledge artifacts (cards, decisions, etc.)
57412
+ save-knowledge Save a problem/solution for future retrieval
57413
+
57414
+ daemon Run the collector on a schedule (every 30 min)
57415
+ mcp-server Start the Haansi MCP server (used by Claude Code)
57416
+ setup-mcp Add haansi-solutions MCP entry to ~/.claude.json
57417
+ setup-daemon Install launchd service to auto-start daemon on login (macOS)
57418
+ help Show this help message
57419
+
57420
+ Options:
57421
+ --version, -v Show installed version
57422
+ --limit <n> Number of results (default: 5 for search, 10 for list)
57423
+
57424
+ Options for search-knowledge:
57425
+ --type <artifact_type> Filter by type (knowledge_card, decision_record, etc.)
57426
+
57427
+ Options for save-knowledge:
57428
+ --problem "description" Problem description (required)
57429
+ --solution "summary" Solution summary (required)
57430
+ --type <session_type> Session type (bug_fix, feature, refactor, general)
57431
+ --complexity <level> simple, moderate, or complex
57432
+ --tags tag1,tag2 Comma-separated tags
57280
57433
 
57281
57434
  Options for collect:
57282
- --force-all Re-upload all sessions, not just new ones
57283
- --dry-run Scan without uploading
57284
- --limit <n> Upload at most n sessions
57435
+ --force-all Re-upload all sessions, not just new ones
57436
+ --dry-run Scan without uploading
57437
+ --limit <n> Upload at most n sessions
57285
57438
 
57286
57439
  Environment variables:
57287
- HAANSI_TOKEN API token (or use \`haansi init\` to save it)
57288
- HAANSI_API_URL API base URL (default: https://api.haansi.co)
57289
- CLAUDE_PROJECT_PATH Collect from a specific project path only
57290
- CLAUDE_COLLECT_SCHEDULE Cron expression for daemon (default: */30 * * * *)
57440
+ HAANSI_TOKEN API token (or use \`haansi init\` to save it)
57441
+ HAANSI_API_URL API base URL (default: https://api.haansi.co)
57442
+ CLAUDE_PROJECT_PATH Collect from a specific project path only
57443
+ CLAUDE_COLLECT_SCHEDULE Cron expression for daemon (default: */30 * * * *)
57291
57444
  `);
57292
57445
  }
57293
57446
  run2().catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "haansi",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Haansi CLI - Session collector and MCP server for Claude Code",
5
5
  "bin": {
6
6
  "haansi": "./dist/haansi.js"