wyrm-mcp 7.2.0 → 7.2.2
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/LICENSE +26 -667
- package/NOTICE +14 -33
- package/dist/activation.d.ts.map +1 -1
- package/dist/activation.js +1 -44
- package/dist/activation.js.map +1 -1
- package/dist/agent-daemon.js +4 -281
- package/dist/agent-loop.js +7 -332
- package/dist/analytics.js +13 -236
- package/dist/attribution.js +1 -49
- package/dist/audit.js +2 -457
- package/dist/auto-capture.js +3 -138
- package/dist/auto-orchestrator.js +1 -325
- package/dist/autoconfig.js +39 -840
- package/dist/buddy-runner.js +1 -109
- package/dist/buddy.js +14 -564
- package/dist/build-flags.js +1 -17
- package/dist/capabilities.js +3 -183
- package/dist/capture.js +1 -56
- package/dist/causality.js +6 -107
- package/dist/cli.js +20 -281
- package/dist/cloud/cli.js +5 -541
- package/dist/cloud/client.js +1 -221
- package/dist/cloud/crypto.js +1 -85
- package/dist/cloud/machine-id.js +2 -113
- package/dist/cloud/recovery.js +1 -60
- package/dist/cloud/sync-engine.js +7 -543
- package/dist/cloud-backup.js +5 -579
- package/dist/cloud-profile.js +1 -138
- package/dist/cloud-sync-entrypoint.js +1 -47
- package/dist/cloud-sync.js +2 -309
- package/dist/constellation.js +12 -168
- package/dist/context-build-budgeted.js +4 -144
- package/dist/context-ranking.js +1 -69
- package/dist/crypto.js +1 -179
- package/dist/daemon-write-endpoint.js +1 -290
- package/dist/daemon-writer.js +2 -406
- package/dist/database.js +43 -1110
- package/dist/deprecations.js +2 -162
- package/dist/design.js +13 -141
- package/dist/event-replication.js +1 -112
- package/dist/events-sse.js +7 -43
- package/dist/events.js +6 -238
- package/dist/failure-patterns.js +42 -659
- package/dist/federation.js +12 -236
- package/dist/goals.js +13 -101
- package/dist/golden.js +3 -355
- package/dist/handlers/agent.js +4 -165
- package/dist/handlers/alias-adapters.js +1 -129
- package/dist/handlers/aliases.js +1 -171
- package/dist/handlers/audit.js +1 -87
- package/dist/handlers/boundary.js +1 -221
- package/dist/handlers/capture.js +73 -1109
- package/dist/handlers/causality.js +7 -114
- package/dist/handlers/cloud.js +85 -382
- package/dist/handlers/companion.js +28 -459
- package/dist/handlers/datalake.js +7 -187
- package/dist/handlers/dispatch-context.js +0 -22
- package/dist/handlers/entity.js +25 -256
- package/dist/handlers/events.js +16 -335
- package/dist/handlers/failure.js +13 -340
- package/dist/handlers/goals.js +4 -296
- package/dist/handlers/intelligence.js +126 -674
- package/dist/handlers/invoicing.js +1 -70
- package/dist/handlers/mcpclient.js +6 -137
- package/dist/handlers/orchestration.js +40 -125
- package/dist/handlers/output-schemas.js +1 -24
- package/dist/handlers/presence.js +3 -99
- package/dist/handlers/project.js +28 -182
- package/dist/handlers/prompts.js +6 -157
- package/dist/handlers/quest.js +4 -224
- package/dist/handlers/recall.js +11 -218
- package/dist/handlers/registry.js +1 -167
- package/dist/handlers/resources.js +1 -288
- package/dist/handlers/review.js +11 -74
- package/dist/handlers/run.js +17 -487
- package/dist/handlers/search.js +15 -326
- package/dist/handlers/session.js +28 -615
- package/dist/handlers/share.js +8 -184
- package/dist/handlers/shims.js +1 -464
- package/dist/handlers/skill.js +67 -449
- package/dist/handlers/survivors.js +1 -120
- package/dist/handlers/symbols.js +8 -109
- package/dist/handlers/syncops.js +4 -302
- package/dist/handlers/types.js +1 -27
- package/dist/harvest.js +5 -191
- package/dist/hours.js +7 -156
- package/dist/http-auth.js +3 -321
- package/dist/http-fast.js +21 -1137
- package/dist/icons.js +1 -47
- package/dist/index.js +2 -924
- package/dist/indexer.js +4 -145
- package/dist/intelligence.js +31 -261
- package/dist/internal-dispatch.js +3 -212
- package/dist/keyset.js +1 -110
- package/dist/knowledge-graph.js +12 -176
- package/dist/license.d.ts +11 -0
- package/dist/license.d.ts.map +1 -1
- package/dist/license.js +2 -414
- package/dist/license.js.map +1 -1
- package/dist/logger.js +2 -199
- package/dist/maintenance.js +2 -148
- package/dist/mcp-client.js +6 -262
- package/dist/memory-artifacts.js +30 -449
- package/dist/migrate-prompt.js +2 -124
- package/dist/migrations.js +40 -655
- package/dist/performance.js +1 -228
- package/dist/presence.js +11 -140
- package/dist/priority-embed.js +5 -164
- package/dist/providers/embedding-provider.js +1 -196
- package/dist/readonly-gate.js +1 -29
- package/dist/rehydration.js +9 -157
- package/dist/reindex.js +1 -88
- package/dist/render-target.js +21 -514
- package/dist/render.js +4 -280
- package/dist/repl-guard.js +1 -173
- package/dist/replication-daemon-entrypoint.js +1 -31
- package/dist/replication-daemon.js +2 -262
- package/dist/resilience.js +1 -591
- package/dist/reverse-bridge.js +5 -360
- package/dist/security.js +1 -244
- package/dist/session-seen.js +3 -51
- package/dist/setup.js +1 -260
- package/dist/skill-author.js +5 -168
- package/dist/spec-kit.js +1 -191
- package/dist/sqlite-busy.js +1 -154
- package/dist/statusline.js +11 -315
- package/dist/sub-agent.js +13 -262
- package/dist/summarizer.js +13 -139
- package/dist/symbols.js +7 -283
- package/dist/sync.js +5 -359
- package/dist/tasks-dispatch.js +1 -84
- package/dist/tasks.js +1 -282
- package/dist/token-budget.js +1 -143
- package/dist/tool-analytics.js +7 -129
- package/dist/tool-annotations.js +1 -365
- package/dist/tool-manifest-v2.json +1 -1
- package/dist/tool-manifest.json +1 -1
- package/dist/tool-profiles.js +1 -75
- package/dist/trace-harvest.js +6 -244
- package/dist/types.js +1 -30
- package/dist/ui-dashboard.js +41 -50
- package/dist/ulid.js +1 -81
- package/dist/validate.js +1 -129
- package/dist/vault.js +1 -534
- package/dist/vectors.js +3 -184
- package/dist/version-check.js +4 -136
- package/dist/visibility.js +19 -155
- package/dist/wyrm-cli.js +98 -2451
- package/dist/wyrm-cli.js.map +1 -1
- package/dist/wyrm-guard.js +14 -424
- package/dist/wyrm-loop.js +3 -150
- package/dist/wyrm-manifest.json +1 -1
- package/dist/wyrm-statusline-daemon.js +1 -11
- package/dist/wyrm-statusline.js +4 -56
- package/dist/wyrm-ui.js +9 -77
- package/package.json +4 -2
|
@@ -1,120 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* v7 F3 (T021/T022) — the 7.0 SURVIVOR set (spec FR-4): the frozen advertised
|
|
3
|
-
* surface.
|
|
4
|
-
*
|
|
5
|
-
* The frozen ~31-verb surface at 7.0.0 is: the hot-path singles that keep
|
|
6
|
-
* their first-class 6.x names verbatim (§6: "every existing CLAUDE.md
|
|
7
|
-
* contract keeps working unmodified"), the action-param nouns whose 6.x name
|
|
8
|
-
* already IS the noun, and the T022 noun shims (src/handlers/shims.ts).
|
|
9
|
-
*
|
|
10
|
-
* The alias spine generator (scripts/gen-alias-spine.mjs) computes the alias
|
|
11
|
-
* keyset as
|
|
12
|
-
*
|
|
13
|
-
* aliases = live ListTools names − SURVIVOR_TOOLS
|
|
14
|
-
*
|
|
15
|
-
* and CI (tests/alias-spine.test.ts) asserts that equation against the REAL
|
|
16
|
-
* booted server — never a prose list (spec FR-4; the panel itself misnamed
|
|
17
|
-
* the orchestration trio, which is why generation from the live wire is law).
|
|
18
|
-
* The CI test also asserts SURVIVOR_TOOLS ⊆ live names (a ghost survivor
|
|
19
|
-
* would silently corrupt the subtraction) — the shims are advertised, so the
|
|
20
|
-
* equation needs no special-casing.
|
|
21
|
-
*
|
|
22
|
-
* T022 reconciliation (recorded deviation): spec FR-4 as approved enumerated
|
|
23
|
-
* 15 singles + 19 nouns = 34 advertised names, conflicting with its own §7
|
|
24
|
-
* criterion 1 ("default ListTools advertises ≤32 tools", version-pinned).
|
|
25
|
-
* The T032 findings pass amended the same reconciliation INTO spec.md FR-4,
|
|
26
|
-
* so the spec is again the single source of truth for the frozen surface
|
|
27
|
-
* (tests/hygiene-security-floor.test.ts locks the amendment in place).
|
|
28
|
-
* Two same-domain folds close the gap without losing any advertised
|
|
29
|
-
* capability:
|
|
30
|
-
* - wyrm_entity_graph → wyrm_entity action=graph (demoted from the T021
|
|
31
|
-
* survivor list to an alias; its 6.x case is untouched);
|
|
32
|
-
* - the events noun → wyrm_replication action=publish|since|subscribe|
|
|
33
|
-
* replicate (the Live-Memory event mesh IS the replication domain).
|
|
34
|
-
* Surface today: 19 + 12 shims + wyrm_run (T027, V7_NEW_TOOL_NAMES) = 32 —
|
|
35
|
-
* exactly at the pin.
|
|
36
|
-
*
|
|
37
|
-
* @copyright 2026 Ghost Protocol (Pvt) Ltd.
|
|
38
|
-
* @license AGPL-3.0-or-later
|
|
39
|
-
*/
|
|
40
|
-
import { SHIM_TOOL_NAMES } from './shims.js';
|
|
41
|
-
/**
|
|
42
|
-
* Survivors whose names exist on the 6.x (pre-shim) surface.
|
|
43
|
-
*
|
|
44
|
-
* Hot-path singles (spec FR-4 / §6 frozen names):
|
|
45
|
-
* buddy (the well-known Buddy Protocol name — wyrm_buddy folds INTO it,
|
|
46
|
-
* T021), wyrm_act, wyrm_call_external, wyrm_capabilities (retires to a
|
|
47
|
-
* resource in 7.1 once the fallback is proven), wyrm_capture,
|
|
48
|
-
* wyrm_context_build, wyrm_decided_because, wyrm_failure_check,
|
|
49
|
-
* wyrm_failure_record, wyrm_recall, wyrm_review, wyrm_search,
|
|
50
|
-
* wyrm_session_prime, wyrm_truth_get, wyrm_truth_set.
|
|
51
|
-
*
|
|
52
|
-
* Action-param nouns whose 6.x name IS already the noun:
|
|
53
|
-
* wyrm_maintenance, wyrm_replication (absorbing sync_conflicts/sync_resolve
|
|
54
|
-
* + the events quartet, T022), wyrm_share, wyrm_stats.
|
|
55
|
-
* (wyrm_entity_graph was in this list at T021; T022 folded it into
|
|
56
|
-
* wyrm_entity action=graph — see the header deviation.)
|
|
57
|
-
*/
|
|
58
|
-
export const LEGACY_SURVIVOR_TOOLS = new Set([
|
|
59
|
-
'buddy',
|
|
60
|
-
'wyrm_act',
|
|
61
|
-
'wyrm_call_external',
|
|
62
|
-
'wyrm_capabilities',
|
|
63
|
-
'wyrm_capture',
|
|
64
|
-
'wyrm_context_build',
|
|
65
|
-
'wyrm_decided_because',
|
|
66
|
-
'wyrm_failure_check',
|
|
67
|
-
'wyrm_failure_record',
|
|
68
|
-
'wyrm_maintenance',
|
|
69
|
-
'wyrm_recall',
|
|
70
|
-
'wyrm_replication',
|
|
71
|
-
'wyrm_review',
|
|
72
|
-
'wyrm_search',
|
|
73
|
-
'wyrm_session_prime',
|
|
74
|
-
'wyrm_share',
|
|
75
|
-
'wyrm_stats',
|
|
76
|
-
'wyrm_truth_get',
|
|
77
|
-
'wyrm_truth_set',
|
|
78
|
-
]);
|
|
79
|
-
/**
|
|
80
|
-
* v7 F3 (T027): NEW first-class 7.0 tools — neither 6.x names (so never rows
|
|
81
|
-
* in the 137-name disposition table, and excluded from the 6.x-name corpora)
|
|
82
|
-
* nor shims (they have real ToolSpec handlers in the registry, not
|
|
83
|
-
* resolveShimCall translations). Every count formula that was
|
|
84
|
-
* `WYRM_TOOL_COUNT + SHIM_TOOL_NAMES.length` is now `+ V7_NEW_TOOL_NAMES
|
|
85
|
-
* .length` too. wyrm_run (spec FR-5, the run loop) is the 32nd survivor —
|
|
86
|
-
* the surface lands exactly at the spec §7 criterion-1 pin.
|
|
87
|
-
*/
|
|
88
|
-
export const V7_NEW_TOOL_NAMES = ['wyrm_run'];
|
|
89
|
-
/**
|
|
90
|
-
* v7 F4 (T040): NEW 7.x tools that are NOT advertised — like V7_NEW_TOOL_NAMES
|
|
91
|
-
* they are neither 6.x names (excluded from the 137-name disposition table +
|
|
92
|
-
* the 6.x-name corpora) nor shims, BUT they are NOT survivors: they live as
|
|
93
|
-
* HIDDEN ALIASES (callable, never on the default ListTools), reached only via a
|
|
94
|
-
* survivor's mode/action. wyrm_capture_trace is reached via
|
|
95
|
-
* wyrm_capture({ mode: 'trace' }) — the same shape wyrm_auto_capture (mode=
|
|
96
|
-
* extract) and wyrm_harvest (mode=artifacts) take, except those were 6.x names
|
|
97
|
-
* so they sit in the legacy corpus; this one is brand-new, so it must be
|
|
98
|
-
* subtracted from the 6.x corpus the same way V7_NEW is, yet stay OUT of
|
|
99
|
-
* SURVIVOR_TOOLS so the §7 criterion-1 ≤32 advertised pin holds.
|
|
100
|
-
*/
|
|
101
|
-
export const V7_HIDDEN_TOOL_NAMES = ['wyrm_capture_trace'];
|
|
102
|
-
/** v7 additions over the 6.x set (advertised survivors + hidden aliases) —
|
|
103
|
-
* the subtraction that re-derives the frozen 137-name 6.x corpus from the
|
|
104
|
-
* live wire (tests/alias-leak.test.ts, scripts/gen-*). */
|
|
105
|
-
export const V7_ADDED_TOOL_NAMES = [...V7_NEW_TOOL_NAMES, ...V7_HIDDEN_TOOL_NAMES];
|
|
106
|
-
/**
|
|
107
|
-
* THE frozen advertised surface (default ListTools): legacy-named survivors +
|
|
108
|
-
* the T022 noun shims + the T027 first-class v7 tools. Everything else is a
|
|
109
|
-
* hidden alias (callable, never advertised) or a CLI exile.
|
|
110
|
-
* NOTE: V7_HIDDEN_TOOL_NAMES are deliberately NOT here — they are hidden
|
|
111
|
-
* aliases, reached via a survivor's mode (the ≤32 advertised pin holds).
|
|
112
|
-
*/
|
|
113
|
-
export const SURVIVOR_TOOLS = new Set([
|
|
114
|
-
...LEGACY_SURVIVOR_TOOLS,
|
|
115
|
-
...SHIM_TOOL_NAMES,
|
|
116
|
-
...V7_NEW_TOOL_NAMES,
|
|
117
|
-
]);
|
|
118
|
-
/** Sorted array view (codepoint order) for deterministic iteration. */
|
|
119
|
-
export const SURVIVOR_NAMES = [...SURVIVOR_TOOLS].sort();
|
|
120
|
-
//# sourceMappingURL=survivors.js.map
|
|
1
|
+
import{SHIM_TOOL_NAMES as _}from"./shims.js";const e=new Set(["buddy","wyrm_act","wyrm_call_external","wyrm_capabilities","wyrm_capture","wyrm_context_build","wyrm_decided_because","wyrm_failure_check","wyrm_failure_record","wyrm_maintenance","wyrm_recall","wyrm_replication","wyrm_review","wyrm_search","wyrm_session_prime","wyrm_share","wyrm_stats","wyrm_truth_get","wyrm_truth_set"]),r=["wyrm_run"],t=["wyrm_capture_trace"],c=[...r,...t],m=new Set([...e,..._,...r]),y=[...m].sort();export{e as LEGACY_SURVIVOR_TOOLS,y as SURVIVOR_NAMES,m as SURVIVOR_TOOLS,c as V7_ADDED_TOOL_NAMES,t as V7_HIDDEN_TOOL_NAMES,r as V7_NEW_TOOL_NAMES};
|
package/dist/handlers/symbols.js
CHANGED
|
@@ -1,109 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
description: "Scan a project for function/class/type/interface definitions across TS/JS/Python/Rust/Go/PHP/Ruby and index them. Idempotent — clears the project's symbols before re-indexing.",
|
|
10
|
-
inputSchema: {
|
|
11
|
-
type: "object",
|
|
12
|
-
properties: {
|
|
13
|
-
projectPath: { type: "string", description: "Project root to scan" },
|
|
14
|
-
},
|
|
15
|
-
required: ["projectPath"],
|
|
16
|
-
},
|
|
17
|
-
annotations: TOOL_ANNOTATIONS["wyrm_symbol_index"],
|
|
18
|
-
aliases: [],
|
|
19
|
-
handler: async (args, ctx) => {
|
|
20
|
-
const { db, symbols } = ctx;
|
|
21
|
-
const { projectPath } = args;
|
|
22
|
-
const project = db.getProject(projectPath);
|
|
23
|
-
if (!project)
|
|
24
|
-
return { content: [{ type: "text", text: "Project not found" }], isError: true };
|
|
25
|
-
const result = symbols.indexProject(project.id, project.path);
|
|
26
|
-
return { content: [{ type: "text", text: ` Indexed ${result.symbols} symbol(s) across ${result.files} file(s) in ${project.name}.` }] };
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
name: "wyrm_symbol_search",
|
|
31
|
-
description: "Look up a symbol across all (or one) project. Cursor's workspace-symbols stops at the repo boundary; this doesn't.",
|
|
32
|
-
inputSchema: {
|
|
33
|
-
type: "object",
|
|
34
|
-
properties: {
|
|
35
|
-
symbol: { type: "string", description: "Name to search for" },
|
|
36
|
-
projectPath: { type: "string", description: "Scope to one project (optional)" },
|
|
37
|
-
kind: { type: "string", enum: ["function", "class", "type", "interface", "const", "export"], description: "Filter by kind" },
|
|
38
|
-
language: { type: "string", description: "Filter by language: ts/tsx/js/jsx/py/rs/go/php/rb" },
|
|
39
|
-
exact: { type: "boolean", description: "Exact match (default: substring)" },
|
|
40
|
-
limit: { type: "number", description: "Max results (default 50)" },
|
|
41
|
-
},
|
|
42
|
-
required: ["symbol"],
|
|
43
|
-
},
|
|
44
|
-
annotations: TOOL_ANNOTATIONS["wyrm_symbol_search"],
|
|
45
|
-
aliases: [],
|
|
46
|
-
handler: async (args, ctx) => {
|
|
47
|
-
const { db, symbols } = ctx;
|
|
48
|
-
const { symbol, projectPath, kind, language, exact, limit } = args;
|
|
49
|
-
const project = projectPath ? db.getProject(projectPath) : null;
|
|
50
|
-
const rows = symbols.search(symbol, {
|
|
51
|
-
projectId: project?.id,
|
|
52
|
-
kind, language, exact,
|
|
53
|
-
limit: Math.min(Math.max(1, limit ?? 50), 500),
|
|
54
|
-
});
|
|
55
|
-
if (rows.length === 0) {
|
|
56
|
-
return { content: [{ type: "text", text: ` No symbols match "${symbol}".` }] };
|
|
57
|
-
}
|
|
58
|
-
const text = rows.map(r => `• [${r.language}] ${r.kind} ${r.symbol} — ${r.file_path}:${r.line}\n ${r.signature ?? ''}`).join('\n');
|
|
59
|
-
return { content: [{ type: "text", text: ` ${rows.length} match(es):\n${text}` }] };
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
name: "wyrm_symbol_callers",
|
|
64
|
-
description: "Best-effort caller graph — files in the project (or workspace) whose indexed signature mentions this symbol. Approximate; for a true caller graph use LSP.",
|
|
65
|
-
inputSchema: {
|
|
66
|
-
type: "object",
|
|
67
|
-
properties: {
|
|
68
|
-
symbol: { type: "string" },
|
|
69
|
-
projectPath: { type: "string", description: "Scope to one project (optional)" },
|
|
70
|
-
},
|
|
71
|
-
required: ["symbol"],
|
|
72
|
-
},
|
|
73
|
-
annotations: TOOL_ANNOTATIONS["wyrm_symbol_callers"],
|
|
74
|
-
aliases: [],
|
|
75
|
-
handler: async (args, ctx) => {
|
|
76
|
-
const { db, symbols } = ctx;
|
|
77
|
-
const { symbol, projectPath } = args;
|
|
78
|
-
const project = projectPath ? db.getProject(projectPath) : null;
|
|
79
|
-
const rows = symbols.callers(symbol, project?.id);
|
|
80
|
-
if (rows.length === 0) {
|
|
81
|
-
return { content: [{ type: "text", text: ` No indexed callers for "${symbol}".` }] };
|
|
82
|
-
}
|
|
83
|
-
const text = rows.slice(0, 50).map(r => `• ${r.file_path}:${r.line} — ${r.signature?.slice(0, 100) ?? ''}`).join('\n');
|
|
84
|
-
return { content: [{ type: "text", text: ` ${rows.length} caller candidate(s) (showing first 50):\n${text}` }] };
|
|
85
|
-
},
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
name: "wyrm_symbol_stats",
|
|
89
|
-
description: "Symbol-index stats — total count, per-language and per-kind breakdown. Project-scoped if projectPath given, else workspace-wide.",
|
|
90
|
-
inputSchema: {
|
|
91
|
-
type: "object",
|
|
92
|
-
properties: {
|
|
93
|
-
projectPath: { type: "string", description: "Scope to one project (optional)" },
|
|
94
|
-
},
|
|
95
|
-
},
|
|
96
|
-
annotations: TOOL_ANNOTATIONS["wyrm_symbol_stats"],
|
|
97
|
-
aliases: [],
|
|
98
|
-
handler: async (args, ctx) => {
|
|
99
|
-
const { db, symbols } = ctx;
|
|
100
|
-
const { projectPath } = args;
|
|
101
|
-
const project = projectPath ? db.getProject(projectPath) : null;
|
|
102
|
-
const s = symbols.stats(project?.id);
|
|
103
|
-
const langs = Object.entries(s.by_language).map(([k, v]) => `${k}: ${v}`).join(', ');
|
|
104
|
-
const kinds = Object.entries(s.by_kind).map(([k, v]) => `${k}: ${v}`).join(', ');
|
|
105
|
-
return { content: [{ type: "text", text: ` Symbols indexed: ${s.total}\nLanguages: ${langs || '—'}\nKinds: ${kinds || '—'}` }] };
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
];
|
|
109
|
-
//# sourceMappingURL=symbols.js.map
|
|
1
|
+
import{TOOL_ANNOTATIONS as m}from"../tool-annotations.js";const u=[{name:"wyrm_symbol_index",description:"Scan a project for function/class/type/interface definitions across TS/JS/Python/Rust/Go/PHP/Ruby and index them. Idempotent \u2014 clears the project's symbols before re-indexing.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Project root to scan"}},required:["projectPath"]},annotations:m.wyrm_symbol_index,aliases:[],handler:async(s,r)=>{const{db:i,symbols:a}=r,{projectPath:e}=s,t=i.getProject(e);if(!t)return{content:[{type:"text",text:"Project not found"}],isError:!0};const o=a.indexProject(t.id,t.path);return{content:[{type:"text",text:`\u{F115D} Indexed ${o.symbols} symbol(s) across ${o.files} file(s) in ${t.name}.`}]}}},{name:"wyrm_symbol_search",description:"Look up a symbol across all (or one) project. Cursor's workspace-symbols stops at the repo boundary; this doesn't.",inputSchema:{type:"object",properties:{symbol:{type:"string",description:"Name to search for"},projectPath:{type:"string",description:"Scope to one project (optional)"},kind:{type:"string",enum:["function","class","type","interface","const","export"],description:"Filter by kind"},language:{type:"string",description:"Filter by language: ts/tsx/js/jsx/py/rs/go/php/rb"},exact:{type:"boolean",description:"Exact match (default: substring)"},limit:{type:"number",description:"Max results (default 50)"}},required:["symbol"]},annotations:m.wyrm_symbol_search,aliases:[],handler:async(s,r)=>{const{db:i,symbols:a}=r,{symbol:e,projectPath:t,kind:o,language:c,exact:l,limit:n}=s,y=t?i.getProject(t):null,d=a.search(e,{projectId:y?.id,kind:o,language:c,exact:l,limit:Math.min(Math.max(1,n??50),500)});if(d.length===0)return{content:[{type:"text",text:`\u{F115D} No symbols match "${e}".`}]};const b=d.map(p=>`\u2022 [${p.language}] ${p.kind} ${p.symbol} \u2014 ${p.file_path}:${p.line}
|
|
2
|
+
${p.signature??""}`).join(`
|
|
3
|
+
`);return{content:[{type:"text",text:`\u{F115D} ${d.length} match(es):
|
|
4
|
+
${b}`}]}}},{name:"wyrm_symbol_callers",description:"Best-effort caller graph \u2014 files in the project (or workspace) whose indexed signature mentions this symbol. Approximate; for a true caller graph use LSP.",inputSchema:{type:"object",properties:{symbol:{type:"string"},projectPath:{type:"string",description:"Scope to one project (optional)"}},required:["symbol"]},annotations:m.wyrm_symbol_callers,aliases:[],handler:async(s,r)=>{const{db:i,symbols:a}=r,{symbol:e,projectPath:t}=s,o=t?i.getProject(t):null,c=a.callers(e,o?.id);if(c.length===0)return{content:[{type:"text",text:`\u{F115D} No indexed callers for "${e}".`}]};const l=c.slice(0,50).map(n=>`\u2022 ${n.file_path}:${n.line} \u2014 ${n.signature?.slice(0,100)??""}`).join(`
|
|
5
|
+
`);return{content:[{type:"text",text:`\u{F115D} ${c.length} caller candidate(s) (showing first 50):
|
|
6
|
+
${l}`}]}}},{name:"wyrm_symbol_stats",description:"Symbol-index stats \u2014 total count, per-language and per-kind breakdown. Project-scoped if projectPath given, else workspace-wide.",inputSchema:{type:"object",properties:{projectPath:{type:"string",description:"Scope to one project (optional)"}}},annotations:m.wyrm_symbol_stats,aliases:[],handler:async(s,r)=>{const{db:i,symbols:a}=r,{projectPath:e}=s,t=e?i.getProject(e):null,o=a.stats(t?.id),c=Object.entries(o.by_language).map(([n,y])=>`${n}: ${y}`).join(", "),l=Object.entries(o.by_kind).map(([n,y])=>`${n}: ${y}`).join(", ");return{content:[{type:"text",text:`\u{F115D} Symbols indexed: ${o.total}
|
|
7
|
+
Languages: ${c||"\u2014"}
|
|
8
|
+
Kinds: ${l||"\u2014"}`}]}}}];export{u as symbolsToolSpecs};
|
package/dist/handlers/syncops.js
CHANGED
|
@@ -1,310 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
* @copyright 2026 Ghost Protocol (Pvt) Ltd.
|
|
3
|
-
* @license AGPL-3.0-or-later
|
|
4
|
-
*/
|
|
5
|
-
import { TOOL_ANNOTATIONS } from "../tool-annotations.js";
|
|
6
|
-
import { logger } from "../logger.js";
|
|
7
|
-
import { asInt } from "../validate.js";
|
|
8
|
-
import { createCipheriv, createDecipheriv, pbkdf2Sync, randomBytes } from "crypto";
|
|
9
|
-
import { chmodSync, copyFileSync, existsSync as fsExistsSync, readFileSync, unlinkSync, writeFileSync } from "fs";
|
|
10
|
-
import { homedir } from "os";
|
|
11
|
-
import { join as pathJoin } from "path";
|
|
12
|
-
export const syncopsToolSpecs = [
|
|
13
|
-
{
|
|
14
|
-
name: "wyrm_prune",
|
|
15
|
-
description: "Prune low-confidence, stale memory artifacts. Dry-run by default — use dry_run:false with confirm_ids to actually delete. Never deletes artifacts in the review queue.",
|
|
16
|
-
inputSchema: {
|
|
17
|
-
type: "object",
|
|
18
|
-
properties: {
|
|
19
|
-
project_id: { type: "number", description: "Project ID (optional, all projects if omitted)" },
|
|
20
|
-
min_confidence: { type: "number", description: "Prune artifacts below this confidence (default: 0.3)" },
|
|
21
|
-
older_than_days: { type: "number", description: "Last accessed more than N days ago (default: 90)" },
|
|
22
|
-
types: { type: "array", items: { type: "string" }, description: "Filter by artifact_type (optional)" },
|
|
23
|
-
dry_run: { type: "boolean", description: "If true (default), return candidates without deleting. Set false to delete." },
|
|
24
|
-
confirm_ids: { type: "array", items: { type: "number" }, description: "Required when dry_run:false — only IDs in this list will be deleted" },
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
annotations: TOOL_ANNOTATIONS["wyrm_prune"],
|
|
28
|
-
aliases: [],
|
|
29
|
-
handler: async (args, ctx) => {
|
|
30
|
-
const { db } = ctx;
|
|
31
|
-
const a = args || {};
|
|
32
|
-
const { project_id: pruneProjectId, min_confidence: pruneMinConf = 0.3, older_than_days: pruneOlderDays = 90, types: pruneTypes, dry_run: pruneDryRun = true, confirm_ids: pruneConfirmIds, } = args;
|
|
33
|
-
const rawDbPrune = db.getDatabase();
|
|
34
|
-
// SECURITY: project_id must never be string-interpolated into SQL —
|
|
35
|
-
// validate at the boundary (throws ValidationError → clean isError), then bind.
|
|
36
|
-
const safePruneProjectId = asInt('project_id', pruneProjectId);
|
|
37
|
-
const projectClausePrune = safePruneProjectId !== undefined ? 'AND project_id = ?' : '';
|
|
38
|
-
const projectParamsPrune = safePruneProjectId !== undefined ? [safePruneProjectId] : [];
|
|
39
|
-
const typeClausePrune = pruneTypes && pruneTypes.length > 0
|
|
40
|
-
? `AND kind IN (${pruneTypes.map(() => '?').join(',')})`
|
|
41
|
-
: '';
|
|
42
|
-
const typeParamsPrune = pruneTypes ?? [];
|
|
43
|
-
const candidateRows = rawDbPrune.prepare(`
|
|
1
|
+
import{TOOL_ANNOTATIONS as E}from"../tool-annotations.js";import{logger as R}from"../logger.js";import{asInt as B}from"../validate.js";import{createCipheriv as L,createDecipheriv as F,pbkdf2Sync as O,randomBytes as C}from"crypto";import{chmodSync as U,copyFileSync as j,existsSync as x,readFileSync as M,unlinkSync as c,writeFileSync as I}from"fs";import{homedir as k}from"os";import{join as D}from"path";const K=[{name:"wyrm_prune",description:"Prune low-confidence, stale memory artifacts. Dry-run by default \u2014 use dry_run:false with confirm_ids to actually delete. Never deletes artifacts in the review queue.",inputSchema:{type:"object",properties:{project_id:{type:"number",description:"Project ID (optional, all projects if omitted)"},min_confidence:{type:"number",description:"Prune artifacts below this confidence (default: 0.3)"},older_than_days:{type:"number",description:"Last accessed more than N days ago (default: 90)"},types:{type:"array",items:{type:"string"},description:"Filter by artifact_type (optional)"},dry_run:{type:"boolean",description:"If true (default), return candidates without deleting. Set false to delete."},confirm_ids:{type:"array",items:{type:"number"},description:"Required when dry_run:false \u2014 only IDs in this list will be deleted"}}},annotations:E.wyrm_prune,aliases:[],handler:async(f,b)=>{const{db:i}=b,N=f||{},{project_id:s,min_confidence:g=.3,older_than_days:p=90,types:d,dry_run:e=!0,confirm_ids:t}=f,y=i.getDatabase(),o=B("project_id",s),h=o!==void 0?"AND project_id = ?":"",_=o!==void 0?[o]:[],w=d&&d.length>0?`AND kind IN (${d.map(()=>"?").join(",")})`:"",u=d??[],m=y.prepare(`
|
|
44
2
|
SELECT id, project_id, kind, problem, confidence, last_accessed_at, access_count
|
|
45
3
|
FROM memory_artifacts
|
|
46
4
|
WHERE confidence < ?
|
|
47
5
|
AND (last_accessed_at IS NULL OR last_accessed_at < datetime('now', ?))
|
|
48
6
|
AND needs_review = 0
|
|
49
7
|
AND supersedes_id IS NULL
|
|
50
|
-
${
|
|
51
|
-
${
|
|
8
|
+
${h}
|
|
9
|
+
${w}
|
|
52
10
|
ORDER BY confidence ASC, last_accessed_at ASC
|
|
53
11
|
LIMIT 500
|
|
54
|
-
`).all(
|
|
55
|
-
if (pruneDryRun) {
|
|
56
|
-
return {
|
|
57
|
-
content: [{
|
|
58
|
-
type: "text",
|
|
59
|
-
text: JSON.stringify({
|
|
60
|
-
dry_run: true,
|
|
61
|
-
count: candidateRows.length,
|
|
62
|
-
candidates: candidateRows.map(r => ({
|
|
63
|
-
id: r.id,
|
|
64
|
-
kind: r.kind,
|
|
65
|
-
problem: r.problem.slice(0, 100),
|
|
66
|
-
confidence: r.confidence,
|
|
67
|
-
last_accessed_at: r.last_accessed_at,
|
|
68
|
-
})),
|
|
69
|
-
}),
|
|
70
|
-
}],
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
// dry_run: false — require confirm_ids
|
|
74
|
-
if (!pruneConfirmIds || pruneConfirmIds.length === 0) {
|
|
75
|
-
return {
|
|
76
|
-
content: [{ type: "text", text: ` **Prune**: dry_run:false requires confirm_ids — provide the IDs from a dry-run to confirm deletion.` }],
|
|
77
|
-
isError: true,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
const candidateIds = new Set(candidateRows.map(r => r.id));
|
|
81
|
-
const toDelete = pruneConfirmIds.filter(id => candidateIds.has(id));
|
|
82
|
-
if (toDelete.length === 0) {
|
|
83
|
-
return {
|
|
84
|
-
content: [{ type: "text", text: ` **Prune**: No matching IDs to delete (confirm_ids not in candidate set).` }],
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
const placeholdersPrune = toDelete.map(() => '?').join(',');
|
|
88
|
-
const deleteResult = rawDbPrune.prepare(`DELETE FROM memory_artifacts WHERE id IN (${placeholdersPrune}) AND needs_review = 0`).run(...toDelete);
|
|
89
|
-
return {
|
|
90
|
-
content: [{
|
|
91
|
-
type: "text",
|
|
92
|
-
text: JSON.stringify({ deleted: deleteResult.changes, ids: toDelete }),
|
|
93
|
-
}],
|
|
94
|
-
};
|
|
95
|
-
},
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
name: "wyrm_sync_export",
|
|
99
|
-
description: "Export an AES-256-GCM encrypted snapshot of the Wyrm database for cross-device sync. Passphrase from WYRM_SYNC_PASSPHRASE env var.",
|
|
100
|
-
inputSchema: {
|
|
101
|
-
type: "object",
|
|
102
|
-
properties: {
|
|
103
|
-
output_path: { type: "string", description: "File path to write the encrypted snapshot" },
|
|
104
|
-
description: { type: "string", description: "Optional description of this export" },
|
|
105
|
-
},
|
|
106
|
-
required: ["output_path"],
|
|
107
|
-
},
|
|
108
|
-
annotations: TOOL_ANNOTATIONS["wyrm_sync_export"],
|
|
109
|
-
aliases: [],
|
|
110
|
-
handler: async (args, ctx) => {
|
|
111
|
-
const { db, server } = ctx;
|
|
112
|
-
const { output_path: exportPath, description: exportDesc } = args;
|
|
113
|
-
const passphrase = process.env.WYRM_SYNC_PASSPHRASE;
|
|
114
|
-
if (!passphrase) {
|
|
115
|
-
return {
|
|
116
|
-
content: [{ type: "text", text: ` **Sync Export**: Set WYRM_SYNC_PASSPHRASE env var to enable encrypted export.` }],
|
|
117
|
-
isError: true,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
const dbPath = db.getDatabasePath();
|
|
121
|
-
const wyrmDir = pathJoin(homedir(), '.wyrm');
|
|
122
|
-
const tempDbPath = pathJoin(wyrmDir, 'wyrm_sync_export_temp.db');
|
|
123
|
-
try {
|
|
124
|
-
// WAL-safe snapshot via VACUUM INTO
|
|
125
|
-
const rawDbExport = db.getDatabase();
|
|
126
|
-
if (fsExistsSync(tempDbPath))
|
|
127
|
-
unlinkSync(tempDbPath);
|
|
128
|
-
rawDbExport.prepare(`VACUUM INTO ?`).run(tempDbPath);
|
|
129
|
-
// Read snapshot
|
|
130
|
-
const plaintext = readFileSync(tempDbPath);
|
|
131
|
-
// Derive key
|
|
132
|
-
const salt = randomBytes(32);
|
|
133
|
-
const iv = randomBytes(16);
|
|
134
|
-
const key = pbkdf2Sync(passphrase, salt, 600000, 32, 'sha256');
|
|
135
|
-
// Encrypt
|
|
136
|
-
const cipher = createCipheriv('aes-256-gcm', key, iv);
|
|
137
|
-
const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
138
|
-
const authTag = cipher.getAuthTag();
|
|
139
|
-
// Binary format: WYRM(4) + version(1) + salt(32) + iv(16) + authTag(16) + data
|
|
140
|
-
const magic = Buffer.from('WYRM');
|
|
141
|
-
const version = Buffer.alloc(1);
|
|
142
|
-
version.writeUInt8(1, 0);
|
|
143
|
-
const output = Buffer.concat([magic, version, salt, iv, authTag, encrypted]);
|
|
144
|
-
writeFileSync(exportPath, output);
|
|
145
|
-
try {
|
|
146
|
-
chmodSync(exportPath, 0o600);
|
|
147
|
-
}
|
|
148
|
-
catch { /* non-fatal */ }
|
|
149
|
-
// Clean up temp
|
|
150
|
-
try {
|
|
151
|
-
unlinkSync(tempDbPath);
|
|
152
|
-
}
|
|
153
|
-
catch { /* non-fatal */ }
|
|
154
|
-
const sizeMb = (output.length / (1024 * 1024)).toFixed(2);
|
|
155
|
-
return {
|
|
156
|
-
content: [{
|
|
157
|
-
type: "text",
|
|
158
|
-
text: JSON.stringify({
|
|
159
|
-
success: true,
|
|
160
|
-
path: exportPath,
|
|
161
|
-
size_mb: parseFloat(sizeMb),
|
|
162
|
-
exported_at: new Date().toISOString(),
|
|
163
|
-
description: exportDesc ?? null,
|
|
164
|
-
}),
|
|
165
|
-
}],
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
catch (err) {
|
|
169
|
-
try {
|
|
170
|
-
if (fsExistsSync(tempDbPath))
|
|
171
|
-
unlinkSync(tempDbPath);
|
|
172
|
-
}
|
|
173
|
-
catch { /* clean up */ }
|
|
174
|
-
logger.error('Sync export failed', { error: err.message });
|
|
175
|
-
return {
|
|
176
|
-
content: [{ type: "text", text: ` **Sync Export** failed. Check server logs for details.` }],
|
|
177
|
-
isError: true,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
},
|
|
182
|
-
{
|
|
183
|
-
name: "wyrm_sync_import",
|
|
184
|
-
description: "Import an encrypted Wyrm snapshot. Use restore_mode:'preview' to inspect without touching the DB; 'restore' to replace current DB (backs up first).",
|
|
185
|
-
inputSchema: {
|
|
186
|
-
type: "object",
|
|
187
|
-
properties: {
|
|
188
|
-
input_path: { type: "string", description: "Path to the encrypted snapshot file" },
|
|
189
|
-
restore_mode: { type: "string", enum: ["preview", "restore"], description: "preview = show stats only; restore = replace DB (backs up first)" },
|
|
190
|
-
},
|
|
191
|
-
required: ["input_path", "restore_mode"],
|
|
192
|
-
},
|
|
193
|
-
annotations: TOOL_ANNOTATIONS["wyrm_sync_import"],
|
|
194
|
-
aliases: [],
|
|
195
|
-
handler: async (args, ctx) => {
|
|
196
|
-
const { db, server } = ctx;
|
|
197
|
-
const { input_path: importPath, restore_mode: restoreMode } = args;
|
|
198
|
-
const passphrase = process.env.WYRM_SYNC_PASSPHRASE;
|
|
199
|
-
if (!passphrase) {
|
|
200
|
-
return {
|
|
201
|
-
content: [{ type: "text", text: ` **Sync Import**: Set WYRM_SYNC_PASSPHRASE env var to enable encrypted import.` }],
|
|
202
|
-
isError: true,
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
const wyrmDirImport = pathJoin(homedir(), '.wyrm');
|
|
206
|
-
const tempImportPath = pathJoin(wyrmDirImport, 'wyrm_sync_import_temp.db');
|
|
207
|
-
try {
|
|
208
|
-
const fileData = readFileSync(importPath);
|
|
209
|
-
// Validate magic bytes
|
|
210
|
-
const magic = fileData.subarray(0, 4).toString('ascii');
|
|
211
|
-
if (magic !== 'WYRM') {
|
|
212
|
-
return { content: [{ type: "text", text: ` Invalid Wyrm snapshot file (bad magic bytes).` }], isError: true };
|
|
213
|
-
}
|
|
214
|
-
const version = fileData.readUInt8(4);
|
|
215
|
-
if (version !== 1) {
|
|
216
|
-
return { content: [{ type: "text", text: ` Unsupported snapshot version: ${version}` }], isError: true };
|
|
217
|
-
}
|
|
218
|
-
const salt = fileData.subarray(5, 37);
|
|
219
|
-
const iv = fileData.subarray(37, 53);
|
|
220
|
-
const authTag = fileData.subarray(53, 69);
|
|
221
|
-
const encrypted = fileData.subarray(69);
|
|
222
|
-
// Derive key & decrypt
|
|
223
|
-
const key = pbkdf2Sync(passphrase, salt, 600000, 32, 'sha256');
|
|
224
|
-
const decipher = createDecipheriv('aes-256-gcm', key, iv);
|
|
225
|
-
decipher.setAuthTag(authTag);
|
|
226
|
-
let decrypted;
|
|
227
|
-
try {
|
|
228
|
-
decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
229
|
-
}
|
|
230
|
-
catch {
|
|
231
|
-
return { content: [{ type: "text", text: ` Decryption failed — wrong passphrase or corrupted snapshot.` }], isError: true };
|
|
232
|
-
}
|
|
233
|
-
if (restoreMode === 'preview') {
|
|
234
|
-
// Open temp DB and return stats
|
|
235
|
-
if (fsExistsSync(tempImportPath))
|
|
236
|
-
unlinkSync(tempImportPath);
|
|
237
|
-
writeFileSync(tempImportPath, decrypted);
|
|
238
|
-
let previewStats = {};
|
|
239
|
-
try {
|
|
240
|
-
const BetterSQLite = (await import('better-sqlite3')).default;
|
|
241
|
-
const previewDb = new BetterSQLite(tempImportPath, { readonly: true });
|
|
242
|
-
const tables = ['projects', 'sessions', 'ground_truths', 'memory_artifacts', 'quests'];
|
|
243
|
-
for (const t of tables) {
|
|
244
|
-
try {
|
|
245
|
-
const row = previewDb.prepare(`SELECT COUNT(*) as n FROM ${t}`).get();
|
|
246
|
-
previewStats[t] = row.n;
|
|
247
|
-
}
|
|
248
|
-
catch {
|
|
249
|
-
previewStats[t] = 0;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
previewDb.close();
|
|
253
|
-
}
|
|
254
|
-
catch { /* stats optional */ }
|
|
255
|
-
try {
|
|
256
|
-
unlinkSync(tempImportPath);
|
|
257
|
-
}
|
|
258
|
-
catch { /* clean up */ }
|
|
259
|
-
return {
|
|
260
|
-
content: [{
|
|
261
|
-
type: "text",
|
|
262
|
-
text: JSON.stringify({ preview: true, stats: previewStats }),
|
|
263
|
-
}],
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
// restore mode
|
|
267
|
-
const dbPathImport = db.getDatabasePath();
|
|
268
|
-
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
269
|
-
const backupPath = `${dbPathImport}.backup.${timestamp}`;
|
|
270
|
-
// Backup current DB
|
|
271
|
-
copyFileSync(dbPathImport, backupPath);
|
|
272
|
-
// Write decrypted to temp
|
|
273
|
-
if (fsExistsSync(tempImportPath))
|
|
274
|
-
unlinkSync(tempImportPath);
|
|
275
|
-
writeFileSync(tempImportPath, decrypted);
|
|
276
|
-
// Close current DB connection, replace file, reopen
|
|
277
|
-
const rawDbImport = db.getDatabase();
|
|
278
|
-
rawDbImport.close();
|
|
279
|
-
copyFileSync(tempImportPath, dbPathImport);
|
|
280
|
-
try {
|
|
281
|
-
unlinkSync(tempImportPath);
|
|
282
|
-
}
|
|
283
|
-
catch { /* clean up */ }
|
|
284
|
-
return {
|
|
285
|
-
content: [{
|
|
286
|
-
type: "text",
|
|
287
|
-
text: JSON.stringify({
|
|
288
|
-
success: true,
|
|
289
|
-
restored_from: importPath,
|
|
290
|
-
backup_at: backupPath,
|
|
291
|
-
}),
|
|
292
|
-
}],
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
catch (err) {
|
|
296
|
-
try {
|
|
297
|
-
if (fsExistsSync(tempImportPath))
|
|
298
|
-
unlinkSync(tempImportPath);
|
|
299
|
-
}
|
|
300
|
-
catch { /* clean up */ }
|
|
301
|
-
logger.error('Sync import failed', { error: err.message });
|
|
302
|
-
return {
|
|
303
|
-
content: [{ type: "text", text: ` **Sync Import** failed. Check server logs for details.` }],
|
|
304
|
-
isError: true,
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
},
|
|
308
|
-
},
|
|
309
|
-
];
|
|
310
|
-
//# sourceMappingURL=syncops.js.map
|
|
12
|
+
`).all(g,`-${p} days`,..._,...u);if(e)return{content:[{type:"text",text:JSON.stringify({dry_run:!0,count:m.length,candidates:m.map(r=>({id:r.id,kind:r.kind,problem:r.problem.slice(0,100),confidence:r.confidence,last_accessed_at:r.last_accessed_at}))})}]};if(!t||t.length===0)return{content:[{type:"text",text:"\u{F115D} **Prune**: dry_run:false requires confirm_ids \u2014 provide the IDs from a dry-run to confirm deletion."}],isError:!0};const l=new Set(m.map(r=>r.id)),n=t.filter(r=>l.has(r));if(n.length===0)return{content:[{type:"text",text:"\u{F115D} **Prune**: No matching IDs to delete (confirm_ids not in candidate set)."}]};const a=n.map(()=>"?").join(","),S=y.prepare(`DELETE FROM memory_artifacts WHERE id IN (${a}) AND needs_review = 0`).run(...n);return{content:[{type:"text",text:JSON.stringify({deleted:S.changes,ids:n})}]}}},{name:"wyrm_sync_export",description:"Export an AES-256-GCM encrypted snapshot of the Wyrm database for cross-device sync. Passphrase from WYRM_SYNC_PASSPHRASE env var.",inputSchema:{type:"object",properties:{output_path:{type:"string",description:"File path to write the encrypted snapshot"},description:{type:"string",description:"Optional description of this export"}},required:["output_path"]},annotations:E.wyrm_sync_export,aliases:[],handler:async(f,b)=>{const{db:i,server:N}=b,{output_path:s,description:g}=f,p=process.env.WYRM_SYNC_PASSPHRASE;if(!p)return{content:[{type:"text",text:"\u{F115D} **Sync Export**: Set WYRM_SYNC_PASSPHRASE env var to enable encrypted export."}],isError:!0};const d=i.getDatabasePath(),e=D(k(),".wyrm"),t=D(e,"wyrm_sync_export_temp.db");try{const y=i.getDatabase();x(t)&&c(t),y.prepare("VACUUM INTO ?").run(t);const o=M(t),h=C(32),_=C(16),w=O(p,h,6e5,32,"sha256"),u=L("aes-256-gcm",w,_),m=Buffer.concat([u.update(o),u.final()]),l=u.getAuthTag(),n=Buffer.from("WYRM"),a=Buffer.alloc(1);a.writeUInt8(1,0);const S=Buffer.concat([n,a,h,_,l,m]);I(s,S);try{U(s,384)}catch{}try{c(t)}catch{}const r=(S.length/(1024*1024)).toFixed(2);return{content:[{type:"text",text:JSON.stringify({success:!0,path:s,size_mb:parseFloat(r),exported_at:new Date().toISOString(),description:g??null})}]}}catch(y){try{x(t)&&c(t)}catch{}return R.error("Sync export failed",{error:y.message}),{content:[{type:"text",text:"\u{F115D} **Sync Export** failed. Check server logs for details."}],isError:!0}}}},{name:"wyrm_sync_import",description:"Import an encrypted Wyrm snapshot. Use restore_mode:'preview' to inspect without touching the DB; 'restore' to replace current DB (backs up first).",inputSchema:{type:"object",properties:{input_path:{type:"string",description:"Path to the encrypted snapshot file"},restore_mode:{type:"string",enum:["preview","restore"],description:"preview = show stats only; restore = replace DB (backs up first)"}},required:["input_path","restore_mode"]},annotations:E.wyrm_sync_import,aliases:[],handler:async(f,b)=>{const{db:i,server:N}=b,{input_path:s,restore_mode:g}=f,p=process.env.WYRM_SYNC_PASSPHRASE;if(!p)return{content:[{type:"text",text:"\u{F115D} **Sync Import**: Set WYRM_SYNC_PASSPHRASE env var to enable encrypted import."}],isError:!0};const d=D(k(),".wyrm"),e=D(d,"wyrm_sync_import_temp.db");try{const t=M(s);if(t.subarray(0,4).toString("ascii")!=="WYRM")return{content:[{type:"text",text:"\u{F115D} Invalid Wyrm snapshot file (bad magic bytes)."}],isError:!0};const o=t.readUInt8(4);if(o!==1)return{content:[{type:"text",text:`\u{F115D} Unsupported snapshot version: ${o}`}],isError:!0};const h=t.subarray(5,37),_=t.subarray(37,53),w=t.subarray(53,69),u=t.subarray(69),m=O(p,h,6e5,32,"sha256"),l=F("aes-256-gcm",m,_);l.setAuthTag(w);let n;try{n=Buffer.concat([l.update(u),l.final()])}catch{return{content:[{type:"text",text:"\u{F115D} Decryption failed \u2014 wrong passphrase or corrupted snapshot."}],isError:!0}}if(g==="preview"){x(e)&&c(e),I(e,n);let P={};try{const T=(await import("better-sqlite3")).default,A=new T(e,{readonly:!0}),Y=["projects","sessions","ground_truths","memory_artifacts","quests"];for(const v of Y)try{const W=A.prepare(`SELECT COUNT(*) as n FROM ${v}`).get();P[v]=W.n}catch{P[v]=0}A.close()}catch{}try{c(e)}catch{}return{content:[{type:"text",text:JSON.stringify({preview:!0,stats:P})}]}}const a=i.getDatabasePath(),S=new Date().toISOString().replace(/[:.]/g,"-"),r=`${a}.backup.${S}`;j(a,r),x(e)&&c(e),I(e,n),i.getDatabase().close(),j(e,a);try{c(e)}catch{}return{content:[{type:"text",text:JSON.stringify({success:!0,restored_from:s,backup_at:r})}]}}catch(t){try{x(e)&&c(e)}catch{}return R.error("Sync import failed",{error:t.message}),{content:[{type:"text",text:"\u{F115D} **Sync Import** failed. Check server logs for details."}],isError:!0}}}}];export{K as syncopsToolSpecs};
|
package/dist/handlers/types.js
CHANGED
|
@@ -1,27 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Handler-module contract (v7 A2 — decompose the index.ts monolith;
|
|
3
|
-
* v7 F3 T018 — ToolSpec contract v2).
|
|
4
|
-
*
|
|
5
|
-
* Tool handlers are being lifted out of the one giant `switch (name)` in
|
|
6
|
-
* index.ts into per-domain modules behind a registry. Each handler is a pure
|
|
7
|
-
* function of (args, ctx) — no module-level closure — which makes it unit-
|
|
8
|
-
* testable and lets the boundary validator (v7 A1) wrap it uniformly.
|
|
9
|
-
*
|
|
10
|
-
* v7 F3 (T018) upgrades the contract from bare handlers to full ToolSpec
|
|
11
|
-
* modules: a domain exports `ToolSpec[]` carrying the COMPLETE tool identity
|
|
12
|
-
* ({name, description, inputSchema, outputSchema, annotations, examples,
|
|
13
|
-
* aliases, handler}), and the registry derives BOTH the dispatch map and the
|
|
14
|
-
* advertised ListTools entries from the same objects — definition and
|
|
15
|
-
* behavior cannot drift because they are one value.
|
|
16
|
-
*
|
|
17
|
-
* @copyright 2026 Ghost Protocol (Pvt) Ltd.
|
|
18
|
-
* @license AGPL-3.0-or-later
|
|
19
|
-
*/
|
|
20
|
-
/** Safely read the text of a content block — returns '' for a resource_link
|
|
21
|
-
* (which carries no text). v7 F4 (T034): lets the few call sites that read
|
|
22
|
-
* `content[0].text` survive the union widening without a cast (the text block
|
|
23
|
-
* is always content[0] by renderResult construction, but TS can't prove it). */
|
|
24
|
-
export function blockText(block) {
|
|
25
|
-
return block && 'text' in block ? block.text : '';
|
|
26
|
-
}
|
|
27
|
-
//# sourceMappingURL=types.js.map
|
|
1
|
+
function e(t){return t&&"text"in t?t.text:""}export{e as blockText};
|