a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
205 lines
12 KiB
Go
205 lines
12 KiB
Go
package profiles
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// The instruction bodies are composed from the shared sections below —
|
|
// no profile hand-authors its own copy of a section. Body text uses §
|
|
// as a backtick placeholder (bt renders it) so markdown tables stay
|
|
// readable inside Go string literals.
|
|
|
|
// bt renders § as a backtick.
|
|
func bt(s string) string { return strings.ReplaceAll(s, "§", "`") }
|
|
|
|
// sectionHeader renders the mandatory-rule opener every profile
|
|
// keeps: the MUST-prefer statement and the deny-hook warning are the
|
|
// two positioning cues that buy tool adoption, so they survive even
|
|
// the lean rendering — lean only condenses the prose around them. The
|
|
// heading doubles as the idempotency sentinel other adapters check
|
|
// (agents.InstructionsSentinel).
|
|
func sectionHeader(lean bool) string {
|
|
if lean {
|
|
return bt(`## MANDATORY: Use Gortex MCP tools instead of Read/Grep/Glob
|
|
|
|
A machine-wide §gortex§ MCP server indexes this code. You MUST prefer graph queries over file reads — PreToolUse hooks deny §Read§ / §Grep§ / §Glob§ on indexed source, and the deny message names the right tool.
|
|
|
|
`)
|
|
}
|
|
return bt(`## MANDATORY: Use Gortex MCP tools instead of Read/Grep/Glob
|
|
|
|
A Gortex daemon is configured machine-wide via the §gortex§ MCP server. Whenever you operate on indexed source (any repo the daemon tracks — check §gortex daemon status§), you MUST prefer graph queries over file reads. PreToolUse hooks deny §Read§ / §Grep§ / §Glob§ against indexed source — the deny message names the right tool.
|
|
|
|
`)
|
|
}
|
|
|
|
// sectionFullRuleTable is the classic nine-row instead-of table used
|
|
// by the core and full profiles.
|
|
// sectionExploreOpener positions the one-shot localization verb as
|
|
// the opening move (standard rendering; the lean profile carries its
|
|
// own condensed line).
|
|
var sectionExploreOpener = bt(`**Start every task with §explore§.** Describe the request in plain words (paste the issue, name the area) and it returns the ranked localization neighborhood — the likely-involved symbols with their source, call paths, and the files to change — in ONE call. Answer or start editing from its output; the granular tools below are for following up on one specific symbol.
|
|
|
|
`)
|
|
|
|
var sectionFullRuleTable = bt(`| Instead of... | Use... |
|
|
|-------------------------------------|------------------------------------------|
|
|
| Localizing a task / bug / "where is X" | §explore§ (one call: ranked neighborhood + source + call paths) |
|
|
| §Grep§ / §grep§ / §rg§ for a symbol | §search_symbols§ (BM25 + camelCase-aware) |
|
|
| §Grep§ for references | §find_usages§ (zero false positives) |
|
|
| Reading / grepping to find callers | §get_callers§ / §get_call_chain§ |
|
|
| §Glob§ over source files | §get_repo_outline§ / §search_symbols§ |
|
|
| §Read§ a file for one symbol | §get_symbol_source§ (§compress_bodies:true§ for the signature only) |
|
|
| §Read§ to understand a file | §get_file_summary§ / §get_editing_context§ |
|
|
| §Read§ a non-indexed / raw file | §read_file§ |
|
|
| §Read§ several symbols' bodies at once | §batch_symbols§ (one call, many bodies) |
|
|
| Multiple reads to explore a task | §smart_context§ (one call) |
|
|
| §Edit§ / §Write§ source | §edit_file§ / §write_file§ / §edit_symbol§ / §rename_symbol§ / §batch_edit§ |
|
|
|
|
`)
|
|
|
|
// sectionCLIFallback is the shell fallback line. Load-bearing for
|
|
// harnesses that mount no MCP tools — present in every profile.
|
|
var sectionCLIFallback = bt(`**CLI fallback (no MCP):** every tool above is reachable from a shell as §gortex call <tool> --arg k=v§ (e.g. §gortex call read_file --arg path=<file>§) — there is no bare §gortex <tool>§ verb.
|
|
|
|
`)
|
|
|
|
// sectionReadDiscipline is the scope-vs-fidelity paragraph.
|
|
var sectionReadDiscipline = bt(`Graph queries *narrow scope*; they do not replace reading the implementation. For the symbol you are about to change or depend on — especially behavior-critical code (migrations, retry / fallback / error-recovery paths, concurrency, compatibility shims) — read the real body with §get_symbol_source§ and do NOT pass §compress_bodies:true§, which elides the branches that carry the risk. §format:"gcx"§ (compact wire, ~27% fewer tokens) and §compress_bodies:true§ (body-eliding) exist on the read / list tools; the parameter legend is in the MCP server instructions.
|
|
|
|
`)
|
|
|
|
// sectionMemoryFull is the full memory-workflow section (core / full).
|
|
var sectionMemoryFull = bt(`## MANDATORY: Session + development memory
|
|
|
|
The graph remembers code; these tools remember **why you made a call**. They are behavior-critical — run each at its trigger, not "optionally":
|
|
|
|
- **Session start / after a compaction** — call §distill_session§ first: prior top symbols, pinned notes, decisions, recent excerpts. Seed your mental model before reading any file.
|
|
- **Immediately after §smart_context§** — call §surface_memories task:"<task>" symbol_ids:"<top hits>"§: cross-session invariants / gotchas / decisions anchored to your working set. If it returns nothing, don't probe further.
|
|
- **At every decision** — pick an approach, reject an alternative, hit a non-obvious constraint, or commit to an invariant → §save_note tags:"decision" body:"<what+why>"§. Mention symbol IDs (§pkg/foo.go::Bar§) in the body for auto-linking; §pinned:true§ for load-bearing notes.
|
|
- **When you learn a durable fact worth teaching the team** — §store_memory kind:"<invariant|gotcha|convention|decision|constraint|incident>" body:"<what+why>" symbol_ids:"<id>" importance:5§. §save_note§ is the per-session scratchpad; §store_memory§ is the workspace-wide store every future agent inherits. Supersede a stale memory with §supersedes:"<old-id>"§.
|
|
- **Before editing a symbol you've touched before** — §query_notes symbol_id:"<id>"§ / §query_memories symbol_id:"<id>"§ surface prior decisions and "do not change this without …" warnings.
|
|
|
|
**Save / store:** decisions, non-obvious constraints, invariants, follow-ups, incident learnings, bug reproductions. **Skip:** play-by-play the diff already shows, anything derivable from the graph, content already in CLAUDE.md.
|
|
|
|
`)
|
|
|
|
// sectionMemoryLean is the one-paragraph memory trio for the lean
|
|
// profile — the triggers survive, the elaboration moves to the guide.
|
|
var sectionMemoryLean = bt(`**Memory (mandatory):** §distill_session§ at session start; §surface_memories task:"<task>"§ right after §smart_context§; §save_note tags:"decision" body:"<what+why>"§ at every decision; §store_memory§ for durable invariants / gotchas the team should inherit; §query_notes§ / §query_memories§ before re-touching a symbol.
|
|
|
|
`)
|
|
|
|
// switchBullet renders the instruction-profile discovery line — the
|
|
// line every profile carries so a switched-down machine can always
|
|
// find its way back. The lean rendering keeps the verb and the
|
|
// next-session caveat, dropping only the roster prose.
|
|
func switchBullet(active string, lean bool) string {
|
|
if lean {
|
|
return bt(fmt.Sprintf(`- **Profiles:** active §%s§. Broader guidance: §gortex instructions switch core§ (or §full§; §list§ shows all) — NEW sessions only.
|
|
`, active))
|
|
}
|
|
return bt(fmt.Sprintf(`- **Instruction profiles** — this block is the active §%s§ profile. §gortex instructions list§ shows the others (§core§ balanced default · §localization§ lean · §full§ maximum guidance); switch with §gortex instructions switch <name>§ — applies to NEW sessions only (instructions, tools/list, and skills all load at session start).
|
|
`, active))
|
|
}
|
|
|
|
// sectionDiscovery builds the reference-and-discovery section shared
|
|
// by core and full; surfaceLine describes what ships eagerly.
|
|
func sectionDiscovery(active, surfaceLine string) string {
|
|
return bt(`## Reference and discovery
|
|
|
|
- **§gortex://guide§ resource** (or the §gortex guide [topic]§ CLI) is the full reference: LLM-provider matrix, capabilities catalog, analyze / search_ast catalogs, token-economy detail, MCP resources, session-start checklist. Read it on demand — it is not pre-paid here.
|
|
- `) + surfaceLine + bt(`- The SessionStart hook injects daemon status. "daemon is not running" → run §gortex daemon start --detach§; "cwd is not covered by any tracked repo" → graph tools are unavailable there.
|
|
`) + switchBullet(active, false)
|
|
}
|
|
|
|
var coreSurfaceLine = bt(`**§tools_search§** — the server publishes a lean tool preset eagerly and defers the rest; call §tools_search§ to discover and load any tool by keyword (every tool stays callable by name).
|
|
`)
|
|
|
|
var fullSurfaceLine = bt(`**§tools_search§** — under this profile the server publishes the full documented dev-cycle preset (~34 workhorse tools) eagerly; the long tail still loads by keyword via §tools_search§ (every tool stays callable by name).
|
|
`)
|
|
|
|
// coreBody is the balanced default: today's slim policy core plus the
|
|
// profile-switch discovery line.
|
|
func coreBody() string {
|
|
return sectionHeader(false) +
|
|
sectionExploreOpener +
|
|
sectionFullRuleTable +
|
|
sectionCLIFallback +
|
|
sectionReadDiscipline +
|
|
sectionMemoryFull +
|
|
sectionDiscovery("core", coreSurfaceLine)
|
|
}
|
|
|
|
// fullBody differs from core only in the eager-surface description —
|
|
// the ToolPreset column of the table is what actually widens the
|
|
// surface.
|
|
func fullBody() string {
|
|
return sectionHeader(false) +
|
|
sectionExploreOpener +
|
|
sectionFullRuleTable +
|
|
sectionCLIFallback +
|
|
sectionReadDiscipline +
|
|
sectionMemoryFull +
|
|
sectionDiscovery("full", fullSurfaceLine)
|
|
}
|
|
|
|
// localizationRow maps an eager tool to its instead-of table row.
|
|
// Tools cued outside the table (the one-shot opener and the liveness
|
|
// check) are listed in localizationNonTableTools; the body test
|
|
// asserts every eager tool is covered by one of the two so the preset
|
|
// and the body cannot drift.
|
|
type localizationRow struct {
|
|
tool, instead, use string
|
|
}
|
|
|
|
var localizationRows = []localizationRow{
|
|
{"explore", "Localizing a task / bug / \"where is X\"", "§explore§ (one call: ranked neighborhood + source + call paths)"},
|
|
{"search_symbols", "§Grep§ / §rg§ for a symbol", "§search_symbols§ (BM25 + camelCase-aware)"},
|
|
{"search_text", "§Grep§ for a literal / regex", "§search_text§ (trigram-indexed)"},
|
|
{"find_usages", "§Grep§ for references", "§find_usages§ (zero false positives)"},
|
|
{"get_callers", "Reading to find callers", "§get_callers§"},
|
|
{"find_implementations", "Hunting interface implementors", "§find_implementations§"},
|
|
{"get_symbol_source", "§Read§ a file for one symbol", "§get_symbol_source§"},
|
|
{"batch_symbols", "Reading several symbols one by one", "§batch_symbols§ (one call, many bodies)"},
|
|
{"get_file_summary", "§Read§ to understand a file", "§get_file_summary§"},
|
|
{"read_file", "§Read§ a non-indexed / raw file", "§read_file§"},
|
|
}
|
|
|
|
// localizationNonTableTools are eager tools cued in prose rather than
|
|
// table rows.
|
|
var localizationNonTableTools = map[string]bool{
|
|
"smart_context": true, // the open-with-one-call cue
|
|
"index_health": true, // the liveness line in discovery
|
|
}
|
|
|
|
func localizationTable() string {
|
|
var sb strings.Builder
|
|
sb.WriteString("| Instead of... | Use... |\n")
|
|
sb.WriteString("|--------------------------------|---------------------------------|\n")
|
|
for _, r := range localizationRows {
|
|
fmt.Fprintf(&sb, "| %s | %s |\n", bt(r.instead), bt(r.use))
|
|
}
|
|
sb.WriteString("\n")
|
|
return sb.String()
|
|
}
|
|
|
|
// localizationBody is the lean profile: every positioning cue (MUST
|
|
// rule, deny warning, one-shot opener, memory triggers, discovery /
|
|
// switch-back path) survives; reference elaboration moves to
|
|
// gortex://guide.
|
|
func localizationBody() string {
|
|
return sectionHeader(true) +
|
|
bt(`**Open every localization task with §explore§** (paste the whole issue — distilled server-side): the ranked neighborhood in one call. Answer from its output; its source is in context — don't re-open those files with §Read§/§Glob§, read one with §get_symbol_source§/§batch_symbols§.
|
|
|
|
`) +
|
|
localizationTable() +
|
|
bt(`Read the real body (§get_symbol_source§, without §compress_bodies§) before you change or depend on a symbol; §smart_context§ assembles the broader working set when explore's neighborhood is not enough. **No MCP mounted?** Any tool works from a shell: §gortex call <tool> --arg k=v§.
|
|
|
|
`) +
|
|
sectionMemoryLean +
|
|
bt(`**Discovery:** lean §localization§ profile — the tools above plus §index_health§ (liveness) ship eagerly; anything else loads by name via §tools_search§. Full reference: §gortex://guide§.
|
|
`) + switchBullet("localization", true)
|
|
}
|