From ce206e4f2974d159fb6691b956c82d0fe92fe02f Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Tue, 17 Mar 2026 03:17:07 -0700 Subject: [PATCH 1/3] agents|feat: Rename `get-branch-context` to `get-session-context` Rename `get-branch-context` to `get-session-context` and extend the manifest schema with `artifact_base_dir` (resolved absolute path) and `artifact_paths` (category suffix paths). Remove `get-project-slug` and `get-default-branch` as standalone skills -- `get-session-context` is now the sole canonical method for resolving all session metadata. Update 18 consuming skills and 4 subagents to use the manifest instead of inlining their own artifact resolution cascades. Replace all inline 3-step preference cascades (`artifacts.base_dir` from `.agents/preferences.yaml` -> `~/.agents/preferences.yaml` -> default) with a single `get-session-context` call. Update `artifact-conventions.md` to reference `get-session-context` as the primary resolution method and replace hardcoded `.ai/` paths with `{base_dir}/` placeholders. Update `branch-format.md` to reference the renamed skill. The `project_slug` fallback changes from git-remote-parsing to bare directory name of the working directory. The `default_branch` derivation reads from preferences only, defaulting to `origin/main`. The immutability contract is extended to cover stale-schema manifests (valid JSON missing required fields added in later schema versions), which trigger re-derivation alongside corrupt manifests. --- .../skills/_data/artifact-conventions.md | 24 +-- .../content/skills/_data/branch-format.md | 2 +- .../content/skills/condense-branch/SKILL.md | 2 +- .../content/skills/create-devlog/SKILL.md | 11 +- .../agents/content/skills/create-pr/SKILL.md | 12 +- .../content/skills/create-ticket/SKILL.md | 7 +- .../content/skills/design-and-plan/SKILL.md | 9 +- .../content/skills/ex-post-facto/SKILL.md | 8 +- .../find-orchestration-savings/SKILL.md | 2 +- .../skills/get-default-branch/SKILL.md | 49 ------ .../content/skills/get-project-slug/SKILL.md | 64 ------- .../SKILL.md | 157 +++++++++++++----- .../content/skills/orchestrate/SKILL.md | 6 +- .../skills/plan-orchestrable-steps/SKILL.md | 19 +-- packages/agents/content/skills/plan/SKILL.md | 8 +- .../agents/content/skills/prepare-pr/SKILL.md | 10 +- .../content/skills/refine-plan/SKILL.md | 11 +- .../content/skills/respond-to-review/SKILL.md | 8 +- .../content/skills/review-change/SKILL.md | 10 +- .../content/skills/save-artifact/SKILL.md | 14 +- .../agents/content/skills/save-plan/SKILL.md | 6 +- .../content/skills/summarize-change/SKILL.md | 8 +- .../content/skills/summarize-chat/SKILL.md | 11 +- .../agents/content/skills/wrap-up/SKILL.md | 16 +- .../content/subagents/aspect-code-reviewer.md | 2 +- .../aspect-silent-failure-reviewer.md | 2 +- .../content/subagents/aspect-test-reviewer.md | 2 +- .../subagents/orchestrated-reviewer.md | 4 +- 28 files changed, 205 insertions(+), 279 deletions(-) delete mode 100644 packages/agents/content/skills/get-default-branch/SKILL.md delete mode 100644 packages/agents/content/skills/get-project-slug/SKILL.md rename packages/agents/content/skills/{get-branch-context => get-session-context}/SKILL.md (57%) diff --git a/packages/agents/content/skills/_data/artifact-conventions.md b/packages/agents/content/skills/_data/artifact-conventions.md index 532852d4..a581a6b4 100644 --- a/packages/agents/content/skills/_data/artifact-conventions.md +++ b/packages/agents/content/skills/_data/artifact-conventions.md @@ -4,7 +4,7 @@ Standards for AI-generated artifact storage, naming, and lifecycle. ## Directory structure -All artifacts live under a configurable base directory (`base_dir`, default `~/.ai`): +All artifacts live under a configurable base directory (`base_dir`, default `~/.ai`). Use `get-session-context` to resolve `artifact_base_dir`: ``` {base_dir}/ @@ -26,7 +26,7 @@ All artifacts live under a configurable base directory (`base_dir`, default `~/. ### Project slug -Always present under `projects/`, even when `.ai/` is inside the project. Constant structure enables simple directory sync for export (`.ai/projects/` ↔ `~/.ai/projects/`). Use `get-project-slug` to obtain. +Always present under `projects/`, even when `{base_dir}/` is inside the project. Constant structure enables simple directory sync for export. Use `get-session-context` to obtain `project_slug`. ### Ticket ID @@ -57,13 +57,15 @@ Artifacts under `{base_dir}/` are ephemeral when `base_dir` is a git-ignored pat ## Path resolution -Skills resolve artifact directories using this algorithm: +Skills resolve artifact directories by invoking `get-session-context` and reading `artifact_base_dir` and `project_slug` from the manifest. This is the canonical method for all artifact path resolution. + +For contexts where the manifest is unavailable (e.g., standalone scripts without skill access), the manual fallback is: 1. Read `artifacts.base_dir` from `.agents/preferences.yaml` 2. If not found there, read from `~/.agents/preferences.yaml` -3. If still not found, use default: `base_dir` = `~/.ai` -4. If `base_dir` is relative, resolve from project root (`git rev-parse --show-toplevel`). If absolute, use as-is. -5. Use `get-project-slug` for the project slug. +3. If still not found, use the default base directory +4. If `base_dir` is relative, resolve from project root. If absolute, use as-is. +5. Read `project.slug` from `.agents/preferences.yaml`, falling back to `~/.agents/preferences.yaml`, then the bare directory name of the working directory. ### Ticket-scoped paths @@ -120,7 +122,7 @@ Artifact ordering is explicit via the sequence number. Timing is captured in the Example run directory (full orchestrated run with iterative review): ``` -.ai/projects/williamthorsen-configs-macos/tickets/MAC-68/20260221-034100Z-orchestrated/ +{base_dir}/projects/williamthorsen-configs-macos/tickets/MAC-68/20260221-034100Z-orchestrated/ run-index.json 01_orchestrator_run-manifest.md # initialization 02_orchestrator_ticket-requirements.md # initialization (optional) @@ -676,10 +678,10 @@ Used by review-producing skills and agents for structured code review findings. Every level degrades gracefully: -- Missing project `.agents/preferences.yaml` → fall back to global `~/.agents/preferences.yaml` -- Missing global preferences → fall back to hardcoded default (`~/.ai`) -- Missing `~/.ai/` directory → created automatically on first artifact save -- Missing `get-project-slug` result → derive from git remote or directory name +- Missing project `.agents/preferences.yaml` -> fall back to global `~/.agents/preferences.yaml` +- Missing global preferences -> fall back to the default base directory +- Missing `{base_dir}/` directory -> created automatically on first artifact save +- Missing `project.slug` in preferences -> use the bare directory name of the working directory ## Migration from status.json (v1) to run-index.json (v2) diff --git a/packages/agents/content/skills/_data/branch-format.md b/packages/agents/content/skills/_data/branch-format.md index 0d9ca034..7af72d1f 100644 --- a/packages/agents/content/skills/_data/branch-format.md +++ b/packages/agents/content/skills/_data/branch-format.md @@ -30,5 +30,5 @@ Old-format branch names like `MAC-123/agents/feat/add-orchestrator` still parse ## Related skills -- `get-branch-context` — Parses the branch name and caches all derived metadata (ticket ID, branch name, and other metadata) in a manifest file for single-lookup access. Preferred over individual skills when multiple metadata fields are needed. +- `get-session-context` — Parses the branch name and caches all derived metadata (ticket ID, branch name, artifact paths, and other metadata) in a manifest file for single-lookup access. Preferred over individual skills when multiple metadata fields are needed. - `get-ticket-id` — Extracts the ticket ID segment. diff --git a/packages/agents/content/skills/condense-branch/SKILL.md b/packages/agents/content/skills/condense-branch/SKILL.md index 694c0533..77b1dc8b 100644 --- a/packages/agents/content/skills/condense-branch/SKILL.md +++ b/packages/agents/content/skills/condense-branch/SKILL.md @@ -24,7 +24,7 @@ If branch exists, increment version number. 4. **Condense commits**: - Use `get-default-branch` to resolve `{default-branch}`. + Use `get-session-context` to obtain `default_branch`. ```bash git reset --soft $(git merge-base {default-branch} HEAD) diff --git a/packages/agents/content/skills/create-devlog/SKILL.md b/packages/agents/content/skills/create-devlog/SKILL.md index 2b4b3d6e..4fa1ec3b 100644 --- a/packages/agents/content/skills/create-devlog/SKILL.md +++ b/packages/agents/content/skills/create-devlog/SKILL.md @@ -48,14 +48,9 @@ Summarize changes made in recent commits or the working tree. ## Saving -Resolve artifact directory: - -1. Read `artifacts.base_dir` and `artifacts.paths.devlogs` from `.agents/preferences.yaml` -2. If not found there, read from `~/.agents/preferences.yaml` -3. If still not found, use defaults: base_dir=`.ai`, path=`devlogs` -4. If base_dir is relative, resolve from project root (`git rev-parse --show-toplevel`). If absolute, use as-is. -5. Use `get-project-slug` for the project slug. -6. Full path: `{base_dir}/projects/{project-slug}/{path}/` +Resolve artifact directory using `get-session-context` to obtain `artifact_base_dir`, `project_slug`, and `artifact_paths`. The devlogs path is `artifact_paths.devlogs` (default: `devlogs`). + +Full path: `{artifact_base_dir}/projects/{project_slug}/{devlogs_path}/` Follow [artifact conventions](_data/artifact-conventions.md). diff --git a/packages/agents/content/skills/create-pr/SKILL.md b/packages/agents/content/skills/create-pr/SKILL.md index 44249e2a..06277e89 100644 --- a/packages/agents/content/skills/create-pr/SKILL.md +++ b/packages/agents/content/skills/create-pr/SKILL.md @@ -11,19 +11,19 @@ Create a pull request using the GitHub CLI and existing PR description. ## Process 1. **Find PR description file**: - - Use `get-ticket-id` and `get-project-slug` - - Look for most recently modified `*_change-summary.md` in `.ai/projects/{project-slug}/tickets/{ticket-id}/` + - Use `get-session-context` to obtain `ticket_id`, `project_slug`, `artifact_base_dir`, and `default_branch` + - Look for most recently modified `*_change-summary.md` in `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` 2. **Extract title**: - Use the first `#` heading from the file as PR title 3. **Check branch sync**: - - ⚠️ If current branch is not up to date with remote, **STOP THIS TASK** 🚨 + - If current branch is not up to date with remote, **STOP THIS TASK** - Run `git status` to verify 4. **Create PR**: - Use `get-default-branch name-only` to resolve `{default-branch}`. + Derive the bare branch name from `default_branch` by stripping the remote prefix (e.g., `origin/main` -> `main`). Extract body from `## What` onward: @@ -32,13 +32,13 @@ Create a pull request using the GitHub CLI and existing PR description. gh pr create \ --title "{extracted title from file}" \ --body "$BODY" \ - --base "{default-branch}" \ + --base "{bare branch name}" \ --draft ``` ## Important -- Use `get-default-branch name-only` for `--base` +- Strip the remote prefix from `default_branch` (e.g., `origin/main` -> `main`) for `--base` - Do NOT use full reference (`origin/main`) - GitHub CLI expects branch name only - Creates PR as draft by default - If instructions are unclear, ask for confirmation before creating diff --git a/packages/agents/content/skills/create-ticket/SKILL.md b/packages/agents/content/skills/create-ticket/SKILL.md index b9bb1105..950afc31 100644 --- a/packages/agents/content/skills/create-ticket/SKILL.md +++ b/packages/agents/content/skills/create-ticket/SKILL.md @@ -12,11 +12,10 @@ Create a ticket on the appropriate platform. The remote platform (e.g., GitHub) ### 1. Resolve project metadata -Get `project_slug` and `base_dir` — but NOT `ticket_id` (that comes from the platform in step 5). +Get `project_slug` and `artifact_base_dir` -- but NOT `ticket_id` (that comes from the platform in step 5). -- Read `project.slug` from `.agents/preferences.yaml`; if absent, use `get-project-slug` +- Use `get-session-context` to obtain `project_slug` and `artifact_base_dir` - Read `project.ticket_prefix` from `.agents/preferences.yaml` (e.g., `CODY-`); if absent, default to empty string -- Read `artifacts.base_dir` from `.agents/preferences.yaml`, falling back to `~/.agents/preferences.yaml`, then default `~/.ai`. If relative, resolve from project root. If absolute, use as-is. ### 2. Write ticket content @@ -87,7 +86,7 @@ If `integrations.jira.enabled: true`, note that Jira creation needs additional c ### 5. Save local artifacts -Ticket directory: `{base_dir}/projects/{project_slug}/tickets/{ticket_id}/` +Ticket directory: `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` `mkdir -p` the target directory before writing. diff --git a/packages/agents/content/skills/design-and-plan/SKILL.md b/packages/agents/content/skills/design-and-plan/SKILL.md index a2ee79bf..2113b76d 100644 --- a/packages/agents/content/skills/design-and-plan/SKILL.md +++ b/packages/agents/content/skills/design-and-plan/SKILL.md @@ -172,13 +172,8 @@ Present the plan to the user. Revise until approved. ### Phase 5: Save artifacts and stop 1. Resolve artifact directory using `save-artifact` conventions: - - Use `get-ticket-id` for ticket ID (auto-generate `{YYYYMMDD}-{4 random hex}` if none found) - - Use `get-project-slug` for project slug - - Resolve `artifacts.base_dir` by checking these sources in order. Use the first value found: - 1. `.agents/preferences.yaml` - 2. `~/.agents/preferences.yaml` - 3. Default: `~/.ai` - - Target: `{base_dir}/projects/{project_slug}/tickets/{ticket_id}/` + - Use `get-session-context` to obtain `ticket_id`, `project_slug`, and `artifact_base_dir` (auto-generate ticket ID as `{YYYYMMDD}-{4 random hex}` if none found) + - Target: `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` - `mkdir -p` the target directory 2. Resolve provenance data: diff --git a/packages/agents/content/skills/ex-post-facto/SKILL.md b/packages/agents/content/skills/ex-post-facto/SKILL.md index de36210c..b21bbdc5 100644 --- a/packages/agents/content/skills/ex-post-facto/SKILL.md +++ b/packages/agents/content/skills/ex-post-facto/SKILL.md @@ -10,7 +10,7 @@ Write an issue ticket (e.g., Jira issue) describing the issues that would have n ## Process -1. **Analyze branch changes** using `get-default-branch`: +1. **Analyze branch changes** using `get-session-context` to obtain `default_branch`: ```bash git diff $DEFAULT_BRANCH...HEAD @@ -63,13 +63,11 @@ git diff $DEFAULT_BRANCH...HEAD ### Path resolution -1. Read `artifacts.base_dir` from `.agents/preferences.yaml`, falling back to `~/.agents/preferences.yaml`, then default `~/.ai` -2. If base_dir is relative, resolve from project root. If absolute, use as-is. -3. Use `get-project-slug` for the project slug. +Use `get-session-context` to obtain `artifact_base_dir`, `project_slug`, and `ticket_id`. Follow [artifact conventions](_data/artifact-conventions.md). -Ticket directory: `{base_dir}/projects/{project-slug}/tickets/{ticket-id}/` +Ticket directory: `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` Artifact type: `ticket`. Filename format: diff --git a/packages/agents/content/skills/find-orchestration-savings/SKILL.md b/packages/agents/content/skills/find-orchestration-savings/SKILL.md index c89069d3..95d99759 100644 --- a/packages/agents/content/skills/find-orchestration-savings/SKILL.md +++ b/packages/agents/content/skills/find-orchestration-savings/SKILL.md @@ -17,7 +17,7 @@ Analyze a completed orchestrated run to identify cost-saving opportunities while 1. **Resolve run directory:** - If a run directory path is provided, use it. - If a run is active in the current session (run-index.json path known), use that. - - Otherwise, use `get-branch-context` to get project_slug and ticket_id, then scan `{artifacts.base_dir}/projects/{project_slug}/tickets/{ticket_id}/` for the most recent completed run (directory with latest timestamp). + - Otherwise, use `get-session-context` to get `project_slug`, `ticket_id`, and `artifact_base_dir`, then scan `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` for the most recent completed run (directory with latest timestamp). - If no run found, report "No completed run found for this context" and exit. 2. **Verify the run directory** contains `run-log.jsonl` and `run-index.json`. If either is missing, report and exit. diff --git a/packages/agents/content/skills/get-default-branch/SKILL.md b/packages/agents/content/skills/get-default-branch/SKILL.md deleted file mode 100644 index 9d0bd182..00000000 --- a/packages/agents/content/skills/get-default-branch/SKILL.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -name: get-default-branch -description: Get the project's default branch reference using config-first optimization -user-invocable: false ---- - -# Get default branch - -Return the project's default branch as a full remote reference (e.g., `origin/main`). - -## Arguments - -- `name-only` — Return branch name only (e.g., `main` instead of `origin/main`) - -## Resolution order - -### 1. Config lookup (preferred) - -Check project-local then global preferences: - -- **Project:** `.agents/preferences.yaml` -- **Global:** `~/.agents/preferences.yaml` - -Look for `repository.default_remote[0]` to get `name` and `default_branch`. - -```yaml -repository: - default_remote: - - name: origin - default_branch: next -``` - -Returns `origin/main` (or `next` if `name-only`). - -### 2. Git fallback - -Only used when config is missing: - -```bash -remote=${1:-origin} -branch=$(git remote show ${remote} 2>/dev/null | grep 'HEAD branch' | cut -d' ' -f5) -echo "${remote}/${branch}" -``` - -## Constraints - -- Always return the **full remote reference** by default — some git commands produce wrong output with bare branch names -- Use `name-only` when the caller needs just the branch name (e.g., `gh pr create --base`) -- Config takes priority over git command; config lookup is instant vs ~500ms for the git command diff --git a/packages/agents/content/skills/get-project-slug/SKILL.md b/packages/agents/content/skills/get-project-slug/SKILL.md deleted file mode 100644 index 2b8d428f..00000000 --- a/packages/agents/content/skills/get-project-slug/SKILL.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -name: get-project-slug -description: Derive project slug for artifact path namespacing -user-invocable: false ---- - -# Get project slug - -Return the project slug used to namespace orchestration artifacts. The slug is derived once and persisted to `.agents/preferences.yaml` for subsequent invocations. - -## Resolution order - -### 1. Config lookup (preferred) - -Check `.agents/preferences.yaml` for `project.slug`: - -```yaml -project: - slug: configs-macos -``` - -If found, return immediately. - -If `project.slug` is not found, fall back to `repository.slug` (deprecated). If the fallback is used, emit a brief note: "Reading slug from deprecated repository.slug — update preferences.yaml to use project.slug." - -### 2. Git remote derivation - -Extract from the `origin` remote URL: - -```bash -git remote get-url origin 2>/dev/null -``` - -Parse the URL (HTTPS or SSH) to extract `{owner}` and `{repo}`: - -- `https://github.com/williamthorsen/configs.macos.git` → `williamthorsen-configs-macos` -- `git@github.com:williamthorsen/configs.macos.git` → `williamthorsen-configs-macos` - -Format: `{owner}-{repo}` in kebab-case (replace dots and underscores with hyphens, lowercase). - -### 3. Directory basename (last resort) - -If no git remote exists: - -```bash -basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)" -``` - -Convert to kebab-case. - -## Persist result - -After deriving from step 2 or 3, **immediately write** the result to `.agents/preferences.yaml` under `project.slug`. - -- If `.agents/preferences.yaml` exists, add `project.slug` to it (preserving existing content) -- If `.agents/preferences.yaml` doesn't exist, create it with just `project.slug` - -This ensures subsequent invocations hit step 1 (instant config lookup). - -## Constraints - -- Always return a single string (the slug) -- Slug must be kebab-case, lowercase, filesystem-safe -- Do not prompt the user — this is a non-interactive utility skill diff --git a/packages/agents/content/skills/get-branch-context/SKILL.md b/packages/agents/content/skills/get-session-context/SKILL.md similarity index 57% rename from packages/agents/content/skills/get-branch-context/SKILL.md rename to packages/agents/content/skills/get-session-context/SKILL.md index 831fc9aa..3e8dbb73 100644 --- a/packages/agents/content/skills/get-branch-context/SKILL.md +++ b/packages/agents/content/skills/get-session-context/SKILL.md @@ -1,14 +1,14 @@ --- -name: get-branch-context -description: Retrieve all branch metadata from a single cached manifest, reducing repeated metadata lookups from ~3 to 1 +name: get-session-context +description: Retrieve all session metadata and artifact paths from a single cached manifest user-invocable: false --- -# Get branch context +# Get session context -Return all branch metadata as a single object by reading a cached manifest file. If the manifest does not exist, derive all metadata and persist it for future invocations. +Return all session metadata as a single object by reading a cached manifest file. If the manifest does not exist, derive all metadata and persist it for future invocations. This is the sole canonical method for resolving `project_slug`, `default_branch`, and `artifact_base_dir`. -**Zero-Bash contract:** This skill uses only Read and Write tools. It never invokes Bash commands or shell scripts. +**Zero-Bash contract:** This skill uses only Read and Write tools. It never invokes Bash commands or shell scripts. Relative paths are resolved using the `Working directory:` field from the system prompt environment block. ## Manifest location @@ -30,17 +30,21 @@ Example: branch `MAC-130/agents/feat/branch-manifest` produces `.agents/MAC-130- "project_slug": "configs-macos", "default_branch": "origin/main", "branch_name": "MAC-130/agents/feat/branch-manifest", + "artifact_base_dir": "/Users/william/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, "created_at": "2026-02-25T19:43:00Z" } ``` -| Field | Type | Description | -| ---------------- | ------------------ | ----------------------------------------------------------------------- | -| `ticket_id` | `string` or `null` | Ticket ID extracted from branch name, or `null` if not derivable | -| `project_slug` | `string` | Project slug for artifact namespacing (from `.agents/preferences.yaml`) | -| `default_branch` | `string` | Full remote reference for the default branch (from preferences) | -| `branch_name` | `string` | Raw branch name as it appears in `gitStatus` | -| `created_at` | `string` | ISO 8601 UTC timestamp of when the manifest was created | +| Field | Type | Description | +| ------------------- | ------------------ | ----------------------------------------------------------------------------------------- | +| `ticket_id` | `string` or `null` | Ticket ID extracted from branch name, or `null` if not derivable | +| `project_slug` | `string` | Project slug for artifact namespacing | +| `default_branch` | `string` | Full remote reference for the default branch (e.g., `origin/main`) | +| `branch_name` | `string` | Raw branch name as it appears in `gitStatus` | +| `artifact_base_dir` | `string` | Resolved absolute path for artifact storage (from preferences or default `~/.ai`) | +| `artifact_paths` | `object` | Category suffix paths relative to the project directory (defaults: chats, devlogs, plans) | +| `created_at` | `string` | ISO 8601 UTC timestamp of when the manifest was created | ## Resolution order @@ -53,15 +57,15 @@ Check for an existing manifest file before performing any derivation. 3. If the branch name is empty or the gitStatus indicates a detached HEAD state, return an error: "Detached HEAD: this skill requires an active branch. Create or check out a branch before invoking this skill." 4. Sanitize the branch name: trim any leading or trailing whitespace, then replace every `/` with `-`. After replacing, remove any trailing `-` characters. 5. Use the Read tool to attempt reading `.agents/{sanitized-branch}.branch-manifest.json`. -6. If the file exists and contains valid JSON, return the manifest object. Done. +6. If the file exists and contains valid JSON, check for stale schema (see [Immutability contract](#immutability-contract)). If the schema is current (all required fields present, including `artifact_base_dir` and `artifact_paths`), return the manifest object. Done. 7. If the file does not exist, use the Read tool to attempt reading `.agents/{sanitized-branch}.manifest.json` (old format). -8. If the old-format file exists and contains valid JSON, return the manifest object. Done. Do not create a new `.branch-manifest.json` file in this case. Old `.manifest.json` files are supported for reading but not automatically migrated. To migrate, delete the old file and re-invoke the skill. -9. If either file exists but contains invalid JSON (corrupt manifest), note the path for overwrite and fall through to step 2 (derivation). A corrupt old-format file is overwritten in place at its old path (`.manifest.json`), not migrated to the new path. +8. If the old-format file exists and contains valid JSON, check for stale schema. If the schema is current, return the manifest object. Done. Do not create a new `.branch-manifest.json` file in this case. Old `.manifest.json` files are supported for reading but not automatically migrated. To migrate, delete the old file and re-invoke the skill. +9. If either file exists but contains invalid JSON (corrupt manifest) or valid JSON with a stale schema (missing required fields), note the path for overwrite and fall through to step 2 (derivation). A corrupt or stale old-format file is overwritten in place at its old path (`.manifest.json`), not migrated to the new path. 10. If neither file exists, fall through to step 2 (derivation). ### 2. Derivation -If no valid manifest exists, derive each field from the branch name and preferences. +If no valid manifest exists (or the existing manifest has a stale schema), derive each field from the branch name, preferences, and the system prompt. #### Branch name @@ -76,10 +80,10 @@ Extract the ticket ID from the **start** of the branch name using these rules: 3. The match is **case-insensitive**: both `MAC-147` and `mac-147` are valid matches. 4. **Normalize to uppercase**: the ticket ID is always stored in uppercase (e.g., `mac-147` becomes `MAC-147`). 5. The ticket ID pattern ends at the first `/` or `_` separator, or at the end of the branch name if it is the entire branch name. Do not continue matching past the first separator or into hyphenated descriptions. For example, `MAC-147-some-description` extracts `MAC-147` (the match stops before the second hyphen because `-s` does not continue the digits pattern), and `MAC-147/feat/foo` extracts `MAC-147`. -6. If no prefixed ticket ID is found, check for a **bare issue number**: one or more digits anchored to the start of the branch name, terminated by `/`, `_`, or `-`, or at the end of the branch name if it is the entire name. Examples: `147/feat/something` → `147`, `42_fix_login` → `42`, `99` → `99`. Do not match digits that appear after non-digit characters. +6. If no prefixed ticket ID is found, check for a **bare issue number**: one or more digits anchored to the start of the branch name, terminated by `/`, `_`, or `-`, or at the end of the branch name if it is the entire name. Examples: `147/feat/something` -> `147`, `42_fix_login` -> `42`, `99` -> `99`. Do not match digits that appear after non-digit characters. 7. If a bare issue number is found, read `project.ticket_prefix` from `.agents/preferences.yaml`: - If `ticket_prefix` is `#` (a GitHub display prefix): return the **bare number only**. The `#` character is a display convention, not an identifier component, and must not appear in file paths. - - If a Jira-style prefix is configured (e.g., `MAC-`): ticket ID = `{ticket_prefix}{number}` (e.g., prefix `MAC-` + number `147` → `MAC-147`). + - If a Jira-style prefix is configured (e.g., `MAC-`): ticket ID = `{ticket_prefix}{number}` (e.g., prefix `MAC-` + number `147` -> `MAC-147`). - If no prefix is configured: ticket ID = the number alone (e.g., `147`). 8. If neither a prefixed ticket ID nor a bare issue number is found, the ticket ID is `null`. @@ -91,47 +95,76 @@ Everything after the ticket ID and its trailing separator is the description. If Read `.agents/preferences.yaml` using the Read tool. Extract the `project.slug` value from the YAML content. Read the file line by line: when you encounter a line `project:` (at the top level, with no leading whitespace), subsequent indented lines belong to that section until the next non-indented line. Match `slug:` within that section and take the value after the colon, trimmed of leading/trailing whitespace and quotes. -If `project.slug` is not found, fall back to `repository.slug` (deprecated field, found under the `repository:` section). If neither is found, default to `unknown`. +If `project.slug` is not found in `.agents/preferences.yaml`, read `~/.agents/preferences.yaml` and check for `project.slug` there. + +If `project.slug` is not found in either file, fall back to `repository.slug` (deprecated field, found under the `repository:` section in `.agents/preferences.yaml`). If none of these are found, use the bare directory name of the working directory (from the `Working directory:` field in the system prompt environment block). For example, if the working directory is `/Users/william/repos/projects/codeassembly`, the default slug is `codeassembly`. Also extract `project.ticket_prefix` from the same `project:` section (e.g., `MAC-`). This value is used by the ticket ID extraction logic (step 7) when a bare issue number is found. If not present, no prefix is applied. #### Default branch -From the same `.agents/preferences.yaml` content, extract `repository.default_remote[0].name` and `repository.default_remote[0].default_branch`. Find the `repository:` section (top-level, no leading whitespace), then `default_remote:` (indented under it). The first list item starts with `- name:` (YAML list items begin with `-`). Extract the `name` value and the `default_branch` value from that list item, trimming whitespace and quotes. +From `.agents/preferences.yaml`, extract `repository.default_remote[0].name` and `repository.default_remote[0].default_branch`. Find the `repository:` section (top-level, no leading whitespace), then `default_remote:` (indented under it). The first list item starts with `- name:` (YAML list items begin with `-`). Extract the `name` value and the `default_branch` value from that list item, trimming whitespace and quotes. Construct the full remote reference as `{name}/{default_branch}` (e.g., `origin/main`). -If preferences are missing or the fields are absent, default to `origin/main`. +If not found in `.agents/preferences.yaml`, check `~/.agents/preferences.yaml` for the same fields. + +If preferences are missing or the fields are absent in both files, default to `origin/main`. + +#### Artifact base directory + +Resolve the artifact base directory by reading preferences in this order: + +1. Read `artifacts.base_dir` from `.agents/preferences.yaml` +2. If not found, read `artifacts.base_dir` from `~/.agents/preferences.yaml` +3. If still not found, use default: `~/.ai` + +Expand `~` using the home directory inferred from the `Working directory:` field in the system prompt environment block (e.g., if the working directory is `/Users/william/repos/myproject`, the home directory is `/Users/william`). + +If the resolved `base_dir` is a relative path, resolve it against the working directory from the `Working directory:` field. If absolute, use as-is. + +Store the fully resolved absolute path in the manifest. + +#### Artifact paths + +From whichever preferences file yielded `base_dir` (or from `.agents/preferences.yaml` if the default was used), read `artifacts.paths.*`: + +- `artifacts.paths.chats` (default: `chats`) +- `artifacts.paths.devlogs` (default: `devlogs`) +- `artifacts.paths.plans` (default: `plans`) + +These are category suffixes relative to the project directory. Apply defaults for any missing category. ### 3. Persist manifest After deriving all fields, persist the manifest using the Write tool. -1. If a corrupt manifest was detected in step 1.9, the Write tool will overwrite it at the path noted in that step (old-format corrupt files are overwritten in place, new-format corrupt files are overwritten at the new path). -2. Construct the JSON object with all 5 fields. +1. If a corrupt or stale-schema manifest was detected in step 1.9, the Write tool will overwrite it at the path noted in that step (old-format files are overwritten in place, new-format files are overwritten at the new path). +2. Construct the JSON object with all 7 fields. 3. For the `created_at` field, take the `currentDate` value (format `YYYY-MM-DD`) from the system prompt and append `T00:00:00Z` to create the timestamp. Since exact time is not available without Bash, use midnight UTC as the time component. Same-day manifests will have identical `created_at` values. -4. If no corrupt file is being overwritten, use the Write tool to save to `.agents/{sanitized-branch}.branch-manifest.json`. +4. If no corrupt or stale file is being overwritten, use the Write tool to save to `.agents/{sanitized-branch}.branch-manifest.json`. **Null serialization:** The `ticket_id` field may be JSON `null`. Serialize as the unquoted keyword `null`, not the string `"null"` or an empty string `""`. -**Immutability contract:** Once created, a manifest file is never overwritten. It captures the branch context at creation time. The one exception is a **corrupt manifest** (invalid JSON): a corrupt file is deleted and the derivation process runs again to produce a new file. +### Immutability contract + +Once created, a manifest file is never overwritten under normal circumstances. It captures the session context at creation time. There are two exception categories that trigger re-derivation: + +1. **Corrupt manifest** (invalid JSON): the file cannot be parsed. Delete it and re-derive all fields. +2. **Stale-schema manifest** (valid JSON but missing required fields): the manifest was created under an older schema version that did not include fields now required (e.g., `artifact_base_dir`, `artifact_paths`). Delete it and re-derive all fields. + +In both cases, the existing file is deleted and the full derivation process runs to produce a new manifest with the current schema. This is a one-time cost per branch when the schema evolves. ## Usage ### Typical agent workflow -Before this skill, agents made up to 3 separate metadata calls: - -``` -get-ticket-id -> MAC-130 -get-project-slug -> configs-macos -get-default-branch -> origin/main -``` +Before this skill, agents made multiple separate metadata calls and inline resolution steps to obtain ticket ID, project slug, default branch, and artifact base directory. With this skill, a single call returns all metadata: ``` -get-branch-context -> { ticket_id, project_slug, default_branch, branch_name, created_at } +get-session-context -> { ticket_id, project_slug, default_branch, branch_name, artifact_base_dir, artifact_paths, created_at } ``` On the first invocation, the skill derives and caches. On subsequent invocations, it reads from the manifest file (single file read, zero permission prompts). @@ -153,6 +186,8 @@ Branch: `MAC-130/agents/feat/branch-manifest` "project_slug": "configs-macos", "default_branch": "origin/main", "branch_name": "MAC-130/agents/feat/branch-manifest", + "artifact_base_dir": "/Users/william/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, "created_at": "2026-02-25T00:00:00Z" } ``` @@ -172,6 +207,8 @@ Branch: `PT-456/fix/login-redirect` "project_slug": "example-project", "default_branch": "origin/main", "branch_name": "PT-456/fix/login-redirect", + "artifact_base_dir": "/Users/william/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, "created_at": "2026-02-25T00:00:00Z" } ``` @@ -191,6 +228,8 @@ Branch: `mac-147` "project_slug": "configs-macos", "default_branch": "origin/main", "branch_name": "mac-147", + "artifact_base_dir": "/Users/william/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, "created_at": "2026-02-27T00:00:00Z" } ``` @@ -211,6 +250,8 @@ Branch: `experiment/try-new-parser` "project_slug": "example-project", "default_branch": "origin/main", "branch_name": "experiment/try-new-parser", + "artifact_base_dir": "/Users/william/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, "created_at": "2026-02-25T00:00:00Z" } ``` @@ -232,6 +273,8 @@ Produces the same ticket ID as the slash-separated variant. Separators `_` and ` "project_slug": "configs-macos", "default_branch": "origin/main", "branch_name": "MAC-130_agents_feat_branch-manifest", + "artifact_base_dir": "/Users/william/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, "created_at": "2026-02-25T00:00:00Z" } ``` @@ -251,6 +294,8 @@ Branch: `NMR-567.2/fix/regression` "project_slug": "example-project", "default_branch": "origin/main", "branch_name": "NMR-567.2/fix/regression", + "artifact_base_dir": "/Users/william/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, "created_at": "2026-02-25T00:00:00Z" } ``` @@ -270,6 +315,8 @@ Branch: `MAC-200` "project_slug": "example-project", "default_branch": "origin/main", "branch_name": "MAC-200", + "artifact_base_dir": "/Users/william/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, "created_at": "2026-02-25T00:00:00Z" } ``` @@ -290,6 +337,8 @@ Branch: `a-1-test` "project_slug": "example-project", "default_branch": "origin/main", "branch_name": "a-1-test", + "artifact_base_dir": "/Users/william/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, "created_at": "2026-02-25T00:00:00Z" } ``` @@ -313,6 +362,8 @@ Preferences: `project.ticket_prefix: MAC-` "project_slug": "configs-macos", "default_branch": "origin/main", "branch_name": "147/feat/improve-parser", + "artifact_base_dir": "/Users/william/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, "created_at": "2026-02-28T00:00:00Z" } ``` @@ -336,6 +387,8 @@ Preferences: no `project.ticket_prefix` configured "project_slug": "example-project", "default_branch": "origin/main", "branch_name": "42_fix_login-redirect", + "artifact_base_dir": "/Users/william/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, "created_at": "2026-02-28T00:00:00Z" } ``` @@ -359,24 +412,50 @@ Preferences: `project.ticket_prefix: '#'` "project_slug": "codeassembly", "default_branch": "origin/main", "branch_name": "152", + "artifact_base_dir": "/Users/william/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, "created_at": "2026-03-03T00:00:00Z" } ``` +#### 12. Custom artifact base directory (relative path) + +Branch: `MAC-200/feat/new-feature` +Preferences: `artifacts.base_dir: .ai` +Working directory: `/Users/william/repos/myproject` + +**Derivation trace:** + +1. Ticket ID: `MAC-200`. +2. `artifacts.base_dir` is `.ai` (relative). Resolve against working directory: `/Users/william/repos/myproject/.ai`. + +```json +{ + "ticket_id": "MAC-200", + "project_slug": "myproject", + "default_branch": "origin/main", + "branch_name": "MAC-200/feat/new-feature", + "artifact_base_dir": "/Users/william/repos/myproject/.ai", + "artifact_paths": { "chats": "chats", "devlogs": "devlogs", "plans": "plans" }, + "created_at": "2026-03-17T00:00:00Z" +} +``` + ## Edge cases - **Corrupt manifest**: If a manifest file exists but contains invalid JSON, delete it and fall through to derivation to produce a new file. +- **Stale-schema manifest**: If a manifest file exists with valid JSON but is missing required fields (e.g., `artifact_base_dir`, `artifact_paths`), delete it and fall through to derivation. This occurs once per branch when the schema evolves. - **Detached HEAD**: If `gitStatus` does not indicate an active branch (no `Current branch:` line or empty value), return an error message. Do not attempt derivation. -- **Missing preferences**: If `.agents/preferences.yaml` cannot be read, default `project_slug` to `unknown` and `default_branch` to `origin/main`. +- **Missing preferences**: If `.agents/preferences.yaml` cannot be read, use `~/.agents/preferences.yaml`. If that is also unavailable, use defaults: `project_slug` from the working directory name, `default_branch` as `origin/main`, `artifact_base_dir` as `~/.ai` (expanded to absolute). - **Author-prefixed branches**: Branch names like `wthorsen/MAC-130` are not matched for ticket ID extraction (the ticket ID must be at the start). The ticket ID will be `null`. (The ticket ID is visually present but not at position zero; extracting it would require a separate enhancement.) ## Constraints -- **Never use Bash** — all operations use Read and Write tools only +- **Never use Bash** -- all operations use Read and Write tools only - Always return the full manifest object, never a subset -- Do not prompt the user — this is a non-interactive utility skill -- Manifest files are immutable once created — never overwrite (the sole exception: corrupt manifests with invalid JSON are deleted and re-derived) +- Do not prompt the user -- this is a non-interactive utility skill +- Manifest files are immutable once created -- exceptions are corrupt manifests (invalid JSON) and stale-schema manifests (missing required fields), both of which are deleted and re-derived - Branch name sanitization only replaces `/` with `-` (do not replace `_`) -- If on a detached HEAD, return an error — do not attempt derivation without a branch name -- Branch names must start with the ticket ID or a bare issue number (per branch naming format). Author-prefixed branches (e.g., `wthorsen/MAC-130`) are not matched — the ticket ID will be `null` (same limitation as before; extraction from non-start positions is not supported). +- If on a detached HEAD, return an error -- do not attempt derivation without a branch name +- Branch names must start with the ticket ID or a bare issue number (per branch naming format). Author-prefixed branches (e.g., `wthorsen/MAC-130`) are not matched -- the ticket ID will be `null` (same limitation as before; extraction from non-start positions is not supported). - Manifest files are local-only (gitignored) and are not committed to the repository. The `.agents/` directory itself is committed for project-scoped config like `preferences.yaml`. diff --git a/packages/agents/content/skills/orchestrate/SKILL.md b/packages/agents/content/skills/orchestrate/SKILL.md index 3e3244c2..bbbe8e4d 100644 --- a/packages/agents/content/skills/orchestrate/SKILL.md +++ b/packages/agents/content/skills/orchestrate/SKILL.md @@ -27,7 +27,7 @@ Wrapper skills (`orchestrate-dev` with optional `--effort=low|medium|high`, `orc 2. **Task description** (required): what to implement 3. `--max-review-rounds=N`: maximum iterative review rounds before marking needs_manual_review (default: 3) -4. `--diff-base=`: reference to diff against for reviews (default: project's default branch via `get-default-branch`) +4. `--diff-base=`: reference to diff against for reviews (default: project's default branch via `get-session-context`) 5. `--approval-threshold=`: findings at this level or above must be fixed for code approval (default: `low`) 6. `--budget-threshold=`: remaining review-round budget is spent only on findings at this level or above (default: `low`) 7. `--models=`: model assignment overrides, comma-separated (e.g., `--models=coder:opus,default:sonnet`) @@ -152,7 +152,7 @@ Prefix the status line with a colored emoji for visual distinction: ## Run initialization -1. **Get context**: Use `get-project-slug` and `get-ticket-id`. Resolve the diff base: use `--diff-base` if provided, otherwise use `get-default-branch`. Then compute the merge-base SHA once: run `git merge-base HEAD {diff-base}` and store the result as `{merge-base-sha}` — this concrete SHA is what you pass to all downstream agents. The ticket ID is optional — if unavailable, `init_run` will auto-generate one. +1. **Get context**: Use `get-session-context` to obtain `project_slug`, `ticket_id`, `default_branch`, and `artifact_base_dir`. Resolve the diff base: use `--diff-base` if provided, otherwise use `default_branch` from the manifest. Then compute the merge-base SHA once: run `git merge-base HEAD {diff-base}` and store the result as `{merge-base-sha}` -- this concrete SHA is what you pass to all downstream agents. The ticket ID is optional -- if unavailable, `init_run` will auto-generate one. 2. **Read ticket** (if available): If the ticket ID resolves to a GitHub issue, read it via `gh issue view {number}` and store the content as `{ticket-content}`. If the read fails (not a GitHub issue, CLI unavailable), continue without ticket content. 3. **Detect external plan and evaluate trust**: Determine whether the task description contains or references an **external plan** — step-by-step implementation instructions with specific file paths or code changes. If it does, set `{externalPlan}` to `true` and extract the plan content. Otherwise, set `{externalPlan}` to `false` and set `{planTrust}` to `null`. @@ -232,7 +232,7 @@ Prefix the status line with a colored emoji for visual distinction: - `optional`: print one-line notice "MCP unavailable — continuing without tracking" and proceed. **Fallback local context generation** (when policy permits continuing without MCP): - - Resolve `{base-dir}` from `artifacts.base_dir` in `.agents/preferences.yaml` then `~/.agents/preferences.yaml`, then default to `~/.ai`. + - Use `artifact_base_dir` from the `get-session-context` manifest as `{base-dir}`. - Generate `{timestamp}` as current UTC time in ISO 8601. - Derive a local timestamp prefix by stripping punctuation from `{timestamp}`: `YYYYMMDD-HHMMSSZ` format. - Use `{ticket-id}` from step 1 if available, otherwise generate as `{YYYYMMDD}-{4 random hex chars}`. diff --git a/packages/agents/content/skills/plan-orchestrable-steps/SKILL.md b/packages/agents/content/skills/plan-orchestrable-steps/SKILL.md index 83490baa..8d01ec9d 100644 --- a/packages/agents/content/skills/plan-orchestrable-steps/SKILL.md +++ b/packages/agents/content/skills/plan-orchestrable-steps/SKILL.md @@ -23,11 +23,10 @@ Before every Task call and after every phase completion, output a status line: ### 1. Resolve context -1. Use `get-project-slug` to obtain the project slug. -2. Use `get-ticket-id` to obtain the ticket ID. If no ticket ID is available, auto-generate one: `{YYYYMMDD}-{4 random hex}`. -3. **Resolve base directory**: Read `artifacts.base_dir` from `.agents/preferences.yaml`, falling back to `~/.agents/preferences.yaml`, then default `~/.ai`. If relative, resolve from project root (`git rev-parse --show-toplevel`). If absolute, use as-is. -4. **Resolve artifact directory**: `{base_dir}/projects/{project-slug}/tickets/{ticket-id}/` — this is the ticket level, NOT inside a run directory. `orchestration-plan.json` is a ticket-level mutable artifact. -5. `mkdir -p {artifact-dir}` +1. Use `get-session-context` to obtain `project_slug`, `ticket_id`, and `artifact_base_dir`. +2. If no ticket ID is available, auto-generate one: `{YYYYMMDD}-{4 random hex}`. +3. **Resolve artifact directory**: `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` -- this is the ticket level, NOT inside a run directory. `orchestration-plan.json` is a ticket-level mutable artifact. +4. `mkdir -p {artifact-dir}` ### 2. Invoke planner agent @@ -111,11 +110,11 @@ When the user approves the plan: ## Artifact layout ``` -{base_dir}/projects/{project-slug}/tickets/{ticket-id}/ -├── orchestration-plan.json ← machine-readable plan (mutable, overwritten each iteration) -├── 20260219-143000Z_planner_orchestration-plan.md ← human-readable plan snapshot (iteration 1) -├── 20260219-144500Z_planner_orchestration-plan.md ← human-readable plan snapshot (iteration 2, after feedback) -└── {run-id}/ ← orchestration run directories (created later by /orchestrate-dev) +{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/ +├── orchestration-plan.json <- machine-readable plan (mutable, overwritten each iteration) +├── 20260219-143000Z_planner_orchestration-plan.md <- human-readable plan snapshot (iteration 1) +├── 20260219-144500Z_planner_orchestration-plan.md <- human-readable plan snapshot (iteration 2, after feedback) +└── {run-id}/ <- orchestration run directories (created later by /orchestrate-dev) ``` - `orchestration-plan.json` is a **ticket-level mutable artifact** — it is overwritten on each planning iteration, not timestamped diff --git a/packages/agents/content/skills/plan/SKILL.md b/packages/agents/content/skills/plan/SKILL.md index 6b386748..ac94a2e8 100644 --- a/packages/agents/content/skills/plan/SKILL.md +++ b/packages/agents/content/skills/plan/SKILL.md @@ -65,11 +65,9 @@ If inside an active run (`run-index.json` exists in a parent directory): ### Ticket context -1. Use `get-ticket-id` for the ticket ID. If none found, auto-generate: `{YYYYMMDD}-{4 random hex}`. -2. Use `get-project-slug` for the project slug. -3. Read `artifacts.base_dir` from `.agents/preferences.yaml`, falling back to `~/.agents/preferences.yaml`, then default `~/.ai`. If relative, resolve from project root (`git rev-parse --show-toplevel`). If absolute, use as-is. -4. Save as ticket-level artifact: `{base_dir}/projects/{project-slug}/tickets/{ticket-id}/{timestamp}_{slug}_plan.md` -5. Slug derived from the plan's descriptive title (kebab-case, max 60 chars). +1. Use `get-session-context` to obtain `ticket_id`, `project_slug`, and `artifact_base_dir`. If no ticket ID is available, auto-generate: `{YYYYMMDD}-{4 random hex}`. +2. Save as ticket-level artifact: `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/{timestamp}_{slug}_plan.md` +3. Slug derived from the plan's descriptive title (kebab-case, max 60 chars). Follow [artifact conventions](_data/artifact-conventions.md). diff --git a/packages/agents/content/skills/prepare-pr/SKILL.md b/packages/agents/content/skills/prepare-pr/SKILL.md index 3a350497..ca020d0d 100644 --- a/packages/agents/content/skills/prepare-pr/SKILL.md +++ b/packages/agents/content/skills/prepare-pr/SKILL.md @@ -10,7 +10,7 @@ Create a pull request description from an existing change summary. ## Process -1. **Get ticket ID** using `get-ticket-id` +1. **Get context** using `get-session-context` to obtain `ticket_id`, `project_slug`, and `artifact_base_dir` 2. **Get current commit hash**: @@ -18,7 +18,7 @@ Create a pull request description from an existing change summary. git rev-parse --short HEAD ``` -3. **Find matching change summary**: Resolve the ticket directory (`{base_dir}/projects/{project-slug}/tickets/{ticket-id}/` — see path resolution in [Saving](#saving)). Look for `*_change-summary.md`. +3. **Find matching change summary**: Resolve the ticket directory (`{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/`). Look for `*_change-summary.md`. - Verify content contains the current commit hash 4. **If no match found**: Use `summarize-change` first, then continue @@ -31,13 +31,11 @@ git rev-parse --short HEAD ### Path resolution -1. Read `artifacts.base_dir` from `.agents/preferences.yaml`, falling back to `~/.agents/preferences.yaml`, then default `~/.ai` -2. If base_dir is relative, resolve from project root. If absolute, use as-is. -3. Use `get-project-slug` for the project slug. +Use `get-session-context` to obtain `artifact_base_dir`, `project_slug`, and `ticket_id`. Follow [artifact conventions](_data/artifact-conventions.md). -Ticket directory: `{base_dir}/projects/{project-slug}/tickets/{ticket-id}/` +Ticket directory: `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` Artifact type: `pull-request`. Filename format: diff --git a/packages/agents/content/skills/refine-plan/SKILL.md b/packages/agents/content/skills/refine-plan/SKILL.md index 8ad3c19f..84ac5839 100644 --- a/packages/agents/content/skills/refine-plan/SKILL.md +++ b/packages/agents/content/skills/refine-plan/SKILL.md @@ -30,14 +30,9 @@ Before every Task call and after every phase completion, output a status line: - GitHub URL (`github.com/.../issues/...`) -> use `gh issue view --json title,body {url}` via Bash to fetch content. - File path -> Read the file. - Other URL -> use WebFetch to retrieve content. -4. Use `get-branch-context` to obtain `ticket_id` and `project_slug`. -5. Resolve `artifacts.base_dir`: - - Read `artifacts.base_dir` from `.agents/preferences.yaml` - - If not found, read from `~/.agents/preferences.yaml` - - If still not found, use default: `~/.ai` - - If relative, resolve from project root (`git rev-parse --show-toplevel`). If absolute, use as-is. -6. Resolve artifact directory: `{base_dir}/projects/{project_slug}/tickets/{ticket_id}/` -7. `mkdir -p {artifact_dir}` +4. Use `get-session-context` to obtain `ticket_id`, `project_slug`, and `artifact_base_dir`. +5. Resolve artifact directory: `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` +6. `mkdir -p {artifact_dir}` ### 2. Detect plan format diff --git a/packages/agents/content/skills/respond-to-review/SKILL.md b/packages/agents/content/skills/respond-to-review/SKILL.md index 5756c311..9afb9fed 100644 --- a/packages/agents/content/skills/respond-to-review/SKILL.md +++ b/packages/agents/content/skills/respond-to-review/SKILL.md @@ -19,7 +19,7 @@ This skill bridges the gap between receiving a code review and implementing fixe ## Process -1. **Get context** using `get-ticket-id` and `get-project-slug` +1. **Get context** using `get-session-context` to obtain `ticket_id`, `project_slug`, and `artifact_base_dir` 2. **Locate the review** per the [Locating the review](#locating-the-review) section 3. **Read prior artifacts** in the run directory chronologically for full context 4. **Parse findings**: extract all numbered findings (F{n}, W{n}, T{n}, R{n}, S{n}, L{n}). See [finding scheme](_data/artifact-conventions.md#finding-scheme-fwtrsl) for category definitions. @@ -37,7 +37,7 @@ If a path argument is provided, read the review directly from that path. If the If no path is provided, find the most recent `reviewer_review` in the active run: -1. Resolve the ticket directory: `{base_dir}/projects/{project-slug}/tickets/{ticket-id}/` +1. Resolve the ticket directory: `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` 2. Find the most recent run directory (highest timestamp in directory name) 3. List files matching `*_reviewer_review.md` or `*_overseer_review.md` in the run directory 4. Select the file with the highest timestamp @@ -161,9 +161,7 @@ Author: {Agent name} (model: {model}) ### Path resolution -1. Read `artifacts.base_dir` from `.agents/preferences.yaml`, falling back to `~/.agents/preferences.yaml`, then default `~/.ai` -2. If base_dir is relative, resolve from project root. If absolute, use as-is. -3. Use `get-project-slug` for the project slug. +Use `get-session-context` to obtain `artifact_base_dir` and `project_slug`. Follow [artifact conventions](_data/artifact-conventions.md). diff --git a/packages/agents/content/skills/review-change/SKILL.md b/packages/agents/content/skills/review-change/SKILL.md index 96d30074..533b3dc1 100644 --- a/packages/agents/content/skills/review-change/SKILL.md +++ b/packages/agents/content/skills/review-change/SKILL.md @@ -15,7 +15,7 @@ Act as a conscientious code reviewer for changes in the current scope. ## Process -1. **Get context** using `get-default-branch`, `get-ticket-id`, and `get-project-slug` +1. **Get context** using `get-session-context` to obtain `default_branch`, `ticket_id`, `project_slug`, and `artifact_base_dir` 2. **Read prior artifacts** — if a run directory exists for this ticket, read all artifacts chronologically for context (including any prior dispositions). _(Branch scope only.)_ 3. **Analyze changes**: - Branch scope: `git diff $DEFAULT_BRANCH...HEAD` @@ -175,9 +175,7 @@ Score: X/10 ### Path resolution -1. Read `artifacts.base_dir` from `.agents/preferences.yaml`, falling back to `~/.agents/preferences.yaml`, then default `~/.ai` -2. If base_dir is relative, resolve from project root. If absolute, use as-is. -3. Use `get-project-slug` for the project slug. +Use `get-session-context` to obtain `artifact_base_dir`, `project_slug`, and `ticket_id`. Follow [artifact conventions](_data/artifact-conventions.md). @@ -185,7 +183,7 @@ Follow [artifact conventions](_data/artifact-conventions.md). The review is saved as a run artifact: `{timestamp}_reviewer_review.md` -1. Resolve ticket directory: `{base_dir}/projects/{project-slug}/tickets/{ticket-id}/` +1. Resolve ticket directory: `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` 2. Find or create a run directory: - **If an active run exists** (the most recent run directory whose `run-index.json` has `context.branch` matching the current branch AND `completedAt` is absent): save into it - **If no active run exists**: create a new run directory named `{timestamp}-interactive` where timestamp matches this review's timestamp @@ -195,7 +193,7 @@ Each review is a separate artifact in the run directory. Do not append to existi ### Commit scope -Ticket directory: `{base_dir}/projects/{project-slug}/tickets/{ticket-id}/` +Ticket directory: `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` Artifact type: `review`. Filename format: diff --git a/packages/agents/content/skills/save-artifact/SKILL.md b/packages/agents/content/skills/save-artifact/SKILL.md index 03d3b549..ce2c7db8 100644 --- a/packages/agents/content/skills/save-artifact/SKILL.md +++ b/packages/agents/content/skills/save-artifact/SKILL.md @@ -77,25 +77,19 @@ Run artifacts are saved by the skills that produce them (`review-change`, `respo ## Path resolution -Resolve the artifact directory before saving: - -1. Read `artifacts.base_dir` from `.agents/preferences.yaml` -2. If not found there, read from `~/.agents/preferences.yaml` -3. If still not found, use default: base_dir=`~/.ai` -4. If base_dir is relative, resolve from project root (`git rev-parse --show-toplevel`). If absolute, use as-is. -5. Use `get-project-slug` for the project slug. +Resolve the artifact directory before saving. Use `get-session-context` to obtain `artifact_base_dir`, `project_slug`, and `ticket_id`. ### Ticket-scoped path ``` -{base_dir}/projects/{project-slug}/tickets/{ticket-id}/ +{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/ ``` -Use `get-ticket-id` for the ticket ID. Create the directory if needed. +Create the directory if needed. ### Non-ticket paths -Read `artifacts.paths.{category}` from preferences.yaml for chats, devlogs, plans. Defaults: `chats`, `devlogs`, `plans`. These are relative to the project directory: `{base_dir}/projects/{project-slug}/{category}/`. +Read `artifact_paths` from the `get-session-context` manifest for category paths (chats, devlogs, plans). These are relative to the project directory: `{artifact_base_dir}/projects/{project_slug}/{category}/`. Follow [artifact conventions](_data/artifact-conventions.md). diff --git a/packages/agents/content/skills/save-plan/SKILL.md b/packages/agents/content/skills/save-plan/SKILL.md index f5b2e505..f90c2858 100644 --- a/packages/agents/content/skills/save-plan/SKILL.md +++ b/packages/agents/content/skills/save-plan/SKILL.md @@ -13,10 +13,8 @@ Save the plan from the current conversation as a ticket-scoped artifact. Useful 1. **Extract plan content** from conversation context (the most recent plan discussed or produced) 2. **Resolve artifact path**: - - Use `get-branch-context` for `ticket_id` and `project_slug` - - Read `artifacts.base_dir` from `.agents/preferences.yaml`, falling back to `~/.agents/preferences.yaml`, then default `~/.ai` - - If `base_dir` is relative, resolve from project root. If absolute, use as-is. - - Ticket directory: `{base_dir}/projects/{project_slug}/tickets/{ticket_id}/` + - Use `get-session-context` to obtain `ticket_id`, `project_slug`, and `artifact_base_dir` + - Ticket directory: `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` 3. **Generate slug** from the plan title or description (kebab-case, max 60 chars) diff --git a/packages/agents/content/skills/summarize-change/SKILL.md b/packages/agents/content/skills/summarize-change/SKILL.md index 33dc3ef1..d6de0a83 100644 --- a/packages/agents/content/skills/summarize-change/SKILL.md +++ b/packages/agents/content/skills/summarize-change/SKILL.md @@ -11,7 +11,7 @@ Analyze the current branch's changes since diverging from the default branch. ## Process 1. **Gather context**: - - Call `get-default-branch`, `get-ticket-id`; consult `_data/work-types.md`. + - Use `get-session-context` to obtain `default_branch` and `ticket_id`; consult `_data/work-types.md`. - Determine workspace from commit subjects: parse `{workspace}|{type}:` prefixes, collect unique values. Single → use it. Multiple → use `*`. None → omit. 2. **Analyze changes**: @@ -98,13 +98,11 @@ When the original ticket is accessible: ### Path resolution -1. Read `artifacts.base_dir` from `.agents/preferences.yaml`, falling back to `~/.agents/preferences.yaml`, then default `~/.ai` -2. If base_dir is relative, resolve from project root. If absolute, use as-is. -3. Use `get-project-slug` for the project slug. +Use `get-session-context` to obtain `artifact_base_dir`, `project_slug`, and `ticket_id`. Follow [artifact conventions](_data/artifact-conventions.md). -Ticket directory: `{base_dir}/projects/{project-slug}/tickets/{ticket-id}/` +Ticket directory: `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/` Artifact type: `change-summary`. Filename format: diff --git a/packages/agents/content/skills/summarize-chat/SKILL.md b/packages/agents/content/skills/summarize-chat/SKILL.md index 97f0764a..76ed9dda 100644 --- a/packages/agents/content/skills/summarize-chat/SKILL.md +++ b/packages/agents/content/skills/summarize-chat/SKILL.md @@ -67,14 +67,9 @@ Use these to mark significant sections: ## Saving -Resolve artifact directory: - -1. Read `artifacts.base_dir` and `artifacts.paths.chats` from `.agents/preferences.yaml` -2. If not found there, read from `~/.agents/preferences.yaml` -3. If still not found, use defaults: base_dir=`.ai`, path=`chats` -4. If base_dir is relative, resolve from project root (`git rev-parse --show-toplevel`). If absolute, use as-is. -5. Use `get-project-slug` for the project slug. -6. Full path: `{base_dir}/projects/{project-slug}/{path}/` +Resolve artifact directory using `get-session-context` to obtain `artifact_base_dir`, `project_slug`, and `artifact_paths`. The chats path is `artifact_paths.chats` (default: `chats`). + +Full path: `{artifact_base_dir}/projects/{project_slug}/{chats_path}/` Follow [artifact conventions](_data/artifact-conventions.md). diff --git a/packages/agents/content/skills/wrap-up/SKILL.md b/packages/agents/content/skills/wrap-up/SKILL.md index 0dea5b55..cd7fc882 100644 --- a/packages/agents/content/skills/wrap-up/SKILL.md +++ b/packages/agents/content/skills/wrap-up/SKILL.md @@ -41,12 +41,12 @@ Gather signals to classify the session and identify actionable items. Check these signals in order to classify the session: -| Signal | How to check | Session type | -| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | -| Orchestrated run artifacts | Look for `run-index.json` in the artifact directory for the current ticket (resolve via `get-branch-context` → ticket ID, then check `{base_dir}/projects/{project-slug}/tickets/{ticket-id}/`) | **Orchestrated** | -| Code changes on branch | `git diff --name-only {default-branch}...HEAD` produces output | **Interactive dev** | -| Review artifacts in conversation | Conversation contains review findings or `/review-change` output | **Review** | -| None of the above | No code changes, no run artifacts, no review artifacts | **Research/exploration** | +| Signal | How to check | Session type | +| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ | +| Orchestrated run artifacts | Look for `run-index.json` in the artifact directory for the current ticket (resolve via `get-session-context` → ticket ID, then check `{base_dir}/projects/{project-slug}/tickets/{ticket-id}/`) | **Orchestrated** | +| Code changes on branch | `git diff --name-only {default-branch}...HEAD` produces output | **Interactive dev** | +| Review artifacts in conversation | Conversation contains review findings or `/review-change` output | **Review** | +| None of the above | No code changes, no run artifacts, no review artifacts | **Research/exploration** | Check from top to bottom. Use the first match. If an orchestrated run also has interactive changes after the run, treat it as orchestrated (the run-summary already captured the orchestrated portion). @@ -106,7 +106,7 @@ For each insight found, assign an `I{n}` ID (sequentially: I1, I2, ...) and sugg - `ticket comment` — if the insight relates to the current ticket's work - `devlog` — if the insight is general knowledge not specific to one ticket -If no ticket is available (from `get-branch-context`), default all destinations to `devlog`. +If no ticket is available (from `get-session-context`), default all destinations to `devlog`. #### 1d. Check code state @@ -214,7 +214,7 @@ Process confirmed actions in this order: 1. **Tickets for findings** — invoke `/create-ticket` once per ticket (or once for combined items). Use the item description as the ticket body seed. Apply the label from the issue's context (feature, bug, refactoring, dependencies, ci, tests). Classify items using the prefix: `fixme` → bug, `todo` → task, `warning` → bug, `recommendation` → improvement, `suggestion` → improvement. 2. **Tickets for legacy items** — invoke `/create-ticket` once per item. Label as technical debt or the appropriate category. -3. **Post insights to ticket** — post each `ticket comment` insight via `gh issue comment {number} --body "{insight}"` (ticket number from `get-branch-context`). If no ticket is available, re-route to devlog. +3. **Post insights to ticket** — post each `ticket comment` insight via `gh issue comment {number} --body "{insight}"` (ticket number from `get-session-context`). If no ticket is available, re-route to devlog. 4. **Save session devlog** — invoke `/create-devlog`. Insights with `devlog` destination are automatically included in the devlog content; no separate action is needed for them. **Between each action**, briefly report the result (ticket URL, artifact path) before proceeding to the next. diff --git a/packages/agents/content/subagents/aspect-code-reviewer.md b/packages/agents/content/subagents/aspect-code-reviewer.md index 3d7ac6ce..2cc1e59f 100644 --- a/packages/agents/content/subagents/aspect-code-reviewer.md +++ b/packages/agents/content/subagents/aspect-code-reviewer.md @@ -6,7 +6,7 @@ maxTurns: 20 skills: - anti-patterns - common-mistakes - - get-default-branch + - get-session-context - review-criteria --- diff --git a/packages/agents/content/subagents/aspect-silent-failure-reviewer.md b/packages/agents/content/subagents/aspect-silent-failure-reviewer.md index 8dc7ad27..d80d557d 100644 --- a/packages/agents/content/subagents/aspect-silent-failure-reviewer.md +++ b/packages/agents/content/subagents/aspect-silent-failure-reviewer.md @@ -5,7 +5,7 @@ tools: [Read, Grep, Glob, Bash, Write] maxTurns: 20 skills: - anti-patterns - - get-default-branch + - get-session-context - review-criteria --- diff --git a/packages/agents/content/subagents/aspect-test-reviewer.md b/packages/agents/content/subagents/aspect-test-reviewer.md index f5236321..04a2969e 100644 --- a/packages/agents/content/subagents/aspect-test-reviewer.md +++ b/packages/agents/content/subagents/aspect-test-reviewer.md @@ -6,7 +6,7 @@ maxTurns: 20 skills: - anti-patterns - common-mistakes - - get-default-branch + - get-session-context - review-criteria --- diff --git a/packages/agents/content/subagents/orchestrated-reviewer.md b/packages/agents/content/subagents/orchestrated-reviewer.md index ec240aed..36fa45cd 100644 --- a/packages/agents/content/subagents/orchestrated-reviewer.md +++ b/packages/agents/content/subagents/orchestrated-reviewer.md @@ -6,7 +6,7 @@ maxTurns: 30 skills: - anti-patterns - common-mistakes - - get-default-branch + - get-session-context - review-criteria - software-engineering --- @@ -29,7 +29,7 @@ You will receive: ## Process -1. **Get the diff**: run `git diff ..HEAD` to see all changes in scope, where `` is the pre-resolved SHA provided in your task prompt. If none was provided, compute it yourself: invoke `get-default-branch`, then run `git merge-base HEAD ` to get the SHA. +1. **Get the diff**: run `git diff ..HEAD` to see all changes in scope, where `` is the pre-resolved SHA provided in your task prompt. If none was provided, compute it yourself: invoke `get-session-context`, then run `git merge-base HEAD ` to get the SHA. 2. **Read changed files**: read the full files, not just diffs, to understand context 3. **Evaluate against criteria**: apply review-criteria skill 4. **Form preliminary findings**: classify each finding into the F/W/T/R/S/L scheme and determine overall criticality From 7c11d2a9d939dab0f8f443df794cb1b79c6b9139 Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Tue, 17 Mar 2026 04:32:37 -0700 Subject: [PATCH 2/3] agents|fix: Update stale placeholder variables to match manifest field names Replace hyphenated template variables ({default-branch}, {project-slug}, {ticket-id}) with underscore form ({default_branch}, {project_slug}, {ticket_id}) matching the get-session-context manifest field names in condense-branch and wrap-up skills. --- .../agents/content/skills/condense-branch/SKILL.md | 2 +- packages/agents/content/skills/wrap-up/SKILL.md | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/agents/content/skills/condense-branch/SKILL.md b/packages/agents/content/skills/condense-branch/SKILL.md index 77b1dc8b..92a76a44 100644 --- a/packages/agents/content/skills/condense-branch/SKILL.md +++ b/packages/agents/content/skills/condense-branch/SKILL.md @@ -27,7 +27,7 @@ If branch exists, increment version number. Use `get-session-context` to obtain `default_branch`. ```bash - git reset --soft $(git merge-base {default-branch} HEAD) + git reset --soft $(git merge-base {default_branch} HEAD) git add --all git commit --message "{title}" --message "{body}" --no-gpg-sign --no-verify ``` diff --git a/packages/agents/content/skills/wrap-up/SKILL.md b/packages/agents/content/skills/wrap-up/SKILL.md index cd7fc882..3c8111e1 100644 --- a/packages/agents/content/skills/wrap-up/SKILL.md +++ b/packages/agents/content/skills/wrap-up/SKILL.md @@ -41,12 +41,12 @@ Gather signals to classify the session and identify actionable items. Check these signals in order to classify the session: -| Signal | How to check | Session type | -| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------ | -| Orchestrated run artifacts | Look for `run-index.json` in the artifact directory for the current ticket (resolve via `get-session-context` → ticket ID, then check `{base_dir}/projects/{project-slug}/tickets/{ticket-id}/`) | **Orchestrated** | -| Code changes on branch | `git diff --name-only {default-branch}...HEAD` produces output | **Interactive dev** | -| Review artifacts in conversation | Conversation contains review findings or `/review-change` output | **Review** | -| None of the above | No code changes, no run artifacts, no review artifacts | **Research/exploration** | +| Signal | How to check | Session type | +| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | +| Orchestrated run artifacts | Look for `run-index.json` in the artifact directory for the current ticket (resolve via `get-session-context` → ticket ID, then check `{artifact_base_dir}/projects/{project_slug}/tickets/{ticket_id}/`) | **Orchestrated** | +| Code changes on branch | `git diff --name-only {default_branch}...HEAD` produces output | **Interactive dev** | +| Review artifacts in conversation | Conversation contains review findings or `/review-change` output | **Review** | +| None of the above | No code changes, no run artifacts, no review artifacts | **Research/exploration** | Check from top to bottom. Use the first match. If an orchestrated run also has interactive changes after the run, treat it as orchestrated (the run-summary already captured the orchestrated portion). @@ -110,7 +110,7 @@ If no ticket is available (from `get-session-context`), default all destinations #### 1d. Check code state -Run `git status` and `git log --oneline {default-branch}..HEAD` to understand: +Run `git status` and `git log --oneline {default_branch}..HEAD` to understand: - Are there uncommitted changes? - How many commits are on the branch? From 6596deb3cdd43aa0608c54abf047f1e61c39f394 Mon Sep 17 00:00:00 2001 From: William Thorsen Date: Tue, 17 Mar 2026 05:00:09 -0700 Subject: [PATCH 3/3] agents|tests: Remove obsolete sync-agent-files.sh parity tests The parity tests compared mergeFrontmatter output against files installed by a sync-agent-files.sh script that no longer exists. They required an external configs.macos repo and have been failing since the script was abandoned. --- .../lib/__tests__/frontmatter-merger.test.ts | 91 ------------------- 1 file changed, 91 deletions(-) diff --git a/packages/agents/src/lib/__tests__/frontmatter-merger.test.ts b/packages/agents/src/lib/__tests__/frontmatter-merger.test.ts index b7a3a566..87ef74e2 100644 --- a/packages/agents/src/lib/__tests__/frontmatter-merger.test.ts +++ b/packages/agents/src/lib/__tests__/frontmatter-merger.test.ts @@ -1,21 +1,7 @@ -import { existsSync } from 'node:fs'; -import { readFile } from 'node:fs/promises'; -import path from 'node:path'; - import { describe, expect, it } from 'vitest'; import { mergeFrontmatter, parseFrontmatter, parseOverlayOverrides } from '../frontmatter-merger.js'; -const CONFIGS_BASE = '/Users/william/repos/configs/macos.live'; -const COMMON_SUBAGENTS = path.join(CONFIGS_BASE, 'agents/common/subagents'); -const CLAUDE_AGENTS = path.join(CONFIGS_BASE, '_HOME/_claude/agents'); -const ROVODEV_SUBAGENTS = path.join(CONFIGS_BASE, '_HOME/_rovodev/subagents'); -const HAS_CONFIGS_REPO = existsSync(CONFIGS_BASE); - -async function readFixture(filePath: string): Promise { - return readFile(filePath, 'utf8'); -} - describe('parseFrontmatter', () => { it('should extract agent name from frontmatter', () => { const content = ['---', 'name: test-agent', 'description: A test agent', '---', '', '# Body'].join('\n'); @@ -165,80 +151,3 @@ describe('mergeFrontmatter', () => { expect(lines[6]).toBe('---'); }); }); - -describe.skipIf(!HAS_CONFIGS_REPO)( - 'mergeFrontmatter parity with sync-agent-files.sh (requires configs.macos repo)', - () => { - it('should produce identical output for orchestrated-coder with claude.yml', async () => { - const source = await readFixture(path.join(COMMON_SUBAGENTS, 'orchestrated-coder.md')); - const overlay = await readFixture(path.join(COMMON_SUBAGENTS, '_data/claude.yml')); - const expected = await readFixture(path.join(CLAUDE_AGENTS, 'orchestrated-coder.md')); - - const result = mergeFrontmatter(source, overlay); - expect(result).toBe(expected); - }); - - it('should produce identical output for orchestrated-coder with rovodev.yml', async () => { - const source = await readFixture(path.join(COMMON_SUBAGENTS, 'orchestrated-coder.md')); - const overlay = await readFixture(path.join(COMMON_SUBAGENTS, '_data/rovodev.yml')); - const expected = await readFixture(path.join(ROVODEV_SUBAGENTS, 'orchestrated-coder.md')); - - const result = mergeFrontmatter(source, overlay); - expect(result).toBe(expected); - }); - - it('should produce identical output for aspect-code-reviewer with claude.yml', async () => { - const source = await readFixture(path.join(COMMON_SUBAGENTS, 'aspect-code-reviewer.md')); - const overlay = await readFixture(path.join(COMMON_SUBAGENTS, '_data/claude.yml')); - const expected = await readFixture(path.join(CLAUDE_AGENTS, 'aspect-code-reviewer.md')); - - const result = mergeFrontmatter(source, overlay); - expect(result).toBe(expected); - }); - - it('should produce identical output for aspect-code-reviewer with rovodev.yml', async () => { - const source = await readFixture(path.join(COMMON_SUBAGENTS, 'aspect-code-reviewer.md')); - const overlay = await readFixture(path.join(COMMON_SUBAGENTS, '_data/rovodev.yml')); - const expected = await readFixture(path.join(ROVODEV_SUBAGENTS, 'aspect-code-reviewer.md')); - - const result = mergeFrontmatter(source, overlay); - expect(result).toBe(expected); - }); - - it('should produce identical output for orchestrated-architect with claude.yml', async () => { - const source = await readFixture(path.join(COMMON_SUBAGENTS, 'orchestrated-architect.md')); - const overlay = await readFixture(path.join(COMMON_SUBAGENTS, '_data/claude.yml')); - const expected = await readFixture(path.join(CLAUDE_AGENTS, 'orchestrated-architect.md')); - - const result = mergeFrontmatter(source, overlay); - expect(result).toBe(expected); - }); - - it('should produce identical output for orchestrated-architect with rovodev.yml', async () => { - const source = await readFixture(path.join(COMMON_SUBAGENTS, 'orchestrated-architect.md')); - const overlay = await readFixture(path.join(COMMON_SUBAGENTS, '_data/rovodev.yml')); - const expected = await readFixture(path.join(ROVODEV_SUBAGENTS, 'orchestrated-architect.md')); - - const result = mergeFrontmatter(source, overlay); - expect(result).toBe(expected); - }); - - it('should produce identical output for planner with claude.yml', async () => { - const source = await readFixture(path.join(COMMON_SUBAGENTS, 'planner.md')); - const overlay = await readFixture(path.join(COMMON_SUBAGENTS, '_data/claude.yml')); - const expected = await readFixture(path.join(CLAUDE_AGENTS, 'planner.md')); - - const result = mergeFrontmatter(source, overlay); - expect(result).toBe(expected); - }); - - it('should produce identical output for planner with rovodev.yml', async () => { - const source = await readFixture(path.join(COMMON_SUBAGENTS, 'planner.md')); - const overlay = await readFixture(path.join(COMMON_SUBAGENTS, '_data/rovodev.yml')); - const expected = await readFixture(path.join(ROVODEV_SUBAGENTS, 'planner.md')); - - const result = mergeFrontmatter(source, overlay); - expect(result).toBe(expected); - }); - }, -);