haansi 0.1.13 → 0.1.15
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/README.md +83 -0
- package/dist/haansi.js +252 -133
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Haansi CLI
|
|
2
|
+
|
|
3
|
+
Session collector and MCP server for Claude Code. Uploads Claude Code session data to the Haansi platform for mining, search, and knowledge retrieval.
|
|
4
|
+
|
|
5
|
+
Published to npm as `haansi`.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g haansi
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires Node.js >= 18.0.0.
|
|
14
|
+
|
|
15
|
+
## Authentication
|
|
16
|
+
|
|
17
|
+
The CLI uses `hsk_` tokens for authentication. Generate a token at https://app.haansi.co/settings/tokens.
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
haansi init
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This prompts for your token, validates it against the API, and saves it to `~/.haansi/credentials.json` (mode 0600). Alternatively, set the `HAANSI_TOKEN` environment variable.
|
|
24
|
+
|
|
25
|
+
## Commands
|
|
26
|
+
|
|
27
|
+
| Command | Description |
|
|
28
|
+
| --------------------------------- | -------------------------------------------------------- |
|
|
29
|
+
| `haansi init` | Authenticate and save your API token |
|
|
30
|
+
| `haansi collect` | Upload new/changed Claude sessions (once) |
|
|
31
|
+
| `haansi daemon` | Run the collector on a schedule (every 30 min) |
|
|
32
|
+
| `haansi setup-daemon` | Install macOS launchd service for auto-start on login |
|
|
33
|
+
| `haansi setup-mcp` | Add haansi-solutions MCP entry to `~/.claude.json` |
|
|
34
|
+
| `haansi mcp-server` | Start the Haansi MCP server (stdio, used by Claude Code) |
|
|
35
|
+
| `haansi config get [key]` | Show preferences (e.g. `session_mode`) |
|
|
36
|
+
| `haansi config set <key> <val>` | Update a preference |
|
|
37
|
+
| `haansi search-solutions <query>` | Semantic search over mined solutions |
|
|
38
|
+
| `haansi list-solutions` | List recent mined solutions |
|
|
39
|
+
| `haansi search-knowledge <query>` | Search knowledge artifacts |
|
|
40
|
+
| `haansi save-knowledge` | Save a problem/solution for future retrieval |
|
|
41
|
+
|
|
42
|
+
### Collect options
|
|
43
|
+
|
|
44
|
+
- `--force-all` -- Re-upload all sessions, not just new ones
|
|
45
|
+
- `--dry-run` -- Scan without uploading
|
|
46
|
+
- `--limit <n>` -- Upload at most n sessions
|
|
47
|
+
|
|
48
|
+
## Configuration
|
|
49
|
+
|
|
50
|
+
| Environment Variable | Description | Default |
|
|
51
|
+
| ------------------------- | ----------------------------------------- | ----------------------- |
|
|
52
|
+
| `HAANSI_TOKEN` | API token (alternative to `haansi init`) | -- |
|
|
53
|
+
| `HAANSI_API_URL` | API base URL | `https://api.haansi.co` |
|
|
54
|
+
| `CLAUDE_PROJECT_PATH` | Collect from a specific project path only | -- |
|
|
55
|
+
| `CLAUDE_COLLECT_SCHEDULE` | Cron expression for daemon | `*/30 * * * *` |
|
|
56
|
+
|
|
57
|
+
### User preferences
|
|
58
|
+
|
|
59
|
+
Set via `haansi config set <key> <value>`:
|
|
60
|
+
|
|
61
|
+
- `session_mode` -- `private` or `org` (default: `org`)
|
|
62
|
+
|
|
63
|
+
## Daemon (macOS)
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
haansi setup-daemon # install and start
|
|
67
|
+
haansi setup-daemon --uninstall # stop and remove
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Installs a launchd plist at `~/Library/LaunchAgents/co.haansi.daemon.plist`. Logs are written to `~/.haansi/daemon.log`.
|
|
71
|
+
|
|
72
|
+
## MCP Server
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
haansi setup-mcp
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Registers `haansi-solutions` in `~/.claude.json` so Claude Code auto-starts the MCP server, providing search and knowledge tools directly in the AI assistant.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
**Package:** `haansi`
|
|
83
|
+
**Location:** `apps/cli/`
|
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.15",
|
|
43
43
|
description: "Haansi CLI - Session collector and MCP server for Claude Code",
|
|
44
44
|
bin: {
|
|
45
45
|
haansi: "./dist/haansi.js"
|
|
@@ -64,6 +64,130 @@ var require_package = __commonJS({
|
|
|
64
64
|
}
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
+
// src/commands/auth-login.ts
|
|
68
|
+
var auth_login_exports = {};
|
|
69
|
+
__export(auth_login_exports, {
|
|
70
|
+
authLogin: () => authLogin
|
|
71
|
+
});
|
|
72
|
+
function openBrowser(url2) {
|
|
73
|
+
const cmd = (0, import_node_os.platform)() === "darwin" ? "open" : (0, import_node_os.platform)() === "win32" ? "start" : "xdg-open";
|
|
74
|
+
try {
|
|
75
|
+
(0, import_node_child_process.exec)(`${cmd} ${JSON.stringify(url2)}`);
|
|
76
|
+
return true;
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function sleep(ms) {
|
|
82
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
83
|
+
}
|
|
84
|
+
async function authLogin() {
|
|
85
|
+
const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL;
|
|
86
|
+
const appUrl = process.env.HAANSI_APP_URL ?? DEFAULT_APP_URL;
|
|
87
|
+
console.log("Haansi CLI \u2014 Browser Login\n");
|
|
88
|
+
const initiateRes = await fetch(`${apiUrl}/api/v1/auth/extension/initiate`, {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: { "Content-Type": "application/json" }
|
|
91
|
+
});
|
|
92
|
+
if (!initiateRes.ok) {
|
|
93
|
+
const body = await initiateRes.text().catch(() => "");
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Failed to start auth session (${initiateRes.status}): ${body.slice(0, 200)}`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
const { session_id } = await initiateRes.json();
|
|
99
|
+
const authUrl = `${appUrl}/auth/cli?session_id=${session_id}`;
|
|
100
|
+
const opened = openBrowser(authUrl);
|
|
101
|
+
if (opened) {
|
|
102
|
+
console.log("A browser window has been opened for you to log in.");
|
|
103
|
+
} else {
|
|
104
|
+
console.log("Could not open browser automatically.");
|
|
105
|
+
}
|
|
106
|
+
console.log(`
|
|
107
|
+
If the browser didn't open, visit this URL:
|
|
108
|
+
${authUrl}
|
|
109
|
+
`);
|
|
110
|
+
console.log("Waiting for approval...");
|
|
111
|
+
const deadline = Date.now() + TIMEOUT_MS;
|
|
112
|
+
let jwt2 = null;
|
|
113
|
+
let orgName = null;
|
|
114
|
+
while (Date.now() < deadline) {
|
|
115
|
+
await sleep(POLL_INTERVAL_MS);
|
|
116
|
+
const pollRes = await fetch(
|
|
117
|
+
`${apiUrl}/api/v1/auth/extension/token?session_id=${encodeURIComponent(session_id)}`
|
|
118
|
+
);
|
|
119
|
+
if (pollRes.status === 410) {
|
|
120
|
+
throw new Error("Session expired or was already used. Please try again.");
|
|
121
|
+
}
|
|
122
|
+
if (!pollRes.ok && pollRes.status !== 200) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const data = await pollRes.json();
|
|
126
|
+
if (data.status === "pending") {
|
|
127
|
+
process.stdout.write(".");
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (data.token) {
|
|
131
|
+
jwt2 = data.token;
|
|
132
|
+
orgName = data.organization?.name ?? null;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
console.log("");
|
|
137
|
+
if (!jwt2) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
"Timed out waiting for approval. You can authenticate manually with `haansi init`."
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
console.log("Creating CLI token...");
|
|
143
|
+
const tokenRes = await fetch(`${apiUrl}/api/v1/cli-tokens`, {
|
|
144
|
+
method: "POST",
|
|
145
|
+
headers: {
|
|
146
|
+
Authorization: `Bearer ${jwt2}`,
|
|
147
|
+
"Content-Type": "application/json"
|
|
148
|
+
},
|
|
149
|
+
body: JSON.stringify({ name: "CLI (haansi auth login)" })
|
|
150
|
+
});
|
|
151
|
+
if (!tokenRes.ok) {
|
|
152
|
+
const body = await tokenRes.text().catch(() => "");
|
|
153
|
+
throw new Error(
|
|
154
|
+
`Failed to create CLI token (${tokenRes.status}): ${body.slice(0, 200)}`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
const { token: hskToken } = await tokenRes.json();
|
|
158
|
+
(0, import_node_fs.mkdirSync)(HAANSI_DIR, { recursive: true });
|
|
159
|
+
(0, import_node_fs.writeFileSync)(
|
|
160
|
+
CREDENTIALS_FILE,
|
|
161
|
+
JSON.stringify({ token: hskToken }, null, 2),
|
|
162
|
+
{
|
|
163
|
+
encoding: "utf-8",
|
|
164
|
+
mode: 384
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
console.log(`
|
|
168
|
+
Authenticated successfully${orgName ? ` (${orgName})` : ""}!`);
|
|
169
|
+
console.log(`Token saved to ${CREDENTIALS_FILE}`);
|
|
170
|
+
console.log(
|
|
171
|
+
"Run `haansi collect` to upload sessions or `haansi setup-mcp` to configure Claude Code."
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
var import_node_child_process, import_node_fs, import_node_path, import_node_os, DEFAULT_API_URL, DEFAULT_APP_URL, HAANSI_DIR, CREDENTIALS_FILE, POLL_INTERVAL_MS, TIMEOUT_MS;
|
|
175
|
+
var init_auth_login = __esm({
|
|
176
|
+
"src/commands/auth-login.ts"() {
|
|
177
|
+
"use strict";
|
|
178
|
+
import_node_child_process = require("node:child_process");
|
|
179
|
+
import_node_fs = require("node:fs");
|
|
180
|
+
import_node_path = require("node:path");
|
|
181
|
+
import_node_os = require("node:os");
|
|
182
|
+
DEFAULT_API_URL = "https://api.haansi.co";
|
|
183
|
+
DEFAULT_APP_URL = "https://app.haansi.co";
|
|
184
|
+
HAANSI_DIR = (0, import_node_path.join)((0, import_node_os.homedir)(), ".haansi");
|
|
185
|
+
CREDENTIALS_FILE = (0, import_node_path.join)(HAANSI_DIR, "credentials.json");
|
|
186
|
+
POLL_INTERVAL_MS = 2e3;
|
|
187
|
+
TIMEOUT_MS = 5 * 60 * 1e3;
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
67
191
|
// src/commands/init.ts
|
|
68
192
|
var init_exports = {};
|
|
69
193
|
__export(init_exports, {
|
|
@@ -93,8 +217,11 @@ async function validateToken(token, apiUrl) {
|
|
|
93
217
|
}
|
|
94
218
|
}
|
|
95
219
|
async function init() {
|
|
96
|
-
const apiUrl = process.env.HAANSI_API_URL ??
|
|
220
|
+
const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL2;
|
|
97
221
|
console.log("Haansi CLI \u2014 Setup\n");
|
|
222
|
+
console.log(
|
|
223
|
+
"Tip: For interactive login, run `haansi auth login` to authenticate via browser.\n"
|
|
224
|
+
);
|
|
98
225
|
console.log(
|
|
99
226
|
`You can generate an API token at https://app.haansi.co/settings/tokens
|
|
100
227
|
`
|
|
@@ -110,28 +237,28 @@ async function init() {
|
|
|
110
237
|
}
|
|
111
238
|
console.log("\nValidating token...");
|
|
112
239
|
await validateToken(token, apiUrl);
|
|
113
|
-
(0,
|
|
114
|
-
(0,
|
|
240
|
+
(0, import_node_fs2.mkdirSync)(HAANSI_DIR2, { recursive: true });
|
|
241
|
+
(0, import_node_fs2.writeFileSync)(CREDENTIALS_FILE2, JSON.stringify({ token }, null, 2), {
|
|
115
242
|
encoding: "utf-8",
|
|
116
243
|
mode: 384
|
|
117
244
|
});
|
|
118
245
|
console.log(`
|
|
119
|
-
Token saved to ${
|
|
246
|
+
Token saved to ${CREDENTIALS_FILE2}`);
|
|
120
247
|
console.log(
|
|
121
248
|
"Run `haansi daemon` to start collecting or `haansi setup-mcp` to configure Claude Code."
|
|
122
249
|
);
|
|
123
250
|
}
|
|
124
|
-
var readline,
|
|
251
|
+
var readline, import_node_fs2, import_node_path2, import_node_os2, DEFAULT_API_URL2, HAANSI_DIR2, CREDENTIALS_FILE2;
|
|
125
252
|
var init_init = __esm({
|
|
126
253
|
"src/commands/init.ts"() {
|
|
127
254
|
"use strict";
|
|
128
255
|
readline = __toESM(require("node:readline"));
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
256
|
+
import_node_fs2 = require("node:fs");
|
|
257
|
+
import_node_path2 = require("node:path");
|
|
258
|
+
import_node_os2 = require("node:os");
|
|
259
|
+
DEFAULT_API_URL2 = "https://api.haansi.co";
|
|
260
|
+
HAANSI_DIR2 = (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".haansi");
|
|
261
|
+
CREDENTIALS_FILE2 = (0, import_node_path2.join)(HAANSI_DIR2, "credentials.json");
|
|
135
262
|
}
|
|
136
263
|
});
|
|
137
264
|
|
|
@@ -567,7 +694,6 @@ function extractGitInfo(projectDir, sessionTimestamp, sessionBranch) {
|
|
|
567
694
|
try {
|
|
568
695
|
(0, import_child_process.execSync)("git rev-parse --is-inside-work-tree", { ...opts, stdio: "pipe" });
|
|
569
696
|
} catch {
|
|
570
|
-
console.warn(`[collector] Not a git repo: ${projectDir}`);
|
|
571
697
|
return empty;
|
|
572
698
|
}
|
|
573
699
|
const result = { ...empty };
|
|
@@ -577,7 +703,6 @@ function extractGitInfo(projectDir, sessionTimestamp, sessionBranch) {
|
|
|
577
703
|
stdio: "pipe"
|
|
578
704
|
}).trim() || null;
|
|
579
705
|
} catch {
|
|
580
|
-
console.warn(`[collector] No git remote for: ${projectDir}`);
|
|
581
706
|
}
|
|
582
707
|
if (sessionTimestamp) {
|
|
583
708
|
const branch = sessionBranch ?? "HEAD";
|
|
@@ -593,7 +718,6 @@ function extractGitInfo(projectDir, sessionTimestamp, sessionBranch) {
|
|
|
593
718
|
try {
|
|
594
719
|
result.gitCommit = (0, import_child_process.execSync)("git rev-parse HEAD", { ...opts, stdio: "pipe" }).trim() || null;
|
|
595
720
|
} catch {
|
|
596
|
-
console.warn(`[collector] No git commits in: ${projectDir}`);
|
|
597
721
|
}
|
|
598
722
|
}
|
|
599
723
|
try {
|
|
@@ -639,13 +763,13 @@ function extractSessionMeta(filePath) {
|
|
|
639
763
|
}
|
|
640
764
|
function resolveToken() {
|
|
641
765
|
if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
|
|
642
|
-
if ((0, import_fs.existsSync)(
|
|
766
|
+
if ((0, import_fs.existsSync)(CREDENTIALS_FILE3)) {
|
|
643
767
|
try {
|
|
644
|
-
const creds = JSON.parse((0, import_fs.readFileSync)(
|
|
768
|
+
const creds = JSON.parse((0, import_fs.readFileSync)(CREDENTIALS_FILE3, "utf-8"));
|
|
645
769
|
if (creds.token) return creds.token;
|
|
646
770
|
} catch {
|
|
647
771
|
logger.warn("Could not parse credentials file", {
|
|
648
|
-
file:
|
|
772
|
+
file: CREDENTIALS_FILE3
|
|
649
773
|
});
|
|
650
774
|
}
|
|
651
775
|
}
|
|
@@ -699,7 +823,10 @@ async function getUploadedSessions(apiUrl, token) {
|
|
|
699
823
|
const data = await apiGet("/sessions/uploaded", apiUrl, token);
|
|
700
824
|
const map2 = /* @__PURE__ */ new Map();
|
|
701
825
|
for (const s of data.sessions) {
|
|
702
|
-
map2.set(s.sessionId,
|
|
826
|
+
map2.set(s.sessionId, {
|
|
827
|
+
lineCount: s.lineCount,
|
|
828
|
+
hasGitMeta: s.hasGitMeta ?? false
|
|
829
|
+
});
|
|
703
830
|
}
|
|
704
831
|
return map2;
|
|
705
832
|
} catch {
|
|
@@ -731,7 +858,7 @@ async function uploadSession(sessionId, content, apiUrl, token, gitInfo) {
|
|
|
731
858
|
}
|
|
732
859
|
}
|
|
733
860
|
async function collect(options) {
|
|
734
|
-
const apiUrl = process.env.HAANSI_API_URL ??
|
|
861
|
+
const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL3;
|
|
735
862
|
const token = resolveToken();
|
|
736
863
|
if (!token && !options.dryRun) {
|
|
737
864
|
throw new Error(
|
|
@@ -749,8 +876,8 @@ async function collect(options) {
|
|
|
749
876
|
const remoteMap = options.forceAll ? /* @__PURE__ */ new Map() : await getUploadedSessions(apiUrl, token);
|
|
750
877
|
const toUpload = [];
|
|
751
878
|
for (const session of localSessions) {
|
|
752
|
-
const
|
|
753
|
-
if (
|
|
879
|
+
const remote = remoteMap.get(session.sessionId);
|
|
880
|
+
if (remote && remote.lineCount >= session.lineCount && remote.hasGitMeta) {
|
|
754
881
|
skipped++;
|
|
755
882
|
} else {
|
|
756
883
|
toUpload.push(session);
|
|
@@ -811,13 +938,9 @@ async function main() {
|
|
|
811
938
|
dryRun,
|
|
812
939
|
limit
|
|
813
940
|
});
|
|
814
|
-
console.log(
|
|
815
|
-
`
|
|
816
|
-
Collector done: ${uploaded} uploaded, ${skipped} unchanged, ${errors} errors`
|
|
817
|
-
);
|
|
818
941
|
if (errors > 0) process.exit(1);
|
|
819
942
|
}
|
|
820
|
-
var import_dotenv, import_path, import_fs, import_os, import_zlib, import_child_process, logger,
|
|
943
|
+
var import_dotenv, import_path, import_fs, import_os, import_zlib, import_child_process, logger, DEFAULT_API_URL3, CREDENTIALS_FILE3, CLAUDE_PROJECTS_DIR, MIN_LINES, EDGE_SCRUB_PATTERNS;
|
|
821
944
|
var init_collector = __esm({
|
|
822
945
|
"../service-capture/claude-sessions/collector.ts"() {
|
|
823
946
|
"use strict";
|
|
@@ -830,8 +953,8 @@ var init_collector = __esm({
|
|
|
830
953
|
init_logger();
|
|
831
954
|
(0, import_dotenv.config)({ path: (0, import_path.resolve)(__dirname, "../../../.env") });
|
|
832
955
|
logger = createLogger("claude-collector");
|
|
833
|
-
|
|
834
|
-
|
|
956
|
+
DEFAULT_API_URL3 = "https://api.haansi.co";
|
|
957
|
+
CREDENTIALS_FILE3 = (0, import_path.join)((0, import_os.homedir)(), ".haansi", "credentials.json");
|
|
835
958
|
CLAUDE_PROJECTS_DIR = (0, import_path.join)((0, import_os.homedir)(), ".claude", "projects");
|
|
836
959
|
MIN_LINES = 4;
|
|
837
960
|
EDGE_SCRUB_PATTERNS = [
|
|
@@ -896,17 +1019,17 @@ var require_task = __commonJS({
|
|
|
896
1019
|
this._execution = execution;
|
|
897
1020
|
}
|
|
898
1021
|
execute(now) {
|
|
899
|
-
let
|
|
1022
|
+
let exec2;
|
|
900
1023
|
try {
|
|
901
|
-
|
|
1024
|
+
exec2 = this._execution(now);
|
|
902
1025
|
} catch (error2) {
|
|
903
1026
|
return this.emit("task-failed", error2);
|
|
904
1027
|
}
|
|
905
|
-
if (
|
|
906
|
-
return
|
|
1028
|
+
if (exec2 instanceof Promise) {
|
|
1029
|
+
return exec2.then(() => this.emit("task-finished")).catch((error2) => this.emit("task-failed", error2));
|
|
907
1030
|
} else {
|
|
908
1031
|
this.emit("task-finished");
|
|
909
|
-
return
|
|
1032
|
+
return exec2;
|
|
910
1033
|
}
|
|
911
1034
|
}
|
|
912
1035
|
};
|
|
@@ -50549,7 +50672,7 @@ var require_view = __commonJS({
|
|
|
50549
50672
|
var dirname = path.dirname;
|
|
50550
50673
|
var basename2 = path.basename;
|
|
50551
50674
|
var extname = path.extname;
|
|
50552
|
-
var
|
|
50675
|
+
var join9 = path.join;
|
|
50553
50676
|
var resolve4 = path.resolve;
|
|
50554
50677
|
module2.exports = View;
|
|
50555
50678
|
function View(name, options) {
|
|
@@ -50611,12 +50734,12 @@ var require_view = __commonJS({
|
|
|
50611
50734
|
};
|
|
50612
50735
|
View.prototype.resolve = function resolve5(dir, file2) {
|
|
50613
50736
|
var ext = this.ext;
|
|
50614
|
-
var path2 =
|
|
50737
|
+
var path2 = join9(dir, file2);
|
|
50615
50738
|
var stat = tryStat(path2);
|
|
50616
50739
|
if (stat && stat.isFile()) {
|
|
50617
50740
|
return path2;
|
|
50618
50741
|
}
|
|
50619
|
-
path2 =
|
|
50742
|
+
path2 = join9(dir, basename2(file2, ext), "index" + ext);
|
|
50620
50743
|
stat = tryStat(path2);
|
|
50621
50744
|
if (stat && stat.isFile()) {
|
|
50622
50745
|
return path2;
|
|
@@ -54261,7 +54384,7 @@ var require_send = __commonJS({
|
|
|
54261
54384
|
var Stream = require("stream");
|
|
54262
54385
|
var util2 = require("util");
|
|
54263
54386
|
var extname = path.extname;
|
|
54264
|
-
var
|
|
54387
|
+
var join9 = path.join;
|
|
54265
54388
|
var normalize = path.normalize;
|
|
54266
54389
|
var resolve4 = path.resolve;
|
|
54267
54390
|
var sep = path.sep;
|
|
@@ -54433,7 +54556,7 @@ var require_send = __commonJS({
|
|
|
54433
54556
|
return res;
|
|
54434
54557
|
}
|
|
54435
54558
|
parts = path2.split(sep);
|
|
54436
|
-
path2 = normalize(
|
|
54559
|
+
path2 = normalize(join9(root, path2));
|
|
54437
54560
|
} else {
|
|
54438
54561
|
if (UP_PATH_REGEXP.test(path2)) {
|
|
54439
54562
|
debug('malicious path "%s"', path2);
|
|
@@ -54566,7 +54689,7 @@ var require_send = __commonJS({
|
|
|
54566
54689
|
if (err) return self.onStatError(err);
|
|
54567
54690
|
return self.error(404);
|
|
54568
54691
|
}
|
|
54569
|
-
var p =
|
|
54692
|
+
var p = join9(path2, self._index[i]);
|
|
54570
54693
|
debug('stat "%s"', p);
|
|
54571
54694
|
fs.stat(p, function(err2, stat) {
|
|
54572
54695
|
if (err2) return next(err2);
|
|
@@ -56663,9 +56786,9 @@ var init_streamableHttp = __esm({
|
|
|
56663
56786
|
var mcp_server_exports = {};
|
|
56664
56787
|
function resolveToken2() {
|
|
56665
56788
|
if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
|
|
56666
|
-
if ((0, import_fs2.existsSync)(
|
|
56789
|
+
if ((0, import_fs2.existsSync)(CREDENTIALS_FILE4)) {
|
|
56667
56790
|
try {
|
|
56668
|
-
const creds = JSON.parse((0, import_fs2.readFileSync)(
|
|
56791
|
+
const creds = JSON.parse((0, import_fs2.readFileSync)(CREDENTIALS_FILE4, "utf-8"));
|
|
56669
56792
|
if (creds.token) return creds.token;
|
|
56670
56793
|
} catch {
|
|
56671
56794
|
}
|
|
@@ -56675,13 +56798,9 @@ function resolveToken2() {
|
|
|
56675
56798
|
function triggerCollectInBackground() {
|
|
56676
56799
|
if (isCollecting) return;
|
|
56677
56800
|
isCollecting = true;
|
|
56678
|
-
collect({}).then((
|
|
56679
|
-
console.error(
|
|
56680
|
-
`[mcp-server] Collection done: ${uploaded} uploaded, ${skipped} unchanged, ${errors} errors`
|
|
56681
|
-
);
|
|
56801
|
+
collect({}).then(() => {
|
|
56682
56802
|
lastCollectTime = Date.now();
|
|
56683
|
-
}).catch((
|
|
56684
|
-
console.error("[mcp-server] Collection error:", err.message);
|
|
56803
|
+
}).catch(() => {
|
|
56685
56804
|
}).finally(() => {
|
|
56686
56805
|
isCollecting = false;
|
|
56687
56806
|
});
|
|
@@ -56723,23 +56842,10 @@ async function apiPost(path, body, options) {
|
|
|
56723
56842
|
async function main3() {
|
|
56724
56843
|
try {
|
|
56725
56844
|
await apiGet2("/sessions/recent?limit=1");
|
|
56726
|
-
|
|
56727
|
-
} catch (err) {
|
|
56728
|
-
console.error(
|
|
56729
|
-
"[mcp-server] WARNING: API connection check failed:",
|
|
56730
|
-
err.message
|
|
56731
|
-
);
|
|
56845
|
+
} catch {
|
|
56732
56846
|
}
|
|
56733
56847
|
try {
|
|
56734
|
-
|
|
56735
|
-
const mode = data?.preferences?.session_mode;
|
|
56736
|
-
if (!mode) {
|
|
56737
|
-
console.error(
|
|
56738
|
-
'[mcp-server] Session mode not configured. Defaulting to "org" (team-wide). To change: haansi config set session_mode private'
|
|
56739
|
-
);
|
|
56740
|
-
} else {
|
|
56741
|
-
console.error(`[mcp-server] Session mode: ${mode}`);
|
|
56742
|
-
}
|
|
56848
|
+
await apiGet2("/users/me/preferences");
|
|
56743
56849
|
} catch {
|
|
56744
56850
|
}
|
|
56745
56851
|
triggerCollectInBackground();
|
|
@@ -56760,18 +56866,13 @@ async function main3() {
|
|
|
56760
56866
|
app.get("/health", (_req, res) => {
|
|
56761
56867
|
res.json({ status: "ok", service: "haansi-solutions-mcp" });
|
|
56762
56868
|
});
|
|
56763
|
-
app.listen(PORT
|
|
56764
|
-
console.error(
|
|
56765
|
-
`[mcp-server] Haansi Solutions MCP server running on HTTP port ${PORT}`
|
|
56766
|
-
);
|
|
56767
|
-
});
|
|
56869
|
+
app.listen(PORT);
|
|
56768
56870
|
} else {
|
|
56769
56871
|
const transport = new StdioServerTransport();
|
|
56770
56872
|
await mcpServer.connect(transport);
|
|
56771
|
-
console.error("[mcp-server] haansi-solutions MCP server running on stdio");
|
|
56772
56873
|
}
|
|
56773
56874
|
}
|
|
56774
|
-
var import_dotenv3, import_path3, import_fs2, import_os2,
|
|
56875
|
+
var import_dotenv3, import_path3, import_fs2, import_os2, DEFAULT_API_URL4, CREDENTIALS_FILE4, API_URL, TOKEN, contextSchema, isCollecting, lastCollectTime, COLLECT_COOLDOWN_MS, mcpServer, server;
|
|
56775
56876
|
var init_mcp_server2 = __esm({
|
|
56776
56877
|
"../service-capture/claude-sessions/mcp-server.ts"() {
|
|
56777
56878
|
"use strict";
|
|
@@ -56785,14 +56886,11 @@ var init_mcp_server2 = __esm({
|
|
|
56785
56886
|
init_stdio2();
|
|
56786
56887
|
init_types2();
|
|
56787
56888
|
(0, import_dotenv3.config)({ path: (0, import_path3.resolve)(__dirname, "../../../.env") });
|
|
56788
|
-
|
|
56789
|
-
|
|
56790
|
-
API_URL = process.env.HAANSI_API_URL ??
|
|
56889
|
+
DEFAULT_API_URL4 = "https://api.haansi.co";
|
|
56890
|
+
CREDENTIALS_FILE4 = (0, import_path3.join)((0, import_os2.homedir)(), ".haansi", "credentials.json");
|
|
56891
|
+
API_URL = process.env.HAANSI_API_URL ?? DEFAULT_API_URL4;
|
|
56791
56892
|
TOKEN = resolveToken2();
|
|
56792
56893
|
if (!TOKEN) {
|
|
56793
|
-
console.error(
|
|
56794
|
-
"[mcp-server] ERROR: HAANSI_TOKEN is required (or set in ~/.haansi/credentials.json)"
|
|
56795
|
-
);
|
|
56796
56894
|
process.exit(1);
|
|
56797
56895
|
}
|
|
56798
56896
|
contextSchema = {
|
|
@@ -56820,7 +56918,7 @@ var init_mcp_server2 = __esm({
|
|
|
56820
56918
|
tools: [
|
|
56821
56919
|
{
|
|
56822
56920
|
name: "search_solutions",
|
|
56823
|
-
description: "
|
|
56921
|
+
description: "Search previously solved similar questions from your organization's knowledge base. Finds how bugs were fixed, what patterns were used, and past resolutions to similar problems. Results are ranked by a composite of relevance, quality, recency, and verification status.",
|
|
56824
56922
|
inputSchema: {
|
|
56825
56923
|
type: "object",
|
|
56826
56924
|
properties: {
|
|
@@ -56942,7 +57040,6 @@ var init_mcp_server2 = __esm({
|
|
|
56942
57040
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
56943
57041
|
maybeCollect();
|
|
56944
57042
|
const { name, arguments: args } = request.params;
|
|
56945
|
-
console.error(`[mcp-server] Tool: ${name}`);
|
|
56946
57043
|
try {
|
|
56947
57044
|
switch (name) {
|
|
56948
57045
|
case "search_solutions": {
|
|
@@ -56973,8 +57070,13 @@ var init_mcp_server2 = __esm({
|
|
|
56973
57070
|
]
|
|
56974
57071
|
};
|
|
56975
57072
|
}
|
|
57073
|
+
const header = `Found ${data.total} answer(s) from similar questions in your organization:
|
|
57074
|
+
|
|
57075
|
+
`;
|
|
56976
57076
|
return {
|
|
56977
|
-
content: [
|
|
57077
|
+
content: [
|
|
57078
|
+
{ type: "text", text: header + data.results.join("\n\n---\n\n") }
|
|
57079
|
+
]
|
|
56978
57080
|
};
|
|
56979
57081
|
}
|
|
56980
57082
|
case "list_recent_solutions": {
|
|
@@ -57067,15 +57169,13 @@ var init_mcp_server2 = __esm({
|
|
|
57067
57169
|
throw new Error(`Unknown tool: ${name}`);
|
|
57068
57170
|
}
|
|
57069
57171
|
} catch (err) {
|
|
57070
|
-
console.error(`[mcp-server] Tool ${name} error:`, err.message);
|
|
57071
57172
|
return {
|
|
57072
57173
|
content: [{ type: "text", text: `Error: ${err.message}` }],
|
|
57073
57174
|
isError: true
|
|
57074
57175
|
};
|
|
57075
57176
|
}
|
|
57076
57177
|
});
|
|
57077
|
-
main3().catch((
|
|
57078
|
-
console.error("[mcp-server] Failed to start:", err);
|
|
57178
|
+
main3().catch(() => {
|
|
57079
57179
|
process.exit(1);
|
|
57080
57180
|
});
|
|
57081
57181
|
}
|
|
@@ -57088,7 +57188,7 @@ __export(setup_mcp_exports, {
|
|
|
57088
57188
|
});
|
|
57089
57189
|
function resolveHaansiBin() {
|
|
57090
57190
|
try {
|
|
57091
|
-
const binPath = (0,
|
|
57191
|
+
const binPath = (0, import_node_child_process2.execSync)("which haansi", { encoding: "utf-8" }).trim();
|
|
57092
57192
|
if (binPath) return binPath;
|
|
57093
57193
|
} catch {
|
|
57094
57194
|
}
|
|
@@ -57096,9 +57196,9 @@ function resolveHaansiBin() {
|
|
|
57096
57196
|
}
|
|
57097
57197
|
async function setupMcp() {
|
|
57098
57198
|
let config6 = {};
|
|
57099
|
-
if ((0,
|
|
57199
|
+
if ((0, import_node_fs3.existsSync)(CLAUDE_JSON)) {
|
|
57100
57200
|
try {
|
|
57101
|
-
config6 = JSON.parse((0,
|
|
57201
|
+
config6 = JSON.parse((0, import_node_fs3.readFileSync)(CLAUDE_JSON, "utf-8"));
|
|
57102
57202
|
} catch {
|
|
57103
57203
|
console.error(
|
|
57104
57204
|
`Warning: Could not parse ${CLAUDE_JSON} \u2014 will overwrite with new config.`
|
|
@@ -57113,22 +57213,22 @@ async function setupMcp() {
|
|
|
57113
57213
|
command: haansiBin,
|
|
57114
57214
|
args: ["mcp-server"]
|
|
57115
57215
|
};
|
|
57116
|
-
(0,
|
|
57216
|
+
(0, import_node_fs3.writeFileSync)(CLAUDE_JSON, JSON.stringify(config6, null, 2) + "\n", "utf-8");
|
|
57117
57217
|
console.log(`MCP server configured in ${CLAUDE_JSON}`);
|
|
57118
57218
|
console.log(` command: ${haansiBin} mcp-server`);
|
|
57119
57219
|
console.log(
|
|
57120
57220
|
"\nRestart Claude Code to activate the haansi-solutions MCP server."
|
|
57121
57221
|
);
|
|
57122
57222
|
}
|
|
57123
|
-
var
|
|
57223
|
+
var import_node_fs3, import_node_path3, import_node_os3, import_node_child_process2, CLAUDE_JSON;
|
|
57124
57224
|
var init_setup_mcp = __esm({
|
|
57125
57225
|
"src/commands/setup-mcp.ts"() {
|
|
57126
57226
|
"use strict";
|
|
57127
|
-
|
|
57128
|
-
|
|
57129
|
-
|
|
57130
|
-
|
|
57131
|
-
CLAUDE_JSON = (0,
|
|
57227
|
+
import_node_fs3 = require("node:fs");
|
|
57228
|
+
import_node_path3 = require("node:path");
|
|
57229
|
+
import_node_os3 = require("node:os");
|
|
57230
|
+
import_node_child_process2 = require("node:child_process");
|
|
57231
|
+
CLAUDE_JSON = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".claude.json");
|
|
57132
57232
|
}
|
|
57133
57233
|
});
|
|
57134
57234
|
|
|
@@ -57139,7 +57239,7 @@ __export(setup_daemon_exports, {
|
|
|
57139
57239
|
});
|
|
57140
57240
|
function findHaansiBin() {
|
|
57141
57241
|
try {
|
|
57142
|
-
return (0,
|
|
57242
|
+
return (0, import_node_child_process3.execSync)("which haansi", { encoding: "utf-8" }).trim();
|
|
57143
57243
|
} catch {
|
|
57144
57244
|
throw new Error(
|
|
57145
57245
|
"Could not find haansi in PATH. Make sure it is installed globally (npm install -g haansi)."
|
|
@@ -57178,26 +57278,28 @@ function buildPlist(haansiPath) {
|
|
|
57178
57278
|
}
|
|
57179
57279
|
async function setupDaemon(uninstall) {
|
|
57180
57280
|
if (uninstall) {
|
|
57181
|
-
if (!(0,
|
|
57281
|
+
if (!(0, import_node_fs4.existsSync)(PLIST_PATH)) {
|
|
57182
57282
|
console.log("Daemon is not installed.");
|
|
57183
57283
|
return;
|
|
57184
57284
|
}
|
|
57185
57285
|
try {
|
|
57186
|
-
(0,
|
|
57187
|
-
} catch {
|
|
57286
|
+
(0, import_node_child_process3.spawnSync)("launchctl", ["unload", PLIST_PATH], { stdio: "pipe" });
|
|
57287
|
+
} catch (err) {
|
|
57288
|
+
console.error("Failed to unload daemon:", err);
|
|
57188
57289
|
}
|
|
57189
|
-
(0,
|
|
57290
|
+
(0, import_node_fs4.unlinkSync)(PLIST_PATH);
|
|
57190
57291
|
console.log("Daemon stopped and removed.");
|
|
57191
57292
|
return;
|
|
57192
57293
|
}
|
|
57193
57294
|
const haansiPath = findHaansiBin();
|
|
57194
|
-
(0,
|
|
57195
|
-
(0,
|
|
57295
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path4.join)((0, import_node_os4.homedir)(), ".haansi"), { recursive: true });
|
|
57296
|
+
(0, import_node_fs4.writeFileSync)(PLIST_PATH, buildPlist(haansiPath), "utf-8");
|
|
57196
57297
|
try {
|
|
57197
|
-
(0,
|
|
57198
|
-
} catch {
|
|
57298
|
+
(0, import_node_child_process3.spawnSync)("launchctl", ["unload", PLIST_PATH], { stdio: "pipe" });
|
|
57299
|
+
} catch (err) {
|
|
57300
|
+
console.error("Failed to unload previous daemon:", err);
|
|
57199
57301
|
}
|
|
57200
|
-
(0,
|
|
57302
|
+
(0, import_node_child_process3.spawnSync)("launchctl", ["load", PLIST_PATH], { stdio: "inherit" });
|
|
57201
57303
|
console.log("Daemon installed and started.");
|
|
57202
57304
|
console.log(`Plist : ${PLIST_PATH}`);
|
|
57203
57305
|
console.log(`Logs : ${LOG_FILE}`);
|
|
@@ -57205,22 +57307,22 @@ async function setupDaemon(uninstall) {
|
|
|
57205
57307
|
The daemon will start automatically on every login.`);
|
|
57206
57308
|
console.log(`To uninstall: haansi setup-daemon --uninstall`);
|
|
57207
57309
|
}
|
|
57208
|
-
var
|
|
57310
|
+
var import_node_child_process3, import_node_fs4, import_node_path4, import_node_os4, PLIST_LABEL, PLIST_PATH, LOG_FILE;
|
|
57209
57311
|
var init_setup_daemon = __esm({
|
|
57210
57312
|
"src/commands/setup-daemon.ts"() {
|
|
57211
57313
|
"use strict";
|
|
57212
|
-
|
|
57213
|
-
|
|
57214
|
-
|
|
57215
|
-
|
|
57314
|
+
import_node_child_process3 = require("node:child_process");
|
|
57315
|
+
import_node_fs4 = require("node:fs");
|
|
57316
|
+
import_node_path4 = require("node:path");
|
|
57317
|
+
import_node_os4 = require("node:os");
|
|
57216
57318
|
PLIST_LABEL = "co.haansi.daemon";
|
|
57217
|
-
PLIST_PATH = (0,
|
|
57218
|
-
(0,
|
|
57319
|
+
PLIST_PATH = (0, import_node_path4.join)(
|
|
57320
|
+
(0, import_node_os4.homedir)(),
|
|
57219
57321
|
"Library",
|
|
57220
57322
|
"LaunchAgents",
|
|
57221
57323
|
`${PLIST_LABEL}.plist`
|
|
57222
57324
|
);
|
|
57223
|
-
LOG_FILE = (0,
|
|
57325
|
+
LOG_FILE = (0, import_node_path4.join)((0, import_node_os4.homedir)(), ".haansi", "daemon.log");
|
|
57224
57326
|
}
|
|
57225
57327
|
});
|
|
57226
57328
|
|
|
@@ -57231,11 +57333,12 @@ __export(config_exports, {
|
|
|
57231
57333
|
});
|
|
57232
57334
|
function resolveToken3() {
|
|
57233
57335
|
if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
|
|
57234
|
-
if ((0,
|
|
57336
|
+
if ((0, import_node_fs5.existsSync)(CREDENTIALS_FILE5)) {
|
|
57235
57337
|
try {
|
|
57236
|
-
const creds = JSON.parse((0,
|
|
57338
|
+
const creds = JSON.parse((0, import_node_fs5.readFileSync)(CREDENTIALS_FILE5, "utf-8"));
|
|
57237
57339
|
if (creds.token) return creds.token;
|
|
57238
|
-
} catch {
|
|
57340
|
+
} catch (err) {
|
|
57341
|
+
console.error("Failed to read credentials file:", err);
|
|
57239
57342
|
}
|
|
57240
57343
|
}
|
|
57241
57344
|
return null;
|
|
@@ -57249,7 +57352,7 @@ async function config5(args) {
|
|
|
57249
57352
|
process.exit(1);
|
|
57250
57353
|
return;
|
|
57251
57354
|
}
|
|
57252
|
-
const apiUrl = process.env.HAANSI_API_URL ??
|
|
57355
|
+
const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL5;
|
|
57253
57356
|
const subcommand = args[0];
|
|
57254
57357
|
if (subcommand === "get") {
|
|
57255
57358
|
const key = args[1];
|
|
@@ -57324,15 +57427,15 @@ Supported keys:
|
|
|
57324
57427
|
if (subcommand !== void 0) process.exit(1);
|
|
57325
57428
|
}
|
|
57326
57429
|
}
|
|
57327
|
-
var
|
|
57430
|
+
var import_node_fs5, import_node_path5, import_node_os5, DEFAULT_API_URL5, CREDENTIALS_FILE5, VALID_SESSION_MODES;
|
|
57328
57431
|
var init_config = __esm({
|
|
57329
57432
|
"src/commands/config.ts"() {
|
|
57330
57433
|
"use strict";
|
|
57331
|
-
|
|
57332
|
-
|
|
57333
|
-
|
|
57334
|
-
|
|
57335
|
-
|
|
57434
|
+
import_node_fs5 = require("node:fs");
|
|
57435
|
+
import_node_path5 = require("node:path");
|
|
57436
|
+
import_node_os5 = require("node:os");
|
|
57437
|
+
DEFAULT_API_URL5 = "https://api.haansi.co";
|
|
57438
|
+
CREDENTIALS_FILE5 = (0, import_node_path5.join)((0, import_node_os5.homedir)(), ".haansi", "credentials.json");
|
|
57336
57439
|
VALID_SESSION_MODES = ["private", "org"];
|
|
57337
57440
|
}
|
|
57338
57441
|
});
|
|
@@ -57346,11 +57449,12 @@ __export(list_exports, {
|
|
|
57346
57449
|
});
|
|
57347
57450
|
function resolveToken4() {
|
|
57348
57451
|
if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
|
|
57349
|
-
if ((0,
|
|
57452
|
+
if ((0, import_node_fs6.existsSync)(CREDENTIALS_FILE6)) {
|
|
57350
57453
|
try {
|
|
57351
|
-
const creds = JSON.parse((0,
|
|
57454
|
+
const creds = JSON.parse((0, import_node_fs6.readFileSync)(CREDENTIALS_FILE6, "utf-8"));
|
|
57352
57455
|
if (creds.token) return creds.token;
|
|
57353
|
-
} catch {
|
|
57456
|
+
} catch (err) {
|
|
57457
|
+
console.error("Failed to read credentials file:", err);
|
|
57354
57458
|
}
|
|
57355
57459
|
}
|
|
57356
57460
|
throw new Error("No token found. Run `haansi init` first.");
|
|
@@ -57381,7 +57485,7 @@ async function apiPost2(path, body, token, apiUrl) {
|
|
|
57381
57485
|
return response.json();
|
|
57382
57486
|
}
|
|
57383
57487
|
async function list(options) {
|
|
57384
|
-
const apiUrl = process.env.HAANSI_API_URL ??
|
|
57488
|
+
const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL6;
|
|
57385
57489
|
const token = resolveToken4();
|
|
57386
57490
|
let data;
|
|
57387
57491
|
if (options.search) {
|
|
@@ -57407,7 +57511,7 @@ async function list(options) {
|
|
|
57407
57511
|
${data.results.length} result(s)`);
|
|
57408
57512
|
}
|
|
57409
57513
|
async function searchKnowledge(options) {
|
|
57410
|
-
const apiUrl = process.env.HAANSI_API_URL ??
|
|
57514
|
+
const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL6;
|
|
57411
57515
|
const token = resolveToken4();
|
|
57412
57516
|
let url2 = `/sessions/knowledge/search?q=${encodeURIComponent(options.query)}&limit=${options.limit}`;
|
|
57413
57517
|
if (options.artifactType) {
|
|
@@ -57424,7 +57528,7 @@ async function searchKnowledge(options) {
|
|
|
57424
57528
|
${data.results.length} result(s)`);
|
|
57425
57529
|
}
|
|
57426
57530
|
async function saveKnowledge(options) {
|
|
57427
|
-
const apiUrl = process.env.HAANSI_API_URL ??
|
|
57531
|
+
const apiUrl = process.env.HAANSI_API_URL ?? DEFAULT_API_URL6;
|
|
57428
57532
|
const token = resolveToken4();
|
|
57429
57533
|
const body = {
|
|
57430
57534
|
problem_description: options.problem,
|
|
@@ -57440,15 +57544,15 @@ async function saveKnowledge(options) {
|
|
|
57440
57544
|
console.log(` Solution: ${data.solution_summary}`);
|
|
57441
57545
|
console.log("\nThis is now searchable via `haansi search-solutions`.");
|
|
57442
57546
|
}
|
|
57443
|
-
var
|
|
57547
|
+
var import_node_fs6, import_node_path6, import_node_os6, DEFAULT_API_URL6, CREDENTIALS_FILE6;
|
|
57444
57548
|
var init_list = __esm({
|
|
57445
57549
|
"src/commands/list.ts"() {
|
|
57446
57550
|
"use strict";
|
|
57447
|
-
|
|
57448
|
-
|
|
57449
|
-
|
|
57450
|
-
|
|
57451
|
-
|
|
57551
|
+
import_node_fs6 = require("node:fs");
|
|
57552
|
+
import_node_path6 = require("node:path");
|
|
57553
|
+
import_node_os6 = require("node:os");
|
|
57554
|
+
DEFAULT_API_URL6 = "https://api.haansi.co";
|
|
57555
|
+
CREDENTIALS_FILE6 = (0, import_node_path6.join)((0, import_node_os6.homedir)(), ".haansi", "credentials.json");
|
|
57452
57556
|
}
|
|
57453
57557
|
});
|
|
57454
57558
|
|
|
@@ -57461,6 +57565,20 @@ async function run2() {
|
|
|
57461
57565
|
return;
|
|
57462
57566
|
}
|
|
57463
57567
|
switch (command) {
|
|
57568
|
+
case "auth": {
|
|
57569
|
+
const subcommand = process.argv[3];
|
|
57570
|
+
if (subcommand === "login") {
|
|
57571
|
+
const { authLogin: authLogin2 } = await Promise.resolve().then(() => (init_auth_login(), auth_login_exports));
|
|
57572
|
+
await authLogin2();
|
|
57573
|
+
} else {
|
|
57574
|
+
console.error(
|
|
57575
|
+
`Unknown auth subcommand: ${subcommand ?? "(none)"}
|
|
57576
|
+
Usage: haansi auth login`
|
|
57577
|
+
);
|
|
57578
|
+
process.exit(1);
|
|
57579
|
+
}
|
|
57580
|
+
break;
|
|
57581
|
+
}
|
|
57464
57582
|
case "init": {
|
|
57465
57583
|
const { init: init2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
57466
57584
|
await init2();
|
|
@@ -57587,7 +57705,8 @@ Usage:
|
|
|
57587
57705
|
haansi <command> [options]
|
|
57588
57706
|
|
|
57589
57707
|
Commands:
|
|
57590
|
-
|
|
57708
|
+
auth login Log in via browser (recommended)
|
|
57709
|
+
init Authenticate with a token (for CI / headless)
|
|
57591
57710
|
collect Upload new/changed Claude sessions once
|
|
57592
57711
|
config get [key] Show your preferences (e.g. session_mode)
|
|
57593
57712
|
config set <key> <val> Update a preference
|