haansi 0.1.9 → 0.1.11

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 +210 -80
  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.9",
42
+ version: "0.1.11",
43
43
  description: "Haansi CLI - Session collector and MCP server for Claude Code",
44
44
  bin: {
45
45
  haansi: "./dist/haansi.js"
@@ -557,6 +557,56 @@ function edgeScrub(content) {
557
557
  }
558
558
  return result;
559
559
  }
560
+ function extractGitInfo(projectDir) {
561
+ const empty = {
562
+ gitRemoteUrl: null,
563
+ gitCommit: null,
564
+ gitBranch: null
565
+ };
566
+ const opts = { cwd: projectDir, timeout: 5e3, encoding: "utf-8" };
567
+ try {
568
+ (0, import_child_process.execSync)("git rev-parse --is-inside-work-tree", { ...opts, stdio: "pipe" });
569
+ } catch {
570
+ return empty;
571
+ }
572
+ const result = { ...empty };
573
+ try {
574
+ result.gitRemoteUrl = (0, import_child_process.execSync)("git remote get-url origin", {
575
+ ...opts,
576
+ stdio: "pipe"
577
+ }).trim() || null;
578
+ } catch {
579
+ }
580
+ try {
581
+ result.gitCommit = (0, import_child_process.execSync)("git rev-parse HEAD", { ...opts, stdio: "pipe" }).trim() || null;
582
+ } catch {
583
+ }
584
+ try {
585
+ result.gitBranch = (0, import_child_process.execSync)("git rev-parse --abbrev-ref HEAD", {
586
+ ...opts,
587
+ stdio: "pipe"
588
+ }).trim() || null;
589
+ } catch {
590
+ }
591
+ return result;
592
+ }
593
+ function extractCwdFromJsonl(filePath) {
594
+ try {
595
+ const content = (0, import_fs.readFileSync)(filePath, "utf-8");
596
+ const lines = content.split("\n").filter((l) => l.trim()).slice(0, 10);
597
+ for (const line of lines) {
598
+ try {
599
+ const entry = JSON.parse(line);
600
+ if (entry.cwd && typeof entry.cwd === "string") {
601
+ return entry.cwd;
602
+ }
603
+ } catch {
604
+ }
605
+ }
606
+ } catch {
607
+ }
608
+ return null;
609
+ }
560
610
  function resolveToken() {
561
611
  if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
562
612
  if ((0, import_fs.existsSync)(CREDENTIALS_FILE2)) {
@@ -627,16 +677,20 @@ async function getUploadedSessions(apiUrl, token) {
627
677
  return /* @__PURE__ */ new Map();
628
678
  }
629
679
  }
630
- async function uploadSession(sessionId, content, apiUrl, token) {
680
+ async function uploadSession(sessionId, content, apiUrl, token, gitInfo) {
631
681
  const body = (0, import_zlib.gzipSync)(Buffer.from(content, "utf-8"));
682
+ const headers = {
683
+ Authorization: `Bearer ${token}`,
684
+ "Content-Type": "application/x-ndjson",
685
+ "Content-Encoding": "gzip",
686
+ "x-session-id": sessionId
687
+ };
688
+ if (gitInfo?.gitRemoteUrl) headers["x-git-remote-url"] = gitInfo.gitRemoteUrl;
689
+ if (gitInfo?.gitCommit) headers["x-git-commit"] = gitInfo.gitCommit;
690
+ if (gitInfo?.gitBranch) headers["x-git-branch"] = gitInfo.gitBranch;
632
691
  const response = await fetch(`${apiUrl}/api/v1/sessions/raw`, {
633
692
  method: "POST",
634
- headers: {
635
- Authorization: `Bearer ${token}`,
636
- "Content-Type": "application/x-ndjson",
637
- "Content-Encoding": "gzip",
638
- "x-session-id": sessionId
639
- },
693
+ headers,
640
694
  body: new Uint8Array(body)
641
695
  });
642
696
  if (!response.ok) {
@@ -682,16 +736,25 @@ async function collect(options) {
682
736
  try {
683
737
  const rawContent = (0, import_fs.readFileSync)(session.filePath, "utf-8");
684
738
  const scrubbed = edgeScrub(rawContent);
739
+ const cwd = extractCwdFromJsonl(session.filePath);
740
+ const gitInfo = cwd ? extractGitInfo(cwd) : { gitRemoteUrl: null, gitCommit: null, gitBranch: null };
685
741
  if (options.dryRun) {
686
742
  logger.info(
687
- `[dry-run] Would upload ${shortId} (${session.lineCount} lines)`
743
+ `[dry-run] Would upload ${shortId} (${session.lineCount} lines)`,
744
+ {
745
+ gitRemoteUrl: gitInfo.gitRemoteUrl,
746
+ gitCommit: gitInfo.gitCommit?.slice(0, 8)
747
+ }
688
748
  );
689
749
  uploaded++;
690
750
  continue;
691
751
  }
692
- await uploadSession(session.sessionId, scrubbed, apiUrl, token);
752
+ await uploadSession(session.sessionId, scrubbed, apiUrl, token, gitInfo);
693
753
  uploaded++;
694
- logger.info(`Uploaded ${shortId} (${session.lineCount} lines)`);
754
+ logger.info(`Uploaded ${shortId} (${session.lineCount} lines)`, {
755
+ gitRemoteUrl: gitInfo.gitRemoteUrl,
756
+ gitCommit: gitInfo.gitCommit?.slice(0, 8)
757
+ });
695
758
  } catch (err) {
696
759
  errors++;
697
760
  logger.error(`Failed to upload ${shortId}`, { err: err.message });
@@ -716,7 +779,7 @@ Collector done: ${uploaded} uploaded, ${skipped} unchanged, ${errors} errors`
716
779
  );
717
780
  if (errors > 0) process.exit(1);
718
781
  }
719
- var import_dotenv, import_path, import_fs, import_os, import_zlib, logger, DEFAULT_API_URL2, CREDENTIALS_FILE2, CLAUDE_PROJECTS_DIR, MIN_LINES, EDGE_SCRUB_PATTERNS;
782
+ 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;
720
783
  var init_collector = __esm({
721
784
  "../service-capture/claude-sessions/collector.ts"() {
722
785
  "use strict";
@@ -725,6 +788,7 @@ var init_collector = __esm({
725
788
  import_fs = require("fs");
726
789
  import_os = require("os");
727
790
  import_zlib = require("zlib");
791
+ import_child_process = require("child_process");
728
792
  init_logger();
729
793
  (0, import_dotenv.config)({ path: (0, import_path.resolve)(__dirname, "../../../.env") });
730
794
  logger = createLogger("claude-collector");
@@ -1742,6 +1806,128 @@ var init_collector_daemon = __esm({
1742
1806
  }
1743
1807
  });
1744
1808
 
1809
+ // ../../packages/@company/types/src/knowledge.ts
1810
+ var init_knowledge = __esm({
1811
+ "../../packages/@company/types/src/knowledge.ts"() {
1812
+ "use strict";
1813
+ }
1814
+ });
1815
+
1816
+ // ../../packages/@company/types/src/user.ts
1817
+ var init_user = __esm({
1818
+ "../../packages/@company/types/src/user.ts"() {
1819
+ "use strict";
1820
+ }
1821
+ });
1822
+
1823
+ // ../../packages/@company/types/src/integration.ts
1824
+ var init_integration = __esm({
1825
+ "../../packages/@company/types/src/integration.ts"() {
1826
+ "use strict";
1827
+ }
1828
+ });
1829
+
1830
+ // ../../packages/@company/types/src/timeline.ts
1831
+ var init_timeline = __esm({
1832
+ "../../packages/@company/types/src/timeline.ts"() {
1833
+ "use strict";
1834
+ }
1835
+ });
1836
+
1837
+ // ../../packages/@company/types/src/artifact.ts
1838
+ var init_artifact = __esm({
1839
+ "../../packages/@company/types/src/artifact.ts"() {
1840
+ "use strict";
1841
+ }
1842
+ });
1843
+
1844
+ // ../../packages/@company/types/src/taxonomy.ts
1845
+ function normalizeSessionType(raw) {
1846
+ if (!raw) return "general";
1847
+ const lower = raw.toLowerCase().trim();
1848
+ if (lower in SESSION_TYPES) return lower;
1849
+ if (lower in SESSION_TYPE_ALIASES) return SESSION_TYPE_ALIASES[lower];
1850
+ return "general";
1851
+ }
1852
+ function normalizeComplexity(raw) {
1853
+ if (!raw) return "moderate";
1854
+ const lower = raw.toLowerCase().trim();
1855
+ if (lower in COMPLEXITIES) return lower;
1856
+ return "moderate";
1857
+ }
1858
+ var SESSION_TYPES, SESSION_TYPE_ALIASES, COMPLEXITIES;
1859
+ var init_taxonomy = __esm({
1860
+ "../../packages/@company/types/src/taxonomy.ts"() {
1861
+ "use strict";
1862
+ SESSION_TYPES = {
1863
+ debugging: {
1864
+ label: "Debugging",
1865
+ description: "Investigating and fixing bugs or errors"
1866
+ },
1867
+ feature_implementation: {
1868
+ label: "Feature Implementation",
1869
+ description: "Building new functionality or features"
1870
+ },
1871
+ code_review: {
1872
+ label: "Code Review",
1873
+ description: "Reviewing code for quality, correctness, or style"
1874
+ },
1875
+ deployment: {
1876
+ label: "Deployment",
1877
+ description: "Deploying, releasing, or provisioning infrastructure"
1878
+ },
1879
+ refactoring: {
1880
+ label: "Refactoring",
1881
+ description: "Restructuring existing code without changing behavior"
1882
+ },
1883
+ configuration: {
1884
+ label: "Configuration",
1885
+ description: "Setting up or modifying project/tool configuration"
1886
+ },
1887
+ question_answer: {
1888
+ label: "Question & Answer",
1889
+ description: "Asking or answering questions about code or systems"
1890
+ },
1891
+ general: {
1892
+ label: "General",
1893
+ description: "General-purpose session not fitting other categories"
1894
+ }
1895
+ };
1896
+ SESSION_TYPE_ALIASES = {
1897
+ bug_fix: "debugging",
1898
+ feature: "feature_implementation",
1899
+ refactor: "refactoring",
1900
+ config: "configuration"
1901
+ };
1902
+ COMPLEXITIES = {
1903
+ simple: {
1904
+ label: "Simple",
1905
+ description: "Straightforward task with few steps"
1906
+ },
1907
+ moderate: {
1908
+ label: "Moderate",
1909
+ description: "Multi-step task requiring some investigation"
1910
+ },
1911
+ complex: {
1912
+ label: "Complex",
1913
+ description: "Involved task with many steps or deep analysis"
1914
+ }
1915
+ };
1916
+ }
1917
+ });
1918
+
1919
+ // ../../packages/@company/types/src/index.ts
1920
+ var init_src = __esm({
1921
+ "../../packages/@company/types/src/index.ts"() {
1922
+ init_knowledge();
1923
+ init_user();
1924
+ init_integration();
1925
+ init_timeline();
1926
+ init_artifact();
1927
+ init_taxonomy();
1928
+ }
1929
+ });
1930
+
1745
1931
  // ../../node_modules/zod/v3/helpers/util.js
1746
1932
  var util, objectUtil, ZodParsedType, getParsedType;
1747
1933
  var init_util = __esm({
@@ -56556,6 +56742,7 @@ var init_mcp_server2 = __esm({
56556
56742
  import_fs2 = require("fs");
56557
56743
  import_os2 = require("os");
56558
56744
  init_collector();
56745
+ init_src();
56559
56746
  init_mcp();
56560
56747
  init_stdio2();
56561
56748
  init_types2();
@@ -56595,7 +56782,7 @@ var init_mcp_server2 = __esm({
56595
56782
  tools: [
56596
56783
  {
56597
56784
  name: "search_solutions",
56598
- description: "Semantic search over past Claude Code solutions stored in the knowledge base. Returns the most relevant distilled paths ranked by cosine similarity.",
56785
+ 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.",
56599
56786
  inputSchema: {
56600
56787
  type: "object",
56601
56788
  properties: {
@@ -56628,31 +56815,8 @@ var init_mcp_server2 = __esm({
56628
56815
  }
56629
56816
  },
56630
56817
  {
56631
- name: "search_knowledge",
56632
- 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.",
56633
- inputSchema: {
56634
- type: "object",
56635
- properties: {
56636
- query: {
56637
- type: "string",
56638
- description: "Keyword or phrase to search for"
56639
- },
56640
- artifact_type: {
56641
- type: "string",
56642
- description: "Filter by type: knowledge_card, decision_record, context_summary, incident_summary, etc."
56643
- },
56644
- limit: {
56645
- type: "number",
56646
- description: "Number of results to return (default: 10, max: 50)"
56647
- },
56648
- _context: contextSchema
56649
- },
56650
- required: ["query"]
56651
- }
56652
- },
56653
- {
56654
- name: "save_knowledge",
56655
- 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).",
56818
+ name: "save_solution",
56819
+ 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.",
56656
56820
  inputSchema: {
56657
56821
  type: "object",
56658
56822
  properties: {
@@ -56666,7 +56830,9 @@ var init_mcp_server2 = __esm({
56666
56830
  },
56667
56831
  session_type: {
56668
56832
  type: "string",
56669
- description: 'Type of session: "bug_fix", "feature", "refactor", "config", "general" (default: "general")'
56833
+ description: `Type of session: ${Object.keys(SESSION_TYPES).map((t) => `"${t}"`).join(", ")} (default: "general"). Aliases: ${Object.entries(
56834
+ SESSION_TYPE_ALIASES
56835
+ ).map(([k, v]) => `"${k}" -> "${v}"`).join(", ")}`
56670
56836
  },
56671
56837
  steps: {
56672
56838
  type: "array",
@@ -56697,7 +56863,7 @@ var init_mcp_server2 = __esm({
56697
56863
  },
56698
56864
  complexity: {
56699
56865
  type: "string",
56700
- description: '"simple", "moderate", or "complex" (default: "moderate")'
56866
+ description: `${Object.keys(COMPLEXITIES).map((c) => `"${c}"`).join(", ")} (default: "moderate")`
56701
56867
  },
56702
56868
  project_path: {
56703
56869
  type: "string",
@@ -56796,41 +56962,7 @@ var init_mcp_server2 = __esm({
56796
56962
  content: [{ type: "text", text: data.results.join("\n\n") }]
56797
56963
  };
56798
56964
  }
56799
- case "search_knowledge": {
56800
- const {
56801
- query,
56802
- artifact_type,
56803
- limit = 10,
56804
- _context
56805
- } = args;
56806
- const n = Math.min(Math.max(1, limit), 50);
56807
- const extraHeaders = {};
56808
- if (_context?.userId) {
56809
- extraHeaders["X-Session-User-Id"] = String(_context.userId);
56810
- }
56811
- if (_context?.orgId) {
56812
- extraHeaders["X-Session-Org-Id"] = String(_context.orgId);
56813
- }
56814
- let url2 = `/sessions/knowledge/search?q=${encodeURIComponent(query)}&limit=${n}`;
56815
- if (artifact_type) {
56816
- url2 += `&artifact_type=${encodeURIComponent(artifact_type)}`;
56817
- }
56818
- const data = await apiGet2(url2, { extraHeaders });
56819
- if (!data.results || data.results.length === 0) {
56820
- return {
56821
- content: [
56822
- {
56823
- type: "text",
56824
- text: "No knowledge artifacts found matching your query."
56825
- }
56826
- ]
56827
- };
56828
- }
56829
- return {
56830
- content: [{ type: "text", text: data.results.join("\n\n---\n\n") }]
56831
- };
56832
- }
56833
- case "save_knowledge": {
56965
+ case "save_solution": {
56834
56966
  const {
56835
56967
  problem_description,
56836
56968
  solution_summary,
@@ -56859,11 +56991,11 @@ var init_mcp_server2 = __esm({
56859
56991
  {
56860
56992
  problem_description,
56861
56993
  solution_summary,
56862
- session_type,
56994
+ session_type: normalizeSessionType(session_type),
56863
56995
  steps,
56864
56996
  tools_used,
56865
56997
  files_modified,
56866
- complexity,
56998
+ complexity: normalizeComplexity(complexity),
56867
56999
  project_path,
56868
57000
  git_branch,
56869
57001
  git_remote_url,
@@ -56879,7 +57011,6 @@ var init_mcp_server2 = __esm({
56879
57011
  type: "text",
56880
57012
  text: [
56881
57013
  "Solution saved successfully.",
56882
- `ID: ${data.id}`,
56883
57014
  `Problem: ${data.problem_description}`,
56884
57015
  `Solution: ${data.solution_summary}`,
56885
57016
  "This is now searchable via search_solutions."
@@ -57261,7 +57392,6 @@ async function saveKnowledge(options) {
57261
57392
  if (options.tags) body.tags = options.tags;
57262
57393
  const data = await apiPost2("/sessions/knowledge", body, token, apiUrl);
57263
57394
  console.log("Knowledge saved successfully.");
57264
- console.log(` ID: ${data.id}`);
57265
57395
  console.log(` Problem: ${data.problem_description}`);
57266
57396
  console.log(` Solution: ${data.solution_summary}`);
57267
57397
  console.log("\nThis is now searchable via `haansi search-solutions`.");
@@ -57439,7 +57569,7 @@ Options for search-knowledge:
57439
57569
  Options for save-knowledge:
57440
57570
  --problem "description" Problem description (required)
57441
57571
  --solution "summary" Solution summary (required)
57442
- --type <session_type> Session type (bug_fix, feature, refactor, general)
57572
+ --type <session_type> Session type (debugging, feature_implementation, code_review, deployment, refactoring, configuration, question_answer, general)
57443
57573
  --complexity <level> simple, moderate, or complex
57444
57574
  --tags tag1,tag2 Comma-separated tags
57445
57575
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "haansi",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Haansi CLI - Session collector and MCP server for Claude Code",
5
5
  "bin": {
6
6
  "haansi": "./dist/haansi.js"