Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,25 @@ if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then
fi
fi
echo "VENDORED_GSTACK: $_VENDORED"
# Semantic code search (sqry)
_SQRY="unavailable"
_SQRY_INDEXED="no"
_SQRY_STALE="no"
if command -v sqry >/dev/null 2>&1; then
_SQRY="available"
_SQRY_VERSION=$(sqry --version 2>/dev/null | head -1 || echo "unknown")
_SQRY_STATUS=$(sqry index --status --json . 2>/dev/null || echo '{}')
if echo "$_SQRY_STATUS" | grep -q '"exists": true' 2>/dev/null; then
_SQRY_INDEXED="yes"
fi
if echo "$_SQRY_STATUS" | grep -q '"stale": true' 2>/dev/null; then
_SQRY_STALE="yes"
fi
fi
echo "SQRY: $_SQRY"
[ "$_SQRY" = "available" ] && echo "SQRY_VERSION: $_SQRY_VERSION"
[ "$_SQRY" = "available" ] && echo "SQRY_INDEXED: $_SQRY_INDEXED"
[ "$_SQRY" = "available" ] && echo "SQRY_STALE: $_SQRY_STALE"
# Detect spawned session (OpenClaw or other orchestrator)
[ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true
```
Expand Down
60 changes: 60 additions & 0 deletions contrib/add-tool/sqry/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# sqry Integration for gstack

[sqry](https://github.com/verivus-oss/sqry) provides AST-based semantic code
search via 34 MCP tools. This integration adds structural code analysis to
gstack skills — callers/callees tracing, cycle detection, complexity metrics,
structural call-path tracing, and more.

## Install

bash contrib/add-tool/sqry/install.sh [claude|codex|gemini|all]

## What it does

When sqry is installed and configured as an MCP server, gstack skills gain a
"Structural Code Analysis" section with contextual tool recommendations:

- `/investigate` gets caller/callee tracing, cycle detection, blast radius analysis
- `/cso` gets structural call-path tracing from input handlers to sinks, dead code detection
- `/review` gets complexity regression checks, cycle detection, semantic diff
- `/retro` gets structural trend analysis and codebase health metrics
- `/plan-eng-review` gets dependency visualization and architecture boundary validation
- `/ship` gets pre-ship structural verification (cycles, dead code, complexity)

See `tools.json` for the complete routing table.

## Architecture: WHEN vs HOW

This integration follows sqry v8's **resource delegation** model:

- **gstack owns WHEN** — `tools.json` defines which sqry tools to use at which
skill phase (e.g., `trace_path` during `/cso` security analysis). This is
gstack's value-add: contextual routing that sqry doesn't know about.
- **sqry owns HOW** — parameter limits, cost tiering, scoping strategies, and
output size guidance are served live by the sqry MCP server as resources
(`sqry://docs/capability-map`, `sqry://docs/tool-guide`). These always match
the user's installed sqry version and update automatically.

This split prevents drift: when sqry adds tools, changes limits, or updates
tiering, gstack agents pick it up automatically without a gstack release.

## Relationship to existing sqry skills

The `sqry-claude`, `sqry-codex`, and `sqry-gemini` skills (shipped with sqry)
teach agents how to *set up and use* sqry. This gstack integration is different —
it wires sqry tools into gstack's *existing workflow skills* so they're used
automatically at the right moment during debugging, review, security audits, etc.

| sqry skills (setup) | gstack add-in (workflow) |
|---------------------|------------------------|
| Teach tool usage | Wire tools into skill phases |
| Manual invocation | Automatic contextual use |
| Generic patterns | Skill-phase routing |
| No index management | Auto-rebuild when stale |
| Parameter guidance inline | Delegates to MCP resources |

## Uninstall

bash contrib/add-tool/sqry/uninstall.sh

This removes the gstack integration. sqry itself remains installed.
12 changes: 12 additions & 0 deletions contrib/add-tool/sqry/detection.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Semantic code search (sqry) — lightweight detection only
# Reference fragment — inlined by preamble.ts resolver
# Only command -v (~1ms) and directory check. No subprocess calls.
# Index staleness is checked at query time by the agent, not here.
_SQRY="unavailable"
_SQRY_INDEXED="unknown"
if command -v sqry-mcp >/dev/null 2>&1; then
_SQRY="available"
[ -d ".sqry" ] && _SQRY_INDEXED="yes" || _SQRY_INDEXED="no"
fi
echo "SQRY: $_SQRY"
[ "$_SQRY" = "available" ] && echo "SQRY_INDEXED: $_SQRY_INDEXED"
109 changes: 109 additions & 0 deletions contrib/add-tool/sqry/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env bash
# Install sqry as a gstack structural code analysis add-in.
# Idempotent — safe to run multiple times.
set -e

AGENT="${1:-claude}"
MIN_VERSION="7.0.0"

echo "=== sqry integration for gstack ==="
echo ""

# 1. Check for sqry CLI
if ! command -v sqry >/dev/null 2>&1; then
echo "sqry not found on PATH."
echo ""
echo "Install via cargo (recommended — builds from source):"
echo " cargo install sqry-cli sqry-mcp"
echo ""
echo "Or download a release binary from:"
echo " https://github.com/verivus-oss/sqry/releases"
echo ""
echo "Then re-run this script."
exit 1
fi

# 2. Check version (normalize: "sqry 7.1.4" -> "7.1.4")
SQRY_VERSION=$(sqry --version 2>/dev/null | awk '{print $2}' || echo "0.0.0")
echo "Found sqry $SQRY_VERSION"

# Portable semver comparator (no sort -V, works on macOS)
version_lt() {
local IFS=.
local i a=($1) b=($2)
for ((i=0; i<${#b[@]}; i++)); do
[ -z "${a[i]}" ] && a[i]=0
if ((10#${a[i]} < 10#${b[i]})); then return 0; fi
if ((10#${a[i]} > 10#${b[i]})); then return 1; fi
done
return 1
}

if version_lt "$SQRY_VERSION" "$MIN_VERSION"; then
echo "sqry $MIN_VERSION+ required. Please upgrade:"
echo " cargo install sqry-cli sqry-mcp"
exit 1
fi

# 3. Check for sqry-mcp
if ! command -v sqry-mcp >/dev/null 2>&1; then
echo "sqry-mcp not found on PATH."
echo ""
echo "Install the MCP server:"
echo " cargo install sqry-mcp"
echo ""
echo "Then re-run this script."
exit 1
fi

echo "Found sqry-mcp at $(command -v sqry-mcp)"

# 4. Configure MCP for the target agent
# Delegate to sqry's own setup command — it knows each host's config format.
echo ""
echo "Configuring MCP server for $AGENT..."

case "$AGENT" in
claude) sqry mcp setup --tool claude ;;
codex) sqry mcp setup --tool codex ;;
gemini) sqry mcp setup --tool gemini ;;
all) sqry mcp setup ;;
*) echo "Warning: Auto-configuration not supported for $AGENT. Run 'sqry mcp setup' manually." ;;
esac

# 5. Verify MCP configuration
echo ""
echo "MCP status:"
sqry mcp status 2>/dev/null || echo " (could not verify — run 'sqry mcp status' manually)"

# 6. Build initial index if not present
if ! sqry index --status --json . 2>/dev/null | grep -q '"exists": true'; then
echo ""
echo "Building initial sqry index..."
sqry index .
echo "Index built."
else
echo ""
echo "sqry index already exists."
if sqry index --status --json . 2>/dev/null | grep -q '"stale": true'; then
echo "Index is stale — rebuilding..."
sqry index .
echo "Index rebuilt."
fi
fi

# 7. Regenerate gstack skills (picks up {{SQRY_CONTEXT}} resolver)
GSTACK_DIR="${GSTACK_ROOT:-$HOME/.claude/skills/gstack}"
if [ -f "$GSTACK_DIR/package.json" ]; then
echo ""
echo "Regenerating gstack skill docs..."
(cd "$GSTACK_DIR" && bun run gen:skill-docs --host all 2>/dev/null) || {
echo "Warning: Could not regenerate skill docs. Run manually:"
echo " cd $GSTACK_DIR && bun run gen:skill-docs --host all"
}
fi

echo ""
echo "Done. sqry structural code analysis is now available in gstack skills."
echo ""
echo "IMPORTANT: Restart your AI agent session for the MCP tools to appear."
87 changes: 87 additions & 0 deletions contrib/add-tool/sqry/tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
{
"tool": "sqry",
"homepage": "https://github.com/verivus-oss/sqry",
"mcp_server_name": "sqry",
"detection": {
"binary": "sqry",
"min_version": "7.0.0",
"rebuild_hint": "If you made structural changes this session, call rebuild_index before your next sqry query."
},
"mcp_resources": {
"capability_map": "sqry://docs/capability-map",
"tool_guide": "sqry://docs/tool-guide",
"manifest": "sqry://meta/manifest"
},
"integrations": {
"investigate": {
"phase": "root-cause-investigation",
"context": "structural root cause analysis",
"tools": [
{ "tool": "direct_callers", "when": "immediate callers of suspect function" },
{ "tool": "direct_callees", "when": "immediate callees of suspect function" },
{ "tool": "call_hierarchy", "when": "multi-level caller/callee chains when one-hop insufficient" },
{ "tool": "is_node_in_cycle", "when": "check if bug site is in circular dependency" },
{ "tool": "trace_path", "when": "call path from entry point to bug site" },
{ "tool": "dependency_impact", "when": "blast radius — what else breaks if this symbol is wrong" },
{ "tool": "get_definition", "when": "jump to definition of symbol from stack traces" },
{ "tool": "get_references", "when": "all usages of suspect symbol across codebase" }
]
},
"cso": {
"phase": "structural-security-analysis",
"context": "AST-powered security audit",
"tools": [
{ "tool": "trace_path", "when": "structural call paths from input handlers to dangerous sinks (exec, eval, innerHTML, raw SQL)" },
{ "tool": "call_hierarchy", "when": "full call tree from auth/authz entry points to verify coverage" },
{ "tool": "find_cycles", "when": "circular dependencies that could cause infinite loops (DoS vectors)" },
{ "tool": "find_unused", "when": "dead code with old vulnerabilities or stale auth checks" },
{ "tool": "complexity_metrics", "when": "high-complexity functions (cyclomatic >15) for manual security review" },
{ "tool": "direct_callers", "when": "verify security-critical functions only called from trusted contexts" },
{ "tool": "semantic_search", "when": "functions matching security patterns (auth*, sanitize*, validate*, escape*)" },
{ "tool": "cross_language_edges", "when": "FFI/HTTP boundaries where trust assumptions change" }
]
},
"review": {
"phase": "structural-diff-analysis",
"context": "structural analysis of changed code",
"tools": [
{ "tool": "complexity_metrics", "when": "cyclomatic complexity of changed files — flag regressions" },
{ "tool": "find_cycles", "when": "check if changed symbols introduced or participate in cycles" },
{ "tool": "dependency_impact", "when": "downstream impact of changed public APIs" },
{ "tool": "find_unused", "when": "newly-dead code after refactors or API changes" },
{ "tool": "semantic_diff", "when": "structural changes between PR branch and base branch" },
{ "tool": "direct_callers", "when": "verify callers of changed functions still work with new signature" }
]
},
"retro": {
"phase": "structural-trend-analysis",
"context": "structural code quality analysis for retrospective",
"tools": [
{ "tool": "semantic_diff", "when": "structural changes between this week's HEAD and last week's tag/commit" },
{ "tool": "complexity_metrics", "when": "complexity trends — adding or reducing complexity?" },
{ "tool": "find_cycles", "when": "new cycles introduced this week" },
{ "tool": "get_insights", "when": "overall codebase health metrics for retrospective dashboard" }
]
},
"plan-eng-review": {
"phase": "architecture-understanding",
"context": "structural architecture analysis for plan review",
"tools": [
{ "tool": "export_graph", "when": "visualize module dependencies to validate architecture boundaries" },
{ "tool": "subgraph", "when": "dependency neighborhood around components the plan modifies" },
{ "tool": "show_dependencies", "when": "dependency tree of modules the plan touches" },
{ "tool": "find_cycles", "when": "existing cycles the plan should address or avoid" },
{ "tool": "cross_language_edges", "when": "cross-language boundaries the plan must respect" }
]
},
"ship": {
"phase": "pre-ship-structural-check",
"context": "structural verification before shipping",
"tools": [
{ "tool": "find_cycles", "when": "no circular dependencies in shipped code" },
{ "tool": "find_unused", "when": "catch dead code being shipped" },
{ "tool": "complexity_metrics", "when": "verify complexity hasn't regressed" }
]
}
}
}
89 changes: 89 additions & 0 deletions contrib/add-tool/sqry/uninstall.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env bash
# Remove sqry integration from gstack.
# Does NOT uninstall sqry itself — only removes the gstack integration.
set -e

echo "=== Removing sqry integration from gstack ==="

# Helper: remove a key from a JSON file using node (portable)
remove_json_key() {
local file="$1" key_path="$2"
[ -f "$file" ] && command -v node >/dev/null 2>&1 || return 0
node -e "
const fs = require('fs');
try {
const s = JSON.parse(fs.readFileSync('$file', 'utf-8'));
const parts = '$key_path'.split('.');
let obj = s;
for (let i = 0; i < parts.length - 1; i++) {
if (!obj[parts[i]]) return;
obj = obj[parts[i]];
}
const last = parts[parts.length - 1];
if (obj[last] !== undefined) {
delete obj[last];
fs.writeFileSync('$file', JSON.stringify(s, null, 2));
console.log('Removed ' + '$key_path' + ' from $file');
}
} catch(e) {}
" 2>/dev/null || true
}

# 1. Claude: global mcpServers.sqry + per-project mcpServers.sqry
for settings in "$HOME/.claude.json" "$HOME/.claude/settings.json"; do
remove_json_key "$settings" "mcpServers.sqry"
# Also clean per-project entries
if [ -f "$settings" ] && command -v node >/dev/null 2>&1; then
node -e "
const fs = require('fs');
try {
const s = JSON.parse(fs.readFileSync('$settings', 'utf-8'));
if (s.projects) {
let changed = false;
for (const [k, v] of Object.entries(s.projects)) {
if (v && v.mcpServers && v.mcpServers.sqry) {
delete v.mcpServers.sqry;
changed = true;
}
}
if (changed) {
fs.writeFileSync('$settings', JSON.stringify(s, null, 2));
console.log('Removed per-project sqry MCP entries from $settings');
}
}
} catch(e) {}
" 2>/dev/null || true
fi
done

# 2. Codex: [mcp_servers.sqry] section in TOML (portable, no sed -i)
CODEX_CONFIG="$HOME/.codex/config.toml"
if [ -f "$CODEX_CONFIG" ] && grep -q '\[mcp_servers\.sqry\]' "$CODEX_CONFIG" 2>/dev/null; then
node -e "
const fs = require('fs');
const lines = fs.readFileSync('$CODEX_CONFIG', 'utf-8').split('\n');
const out = [];
let skip = false;
for (const line of lines) {
if (/^\[mcp_servers\.sqry[\].]/.test(line.trim())) { skip = true; continue; }
if (skip && line.startsWith('[') && !/^\[mcp_servers\.sqry[\].]/.test(line.trim())) { skip = false; }
if (!skip) out.push(line);
}
fs.writeFileSync('$CODEX_CONFIG', out.join('\n'));
console.log('Removed [mcp_servers.sqry] from Codex config');
" 2>/dev/null || true
fi

# 3. Gemini: mcpServers.sqry in JSON
GEMINI_CONFIG="$HOME/.gemini/settings.json"
remove_json_key "$GEMINI_CONFIG" "mcpServers.sqry"

# 4. Regenerate gstack skills ({{SQRY_CONTEXT}} emits nothing without sqry)
GSTACK_DIR="${GSTACK_ROOT:-$HOME/.claude/skills/gstack}"
if [ -f "$GSTACK_DIR/package.json" ]; then
echo "Regenerating gstack skill docs..."
(cd "$GSTACK_DIR" && bun run gen:skill-docs --host all 2>/dev/null) || true
fi

echo "Done. sqry integration removed. sqry itself is still installed."
echo "To fully uninstall sqry: see https://github.com/verivus-oss/sqry#uninstall"
2 changes: 2 additions & 0 deletions cso/SKILL.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ This is NOT a checklist — it's a reasoning phase. The output is understanding,

{{LEARNINGS_SEARCH}}

{{SQRY_CONTEXT}}

### Phase 1: Attack Surface Census

Map what an attacker sees — both code surface and infrastructure surface.
Expand Down
Loading
Loading