Skip to content

Commit c4cf088

Browse files
authored
Merge pull request #1821 from entireio/feat/agent-help-examples-and-injection-invariant
feat(agent-help): teach agents entire usage via examples + injected invariant
2 parents f502d6b + 1c5dfb4 commit c4cf088

10 files changed

Lines changed: 99 additions & 23 deletions

cmd/entire/cli/agent_help_cmd.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ type agentHelpJSON struct {
204204
Command string `json:"command"`
205205
Short string `json:"short,omitempty"`
206206
Long string `json:"long,omitempty"`
207+
Example string `json:"example,omitempty"`
207208
Repo string `json:"repo,omitempty"`
208209
Flags []agentHelpFlagJSON `json:"flags,omitempty"`
209210
Subcommands []agentHelpSubcommandJSON `json:"subcommands,omitempty"`
@@ -215,6 +216,7 @@ func renderAgentHelpJSON(rootCmd, target *cobra.Command, repoLine string, trails
215216
Command: target.CommandPath(),
216217
Short: target.Short,
217218
Long: strings.TrimSpace(target.Long),
219+
Example: strings.TrimSpace(target.Example),
218220
Repo: repoLine,
219221
}
220222
if target != rootCmd {
@@ -298,6 +300,11 @@ func renderAgentHelpCommand(cmd *cobra.Command, repoLine string, trailsEnabled b
298300
b.WriteString(long)
299301
b.WriteString("\n")
300302
}
303+
if example := strings.TrimSpace(cmd.Example); example != "" {
304+
b.WriteString("\nExamples:\n")
305+
b.WriteString(example)
306+
b.WriteString("\n")
307+
}
301308
b.WriteString("\n")
302309
b.WriteString(agentHelpRepoBlock(repoLine))
303310

cmd/entire/cli/agent_help_cmd_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,38 @@ func TestRenderAgentHelpCommand_ShowsFlagsAndSubcommands(t *testing.T) {
384384
}
385385
}
386386

387+
// A command's Example field must reach agents in both output modes: agent-help
388+
// is the only surface agents read, and an example is what removes arg-format
389+
// guesswork (e.g. <file>:<line>).
390+
func TestRenderAgentHelpCommand_RendersExample(t *testing.T) {
391+
t.Parallel()
392+
393+
cmd := &cobra.Command{
394+
Use: "why <file>[:line]",
395+
Short: "Show why a line exists",
396+
Example: " entire why src/auth.go:42 --json",
397+
}
398+
399+
text := renderAgentHelpCommand(cmd, agentHelpTestRepo, true)
400+
if !strings.Contains(text, "Examples:") || !strings.Contains(text, "entire why src/auth.go:42 --json") {
401+
t.Fatalf("text agent-help must render the example:\n%s", text)
402+
}
403+
404+
root := &cobra.Command{Use: "entire"}
405+
root.AddCommand(cmd)
406+
jsonOut, err := renderAgentHelpJSON(root, cmd, agentHelpTestRepo, true)
407+
if err != nil {
408+
t.Fatal(err)
409+
}
410+
var doc agentHelpJSON
411+
if err := json.Unmarshal([]byte(jsonOut), &doc); err != nil {
412+
t.Fatalf("json agent-help must parse: %v\n%s", err, jsonOut)
413+
}
414+
if doc.Example != "entire why src/auth.go:42 --json" {
415+
t.Fatalf("json agent-help must carry the trimmed example, got %q", doc.Example)
416+
}
417+
}
418+
387419
// The top-level rendering lists the live command map (including the revealed
388420
// trail command), states the auto-detected repo, and carries the standing rule.
389421
func TestRenderAgentHelpTop_ListsCommandsRepoAndRule(t *testing.T) {

cmd/entire/cli/attribution.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,11 @@ func newBlameCmd() *cobra.Command {
149149
// Hidden from `entire help` while the feature is still maturing —
150150
// advertised under `entire labs`, and `entire blame` / `entire blame
151151
// --help` keep working normally.
152-
Hidden: true,
153-
Short: "Show which lines came from Entire checkpoints",
154-
Long: "Show git-blame-style line attribution enriched with Entire checkpoint metadata.\n\nLimit to a line or range with <file>:12, <file>:12-20, or the --line flag.",
155-
Args: cobra.ExactArgs(1),
152+
Hidden: true,
153+
Short: "Show which lines came from Entire checkpoints",
154+
Long: "Show git-blame-style line attribution enriched with Entire checkpoint metadata.\n\nLimit to a line or range with <file>:12, <file>:12-20, or the --line flag.",
155+
Example: " entire blame src/auth.go\n entire blame src/auth.go:10-40 --json",
156+
Args: cobra.ExactArgs(1),
156157
RunE: func(cmd *cobra.Command, args []string) error {
157158
return runAttributionBlame(cmd.Context(), cmd.OutOrStdout(), args[0], attributionBlameOptions{
158159
LineFlag: lineFlag,
@@ -177,10 +178,11 @@ func newWhyCmd() *cobra.Command {
177178
// Hidden from `entire help` while the feature is still maturing —
178179
// advertised under `entire labs`, and `entire why` / `entire why
179180
// --help` keep working normally.
180-
Hidden: true,
181-
Short: "Show why a line exists",
182-
Long: "Explain the commit, checkpoint, prompt, and session behind a file or line.\n\nTarget a specific line with <file>:12 or the --line flag.",
183-
Args: cobra.ExactArgs(1),
181+
Hidden: true,
182+
Short: "Show why a line exists",
183+
Long: "Explain the commit, checkpoint, prompt, and session behind a file or line.\n\nTarget a specific line with <file>:12 or the --line flag.",
184+
Example: " entire why src/auth.go:42\n entire why src/auth.go:42 --json",
185+
Args: cobra.ExactArgs(1),
184186
RunE: func(cmd *cobra.Command, args []string) error {
185187
return runAttributionWhy(cmd.Context(), cmd.OutOrStdout(), args[0], attributionWhyOptions{
186188
LineFlag: lineFlag,

cmd/entire/cli/checkpoint_group.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ Examples:
5050
func newCheckpointSearchCmd() *cobra.Command {
5151
cmd := newSearchCmd()
5252
cmd.Hidden = false
53+
// newSearchCmd's examples use the `entire search` prefix for that top-level
54+
// alias; under the canonical `checkpoint` group they must match this path.
55+
cmd.Example = " entire checkpoint search \"retry backoff\" --json\n entire checkpoint search \"auth timeout author:alice date:week\"\n entire checkpoint search --code \"parseToken\""
5356
return cmd
5457
}
5558

cmd/entire/cli/checkpoint_tokens.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ from the checkpoint remote.
8484
8585
Use --compare <checkpoint-id> to compare this checkpoint against a previous
8686
checkpoint and qualify observed token reduction or increase.`,
87-
Args: cobra.ExactArgs(1),
87+
Example: " entire checkpoint tokens a1b2\n entire checkpoint tokens a1b2 --compare c3d4\n entire checkpoint tokens a1b2 --json",
88+
Args: cobra.ExactArgs(1),
8889
RunE: func(cmd *cobra.Command, args []string) error {
8990
if jsonFlag && agentBriefFlag {
9091
return errors.New("--json and --agent-brief are mutually exclusive")

cmd/entire/cli/experts_cmd.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,10 +182,18 @@ func (s expertsStyles) link(style lipgloss.Style, url, text string) string {
182182
func newExpertsCmd() *cobra.Command {
183183
f := &expertsFlags{limit: 8}
184184
cmd := &cobra.Command{
185-
Use: "experts [scope-or-query]",
186-
Short: "Rank agent provenance for code scopes",
187-
Hidden: true,
188-
Args: cobra.ArbitraryArgs,
185+
Use: "experts [scope-or-query]",
186+
Short: "Rank agent provenance for code scopes",
187+
Long: `Rank which agents, skills, and tools have provenance over a code scope — who
188+
and what has touched the given code.
189+
190+
The argument is either a single scope (a file or directory path) or a
191+
natural-language query. A scope or query is required unless --staged is set,
192+
which uses the staged file paths as scopes instead. Results come from the
193+
entire-api cell keyed on the repo, so the repo must be mirrored.`,
194+
Example: " entire experts src/payments --json\n entire experts \"who owns token refresh\" --json\n entire experts --staged",
195+
Hidden: true,
196+
Args: cobra.ArbitraryArgs,
189197
RunE: func(cmd *cobra.Command, args []string) error {
190198
return runExperts(cmd.Context(), cmd.OutOrStdout(), cmd.ErrOrStderr(), args, f)
191199
},

cmd/entire/cli/lifecycle.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -416,20 +416,24 @@ func normalizeToolUsePaths(files []string, eventCWD, repoRoot string) []string {
416416
// handleLifecycleTurnStart handles turn start: captures pre-prompt state,
417417
// ensures strategy setup, initializes session.
418418
// entireTrailContextInjection is the one-time, model-facing pointer Entire
419-
// injects on the first turn of a session. It deliberately enumerates NO flags or
420-
// subcommands — that surface is fetched on demand via `entire agent-help`, which
421-
// always matches the installed CLI — so the injection never goes stale when the
422-
// command surface grows. It names the auto-detected repo (from the already-loaded
423-
// session scope, no IO) and carries the standing rule that the agent is inside
424-
// the repo and must never ask the user for the repo name. Kept terse: it costs
425-
// context-window tokens on the first turn of every session.
419+
// injects on the first turn of a session. It points at `entire agent-help` for
420+
// the full flag/subcommand surface — fetched on demand so that surface never goes
421+
// stale here as it grows — and adds only a small, stable behavioral invariant an
422+
// agent must know even if it never drills in: commits auto-capture checkpoints,
423+
// the two stable query anchors (`why`, `checkpoint search`) for recovering intent
424+
// before edits, and that setup/destructive commands belong to the user. It also
425+
// names the auto-detected repo (from the already-loaded session scope, no IO) and
426+
// the standing rule that the agent is inside the repo and must never ask the user
427+
// for the repo name. Kept terse: it costs context-window tokens on the first turn
428+
// of every session.
426429
func entireTrailContextInjection(scope trailEnablementScope) string {
427430
repo := ""
428431
if scope.Forge != "" && scope.Owner != "" && scope.Repo != "" {
429432
repo = trailEnablementRepoKey(scope.Forge, scope.Owner, scope.Repo)
430433
}
431434
var b strings.Builder
432435
b.WriteString("Entire is enabled for this repo. Run `entire agent-help` to see what entire does and which subcommand to use, then `entire agent-help <command>` for that command's exact, current flags. ")
436+
b.WriteString("Commits automatically capture the AI session as a checkpoint, so never create checkpoints by hand — just commit normally. Before large edits, `entire why <file>:<line>` and `entire checkpoint search` recover the intent behind existing code. Leave setup and destructive commands (enable, disable, clean, rewind, auth) to the user. ")
433437
// Mirror agentHelpRepoBlock's defense-in-depth: this string is injected raw
434438
// into the agent's model context (no escaping), so a repo key carrying control
435439
// characters (e.g. an <sessionID>.trail-scope.json cache written by a pre-fix

cmd/entire/cli/search_cmd.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,9 @@ displayed in an interactive table. Use --json for machine-readable output.
5555
5656
CLI queries also support inline filters like author:<name>, date:<week|month>,
5757
branch:<name>, repo:<owner/name>, and repo:* to search all accessible repos.`,
58-
Args: cobra.ArbitraryArgs,
59-
Hidden: true,
58+
Example: " entire search \"retry backoff\" --json\n entire search \"auth timeout author:alice date:week\"\n entire search --code \"parseToken\"",
59+
Args: cobra.ArbitraryArgs,
60+
Hidden: true,
6061
RunE: func(cmd *cobra.Command, args []string) error {
6162
ctx := cmd.Context()
6263
query := strings.Join(args, " ")

cmd/entire/cli/search_cmd_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,23 @@ func TestSearchCmd_AccessibleModeRequiresQuery(t *testing.T) {
4242
}
4343
}
4444

45+
// Each instance's examples must use its own command path: the top-level alias
46+
// is `entire search`, the canonical form under the checkpoint group is
47+
// `entire checkpoint search`. A shared prefix would mislead one command's help.
48+
func TestSearchCmd_ExamplesMatchCommandPath(t *testing.T) {
49+
t.Parallel()
50+
51+
topLevel := newSearchCmd().Example
52+
if !strings.Contains(topLevel, "entire search ") || strings.Contains(topLevel, "checkpoint search") {
53+
t.Fatalf("top-level search examples must use the `entire search` prefix:\n%s", topLevel)
54+
}
55+
56+
checkpoint := newCheckpointSearchCmd().Example
57+
if !strings.Contains(checkpoint, "entire checkpoint search ") {
58+
t.Fatalf("checkpoint search examples must use the `entire checkpoint search` prefix:\n%s", checkpoint)
59+
}
60+
}
61+
4562
func TestSearchCmd_HelpMentionsRepoFlagAndInlineFilters(t *testing.T) {
4663
t.Parallel()
4764

cmd/entire/cli/session_tokens.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ already captured for the session.
9595
Use --agent-brief when an agent needs compact guidance for the next step, for
9696
example: "Use Entire token tracking to check how this session is doing and
9797
optimize next steps."`,
98-
Args: cobra.MaximumNArgs(1),
98+
Example: " entire session tokens\n entire session tokens --current --agent-brief\n entire session tokens --json",
99+
Args: cobra.MaximumNArgs(1),
99100
RunE: func(cmd *cobra.Command, args []string) error {
100101
if jsonFlag && agentBriefFlag {
101102
return errors.New("--json and --agent-brief are mutually exclusive")

0 commit comments

Comments
 (0)