linksee-memory 0.10.0 → 0.11.0

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.
@@ -23,6 +23,7 @@ import { confirmForget } from './elicitation.js';
23
23
  import { getTruthView, getDecisionDetail, resolveDrift } from '../lib/truth-engine.js';
24
24
  import { declareAnchor, setNodeFields } from '../lib/drift-anchors.js';
25
25
  import { getReinjectionFriction, setGateMode } from '../lib/guard.js';
26
+ import { whereAmI } from '../lib/map-view.js';
26
27
  const SERVER_VERSION = '0.10.0';
27
28
  const db = openDb();
28
29
  runMigrations(db);
@@ -158,6 +159,19 @@ const TOOLS = [
158
159
  },
159
160
  },
160
161
  },
162
+ {
163
+ name: 'where_am_i',
164
+ description: 'Locate the current topic on the Current Truth Map and report "you are here" + blast radius — the per-turn re-anchor.\n\nReturns the matching Map node(s) + journey stage (発見→…→拡張), the BLAST RADIUS (what becomes suspect if you change this — the must-stay-consistent-with / should-align-with / realizes dependents; e.g. editing the README implicates the LP), and the decision behind the node (linked anchor), if any.\n\nThree ways to call:\n• NO ARGS → auto-locates from the files you JUST edited this session (the zero-effort re-anchor — call it freely as you work).\n• query: "<topic>" → lexical locate by topic.\n• node_id: "<id>" → exact node.\n\nThis is how you avoid optimizing one node while silently breaking its neighbors (change the spec → npm/Docs/LP must move too). Matching is lexical (no embeddings).\n\nWHEN TO CALL:\n• Right after editing files — call with no args to see what you just touched + its blast radius.\n• When the topic shifts — re-anchor to the new node.\n• When the user asks "what does changing X affect?" / "where does this fit?"',
165
+ inputSchema: {
166
+ type: 'object',
167
+ properties: {
168
+ query: { type: 'string', description: 'Topic to locate (e.g. "changing the telemetry contract"). Omit to auto-locate from your recent edits.' },
169
+ node_id: { type: 'string', description: 'Exact Map node id, if known (e.g. "readme") — bypasses lexical match' },
170
+ project: { type: 'string', description: 'Map project slug (defaults to the most recently imported Map)' },
171
+ limit: { type: 'number', description: 'Max matches (default 3)' },
172
+ },
173
+ },
174
+ },
161
175
  {
162
176
  name: 'check_decision',
163
177
  description: 'Deep-dive into a specific decision/anchor — its state, premises, drift edges, and pending candidates.\n\nReturns the full context for one truth-map node: what was decided, why, what reality says, whether it\'s drifting, and what actions are pending.\n\nWHEN TO CALL:\n• When the user asks about a specific decision ("what happened with X?")\n• Before resolving a drift signal — understand the full picture first\n• When reviewing premises of a decision ("is this still true?")',
@@ -1238,6 +1252,34 @@ function handleDriftStatus(args) {
1238
1252
  counts: view.counts,
1239
1253
  });
1240
1254
  }
1255
+ function handleWhereAmI(args) {
1256
+ const res = whereAmI(db, {
1257
+ query: args?.query, node_id: args?.node_id, project: args?.project, limit: args?.limit,
1258
+ });
1259
+ if (res.matched.length === 0) {
1260
+ return JSON.stringify({
1261
+ ok: true, project: res.project, located: false,
1262
+ hint: res.project ? 'No node matched. Try a node id or a topical keyword.' : 'No Map imported yet — run linksee-memory-map.',
1263
+ });
1264
+ }
1265
+ const located = res.matched.map((m) => ({
1266
+ node: m.node.id,
1267
+ stage: m.stage_label,
1268
+ status: m.node.status,
1269
+ statement: m.node.statement,
1270
+ why: m.match_reason,
1271
+ // the whole point: what else moves if you touch this
1272
+ blast_radius: m.blast.map((b) => `${b.id} (${b.relation})`),
1273
+ decision: m.anchor ? `#${m.anchor.id}: ${m.anchor.statement}` : null,
1274
+ }));
1275
+ const top = res.matched[0];
1276
+ const youAreHere = `You are at "${top.node.id}"`
1277
+ + (top.stage_label ? ` in stage 「${top.stage_label}」` : '')
1278
+ + `. Touching it implicates ${top.blast.length} node(s)`
1279
+ + (top.blast.length ? `: ${top.blast.map((b) => b.id).join(', ')}` : ' (isolated)')
1280
+ + '.';
1281
+ return JSON.stringify({ ok: true, project: res.project, located: true, you_are_here: youAreHere, job: res.job, matched: located });
1282
+ }
1241
1283
  function handleCheckDecision(args) {
1242
1284
  if (!args?.anchor_id)
1243
1285
  throw new Error('anchor_id is required');
@@ -1635,6 +1677,9 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
1635
1677
  case 'drift_status':
1636
1678
  text = handleDriftStatus(args);
1637
1679
  break;
1680
+ case 'where_am_i':
1681
+ text = handleWhereAmI(args);
1682
+ break;
1638
1683
  case 'check_decision':
1639
1684
  text = handleCheckDecision(args);
1640
1685
  break;