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.
- package/dist/haansi.js +54 -73
- 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.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
|
-
|
|
581
|
-
|
|
582
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
|
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
|
|
740
|
-
const gitInfo = cwd ? extractGitInfo(
|
|
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
|
|
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: "
|
|
56819
|
-
description: "
|
|
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 "
|
|
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,
|