@hasna/terminal 4.3.1 → 4.3.3
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/App.js +404 -0
- package/dist/Browse.js +79 -0
- package/dist/FuzzyPicker.js +47 -0
- package/dist/Onboarding.js +51 -0
- package/dist/Spinner.js +12 -0
- package/dist/StatusBar.js +49 -0
- package/dist/ai.js +316 -0
- package/dist/cache.js +42 -0
- package/dist/cli.js +778 -0
- package/dist/command-rewriter.js +64 -0
- package/dist/command-validator.js +86 -0
- package/dist/compression.js +91 -0
- package/dist/context-hints.js +285 -0
- package/dist/db/pg-migrations.js +70 -0
- package/dist/diff-cache.js +107 -0
- package/dist/discover.js +212 -0
- package/dist/economy.js +155 -0
- package/dist/expand-store.js +44 -0
- package/dist/file-cache.js +72 -0
- package/dist/file-index.js +62 -0
- package/dist/history.js +62 -0
- package/dist/lazy-executor.js +54 -0
- package/dist/line-dedup.js +59 -0
- package/dist/loop-detector.js +75 -0
- package/dist/mcp/install.js +189 -0
- package/dist/mcp/server.js +90 -0
- package/dist/mcp/tools/batch.js +111 -0
- package/dist/mcp/tools/execute.js +194 -0
- package/dist/mcp/tools/files.js +290 -0
- package/dist/mcp/tools/git.js +233 -0
- package/dist/mcp/tools/helpers.js +63 -0
- package/dist/mcp/tools/memory.js +151 -0
- package/dist/mcp/tools/meta.js +138 -0
- package/dist/mcp/tools/process.js +50 -0
- package/dist/mcp/tools/project.js +251 -0
- package/dist/mcp/tools/search.js +86 -0
- package/dist/noise-filter.js +94 -0
- package/dist/output-processor.js +233 -0
- package/dist/output-store.js +112 -0
- package/dist/paths.js +28 -0
- package/dist/providers/anthropic.js +43 -0
- package/dist/providers/base.js +4 -0
- package/dist/providers/cerebras.js +8 -0
- package/dist/providers/groq.js +8 -0
- package/dist/providers/index.js +142 -0
- package/dist/providers/openai-compat.js +93 -0
- package/dist/providers/xai.js +8 -0
- package/dist/recipes/model.js +20 -0
- package/dist/recipes/storage.js +153 -0
- package/dist/search/content-search.js +70 -0
- package/dist/search/file-search.js +61 -0
- package/dist/search/filters.js +34 -0
- package/dist/search/index.js +5 -0
- package/dist/search/semantic.js +346 -0
- package/dist/session-boot.js +59 -0
- package/dist/session-context.js +55 -0
- package/dist/sessions-db.js +240 -0
- package/dist/smart-display.js +286 -0
- package/dist/snapshots.js +51 -0
- package/dist/supervisor.js +112 -0
- package/dist/test-watchlist.js +131 -0
- package/dist/tokens.js +17 -0
- package/dist/tool-profiles.js +130 -0
- package/dist/tree.js +94 -0
- package/dist/usage-cache.js +65 -0
- package/package.json +2 -1
- package/src/Onboarding.tsx +1 -1
- package/src/ai.ts +5 -4
- package/src/cache.ts +2 -2
- package/src/db/pg-migrations.ts +77 -0
- package/src/economy.ts +3 -3
- package/src/history.ts +2 -2
- package/src/mcp/server.ts +55 -0
- package/src/mcp/tools/memory.ts +4 -2
- package/src/output-store.ts +2 -1
- package/src/paths.ts +32 -0
- package/src/recipes/storage.ts +3 -3
- package/src/session-context.ts +2 -2
- package/src/sessions-db.ts +15 -4
- package/src/tool-profiles.ts +4 -3
- package/src/usage-cache.ts +2 -2
package/src/mcp/server.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { z } from "zod";
|
|
5
6
|
import { createSession } from "../sessions-db.js";
|
|
6
7
|
import { createHelpers } from "./tools/helpers.js";
|
|
7
8
|
|
|
@@ -15,6 +16,7 @@ import { registerProcessTools } from "./tools/process.js";
|
|
|
15
16
|
import { registerBatchTools } from "./tools/batch.js";
|
|
16
17
|
import { registerMemoryTools } from "./tools/memory.js";
|
|
17
18
|
import { registerMetaTools } from "./tools/meta.js";
|
|
19
|
+
import { registerCloudTools } from "@hasna/cloud";
|
|
18
20
|
|
|
19
21
|
// ── server ───────────────────────────────────────────────────────────────────
|
|
20
22
|
|
|
@@ -49,6 +51,59 @@ export function createServer(): McpServer {
|
|
|
49
51
|
registerBatchTools(server, h);
|
|
50
52
|
registerMemoryTools(server, h);
|
|
51
53
|
registerMetaTools(server, h);
|
|
54
|
+
registerCloudTools(server, "terminal");
|
|
55
|
+
|
|
56
|
+
// ── Agent Tools ──────────────────────────────────────────────────────────
|
|
57
|
+
const _agentReg = new Map<string, { id: string; name: string; last_seen_at: string; project_id?: string }>();
|
|
58
|
+
|
|
59
|
+
server.tool(
|
|
60
|
+
"register_agent",
|
|
61
|
+
"Register an agent session (idempotent). Auto-updates last_seen_at on re-register.",
|
|
62
|
+
{ name: z.string(), session_id: z.string().optional() },
|
|
63
|
+
async (a) => {
|
|
64
|
+
const existing = [..._agentReg.values()].find(x => x.name === a.name);
|
|
65
|
+
if (existing) { existing.last_seen_at = new Date().toISOString(); return { content: [{ type: "text" as const, text: JSON.stringify(existing) }] }; }
|
|
66
|
+
const id = Math.random().toString(36).slice(2, 10);
|
|
67
|
+
const ag = { id, name: a.name, last_seen_at: new Date().toISOString() };
|
|
68
|
+
_agentReg.set(id, ag);
|
|
69
|
+
return { content: [{ type: "text" as const, text: JSON.stringify(ag) }] };
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
server.tool(
|
|
74
|
+
"heartbeat",
|
|
75
|
+
"Update last_seen_at to signal agent is active.",
|
|
76
|
+
{ agent_id: z.string() },
|
|
77
|
+
async (a) => {
|
|
78
|
+
const ag = _agentReg.get(a.agent_id);
|
|
79
|
+
if (!ag) return { content: [{ type: "text" as const, text: `Agent not found: ${a.agent_id}` }], isError: true };
|
|
80
|
+
ag.last_seen_at = new Date().toISOString();
|
|
81
|
+
return { content: [{ type: "text" as const, text: JSON.stringify({ id: ag.id, name: ag.name, last_seen_at: ag.last_seen_at }) }] };
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
server.tool(
|
|
86
|
+
"set_focus",
|
|
87
|
+
"Set active project context for this agent session.",
|
|
88
|
+
{ agent_id: z.string(), project_id: z.string().nullable().optional() },
|
|
89
|
+
async (a) => {
|
|
90
|
+
const ag = _agentReg.get(a.agent_id);
|
|
91
|
+
if (!ag) return { content: [{ type: "text" as const, text: `Agent not found: ${a.agent_id}` }], isError: true };
|
|
92
|
+
(ag as any).project_id = a.project_id ?? undefined;
|
|
93
|
+
return { content: [{ type: "text" as const, text: a.project_id ? `Focus: ${a.project_id}` : "Focus cleared" }] };
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
server.tool(
|
|
98
|
+
"list_agents",
|
|
99
|
+
"List all registered agents.",
|
|
100
|
+
{},
|
|
101
|
+
async () => {
|
|
102
|
+
const agents = [..._agentReg.values()];
|
|
103
|
+
if (agents.length === 0) return { content: [{ type: "text" as const, text: "No agents registered." }] };
|
|
104
|
+
return { content: [{ type: "text" as const, text: JSON.stringify(agents, null, 2) }] };
|
|
105
|
+
}
|
|
106
|
+
);
|
|
52
107
|
|
|
53
108
|
return server;
|
|
54
109
|
}
|
package/src/mcp/tools/memory.ts
CHANGED
|
@@ -136,7 +136,8 @@ export function registerMemoryTools(server: McpServer, h: ToolHelpers): void {
|
|
|
136
136
|
async ({ name, value }) => {
|
|
137
137
|
const { existsSync, readFileSync, writeFileSync, chmodSync } = await import("fs");
|
|
138
138
|
const { join } = await import("path");
|
|
139
|
-
const
|
|
139
|
+
const { getTerminalDir } = await import("../../paths.js");
|
|
140
|
+
const secretsFile = join(getTerminalDir(), "secrets.json");
|
|
140
141
|
let secrets: Record<string, string> = {};
|
|
141
142
|
if (existsSync(secretsFile)) {
|
|
142
143
|
try { secrets = JSON.parse(readFileSync(secretsFile, "utf8")); } catch {}
|
|
@@ -157,7 +158,8 @@ export function registerMemoryTools(server: McpServer, h: ToolHelpers): void {
|
|
|
157
158
|
async () => {
|
|
158
159
|
const { existsSync, readFileSync } = await import("fs");
|
|
159
160
|
const { join } = await import("path");
|
|
160
|
-
const
|
|
161
|
+
const { getTerminalDir } = await import("../../paths.js");
|
|
162
|
+
const secretsFile = join(getTerminalDir(), "secrets.json");
|
|
161
163
|
let names: string[] = [];
|
|
162
164
|
if (existsSync(secretsFile)) {
|
|
163
165
|
try { names = Object.keys(JSON.parse(readFileSync(secretsFile, "utf8"))); } catch {}
|
package/src/output-store.ts
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
import { existsSync, mkdirSync, writeFileSync, readdirSync, statSync, unlinkSync } from "fs";
|
|
5
5
|
import { join } from "path";
|
|
6
6
|
import { createHash } from "crypto";
|
|
7
|
+
import { getTerminalDir } from "./paths.js";
|
|
7
8
|
|
|
8
|
-
const OUTPUTS_DIR = join(
|
|
9
|
+
const OUTPUTS_DIR = join(getTerminalDir(), "outputs");
|
|
9
10
|
|
|
10
11
|
/** Ensure outputs directory exists */
|
|
11
12
|
function ensureDir() {
|
package/src/paths.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Centralized path resolution for open-terminal global data directory.
|
|
2
|
+
// Migrated from ~/.terminal/ to ~/.hasna/terminal/ with backward compat.
|
|
3
|
+
|
|
4
|
+
import { existsSync, mkdirSync } from "fs";
|
|
5
|
+
import { homedir } from "os";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get the global terminal data directory.
|
|
10
|
+
* New default: ~/.hasna/terminal/
|
|
11
|
+
* Legacy fallback: ~/.terminal/ (if it exists and new dir doesn't)
|
|
12
|
+
* Env override: HASNA_TERMINAL_DIR or TERMINAL_DIR
|
|
13
|
+
*/
|
|
14
|
+
export function getTerminalDir(): string {
|
|
15
|
+
if (process.env.HASNA_TERMINAL_DIR) return process.env.HASNA_TERMINAL_DIR;
|
|
16
|
+
if (process.env.TERMINAL_DIR) return process.env.TERMINAL_DIR;
|
|
17
|
+
|
|
18
|
+
const home = homedir();
|
|
19
|
+
const newDir = join(home, ".hasna", "terminal");
|
|
20
|
+
const legacyDir = join(home, ".terminal");
|
|
21
|
+
|
|
22
|
+
// Use legacy dir if it exists and new one doesn't yet (backward compat)
|
|
23
|
+
if (!existsSync(newDir) && existsSync(legacyDir)) {
|
|
24
|
+
return legacyDir;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!existsSync(newDir)) {
|
|
28
|
+
mkdirSync(newDir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return newDir;
|
|
32
|
+
}
|
package/src/recipes/storage.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
// Recipes storage — global (~/.terminal/recipes.json) + per-project (.terminal/recipes.json)
|
|
1
|
+
// Recipes storage — global (~/.hasna/terminal/recipes.json) + per-project (.terminal/recipes.json)
|
|
2
2
|
|
|
3
3
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
4
|
-
import { homedir } from "os";
|
|
5
4
|
import { join } from "path";
|
|
6
5
|
import type { Recipe, Collection, RecipeStore } from "./model.js";
|
|
7
6
|
import { genId, extractVariables } from "./model.js";
|
|
7
|
+
import { getTerminalDir } from "../paths.js";
|
|
8
8
|
|
|
9
|
-
const GLOBAL_DIR =
|
|
9
|
+
const GLOBAL_DIR = getTerminalDir();
|
|
10
10
|
const GLOBAL_FILE = join(GLOBAL_DIR, "recipes.json");
|
|
11
11
|
|
|
12
12
|
function projectFile(projectPath: string): string {
|
package/src/session-context.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// Enables: terminal "show auth code" → terminal "explain that function"
|
|
3
3
|
|
|
4
4
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
5
|
-
import { homedir } from "os";
|
|
6
5
|
import { join } from "path";
|
|
6
|
+
import { getTerminalDir } from "./paths.js";
|
|
7
7
|
|
|
8
|
-
const DIR =
|
|
8
|
+
const DIR = getTerminalDir();
|
|
9
9
|
const CTX_FILE = join(DIR, "session-context.json");
|
|
10
10
|
const MAX_ENTRIES = 5;
|
|
11
11
|
|
package/src/sessions-db.ts
CHANGED
|
@@ -2,20 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
// @ts-ignore — bun:sqlite is a bun built-in
|
|
4
4
|
import { Database } from "bun:sqlite";
|
|
5
|
+
import { SqliteAdapter } from "@hasna/cloud";
|
|
5
6
|
import { existsSync, mkdirSync } from "fs";
|
|
6
|
-
import { homedir } from "os";
|
|
7
7
|
import { join } from "path";
|
|
8
8
|
import { randomUUID } from "crypto";
|
|
9
|
+
import { getTerminalDir } from "./paths.js";
|
|
9
10
|
|
|
10
|
-
const DIR =
|
|
11
|
-
const DB_PATH = join(DIR, "sessions.db");
|
|
11
|
+
const DIR = getTerminalDir();
|
|
12
|
+
const DB_PATH = process.env.HASNA_TERMINAL_DB_PATH ?? process.env.TERMINAL_DB_PATH ?? join(DIR, "sessions.db");
|
|
12
13
|
|
|
13
14
|
let db: Database | null = null;
|
|
14
15
|
|
|
15
16
|
function getDb(): Database {
|
|
16
17
|
if (db) return db;
|
|
17
18
|
if (!existsSync(DIR)) mkdirSync(DIR, { recursive: true });
|
|
18
|
-
db = new
|
|
19
|
+
db = new SqliteAdapter(DB_PATH) as unknown as Database;
|
|
19
20
|
db.exec("PRAGMA journal_mode = WAL");
|
|
20
21
|
|
|
21
22
|
db.exec(`
|
|
@@ -71,6 +72,16 @@ function getDb(): Database {
|
|
|
71
72
|
);
|
|
72
73
|
|
|
73
74
|
CREATE INDEX IF NOT EXISTS idx_corrections_prompt ON corrections(prompt);
|
|
75
|
+
|
|
76
|
+
CREATE TABLE IF NOT EXISTS feedback (
|
|
77
|
+
id TEXT PRIMARY KEY DEFAULT (lower(hex(randomblob(16)))),
|
|
78
|
+
message TEXT NOT NULL,
|
|
79
|
+
email TEXT,
|
|
80
|
+
category TEXT DEFAULT 'general',
|
|
81
|
+
version TEXT,
|
|
82
|
+
machine_id TEXT,
|
|
83
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
84
|
+
);
|
|
74
85
|
`);
|
|
75
86
|
|
|
76
87
|
return db;
|
package/src/tool-profiles.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// Tool profiles — config-driven AI enhancement for specific command categories
|
|
2
|
-
// Profiles are loaded from ~/.terminal/profiles/ (user-customizable)
|
|
2
|
+
// Profiles are loaded from ~/.hasna/terminal/profiles/ (user-customizable)
|
|
3
3
|
// Each profile tells the AI how to handle a specific tool's output
|
|
4
4
|
|
|
5
5
|
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
6
6
|
import { join } from "path";
|
|
7
|
+
import { getTerminalDir } from "./paths.js";
|
|
7
8
|
|
|
8
9
|
export interface ToolProfile {
|
|
9
10
|
name: string;
|
|
@@ -23,7 +24,7 @@ export interface ToolProfile {
|
|
|
23
24
|
};
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
const PROFILES_DIR = join(
|
|
27
|
+
const PROFILES_DIR = join(getTerminalDir(), "profiles");
|
|
27
28
|
|
|
28
29
|
/** Built-in profiles — sensible defaults, user can override */
|
|
29
30
|
const BUILTIN_PROFILES: ToolProfile[] = [
|
|
@@ -90,7 +91,7 @@ const BUILTIN_PROFILES: ToolProfile[] = [
|
|
|
90
91
|
},
|
|
91
92
|
];
|
|
92
93
|
|
|
93
|
-
/** Load user profiles from ~/.terminal/profiles/ */
|
|
94
|
+
/** Load user profiles from ~/.hasna/terminal/profiles/ */
|
|
94
95
|
function loadUserProfiles(): ToolProfile[] {
|
|
95
96
|
if (!existsSync(PROFILES_DIR)) return [];
|
|
96
97
|
|
package/src/usage-cache.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
// After 3 identical prompt→command mappings, cache locally
|
|
3
3
|
|
|
4
4
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
5
|
-
import { homedir } from "os";
|
|
6
5
|
import { join } from "path";
|
|
7
6
|
import { createHash } from "crypto";
|
|
7
|
+
import { getTerminalDir } from "./paths.js";
|
|
8
8
|
|
|
9
|
-
const DIR =
|
|
9
|
+
const DIR = getTerminalDir();
|
|
10
10
|
const CACHE_FILE = join(DIR, "learned.json");
|
|
11
11
|
|
|
12
12
|
interface LearnedEntry {
|