@launchsecure/launch-kit 0.0.11 → 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.
@@ -9159,7 +9159,7 @@ Returns: { pattern, filter, files_searched, total_matches, matches: [{file, line
9159
9159
  name: "inspect_node",
9160
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?", "what validation does this API do?", "what props does this component accept?"
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
  }
@@ -4074,7 +4112,7 @@ Returns: { pattern, filter, files_searched, total_matches, matches: [{file, line
4074
4112
  name: "inspect_node",
4075
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?", "what validation does this API do?", "what props does this component accept?"
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.11",
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",