@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.
Files changed (75) hide show
  1. package/README.md +77 -215
  2. package/agents/lesson-drafter.md +3 -8
  3. package/agents/pattern-detector.md +0 -1
  4. package/bin/roster.js +7233 -1197
  5. package/data/plan-ceilings.yaml +57 -0
  6. package/package.json +8 -3
  7. package/skills/chief-of-staff/SKILL.md +199 -59
  8. package/skills/dreamer/SKILL.md +8 -7
  9. package/skills/roster-orchestrator/SKILL.md +53 -25
  10. package/templates/CLAUDE.project.template.md +1 -1
  11. package/templates/CONTEXT.template.md +2 -2
  12. package/templates/gitignore-defaults.txt +2 -0
  13. package/templates/hooks/banner.sh +47 -0
  14. package/templates/scaffold/chief-of-staff/README.md +16 -24
  15. package/templates/scaffold/chief-of-staff/agent.md +22 -32
  16. package/templates/scaffold/chief-of-staff/plans/audit-agent.yaml +4 -4
  17. package/templates/scaffold/chief-of-staff/plans/audit-repo.yaml +5 -4
  18. package/templates/scaffold/chief-of-staff/plans/create-agent.yaml +5 -34
  19. package/templates/scaffold/config/project.yaml.template +10 -0
  20. package/templates/scaffold/conventions.md +188 -173
  21. package/templates/scaffold/dreamer/README.md +2 -2
  22. package/templates/scaffold/dreamer/agent.md +0 -1
  23. package/templates/scaffold/dreamer/plans/nightly-reflection.yaml +23 -37
  24. package/templates/scaffold/dreamer/subagents/lesson-drafter.md +2 -7
  25. package/templates/scaffold/{projects/_demo/guidelines → guidelines}/asset-links.md +4 -0
  26. package/templates/scaffold/{projects/_demo/guidelines → guidelines}/brand-book.md +4 -0
  27. package/templates/scaffold/{projects/_demo/guidelines → guidelines}/messaging.md +4 -0
  28. package/templates/scaffold/{projects/_demo/guidelines → guidelines}/voice.md +4 -0
  29. package/templates/scaffold/logs/cron/.gitkeep +1 -0
  30. package/templates/scaffold/ops/EXPERT.md +5 -5
  31. package/templates/scaffold/scripts/audit-agent.sh +326 -0
  32. package/templates/scaffold/scripts/audit-repo.sh +218 -0
  33. package/templates/scaffold/scripts/create-function.sh +267 -0
  34. package/templates/scaffold/scripts/lib/README.md +6 -1
  35. package/templates/scaffold/scripts/lib/bindings-prompt.sh +53 -0
  36. package/templates/scaffold/scripts/lib/functions.sh +17 -5
  37. package/templates/scaffold/scripts/new-agent.sh +416 -0
  38. package/templates/scaffold/scripts/rename-agent.sh +91 -0
  39. package/templates/scaffold/scripts/save-state.sh +32 -0
  40. package/agents/critic.md +0 -74
  41. package/agents/enricher.md +0 -56
  42. package/agents/promotion-arbiter.md +0 -71
  43. package/agents/prospector.md +0 -51
  44. package/agents/writer.md +0 -58
  45. package/skills/sdr/SKILL.md +0 -147
  46. package/templates/scaffold/chief-of-staff/plans/add-agent-to-project.yaml +0 -45
  47. package/templates/scaffold/chief-of-staff/plans/archive-project.yaml +0 -51
  48. package/templates/scaffold/chief-of-staff/plans/audit-project.yaml +0 -34
  49. package/templates/scaffold/chief-of-staff/plans/create-project.yaml +0 -65
  50. package/templates/scaffold/chief-of-staff/plans/remove-agent-from-project.yaml +0 -50
  51. package/templates/scaffold/chief-of-staff/plans/rename-project.yaml +0 -62
  52. package/templates/scaffold/chief-of-staff/plans/unarchive-project.yaml +0 -41
  53. package/templates/scaffold/dreamer/subagents/promotion-arbiter.md +0 -64
  54. package/templates/scaffold/gtm/sdr/.claude/settings.json +0 -3
  55. package/templates/scaffold/gtm/sdr/.mcp.json +0 -21
  56. package/templates/scaffold/gtm/sdr/README.md +0 -46
  57. package/templates/scaffold/gtm/sdr/agent.md +0 -136
  58. package/templates/scaffold/gtm/sdr/plans/cold-outreach.yaml +0 -92
  59. package/templates/scaffold/gtm/sdr/projects/_demo/asset-references.md +0 -7
  60. package/templates/scaffold/gtm/sdr/projects/_demo/config/default.yaml +0 -69
  61. package/templates/scaffold/gtm/sdr/projects/_demo/log/feedback/.gitkeep +0 -0
  62. package/templates/scaffold/gtm/sdr/projects/_demo/log/runs/.gitkeep +0 -0
  63. package/templates/scaffold/gtm/sdr/projects/_demo/playbook/.gitkeep +0 -0
  64. package/templates/scaffold/gtm/sdr/subagents/critic.md +0 -67
  65. package/templates/scaffold/gtm/sdr/subagents/enricher.md +0 -49
  66. package/templates/scaffold/gtm/sdr/subagents/prospector.md +0 -44
  67. package/templates/scaffold/gtm/sdr/subagents/writer.md +0 -51
  68. package/templates/scaffold/projects/_demo/CLAUDE.md +0 -35
  69. package/templates/scaffold/projects/_demo/README.md +0 -16
  70. package/templates/scaffold/projects/_demo/assets/.gitkeep +0 -0
  71. package/templates/scaffold/projects/_demo/config/default.yaml +0 -28
  72. package/templates/scaffold/projects/_demo/state.md +0 -11
  73. package/templates/scaffold/scripts/new-project.sh +0 -125
  74. /package/templates/scaffold/gtm/{sdr/playbook/.gitkeep → .gitkeep} +0 -0
  75. /package/templates/scaffold/{projects/_demo/guidelines → guidelines}/icps/_persona-template.md +0 -0
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env bash
2
+ # audit-repo.sh — full workspace audit; runs agent audits and repo-level checks
3
+ # Usage: bash scripts/audit-repo.sh
4
+
5
+ set -euo pipefail
6
+
7
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
8
+
9
+ source "$ROOT/scripts/lib/functions.sh"
10
+
11
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
12
+ RUN_TIME=$(date +%Y-%m-%d-%H%M)
13
+ LOG_DIR="$ROOT/chief-of-staff/logs/$(date +%Y-%m)"
14
+ mkdir -p "$LOG_DIR"
15
+ REPORT="$LOG_DIR/audit-repo-$RUN_TIME.md"
16
+
17
+ REPO_FAILURES=()
18
+ REPO_WARNINGS=()
19
+ REPO_PASSED=()
20
+
21
+ # === Repo-level checks ===
22
+
23
+ # Universal .mcp.json
24
+ if [ ! -f "$ROOT/.mcp.json" ]; then
25
+ REPO_WARNINGS+=("[.mcp.json] universal MCP config missing")
26
+ elif command -v python3 >/dev/null 2>&1; then
27
+ if ! python3 -c "import json; json.load(open('$ROOT/.mcp.json'))" 2>/dev/null; then
28
+ REPO_FAILURES+=("[.mcp.json] invalid JSON")
29
+ else
30
+ REPO_PASSED+=("[.mcp.json] valid")
31
+ fi
32
+ fi
33
+
34
+ # Universal .claude/settings.json
35
+ if [ ! -f "$ROOT/.claude/settings.json" ]; then
36
+ REPO_WARNINGS+=("[.claude/settings.json] universal settings missing")
37
+ elif command -v python3 >/dev/null 2>&1; then
38
+ if ! python3 -c "import json; json.load(open('$ROOT/.claude/settings.json'))" 2>/dev/null; then
39
+ REPO_FAILURES+=("[.claude/settings.json] invalid JSON")
40
+ else
41
+ REPO_PASSED+=("[.claude/settings.json] valid")
42
+ fi
43
+ fi
44
+
45
+ # Required root files
46
+ for f in CLAUDE.md conventions.md README.md; do
47
+ if [ ! -f "$ROOT/$f" ]; then
48
+ REPO_FAILURES+=("[$f] missing at repo root")
49
+ else
50
+ REPO_PASSED+=("[$f] present")
51
+ fi
52
+ done
53
+
54
+ # config/project.yaml — workspace identity (v1 shape)
55
+ if [ ! -f "$ROOT/config/project.yaml" ]; then
56
+ REPO_FAILURES+=("[config/project.yaml] missing — workspace identity required")
57
+ elif command -v python3 >/dev/null 2>&1; then
58
+ if ! python3 -c "import yaml; yaml.safe_load(open('$ROOT/config/project.yaml'))" 2>/dev/null; then
59
+ REPO_FAILURES+=("[config/project.yaml] YAML parse error")
60
+ else
61
+ REPO_PASSED+=("[config/project.yaml] valid")
62
+ fi
63
+ fi
64
+
65
+ # guidelines/ — cross-agent substrate
66
+ if [ ! -d "$ROOT/guidelines" ]; then
67
+ REPO_FAILURES+=("[guidelines/] missing — workspace substrate dir required")
68
+ else
69
+ REPO_PASSED+=("[guidelines/] present")
70
+ for f in voice.md messaging.md brand-book.md asset-links.md; do
71
+ if [ ! -f "$ROOT/guidelines/$f" ]; then
72
+ REPO_WARNINGS+=("[guidelines/$f] missing — required substrate file")
73
+ fi
74
+ done
75
+ if [ ! -d "$ROOT/guidelines/icps" ] || [ -z "$(find "$ROOT/guidelines/icps" -maxdepth 1 -name '*.md' -type f 2>/dev/null | head -1)" ]; then
76
+ REPO_WARNINGS+=("[guidelines/icps/] empty or missing — at least one persona file required")
77
+ fi
78
+ fi
79
+
80
+ # Build a regex of registered functions for matching grandparent dirs.
81
+ REGISTERED_FNS_PIPE=$(read_functions 2>/dev/null | tr '\n' '|' | sed 's/|$//')
82
+
83
+ # Registered function should have a folder
84
+ while IFS= read -r fn; do
85
+ [ -z "$fn" ] && continue
86
+ if [ ! -d "$ROOT/$fn" ]; then
87
+ REPO_FAILURES+=("[$fn] registered in .config/functions.yaml but folder does not exist")
88
+ REPO_FAILURES+=(" → Suggested fix: mkdir $fn && cp <stub README>, OR remove from .config/functions.yaml")
89
+ fi
90
+ done < <(read_functions 2>/dev/null || true)
91
+
92
+ # has_expert: true → EXPERT.md must exist
93
+ while IFS=$'\t' read -r slug has_expert; do
94
+ [ -z "$slug" ] && continue
95
+ if [ "$has_expert" = "true" ] && [ ! -f "$ROOT/$slug/EXPERT.md" ]; then
96
+ REPO_WARNINGS+=("[$slug/EXPERT.md] registry says has_expert=true but file missing")
97
+ fi
98
+ done < <(read_functions_with_metadata 2>/dev/null || true)
99
+
100
+ # HITL channel env vars: every function should have SLACK_HITL_CHANNEL_<FN> in .env.example
101
+ ENV_EXAMPLE="$ROOT/.env.example"
102
+ if [ -f "$ENV_EXAMPLE" ]; then
103
+ while IFS= read -r fn; do
104
+ [ -z "$fn" ] && continue
105
+ var="SLACK_HITL_CHANNEL_$(echo "$fn" | tr '[:lower:]-' '[:upper:]_')"
106
+ if ! grep -q "^${var}=" "$ENV_EXAMPLE"; then
107
+ REPO_WARNINGS+=("[.env.example] missing $var (function '$fn' has no HITL channel env var)")
108
+ fi
109
+ done < <(read_functions 2>/dev/null || true)
110
+ if ! grep -q "^SLACK_HITL_CHANNEL_ADMIN=" "$ENV_EXAMPLE"; then
111
+ REPO_WARNINGS+=("[.env.example] missing SLACK_HITL_CHANNEL_ADMIN (used by dreamer + chief-of-staff)")
112
+ fi
113
+ fi
114
+
115
+ # Function-shaped top-level dirs not in the registry
116
+ KNOWN_NON_FUNCTIONS="dreamer chief-of-staff scripts logs _archive"
117
+ for dir in "$ROOT"/*/; do
118
+ basename=$(basename "$dir")
119
+ [[ "$basename" == .* ]] && continue
120
+ echo "$KNOWN_NON_FUNCTIONS" | grep -qw "$basename" && continue
121
+ if [ -n "$REGISTERED_FNS_PIPE" ] && echo "$basename" | grep -qE "^($REGISTERED_FNS_PIPE)\$"; then
122
+ continue
123
+ fi
124
+ if find "$dir" -maxdepth 2 -name 'agent.md' -type f 2>/dev/null | head -1 | grep -q .; then
125
+ REPO_WARNINGS+=("[$basename/] looks function-shaped but not registered in .config/functions.yaml")
126
+ REPO_WARNINGS+=(" → Suggested fix: add to registry via 'bash scripts/create-function.sh $basename' (or remove if intended)")
127
+ fi
128
+ done
129
+
130
+ # === Run audit-agent for each agent ===
131
+ AGENTS=()
132
+ while IFS= read -r fn; do
133
+ [ -z "$fn" ] && continue
134
+ if [ -d "$ROOT/$fn" ]; then
135
+ while IFS= read -r path; do
136
+ AGENTS+=("$fn:$(basename "$path")")
137
+ done < <(find "$ROOT/$fn" -maxdepth 1 -mindepth 1 -type d 2>/dev/null || true)
138
+ fi
139
+ done < <(read_functions 2>/dev/null || true)
140
+
141
+ AGENT_RESULTS=()
142
+ for entry in "${AGENTS[@]:-}"; do
143
+ FN="${entry%%:*}"
144
+ AGENT="${entry##*:}"
145
+ RESULT=$(bash "$ROOT/scripts/audit-agent.sh" "$FN" "$AGENT" 2>&1 | head -5 || echo " (audit failed)")
146
+ AGENT_RESULTS+=("### $FN/$AGENT")
147
+ AGENT_RESULTS+=("\`\`\`")
148
+ while IFS= read -r line; do
149
+ AGENT_RESULTS+=("$line")
150
+ done <<< "$RESULT"
151
+ AGENT_RESULTS+=("\`\`\`")
152
+ AGENT_RESULTS+=("")
153
+ done
154
+
155
+ # === Status ===
156
+ if [ ${#REPO_FAILURES[@]} -gt 0 ]; then
157
+ STATUS="fail"
158
+ elif [ ${#REPO_WARNINGS[@]} -gt 0 ]; then
159
+ STATUS="warn"
160
+ else
161
+ STATUS="pass"
162
+ fi
163
+
164
+ # Write report
165
+ {
166
+ echo "---"
167
+ echo "operation: audit-repo"
168
+ echo "ran: $TIMESTAMP"
169
+ echo "status: $STATUS"
170
+ echo "---"
171
+ echo ""
172
+ echo "# Repo Audit"
173
+ echo ""
174
+ echo "## Summary"
175
+ echo "- Agents audited: ${#AGENTS[@]}"
176
+ echo "- Repo-level passed: ${#REPO_PASSED[@]}"
177
+ echo "- Repo-level warnings: ${#REPO_WARNINGS[@]}"
178
+ echo "- Repo-level failures: ${#REPO_FAILURES[@]}"
179
+ echo ""
180
+ if [ ${#REPO_FAILURES[@]} -gt 0 ]; then
181
+ echo "## Repo-level Failures"
182
+ for line in "${REPO_FAILURES[@]}"; do
183
+ echo "- $line"
184
+ done
185
+ echo ""
186
+ fi
187
+ if [ ${#REPO_WARNINGS[@]} -gt 0 ]; then
188
+ echo "## Repo-level Warnings"
189
+ for line in "${REPO_WARNINGS[@]}"; do
190
+ echo "- $line"
191
+ done
192
+ echo ""
193
+ fi
194
+ if [ ${#REPO_PASSED[@]} -gt 0 ]; then
195
+ echo "## Repo-level Passed"
196
+ for line in "${REPO_PASSED[@]}"; do
197
+ echo "- $line"
198
+ done
199
+ echo ""
200
+ fi
201
+ echo "## Agent audits (summaries)"
202
+ echo ""
203
+ for line in "${AGENT_RESULTS[@]:-}"; do
204
+ echo "$line"
205
+ done
206
+ echo ""
207
+ echo "Individual audit reports are in $LOG_DIR/"
208
+ } > "$REPORT"
209
+
210
+ # Print summary
211
+ echo "Repo audit: $STATUS"
212
+ echo " Agents: ${#AGENTS[@]}"
213
+ echo " Repo-level: ${#REPO_PASSED[@]} passed, ${#REPO_WARNINGS[@]} warnings, ${#REPO_FAILURES[@]} failures"
214
+ [ ${#REPO_FAILURES[@]} -gt 0 ] && {
215
+ echo "Repo failures:"
216
+ for line in "${REPO_FAILURES[@]}"; do echo " $line"; done
217
+ }
218
+ echo "Full report: $REPORT"
@@ -0,0 +1,267 @@
1
+ #!/usr/bin/env bash
2
+ # create-function.sh — register a new function category and scaffold its folder
3
+ # Usage: bash scripts/create-function.sh <slug> [--description "..."] [--with-expert] [--no-confirm]
4
+
5
+ set -euo pipefail
6
+
7
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
8
+ CONFIG="$ROOT/.config/functions.yaml"
9
+
10
+ source "$ROOT/scripts/lib/functions.sh"
11
+
12
+ usage() {
13
+ cat <<EOF
14
+ Usage: $0 <slug> [--description "..."] [--with-expert] [--no-confirm]
15
+
16
+ <slug> required, lowercase kebab-case, ^[a-z][a-z0-9-]*\$
17
+ --description one-line description (prompted if omitted in TTY)
18
+ --with-expert scaffold EXPERT.md stub and set has_expert: true
19
+ --no-confirm skip the interactive proliferation prompt
20
+
21
+ Environment:
22
+ AGENT_TEAM_NO_CONFIRM=1 equivalent to --no-confirm
23
+ EOF
24
+ }
25
+
26
+ if [ $# -lt 1 ]; then
27
+ usage >&2
28
+ exit 1
29
+ fi
30
+
31
+ SLUG=""
32
+ DESCRIPTION=""
33
+ WITH_EXPERT="" # "" | "true" | "false"; "" means unresolved (will prompt)
34
+ DESC_PROVIDED=0
35
+ NO_CONFIRM=0
36
+
37
+ while [ $# -gt 0 ]; do
38
+ case "$1" in
39
+ --description)
40
+ [ $# -lt 2 ] && { echo "ERROR: --description requires a value" >&2; exit 1; }
41
+ DESCRIPTION="$2"
42
+ DESC_PROVIDED=1
43
+ shift 2
44
+ ;;
45
+ --description=*)
46
+ DESCRIPTION="${1#--description=}"
47
+ DESC_PROVIDED=1
48
+ shift
49
+ ;;
50
+ --with-expert)
51
+ WITH_EXPERT="true"
52
+ shift
53
+ ;;
54
+ --no-confirm)
55
+ NO_CONFIRM=1
56
+ shift
57
+ ;;
58
+ -h|--help)
59
+ usage
60
+ exit 0
61
+ ;;
62
+ --*)
63
+ echo "ERROR: unknown flag '$1'" >&2
64
+ usage >&2
65
+ exit 1
66
+ ;;
67
+ *)
68
+ if [ -z "$SLUG" ]; then
69
+ SLUG="$1"
70
+ shift
71
+ else
72
+ echo "ERROR: unexpected positional arg '$1'" >&2
73
+ exit 1
74
+ fi
75
+ ;;
76
+ esac
77
+ done
78
+
79
+ if [ "${AGENT_TEAM_NO_CONFIRM:-0}" = "1" ]; then
80
+ NO_CONFIRM=1
81
+ fi
82
+
83
+ if [ -z "$SLUG" ]; then
84
+ echo "ERROR: <slug> is required" >&2
85
+ usage >&2
86
+ exit 1
87
+ fi
88
+
89
+ # 1. Validate slug format
90
+ if ! [[ "$SLUG" =~ ^[a-z][a-z0-9-]*$ ]]; then
91
+ echo "ERROR: slug '$SLUG' is invalid. Must be lowercase, start with a letter, and only contain a-z, 0-9, and hyphens." >&2
92
+ exit 1
93
+ fi
94
+
95
+ # 2. Check config exists and is parseable
96
+ if [ ! -f "$CONFIG" ]; then
97
+ echo "ERROR: $CONFIG not found or unreadable" >&2
98
+ exit 1
99
+ fi
100
+ if ! read_functions >/dev/null 2>&1; then
101
+ echo "ERROR: failed to read $CONFIG (malformed YAML or unreadable)" >&2
102
+ read_functions >/dev/null # re-run to surface stderr
103
+ exit 1
104
+ fi
105
+
106
+ # 3. Check slug not already in registry
107
+ if is_valid_function "$SLUG"; then
108
+ echo "ERROR: function '$SLUG' is already registered in $CONFIG" >&2
109
+ exit 1
110
+ fi
111
+
112
+ # 4. Check folder doesn't exist
113
+ TARGET="$ROOT/$SLUG"
114
+ if [ -e "$TARGET" ]; then
115
+ echo "ERROR: '$TARGET' already exists on disk" >&2
116
+ exit 1
117
+ fi
118
+
119
+ # 5. Resolve description
120
+ is_tty() { [ -t 0 ] && [ -t 1 ]; }
121
+
122
+ if [ $DESC_PROVIDED -eq 0 ]; then
123
+ if is_tty; then
124
+ printf "Description (one line, e.g. 'Research — discovery, market analysis'): " >&2
125
+ IFS= read -r DESCRIPTION
126
+ fi
127
+ if [ -z "$DESCRIPTION" ]; then
128
+ echo "ERROR: --description not provided and not running interactively" >&2
129
+ exit 1
130
+ fi
131
+ fi
132
+
133
+ # Strip trailing whitespace
134
+ DESCRIPTION="${DESCRIPTION%"${DESCRIPTION##*[![:space:]]}"}"
135
+ if [ -z "$DESCRIPTION" ]; then
136
+ echo "ERROR: description is empty" >&2
137
+ exit 1
138
+ fi
139
+
140
+ # 6. Resolve has_expert
141
+ if [ -z "$WITH_EXPERT" ]; then
142
+ if is_tty; then
143
+ printf "Does this function need an expert? (y/N): " >&2
144
+ IFS= read -r ANS
145
+ case "$ANS" in
146
+ y|Y|yes|YES) WITH_EXPERT="true" ;;
147
+ *) WITH_EXPERT="false" ;;
148
+ esac
149
+ else
150
+ WITH_EXPERT="false"
151
+ fi
152
+ fi
153
+
154
+ # 7. Soft proliferation reminder
155
+ if [ "$NO_CONFIRM" -eq 0 ] && is_tty; then
156
+ cat >&2 <<EOF
157
+ About to create function: $SLUG
158
+ Reminder: function categories should map to how you mentally divide work.
159
+ Will you have at least 2-3 agents in this function within ~90 days?
160
+ If not, the agent likely fits an existing function. Press Ctrl-C to abort,
161
+ or any key to proceed.
162
+ EOF
163
+ read -r -n 1 _ || true
164
+ echo "" >&2
165
+ fi
166
+
167
+ # === Mutations begin here ===
168
+ # Track what we created so we can roll back on failure.
169
+ CREATED_PATHS=()
170
+ CONFIG_BACKUP="$(mktemp -t functions-yaml-backup.XXXXXX)" || {
171
+ echo "ERROR: could not create backup tempfile" >&2
172
+ exit 1
173
+ }
174
+ cp "$CONFIG" "$CONFIG_BACKUP"
175
+
176
+ rollback() {
177
+ echo "Rolling back partial changes..." >&2
178
+ for p in "${CREATED_PATHS[@]:-}"; do
179
+ [ -e "$p" ] && rm -rf "$p"
180
+ done
181
+ cp "$CONFIG_BACKUP" "$CONFIG"
182
+ }
183
+ trap 'rollback; rm -f "$CONFIG_BACKUP"' ERR
184
+
185
+ # 8. Append entry to YAML
186
+ {
187
+ printf ' - slug: %s\n' "$SLUG"
188
+ printf ' description: %s\n' "$DESCRIPTION"
189
+ printf ' has_expert: %s\n' "$WITH_EXPERT"
190
+ } >> "$CONFIG"
191
+
192
+ # 9. Create folder
193
+ mkdir -p "$TARGET"
194
+ CREATED_PATHS+=("$TARGET")
195
+
196
+ # 10. Stub README.md
197
+ README_PATH="$TARGET/README.md"
198
+ {
199
+ printf '# Global %s agents\n\n' "$SLUG"
200
+ printf '%s\n\n' "$DESCRIPTION"
201
+ printf 'No agents yet. Add via:\n\n'
202
+ printf '```bash\n'
203
+ printf 'bash scripts/new-agent.sh %s <agent-name>\n' "$SLUG"
204
+ printf '```\n\n'
205
+ printf "When you add the first agent here, see \`gtm/sdr/\` as the canonical example of an agent's directory layout.\n"
206
+ } > "$README_PATH"
207
+
208
+ # 11. Optional EXPERT.md stub
209
+ if [ "$WITH_EXPERT" = "true" ]; then
210
+ EXPERT_PATH="$TARGET/EXPERT.md"
211
+ cat > "$EXPERT_PATH" <<EOF
212
+ # $SLUG Expert
213
+
214
+ <!--
215
+ This is a stub. Fill in the expert system prompt for this function.
216
+
217
+ Experts shape SUBSTRATE (project guidelines), not artifacts. They critique
218
+ and generate guideline files in \`guidelines/\` at the workspace root.
219
+
220
+ Required sections (see other EXPERT.md files for examples once they exist):
221
+ - Identity (1 paragraph)
222
+ - Scope (guide / critique / generate guidelines)
223
+ - Skill routing (table)
224
+ - Practitioner panel (optional, only if it adds value)
225
+ - Read-first protocol
226
+ - Output rules (what writes where, what doesn't)
227
+ - Stage filter (early-stage constraints)
228
+
229
+ Keep it concise. Lean on skills rather than restating their content here.
230
+ -->
231
+
232
+ # Stub: replace with the function's expert system prompt.
233
+ EOF
234
+ fi
235
+
236
+ # 12. Append to operation log
237
+ LOG_DIR="$ROOT/chief-of-staff/logs/$(date +%Y-%m)"
238
+ mkdir -p "$LOG_DIR"
239
+ LOG_FILE="$LOG_DIR/operations-$(date +%Y-%m-%d).md"
240
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
241
+ {
242
+ echo ""
243
+ echo "## $TIMESTAMP — create-function: $SLUG"
244
+ echo "Description: $DESCRIPTION"
245
+ echo "has_expert: $WITH_EXPERT"
246
+ } >> "$LOG_FILE"
247
+
248
+ # Success — disarm rollback trap
249
+ trap - ERR
250
+ rm -f "$CONFIG_BACKUP"
251
+
252
+ echo ""
253
+ echo "✓ Function '$SLUG' created."
254
+ echo " Folder: $TARGET/"
255
+ echo " README: $README_PATH"
256
+ [ "$WITH_EXPERT" = "true" ] && echo " EXPERT.md: $TARGET/EXPERT.md (stub)"
257
+ echo " Registry: $CONFIG (entry appended)"
258
+ echo " Log: $LOG_FILE"
259
+ echo ""
260
+ SLUG_ENV=$(echo "$SLUG" | tr '[:lower:]-' '[:upper:]_')
261
+
262
+ echo "Reminders for the new function:"
263
+ echo " 1. HITL routing — agents in this function will route to #${SLUG}"
264
+ echo " - Create the Slack channel #${SLUG}"
265
+ echo " - Add to .env: SLACK_HITL_CHANNEL_${SLUG_ENV}=#${SLUG}"
266
+ [ "$WITH_EXPERT" = "true" ] && echo " 2. Fill in $TARGET/EXPERT.md with the expert system prompt"
267
+ echo " $([ "$WITH_EXPERT" = "true" ] && echo 3 || echo 2). Add the first agent: bash scripts/new-agent.sh $SLUG <agent-name>"
@@ -1,12 +1,17 @@
1
1
  # Shared script libraries
2
2
 
3
- Helper functions for use across scripts. Empty for now — add as scripts grow.
3
+ Helper functions for use across scripts.
4
4
 
5
5
  Conventions:
6
6
  - Bash: `<n>.sh`, sourced via `source "$(dirname $0)/lib/<n>.sh"`
7
7
  - Python: `<n>.py` if needed (use `pip install --break-system-packages`)
8
8
  - Keep functions narrow
9
9
 
10
+ ## Current inhabitants
11
+
12
+ - `functions.sh` — read/validate the function registry at `.config/functions.yaml`. Pure bash + falls back to `python3` + `pyyaml` when available for safer YAML parsing.
13
+ - `bindings-prompt.sh` — **disabled in v1.0**. Phase 2 will rebuild this around the env-merge loader (config.yaml `tools:` metadata + `/.env` values). Until then, invocation aborts with manual-configuration instructions (edit agent.md `## Tools and bindings` → mirror the YAML block into `<agent>/config.yaml` → add env-var values to `/.env`). The file remains in the tarball + executable so smoke continues to assert presence; the runtime behavior is the guard message.
14
+
10
15
  Example future additions:
11
16
  - `lib/lesson.sh` — read/write lesson files, validate schema
12
17
  - `lib/run.sh` — append to run files, format frontmatter
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env bash
2
+ # bindings-prompt.sh — DISABLED in v1.0 (ROS-78).
3
+ #
4
+ # Pre-v1.0 behavior: read `## Tools and bindings` from agent.md (legacy
5
+ # two-level `tool: key: {required, description}` schema), prompt the user
6
+ # for binding values, and append a `tools:` block to a project-instance
7
+ # config at `projects/<inst>/config/default.yaml`.
8
+ #
9
+ # Both inputs change in v1.0:
10
+ # 1. The shipped `## Tools and bindings` block in agent.md is now a flat
11
+ # schema (`tool: { env_var, required, description }` per
12
+ # conventions.md). The legacy parser silently emits an empty
13
+ # `tools.<tool>:` block when it encounters scalar values where it
14
+ # expects a mapping.
15
+ # 2. The instance-config target is gone — agent config lives at
16
+ # `<function>/<agent>/config.yaml`, and env-var values belong in
17
+ # workspace `/.env` (overridable in `<agent>/.env`).
18
+ #
19
+ # The proper v1.0 flow (read agent.md flat schema → write metadata to
20
+ # config.yaml `tools:` block → prompt for required env-var values and
21
+ # append to `/.env`) requires the env-merge loader that ships in Phase 2.
22
+ # Rather than ship a script whose advertised behavior is broken against
23
+ # the new schema, this script aborts with manual-configuration instructions
24
+ # (edit agent.md schema → mirror to config.yaml → fill /.env).
25
+ #
26
+ # Tracking: re-enable as part of the Phase 2 cli-plumbing reshape
27
+ # (env-merge loader + doctor checks 13-15).
28
+
29
+ set -euo pipefail
30
+
31
+ cat >&2 <<'MSG'
32
+ bindings-prompt.sh is disabled in Roster v1.0.
33
+
34
+ The legacy two-level schema parser this script uses is incompatible with
35
+ the flat ## Tools and bindings schema documented in conventions.md
36
+ ("Tool bindings" section). The Phase 2 reshape (env-merge loader +
37
+ config.yaml/.env split) will rebuild this flow.
38
+
39
+ Until Phase 2 lands, configure tool bindings by hand:
40
+
41
+ 1. Open <function>/<agent>/agent.md and confirm the ## Tools and
42
+ bindings YAML block lists each tool with env_var, required, and
43
+ description fields (see conventions.md § "Tool bindings").
44
+ 2. Mirror that block as the tools: mapping in
45
+ <function>/<agent>/config.yaml.
46
+ 3. Add the corresponding env var values to workspace /.env (or, for
47
+ agent-scoped overrides, <function>/<agent>/.env).
48
+
49
+ (The chief-of-staff guided create-agent flow shipped in v0.4 is also on
50
+ the pre-v1 shape; it will be updated alongside the env-merge loader.)
51
+ MSG
52
+
53
+ exit 2
@@ -20,7 +20,8 @@ read_functions() {
20
20
  fi
21
21
  if _have_pyyaml; then
22
22
  python3 -c "
23
- import yaml, sys
23
+ import yaml, re, sys
24
+ SLUG_RE = re.compile(r'^[a-z][a-z0-9-]*\$')
24
25
  try:
25
26
  with open('$config') as f:
26
27
  data = yaml.safe_load(f) or {}
@@ -29,12 +30,23 @@ except yaml.YAMLError as e:
29
30
  sys.exit(1)
30
31
  for fn in data.get('functions', []):
31
32
  slug = fn.get('slug', '')
32
- if slug:
33
- print(slug)
33
+ if not slug:
34
+ continue
35
+ if not SLUG_RE.match(slug):
36
+ sys.stderr.write(\"ERROR: malformed slug '\" + slug + \"' in $config — must match ^[a-z][a-z0-9-]*\$\n\")
37
+ sys.exit(1)
38
+ print(slug)
34
39
  " || return 1
35
40
  else
36
- grep -E '^[[:space:]]*-[[:space:]]*slug:[[:space:]]*' "$config" \
37
- | sed -E 's/^[[:space:]]*-[[:space:]]*slug:[[:space:]]*//; s/[[:space:]]*$//'
41
+ local slug
42
+ while IFS= read -r slug; do
43
+ if ! [[ "$slug" =~ ^[a-z][a-z0-9-]*$ ]]; then
44
+ echo "ERROR: malformed slug '$slug' in $config — must match ^[a-z][a-z0-9-]*\$" >&2
45
+ return 1
46
+ fi
47
+ printf '%s\n' "$slug"
48
+ done < <(grep -E '^[[:space:]]*-[[:space:]]*slug:[[:space:]]*' "$config" \
49
+ | sed -E 's/^[[:space:]]*-[[:space:]]*slug:[[:space:]]*//; s/[[:space:]]*$//')
38
50
  fi
39
51
  }
40
52