Pattern under test

autohand.git-insights contributes tools only. It proves that a manifest can declare multiple contributions and that registration does not depend on filesystem enumeration order.

autohand.git-insights/
  autohand.extension.json
  README.md
  tools/recent-history.json
  tools/changed-files.json

Create the manifest

{
  "$schema": "https://raw.githubusercontent.com/autohandai/code-extensions/main/schema/autohand.extension.schema.json",
  "schemaVersion": 1,
  "extensionApi": 1,
  "id": "autohand.git-insights",
  "name": "Git Insights",
  "version": "1.0.0",
  "description": "Inspect recent history and changed files with reusable Git tools.",
  "license": "Apache-2.0",
  "repository": "https://github.com/autohandai/code-extensions",
  "contributes": {
    "tools": ["tools/recent-history.json", "tools/changed-files.json"]
  }
}

The declared order is clear to readers, but runtime identity is the tool name. Every package and contribution is processed deterministically, and duplicate contribution paths or names are rejected.

Add bounded recent history

{
  "name": "recent_history",
  "description": "Show a bounded number of recent commits",
  "parameters": {
    "type": "object",
    "properties": {
      "count": {
        "type": "number",
        "description": "Maximum number of commits"
      }
    },
    "required": ["count"]
  },
  "handler": "git log --max-count={{count}} --oneline",
  "source": "user"
}

Bounding output is part of tool design. The parameter schema requires a number, while the fixed --max-count option keeps the command's purpose stable.

Add changed-file evidence

{
  "name": "changed_files_since",
  "description": "List files changed between a base revision and HEAD",
  "parameters": {
    "type": "object",
    "properties": {
      "base": {
        "type": "string",
        "description": "Base branch, tag, or commit"
      }
    },
    "required": ["base"]
  },
  "handler": "git diff --name-only {{base}}...HEAD",
  "source": "user"
}

Three-dot diff semantics compare the merge base of base and HEAD to HEAD. Document this because two-dot and three-dot ranges answer different questions.

Install into a repository

autohand --path /work/sample extensions validate ./autohand.git-insights
autohand --path /work/sample extensions install ./autohand.git-insights --scope project
autohand --path /work/sample extensions show autohand.git-insights --scope project --json
autohand --path /work/sample extensions doctor

The JSON report should contain exactly recent_history and changed_files_since, no agents, project scope, enabled state, and copied installation.

Exercise both tools

Use a repository with at least three commits and a feature branch that diverges from main. Ask:

Use recent_history with count 3. Then use changed_files_since with base main.
Explain which commits and files are part of this branch. Do not modify the repo.

Confirm that output is limited, revision errors are returned truthfully, and a read-only description does not imply automatic approval. Permission policy and hooks still apply.

Test deterministic conflict handling

  1. Create a second fixture extension that also declares recent_history.
  2. Validate both packages independently.
  3. Install the real package, then attempt to install the conflicting fixture.
  4. Confirm installation fails with the existing owner id rather than selecting whichever directory was read first.
  5. Run doctor and confirm the active package remains healthy.

Remove the package

autohand --path /work/sample extensions remove autohand.git-insights --scope project --yes
autohand --path /work/sample extensions list --scope project
autohand --path /work/sample extensions doctor

Neither tool should remain in a fresh session. Any unrelated extension in the same project root must remain installed and active.