@firatcand/roster 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -215
- package/agents/lesson-drafter.md +3 -8
- package/agents/pattern-detector.md +0 -1
- package/bin/roster.js +7233 -1197
- package/data/plan-ceilings.yaml +57 -0
- package/package.json +8 -3
- package/skills/chief-of-staff/SKILL.md +199 -59
- package/skills/dreamer/SKILL.md +8 -7
- package/skills/roster-orchestrator/SKILL.md +53 -25
- package/templates/CLAUDE.project.template.md +1 -1
- package/templates/CONTEXT.template.md +2 -2
- package/templates/gitignore-defaults.txt +2 -0
- package/templates/hooks/banner.sh +47 -0
- package/templates/scaffold/chief-of-staff/README.md +16 -24
- package/templates/scaffold/chief-of-staff/agent.md +22 -32
- package/templates/scaffold/chief-of-staff/plans/audit-agent.yaml +4 -4
- package/templates/scaffold/chief-of-staff/plans/audit-repo.yaml +5 -4
- package/templates/scaffold/chief-of-staff/plans/create-agent.yaml +5 -34
- package/templates/scaffold/config/project.yaml.template +10 -0
- package/templates/scaffold/conventions.md +188 -173
- package/templates/scaffold/dreamer/README.md +2 -2
- package/templates/scaffold/dreamer/agent.md +0 -1
- package/templates/scaffold/dreamer/plans/nightly-reflection.yaml +23 -37
- package/templates/scaffold/dreamer/subagents/lesson-drafter.md +2 -7
- package/templates/scaffold/{projects/_demo/guidelines → guidelines}/asset-links.md +4 -0
- package/templates/scaffold/{projects/_demo/guidelines → guidelines}/brand-book.md +4 -0
- package/templates/scaffold/{projects/_demo/guidelines → guidelines}/messaging.md +4 -0
- package/templates/scaffold/{projects/_demo/guidelines → guidelines}/voice.md +4 -0
- package/templates/scaffold/logs/cron/.gitkeep +1 -0
- package/templates/scaffold/ops/EXPERT.md +5 -5
- package/templates/scaffold/scripts/audit-agent.sh +326 -0
- package/templates/scaffold/scripts/audit-repo.sh +218 -0
- package/templates/scaffold/scripts/create-function.sh +267 -0
- package/templates/scaffold/scripts/lib/README.md +6 -1
- package/templates/scaffold/scripts/lib/bindings-prompt.sh +53 -0
- package/templates/scaffold/scripts/lib/functions.sh +17 -5
- package/templates/scaffold/scripts/new-agent.sh +416 -0
- package/templates/scaffold/scripts/rename-agent.sh +91 -0
- package/templates/scaffold/scripts/save-state.sh +32 -0
- package/agents/critic.md +0 -74
- package/agents/enricher.md +0 -56
- package/agents/promotion-arbiter.md +0 -71
- package/agents/prospector.md +0 -51
- package/agents/writer.md +0 -58
- package/skills/sdr/SKILL.md +0 -147
- package/templates/scaffold/chief-of-staff/plans/add-agent-to-project.yaml +0 -45
- package/templates/scaffold/chief-of-staff/plans/archive-project.yaml +0 -51
- package/templates/scaffold/chief-of-staff/plans/audit-project.yaml +0 -34
- package/templates/scaffold/chief-of-staff/plans/create-project.yaml +0 -65
- package/templates/scaffold/chief-of-staff/plans/remove-agent-from-project.yaml +0 -50
- package/templates/scaffold/chief-of-staff/plans/rename-project.yaml +0 -62
- package/templates/scaffold/chief-of-staff/plans/unarchive-project.yaml +0 -41
- package/templates/scaffold/dreamer/subagents/promotion-arbiter.md +0 -64
- package/templates/scaffold/gtm/sdr/.claude/settings.json +0 -3
- package/templates/scaffold/gtm/sdr/.mcp.json +0 -21
- package/templates/scaffold/gtm/sdr/README.md +0 -46
- package/templates/scaffold/gtm/sdr/agent.md +0 -136
- package/templates/scaffold/gtm/sdr/plans/cold-outreach.yaml +0 -92
- package/templates/scaffold/gtm/sdr/projects/_demo/asset-references.md +0 -7
- package/templates/scaffold/gtm/sdr/projects/_demo/config/default.yaml +0 -69
- package/templates/scaffold/gtm/sdr/projects/_demo/log/feedback/.gitkeep +0 -0
- package/templates/scaffold/gtm/sdr/projects/_demo/log/runs/.gitkeep +0 -0
- package/templates/scaffold/gtm/sdr/projects/_demo/playbook/.gitkeep +0 -0
- package/templates/scaffold/gtm/sdr/subagents/critic.md +0 -67
- package/templates/scaffold/gtm/sdr/subagents/enricher.md +0 -49
- package/templates/scaffold/gtm/sdr/subagents/prospector.md +0 -44
- package/templates/scaffold/gtm/sdr/subagents/writer.md +0 -51
- package/templates/scaffold/projects/_demo/CLAUDE.md +0 -35
- package/templates/scaffold/projects/_demo/README.md +0 -16
- package/templates/scaffold/projects/_demo/assets/.gitkeep +0 -0
- package/templates/scaffold/projects/_demo/config/default.yaml +0 -28
- package/templates/scaffold/projects/_demo/state.md +0 -11
- package/templates/scaffold/scripts/new-project.sh +0 -125
- /package/templates/scaffold/gtm/{sdr/playbook/.gitkeep → .gitkeep} +0 -0
- /package/templates/scaffold/{projects/_demo/guidelines → guidelines}/icps/_persona-template.md +0 -0
|
@@ -9,7 +9,7 @@ Cross-domain pattern detection matters. A lesson observed in Twitter automation
|
|
|
9
9
|
## Files
|
|
10
10
|
|
|
11
11
|
- `agent.md` — orchestrator contract
|
|
12
|
-
- `subagents/` — pattern-detector, lesson-drafter
|
|
12
|
+
- `subagents/` — pattern-detector, lesson-drafter
|
|
13
13
|
- `playbook/` — the dreamer's own lessons (lessons about how to learn)
|
|
14
14
|
- `logs/` — its own runs
|
|
15
15
|
- `state.md` — last processed cutoff
|
|
@@ -17,7 +17,7 @@ Cross-domain pattern detection matters. A lesson observed in Twitter automation
|
|
|
17
17
|
|
|
18
18
|
## Invocation
|
|
19
19
|
|
|
20
|
-
Nightly via
|
|
20
|
+
Nightly via the native desktop scheduler. Register with `roster schedule install` — each fire spawns a fresh CLI session in the workspace, loads `CONTEXT.md`, invokes the `roster-orchestrator` skill, and dispatches the dreamer in isolated subagent context. See `conventions.md` § Schedules for the model.
|
|
21
21
|
|
|
22
22
|
On-demand from a session: "Run the dreamer on the last week's outreach runs across all projects."
|
|
23
23
|
|
|
@@ -45,7 +45,6 @@ Typically scheduled nightly via cron or `/schedule`. When invoked without a plan
|
|
|
45
45
|
|
|
46
46
|
- `pattern-detector.md` — finds patterns across runs+feedback
|
|
47
47
|
- `lesson-drafter.md` — drafts a single lesson in schema format
|
|
48
|
-
- `promotion-arbiter.md` — decides project vs global scope for validated lessons
|
|
49
48
|
|
|
50
49
|
## Tools and bindings
|
|
51
50
|
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
plan: nightly-reflection
|
|
2
2
|
description: |
|
|
3
|
-
Reads runs and feedback across all agents
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
pattern is observed in 2+ projects. Updates dreamer/state.md with the new
|
|
8
|
-
cutoff and a summary.
|
|
3
|
+
Reads runs and feedback across all agents since the last cutoff, detects
|
|
4
|
+
patterns, drafts lesson candidates, surfaces them for HITL approval via
|
|
5
|
+
Slack #admin, and writes approved lessons to each agent's playbook.
|
|
6
|
+
Updates dreamer/state.md with the new cutoff and a summary.
|
|
9
7
|
|
|
10
8
|
inputs:
|
|
11
9
|
mode:
|
|
@@ -14,7 +12,7 @@ inputs:
|
|
|
14
12
|
description: nightly | weekly | on-demand. Selects how aggressively to scan and how to weight evidence.
|
|
15
13
|
scope:
|
|
16
14
|
required: false
|
|
17
|
-
description: Limit to one
|
|
15
|
+
description: Limit to one agent (function/agent) — useful for on-demand runs.
|
|
18
16
|
since:
|
|
19
17
|
required: false
|
|
20
18
|
description: ISO timestamp cutoff. If omitted, uses last_processed_through from dreamer/state.md.
|
|
@@ -24,7 +22,6 @@ outputs:
|
|
|
24
22
|
candidates_drafted: integer
|
|
25
23
|
candidates_approved: integer
|
|
26
24
|
lessons_written: integer
|
|
27
|
-
lessons_promoted: integer
|
|
28
25
|
conflicts_surfaced: integer
|
|
29
26
|
|
|
30
27
|
steps:
|
|
@@ -35,9 +32,9 @@ steps:
|
|
|
35
32
|
|
|
36
33
|
- id: identify_material
|
|
37
34
|
description: |
|
|
38
|
-
Walk every agent's <function>/<agent>/
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
Walk every agent's <function>/<agent>/log/runs/<YYYY-MM>/ and
|
|
36
|
+
log/feedback/<YYYY-MM>/ for files newer than the cutoff. Match runs to
|
|
37
|
+
feedback by filename. Apply ${inputs.scope} filter if provided.
|
|
41
38
|
|
|
42
39
|
- id: detect_patterns
|
|
43
40
|
subagent: pattern-detector
|
|
@@ -62,52 +59,41 @@ steps:
|
|
|
62
59
|
description: |
|
|
63
60
|
For each candidate that meets threshold or extends an existing lesson,
|
|
64
61
|
draft a lesson in schema format with frontmatter (id, source: dreamer,
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
status: candidate, agent, created). Place drafts in
|
|
63
|
+
<function>/<agent>/pending/.
|
|
67
64
|
args:
|
|
68
65
|
input_from: accumulate_evidence
|
|
69
66
|
|
|
70
|
-
- id: arbitrate_promotion
|
|
71
|
-
subagent: promotion-arbiter
|
|
72
|
-
description: |
|
|
73
|
-
For any project lesson validated in 2+ projects, decide whether to
|
|
74
|
-
promote to global scope. Returns project vs global designation per
|
|
75
|
-
candidate.
|
|
76
|
-
args:
|
|
77
|
-
input_from: draft_lessons
|
|
78
|
-
|
|
79
67
|
- id: hitl_routing
|
|
80
68
|
description: |
|
|
81
|
-
Post all candidates
|
|
69
|
+
Post all candidates to Slack #admin (channel from
|
|
82
70
|
SLACK_HITL_CHANNEL_ADMIN env var) as threaded messages, one per
|
|
83
71
|
candidate. Format: "Candidate lesson <id>: <title>. Approve / Reject /
|
|
84
72
|
Defer." TTL 7 days. If Slack unavailable, queue candidates locally in
|
|
85
|
-
|
|
73
|
+
<function>/<agent>/pending/ and retry next run.
|
|
86
74
|
approval: slack
|
|
87
75
|
|
|
88
76
|
- id: apply_approvals
|
|
89
77
|
description: |
|
|
90
|
-
On approval,
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
Respect human-written lessons (source: human) — never modify or
|
|
98
|
-
supersede without explicit HITL approval.
|
|
78
|
+
On approval, move the candidate file from <function>/<agent>/pending/
|
|
79
|
+
to <function>/<agent>/playbook/L-...md. There is no scope decision;
|
|
80
|
+
v1 has a single playbook per agent. All dreamer-written lessons get
|
|
81
|
+
source: dreamer in frontmatter. Respect human-written lessons
|
|
82
|
+
(source: human) — never modify or supersede without explicit HITL
|
|
83
|
+
approval. Retired candidates update an existing lesson's status:
|
|
84
|
+
retired with reason.
|
|
99
85
|
|
|
100
86
|
- id: update_state
|
|
101
87
|
description: |
|
|
102
88
|
Write timestamp + summary to dreamer/state.md:
|
|
103
89
|
last_processed_through: <ISO timestamp>
|
|
104
|
-
last_run_summary: drafted N, approved M, written K
|
|
90
|
+
last_run_summary: drafted N, approved M, written K
|
|
105
91
|
|
|
106
92
|
- id: write_run_log
|
|
107
93
|
description: |
|
|
108
94
|
Write run details to dreamer/logs/<YYYY-MM>/<YYYY-MM-DD-HHMM>.md
|
|
109
|
-
including: material processed (counts by
|
|
110
|
-
|
|
111
|
-
|
|
95
|
+
including: material processed (counts by agent), patterns detected,
|
|
96
|
+
lesson candidates drafted (Slack thread links), approvals applied,
|
|
97
|
+
conflicts surfaced.
|
|
112
98
|
|
|
113
99
|
approval_channel: slack
|
|
@@ -9,20 +9,17 @@ Take a candidate pattern and draft a lesson file in the schema defined in `conve
|
|
|
9
9
|
- `pattern` (object): output from pattern-detector
|
|
10
10
|
- `existing_lesson` (object, optional): if extending an existing lesson, the current version
|
|
11
11
|
- `agent` (string): which agent
|
|
12
|
-
- `project` (string): which project sourced it
|
|
13
12
|
|
|
14
13
|
## Output
|
|
15
14
|
|
|
16
15
|
```yaml
|
|
17
16
|
suggested_filename: L-2026-04-26-001.md
|
|
18
|
-
suggested_path: <function>/<agent>/
|
|
17
|
+
suggested_path: <function>/<agent>/playbook/
|
|
19
18
|
status: candidate
|
|
20
19
|
lesson_markdown: |
|
|
21
20
|
---
|
|
22
21
|
id: L-2026-04-26-001
|
|
23
22
|
source: dreamer
|
|
24
|
-
scope: project # or global
|
|
25
|
-
project: _demo # or "—" if scope=global
|
|
26
23
|
agent: sdr
|
|
27
24
|
...full frontmatter per conventions...
|
|
28
25
|
---
|
|
@@ -31,7 +28,6 @@ lesson_markdown: |
|
|
|
31
28
|
|
|
32
29
|
## Pattern observed
|
|
33
30
|
## Recommendation
|
|
34
|
-
## Why this might be project-specific
|
|
35
31
|
## Retirement criteria
|
|
36
32
|
```
|
|
37
33
|
|
|
@@ -43,9 +39,8 @@ None.
|
|
|
43
39
|
|
|
44
40
|
- Use the exact schema in `conventions.md`. Don't invent fields.
|
|
45
41
|
- Always set `source: dreamer`.
|
|
46
|
-
- Default to `scope: project` unless explicitly handling a promotion case.
|
|
47
42
|
- Cite evidence in body, not just frontmatter.
|
|
48
|
-
- Body has
|
|
43
|
+
- Body has 3 sections: pattern, recommendation, retirement criteria. That's it.
|
|
49
44
|
|
|
50
45
|
## Quality bar
|
|
51
46
|
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
> **Example content — replace with your project's actuals.**
|
|
2
|
+
> This file ships filled with an illustrative brand ("Acme Corp") so you can
|
|
3
|
+
> see the expected shape. Overwrite freely; no agent will warn if you do.
|
|
4
|
+
|
|
1
5
|
# Messaging — Acme Corp
|
|
2
6
|
|
|
3
7
|
## Headline value props
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
> **Example content — replace with your project's actuals.**
|
|
2
|
+
> This file ships filled with an illustrative brand ("Acme Corp") so you can
|
|
3
|
+
> see the expected shape. Overwrite freely; no agent will warn if you do.
|
|
4
|
+
|
|
1
5
|
# Voice — Acme Corp
|
|
2
6
|
|
|
3
7
|
## Adjectives describing the brand voice
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Cron log directory
|
|
@@ -11,7 +11,7 @@ Ops advisor for an early-stage solo founder running an agent team. Cover automat
|
|
|
11
11
|
|
|
12
12
|
## Scope
|
|
13
13
|
|
|
14
|
-
- **Critique**: Audit `
|
|
14
|
+
- **Critique**: Audit `roster/<function>/schedules.yaml`, `.roster/schedule-specs/`, agent `config/default.yaml` files, `.env` patterns, and ops-related project guidelines when they exist. State the principle being violated. Score risk: data loss > silent failure > cost > polish.
|
|
15
15
|
- **Generate guidelines**: Produce or refine ops-related guideline files when a project demands them — `projects/<project>/guidelines/ops-runbook.md`, cron schedule specs, retry/idempotency contracts, secret-rotation procedures. Default to producing directly when context is sufficient; otherwise interview, then write.
|
|
16
16
|
- **Guide**: Scheduling decisions, secrets management, deployment patterns, observability strategy, failure-mode reasoning. Strategic output — files only when the task asks for substrate.
|
|
17
17
|
|
|
@@ -22,16 +22,16 @@ You do **NOT** produce tactical artifacts (specific cron wrapper shell scripts,
|
|
|
22
22
|
On invocation, read in this order:
|
|
23
23
|
|
|
24
24
|
1. `projects/<project>/CLAUDE.md` — project identity and what runs against it
|
|
25
|
-
2. `
|
|
25
|
+
2. `roster/<function>/schedules.yaml` and `.roster/schedule-specs/` — current automation surface (Phase 2.5 native-scheduler model; see `conventions.md` § Schedules and [ADR-0001](../../docs/adr/0001-scheduling-architecture.md))
|
|
26
26
|
3. The relevant agent's `agent.md` and `config/default.yaml` — tool bindings, schedules, caps
|
|
27
27
|
4. `projects/<project>/state.md` — current focus
|
|
28
|
-
5. `logs/cron/*` for recent failures, if a reliability question
|
|
28
|
+
5. `logs/cron/*` for recent `roster schedule install --tool codex --via cron` failures, if a reliability question
|
|
29
29
|
|
|
30
30
|
Identify gaps. Ask only about gaps. Don't re-ask what's already in substrate. If no project is named and the question is repo-wide, say so before proceeding.
|
|
31
31
|
|
|
32
32
|
## What you cover
|
|
33
33
|
|
|
34
|
-
- Scheduling (
|
|
34
|
+
- Scheduling (native desktop scheduler via `roster schedule install`, the `roster schedule install --tool codex --via cron` crontab path, GitHub Actions scheduled workflows)
|
|
35
35
|
- Secrets management (`.env`, env-var conventions, rotation, SOPS or similar when justified)
|
|
36
36
|
- Deployment patterns (script-based, GitHub Actions, manual checklists)
|
|
37
37
|
- Observability (`logs/cron/`, structured logging, alerting thresholds, "did it run" verification)
|
|
@@ -62,7 +62,7 @@ When a task spans skills (e.g., "design the cron + monitoring + alert chain for
|
|
|
62
62
|
## Behavior rules
|
|
63
63
|
|
|
64
64
|
- **Idempotency first.** Every operation must be safe to re-run. If it isn't, name the guard.
|
|
65
|
-
- **Observability is non-negotiable.** If you can't tell whether it ran, it didn't.
|
|
65
|
+
- **Observability is non-negotiable.** If you can't tell whether it ran, it didn't. For `--via cron` installs (Codex-only — `roster schedule install --tool codex --via cron`), stdout/stderr lands in `logs/cron/<job>.log` per `conventions.md` § Schedules. For UI-handoff installs (Claude Scheduled Tasks, Codex Automations), the scheduler owns the log surface — check the host app's run history.
|
|
66
66
|
- **Cost-aware.** Name the cost of every recommendation — dollars, time, on-call burden.
|
|
67
67
|
- **Name failure modes.** Don't ship a recommendation without stating what happens when it fails.
|
|
68
68
|
- **Stay in your lane.** Reusable substrate and patterns. One-off incident response and per-run remediations are agent work, not expert work.
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# audit-agent.sh — checks agent structure completeness, reports issues with suggested fixes
|
|
3
|
+
# Usage: bash scripts/audit-agent.sh <function> <agent>
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
if [ $# -ne 2 ]; then
|
|
8
|
+
echo "Usage: $0 <function> <agent>"
|
|
9
|
+
exit 1
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
FN="$1"
|
|
13
|
+
AGENT="$2"
|
|
14
|
+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
15
|
+
AGENT_DIR="$ROOT/$FN/$AGENT"
|
|
16
|
+
|
|
17
|
+
source "$ROOT/scripts/lib/functions.sh"
|
|
18
|
+
|
|
19
|
+
if ! is_valid_function "$FN"; then
|
|
20
|
+
echo "ERROR: '$FN' is not a registered function." >&2
|
|
21
|
+
echo "Registered functions:" >&2
|
|
22
|
+
read_functions | sed 's/^/ - /' >&2
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
if [ ! -d "$AGENT_DIR" ]; then
|
|
27
|
+
echo "ERROR: Agent '$FN/$AGENT' not found at $AGENT_DIR"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
32
|
+
RUN_TIME=$(date +%Y-%m-%d-%H%M)
|
|
33
|
+
LOG_DIR="$ROOT/chief-of-staff/logs/$(date +%Y-%m)"
|
|
34
|
+
mkdir -p "$LOG_DIR"
|
|
35
|
+
REPORT="$LOG_DIR/audit-$FN-$AGENT-$RUN_TIME.md"
|
|
36
|
+
|
|
37
|
+
FAILURES=()
|
|
38
|
+
WARNINGS=()
|
|
39
|
+
PASSED=()
|
|
40
|
+
|
|
41
|
+
# === agent.md required sections ===
|
|
42
|
+
if [ ! -f "$AGENT_DIR/agent.md" ]; then
|
|
43
|
+
FAILURES+=("[$FN/$AGENT/agent.md] missing")
|
|
44
|
+
else
|
|
45
|
+
REQUIRED_SECTIONS=("## Purpose" "## Inputs" "## Plans" "## Subagents" "## Outputs" "## Approval" "## Lessons protocol")
|
|
46
|
+
MISSING=()
|
|
47
|
+
for section in "${REQUIRED_SECTIONS[@]}"; do
|
|
48
|
+
if ! grep -qF "$section" "$AGENT_DIR/agent.md"; then
|
|
49
|
+
MISSING+=("$section")
|
|
50
|
+
fi
|
|
51
|
+
done
|
|
52
|
+
if [ ${#MISSING[@]} -gt 0 ]; then
|
|
53
|
+
FAILURES+=("[$FN/$AGENT/agent.md] missing required sections: ${MISSING[*]}")
|
|
54
|
+
FAILURES+=(" → Suggested fix: add the missing sections per conventions.md § 'Agent contract'")
|
|
55
|
+
else
|
|
56
|
+
PASSED+=("[$FN/$AGENT/agent.md] all required sections present")
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# ## Tools and bindings is required ONLY for agents that use external tools.
|
|
60
|
+
# Missing → warning, not failure (agents reading only workspace guidelines
|
|
61
|
+
# don't need it). The chief-of-staff create-agent guided flow adds the
|
|
62
|
+
# section when the user names tools.
|
|
63
|
+
if ! grep -qF "## Tools and bindings" "$AGENT_DIR/agent.md"; then
|
|
64
|
+
WARNINGS+=("[$FN/$AGENT/agent.md] '## Tools and bindings' not declared — fine for tool-less agents; required if this agent calls external APIs")
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Steps section should NOT be present anymore — workflows live in plans/
|
|
68
|
+
if grep -qE '^## Steps' "$AGENT_DIR/agent.md"; then
|
|
69
|
+
WARNINGS+=("[$FN/$AGENT/agent.md] still has a '## Steps' section — workflow logic should live in plans/<plan>.yaml, not agent.md")
|
|
70
|
+
WARNINGS+=(" → Suggested fix: extract workflow steps into a plan file under $FN/$AGENT/plans/ and remove the section from agent.md")
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# === plans/ directory ===
|
|
75
|
+
PLANS_DIR="$AGENT_DIR/plans"
|
|
76
|
+
if [ ! -d "$PLANS_DIR" ]; then
|
|
77
|
+
WARNINGS+=("[$FN/$AGENT/plans/] missing — agent has no plans declared")
|
|
78
|
+
WARNINGS+=(" → Suggested fix: mkdir $PLANS_DIR && add at least one .yaml plan file")
|
|
79
|
+
else
|
|
80
|
+
PLAN_COUNT=$(find "$PLANS_DIR" -maxdepth 1 -name '*.yaml' -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
81
|
+
if [ "$PLAN_COUNT" -eq 0 ]; then
|
|
82
|
+
WARNINGS+=("[$FN/$AGENT/plans/] empty — agent has no plans declared")
|
|
83
|
+
WARNINGS+=(" → Suggested fix: add at least one .yaml plan to $PLANS_DIR")
|
|
84
|
+
else
|
|
85
|
+
PASSED+=("[$FN/$AGENT/plans/] $PLAN_COUNT plan(s)")
|
|
86
|
+
for plan in "$PLANS_DIR"/*.yaml; do
|
|
87
|
+
[ -f "$plan" ] || continue
|
|
88
|
+
REL="${plan#$ROOT/}"
|
|
89
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
90
|
+
if ! python3 -c "import yaml; yaml.safe_load(open('$plan'))" 2>/dev/null; then
|
|
91
|
+
FAILURES+=("[$REL] YAML parse error")
|
|
92
|
+
else
|
|
93
|
+
PASSED+=("[$REL] valid YAML")
|
|
94
|
+
fi
|
|
95
|
+
fi
|
|
96
|
+
done
|
|
97
|
+
fi
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# === Slash command file ===
|
|
101
|
+
SLASH_CMD="$ROOT/.claude/commands/$AGENT.md"
|
|
102
|
+
if [ ! -f "$SLASH_CMD" ]; then
|
|
103
|
+
WARNINGS+=("[.claude/commands/$AGENT.md] missing — slash command not registered")
|
|
104
|
+
WARNINGS+=(" → Suggested fix: scaffold via 'bash scripts/new-agent.sh' template, or copy from another agent's slash command file")
|
|
105
|
+
else
|
|
106
|
+
PASSED+=("[.claude/commands/$AGENT.md] present")
|
|
107
|
+
fi
|
|
108
|
+
|
|
109
|
+
# README
|
|
110
|
+
if [ ! -f "$AGENT_DIR/README.md" ]; then
|
|
111
|
+
FAILURES+=("[$FN/$AGENT/README.md] missing")
|
|
112
|
+
else
|
|
113
|
+
PASSED+=("[$FN/$AGENT/README.md] present")
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# config.yaml — agent config (guideline refs + tool bindings).
|
|
117
|
+
# Schema check: single-document mapping with agent=$FN/$AGENT, plans_dir,
|
|
118
|
+
# guideline_refs (mapping), tools (mapping). Drift here breaks the runtime
|
|
119
|
+
# loader (Phase 2) and `chief-of-staff create-agent` reuse.
|
|
120
|
+
if [ ! -f "$AGENT_DIR/config.yaml" ]; then
|
|
121
|
+
FAILURES+=("[$FN/$AGENT/config.yaml] missing")
|
|
122
|
+
FAILURES+=(" → Suggested fix: add config.yaml at agent root with at least 'agent: $FN/$AGENT' and 'plans_dir: ./plans/'")
|
|
123
|
+
elif command -v python3 >/dev/null 2>&1; then
|
|
124
|
+
CFG_RC=0
|
|
125
|
+
CFG_MSG="$(AGENT_EXPECT="$FN/$AGENT" CFG_PATH="$AGENT_DIR/config.yaml" python3 - <<'PYEOF' 2>&1
|
|
126
|
+
import os, sys
|
|
127
|
+
try:
|
|
128
|
+
import yaml
|
|
129
|
+
except ImportError:
|
|
130
|
+
sys.stderr.write("pyyaml-missing")
|
|
131
|
+
sys.exit(2)
|
|
132
|
+
expect = os.environ["AGENT_EXPECT"]
|
|
133
|
+
path = os.environ["CFG_PATH"]
|
|
134
|
+
with open(path) as f:
|
|
135
|
+
try:
|
|
136
|
+
doc = yaml.safe_load(f)
|
|
137
|
+
except yaml.YAMLError as e:
|
|
138
|
+
sys.stderr.write(f"yaml-parse-error: {e}")
|
|
139
|
+
sys.exit(1)
|
|
140
|
+
if not isinstance(doc, dict):
|
|
141
|
+
sys.stderr.write("not-a-mapping")
|
|
142
|
+
sys.exit(1)
|
|
143
|
+
errs = []
|
|
144
|
+
agent = doc.get("agent")
|
|
145
|
+
if agent != expect:
|
|
146
|
+
errs.append(f"agent field is {agent!r}, expected {expect!r}")
|
|
147
|
+
if "plans_dir" not in doc:
|
|
148
|
+
errs.append("missing plans_dir")
|
|
149
|
+
gr = doc.get("guideline_refs")
|
|
150
|
+
if gr is not None and not isinstance(gr, dict):
|
|
151
|
+
errs.append("guideline_refs is not a mapping")
|
|
152
|
+
tools = doc.get("tools")
|
|
153
|
+
if tools is not None and not isinstance(tools, dict):
|
|
154
|
+
errs.append("tools is not a mapping")
|
|
155
|
+
if errs:
|
|
156
|
+
sys.stderr.write("; ".join(errs))
|
|
157
|
+
sys.exit(1)
|
|
158
|
+
PYEOF
|
|
159
|
+
)" || CFG_RC=$?
|
|
160
|
+
if [ $CFG_RC -eq 0 ]; then
|
|
161
|
+
PASSED+=("[$FN/$AGENT/config.yaml] valid (schema)")
|
|
162
|
+
elif [ $CFG_RC -eq 2 ]; then
|
|
163
|
+
PASSED+=("[$FN/$AGENT/config.yaml] present (schema not validated, pyyaml missing)")
|
|
164
|
+
else
|
|
165
|
+
FAILURES+=("[$FN/$AGENT/config.yaml] $CFG_MSG")
|
|
166
|
+
FAILURES+=(" → Suggested fix: open the file and ensure it is a single YAML mapping with 'agent: $FN/$AGENT', 'plans_dir', a 'guideline_refs:' mapping, and a 'tools:' mapping (may be empty)")
|
|
167
|
+
fi
|
|
168
|
+
else
|
|
169
|
+
PASSED+=("[$FN/$AGENT/config.yaml] present (schema not validated, python3 missing)")
|
|
170
|
+
fi
|
|
171
|
+
|
|
172
|
+
# .mcp.json valid JSON
|
|
173
|
+
if [ ! -f "$AGENT_DIR/.mcp.json" ]; then
|
|
174
|
+
WARNINGS+=("[$FN/$AGENT/.mcp.json] missing (no agent-scoped MCPs configured)")
|
|
175
|
+
else
|
|
176
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
177
|
+
if ! python3 -c "import json; json.load(open('$AGENT_DIR/.mcp.json'))" 2>/dev/null; then
|
|
178
|
+
FAILURES+=("[$FN/$AGENT/.mcp.json] invalid JSON")
|
|
179
|
+
FAILURES+=(" → Suggested fix: validate with: python3 -c 'import json; json.load(open(\"$AGENT_DIR/.mcp.json\"))'")
|
|
180
|
+
else
|
|
181
|
+
PASSED+=("[$FN/$AGENT/.mcp.json] valid JSON")
|
|
182
|
+
fi
|
|
183
|
+
else
|
|
184
|
+
PASSED+=("[$FN/$AGENT/.mcp.json] present (JSON not validated, python3 missing)")
|
|
185
|
+
fi
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
# .claude/settings.json
|
|
189
|
+
if [ ! -f "$AGENT_DIR/.claude/settings.json" ]; then
|
|
190
|
+
WARNINGS+=("[$FN/$AGENT/.claude/settings.json] missing")
|
|
191
|
+
else
|
|
192
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
193
|
+
if ! python3 -c "import json; json.load(open('$AGENT_DIR/.claude/settings.json'))" 2>/dev/null; then
|
|
194
|
+
FAILURES+=("[$FN/$AGENT/.claude/settings.json] invalid JSON")
|
|
195
|
+
else
|
|
196
|
+
PASSED+=("[$FN/$AGENT/.claude/settings.json] valid JSON")
|
|
197
|
+
fi
|
|
198
|
+
else
|
|
199
|
+
PASSED+=("[$FN/$AGENT/.claude/settings.json] present")
|
|
200
|
+
fi
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
# subagents/ exists
|
|
204
|
+
if [ ! -d "$AGENT_DIR/subagents" ]; then
|
|
205
|
+
WARNINGS+=("[$FN/$AGENT/subagents/] missing (may be intentional for very simple agents)")
|
|
206
|
+
else
|
|
207
|
+
# Check each subagent has required sections
|
|
208
|
+
for sub in "$AGENT_DIR/subagents"/*.md; do
|
|
209
|
+
[ -f "$sub" ] || continue
|
|
210
|
+
BASENAME=$(basename "$sub")
|
|
211
|
+
[ "$BASENAME" = "_template.md" ] && continue
|
|
212
|
+
REL="${sub#$ROOT/}"
|
|
213
|
+
SUB_REQUIRED=("## Role" "## Inputs" "## Output" "## Tools" "## Boundaries" "## Quality bar")
|
|
214
|
+
SUB_MISSING=()
|
|
215
|
+
for section in "${SUB_REQUIRED[@]}"; do
|
|
216
|
+
if ! grep -qF "$section" "$sub"; then
|
|
217
|
+
SUB_MISSING+=("$section")
|
|
218
|
+
fi
|
|
219
|
+
done
|
|
220
|
+
if [ ${#SUB_MISSING[@]} -gt 0 ]; then
|
|
221
|
+
WARNINGS+=("[$REL] missing sections: ${SUB_MISSING[*]}")
|
|
222
|
+
else
|
|
223
|
+
PASSED+=("[$REL] all subagent sections present")
|
|
224
|
+
fi
|
|
225
|
+
done
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
# playbook/ exists
|
|
229
|
+
if [ ! -d "$AGENT_DIR/playbook" ]; then
|
|
230
|
+
WARNINGS+=("[$FN/$AGENT/playbook/] missing")
|
|
231
|
+
else
|
|
232
|
+
PASSED+=("[$FN/$AGENT/playbook/] present")
|
|
233
|
+
fi
|
|
234
|
+
|
|
235
|
+
# Flat-shape directories
|
|
236
|
+
for d in logs/runs logs/feedback pending; do
|
|
237
|
+
if [ ! -d "$AGENT_DIR/$d" ]; then
|
|
238
|
+
WARNINGS+=("[$FN/$AGENT/$d/] missing")
|
|
239
|
+
fi
|
|
240
|
+
done
|
|
241
|
+
|
|
242
|
+
# asset-references.md at agent root
|
|
243
|
+
if [ ! -f "$AGENT_DIR/asset-references.md" ]; then
|
|
244
|
+
WARNINGS+=("[$FN/$AGENT/asset-references.md] missing")
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
# === Status ===
|
|
248
|
+
if [ ${#FAILURES[@]} -gt 0 ]; then
|
|
249
|
+
STATUS="fail"
|
|
250
|
+
elif [ ${#WARNINGS[@]} -gt 0 ]; then
|
|
251
|
+
STATUS="warn"
|
|
252
|
+
else
|
|
253
|
+
STATUS="pass"
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
count_items() {
|
|
257
|
+
local arr=("$@")
|
|
258
|
+
local n=0
|
|
259
|
+
for item in "${arr[@]}"; do
|
|
260
|
+
if ! [[ "$item" =~ ^[[:space:]]*→ ]]; then
|
|
261
|
+
n=$((n+1))
|
|
262
|
+
fi
|
|
263
|
+
done
|
|
264
|
+
echo $n
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
N_FAIL=0
|
|
268
|
+
for item in "${FAILURES[@]:-}"; do
|
|
269
|
+
[ -z "$item" ] && continue
|
|
270
|
+
[[ "$item" =~ ^[[:space:]]+→ ]] && continue
|
|
271
|
+
N_FAIL=$((N_FAIL + 1))
|
|
272
|
+
done
|
|
273
|
+
N_WARN=0
|
|
274
|
+
for item in "${WARNINGS[@]:-}"; do
|
|
275
|
+
[ -z "$item" ] && continue
|
|
276
|
+
[[ "$item" =~ ^[[:space:]]+→ ]] && continue
|
|
277
|
+
N_WARN=$((N_WARN + 1))
|
|
278
|
+
done
|
|
279
|
+
N_PASS=${#PASSED[@]}
|
|
280
|
+
|
|
281
|
+
# Write report
|
|
282
|
+
{
|
|
283
|
+
echo "---"
|
|
284
|
+
echo "operation: audit-agent"
|
|
285
|
+
echo "function: $FN"
|
|
286
|
+
echo "agent: $AGENT"
|
|
287
|
+
echo "ran: $TIMESTAMP"
|
|
288
|
+
echo "status: $STATUS"
|
|
289
|
+
echo "---"
|
|
290
|
+
echo ""
|
|
291
|
+
echo "# Audit: $FN/$AGENT"
|
|
292
|
+
echo ""
|
|
293
|
+
echo "## Summary"
|
|
294
|
+
echo "- $N_PASS passed"
|
|
295
|
+
echo "- $N_WARN warnings"
|
|
296
|
+
echo "- $N_FAIL failures"
|
|
297
|
+
echo ""
|
|
298
|
+
if [ $N_FAIL -gt 0 ]; then
|
|
299
|
+
echo "## Failures"
|
|
300
|
+
for line in "${FAILURES[@]}"; do
|
|
301
|
+
echo "- $line"
|
|
302
|
+
done
|
|
303
|
+
echo ""
|
|
304
|
+
fi
|
|
305
|
+
if [ $N_WARN -gt 0 ]; then
|
|
306
|
+
echo "## Warnings"
|
|
307
|
+
for line in "${WARNINGS[@]}"; do
|
|
308
|
+
echo "- $line"
|
|
309
|
+
done
|
|
310
|
+
echo ""
|
|
311
|
+
fi
|
|
312
|
+
if [ $N_PASS -gt 0 ]; then
|
|
313
|
+
echo "## Passed"
|
|
314
|
+
for line in "${PASSED[@]}"; do
|
|
315
|
+
echo "- $line"
|
|
316
|
+
done
|
|
317
|
+
fi
|
|
318
|
+
} > "$REPORT"
|
|
319
|
+
|
|
320
|
+
echo "Audit: $FN/$AGENT — $STATUS"
|
|
321
|
+
echo " Passed: $N_PASS, Warnings: $N_WARN, Failures: $N_FAIL"
|
|
322
|
+
[ $N_FAIL -gt 0 ] && {
|
|
323
|
+
echo "Failures:"
|
|
324
|
+
for line in "${FAILURES[@]}"; do echo " $line"; done
|
|
325
|
+
}
|
|
326
|
+
echo "Full report: $REPORT"
|