haansi 0.1.10 → 0.1.12

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 +54 -73
  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.10",
42
+ version: "0.1.12",
43
43
  description: "Haansi CLI - Session collector and MCP server for Claude Code",
44
44
  bin: {
45
45
  haansi: "./dist/haansi.js"
@@ -557,7 +557,7 @@ function edgeScrub(content) {
557
557
  }
558
558
  return result;
559
559
  }
560
- function extractGitInfo(projectDir) {
560
+ function extractGitInfo(projectDir, sessionTimestamp, sessionBranch) {
561
561
  const empty = {
562
562
  gitRemoteUrl: null,
563
563
  gitCommit: null,
@@ -567,6 +567,7 @@ function extractGitInfo(projectDir) {
567
567
  try {
568
568
  (0, import_child_process.execSync)("git rev-parse --is-inside-work-tree", { ...opts, stdio: "pipe" });
569
569
  } catch {
570
+ console.warn(`[collector] Not a git repo: ${projectDir}`);
570
571
  return empty;
571
572
  }
572
573
  const result = { ...empty };
@@ -576,10 +577,24 @@ function extractGitInfo(projectDir) {
576
577
  stdio: "pipe"
577
578
  }).trim() || null;
578
579
  } catch {
580
+ console.warn(`[collector] No git remote for: ${projectDir}`);
579
581
  }
580
- try {
581
- result.gitCommit = (0, import_child_process.execSync)("git rev-parse HEAD", { ...opts, stdio: "pipe" }).trim() || null;
582
- } catch {
582
+ if (sessionTimestamp) {
583
+ const branch = sessionBranch ?? "HEAD";
584
+ try {
585
+ result.gitCommit = (0, import_child_process.execSync)(
586
+ `git log --before="${sessionTimestamp}" -1 --format=%H ${branch}`,
587
+ { ...opts, stdio: "pipe" }
588
+ ).trim() || null;
589
+ } catch {
590
+ }
591
+ }
592
+ if (!result.gitCommit) {
593
+ try {
594
+ result.gitCommit = (0, import_child_process.execSync)("git rev-parse HEAD", { ...opts, stdio: "pipe" }).trim() || null;
595
+ } catch {
596
+ console.warn(`[collector] No git commits in: ${projectDir}`);
597
+ }
583
598
  }
584
599
  try {
585
600
  result.gitBranch = (0, import_child_process.execSync)("git rev-parse --abbrev-ref HEAD", {
@@ -588,24 +603,39 @@ function extractGitInfo(projectDir) {
588
603
  }).trim() || null;
589
604
  } catch {
590
605
  }
606
+ if (!result.gitBranch && sessionBranch) {
607
+ result.gitBranch = sessionBranch;
608
+ }
591
609
  return result;
592
610
  }
593
- function extractCwdFromJsonl(filePath) {
611
+ function extractSessionMeta(filePath) {
612
+ const result = {
613
+ cwd: null,
614
+ timestamp: null,
615
+ gitBranch: null
616
+ };
594
617
  try {
595
618
  const content = (0, import_fs.readFileSync)(filePath, "utf-8");
596
- const lines = content.split("\n").filter((l) => l.trim()).slice(0, 10);
619
+ const lines = content.split("\n").filter((l) => l.trim()).slice(0, 50);
597
620
  for (const line of lines) {
598
621
  try {
599
622
  const entry = JSON.parse(line);
600
- if (entry.cwd && typeof entry.cwd === "string") {
601
- return entry.cwd;
623
+ if (!result.cwd && entry.cwd && typeof entry.cwd === "string") {
624
+ result.cwd = entry.cwd;
625
+ }
626
+ if (!result.timestamp && entry.timestamp && typeof entry.timestamp === "string") {
627
+ result.timestamp = entry.timestamp;
602
628
  }
629
+ if (!result.gitBranch && entry.gitBranch && typeof entry.gitBranch === "string") {
630
+ result.gitBranch = entry.gitBranch;
631
+ }
632
+ if (result.cwd && result.timestamp && result.gitBranch) break;
603
633
  } catch {
604
634
  }
605
635
  }
606
636
  } catch {
607
637
  }
608
- return null;
638
+ return result;
609
639
  }
610
640
  function resolveToken() {
611
641
  if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
@@ -736,8 +766,16 @@ async function collect(options) {
736
766
  try {
737
767
  const rawContent = (0, import_fs.readFileSync)(session.filePath, "utf-8");
738
768
  const scrubbed = edgeScrub(rawContent);
739
- const cwd = extractCwdFromJsonl(session.filePath);
740
- const gitInfo = cwd ? extractGitInfo(cwd) : { gitRemoteUrl: null, gitCommit: null, gitBranch: null };
769
+ const sessionMeta = extractSessionMeta(session.filePath);
770
+ const gitInfo = sessionMeta.cwd ? extractGitInfo(
771
+ sessionMeta.cwd,
772
+ sessionMeta.timestamp ?? void 0,
773
+ sessionMeta.gitBranch ?? void 0
774
+ ) : {
775
+ gitRemoteUrl: null,
776
+ gitCommit: null,
777
+ gitBranch: sessionMeta.gitBranch
778
+ };
741
779
  if (options.dryRun) {
742
780
  logger.info(
743
781
  `[dry-run] Would upload ${shortId} (${session.lineCount} lines)`,
@@ -56782,7 +56820,7 @@ var init_mcp_server2 = __esm({
56782
56820
  tools: [
56783
56821
  {
56784
56822
  name: "search_solutions",
56785
- description: "Semantic search over past Claude Code solutions stored in the knowledge base. Returns the most relevant distilled paths ranked by cosine similarity.",
56823
+ 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.",
56786
56824
  inputSchema: {
56787
56825
  type: "object",
56788
56826
  properties: {
@@ -56815,31 +56853,8 @@ var init_mcp_server2 = __esm({
56815
56853
  }
56816
56854
  },
56817
56855
  {
56818
- name: "search_knowledge",
56819
- description: "Search knowledge artifacts (knowledge cards, decision records, context summaries, incident summaries, etc.) for non-technical context and organizational knowledge. Use this when you need broader context beyond coding solutions \u2014 e.g., project decisions, incident details, team context.",
56820
- inputSchema: {
56821
- type: "object",
56822
- properties: {
56823
- query: {
56824
- type: "string",
56825
- description: "Keyword or phrase to search for"
56826
- },
56827
- artifact_type: {
56828
- type: "string",
56829
- description: "Filter by type: knowledge_card, decision_record, context_summary, incident_summary, etc."
56830
- },
56831
- limit: {
56832
- type: "number",
56833
- description: "Number of results to return (default: 10, max: 50)"
56834
- },
56835
- _context: contextSchema
56836
- },
56837
- required: ["query"]
56838
- }
56839
- },
56840
- {
56841
- name: "save_knowledge",
56842
- description: "Save a resolved problem and its solution so it can be found later by search_solutions. Use this when you've solved a non-trivial problem, discovered a useful pattern, or want to record a solution that may help in future sessions. Respects the user's privacy setting (org-wide or private).",
56856
+ name: "save_solution",
56857
+ description: "Save a resolved coding problem and its solution as a searchable artifact. The solution will be discoverable via search_solutions (semantic) and search_artifacts (keyword/hybrid). Use this when you've solved a non-trivial problem, discovered a useful pattern, or want to record a solution that may help in future sessions.",
56843
56858
  inputSchema: {
56844
56859
  type: "object",
56845
56860
  properties: {
@@ -56985,41 +57000,7 @@ var init_mcp_server2 = __esm({
56985
57000
  content: [{ type: "text", text: data.results.join("\n\n") }]
56986
57001
  };
56987
57002
  }
56988
- case "search_knowledge": {
56989
- const {
56990
- query,
56991
- artifact_type,
56992
- limit = 10,
56993
- _context
56994
- } = args;
56995
- const n = Math.min(Math.max(1, limit), 50);
56996
- const extraHeaders = {};
56997
- if (_context?.userId) {
56998
- extraHeaders["X-Session-User-Id"] = String(_context.userId);
56999
- }
57000
- if (_context?.orgId) {
57001
- extraHeaders["X-Session-Org-Id"] = String(_context.orgId);
57002
- }
57003
- let url2 = `/sessions/knowledge/search?q=${encodeURIComponent(query)}&limit=${n}`;
57004
- if (artifact_type) {
57005
- url2 += `&artifact_type=${encodeURIComponent(artifact_type)}`;
57006
- }
57007
- const data = await apiGet2(url2, { extraHeaders });
57008
- if (!data.results || data.results.length === 0) {
57009
- return {
57010
- content: [
57011
- {
57012
- type: "text",
57013
- text: "No knowledge artifacts found matching your query."
57014
- }
57015
- ]
57016
- };
57017
- }
57018
- return {
57019
- content: [{ type: "text", text: data.results.join("\n\n---\n\n") }]
57020
- };
57021
- }
57022
- case "save_knowledge": {
57003
+ case "save_solution": {
57023
57004
  const {
57024
57005
  problem_description,
57025
57006
  solution_summary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "haansi",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "Haansi CLI - Session collector and MCP server for Claude Code",
5
5
  "bin": {
6
6
  "haansi": "./dist/haansi.js"