@launchsecure/launch-kit 0.0.10 → 0.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9034,7 +9034,7 @@ var TOOLS = [
9034
9034
  },
9035
9035
  {
9036
9036
  name: "read_graph",
9037
- description: 'Query the structural project graph \u2014 a smart Glob replacement that locates files by type/module/name and returns structural metadata (imports, renders, routes, relations). \n\nUSE THIS FOR: "where is X", "what files are in module Y", "what pages exist under /admin", "what components does Z render", "what tables relate to User", "list all hooks in auth module". \n\nDO NOT USE FOR: finding text/code content (use Grep), reading actual source code (use Read), understanding behavior/logic/patterns (graph has no code semantics \u2014 only names and edges). \n\nQUERY PARAMS (at least one required for node data \u2014 unfiltered calls return summary only to stay in context):\n- search: substring match on node id, name, or route\n- type: filter by node type (ui layer: page, layout, component, ui, hook, context, config, util; api layer: endpoint; db layer: table, enum)\n- module: filter by module tag (computed from directory structure, e.g. "auth", "admin", "settings")\n- node_id: return this node + its neighborhood (incoming+outgoing edges within `hops`)\n- hops: neighborhood radius when node_id is set (default 1)\n- minimal: return only id/type/name/module/route per node (skip heavy fields like columns, exports)\n- include_edges: return the actual edge list. Default: TRUE for neighborhood queries (node_id), FALSE for filter queries (search/type/module). Filter responses always include `edge_count`; only pass include_edges:true when you actually need to inspect individual edges (e.g. "which components render X"). This default cuts typical filter responses in half.\n\nBATCH MODE: pass `queries` (array of query objects) to run multiple independent queries in a single call. Each query object uses the same params (layer/search/type/module/node_id/hops/minimal). Returns { batch: true, count, results: [{index, query, result}, ...] }. Use this when you need multiple graph views up-front (e.g. scoping a feature across ui+api+db layers) to save round-trips. When batch mode is used, top-level params are ignored.\n\nReturns: filtered nodes + edges between them. If no filter given, returns per-layer counts and type breakdown only.\n\nWIRE FORMAT (compact): responses that include nodes/edges use short keys and edge-by-index refs to cut payload ~40-60%. Every such response carries a `_schema` legend. Quick reference:\n nodes[]: { i: id, t: type, n: name, m: module, r: route, mt: methods, x: exports, c: columns }\n edges[]: { s: source_node_index, d: target_node_index, t: type, l: label }\nedges.s / edges.d are 0-based indices into THIS response\'s nodes array. If a referenced node is not in the response (boundary case), s/d may instead contain the full node id string \u2014 always check the type.\n\nPAGINATION (filter queries):\n- Use `offset` and `limit` to paginate through large result sets.\n- Response includes: `total` (matched), `returned` (in this page), `has_more`, `next_offset`.\n- If `has_more: true`, call again with `offset: next_offset` to get the next page.\n\nBUDGET GUARDS:\n- Neighborhood queries stop expanding when the projected response exceeds budget. The response then contains `budget_exceeded: true` plus `hops_traversed < hops_requested`. When this happens, drill into a specific neighbor with another node_id call rather than retrying with larger hops \u2014 it will just truncate again.\n- Batch mode caps total response size. Once the budget is hit, later queries return `{skipped: true, reason: "batch_budget_exhausted"}` and you must re-run them individually.',
9037
+ description: 'Query the structural project graph \u2014 use INSTEAD of Glob and Grep for locating files, understanding structure, and navigating the codebase. Faster and more accurate than file-system search because it returns typed nodes with metadata and relationships. \n\nUSE THIS FOR: "where is X", "what files are in module Y", "what pages exist under /admin", "what components does Z render", "what tables relate to User", "list all hooks in auth module", "which endpoints touch the User table", "what auth strategy does this endpoint use". \n\nDO NOT USE FOR: understanding what\'s INSIDE a component (use inspect_node for elements, conditions, state, variables, responses), reading actual source code (use Read). \n\nQUERY PARAMS (at least one required for node data \u2014 unfiltered calls return summary only to stay in context):\n- search: substring match on node id, name, or route\n- type: filter by node type (ui layer: page, layout, component, ui, hook, context, config, util; api layer: endpoint; db layer: table, enum)\n- module: filter by module tag (computed from directory structure, e.g. "auth", "admin", "settings")\n- node_id: return this node + its neighborhood (incoming+outgoing edges within `hops`)\n- hops: neighborhood radius when node_id is set (default 1)\n- minimal: return only id/type/name/module/route per node (skip heavy fields like columns, exports)\n- include_edges: return the actual edge list. Default: TRUE for neighborhood queries (node_id), FALSE for filter queries (search/type/module). Filter responses always include `edge_count`; only pass include_edges:true when you actually need to inspect individual edges (e.g. "which components render X"). This default cuts typical filter responses in half.\n\nBATCH MODE: pass `queries` (array of query objects) to run multiple independent queries in a single call. Each query object uses the same params (layer/search/type/module/node_id/hops/minimal). Returns { batch: true, count, results: [{index, query, result}, ...] }. Use this when you need multiple graph views up-front (e.g. scoping a feature across ui+api+db layers) to save round-trips. When batch mode is used, top-level params are ignored.\n\nReturns: filtered nodes + edges between them. If no filter given, returns per-layer counts and type breakdown only.\n\nWIRE FORMAT (compact): responses that include nodes/edges use short keys and edge-by-index refs to cut payload ~40-60%. Every such response carries a `_schema` legend. Quick reference:\n nodes[]: { i: id, t: type, n: name, m: module, r: route, mt: methods, x: exports, c: columns }\n edges[]: { s: source_node_index, d: target_node_index, t: type, l: label }\nedges.s / edges.d are 0-based indices into THIS response\'s nodes array. If a referenced node is not in the response (boundary case), s/d may instead contain the full node id string \u2014 always check the type.\n\nPAGINATION (filter queries):\n- Use `offset` and `limit` to paginate through large result sets.\n- Response includes: `total` (matched), `returned` (in this page), `has_more`, `next_offset`.\n- If `has_more: true`, call again with `offset: next_offset` to get the next page.\n\nBUDGET GUARDS:\n- Neighborhood queries stop expanding when the projected response exceeds budget. The response then contains `budget_exceeded: true` plus `hops_traversed < hops_requested`. When this happens, drill into a specific neighbor with another node_id call rather than retrying with larger hops \u2014 it will just truncate again.\n- Batch mode caps total response size. Once the budget is hit, later queries return `{skipped: true, reason: "batch_budget_exhausted"}` and you must re-run them individually.',
9038
9038
  inputSchema: {
9039
9039
  type: "object",
9040
9040
  properties: {
@@ -9109,7 +9109,7 @@ var TOOLS = [
9109
9109
  },
9110
9110
  {
9111
9111
  name: "grep_nodes",
9112
- description: `Search for text patterns WITHIN files selected by the project graph. Combines structural filtering (type/module/neighborhood) with regex content search \u2014 narrower than plain Grep because it only scans files matching the graph filter, reducing noise from tests, docs, generated code, unrelated modules.
9112
+ description: `Search for text patterns WITHIN files selected by the project graph. Use INSTEAD of Grep when searching within code \u2014 combines structural filtering (type/module/neighborhood) with regex content search. Narrower than plain Grep because it only scans files matching the graph filter, reducing noise from tests, docs, generated code, unrelated modules.
9113
9113
 
9114
9114
  USE THIS FOR: "which auth hooks use JWT decoding", "find TODO comments in pages only", "which deployment writers call Sentry", "what validation schemas exist in form components". It's grep scoped to a structurally-selected file set.
9115
9115
 
@@ -9157,9 +9157,9 @@ Returns: { pattern, filter, files_searched, total_matches, matches: [{file, line
9157
9157
  },
9158
9158
  {
9159
9159
  name: "inspect_node",
9160
- description: `Get deep AST data for specific graph nodes \u2014 what's INSIDE a component or endpoint. Returns elements (JSX), state hooks, conditions, variables, responses, and request params.
9160
+ description: `Get deep AST data for specific graph nodes \u2014 what's INSIDE a component or endpoint. Returns elements (JSX), state hooks, conditions, variables, responses, and request params. Use INSTEAD of Grep/Read when you need to understand component internals without reading source.
9161
9161
 
9162
- USE THIS FOR: "what elements does LoginPage have?", "what conditions does the login endpoint check?", "what state does SettingsPage manage?", "what responses can this endpoint return?"
9162
+ USE THIS FOR: "what elements does LoginPage have?", "what conditions does the login endpoint check?", "what state does SettingsPage manage?", "what responses can this endpoint return?", "what validation does this API do?", "what props does this component accept?", "which endpoints check for isAdmin?", "find all conditions mentioning rateLimit"
9163
9163
 
9164
9164
  DO NOT USE FOR: structural queries (use read_graph), content search (use grep_nodes).
9165
9165
 
@@ -9184,6 +9184,14 @@ Returns deep fields only \u2014 not structural metadata (use read_graph for that
9184
9184
  type: "array",
9185
9185
  items: { type: "string" },
9186
9186
  description: "Specific deep fields to return. Options: elements, stateVars, conditions, variables, responses, params. Omit for all."
9187
+ },
9188
+ filter: {
9189
+ type: "string",
9190
+ description: "Regex pattern to search WITHIN deep field values. Only returns nodes where at least one deep field matches. Searches across all string values in the requested fields (condition tests, variable inits, element tags/props, response bodies, etc.). When set, search becomes optional and node limit is raised to 50."
9191
+ },
9192
+ case_insensitive: {
9193
+ type: "boolean",
9194
+ description: "Case-insensitive filter matching. Default true."
9187
9195
  }
9188
9196
  },
9189
9197
  required: ["layer"]
@@ -9660,8 +9668,10 @@ function handleInspectNode(args) {
9660
9668
  const nodeId = args.node_id;
9661
9669
  const search = args.search;
9662
9670
  const fields = args.fields;
9671
+ const filter = args.filter;
9672
+ const caseInsensitive = args.case_insensitive ?? true;
9663
9673
  if (!layer) return err("layer is required.");
9664
- if (!nodeId && !search) return err("Either node_id or search is required.");
9674
+ if (!nodeId && !search && !filter) return err("Either node_id, search, or filter is required.");
9665
9675
  const graph = readGraph(rootDir, layer);
9666
9676
  if (!graph) return err(`No graph found for layer "${layer}". Run generate_graph first.`);
9667
9677
  let matched;
@@ -9669,30 +9679,66 @@ function handleInspectNode(args) {
9669
9679
  const node = graph.nodes.find((n) => n.id === nodeId);
9670
9680
  if (!node) return err(`Node "${nodeId}" not found in ${layer} layer.`);
9671
9681
  matched = [node];
9672
- } else {
9682
+ } else if (search) {
9673
9683
  const searchLower = search.toLowerCase();
9674
9684
  matched = graph.nodes.filter(
9675
9685
  (n) => n.id.toLowerCase().includes(searchLower) || n.name.toLowerCase().includes(searchLower) || n.route?.toLowerCase().includes(searchLower)
9676
9686
  );
9677
- }
9678
- if (matched.length === 0) return err(`No nodes matching "${search}" in ${layer} layer.`);
9679
- if (matched.length > 5) {
9680
- return err(`${matched.length} nodes match "${search}". Narrow your search (max 5 for inspect_node).`);
9687
+ } else {
9688
+ matched = graph.nodes;
9681
9689
  }
9682
9690
  const allDeepFields = ["elements", "stateVars", "conditions", "variables", "responses", "params"];
9683
9691
  const requestedFields = fields ?? allDeepFields;
9684
- const results = matched.map((node) => {
9692
+ let filterRegex = null;
9693
+ if (filter) {
9694
+ try {
9695
+ filterRegex = new RegExp(filter, caseInsensitive ? "i" : "");
9696
+ } catch {
9697
+ return err(`Invalid regex pattern: "${filter}"`);
9698
+ }
9699
+ }
9700
+ function deepMatch(obj, regex) {
9701
+ if (typeof obj === "string") return regex.test(obj);
9702
+ if (Array.isArray(obj)) return obj.some((item) => deepMatch(item, regex));
9703
+ if (obj && typeof obj === "object") {
9704
+ return Object.values(obj).some((val) => deepMatch(val, regex));
9705
+ }
9706
+ return false;
9707
+ }
9708
+ const results = [];
9709
+ const maxResults = filter ? 50 : 5;
9710
+ for (const node of matched) {
9685
9711
  const deep = { id: node.id, name: node.name, type: node.type };
9712
+ let hasData = false;
9686
9713
  for (const field of requestedFields) {
9687
9714
  if (allDeepFields.includes(field) && node[field] != null) {
9688
9715
  deep[field] = node[field];
9716
+ hasData = true;
9689
9717
  }
9690
9718
  }
9691
- return deep;
9692
- });
9719
+ if (filterRegex) {
9720
+ let fieldMatches = false;
9721
+ for (const field of requestedFields) {
9722
+ if (node[field] != null && deepMatch(node[field], filterRegex)) {
9723
+ fieldMatches = true;
9724
+ break;
9725
+ }
9726
+ }
9727
+ if (!fieldMatches) continue;
9728
+ }
9729
+ if (hasData || !filter) {
9730
+ results.push(deep);
9731
+ }
9732
+ if (results.length >= maxResults) break;
9733
+ }
9734
+ if (results.length === 0) {
9735
+ const hint = filter ? `No nodes with deep fields matching /${filter}/${caseInsensitive ? "i" : ""} in ${layer} layer.` : `No nodes matching "${search}" in ${layer} layer.`;
9736
+ return err(hint);
9737
+ }
9693
9738
  return okJson({
9694
9739
  layer,
9695
9740
  matched: results.length,
9741
+ ...results.length >= maxResults ? { truncated: true, hint: `Showing first ${maxResults} matches. Narrow with search param.` } : {},
9696
9742
  nodes: results
9697
9743
  });
9698
9744
  }
@@ -3519,8 +3519,10 @@ function handleInspectNode(args) {
3519
3519
  const nodeId = args.node_id;
3520
3520
  const search = args.search;
3521
3521
  const fields = args.fields;
3522
+ const filter = args.filter;
3523
+ const caseInsensitive = args.case_insensitive ?? true;
3522
3524
  if (!layer) return err("layer is required.");
3523
- if (!nodeId && !search) return err("Either node_id or search is required.");
3525
+ if (!nodeId && !search && !filter) return err("Either node_id, search, or filter is required.");
3524
3526
  const graph = readGraph(rootDir, layer);
3525
3527
  if (!graph) return err(`No graph found for layer "${layer}". Run generate_graph first.`);
3526
3528
  let matched;
@@ -3528,30 +3530,66 @@ function handleInspectNode(args) {
3528
3530
  const node = graph.nodes.find((n) => n.id === nodeId);
3529
3531
  if (!node) return err(`Node "${nodeId}" not found in ${layer} layer.`);
3530
3532
  matched = [node];
3531
- } else {
3533
+ } else if (search) {
3532
3534
  const searchLower = search.toLowerCase();
3533
3535
  matched = graph.nodes.filter(
3534
3536
  (n) => n.id.toLowerCase().includes(searchLower) || n.name.toLowerCase().includes(searchLower) || n.route?.toLowerCase().includes(searchLower)
3535
3537
  );
3536
- }
3537
- if (matched.length === 0) return err(`No nodes matching "${search}" in ${layer} layer.`);
3538
- if (matched.length > 5) {
3539
- return err(`${matched.length} nodes match "${search}". Narrow your search (max 5 for inspect_node).`);
3538
+ } else {
3539
+ matched = graph.nodes;
3540
3540
  }
3541
3541
  const allDeepFields = ["elements", "stateVars", "conditions", "variables", "responses", "params"];
3542
3542
  const requestedFields = fields ?? allDeepFields;
3543
- const results = matched.map((node) => {
3543
+ let filterRegex = null;
3544
+ if (filter) {
3545
+ try {
3546
+ filterRegex = new RegExp(filter, caseInsensitive ? "i" : "");
3547
+ } catch {
3548
+ return err(`Invalid regex pattern: "${filter}"`);
3549
+ }
3550
+ }
3551
+ function deepMatch(obj, regex) {
3552
+ if (typeof obj === "string") return regex.test(obj);
3553
+ if (Array.isArray(obj)) return obj.some((item) => deepMatch(item, regex));
3554
+ if (obj && typeof obj === "object") {
3555
+ return Object.values(obj).some((val) => deepMatch(val, regex));
3556
+ }
3557
+ return false;
3558
+ }
3559
+ const results = [];
3560
+ const maxResults = filter ? 50 : 5;
3561
+ for (const node of matched) {
3544
3562
  const deep = { id: node.id, name: node.name, type: node.type };
3563
+ let hasData = false;
3545
3564
  for (const field of requestedFields) {
3546
3565
  if (allDeepFields.includes(field) && node[field] != null) {
3547
3566
  deep[field] = node[field];
3567
+ hasData = true;
3548
3568
  }
3549
3569
  }
3550
- return deep;
3551
- });
3570
+ if (filterRegex) {
3571
+ let fieldMatches = false;
3572
+ for (const field of requestedFields) {
3573
+ if (node[field] != null && deepMatch(node[field], filterRegex)) {
3574
+ fieldMatches = true;
3575
+ break;
3576
+ }
3577
+ }
3578
+ if (!fieldMatches) continue;
3579
+ }
3580
+ if (hasData || !filter) {
3581
+ results.push(deep);
3582
+ }
3583
+ if (results.length >= maxResults) break;
3584
+ }
3585
+ if (results.length === 0) {
3586
+ const hint = filter ? `No nodes with deep fields matching /${filter}/${caseInsensitive ? "i" : ""} in ${layer} layer.` : `No nodes matching "${search}" in ${layer} layer.`;
3587
+ return err(hint);
3588
+ }
3552
3589
  return okJson({
3553
3590
  layer,
3554
3591
  matched: results.length,
3592
+ ...results.length >= maxResults ? { truncated: true, hint: `Showing first ${maxResults} matches. Narrow with search param.` } : {},
3555
3593
  nodes: results
3556
3594
  });
3557
3595
  }
@@ -3949,7 +3987,7 @@ var init_graph_mcp = __esm({
3949
3987
  },
3950
3988
  {
3951
3989
  name: "read_graph",
3952
- description: 'Query the structural project graph \u2014 a smart Glob replacement that locates files by type/module/name and returns structural metadata (imports, renders, routes, relations). \n\nUSE THIS FOR: "where is X", "what files are in module Y", "what pages exist under /admin", "what components does Z render", "what tables relate to User", "list all hooks in auth module". \n\nDO NOT USE FOR: finding text/code content (use Grep), reading actual source code (use Read), understanding behavior/logic/patterns (graph has no code semantics \u2014 only names and edges). \n\nQUERY PARAMS (at least one required for node data \u2014 unfiltered calls return summary only to stay in context):\n- search: substring match on node id, name, or route\n- type: filter by node type (ui layer: page, layout, component, ui, hook, context, config, util; api layer: endpoint; db layer: table, enum)\n- module: filter by module tag (computed from directory structure, e.g. "auth", "admin", "settings")\n- node_id: return this node + its neighborhood (incoming+outgoing edges within `hops`)\n- hops: neighborhood radius when node_id is set (default 1)\n- minimal: return only id/type/name/module/route per node (skip heavy fields like columns, exports)\n- include_edges: return the actual edge list. Default: TRUE for neighborhood queries (node_id), FALSE for filter queries (search/type/module). Filter responses always include `edge_count`; only pass include_edges:true when you actually need to inspect individual edges (e.g. "which components render X"). This default cuts typical filter responses in half.\n\nBATCH MODE: pass `queries` (array of query objects) to run multiple independent queries in a single call. Each query object uses the same params (layer/search/type/module/node_id/hops/minimal). Returns { batch: true, count, results: [{index, query, result}, ...] }. Use this when you need multiple graph views up-front (e.g. scoping a feature across ui+api+db layers) to save round-trips. When batch mode is used, top-level params are ignored.\n\nReturns: filtered nodes + edges between them. If no filter given, returns per-layer counts and type breakdown only.\n\nWIRE FORMAT (compact): responses that include nodes/edges use short keys and edge-by-index refs to cut payload ~40-60%. Every such response carries a `_schema` legend. Quick reference:\n nodes[]: { i: id, t: type, n: name, m: module, r: route, mt: methods, x: exports, c: columns }\n edges[]: { s: source_node_index, d: target_node_index, t: type, l: label }\nedges.s / edges.d are 0-based indices into THIS response\'s nodes array. If a referenced node is not in the response (boundary case), s/d may instead contain the full node id string \u2014 always check the type.\n\nPAGINATION (filter queries):\n- Use `offset` and `limit` to paginate through large result sets.\n- Response includes: `total` (matched), `returned` (in this page), `has_more`, `next_offset`.\n- If `has_more: true`, call again with `offset: next_offset` to get the next page.\n\nBUDGET GUARDS:\n- Neighborhood queries stop expanding when the projected response exceeds budget. The response then contains `budget_exceeded: true` plus `hops_traversed < hops_requested`. When this happens, drill into a specific neighbor with another node_id call rather than retrying with larger hops \u2014 it will just truncate again.\n- Batch mode caps total response size. Once the budget is hit, later queries return `{skipped: true, reason: "batch_budget_exhausted"}` and you must re-run them individually.',
3990
+ description: 'Query the structural project graph \u2014 use INSTEAD of Glob and Grep for locating files, understanding structure, and navigating the codebase. Faster and more accurate than file-system search because it returns typed nodes with metadata and relationships. \n\nUSE THIS FOR: "where is X", "what files are in module Y", "what pages exist under /admin", "what components does Z render", "what tables relate to User", "list all hooks in auth module", "which endpoints touch the User table", "what auth strategy does this endpoint use". \n\nDO NOT USE FOR: understanding what\'s INSIDE a component (use inspect_node for elements, conditions, state, variables, responses), reading actual source code (use Read). \n\nQUERY PARAMS (at least one required for node data \u2014 unfiltered calls return summary only to stay in context):\n- search: substring match on node id, name, or route\n- type: filter by node type (ui layer: page, layout, component, ui, hook, context, config, util; api layer: endpoint; db layer: table, enum)\n- module: filter by module tag (computed from directory structure, e.g. "auth", "admin", "settings")\n- node_id: return this node + its neighborhood (incoming+outgoing edges within `hops`)\n- hops: neighborhood radius when node_id is set (default 1)\n- minimal: return only id/type/name/module/route per node (skip heavy fields like columns, exports)\n- include_edges: return the actual edge list. Default: TRUE for neighborhood queries (node_id), FALSE for filter queries (search/type/module). Filter responses always include `edge_count`; only pass include_edges:true when you actually need to inspect individual edges (e.g. "which components render X"). This default cuts typical filter responses in half.\n\nBATCH MODE: pass `queries` (array of query objects) to run multiple independent queries in a single call. Each query object uses the same params (layer/search/type/module/node_id/hops/minimal). Returns { batch: true, count, results: [{index, query, result}, ...] }. Use this when you need multiple graph views up-front (e.g. scoping a feature across ui+api+db layers) to save round-trips. When batch mode is used, top-level params are ignored.\n\nReturns: filtered nodes + edges between them. If no filter given, returns per-layer counts and type breakdown only.\n\nWIRE FORMAT (compact): responses that include nodes/edges use short keys and edge-by-index refs to cut payload ~40-60%. Every such response carries a `_schema` legend. Quick reference:\n nodes[]: { i: id, t: type, n: name, m: module, r: route, mt: methods, x: exports, c: columns }\n edges[]: { s: source_node_index, d: target_node_index, t: type, l: label }\nedges.s / edges.d are 0-based indices into THIS response\'s nodes array. If a referenced node is not in the response (boundary case), s/d may instead contain the full node id string \u2014 always check the type.\n\nPAGINATION (filter queries):\n- Use `offset` and `limit` to paginate through large result sets.\n- Response includes: `total` (matched), `returned` (in this page), `has_more`, `next_offset`.\n- If `has_more: true`, call again with `offset: next_offset` to get the next page.\n\nBUDGET GUARDS:\n- Neighborhood queries stop expanding when the projected response exceeds budget. The response then contains `budget_exceeded: true` plus `hops_traversed < hops_requested`. When this happens, drill into a specific neighbor with another node_id call rather than retrying with larger hops \u2014 it will just truncate again.\n- Batch mode caps total response size. Once the budget is hit, later queries return `{skipped: true, reason: "batch_budget_exhausted"}` and you must re-run them individually.',
3953
3991
  inputSchema: {
3954
3992
  type: "object",
3955
3993
  properties: {
@@ -4024,7 +4062,7 @@ var init_graph_mcp = __esm({
4024
4062
  },
4025
4063
  {
4026
4064
  name: "grep_nodes",
4027
- description: `Search for text patterns WITHIN files selected by the project graph. Combines structural filtering (type/module/neighborhood) with regex content search \u2014 narrower than plain Grep because it only scans files matching the graph filter, reducing noise from tests, docs, generated code, unrelated modules.
4065
+ description: `Search for text patterns WITHIN files selected by the project graph. Use INSTEAD of Grep when searching within code \u2014 combines structural filtering (type/module/neighborhood) with regex content search. Narrower than plain Grep because it only scans files matching the graph filter, reducing noise from tests, docs, generated code, unrelated modules.
4028
4066
 
4029
4067
  USE THIS FOR: "which auth hooks use JWT decoding", "find TODO comments in pages only", "which deployment writers call Sentry", "what validation schemas exist in form components". It's grep scoped to a structurally-selected file set.
4030
4068
 
@@ -4072,9 +4110,9 @@ Returns: { pattern, filter, files_searched, total_matches, matches: [{file, line
4072
4110
  },
4073
4111
  {
4074
4112
  name: "inspect_node",
4075
- description: `Get deep AST data for specific graph nodes \u2014 what's INSIDE a component or endpoint. Returns elements (JSX), state hooks, conditions, variables, responses, and request params.
4113
+ description: `Get deep AST data for specific graph nodes \u2014 what's INSIDE a component or endpoint. Returns elements (JSX), state hooks, conditions, variables, responses, and request params. Use INSTEAD of Grep/Read when you need to understand component internals without reading source.
4076
4114
 
4077
- USE THIS FOR: "what elements does LoginPage have?", "what conditions does the login endpoint check?", "what state does SettingsPage manage?", "what responses can this endpoint return?"
4115
+ USE THIS FOR: "what elements does LoginPage have?", "what conditions does the login endpoint check?", "what state does SettingsPage manage?", "what responses can this endpoint return?", "what validation does this API do?", "what props does this component accept?", "which endpoints check for isAdmin?", "find all conditions mentioning rateLimit"
4078
4116
 
4079
4117
  DO NOT USE FOR: structural queries (use read_graph), content search (use grep_nodes).
4080
4118
 
@@ -4099,6 +4137,14 @@ Returns deep fields only \u2014 not structural metadata (use read_graph for that
4099
4137
  type: "array",
4100
4138
  items: { type: "string" },
4101
4139
  description: "Specific deep fields to return. Options: elements, stateVars, conditions, variables, responses, params. Omit for all."
4140
+ },
4141
+ filter: {
4142
+ type: "string",
4143
+ description: "Regex pattern to search WITHIN deep field values. Only returns nodes where at least one deep field matches. Searches across all string values in the requested fields (condition tests, variable inits, element tags/props, response bodies, etc.). When set, search becomes optional and node limit is raised to 50."
4144
+ },
4145
+ case_insensitive: {
4146
+ type: "boolean",
4147
+ description: "Case-insensitive filter matching. Default true."
4102
4148
  }
4103
4149
  },
4104
4150
  required: ["layer"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@launchsecure/launch-kit",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "LaunchSecure toolkit — launch-pod (pipeline pod), launch-chart (project graph MCP), and more.",
5
5
  "license": "MIT",
6
6
  "author": "LaunchSecure - AutomateWithUs",