Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"agents": [
"./agents/oracle",
"./agents/architect",
"./agents/recon"
"./agents/sentinel"
],
"skills": [
"./skills/spec-brainstorm",
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ Process skills come first. Knowledge skills get invoked by process skills when n

**Deep Thinking** (3 skills)
- `oracle-debug` - Systematic debugging, root cause before fixes
- `oracle-grill-me` - Socratic interrogation of plans against domain model and documented decisions
- `oracle-grill-me` - Socratic review of plans, specs, decisions, and ideas until ambiguity is resolved, shared understanding is reached, and the resulting domain language and decisions are recorded
- `oracle-domain-modelling` - Build and sharpen the project's domain model

**TypeScript Patterns** (8 skills)
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- **breaking:** rename the `recon` agent to `sentinel`. Existing Atelier configuration is migrated automatically and legacy generated files are removed during the next command.

## [1.4.0](https://github.com/martinffx/atelier/compare/v1.3.0...v1.4.0) (2026-07-18)


Expand Down
215 changes: 120 additions & 95 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion agents/architect.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ Before finishing, confirm you have:
- DO apply architectural patterns and load relevant skills
- DON'T write implementation code (that's for `spec-implement`)
- DON'T conduct discovery interviews (that's `oracle`)
- DON'T handle file operations or template application (that's `recon`)
- DON'T handle file operations or template application (that's `sentinel`)
6 changes: 3 additions & 3 deletions agents/recon.md → agents/sentinel.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
name: recon
description: Fast codebase reconnaissance and exploration, concise summarization of findings, precise location of relevant context, and efficient workspace preparation through targeted file operations and template application
name: sentinel
description: Fast codebase reconnaissance and exploration, concise summaries of findings, precise location of relevant context, and efficient workspace preparation through targeted file operations and template application
---

You are the **Recon**, a fast reconnaissance agent. Your job is to explore codebases, summarize what you find, highlight relevant context, and point others to exactly where things live. You move quickly, read minimally, and never break existing work.
You are the **Sentinel**, a fast reconnaissance agent. Your job is to explore codebases, summarize what you find, highlight relevant context, and point others to exactly where things live. You move quickly, read minimally, and never break existing work.

## Role

Expand Down
4 changes: 2 additions & 2 deletions skills/code-review/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Use these concrete harness subagent types. If an exact match is unavailable, use

| `subagent_type` | Purpose |
|-----------------|---------|
| `recon` | Triage only: changed-file analysis, context retrieval, reviewer selection |
| `sentinel` | Triage only: changed-file analysis, context retrieval, reviewer selection |
| `oracle` | Parallel reviewer personas, evidence-based critique, failure-mode analysis, challenge validation |
| `architect` | Architecture, design-boundary, data-model, and API-contract review |

Expand Down Expand Up @@ -67,7 +67,7 @@ Follows [code-subagents](../code-subagents/SKILL.md) patterns:

| Agent | Used In Step |
|-------|--------------|
| `recon` | Triage only (context retrieval, file analysis) |
| `sentinel` | Triage only (context retrieval, file analysis) |
| `oracle` | Reviewers and challenge validation |
| `architect` | Architect (architecture review) |

Expand Down
6 changes: 3 additions & 3 deletions skills/code-review/references/rq.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ Capture the list of changed files and the full diff.

**Purpose:** Analyze diff to determine context, select reviewers, identify relevant skills to look for.

**Uses:** `recon` agent.
**Uses:** `sentinel` agent.

### Subagent Invocation

```yaml
subagent_type: recon
subagent_type: sentinel
description: "Triage diff for code review"
prompt: |
Analyze this code diff to determine review needs.
Expand Down Expand Up @@ -364,7 +364,7 @@ Display findings in terminal per [output.md](./output.md).
| Step | Subagent | Uses | Parallel? | Purpose |
|------|----------|------|----------|---------|
| 1 | Get Diff | inline | — | `git diff <branch>` |
| 2 | Triage | `recon` agent | No | Detect context, select reviewers, identify relevant skills to look for |
| 2 | Triage | `sentinel` agent | No | Detect context, select reviewers, identify relevant skills to look for |
| 3 | Reviewers | `oracle` agent | Yes (per reviewer) | Specialty analysis |
| 4 | Synthesis | inline | No | Deduplicate and group |
| 5 | Architect | `architect` agent | No | Architecture review |
Expand Down
8 changes: 4 additions & 4 deletions src/adapters/claude.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe('claude adapter', () => {

it('installAgents writes agent files', () => {
claudeAdapter.installAgents(section(), basePath);
for (const name of ['recon', 'oracle', 'architect']) {
for (const name of ['sentinel', 'oracle', 'architect']) {
const path = join(basePath, '.claude', 'agents', `${name}.md`);
expect(existsSync(path)).toBe(true);
const content = readFileSync(path, 'utf-8');
Expand All @@ -75,7 +75,7 @@ describe('claude adapter', () => {
const files = claudeAdapter.fileList(basePath);
expect(files.map(f => f.path)).toEqual([
join(basePath, '.claude', 'settings.json'),
join(basePath, '.claude', 'agents', 'recon.md'),
join(basePath, '.claude', 'agents', 'sentinel.md'),
join(basePath, '.claude', 'agents', 'oracle.md'),
join(basePath, '.claude', 'agents', 'architect.md'),
]);
Expand All @@ -88,7 +88,7 @@ describe('claude adapter', () => {

claudeAdapter.remove(s, basePath);

expect(existsSync(join(basePath, '.claude', 'agents', 'recon.md'))).toBe(false);
expect(existsSync(join(basePath, '.claude', 'agents', 'sentinel.md'))).toBe(false);
expect(existsSync(join(basePath, '.claude', 'agents', 'oracle.md'))).toBe(false);
expect(existsSync(join(basePath, '.claude', 'agents', 'architect.md'))).toBe(false);
expect(existsSync(join(basePath, '.claude', 'agents'))).toBe(false);
Expand Down Expand Up @@ -126,6 +126,6 @@ describe('claude adapter', () => {

expect(existsSync(userAgent)).toBe(true);
expect(existsSync(agentsDir)).toBe(true);
expect(existsSync(join(agentsDir, 'recon.md'))).toBe(false);
expect(existsSync(join(agentsDir, 'sentinel.md'))).toBe(false);
});
});
6 changes: 4 additions & 2 deletions src/adapters/claude.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { FileWriteError, HarnessConfigError } from '../utils/errors.js';
import { shortPath } from '../services/paths.js';
import { SimpleConfigSchema } from '../utils/schemas.js';
import { promptForSimpleModels } from '../services/prompt.js';
import { AGENT_NAMES } from '../constants.js';
import { AGENT_NAMES, LEGACY_AGENT_NAME } from '../constants.js';

const ANTHROPIC_MODELS = ['haiku', 'sonnet', 'opus', 'opusplan'] as const;

const DEFAULT_MODELS = {
default_model: 'opusplan',
recon: 'haiku',
sentinel: 'haiku',
oracle: 'opus',
architect: 'opus',
} as const;
Expand Down Expand Up @@ -59,6 +59,7 @@ function installAgents(section: HarnessSection, basePath: string): void {
} catch (err) {
throw new FileWriteError(agentsDir, err instanceof Error ? err.message : String(err));
}
rmSync(join(agentsDir, `${LEGACY_AGENT_NAME}.md`), { force: true });

for (const agent of config.agents) {
const template = readTemplate(agent.template);
Expand Down Expand Up @@ -115,6 +116,7 @@ function remove(section: HarnessSection, basePath: string): void {
rmSync(file, { force: true });
}
}
rmSync(join(agentsDir, `${LEGACY_AGENT_NAME}.md`), { force: true });

removeDirIfEmpty(agentsDir);
removeAtelierSettings(claudeDir);
Expand Down
8 changes: 4 additions & 4 deletions src/adapters/codex.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('codex adapter', () => {

it('installAgents writes agent toml files', () => {
codexAdapter.installAgents(section(), basePath);
for (const name of ['recon', 'oracle', 'architect']) {
for (const name of ['sentinel', 'oracle', 'architect']) {
const path = join(basePath, '.codex', 'agents', `${name}.toml`);
expect(existsSync(path)).toBe(true);
const content = TOML.parse(readFileSync(path, 'utf-8')) as Record<string, unknown>;
Expand All @@ -69,7 +69,7 @@ describe('codex adapter', () => {
const files = codexAdapter.fileList(basePath);
expect(files.map(f => f.path)).toEqual([
join(basePath, '.codex', 'config.toml'),
join(basePath, '.codex', 'agents', 'recon.toml'),
join(basePath, '.codex', 'agents', 'sentinel.toml'),
join(basePath, '.codex', 'agents', 'oracle.toml'),
join(basePath, '.codex', 'agents', 'architect.toml'),
]);
Expand All @@ -81,7 +81,7 @@ describe('codex adapter', () => {
codexAdapter.installAgents(s, basePath);
codexAdapter.remove(s, basePath);

expect(existsSync(join(basePath, '.codex', 'agents', 'recon.toml'))).toBe(false);
expect(existsSync(join(basePath, '.codex', 'agents', 'sentinel.toml'))).toBe(false);
expect(existsSync(join(basePath, '.codex', 'agents', 'oracle.toml'))).toBe(false);
expect(existsSync(join(basePath, '.codex', 'agents', 'architect.toml'))).toBe(false);
expect(existsSync(join(basePath, '.codex', 'agents'))).toBe(false);
Expand Down Expand Up @@ -118,7 +118,7 @@ describe('codex adapter', () => {

expect(existsSync(userAgent)).toBe(true);
expect(existsSync(agentsDir)).toBe(true);
expect(existsSync(join(agentsDir, 'recon.toml'))).toBe(false);
expect(existsSync(join(agentsDir, 'sentinel.toml'))).toBe(false);

// Cleanup so the empty-dir removal does not affect other assertions
rmSync(agentsDir, { recursive: true, force: true });
Expand Down
6 changes: 4 additions & 2 deletions src/adapters/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import inquirer from 'inquirer';
import * as TOML from 'smol-toml';
import { readTemplate } from '../utils/templates.js';
import type { CodexConfig, HarnessAdapter, FileEntry, Provider, HarnessSection } from '../types.js';
import { AGENT_NAMES } from '../constants.js';
import { AGENT_NAMES, LEGACY_AGENT_NAME } from '../constants.js';
import { FileWriteError, HarnessConfigError } from '../utils/errors.js';
import { shortPath } from '../services/paths.js';
import { SimpleConfigSchema } from '../utils/schemas.js';
Expand All @@ -23,7 +23,7 @@ const OPENAI_MODELS = [

const DEFAULT_MODELS = {
default_model: 'gpt-5.6-terra',
recon: 'gpt-5.6-luna',
sentinel: 'gpt-5.6-luna',
oracle: 'gpt-5.6-sol',
architect: 'gpt-5.6-sol',
} as const;
Expand Down Expand Up @@ -83,6 +83,7 @@ function installAgents(section: HarnessSection, basePath: string): void {
} catch (err) {
throw new FileWriteError(agentsDir, err instanceof Error ? err.message : String(err));
}
rmSync(join(agentsDir, `${LEGACY_AGENT_NAME}.toml`), { force: true });

for (const agent of config.agents) {
const template = readTemplate(agent.template);
Expand Down Expand Up @@ -161,6 +162,7 @@ function remove(section: HarnessSection, basePath: string): void {
rmSync(file, { force: true });
}
}
rmSync(join(agentsDir, `${LEGACY_AGENT_NAME}.toml`), { force: true });

removeDirIfEmpty(agentsDir);
cleanCodexConfig(codexDir);
Expand Down
6 changes: 3 additions & 3 deletions src/adapters/cursor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ describe('cursor adapter', () => {
cursorAdapter.mergeHarnessConfig(cursorAdapter.defaultSection(), basePath);
cursorAdapter.installAgents(cursorAdapter.defaultSection(), basePath);

const agentPath = join(basePath, '.cursor', 'agents', 'recon.md');
const agentPath = join(basePath, '.cursor', 'agents', 'sentinel.md');
expect(readFileSync(agentPath, 'utf-8')).toContain('model: composer-2.5');
expect(readFileSync(agentPath, 'utf-8')).toContain('name: recon');
expect(readFileSync(agentPath, 'utf-8')).toContain('name: sentinel');
expect(readFileSync(nativeConfigPath, 'utf-8')).toBe('{"user":"config"}\n');
});

Expand All @@ -51,7 +51,7 @@ describe('cursor adapter', () => {
writeFileSync(userAgentPath, '---\nname: user\n---\n');
cursorAdapter.remove(section, basePath);

expect(existsSync(join(basePath, '.cursor', 'agents', 'recon.md'))).toBe(false);
expect(existsSync(join(basePath, '.cursor', 'agents', 'sentinel.md'))).toBe(false);
expect(existsSync(userAgentPath)).toBe(true);
expect(readFileSync(nativeConfigPath, 'utf-8')).toBe('{"user":"config"}\n');
});
Expand Down
6 changes: 4 additions & 2 deletions src/adapters/cursor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { existsSync, mkdirSync, readdirSync, rmSync, rmdirSync, writeFileSync } from 'fs';
import { join } from 'path';
import inquirer from 'inquirer';
import { AGENT_NAMES } from '../constants.js';
import { AGENT_NAMES, LEGACY_AGENT_NAME } from '../constants.js';
import { shortPath } from '../services/paths.js';
import type { CursorConfig, FileEntry, HarnessAdapter, HarnessSection, Provider } from '../types.js';
import { CursorConfigSchema } from '../utils/schemas.js';
Expand All @@ -21,7 +21,7 @@ const CURSOR_MODELS = [
] as const;

const DEFAULT_MODELS = {
recon: 'composer-2.5',
sentinel: 'composer-2.5',
oracle: 'claude-opus-4-8-high',
architect: 'gpt-5.6-sol-medium',
} as const;
Expand Down Expand Up @@ -76,6 +76,7 @@ function installAgents(section: HarnessSection, basePath: string): void {
} catch (err) {
throw new FileWriteError(agentsDir, err instanceof Error ? err.message : String(err));
}
rmSync(join(agentsDir, `${LEGACY_AGENT_NAME}.md`), { force: true });

for (const agent of config.agents) {
const template = readTemplate(agent.template);
Expand Down Expand Up @@ -105,6 +106,7 @@ function remove(section: HarnessSection, basePath: string): void {
for (const agent of config.agents) {
rmSync(join(agentsDir, `${agent.name}.md`), { force: true });
}
rmSync(join(agentsDir, `${LEGACY_AGENT_NAME}.md`), { force: true });
if (existsSync(agentsDir) && readdirSync(agentsDir).length === 0) {
rmdirSync(agentsDir);
}
Expand Down
10 changes: 5 additions & 5 deletions src/adapters/opencode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe('opencode adapter', () => {
expect(openAiSection.build_model).toBe('openai/gpt-5.6-terra');
expect(openAiSection.plan_model).toBe('openai/gpt-5.6-sol');
expect(openAiSection.agents).toEqual([
{ template: 'recon', name: 'recon', model: 'openai/gpt-5.6-luna' },
{ template: 'sentinel', name: 'sentinel', model: 'openai/gpt-5.6-luna' },
{ template: 'oracle', name: 'oracle', model: 'openai/gpt-5.6-sol' },
{ template: 'architect', name: 'architect', model: 'openai/gpt-5.6-sol' },
]);
Expand Down Expand Up @@ -69,7 +69,7 @@ describe('opencode adapter', () => {
it('installAgents writes agent files', () => {
opencodeAdapter.installAgents(section(), basePath);
const agentsDir = join(getOpencodeRoot(basePath), 'agent');
for (const name of ['recon', 'oracle', 'architect']) {
for (const name of ['sentinel', 'oracle', 'architect']) {
const path = join(agentsDir, `${name}.md`);
expect(existsSync(path)).toBe(true);
const content = readFileSync(path, 'utf-8');
Expand All @@ -84,7 +84,7 @@ describe('opencode adapter', () => {
const files = opencodeAdapter.fileList(basePath);
expect(files.map(f => f.path)).toEqual([
join(basePath, 'opencode.json'),
join(getOpencodeRoot(basePath), 'agent', 'recon.md'),
join(getOpencodeRoot(basePath), 'agent', 'sentinel.md'),
join(getOpencodeRoot(basePath), 'agent', 'oracle.md'),
join(getOpencodeRoot(basePath), 'agent', 'architect.md'),
]);
Expand All @@ -103,7 +103,7 @@ describe('opencode adapter', () => {

opencodeAdapter.remove(s, basePath);

expect(existsSync(join(root, 'agent', 'recon.md'))).toBe(false);
expect(existsSync(join(root, 'agent', 'sentinel.md'))).toBe(false);
expect(existsSync(join(root, 'agent', 'oracle.md'))).toBe(false);
expect(existsSync(join(root, 'agent', 'architect.md'))).toBe(false);
expect(existsSync(join(root, 'agent'))).toBe(false);
Expand Down Expand Up @@ -141,7 +141,7 @@ describe('opencode adapter', () => {

expect(existsSync(userAgent)).toBe(true);
expect(existsSync(agentsDir)).toBe(true);
expect(existsSync(join(agentsDir, 'recon.md'))).toBe(false);
expect(existsSync(join(agentsDir, 'sentinel.md'))).toBe(false);

rmSync(agentsDir, { recursive: true, force: true });
});
Expand Down
Loading