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.
- package/dist/haansi.js +210 -80
- 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.
|
|
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
|
|
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: "
|
|
56632
|
-
description: "
|
|
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:
|
|
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:
|
|
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 "
|
|
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 (
|
|
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
|
|