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
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Why not just write your own commands? → [FAQ](docs/FAQ.md#-why-aidd-instead-of

## ✅ Prerequisites

- **An AI coding tool** — Claude Code (native), or Cursor / Copilot / Codex / OpenCode / Kilo Code (see [Compatibility](#-compatibility)).
- **An AI coding tool** — Claude Code (native), or Cursor / Copilot / Codex / OpenCode / Kilo Code / Antigravity CLI (see [Compatibility](#-compatibility)).
- **[Node](https://nodejs.org) 22 or later** on your `PATH`, only for the plugin that ships hooks ([what they do](docs/ARCHITECTURE.md#-bundled-hooks)); the workflows themselves are markdown and need nothing.

## 🔌 Compatibility
Expand All @@ -50,6 +50,7 @@ Why not just write your own commands? → [FAQ](docs/FAQ.md#-why-aidd-instead-of
| **Codex** | ✅ Supported | Marketplace · Flat |
| **OpenCode** | ✅ Supported | Flat |
| **Kilo Code** | ✅ Supported | Flat |
| **Antigravity CLI** | ✅ Supported | Flat |
| **Gemini · Mistral** | 🚧 In progress | — |

<sub>**Marketplace** = installed and updated through your tool's plugin manager. **Flat** = files copied directly into your project, no plugin manager involved. Install steps per tool → [Other tools](#other-tools).</sub>
Expand Down Expand Up @@ -185,6 +186,18 @@ codex plugin add aidd-context@aidd-framework # per plugin

</details>

<details>
<summary><strong>Antigravity CLI</strong> — Flat only</summary>

1. Unzip the `antigravity-flat` archive into your project root → `.agents/`: skills, agents, and the memory hook in `.agents/hooks.json`.
2. Start `agy` in the project. A hook already in `.agents/hooks.json` under another name is kept.

Codex flat also writes `.agents/skills/`: removing one of the two from a project that has both deletes skills the other still uses.

[Docs](https://antigravity.google/docs/cli/overview)

</details>

## 🚀 Quick start

Three ways in — pick one:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
status: pending
status: done
---

<!-- Fill or omit these sections; never add, rename, or reorder one. -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
objective: "`aidd setup --ai antigravity` and `aidd translate --to antigravity --as flat` write AIDD skills, the memory hook and agents under `.agents/`, which `agy` loads, without touching files AIDD did not write."
status: in-progress
status: implemented
---

<!-- Fill or omit these sections; never add, rename, or reorder one. -->
Expand Down
5 changes: 3 additions & 2 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
`@ai-driven-dev/cli` installs AI tool runtime configs, IDE integrations, and plugins from an AIDD marketplace into a project.
Every file it writes is hash-tracked in a manifest, so drift is detected and owned files can be restored.

Supported AI tools: Claude Code, Cursor, GitHub Copilot, Codex, OpenCode, Kilo Code. Supported IDE: VS Code.
Supported AI tools: Claude Code, Cursor, GitHub Copilot, Codex, OpenCode, Kilo Code, Antigravity CLI. Supported IDE: VS Code.
Requires Node.js >= 22.12, and `git` to fetch marketplace plugins.

## Install
Expand Down Expand Up @@ -139,8 +139,9 @@ Two output layouts, chosen by `--as`:
| `codex` | yes | yes | `.codex/` |
| `opencode` | no | yes | `.opencode/` |
| `kilo` | no | yes | `.kilo/` |
| `antigravity` | no | yes | `.agents/` |

OpenCode and Kilo Code declare no marketplace contract, so they are flat only. Every other target accepts both layouts.
OpenCode, Kilo Code and Antigravity CLI declare no marketplace contract, so they are flat only. Every other target accepts both layouts.

## Environment variables

Expand Down
1 change: 1 addition & 0 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"smoke:full": "pnpm build && SMOKE_REMOTE=1 bash scripts/smoke-tools.sh",
"smoke:real": "pnpm build && bash scripts/smoke-real.sh",
"smoke:collision": "pnpm build && bash scripts/smoke-collision.sh",
"smoke:antigravity": "pnpm build && bash scripts/smoke-antigravity.sh",
"typecheck": "tsc --noEmit",
"lint": "biome check .",
"format": "biome format --write .",
Expand Down
130 changes: 130 additions & 0 deletions cli/scripts/smoke-antigravity.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#!/usr/bin/env bash
# Runtime smoke against a real, logged-in `agy`: after `aidd setup --ai antigravity`, the host
# loads and fires the project memory hook, expands an AIDD skill, and lists an AIDD agent.
# Local only, never in CI or lefthook: a turn needs a Google login. `pnpm smoke:antigravity`.
#
# The hook sessions run interactive under a pty, killed after a bound: older `agy` fired no hook in
# print mode (`-p`), and a print turn waits on every user MCP server. Its log and the files it
# touched are the evidence. HOME stays real for the login; `AIDD_USER_CONFIG_DIR` is relocated so
# setup never touches the real aidd profile.
set -uo pipefail

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
FRAMEWORK="$(cd "$ROOT/.." && pwd)"
CLI="$ROOT/dist/cli.js"
TURN_SECONDS="${SMOKE_AGY_TIMEOUT:-60}"

SKILL="aidd-context-11-explore"
AGENT="aidd-dev-executor"
GHOST_AGENT="aidd-dev-ghost"

command -v agy >/dev/null 2>&1 || { echo "FATAL: agy not on PATH — install Antigravity CLI and log in"; exit 1; }
[[ -f "$CLI" ]] || { echo "FATAL: $CLI missing — run 'pnpm build' first"; exit 1; }

TMP="$(mktemp -d)"
WS="$TMP/project"
SESSION_PID=""
cleanup() {
if [[ -n "$SESSION_PID" ]]; then
pkill -P "$SESSION_PID" 2>/dev/null
kill "$SESSION_PID" 2>/dev/null
fi
rm -rf "$TMP"
}
trap cleanup EXIT

export AIDD_USER_CONFIG_DIR="$TMP/aidd-config"
export AIDD_SKIP_UPDATE_CHECK=1

# Under a pseudo-terminal: util-linux script(1) takes the command as one -c string, BSD as arguments.
pty_run() {
if script --version >/dev/null 2>&1; then
script -qec "$(printf '%q ' "$@")" /dev/null
else
script -q /dev/null "$@"
fi
}

# How many named hooks agy loads, from its log: user-global hooks included.
hook_count() { grep -Eo 'loaded [0-9]+ named hooks' "$1" 2>/dev/null | head -1 | grep -Eo '[0-9]+'; }

FAIL=0
pass() { echo " PASS $1"; echo " $2"; }
fail() { echo " FAIL $1"; FAIL=$((FAIL + 1)); }

echo "agy $(agy --version 2>&1 | head -1), $(date '+%Y-%m-%d %H:%M %Z')"

# A project with a memory bank and an empty memory block, so the hook has something to fill.
mkdir -p "$WS/aidd_docs/memory"
printf '# Architecture\n' >"$WS/aidd_docs/memory/architecture.md"
printf '# Project\n\n## Memory Management\n\n<!-- aidd_project_memory:start -->\n<!-- aidd_project_memory:end -->\n' >"$WS/AGENTS.md"
git -C "$WS" init -q
git -C "$WS" -c user.email=smoke@aidd -c user.name=smoke commit -q --allow-empty -m init

if ! (cd "$WS" && node "$CLI" setup --source local --path "$FRAMEWORK" --ai antigravity --plugins recommended --yes) >"$TMP/setup.log" 2>&1; then
echo "FATAL: aidd setup failed"; tail -5 "$TMP/setup.log"; exit 1
fi
for f in ".agents/skills/$SKILL/SKILL.md" ".agents/hooks.json" ".agents/agents/$AGENT/agent.md"; do
[[ -f "$WS/$f" ]] || { echo "FATAL: setup did not write $f"; exit 1; }
done

# One interactive session logging to $1, killed once `$2…` succeeds or the bound runs out.
session() {
local log="$1"; shift
(cd "$WS" && pty_run agy -i "reply OK" --mode plan --add-dir "$WS" --log-file "$log") >"$log.out" 2>&1 &
SESSION_PID=$!
for ((i = 0; i < TURN_SECONDS; i++)); do
"$@" && break
sleep 1
done
pkill -P "$SESSION_PID" 2>/dev/null
kill "$SESSION_PID" 2>/dev/null
wait "$SESSION_PID" 2>/dev/null
SESSION_PID=""
}

LOG="$TMP/agy-session.log"
session "$LOG" grep -q 'aidd_docs/memory/architecture.md' "$WS/AGENTS.md"

# Startup logs "not logged into Antigravity" before the stored login is read, so only a missing
# success line means no login.
if ! grep -q "OAuth: authenticated successfully" "$LOG" 2>/dev/null; then
echo "FATAL: agy is not logged in — run 'agy' once and sign in"; exit 1
fi

# The log never names a hook, and user-global hooks count too, so the project's own are proven by
# difference: the same session, in the same mode, without .agents/hooks.json must load fewer.
BASE_LOG="$TMP/agy-baseline.log"
[[ -f "$WS/.agents/hooks.json" ]] && mv "$WS/.agents/hooks.json" "$TMP/hooks.json"
session "$BASE_LOG" grep -qs 'named hooks' "$BASE_LOG"
[[ -f "$TMP/hooks.json" ]] && mv "$TMP/hooks.json" "$WS/.agents/hooks.json"
with="$(hook_count "$LOG")"
without="$(hook_count "$BASE_LOG")"
if [[ -n "$with" && -n "$without" && "$with" -gt "$without" ]]; then
pass "project hooks loaded" "$with named hooks with .agents/hooks.json, $without without"
else
fail "hooks: .agents/hooks.json adds no loaded hook (${with:-?} with, ${without:-?} without)"
fi

line="$(grep -m1 'aidd_docs/memory/architecture.md' "$WS/AGENTS.md")"
[[ -n "$line" ]] && pass "memory refreshed at the repo root" "AGENTS.md: $line" || fail "memory: AGENTS.md block still empty, the SessionStart hook did not run at the repo root"

# The expansion is logged before the model answers, so a short bound is enough.
SKILL_LOG="$TMP/agy-skill.log"
(cd "$WS" && agy --add-dir "$WS" --mode plan --print-timeout 20s --log-file "$SKILL_LOG" -p "/$SKILL") >/dev/null 2>&1
line="$(grep -o "expanded slash command \"$SKILL\" (skill)" "$SKILL_LOG" 2>/dev/null | head -1)"
[[ -n "$line" ]] && pass "skill expanded" "$line" || fail "skill: no 'expanded slash command \"$SKILL\"' line in the agy log"

# Print mode answers /agents without the model.
AGENTS_JSON="$TMP/agents.json"
(cd "$WS" && agy --add-dir "$WS" --output-format json --print-timeout "${TURN_SECONDS}s" -p "/agents") >"$AGENTS_JSON" 2>&1
line="$(grep -o "\"$AGENT\"" "$AGENTS_JSON" | head -1)"
if [[ -n "$line" ]] && ! grep -q "\"$GHOST_AGENT\"" "$AGENTS_JSON"; then
pass "agent listed" "/agents: $line, $GHOST_AGENT absent"
else
fail "agents: /agents does not list \"$AGENT\" (or lists the invented $GHOST_AGENT)"
fi

echo
[[ "$FAIL" -eq 0 ]] && { echo "smoke-antigravity: all passed"; exit 0; }
echo "smoke-antigravity: $FAIL failed"; exit 1
2 changes: 1 addition & 1 deletion docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ You can write your own Claude Code skills — nothing stops you. AIDD exists bec
- **Install / first run** → [Quick start](../README.md#-quick-start).
- **Update plugins** → `/plugin marketplace update aidd-framework`, or see [Versioning & updates](MARKETPLACE.md#-versioning--updates).
- **Private repo?** Yes — `/plugin marketplace add` just needs GitHub read access (via `gh auth login` or a PAT).
- **Cursor / Copilot / Codex / OpenCode / Kilo Code?** Each other tool installs via its own native mechanism (project files, local plugins, or a plugin command) from the [release](https://github.com/ai-driven-dev/framework/releases/latest) archives. Steps per tool → [Other tools](../README.md#other-tools).
- **Cursor / Copilot / Codex / OpenCode / Kilo Code / Antigravity CLI?** Each other tool installs via its own native mechanism (project files, local plugins, or a plugin command) from the [release](https://github.com/ai-driven-dev/framework/releases/latest) archives. Steps per tool → [Other tools](../README.md#other-tools).

## 💸 Cost and quotas

Expand Down
1 change: 1 addition & 0 deletions docs/MAINTAINERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ Roles, promotion, and inactivity rules → [`GOVERNANCE.md`](../GOVERNANCE.md#-r
- The marketplace is Claude Code native.
- Other tools are served by per-release archives the `aidd-cli` builds; this repo stays Claude-authored and tool-agnostic in its prose.
- Keep tool-specific detail in plugin READMEs.
- Antigravity CLI has no runtime check in CI: a turn needs a Google login. Before a release that touches its profile, run `cd cli && pnpm smoke:antigravity` on a machine where `agy` is logged in. It installs AIDD into a throwaway project, then checks the `agy` log for a skill expansion and the loaded hooks, the project memory for the hook's refresh, and `/agents` for an AIDD agent.

## 🧱 Build your own plugin

Expand Down
1 change: 1 addition & 0 deletions plugins/aidd-context/hooks/update_memory.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const TOOL_FILES = {
codex: "AGENTS.md",
cursor: "AGENTS.md",
opencode: "AGENTS.md",
antigravity: "AGENTS.md",
copilot: ".github/copilot-instructions.md",
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ A tool is used when its own dir exists, or when a file only that tool reads exis
| codex | `.codex/` | `AGENTS.md` |
| cursor | `.cursor/` or `.cursorrules` | `AGENTS.md` |
| opencode | `.opencode/` | `AGENTS.md` |
| antigravity | `.agents/agents/`, `.agents/rules/` or `.agents/hooks.json` | `AGENTS.md` |
| copilot | `.github/copilot-instructions.md` or `.github/{instructions,agents,skills,prompts}/` | `.github/copilot-instructions.md` |

- Detected tools only. An unused optional tool is omitted, never crossed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The AI tools a project can use.
| codex | `.codex/` | `AGENTS.md` |
| cursor | `.cursor/` or `.cursorrules` | `AGENTS.md` |
| opencode | `.opencode/` | `AGENTS.md` |
| antigravity | `.agents/agents/`, `.agents/rules/` or `.agents/hooks.json` | `AGENTS.md` |
| copilot | `.github/copilot-instructions.md` or `.github/{instructions,agents,skills,prompts}/` | `.github/copilot-instructions.md` |

- A shared `AGENTS.md` is a wiring target, never a detection signal.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ Which AI tools a project has installed.
| OpenCode | `.opencode/` |
| GitHub Copilot | `.github/copilot-instructions.md` |
| Codex CLI | `.codex/` |
| Antigravity CLI | `.agents/agents/`, `.agents/rules/` or `.agents/hooks.json` |

A bare `AGENTS.md` means Cursor, OpenCode, or Codex. Several signals can coexist.
A bare `AGENTS.md` means Cursor, OpenCode, Codex, or Antigravity CLI. `.agents/skills/` alone is no signal: Codex and Antigravity CLI both read it. Several signals can coexist.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Where a skill tree is written, per tool.
| OpenCode | `.opencode/skills/<name>/` |
| GitHub Copilot | `.github/skills/<name>/` |
| Codex CLI | `.agents/skills/<name>/` |
| Antigravity CLI | `.agents/skills/<name>/` |
| Plugin source | `plugins/<plugin>/skills/<name>/` |

## Frontmatter
Expand All @@ -23,3 +24,4 @@ Emit `description` always, `name` only where listed, drop the rest.
| Cursor, GitHub Copilot | `name`, `description`, opt `allowed-tools` |
| OpenCode | `description`, opt `permission` map |
| Codex CLI | `name`, `description` (strips the rest) |
| Antigravity CLI | `name`, `description` |
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@

## Contracts

[Claude](https://code.claude.com/docs/en/memory), [Cursor](https://cursor.com/docs/rules), [Cursor CLI](https://cursor.com/docs/cli/using#rules), [Copilot CLI](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-custom-instructions), [Copilot support](https://docs.github.com/en/copilot/reference/custom-instructions-support), [Codex](https://learn.chatgpt.com/docs/agent-configuration/agents-md), [OpenCode V1](https://opencode.ai/docs/rules/), [OpenCode V2](https://opencode.ai/v2/docs/instructions/).
[Claude](https://code.claude.com/docs/en/memory), [Cursor](https://cursor.com/docs/rules), [Cursor CLI](https://cursor.com/docs/cli/using#rules), [Copilot CLI](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-custom-instructions), [Copilot support](https://docs.github.com/en/copilot/reference/custom-instructions-support), [Codex](https://learn.chatgpt.com/docs/agent-configuration/agents-md), [OpenCode V1](https://opencode.ai/docs/rules/), [OpenCode V2](https://opencode.ai/v2/docs/instructions/), [Antigravity](https://antigravity.google/docs/rules).
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
| Copilot / `copilot` | `.github/instructions/<NN>-<name>.instructions.md` | comma-joined `applyTo`; all-files `**` |
| Codex / `codex` | root `AGENTS.md` | shared signed `aidd_rules`, textual scope |
| OpenCode / `opencode` | root `AGENTS.md` | same contribution |
| Antigravity / `antigravity` | `.agents/rules/<slug>.md` | with paths: `trigger: glob`, `description`, comma-joined `globs`; all-files: `trigger: always_on`, `description`, no `globs` |

Copilot `<name>` drops the slug's numeric prefix: category `01-standards`, slug `1-local-names` → `01-local-names.instructions.md`.

Antigravity reads only the direct `.md` children of `.agents/rules/`, so it drops the category folder and keeps the slug, e.g. `1-local-names.md`; the slug's category digit keeps that flat name unique. Measured, `agy` truncates a rule file near 23 to 25 KB, so a rendered Antigravity rule over 24,000 bytes refuses the request before writes. Whether `agy` activates a `glob` rule only on matching files is unmeasured.

## Operations

Writer: absolute `scripts/write-rule.cjs` beside the loaded installed `SKILL.md`. Assume no checkout, project-relative or CLI path.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const fs = require("node:fs");
const path = require("node:path");
const { createHash, randomUUID } = require("node:crypto");

const TOOLS = ["claude", "cursor", "copilot", "codex", "opencode"];
const TOOLS = ["claude", "cursor", "copilot", "codex", "opencode", "antigravity"];
const CATEGORIES = ["architecture", "standards", "programming-languages", "frameworks-and-libraries", "tooling", "testing", "design-patterns", "quality", "domain", "other"];
const SHARED = ["codex", "opencode"];
const SOURCE = "aidd_docs/rules";
Expand Down Expand Up @@ -37,7 +37,7 @@ function options(args) {

function tools(value) {
const selected = typeof value === "string" ? value.split(",") : value;
if (!Array.isArray(selected) || !selected.length || new Set(selected).size !== selected.length || selected.some((tool) => !TOOLS.includes(tool))) fail("Targets must be distinct confirmed tools: claude,cursor,copilot,codex,opencode.");
if (!Array.isArray(selected) || !selected.length || new Set(selected).size !== selected.length || selected.some((tool) => !TOOLS.includes(tool))) fail("Targets must be distinct confirmed tools: claude,cursor,copilot,codex,opencode,antigravity.");
return [...selected].sort();
}

Expand Down Expand Up @@ -108,6 +108,8 @@ function nativePath(rule, tool) {
const id = identity(rule.category, rule.slug);
if (tool === "claude") return `.claude/rules/${id}.md`;
if (tool === "cursor") return `.cursor/rules/${id}.mdc`;
// Antigravity loads only direct children; the slug's category digit keeps the flat name unique.
if (tool === "antigravity") return `.agents/rules/${rule.slug}.md`;
return `.github/instructions/${rule.category.slice(0, 2)}-${rule.slug.slice(2)}.instructions.md`;
}

Expand All @@ -117,9 +119,12 @@ function renderNative(rule, tool, signed = true) {
if (tool === "claude") fields = rule.paths.length ? `paths:\n${rule.paths.map((glob) => ` - ${quote(glob)}\n`).join("")}` : "";
if (tool === "cursor") fields = `description: ${quote(rule.description)}\n${rule.paths.length ? `globs: ${quote(rule.paths.join(","))}\n` : ""}alwaysApply: ${!rule.paths.length}\n`;
if (tool === "copilot") fields = `applyTo: ${quote(rule.paths.length ? rule.paths.join(",") : "**")}\n`;
if (tool === "antigravity") fields = `trigger: ${rule.paths.length ? "glob" : "always_on"}\ndescription: ${quote(rule.description)}\n${rule.paths.length ? `globs: ${quote(rule.paths.join(","))}\n` : ""}`;
const text = (fields ? `---\n${fields}---\n` : "") + rule.body;
safeText(text);
return signed ? `${text}\n<!-- aidd_rule_output:path=${nativePath(rule, tool)} sha256=${hash(text)} -->\n` : text;
const output = signed ? `${text}\n<!-- aidd_rule_output:path=${nativePath(rule, tool)} sha256=${hash(text)} -->\n` : text;
if (tool === "antigravity" && Buffer.byteLength(output) > 24000) fail("Antigravity rule exceeds its 24,000-byte per-file limit.");
return output;
}

function ownedNative(existing, prior, tool, relative) {
Expand Down
Loading
Loading