Skip to content
Merged
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
50 changes: 25 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,28 @@
# agent-init

`agent-init` adds an agentic development workflow to a Go, TypeScript, or Infrastructure-as-Code project — primarily by enhancing one you already have, optionally by bootstrapping a new repo from scratch.
`agent-init` sets a repository up for sandboxed agentic development. It drops in a devcontainer, an `AGENTS.md`/`CLAUDE.md` house-rules pair, a `check.sh` "done-gate", a `Justfile`, pre-commit hooks, and a codebase map — all tuned to a chosen project flavor.

It also supports project management workspaces, by bootstrapping agents and folder structures as well as MCP integrations for Azure DevOps, Jira or GitHub.
The primary use is **adding this envelope to a project you already have**; it can also bootstrap a fresh repo from scratch. Beyond code, it scaffolds two non-code workspaces: a **project-management** workspace (epics, decisions, stakeholders, plus MCP tracker integrations for Jira, Azure DevOps, or GitHub) and a **Claude Cowork** folder for document-centred analysis and design work.

In addition you can bootstrap a workspace for claude cowork, useful for projects centered around documents, analysis and design rather than implementation.
## Quickstart

## Documentation

This README is the front door. The full reference lives under [`docs/`](./docs/):

- [`docs/README.md`](./docs/README.md) — the docs index and per-feature documentation convention.
- [`docs/cli.md`](./docs/cli.md) — full CLI reference: every subcommand, flag, and exit behavior.
- [`docs/engine/flavor-hooks.md`](./docs/engine/flavor-hooks.md) — the per-flavor engine hooks (`Symlinks`, `NextSteps`, `CommonTemplates`) that let one engine serve code and non-code flavors.
```bash
# Add the agentic envelope to an existing Go CLI — none of your source is touched.
agent-init init go-cli --agents-only ~/repos/my-cli

One page per flavor:
# Or scaffold a brand-new project from a flavor.
agent-init init go-cli ./my-new-tool

- [`docs/flavors/fullstack.md`](./docs/flavors/fullstack.md) — TypeScript/Node frontend + backend, Playwright recording, OpenAPI clients.
- [`docs/flavors/go-cli.md`](./docs/flavors/go-cli.md) — Go command-line tool; includes a worked `--agents-only` example.
- [`docs/flavors/go-backend.md`](./docs/flavors/go-backend.md) — Go HTTP backend with `/healthz`.
- [`docs/flavors/iac.md`](./docs/flavors/iac.md) — combined Terraform + Ansible scaffold.
- [`docs/flavors/claude-cowork.md`](./docs/flavors/claude-cowork.md) — OneDrive-backed document-collaboration workspace.
- [`docs/flavors/project-management.md`](./docs/flavors/project-management.md) — project-management workspace; ships five skills and tracker integrations.
# See every flavor, or drill into any command.
agent-init list-flavors
agent-init --help
```

## What it's for

The primary use case is **adding agents to existing projects**. Most code already exists. Run:

```bash
agent-init init <flavor> --agents-only ./your-existing-repo
```

…and the scaffold drops in just the agentic envelope — a devcontainer, an `AGENTS.md`/`CLAUDE.md` pair (the agent's house rules), helper scripts, a `Justfile`, and pre-commit hooks — **without touching** your `go.mod`, `package.json`, `main.go`, or any existing source. The "fresh project" mode (omit `--agents-only`) is supported and useful when you genuinely are starting from zero, but it's the secondary path.
Adding agents to a project you already have is the main path, and the scaffold is careful about it. `--agents-only` drops in just the envelope — devcontainer, `AGENTS.md`/`CLAUDE.md` house rules, helper scripts, `Justfile`, pre-commit hooks — **without touching** your `go.mod`, `package.json`, `main.go`, or any existing source. Omit the flag and you also get the flavor's full fresh-project layout, useful when you're starting from zero.

Two things this is trying to enforce:
Two things the envelope is built to enforce:

- **Sandbox the agent.** The devcontainer is the work surface. Agents run inside it with a bounded toolchain (Go, Node, lint, security scanners — whatever the chosen flavor needs) and write only into the mounted workspace. Credentials, host SSH keys, and cloud configs are explicit opt-in mounts — commented out in `devcontainer.json` by default so the agent doesn't pick them up unless you choose to share them.
- **Force agents to verify.** Every scaffolded project ships a `check.sh` "done-gate" that runs codemap regeneration, formatting, linting, type-checking, tests, and (per flavor) cross-builds, vulnerability scans, or security scans. The agent's contract — encoded in the generated `AGENTS.md` — is: don't declare a task complete until `./.agent/scripts/check.sh` passes. This is the simplest mechanism we've found that stops agents from cutting work short with "I think it's done."
Expand All @@ -49,6 +38,17 @@ Two things this is trying to enforce:
| [`project-management`](./docs/flavors/project-management.md) | Project-management workspace (epics, meetings, decisions, stakeholders, time plans). Ships five skills (`/intake-meeting`, `/break-down-epic`, `/log-decision`, `/track-stakeholder`, `/sync-tracker`) and supports MCP tracker integrations via `agent-init add-tracker {jira\|ado\|gh}`. |
| [`iac`](./docs/flavors/iac.md) | Combined Terraform + Ansible scaffold. Ships `terraform/` (root module, `modules/`) and `ansible/` (`inventory/`, `playbooks/`, `roles/`) trees, a devcontainer with `terraform` + `tflint` + `tfsec` + `trivy` + `ansible-core` + `ansible-lint` + `yamllint`, and a Justfile whose recipes auto-detect which toolchain is present. Cloud-credential and `~/.ssh` mounts are commented out by default with a warning. |

## Documentation

This README is the front door. The full reference lives under [`docs/`](./docs/):

- [`docs/cli.md`](./docs/cli.md) — full CLI reference: every subcommand, flag, and exit behavior.
- [`docs/engine/flavor-hooks.md`](./docs/engine/flavor-hooks.md) — the per-flavor engine hooks (`Symlinks`, `NextSteps`, `CommonTemplates`) that let one engine serve code and non-code flavors.
- [`docs/engine/releases.md`](./docs/engine/releases.md) — the tag-driven release and build-provenance flow.
- [`docs/README.md`](./docs/README.md) — the docs index and per-feature documentation convention.

Each flavor has its own page under [`docs/flavors/`](./docs/flavors/), linked from the table above.

## Build

```bash
Expand Down
35 changes: 15 additions & 20 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,38 +23,33 @@ docs/
├── cli.md # subcommands + flags (DONE)
├── engine/
│ ├── flavor-hooks.md # Symlinks, NextSteps, CommonTemplates (DONE)
│ ├── templates.md # .tmpl content substitution
│ ├── path-templating.md
│ ├── common-overlay.md
│ ├── done-gate.md
│ └── releases.md # tag-driven release flow (DONE)
│ ├── releases.md # tag-driven release flow (DONE)
│ ├── templates.md # .tmpl content substitution (TODO)
│ ├── path-templating.md # {{.ProjectName}} in file paths (TODO)
│ ├── common-overlay.md # common/ fallback layer (TODO)
│ └── done-gate.md # what check.sh runs downstream (TODO)
└── flavors/
├── fullstack.md # (DONE)
├── go-cli.md # (DONE — worked --agents-only example)
├── go-backend.md # (DONE)
├── iac.md # (DONE — Terraform + Ansible)
├── claude-cowork.md # (DONE)
├── project-management.md # (DONE — with worked examples)
├── fullstack.md
├── go-cli.md
└── go-backend.md
└── project-management.md # (DONE — worked skill examples)
```

Not all of these exist yet. The backlog below lists what's needed.
Every flavor and subcommand is documented. The four engine docs marked `TODO` are the remaining gap; the backlog below tracks them.

## Backlog

Features that exist in the code but don't yet have a doc entry. The next agent to touch one of these is on the hook to write its doc.
Features that exist in the code but don't yet have a doc entry. The next agent to touch one of these is on the hook to write its doc. Every flavor is documented; the remaining gap is four engine-capability docs.

### Engine

- [ ] `engine/templates.md` — `.tmpl` opt-in for content substitution. Source: [internal/scaffold/scaffold.go:155-167](../internal/scaffold/scaffold.go#L155-L167).
- [ ] `engine/path-templating.md` — `{{.ProjectName}}` in file paths; the `.tmpl` workaround for `cmd/{{.ProjectName}}/` directories. Source: [internal/scaffold/scaffold.go:169-182](../internal/scaffold/scaffold.go#L169-L182).
- [ ] `engine/common-overlay.md` — `internal/flavors/common/` as a fallback layer; flavor-first conflict resolution. Source: [internal/flavors/common/flavor.go](../internal/flavors/common/flavor.go), [internal/scaffold/scaffold.go:80-114](../internal/scaffold/scaffold.go#L80-L114).
- [ ] `engine/templates.md` — `.tmpl` opt-in for content substitution. Source: [scaffold.go:306-318](../internal/scaffold/scaffold.go#L306-L318) (content render) and the `.tmpl` strip in [walkLayer, scaffold.go:163](../internal/scaffold/scaffold.go#L163).
- [ ] `engine/path-templating.md` — `{{.ProjectName}}` in file paths; the `.tmpl` workaround for `cmd/{{.ProjectName}}/` directories. Source: [renderPath, scaffold.go:320](../internal/scaffold/scaffold.go#L320).
- [ ] `engine/common-overlay.md` — `internal/flavors/common/` as a fallback layer; flavor-first conflict resolution. Source: [Overlay, scaffold.go:102](../internal/scaffold/scaffold.go#L102) and [walkLayer, scaffold.go:163](../internal/scaffold/scaffold.go#L163).
- [ ] `engine/done-gate.md` — what `check.sh` runs in scaffolded projects and how `maybe_step` skips missing recipes. Source: [internal/flavors/common/templates/.agent/scripts/check.sh](../internal/flavors/common/templates/.agent/scripts/check.sh).

### Flavors

- [x] `flavors/fullstack.md` — TypeScript/Node frontend + backend; supports `--agents-only`.
- [x] `flavors/go-cli.md` — fresh-project Go CLI scaffold + `--agents-only` mode for existing projects.
- [x] `flavors/go-backend.md` — Go HTTP backend scaffold + `--agents-only` mode.

## Style

- Plain prose. No emojis. No marketing adjectives ("powerful", "elegant", "seamless").
Expand Down
7 changes: 4 additions & 3 deletions docs/cli.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CLI

`agent-init` is a small CLI with six subcommands. Source: [internal/cli/cli.go](../internal/cli/cli.go).
`agent-init` is a small CLI with seven subcommands. Source: [internal/cli/cli.go](../internal/cli/cli.go).

```
agent-init init [flavor] [target-dir]
Expand Down Expand Up @@ -44,7 +44,7 @@ agent-init ./my-tool # path-only form; implies fullstack
- With `--visibility=local`: after the scaffold is written (symlinks and `git init` included — visibility controls tracking, not creation), a fenced block is appended to the committed `.gitignore`, creating it if absent. The block covers the agentic envelope (`.agent/`, `/AGENTS.md`, `/CLAUDE.md`, `.devcontainer/`, `/Justfile`, `.pre-commit-config.yaml`). It is delimited by `# >>> agent-init (private) >>>` / `# <<< agent-init <<<` markers, so re-running replaces it in place (never duplicates) and it can be removed by hand to undo. `init` prints the absolute path it edited. `--dry-run` previews the path and block, writing nothing. Block management lives in [internal/gitignore](../internal/gitignore/gitignore.go).
- With `--visibility=hidden` (or `--private`): the identical block is written to `.git/info/exclude` instead of `.gitignore`, creating the `.git/info` directory if absent. `.git/info/exclude` is git's per-repo, never-committed ignore file, so a teammate cloning the repo sees no agent-init trace. The mode is otherwise the same as `local`: idempotent in-place replacement, the absolute path is announced, `--dry-run` previews and writes nothing, and the symlink trio is still created (visibility controls tracking, not creation). Because `.git/info/exclude` does not appear in `git diff`, remember to remove the fenced block by hand to undo.
- With `--visibility=global-default`: the **same** fenced block is written to your machine-wide git excludes file instead of a repo file, so the scaffold is ignored in **every** git repository on the machine. **This is action-at-a-distance.** The command prints a loud machine-wide warning on stderr and always announces the absolute path it edited. The target file is `git config --global core.excludesfile` if set (honored even when it points somewhere unusual); otherwise `${XDG_CONFIG_HOME:-~/.config}/git/ignore`, which is created and set as `core.excludesfile` only when no global excludes is configured. No other global-config key is touched. Idempotent (the marked block is replaced in place) and reversible (remove the block by hand). `--dry-run` resolves and prints the target path and the block but writes nothing and touches no git config. To commit the scaffold openly in a specific repo despite the global default, force-add it there — `git add -f .agent AGENTS.md CLAUDE.md .devcontainer Justfile .pre-commit-config.yaml` — since git never re-ignores a tracked file (gitignore negation cannot re-include a file under an excluded directory, so force-add is the documented override). The global excludes-file resolution lives in [internal/gitconfig](../internal/gitconfig/gitconfig.go); the block content is shared from [internal/gitignore](../internal/gitignore/gitignore.go).
- Source: [scaffold.go:31](../internal/scaffold/scaffold.go#L31) (`Run`), [cli.go:applyVisibility](../internal/cli/cli.go).
- Source: [scaffold.go:62](../internal/scaffold/scaffold.go#L62) (`Run`), [cli.go:applyVisibility](../internal/cli/cli.go).

### Output

Expand Down Expand Up @@ -137,6 +137,7 @@ claude-cowork Shared document-collaboration folder ...
fullstack TypeScript/Node frontend and backend ...
go-backend Go HTTP backend scaffold ...
go-cli Go command-line tool scaffold ...
iac Combined Terraform + Ansible scaffold ...
project-management Project-management workspace ...
```

Expand Down Expand Up @@ -229,7 +230,7 @@ Invalid input prints a short hint and points the user at `--help`, then exits
non-zero. Specific cases worth knowing:

- **Unknown subcommand** prints `unknown command "foo"` followed by `Run 'agent-init --help' for usage`.
- **Unknown flavor** prints the list of known flavors: `unknown flavor "foo" (known: claude-cowork, fullstack, go-backend, go-cli, project-management)`, then the init `--help` hint.
- **Unknown flavor** prints the list of known flavors: `unknown flavor "foo" (known: claude-cowork, fullstack, go-backend, go-cli, iac, project-management)`, then the init `--help` hint.
- **Unknown tracker** prints the list of known trackers, then the add-tracker `--help` hint.
- **`add-tracker` on a target without `.mcp.json`** suggests the corresponding `init` command.

Expand Down
10 changes: 5 additions & 5 deletions docs/engine/flavor-hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ Per-flavor customization points on the `Flavor` struct that let one engine suppo

| Field on `Flavor` | Type | Default when nil/empty | Source |
|---|---|---|---|
| `Symlinks` | `[]Symlink` | No symlinks created | [flavor.go:37-40](../../internal/flavors/flavor.go#L37-L40) |
| `Symlinks` | `[]Symlink` | No symlinks created | [flavor.go:25](../../internal/flavors/flavor.go#L25) (field); [flavor.go:55-58](../../internal/flavors/flavor.go#L55-L58) (type) |
| `NextSteps` | `func(target string) string` | Default code-project message (devcontainer + just check) | [flavor.go:30](../../internal/flavors/flavor.go#L30) |
| `CommonTemplates` | `fs.FS` | No common-overlay layer walked | [flavor.go:18-19](../../internal/flavors/flavor.go#L18-L19) |
| `CommonTemplates` | `fs.FS` | No common-overlay layer walked | [flavor.go:22](../../internal/flavors/flavor.go#L22) |

## Symlinks

Expand All @@ -19,9 +19,9 @@ type Symlink struct {
}
```

`createSymlinks` ([scaffold.go:209](../../internal/scaffold/scaffold.go#L209)) iterates `opts.Flavor.Symlinks` and creates each in order. `Target` is written verbatim — relative-path conventions like `.agent/AGENTS.md` survive into the scaffolded tree. The engine creates parent directories as needed but won't replace existing directories with symlinks (returns an error instead).
`createSymlinks` ([scaffold.go:335](../../internal/scaffold/scaffold.go#L335)) iterates `opts.Flavor.Symlinks` and creates each in order. `Target` is written verbatim — relative-path conventions like `.agent/AGENTS.md` survive into the scaffolded tree. The engine creates parent directories as needed but won't replace existing directories with symlinks (returns an error instead).

**Code-flavor convention.** All three code flavors share `codeFlavorSymlinks()` in [registry.go:70-76](../../internal/flavors/registry.go#L70-L76):
**Code-flavor convention.** All four code flavors (`fullstack`, `go-cli`, `go-backend`, `iac`) share `codeFlavorSymlinks()` in [registry.go:145-151](../../internal/flavors/registry.go#L145-L151):

```go
{Path: "AGENTS.md", Target: ".agent/AGENTS.md"},
Expand All @@ -39,7 +39,7 @@ Four entry points (root `AGENTS.md`, root `CLAUDE.md`, `.agent/AGENTS.md`, `.age
NextSteps func(target string) string
```

If set, the engine calls `flavor.NextSteps(target)` after writing and prints the returned string verbatim ([scaffold.go:272-289](../../internal/scaffold/scaffold.go#L272-L289)). If nil, the engine prints its default code-project message — devcontainer up, devcontainer exec, just check.
If set, the engine calls `flavor.NextSteps(target)` after writing and prints the returned string verbatim (`printNextSteps`, [scaffold.go:470](../../internal/scaffold/scaffold.go#L470)). If nil, the engine prints its default code-project message — devcontainer up, devcontainer exec, just check.

The signature takes the scaffold target so flavors can interpolate paths into shell commands the user is about to copy:

Expand Down
2 changes: 1 addition & 1 deletion docs/flavors/claude-cowork.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The post-scaffold message ([flavor.go:25-43](../../internal/flavors/claudecowork

These differences are flavor-controlled, not engine-hardcoded. The relevant engine hooks:

- **Symlinks** are declarative via `Flavor.Symlinks` ([flavor.go:25](../../internal/flavors/flavor.go#L25); type definition at [flavor.go:37-40](../../internal/flavors/flavor.go#L37-L40)); claude-cowork sets it to nil.
- **Symlinks** are declarative via `Flavor.Symlinks` ([flavor.go:25](../../internal/flavors/flavor.go#L25); type definition at [flavor.go:55-58](../../internal/flavors/flavor.go#L55-L58)); claude-cowork sets it to nil.
- **Next-steps message** is a per-flavor `NextSteps func(target string) string` hook ([flavor.go:26-30](../../internal/flavors/flavor.go#L26-L30)); claude-cowork supplies its own.
- **`CommonTemplates`** is optional; flavors that don't set it skip the overlay.

Expand Down
Loading