aidevops 3.13.95 → 3.14.1
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 +0 -1
- package/VERSION +1 -1
- package/aidevops.sh +44 -26
- package/package.json +1 -1
- package/setup.sh +25 -21
- package/aidevops-init-lib.sh +0 -1411
- package/aidevops-repos-lib.sh +0 -700
- package/aidevops-skills-plugin-lib.sh +0 -697
- package/aidevops-status-lib.sh +0 -141
- package/aidevops-update-lib.sh +0 -512
- package/aidevops-upgrade-planning-lib.sh +0 -370
- package/setup-modules/agent-deploy.sh +0 -1035
- package/setup-modules/agent-runtime.sh +0 -287
- package/setup-modules/config.sh +0 -478
- package/setup-modules/core.sh +0 -736
- package/setup-modules/mcp-setup.sh +0 -947
- package/setup-modules/migrations.sh +0 -1688
- package/setup-modules/plugins.sh +0 -728
- package/setup-modules/post-setup.sh +0 -301
- package/setup-modules/schedulers-linux.sh +0 -386
- package/setup-modules/schedulers-platform.sh +0 -1072
- package/setup-modules/schedulers-pulse.sh +0 -978
- package/setup-modules/schedulers.sh +0 -565
- package/setup-modules/shell-env.sh +0 -1240
- package/setup-modules/tool-beads.sh +0 -324
- package/setup-modules/tool-install.sh +0 -2134
package/aidevops-init-lib.sh
DELETED
|
@@ -1,1411 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
# SPDX-FileCopyrightText: 2025-2026 Marcus Quinn
|
|
4
|
-
# =============================================================================
|
|
5
|
-
# aidevops Init and Scaffold Library
|
|
6
|
-
# =============================================================================
|
|
7
|
-
# Project initialisation and scaffolding functions extracted from aidevops.sh
|
|
8
|
-
# to keep the orchestrator under the 2000-line file-size threshold.
|
|
9
|
-
#
|
|
10
|
-
# Covers:
|
|
11
|
-
# 1. Scaffold helpers: _scaffold_contributing, _scaffold_security, _scaffold_coc,
|
|
12
|
-
# scaffold_repo_courtesy_files, _generate_security_section, scaffold_agents_md,
|
|
13
|
-
# _update_agents_md_security
|
|
14
|
-
# 2. Init helpers: _init_parse_features, _seed_mission_control_template,
|
|
15
|
-
# _init_scaffold_commands_symlinks, _init_scaffold_scope_gated_files
|
|
16
|
-
# 3. cmd_init: main init command dispatcher
|
|
17
|
-
#
|
|
18
|
-
# Usage: source "${SCRIPT_DIR}/aidevops-init-lib.sh"
|
|
19
|
-
#
|
|
20
|
-
# Dependencies:
|
|
21
|
-
# - INSTALL_DIR, AGENTS_DIR, CONFIG_DIR (set by aidevops.sh)
|
|
22
|
-
# - print_* helpers and utility functions (defined in aidevops.sh before sourcing)
|
|
23
|
-
# - register_repo(), get_repo_slug() from aidevops-repos-lib.sh (sourced first)
|
|
24
|
-
#
|
|
25
|
-
# Part of aidevops framework: https://aidevops.sh
|
|
26
|
-
|
|
27
|
-
# Apply strict mode only when executed directly (not when sourced)
|
|
28
|
-
[[ "${BASH_SOURCE[0]}" == "${0}" ]] && set -euo pipefail
|
|
29
|
-
|
|
30
|
-
# Include guard
|
|
31
|
-
[[ -n "${_AIDEVOPS_INIT_LIB_LOADED:-}" ]] && return 0
|
|
32
|
-
_AIDEVOPS_INIT_LIB_LOADED=1
|
|
33
|
-
|
|
34
|
-
_AGENT_SOURCE_TEMPLATE_VERSION="1"
|
|
35
|
-
|
|
36
|
-
_agent_source_template_dir() {
|
|
37
|
-
printf '%s\n' "${AGENTS_DIR}/templates/agent-source-repo"
|
|
38
|
-
return 0
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
_agent_source_apply_managed_template_file() {
|
|
42
|
-
local project_root="$1"
|
|
43
|
-
local relative_path="$2"
|
|
44
|
-
local template_dir dest src dest_dir
|
|
45
|
-
template_dir=$(_agent_source_template_dir)
|
|
46
|
-
src="$template_dir/$relative_path"
|
|
47
|
-
dest="$project_root/$relative_path"
|
|
48
|
-
dest_dir="${dest%/*}"
|
|
49
|
-
|
|
50
|
-
[[ -f "$src" ]] || return 1
|
|
51
|
-
[[ "$dest_dir" != "$dest" ]] && mkdir -p "$dest_dir"
|
|
52
|
-
|
|
53
|
-
if [[ ! -f "$dest" ]]; then
|
|
54
|
-
cp "$src" "$dest"
|
|
55
|
-
return 0
|
|
56
|
-
fi
|
|
57
|
-
|
|
58
|
-
if ! grep -q '<!-- aidevops:agent-source-template:start -->' "$dest" 2>/dev/null; then
|
|
59
|
-
return 0
|
|
60
|
-
fi
|
|
61
|
-
if ! grep -q '<!-- aidevops:agent-source-template:end -->' "$dest" 2>/dev/null; then
|
|
62
|
-
return 0
|
|
63
|
-
fi
|
|
64
|
-
|
|
65
|
-
python3 - "$dest" "$src" <<'PY'
|
|
66
|
-
from pathlib import Path
|
|
67
|
-
import sys
|
|
68
|
-
|
|
69
|
-
dest = Path(sys.argv[1])
|
|
70
|
-
src = Path(sys.argv[2])
|
|
71
|
-
start = "<!-- aidevops:agent-source-template:start -->"
|
|
72
|
-
end = "<!-- aidevops:agent-source-template:end -->"
|
|
73
|
-
old = dest.read_text()
|
|
74
|
-
new = src.read_text()
|
|
75
|
-
if start not in old or end not in old or start not in new or end not in new:
|
|
76
|
-
sys.exit(0)
|
|
77
|
-
old_prefix = old.split(start, 1)[0]
|
|
78
|
-
old_suffix = old.split(end, 1)[1]
|
|
79
|
-
new_block = start + new.split(start, 1)[1].split(end, 1)[0] + end
|
|
80
|
-
dest.write_text(old_prefix + new_block + old_suffix)
|
|
81
|
-
PY
|
|
82
|
-
return 0
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
seed_agent_source_repo_templates() {
|
|
86
|
-
local project_root="$1"
|
|
87
|
-
local template_dir
|
|
88
|
-
template_dir=$(_agent_source_template_dir)
|
|
89
|
-
|
|
90
|
-
if [[ ! -d "$template_dir" ]]; then
|
|
91
|
-
print_warning "Agent source template directory missing: $template_dir"
|
|
92
|
-
return 1
|
|
93
|
-
fi
|
|
94
|
-
|
|
95
|
-
local rel_dir
|
|
96
|
-
for rel_dir in \
|
|
97
|
-
".agents" \
|
|
98
|
-
".agents/tools" \
|
|
99
|
-
".agents/services" \
|
|
100
|
-
".agents/workflows" \
|
|
101
|
-
".agents/reference" \
|
|
102
|
-
".agents/scripts" \
|
|
103
|
-
".agents/scripts/commands" \
|
|
104
|
-
".agents/configs" \
|
|
105
|
-
".agents/bundles" \
|
|
106
|
-
".agents/templates" \
|
|
107
|
-
".agents/rules" \
|
|
108
|
-
".agents/tests" \
|
|
109
|
-
".agents/custom" \
|
|
110
|
-
".agents/draft"; do
|
|
111
|
-
mkdir -p "$project_root/$rel_dir"
|
|
112
|
-
done
|
|
113
|
-
|
|
114
|
-
_agent_source_apply_managed_template_file "$project_root" "AGENTS.md" || return 1
|
|
115
|
-
_agent_source_apply_managed_template_file "$project_root" ".agents/AGENTS.md" || return 1
|
|
116
|
-
print_success "Seeded agent-source repository templates (v${_AGENT_SOURCE_TEMPLATE_VERSION})"
|
|
117
|
-
return 0
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
# Scaffold standard repo courtesy files if they don't exist
|
|
121
|
-
# Scaffold helpers (extracted for complexity reduction)
|
|
122
|
-
_scaffold_contributing() {
|
|
123
|
-
local project_root="$1" repo_name="$2"
|
|
124
|
-
[[ -f "$project_root/CONTRIBUTING.md" ]] && return 1
|
|
125
|
-
local c="# Contributing to $repo_name"
|
|
126
|
-
c="$c"$'\n\n'"Thanks for your interest in contributing!"
|
|
127
|
-
c="$c"$'\n\n'"## Quick Start"$'\n\n'"1. Fork the repository"
|
|
128
|
-
c="$c"$'\n'"2. Create a branch: \`git checkout -b feature/your-feature\`"
|
|
129
|
-
c="$c"$'\n'"3. Make your changes"
|
|
130
|
-
c="$c"$'\n'"4. Commit with conventional commits: \`git commit -m \"feat: add new feature\"\`"
|
|
131
|
-
c="$c"$'\n'"5. Push and open a PR"
|
|
132
|
-
c="$c"$'\n\n'"## Commit Messages"$'\n\n'"We use [Conventional Commits](https://www.conventionalcommits.org/):"
|
|
133
|
-
c="$c"$'\n\n'"- \`feat:\` - New feature"$'\n'"- \`fix:\` - Bug fix"$'\n'"- \`docs:\` - Documentation only"
|
|
134
|
-
c="$c"$'\n'"- \`refactor:\` - Code change that neither fixes a bug nor adds a feature"$'\n'"- \`chore:\` - Maintenance tasks"
|
|
135
|
-
printf '%s\n' "$c" >"$project_root/CONTRIBUTING.md"
|
|
136
|
-
return 0
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
_scaffold_security() {
|
|
140
|
-
local project_root="$1"
|
|
141
|
-
[[ -f "$project_root/SECURITY.md" ]] && return 1
|
|
142
|
-
local se="" ge
|
|
143
|
-
ge=$(git -C "$project_root" config user.email 2>/dev/null || echo "")
|
|
144
|
-
[[ -n "$ge" ]] && se="$ge"
|
|
145
|
-
cat >"$project_root/SECURITY.md" <<SECEOF
|
|
146
|
-
# Security Policy
|
|
147
|
-
|
|
148
|
-
## Reporting a Vulnerability
|
|
149
|
-
|
|
150
|
-
If you discover a security vulnerability, please report it privately.
|
|
151
|
-
SECEOF
|
|
152
|
-
[[ -n "$se" ]] && cat >>"$project_root/SECURITY.md" <<SECEOF
|
|
153
|
-
|
|
154
|
-
**Email:** $se
|
|
155
|
-
|
|
156
|
-
Please do not open public issues for security vulnerabilities.
|
|
157
|
-
SECEOF
|
|
158
|
-
return 0
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
_scaffold_coc() {
|
|
162
|
-
local project_root="$1"
|
|
163
|
-
[[ -f "$project_root/CODE_OF_CONDUCT.md" ]] && return 1
|
|
164
|
-
cat >"$project_root/CODE_OF_CONDUCT.md" <<'COCEOF'
|
|
165
|
-
# Contributor Covenant Code of Conduct
|
|
166
|
-
|
|
167
|
-
## Our Pledge
|
|
168
|
-
|
|
169
|
-
We as members, contributors, and leaders pledge to make participation in our
|
|
170
|
-
community a harassment-free experience for everyone.
|
|
171
|
-
|
|
172
|
-
## Our Standards
|
|
173
|
-
|
|
174
|
-
Examples of behavior that contributes to a positive environment:
|
|
175
|
-
|
|
176
|
-
- Using welcoming and inclusive language
|
|
177
|
-
- Being respectful of differing viewpoints and experiences
|
|
178
|
-
- Gracefully accepting constructive criticism
|
|
179
|
-
- Focusing on what is best for the community
|
|
180
|
-
|
|
181
|
-
## Attribution
|
|
182
|
-
|
|
183
|
-
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
|
|
184
|
-
version 2.1.
|
|
185
|
-
COCEOF
|
|
186
|
-
return 0
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
# Scaffold standard repo courtesy files if they don't exist
|
|
190
|
-
# Creates: README.md, LICENCE, CHANGELOG.md, CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md
|
|
191
|
-
scaffold_repo_courtesy_files() {
|
|
192
|
-
local project_root="$1"
|
|
193
|
-
local scope="${2:-standard}" # Default to standard for backward compatibility
|
|
194
|
-
local created=0
|
|
195
|
-
local repo_name
|
|
196
|
-
repo_name=$(basename "$project_root")
|
|
197
|
-
local author_name
|
|
198
|
-
author_name=$(git -C "$project_root" config user.name 2>/dev/null || echo "")
|
|
199
|
-
local current_year
|
|
200
|
-
current_year=$(date +%Y)
|
|
201
|
-
print_info "Checking repo courtesy files (scope: $scope)..."
|
|
202
|
-
|
|
203
|
-
# README.md: requires "standard" scope
|
|
204
|
-
if _scope_includes "$scope" "standard"; then
|
|
205
|
-
if [[ ! -f "$project_root/README.md" ]]; then
|
|
206
|
-
local rc="# $repo_name"
|
|
207
|
-
if [[ -f "$project_root/.aidevops.json" ]]; then
|
|
208
|
-
local desc
|
|
209
|
-
desc=$(jq -r '.description // empty' "$project_root/.aidevops.json" 2>/dev/null || echo "")
|
|
210
|
-
[[ -n "$desc" ]] && rc="$rc"$'\n\n'"$desc"
|
|
211
|
-
fi
|
|
212
|
-
{ [[ -f "$project_root/LICENCE" ]] || [[ -f "$project_root/LICENSE" ]]; } && rc="$rc"$'\n\n'"## Licence"$'\n\n'"See [LICENCE](LICENCE) for details."
|
|
213
|
-
printf '%s\n' "$rc" >"$project_root/README.md"
|
|
214
|
-
((++created))
|
|
215
|
-
fi
|
|
216
|
-
fi
|
|
217
|
-
|
|
218
|
-
# LICENCE: requires "public" scope
|
|
219
|
-
if _scope_includes "$scope" "public"; then
|
|
220
|
-
if [[ ! -f "$project_root/LICENCE" ]] && [[ ! -f "$project_root/LICENSE" ]]; then
|
|
221
|
-
local lh="${author_name:-$(whoami)}"
|
|
222
|
-
cat >"$project_root/LICENCE" <<LICEOF
|
|
223
|
-
MIT License
|
|
224
|
-
|
|
225
|
-
Copyright (c) $current_year $lh
|
|
226
|
-
|
|
227
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
228
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
229
|
-
in the Software without restriction, including without limitation the rights
|
|
230
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
231
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
232
|
-
furnished to do so, subject to the following conditions:
|
|
233
|
-
|
|
234
|
-
The above copyright notice and this permission notice shall be included in all
|
|
235
|
-
copies or substantial portions of the Software.
|
|
236
|
-
|
|
237
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
238
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
239
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
240
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
241
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
242
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
243
|
-
SOFTWARE.
|
|
244
|
-
LICEOF
|
|
245
|
-
((++created))
|
|
246
|
-
fi
|
|
247
|
-
fi
|
|
248
|
-
|
|
249
|
-
# CHANGELOG.md: requires "public" scope
|
|
250
|
-
if _scope_includes "$scope" "public"; then
|
|
251
|
-
if [[ ! -f "$project_root/CHANGELOG.md" ]]; then
|
|
252
|
-
cat >"$project_root/CHANGELOG.md" <<'CHEOF'
|
|
253
|
-
# Changelog
|
|
254
|
-
|
|
255
|
-
All notable changes to this project will be documented in this file.
|
|
256
|
-
|
|
257
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
258
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
259
|
-
|
|
260
|
-
## [Unreleased]
|
|
261
|
-
CHEOF
|
|
262
|
-
((++created))
|
|
263
|
-
fi
|
|
264
|
-
fi
|
|
265
|
-
|
|
266
|
-
# CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md: require "public" scope
|
|
267
|
-
if _scope_includes "$scope" "public"; then
|
|
268
|
-
_scaffold_contributing "$project_root" "$repo_name" && ((++created))
|
|
269
|
-
_scaffold_security "$project_root" && ((++created))
|
|
270
|
-
_scaffold_coc "$project_root" && ((++created))
|
|
271
|
-
fi
|
|
272
|
-
|
|
273
|
-
[[ $created -gt 0 ]] && print_success "Created $created repo courtesy file(s) (README, LICENCE, CHANGELOG, etc.)" || print_info "Repo courtesy files already exist"
|
|
274
|
-
return 0
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
# Generate the Security section content based on project type (t1412.11)
|
|
278
|
-
# Returns the content via stdout so callers can use it for create or update.
|
|
279
|
-
_generate_security_section() {
|
|
280
|
-
local project_root="$1"
|
|
281
|
-
|
|
282
|
-
# Detect project type for security recommendations
|
|
283
|
-
local is_js_ts=false
|
|
284
|
-
if [[ -f "$project_root/package.json" ]]; then
|
|
285
|
-
is_js_ts=true
|
|
286
|
-
fi
|
|
287
|
-
|
|
288
|
-
cat <<'SECHEADER'
|
|
289
|
-
## Security
|
|
290
|
-
|
|
291
|
-
SECHEADER
|
|
292
|
-
|
|
293
|
-
if [[ "$is_js_ts" == "true" ]]; then
|
|
294
|
-
cat <<'SECEOF'
|
|
295
|
-
### Prompt Injection Defense
|
|
296
|
-
|
|
297
|
-
Any feature that processes untrusted content (tool outputs, user input, webhook
|
|
298
|
-
payloads) and passes it to an LLM must defend against prompt injection. This is
|
|
299
|
-
a JS/TS project — use `@stackone/defender` to sanitize untrusted tool results
|
|
300
|
-
before they enter the LLM context:
|
|
301
|
-
|
|
302
|
-
```bash
|
|
303
|
-
npm install @stackone/defender
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
import { createPromptDefense } from '@stackone/defender';
|
|
308
|
-
|
|
309
|
-
const defense = createPromptDefense({ enableTier2: true, blockHighRisk: true });
|
|
310
|
-
|
|
311
|
-
// Sanitize untrusted tool output before adding to LLM context
|
|
312
|
-
const result = await defense.defendToolResult(toolResponse, 'tool_name');
|
|
313
|
-
if (!result.allowed) {
|
|
314
|
-
throw new Error('Blocked untrusted content');
|
|
315
|
-
}
|
|
316
|
-
const safeContent = result.sanitized;
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
For features that don't use LLMs but process untrusted text (webhooks, form
|
|
320
|
-
submissions, API endpoints), validate and sanitize inputs at the boundary.
|
|
321
|
-
|
|
322
|
-
### General Security Rules
|
|
323
|
-
|
|
324
|
-
- Never log or expose API keys, tokens, or credentials in output
|
|
325
|
-
- Store secrets via `aidevops secret set <NAME>` (gopass-encrypted) or
|
|
326
|
-
environment variables — never hardcode them in source
|
|
327
|
-
- Use `<PLACEHOLDER>` values in code examples; note the secure storage location
|
|
328
|
-
- Validate all external input (user input, webhook payloads, API responses)
|
|
329
|
-
- Pin third-party GitHub Actions to SHA hashes, not branch tags
|
|
330
|
-
- Run `aidevops security audit` periodically to check security posture
|
|
331
|
-
- See `~/.aidevops/agents/tools/security/prompt-injection-defender.md` for
|
|
332
|
-
the framework's prompt injection defense patterns
|
|
333
|
-
SECEOF
|
|
334
|
-
else
|
|
335
|
-
cat <<'SECEOF'
|
|
336
|
-
### Prompt Injection Defense
|
|
337
|
-
|
|
338
|
-
Any feature that passes untrusted content to an LLM — user input, tool outputs,
|
|
339
|
-
retrieved documents, emails, tickets, or webhook payloads — must defend against
|
|
340
|
-
prompt injection. Sanitize and validate that content before including it in
|
|
341
|
-
prompts:
|
|
342
|
-
|
|
343
|
-
- Strip or escape control characters and instruction-like patterns
|
|
344
|
-
- Use structured prompt templates with clear system/user boundaries
|
|
345
|
-
- Never concatenate raw external content directly into system prompts
|
|
346
|
-
- Validate all externally sourced content (tool results, API responses, database
|
|
347
|
-
records) before inclusion in prompts
|
|
348
|
-
- Consider allowlist-based input validation where possible
|
|
349
|
-
|
|
350
|
-
### General Security Rules
|
|
351
|
-
|
|
352
|
-
- Never log or expose API keys, tokens, or credentials in output
|
|
353
|
-
- Store secrets via `aidevops secret set <NAME>` (gopass-encrypted) or
|
|
354
|
-
environment variables — never hardcode them in source
|
|
355
|
-
- Use `<PLACEHOLDER>` values in code examples; note the secure storage location
|
|
356
|
-
- Validate all external input (user input, webhook payloads, API responses)
|
|
357
|
-
- Pin third-party GitHub Actions to SHA hashes, not branch tags
|
|
358
|
-
- Run `aidevops security audit` periodically to check security posture
|
|
359
|
-
- See `~/.aidevops/agents/tools/security/prompt-injection-defender.md` for
|
|
360
|
-
the framework's prompt injection defense patterns
|
|
361
|
-
SECEOF
|
|
362
|
-
fi
|
|
363
|
-
|
|
364
|
-
return 0
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
# Scaffold .agents/AGENTS.md with context-aware Security section (t1412.11)
|
|
368
|
-
# Idempotent: creates the file if missing, or updates the Security section
|
|
369
|
-
# in an existing file (preserving all other custom content).
|
|
370
|
-
scaffold_agents_md() {
|
|
371
|
-
local project_root="$1"
|
|
372
|
-
local agents_md="$project_root/.agents/AGENTS.md"
|
|
373
|
-
|
|
374
|
-
mkdir -p "$(dirname "$agents_md")"
|
|
375
|
-
|
|
376
|
-
if [[ -f "$agents_md" ]]; then
|
|
377
|
-
# File exists — update the Security section idempotently
|
|
378
|
-
_update_agents_md_security "$project_root"
|
|
379
|
-
return $?
|
|
380
|
-
fi
|
|
381
|
-
|
|
382
|
-
# File missing — create from scratch with base template + security
|
|
383
|
-
local security_content
|
|
384
|
-
security_content=$(_generate_security_section "$project_root")
|
|
385
|
-
|
|
386
|
-
cat >"$agents_md" <<'AGENTSEOF'
|
|
387
|
-
# Agent Instructions
|
|
388
|
-
|
|
389
|
-
This directory contains project-specific agent context. The [aidevops](https://aidevops.sh)
|
|
390
|
-
framework is loaded separately via the global config (`~/.aidevops/agents/`).
|
|
391
|
-
|
|
392
|
-
## Purpose
|
|
393
|
-
|
|
394
|
-
Files in `.agents/` provide project-specific instructions that AI assistants
|
|
395
|
-
read when working in this repository. Use this for:
|
|
396
|
-
|
|
397
|
-
- Domain-specific conventions not covered by the framework
|
|
398
|
-
- Project architecture decisions and patterns
|
|
399
|
-
- API design rules, data models, naming conventions
|
|
400
|
-
- Integration details (third-party services, deployment targets)
|
|
401
|
-
|
|
402
|
-
## Adding Agents
|
|
403
|
-
|
|
404
|
-
Create `.md` files in this directory for domain-specific context:
|
|
405
|
-
|
|
406
|
-
```text
|
|
407
|
-
.agents/
|
|
408
|
-
AGENTS.md # This file - overview and index
|
|
409
|
-
api-patterns.md # API design conventions
|
|
410
|
-
deployment.md # Deployment procedures
|
|
411
|
-
data-model.md # Database schema and relationships
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
Each file is read on demand by AI assistants when relevant to the task.
|
|
415
|
-
|
|
416
|
-
AGENTSEOF
|
|
417
|
-
|
|
418
|
-
# Append the generated security section
|
|
419
|
-
printf '%s\n' "$security_content" >>"$agents_md"
|
|
420
|
-
|
|
421
|
-
return 0
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
# Update the Security section in an existing .agents/AGENTS.md (t1412.11)
|
|
425
|
-
# Replaces everything from "## Security" to the next "## " heading (or EOF)
|
|
426
|
-
# with the latest security guidance. Preserves all other content.
|
|
427
|
-
_update_agents_md_security() {
|
|
428
|
-
local project_root="$1"
|
|
429
|
-
local agents_md="$project_root/.agents/AGENTS.md"
|
|
430
|
-
local tmp_file="${agents_md}.tmp.$$"
|
|
431
|
-
|
|
432
|
-
local security_content
|
|
433
|
-
security_content=$(_generate_security_section "$project_root")
|
|
434
|
-
|
|
435
|
-
local in_security=false
|
|
436
|
-
local has_security_section=false
|
|
437
|
-
|
|
438
|
-
# Process line by line: skip old Security section, insert new one
|
|
439
|
-
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
440
|
-
# Match "## Security" exactly, with optional trailing whitespace
|
|
441
|
-
if [[ "$line" =~ ^'## Security'[[:space:]]*$ ]]; then
|
|
442
|
-
# Found the Security heading — replace it
|
|
443
|
-
in_security=true
|
|
444
|
-
has_security_section=true
|
|
445
|
-
printf '%s\n' "$security_content" >>"$tmp_file"
|
|
446
|
-
continue
|
|
447
|
-
fi
|
|
448
|
-
|
|
449
|
-
if [[ "$in_security" == "true" ]]; then
|
|
450
|
-
# Check if we've hit the next ## heading (end of Security section)
|
|
451
|
-
if [[ "$line" == "## "* ]]; then
|
|
452
|
-
in_security=false
|
|
453
|
-
printf '%s\n' "$line" >>"$tmp_file"
|
|
454
|
-
fi
|
|
455
|
-
# Skip lines within the old Security section
|
|
456
|
-
continue
|
|
457
|
-
fi
|
|
458
|
-
|
|
459
|
-
printf '%s\n' "$line" >>"$tmp_file"
|
|
460
|
-
done <"$agents_md"
|
|
461
|
-
|
|
462
|
-
if [[ "$has_security_section" == "false" ]]; then
|
|
463
|
-
# No existing Security section — append it
|
|
464
|
-
printf '\n%s\n' "$security_content" >>"$tmp_file"
|
|
465
|
-
fi
|
|
466
|
-
|
|
467
|
-
mv "$tmp_file" "$agents_md"
|
|
468
|
-
|
|
469
|
-
return 0
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
# Init helpers (extracted for complexity reduction)
|
|
473
|
-
_init_parse_features() {
|
|
474
|
-
local features="$1"
|
|
475
|
-
case "$features" in
|
|
476
|
-
all) echo "planning git_workflow code_quality time_tracking database beads security" ;;
|
|
477
|
-
planning) echo "planning" ;; git-workflow) echo "git_workflow" ;; code-quality) echo "code_quality" ;;
|
|
478
|
-
time-tracking) echo "time_tracking planning" ;; database) echo "database" ;;
|
|
479
|
-
beads) echo "beads planning" ;; sops) echo "sops" ;; security) echo "security" ;;
|
|
480
|
-
*)
|
|
481
|
-
local result=""
|
|
482
|
-
IFS=',' read -ra FL <<<"$features"
|
|
483
|
-
for f in "${FL[@]}"; do
|
|
484
|
-
case "$f" in
|
|
485
|
-
planning) result="$result planning" ;; git-workflow) result="$result git_workflow" ;;
|
|
486
|
-
code-quality) result="$result code_quality" ;; time-tracking) result="$result time_tracking planning" ;;
|
|
487
|
-
database) result="$result database" ;; beads) result="$result beads planning" ;;
|
|
488
|
-
sops) result="$result sops" ;; security) result="$result security" ;;
|
|
489
|
-
esac
|
|
490
|
-
done
|
|
491
|
-
echo "$result"
|
|
492
|
-
;;
|
|
493
|
-
esac
|
|
494
|
-
return 0
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
# Seed mission-control onboarding template when initializing a mission-control repo.
|
|
498
|
-
# Usage: _seed_mission_control_template <project_root> <personal|org>
|
|
499
|
-
_seed_mission_control_template() {
|
|
500
|
-
local project_root="$1"
|
|
501
|
-
local scope="$2"
|
|
502
|
-
local seed_file="$project_root/todo/mission-control-seed.md"
|
|
503
|
-
|
|
504
|
-
if [[ -z "$scope" ]]; then
|
|
505
|
-
return 0
|
|
506
|
-
fi
|
|
507
|
-
|
|
508
|
-
if [[ -f "$seed_file" ]]; then
|
|
509
|
-
return 0
|
|
510
|
-
fi
|
|
511
|
-
|
|
512
|
-
mkdir -p "$project_root/todo"
|
|
513
|
-
|
|
514
|
-
if [[ "$scope" == "personal" ]]; then
|
|
515
|
-
cat >"$seed_file" <<'EOF'
|
|
516
|
-
# Mission Control Seed (Personal)
|
|
517
|
-
|
|
518
|
-
Starter checklist for a personal mission-control repo initialized with aidevops.
|
|
519
|
-
|
|
520
|
-
## First-Day Setup
|
|
521
|
-
|
|
522
|
-
- [ ] Confirm `~/.config/aidevops/repos.json` has all active repos registered with correct `slug` and `path`
|
|
523
|
-
- [ ] Set `pulse: true` only for repos you want actively supervised
|
|
524
|
-
- [ ] Add `pulse_hours` windows to avoid dispatch during daytime manual development
|
|
525
|
-
- [ ] Verify profile and archive repos are `pulse: false` and `priority: "profile"` where applicable
|
|
526
|
-
|
|
527
|
-
## Operating Rhythm
|
|
528
|
-
|
|
529
|
-
- [ ] Define weekly review cadence for `aidevops pulse` health and backlog aging
|
|
530
|
-
- [ ] Add hygiene tasks for stale branches, stale worktrees, and stale queued issues
|
|
531
|
-
- [ ] Track cross-repo blockers in TODO with clear `blocked-by:` links
|
|
532
|
-
EOF
|
|
533
|
-
else
|
|
534
|
-
cat >"$seed_file" <<'EOF'
|
|
535
|
-
# Mission Control Seed (Organization)
|
|
536
|
-
|
|
537
|
-
Starter checklist for an organization mission-control repo initialized with aidevops.
|
|
538
|
-
|
|
539
|
-
## First-Day Setup
|
|
540
|
-
|
|
541
|
-
- [ ] Register all managed org repos in `~/.config/aidevops/repos.json` with `slug`, `path`, `priority`, and `maintainer`
|
|
542
|
-
- [ ] Set `pulse: true` only for repos approved for autonomous dispatch
|
|
543
|
-
- [ ] Configure `pulse_hours` and optional `pulse_expires` windows for sprint-based focus
|
|
544
|
-
- [ ] Keep sensitive/internal-only repos `pulse: false` until policy checks are complete
|
|
545
|
-
|
|
546
|
-
## Governance
|
|
547
|
-
|
|
548
|
-
- [ ] Define maintainer response SLA for `needs-maintainer-review` triage
|
|
549
|
-
- [ ] Document worker guardrails (release, merge, and security boundaries)
|
|
550
|
-
- [ ] Add a weekly audit task for repo registration drift and label hygiene
|
|
551
|
-
EOF
|
|
552
|
-
fi
|
|
553
|
-
|
|
554
|
-
print_success "Seeded mission-control template: todo/mission-control-seed.md (${scope})"
|
|
555
|
-
return 0
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
# Scaffold .agents/commands/ and .windsurf/workflows/ symlinks so that clients
|
|
559
|
-
# which read repo-local command directories (Amp reads .agents/commands/ natively;
|
|
560
|
-
# Windsurf reads .windsurf/workflows/) see the aidevops main-agent slash commands.
|
|
561
|
-
#
|
|
562
|
-
# Behavior is idempotent:
|
|
563
|
-
# - If .agents/commands/ already contains the expected aidevops-*.md symlinks
|
|
564
|
-
# (this repo IS the aidevops source), do nothing.
|
|
565
|
-
# - Otherwise link .agents/commands/ → ~/.aidevops/agents/commands/
|
|
566
|
-
# - Always link .windsurf/workflows/ → ../.agents/commands/ (relative)
|
|
567
|
-
_init_scaffold_commands_symlinks() {
|
|
568
|
-
local project_root="$1"
|
|
569
|
-
local source_dir="$HOME/.aidevops/agents/commands"
|
|
570
|
-
local commands_dir="$project_root/.agents/commands"
|
|
571
|
-
local windsurf_dir="$project_root/.windsurf"
|
|
572
|
-
local workflows_link="$windsurf_dir/workflows"
|
|
573
|
-
|
|
574
|
-
# If .agents/commands/ already contains main-agent symlinks, this repo
|
|
575
|
-
# manages them directly (e.g. the aidevops source repo itself) — leave
|
|
576
|
-
# it alone so we never overwrite authoritative content.
|
|
577
|
-
if [[ -e "$commands_dir/aidevops-build-plus.md" ]]; then
|
|
578
|
-
print_info ".agents/commands/ already contains main-agent symlinks — preserving"
|
|
579
|
-
elif [[ ! -d "$source_dir" ]]; then
|
|
580
|
-
print_warning "Framework commands dir not found at $source_dir — run setup.sh first to deploy main-agent symlinks"
|
|
581
|
-
elif [[ -L "$commands_dir" ]]; then
|
|
582
|
-
# Existing symlink — point at the canonical source
|
|
583
|
-
local current_target
|
|
584
|
-
current_target=$(readlink "$commands_dir")
|
|
585
|
-
if [[ "$current_target" != "$source_dir" ]]; then
|
|
586
|
-
rm "$commands_dir"
|
|
587
|
-
ln -s "$source_dir" "$commands_dir"
|
|
588
|
-
print_success "Re-linked .agents/commands/ → $source_dir"
|
|
589
|
-
else
|
|
590
|
-
print_info ".agents/commands/ already linked correctly"
|
|
591
|
-
fi
|
|
592
|
-
elif [[ -d "$commands_dir" ]]; then
|
|
593
|
-
print_warning ".agents/commands/ exists as a real directory — not overwriting"
|
|
594
|
-
else
|
|
595
|
-
ln -s "$source_dir" "$commands_dir"
|
|
596
|
-
print_success "Linked .agents/commands/ → $source_dir (Amp reads this natively)"
|
|
597
|
-
fi
|
|
598
|
-
|
|
599
|
-
# .windsurf/workflows/ → ../.agents/commands/ (relative, so the link
|
|
600
|
-
# resolves inside the repo regardless of checkout path).
|
|
601
|
-
mkdir -p "$windsurf_dir"
|
|
602
|
-
if [[ -L "$workflows_link" ]]; then
|
|
603
|
-
print_info ".windsurf/workflows/ already linked"
|
|
604
|
-
elif [[ -d "$workflows_link" ]]; then
|
|
605
|
-
print_warning ".windsurf/workflows/ exists as a real directory — not overwriting"
|
|
606
|
-
else
|
|
607
|
-
(cd "$windsurf_dir" && ln -s "../.agents/commands" workflows)
|
|
608
|
-
print_success "Linked .windsurf/workflows/ → ../.agents/commands (Windsurf slash commands)"
|
|
609
|
-
fi
|
|
610
|
-
return 0
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
# Scaffold optional files gated by init_scope (t2265).
|
|
614
|
-
# Extracted from cmd_init to reduce nesting depth and function length.
|
|
615
|
-
# Usage: _init_scaffold_scope_gated_files <project_root> <init_scope> <repo_name>
|
|
616
|
-
_init_scaffold_scope_gated_files() {
|
|
617
|
-
local project_root="$1"
|
|
618
|
-
local init_scope="$2"
|
|
619
|
-
local repo_name="$3"
|
|
620
|
-
|
|
621
|
-
# Collaborator pointer files — require standard scope
|
|
622
|
-
if _scope_includes "$init_scope" "standard"; then
|
|
623
|
-
local pointer_content="Read AGENTS.md for all project context and instructions."
|
|
624
|
-
local pointer_files=(".cursorrules" ".windsurfrules" ".clinerules" ".github/copilot-instructions.md")
|
|
625
|
-
local pointer_created=0
|
|
626
|
-
local pf
|
|
627
|
-
for pf in "${pointer_files[@]}"; do
|
|
628
|
-
local pf_path="$project_root/$pf"
|
|
629
|
-
if [[ ! -f "$pf_path" ]]; then
|
|
630
|
-
mkdir -p "$(dirname "$pf_path")"
|
|
631
|
-
echo "$pointer_content" >"$pf_path"
|
|
632
|
-
((++pointer_created))
|
|
633
|
-
fi
|
|
634
|
-
done
|
|
635
|
-
if [[ $pointer_created -gt 0 ]]; then
|
|
636
|
-
print_success "Created $pointer_created collaborator pointer file(s) (.cursorrules, etc.)"
|
|
637
|
-
else
|
|
638
|
-
print_info "Collaborator pointer files already exist"
|
|
639
|
-
fi
|
|
640
|
-
else
|
|
641
|
-
print_info "Collaborator pointer files skipped (init_scope: $init_scope)"
|
|
642
|
-
fi
|
|
643
|
-
|
|
644
|
-
# DESIGN.md — requires standard scope
|
|
645
|
-
if _scope_includes "$init_scope" "standard"; then
|
|
646
|
-
if [[ ! -f "$project_root/DESIGN.md" ]]; then
|
|
647
|
-
local design_template="$AGENTS_DIR/templates/DESIGN.md.template"
|
|
648
|
-
if [[ -f "$design_template" ]]; then
|
|
649
|
-
sed "s/{Project Name}/$repo_name/g" "$design_template" >"$project_root/DESIGN.md"
|
|
650
|
-
print_success "Created DESIGN.md (design system skeleton — populate with tools/design/design-md.md)"
|
|
651
|
-
fi
|
|
652
|
-
else
|
|
653
|
-
print_info "DESIGN.md already exists, skipping"
|
|
654
|
-
fi
|
|
655
|
-
else
|
|
656
|
-
print_info "DESIGN.md skipped (init_scope: $init_scope)"
|
|
657
|
-
fi
|
|
658
|
-
|
|
659
|
-
# Courtesy files (README, LICENCE, CHANGELOG, etc.) — scope handled internally
|
|
660
|
-
scaffold_repo_courtesy_files "$project_root" "$init_scope"
|
|
661
|
-
|
|
662
|
-
# MODELS.md — requires standard scope
|
|
663
|
-
if _scope_includes "$init_scope" "standard"; then
|
|
664
|
-
local generate_models_script="$AGENTS_DIR/scripts/generate-models-md.sh"
|
|
665
|
-
if [[ -x "$generate_models_script" ]] && command -v sqlite3 &>/dev/null; then
|
|
666
|
-
print_info "Generating MODELS.md (model performance leaderboard)..."
|
|
667
|
-
if "$generate_models_script" --output "$project_root/MODELS.md" --repo-path "$project_root" --quiet 2>/dev/null; then
|
|
668
|
-
print_success "Created MODELS.md (per-repo model leaderboard)"
|
|
669
|
-
else
|
|
670
|
-
print_warning "MODELS.md generation failed (will be populated as tasks run)"
|
|
671
|
-
fi
|
|
672
|
-
else
|
|
673
|
-
print_info "MODELS.md skipped (sqlite3 or generate script not available)"
|
|
674
|
-
fi
|
|
675
|
-
else
|
|
676
|
-
print_info "MODELS.md skipped (init_scope: $init_scope)"
|
|
677
|
-
fi
|
|
678
|
-
|
|
679
|
-
return 0
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
# Init command - initialize aidevops in a project
|
|
683
|
-
cmd_init() {
|
|
684
|
-
local features="${1:-all}"
|
|
685
|
-
|
|
686
|
-
print_header "Initialize AI DevOps in Project"
|
|
687
|
-
echo ""
|
|
688
|
-
|
|
689
|
-
# Check if we're in a git repo
|
|
690
|
-
if ! git rev-parse --is-inside-work-tree &>/dev/null; then
|
|
691
|
-
print_error "Not in a git repository"
|
|
692
|
-
print_info "Run 'git init' first or navigate to a git repository"
|
|
693
|
-
return 1
|
|
694
|
-
fi
|
|
695
|
-
|
|
696
|
-
# Check for protected branch and offer worktree
|
|
697
|
-
if ! check_protected_branch "chore" "aidevops-init"; then
|
|
698
|
-
return 1
|
|
699
|
-
fi
|
|
700
|
-
|
|
701
|
-
local project_root
|
|
702
|
-
project_root=$(git rev-parse --show-toplevel)
|
|
703
|
-
print_info "Project root: $project_root"
|
|
704
|
-
echo ""
|
|
705
|
-
|
|
706
|
-
# Parse features using helper
|
|
707
|
-
local parsed
|
|
708
|
-
parsed=$(_init_parse_features "$features")
|
|
709
|
-
local enable_planning=false enable_git_workflow=false enable_code_quality=false
|
|
710
|
-
local enable_time_tracking=false enable_database=false enable_beads=false
|
|
711
|
-
local enable_sops=false enable_security=false
|
|
712
|
-
local _f
|
|
713
|
-
for _f in $parsed; do
|
|
714
|
-
case "$_f" in
|
|
715
|
-
planning) enable_planning=true ;; git_workflow) enable_git_workflow=true ;;
|
|
716
|
-
code_quality) enable_code_quality=true ;; time_tracking) enable_time_tracking=true ;;
|
|
717
|
-
database) enable_database=true ;; beads) enable_beads=true ;;
|
|
718
|
-
sops) enable_sops=true ;; security) enable_security=true ;;
|
|
719
|
-
esac
|
|
720
|
-
done
|
|
721
|
-
|
|
722
|
-
# Determine init_scope: minimal | standard | public
|
|
723
|
-
# Infer from context when not set; user can override via repos.json or .aidevops.json
|
|
724
|
-
local init_scope
|
|
725
|
-
init_scope=$(_infer_init_scope "$project_root")
|
|
726
|
-
print_info "Init scope: $init_scope (controls which scaffolding files are created)"
|
|
727
|
-
|
|
728
|
-
local is_agent_source=false
|
|
729
|
-
if is_agent_source_repo "$project_root"; then
|
|
730
|
-
is_agent_source=true
|
|
731
|
-
print_info "Agent source repo: true (seeding core-style agent organization)"
|
|
732
|
-
fi
|
|
733
|
-
|
|
734
|
-
# Create .aidevops.json config
|
|
735
|
-
local config_file="$project_root/.aidevops.json"
|
|
736
|
-
local aidevops_version
|
|
737
|
-
aidevops_version=$(get_version)
|
|
738
|
-
|
|
739
|
-
print_info "Creating .aidevops.json..."
|
|
740
|
-
cat >"$config_file" <<EOF
|
|
741
|
-
{
|
|
742
|
-
"version": "$aidevops_version",
|
|
743
|
-
"initialized": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
744
|
-
"init_scope": "$init_scope",
|
|
745
|
-
"agent_source": $is_agent_source,
|
|
746
|
-
"features": {
|
|
747
|
-
"planning": $enable_planning,
|
|
748
|
-
"git_workflow": $enable_git_workflow,
|
|
749
|
-
"code_quality": $enable_code_quality,
|
|
750
|
-
"time_tracking": $enable_time_tracking,
|
|
751
|
-
"database": $enable_database,
|
|
752
|
-
"beads": $enable_beads,
|
|
753
|
-
"sops": $enable_sops,
|
|
754
|
-
"security": $enable_security
|
|
755
|
-
},
|
|
756
|
-
"time_tracking": {
|
|
757
|
-
"enabled": $enable_time_tracking,
|
|
758
|
-
"prompt_on_commit": true,
|
|
759
|
-
"auto_record_branch_start": true
|
|
760
|
-
},
|
|
761
|
-
"database": {
|
|
762
|
-
"enabled": $enable_database,
|
|
763
|
-
"schema_path": "schemas",
|
|
764
|
-
"migrations_path": "migrations",
|
|
765
|
-
"seeds_path": "seeds",
|
|
766
|
-
"auto_generate_migration": true
|
|
767
|
-
},
|
|
768
|
-
"beads": {
|
|
769
|
-
"enabled": $enable_beads,
|
|
770
|
-
"sync_on_commit": false,
|
|
771
|
-
"auto_ready_check": true
|
|
772
|
-
},
|
|
773
|
-
"sops": {
|
|
774
|
-
"enabled": $enable_sops,
|
|
775
|
-
"backend": "age",
|
|
776
|
-
"patterns": ["*.secret.yaml", "*.secret.json", "configs/*.enc.json", "configs/*.enc.yaml"]
|
|
777
|
-
},
|
|
778
|
-
"plugins": []
|
|
779
|
-
}
|
|
780
|
-
EOF
|
|
781
|
-
# Note: plugins array is always present but empty by default.
|
|
782
|
-
# Users add plugins via: aidevops plugin add <repo-url> [--namespace <name>]
|
|
783
|
-
# Schema per plugin entry:
|
|
784
|
-
# {
|
|
785
|
-
# "name": "pro",
|
|
786
|
-
# "repo": "https://github.com/user/aidevops-pro.git",
|
|
787
|
-
# "branch": "main",
|
|
788
|
-
# "namespace": "pro",
|
|
789
|
-
# "enabled": true
|
|
790
|
-
# }
|
|
791
|
-
# Plugins deploy to ~/.aidevops/agents/<namespace>/ (namespaced, no collisions)
|
|
792
|
-
print_success "Created .aidevops.json"
|
|
793
|
-
|
|
794
|
-
# Derive repo name for scaffolding
|
|
795
|
-
# In worktrees, basename gives the worktree dir name (e.g., "repo-chore-foo"),
|
|
796
|
-
# not the actual repo name. Prefer: git remote URL > main worktree basename > cwd basename.
|
|
797
|
-
local repo_name
|
|
798
|
-
local remote_url
|
|
799
|
-
remote_url=$(git -C "$project_root" remote get-url origin 2>/dev/null || true)
|
|
800
|
-
local repo_slug=""
|
|
801
|
-
if [[ -n "$remote_url" ]]; then
|
|
802
|
-
repo_slug=$(echo "$remote_url" | sed 's|.*github\.com[:/]||;s|\.git$||')
|
|
803
|
-
fi
|
|
804
|
-
if [[ -n "$remote_url" ]]; then
|
|
805
|
-
repo_name=$(basename "$remote_url" .git)
|
|
806
|
-
else
|
|
807
|
-
# No remote — try main worktree path (first line of `git worktree list`)
|
|
808
|
-
local main_wt
|
|
809
|
-
main_wt=$(git -C "$project_root" worktree list --porcelain 2>/dev/null | head -1 | sed 's/^worktree //')
|
|
810
|
-
if [[ -n "$main_wt" ]]; then
|
|
811
|
-
repo_name=$(basename "$main_wt")
|
|
812
|
-
else
|
|
813
|
-
repo_name=$(basename "$project_root")
|
|
814
|
-
fi
|
|
815
|
-
fi
|
|
816
|
-
|
|
817
|
-
# Create .agents/ directory for project-specific agent context
|
|
818
|
-
# (The aidevops framework is loaded globally via ~/.aidevops/agents/ — this
|
|
819
|
-
# directory is for project-specific agents, conventions, and architecture docs)
|
|
820
|
-
if [[ -L "$project_root/.agents" ]]; then
|
|
821
|
-
# Migrate legacy symlink to real directory
|
|
822
|
-
rm -f "$project_root/.agents"
|
|
823
|
-
print_info "Removed legacy .agents symlink (framework is loaded globally now)"
|
|
824
|
-
fi
|
|
825
|
-
# Also clean up legacy .agent symlink/directory
|
|
826
|
-
if [[ -L "$project_root/.agent" ]]; then
|
|
827
|
-
rm -f "$project_root/.agent"
|
|
828
|
-
print_info "Removed legacy .agent symlink"
|
|
829
|
-
elif [[ -d "$project_root/.agent" && ! -d "$project_root/.agents" ]]; then
|
|
830
|
-
mv "$project_root/.agent" "$project_root/.agents"
|
|
831
|
-
print_success "Migrated .agent/ -> .agents/ directory"
|
|
832
|
-
fi
|
|
833
|
-
|
|
834
|
-
if [[ ! -d "$project_root/.agents" ]]; then
|
|
835
|
-
mkdir -p "$project_root/.agents"
|
|
836
|
-
print_success "Created .agents/ directory"
|
|
837
|
-
fi
|
|
838
|
-
|
|
839
|
-
# Link .agents/commands/ and .windsurf/workflows/ so Amp (native) and Windsurf
|
|
840
|
-
# (symlinked) can see the aidevops main-agent slash commands.
|
|
841
|
-
_init_scaffold_commands_symlinks "$project_root"
|
|
842
|
-
|
|
843
|
-
if [[ "$is_agent_source" == "true" ]]; then
|
|
844
|
-
seed_agent_source_repo_templates "$project_root"
|
|
845
|
-
else
|
|
846
|
-
# Scaffold or update .agents/AGENTS.md (idempotent — creates if missing,
|
|
847
|
-
# updates Security section if file already exists)
|
|
848
|
-
local _agents_md_existed=false
|
|
849
|
-
[[ -f "$project_root/.agents/AGENTS.md" ]] && _agents_md_existed=true
|
|
850
|
-
scaffold_agents_md "$project_root"
|
|
851
|
-
if [[ "$_agents_md_existed" == "true" ]]; then
|
|
852
|
-
print_success "Updated Security section in .agents/AGENTS.md"
|
|
853
|
-
else
|
|
854
|
-
print_success "Created .agents/AGENTS.md"
|
|
855
|
-
fi
|
|
856
|
-
fi
|
|
857
|
-
|
|
858
|
-
# Scaffold root AGENTS.md if missing
|
|
859
|
-
if [[ "$is_agent_source" != "true" && ! -f "$project_root/AGENTS.md" ]]; then
|
|
860
|
-
cat >"$project_root/AGENTS.md" <<ROOTAGENTSEOF
|
|
861
|
-
# $repo_name
|
|
862
|
-
|
|
863
|
-
<!-- AI-CONTEXT-START -->
|
|
864
|
-
|
|
865
|
-
## Quick Reference
|
|
866
|
-
|
|
867
|
-
- **Build**: \`# TODO: add build command\`
|
|
868
|
-
- **Test**: \`# TODO: add test command\`
|
|
869
|
-
- **Deploy**: \`# TODO: add deploy command\`
|
|
870
|
-
|
|
871
|
-
## Project Overview
|
|
872
|
-
|
|
873
|
-
<!-- Brief description of what this project does and why it exists. -->
|
|
874
|
-
|
|
875
|
-
## Architecture
|
|
876
|
-
|
|
877
|
-
<!-- Key architectural decisions, tech stack, directory structure. -->
|
|
878
|
-
|
|
879
|
-
## Conventions
|
|
880
|
-
|
|
881
|
-
- Commits: [Conventional Commits](https://www.conventionalcommits.org/)
|
|
882
|
-
- Branches: \`feature/\`, \`bugfix/\`, \`hotfix/\`, \`refactor/\`, \`chore/\`
|
|
883
|
-
|
|
884
|
-
## Key Files
|
|
885
|
-
|
|
886
|
-
| File | Purpose |
|
|
887
|
-
|------|---------|
|
|
888
|
-
| \`.agents/AGENTS.md\` | Project-specific agent instructions |
|
|
889
|
-
| \`TODO.md\` | Task tracking |
|
|
890
|
-
| \`CHANGELOG.md\` | Version history |
|
|
891
|
-
|
|
892
|
-
<!-- AI-CONTEXT-END -->
|
|
893
|
-
ROOTAGENTSEOF
|
|
894
|
-
print_success "Created AGENTS.md"
|
|
895
|
-
fi
|
|
896
|
-
|
|
897
|
-
# Create planning files if enabled
|
|
898
|
-
if [[ "$enable_planning" == "true" ]]; then
|
|
899
|
-
print_info "Setting up planning files..."
|
|
900
|
-
|
|
901
|
-
# Create TODO.md from template
|
|
902
|
-
if [[ ! -f "$project_root/TODO.md" ]]; then
|
|
903
|
-
if [[ -f "$AGENTS_DIR/templates/todo-template.md" ]]; then
|
|
904
|
-
cp "$AGENTS_DIR/templates/todo-template.md" "$project_root/TODO.md"
|
|
905
|
-
print_success "Created TODO.md"
|
|
906
|
-
else
|
|
907
|
-
# Fallback minimal template
|
|
908
|
-
cat >"$project_root/TODO.md" <<'EOF'
|
|
909
|
-
# TODO
|
|
910
|
-
|
|
911
|
-
## In Progress
|
|
912
|
-
|
|
913
|
-
<!-- Tasks currently being worked on -->
|
|
914
|
-
|
|
915
|
-
## Backlog
|
|
916
|
-
|
|
917
|
-
<!-- Prioritized list of upcoming tasks -->
|
|
918
|
-
|
|
919
|
-
---
|
|
920
|
-
|
|
921
|
-
*Format: `- [ ] Task description @owner #tag ~estimate`*
|
|
922
|
-
*Time tracking: `started:`, `completed:`, `actual:`*
|
|
923
|
-
EOF
|
|
924
|
-
print_success "Created TODO.md (minimal template)"
|
|
925
|
-
fi
|
|
926
|
-
else
|
|
927
|
-
print_warning "TODO.md already exists, skipping"
|
|
928
|
-
fi
|
|
929
|
-
|
|
930
|
-
# Create todo/ directory and PLANS.md
|
|
931
|
-
mkdir -p "$project_root/todo/tasks"
|
|
932
|
-
|
|
933
|
-
if [[ ! -f "$project_root/todo/PLANS.md" ]]; then
|
|
934
|
-
if [[ -f "$AGENTS_DIR/templates/plans-template.md" ]]; then
|
|
935
|
-
cp "$AGENTS_DIR/templates/plans-template.md" "$project_root/todo/PLANS.md"
|
|
936
|
-
print_success "Created todo/PLANS.md"
|
|
937
|
-
else
|
|
938
|
-
# Fallback minimal template
|
|
939
|
-
cat >"$project_root/todo/PLANS.md" <<'EOF'
|
|
940
|
-
# Execution Plans
|
|
941
|
-
|
|
942
|
-
Complex, multi-session work that requires detailed planning.
|
|
943
|
-
|
|
944
|
-
## Active Plans
|
|
945
|
-
|
|
946
|
-
<!-- Plans currently in progress -->
|
|
947
|
-
|
|
948
|
-
## Completed Plans
|
|
949
|
-
|
|
950
|
-
<!-- Archived completed plans -->
|
|
951
|
-
|
|
952
|
-
---
|
|
953
|
-
|
|
954
|
-
*See `.agents/workflows/plans.md` for planning workflow*
|
|
955
|
-
EOF
|
|
956
|
-
print_success "Created todo/PLANS.md (minimal template)"
|
|
957
|
-
fi
|
|
958
|
-
else
|
|
959
|
-
print_warning "todo/PLANS.md already exists, skipping"
|
|
960
|
-
fi
|
|
961
|
-
|
|
962
|
-
# Create .gitkeep in tasks
|
|
963
|
-
touch "$project_root/todo/tasks/.gitkeep"
|
|
964
|
-
|
|
965
|
-
# Seed mission-control starter template for personal/org control repos
|
|
966
|
-
local init_actor=""
|
|
967
|
-
if command -v gh &>/dev/null; then
|
|
968
|
-
init_actor=$(gh api user --jq '.login' 2>/dev/null || echo "")
|
|
969
|
-
fi
|
|
970
|
-
local mission_scope=""
|
|
971
|
-
mission_scope=$(_resolve_mission_control_scope "$repo_slug" "$init_actor" 2>/dev/null || echo "")
|
|
972
|
-
_seed_mission_control_template "$project_root" "$mission_scope"
|
|
973
|
-
fi
|
|
974
|
-
|
|
975
|
-
# Create database directories if enabled
|
|
976
|
-
if [[ "$enable_database" == "true" ]]; then
|
|
977
|
-
print_info "Setting up database schema directories..."
|
|
978
|
-
|
|
979
|
-
# Create schemas directory with AGENTS.md
|
|
980
|
-
if [[ ! -d "$project_root/schemas" ]]; then
|
|
981
|
-
mkdir -p "$project_root/schemas"
|
|
982
|
-
cat >"$project_root/schemas/AGENTS.md" <<'EOF'
|
|
983
|
-
# Database Schemas
|
|
984
|
-
|
|
985
|
-
Declarative schema files - source of truth for database structure.
|
|
986
|
-
|
|
987
|
-
See: `@sql-migrations` or `.agents/workflows/sql-migrations.md`
|
|
988
|
-
EOF
|
|
989
|
-
print_success "Created schemas/ directory"
|
|
990
|
-
else
|
|
991
|
-
print_warning "schemas/ already exists, skipping"
|
|
992
|
-
fi
|
|
993
|
-
|
|
994
|
-
# Create migrations directory with AGENTS.md
|
|
995
|
-
if [[ ! -d "$project_root/migrations" ]]; then
|
|
996
|
-
mkdir -p "$project_root/migrations"
|
|
997
|
-
cat >"$project_root/migrations/AGENTS.md" <<'EOF'
|
|
998
|
-
# Database Migrations
|
|
999
|
-
|
|
1000
|
-
Auto-generated versioned migration files. Do not edit manually.
|
|
1001
|
-
|
|
1002
|
-
See: `@sql-migrations` or `.agents/workflows/sql-migrations.md`
|
|
1003
|
-
EOF
|
|
1004
|
-
print_success "Created migrations/ directory"
|
|
1005
|
-
else
|
|
1006
|
-
print_warning "migrations/ already exists, skipping"
|
|
1007
|
-
fi
|
|
1008
|
-
|
|
1009
|
-
# Create seeds directory with AGENTS.md
|
|
1010
|
-
if [[ ! -d "$project_root/seeds" ]]; then
|
|
1011
|
-
mkdir -p "$project_root/seeds"
|
|
1012
|
-
cat >"$project_root/seeds/AGENTS.md" <<'EOF'
|
|
1013
|
-
# Database Seeds
|
|
1014
|
-
|
|
1015
|
-
Initial and reference data (roles, statuses, test accounts).
|
|
1016
|
-
|
|
1017
|
-
See: `@sql-migrations` or `.agents/workflows/sql-migrations.md`
|
|
1018
|
-
EOF
|
|
1019
|
-
print_success "Created seeds/ directory"
|
|
1020
|
-
else
|
|
1021
|
-
print_warning "seeds/ already exists, skipping"
|
|
1022
|
-
fi
|
|
1023
|
-
fi
|
|
1024
|
-
|
|
1025
|
-
# Initialize Beads if enabled
|
|
1026
|
-
if [[ "$enable_beads" == "true" ]]; then
|
|
1027
|
-
print_info "Setting up Beads task graph..."
|
|
1028
|
-
|
|
1029
|
-
# Check if Beads CLI is installed
|
|
1030
|
-
if ! command -v bd &>/dev/null; then
|
|
1031
|
-
print_warning "Beads CLI (bd) not installed"
|
|
1032
|
-
echo " Install with: brew install steveyegge/beads/bd"
|
|
1033
|
-
echo " Or download: https://github.com/steveyegge/beads/releases"
|
|
1034
|
-
echo " Or via Go: go install github.com/steveyegge/beads/cmd/bd@latest"
|
|
1035
|
-
else
|
|
1036
|
-
# Initialize Beads in the project
|
|
1037
|
-
if [[ ! -d "$project_root/.beads" ]]; then
|
|
1038
|
-
print_info "Initializing Beads database..."
|
|
1039
|
-
if (cd "$project_root" && bd init 2>/dev/null); then
|
|
1040
|
-
print_success "Beads initialized"
|
|
1041
|
-
else
|
|
1042
|
-
print_warning "Beads init failed - run manually: bd init"
|
|
1043
|
-
fi
|
|
1044
|
-
else
|
|
1045
|
-
print_info "Beads already initialized"
|
|
1046
|
-
fi
|
|
1047
|
-
|
|
1048
|
-
# Run initial sync from TODO.md/PLANS.md
|
|
1049
|
-
if [[ -f "$AGENTS_DIR/scripts/beads-sync-helper.sh" ]]; then
|
|
1050
|
-
print_info "Syncing tasks to Beads..."
|
|
1051
|
-
if bash "$AGENTS_DIR/scripts/beads-sync-helper.sh" push "$project_root" 2>/dev/null; then
|
|
1052
|
-
print_success "Tasks synced to Beads"
|
|
1053
|
-
else
|
|
1054
|
-
print_warning "Beads sync failed - run manually: beads-sync-helper.sh push"
|
|
1055
|
-
fi
|
|
1056
|
-
fi
|
|
1057
|
-
fi
|
|
1058
|
-
fi
|
|
1059
|
-
|
|
1060
|
-
# Initialize SOPS if enabled
|
|
1061
|
-
if [[ "$enable_sops" == "true" ]]; then
|
|
1062
|
-
print_info "Setting up SOPS encrypted config support..."
|
|
1063
|
-
|
|
1064
|
-
# Check for sops and age
|
|
1065
|
-
local sops_ready=true
|
|
1066
|
-
if ! command -v sops &>/dev/null; then
|
|
1067
|
-
print_warning "SOPS not installed"
|
|
1068
|
-
echo " Install with: brew install sops"
|
|
1069
|
-
sops_ready=false
|
|
1070
|
-
fi
|
|
1071
|
-
if ! command -v age-keygen &>/dev/null; then
|
|
1072
|
-
print_warning "age not installed (default SOPS backend)"
|
|
1073
|
-
echo " Install with: brew install age"
|
|
1074
|
-
sops_ready=false
|
|
1075
|
-
fi
|
|
1076
|
-
|
|
1077
|
-
# Generate age key if none exists
|
|
1078
|
-
local age_key_file="$HOME/.config/sops/age/keys.txt"
|
|
1079
|
-
if [[ "$sops_ready" == "true" ]] && [[ ! -f "$age_key_file" ]]; then
|
|
1080
|
-
print_info "Generating age key for SOPS..."
|
|
1081
|
-
mkdir -p "$(dirname "$age_key_file")"
|
|
1082
|
-
age-keygen -o "$age_key_file" 2>/dev/null
|
|
1083
|
-
chmod 600 "$age_key_file"
|
|
1084
|
-
print_success "Age key generated at $age_key_file"
|
|
1085
|
-
fi
|
|
1086
|
-
|
|
1087
|
-
# Create .sops.yaml if it doesn't exist
|
|
1088
|
-
if [[ ! -f "$project_root/.sops.yaml" ]]; then
|
|
1089
|
-
local age_pubkey=""
|
|
1090
|
-
if [[ -f "$age_key_file" ]]; then
|
|
1091
|
-
age_pubkey=$(grep -o 'age1[a-z0-9]*' "$age_key_file" | head -1)
|
|
1092
|
-
fi
|
|
1093
|
-
|
|
1094
|
-
if [[ -n "$age_pubkey" ]]; then
|
|
1095
|
-
cat >"$project_root/.sops.yaml" <<SOPSEOF
|
|
1096
|
-
# SOPS configuration - encrypts values in config files while keeping keys visible
|
|
1097
|
-
# See: .agents/tools/credentials/sops.md
|
|
1098
|
-
creation_rules:
|
|
1099
|
-
- path_regex: '\.secret\.(yaml|yml|json)$'
|
|
1100
|
-
age: >-
|
|
1101
|
-
$age_pubkey
|
|
1102
|
-
- path_regex: 'configs/.*\.enc\.(yaml|yml|json)$'
|
|
1103
|
-
age: >-
|
|
1104
|
-
$age_pubkey
|
|
1105
|
-
SOPSEOF
|
|
1106
|
-
print_success "Created .sops.yaml with age key"
|
|
1107
|
-
else
|
|
1108
|
-
cat >"$project_root/.sops.yaml" <<'SOPSEOF'
|
|
1109
|
-
# SOPS configuration - encrypts values in config files while keeping keys visible
|
|
1110
|
-
# See: .agents/tools/credentials/sops.md
|
|
1111
|
-
#
|
|
1112
|
-
# Generate an age key first:
|
|
1113
|
-
# age-keygen -o ~/.config/sops/age/keys.txt
|
|
1114
|
-
#
|
|
1115
|
-
# Then replace AGE_PUBLIC_KEY below with your public key:
|
|
1116
|
-
creation_rules:
|
|
1117
|
-
- path_regex: '\.secret\.(yaml|yml|json)$'
|
|
1118
|
-
age: >-
|
|
1119
|
-
AGE_PUBLIC_KEY
|
|
1120
|
-
- path_regex: 'configs/.*\.enc\.(yaml|yml|json)$'
|
|
1121
|
-
age: >-
|
|
1122
|
-
AGE_PUBLIC_KEY
|
|
1123
|
-
SOPSEOF
|
|
1124
|
-
print_warning "Created .sops.yaml template (replace AGE_PUBLIC_KEY with your key)"
|
|
1125
|
-
fi
|
|
1126
|
-
else
|
|
1127
|
-
print_info ".sops.yaml already exists"
|
|
1128
|
-
fi
|
|
1129
|
-
fi
|
|
1130
|
-
|
|
1131
|
-
# Ensure .gitattributes has ai-training=false (opt out of AI model training)
|
|
1132
|
-
# GitHub and other platforms respect this attribute to exclude repo content
|
|
1133
|
-
# from AI/ML training datasets. Idempotent — only adds if not already present.
|
|
1134
|
-
local gitattributes="$project_root/.gitattributes"
|
|
1135
|
-
if [[ -f "$gitattributes" ]]; then
|
|
1136
|
-
if ! grep -qE '^\*[[:space:]]+ai-training=false' "$gitattributes" 2>/dev/null; then
|
|
1137
|
-
ensure_trailing_newline "$gitattributes"
|
|
1138
|
-
{
|
|
1139
|
-
echo ""
|
|
1140
|
-
echo "# Opt out of AI model training"
|
|
1141
|
-
echo "* ai-training=false"
|
|
1142
|
-
} >>"$gitattributes"
|
|
1143
|
-
print_success "Added ai-training=false to .gitattributes"
|
|
1144
|
-
else
|
|
1145
|
-
print_info ".gitattributes already has ai-training=false"
|
|
1146
|
-
fi
|
|
1147
|
-
else
|
|
1148
|
-
cat >"$gitattributes" <<'GITATTRSEOF'
|
|
1149
|
-
# Opt out of AI model training
|
|
1150
|
-
* ai-training=false
|
|
1151
|
-
GITATTRSEOF
|
|
1152
|
-
print_success "Created .gitattributes with ai-training=false"
|
|
1153
|
-
fi
|
|
1154
|
-
|
|
1155
|
-
# Add aidevops runtime artifacts to .gitignore
|
|
1156
|
-
# Note: .agents/ itself is NOT ignored — it contains committed project-specific agents.
|
|
1157
|
-
# Only runtime artifacts (loop state, tmp, memory) are ignored.
|
|
1158
|
-
local gitignore="$project_root/.gitignore"
|
|
1159
|
-
if [[ -f "$gitignore" ]]; then
|
|
1160
|
-
local gitignore_updated=false
|
|
1161
|
-
|
|
1162
|
-
# Remove legacy bare ".agents" entry if present (was added by older versions)
|
|
1163
|
-
if grep -q "^\.agents$" "$gitignore" 2>/dev/null; then
|
|
1164
|
-
sed -i '' '/^\.agents$/d' "$gitignore" 2>/dev/null ||
|
|
1165
|
-
sed -i '/^\.agents$/d' "$gitignore" 2>/dev/null || true
|
|
1166
|
-
# Also remove the "# aidevops" comment if it's now orphaned
|
|
1167
|
-
sed -i '' '/^# aidevops$/{ N; /^# aidevops\n$/d; }' "$gitignore" 2>/dev/null || true
|
|
1168
|
-
print_info "Removed legacy bare .agents from .gitignore (now tracked)"
|
|
1169
|
-
gitignore_updated=true
|
|
1170
|
-
fi
|
|
1171
|
-
|
|
1172
|
-
# Remove legacy bare ".agent" entry if present
|
|
1173
|
-
if grep -q "^\.agent$" "$gitignore" 2>/dev/null; then
|
|
1174
|
-
sed -i '' '/^\.agent$/d' "$gitignore" 2>/dev/null ||
|
|
1175
|
-
sed -i '/^\.agent$/d' "$gitignore" 2>/dev/null || true
|
|
1176
|
-
gitignore_updated=true
|
|
1177
|
-
fi
|
|
1178
|
-
|
|
1179
|
-
# Add runtime artifact ignores
|
|
1180
|
-
if ! grep -q "^\.agents/loop-state/" "$gitignore" 2>/dev/null; then
|
|
1181
|
-
# Ensure trailing newline before appending (prevents malformed entries like *.zip.agents/loop-state/)
|
|
1182
|
-
ensure_trailing_newline "$gitignore"
|
|
1183
|
-
{
|
|
1184
|
-
echo ""
|
|
1185
|
-
echo "# aidevops runtime artifacts"
|
|
1186
|
-
echo ".agents/loop-state/"
|
|
1187
|
-
echo ".agents/tmp/"
|
|
1188
|
-
echo ".agents/memory/"
|
|
1189
|
-
} >>"$gitignore"
|
|
1190
|
-
print_success "Added .agents/ runtime artifact ignores to .gitignore"
|
|
1191
|
-
gitignore_updated=true
|
|
1192
|
-
fi
|
|
1193
|
-
|
|
1194
|
-
# Add .aidevops.json to gitignore (local config, not committed).
|
|
1195
|
-
# If .aidevops.json is already tracked by git (committed by older framework
|
|
1196
|
-
# versions), untrack it first — adding a tracked file to .gitignore is a
|
|
1197
|
-
# no-op and the file keeps showing in git diff on every re-init (#2570 bug 3).
|
|
1198
|
-
if ! grep -q "^\.aidevops\.json$" "$gitignore" 2>/dev/null; then
|
|
1199
|
-
if git -C "$project_root" ls-files --error-unmatch .aidevops.json &>/dev/null; then
|
|
1200
|
-
git -C "$project_root" rm --cached .aidevops.json &>/dev/null || true
|
|
1201
|
-
print_info "Untracked .aidevops.json from git (was committed by older version)"
|
|
1202
|
-
fi
|
|
1203
|
-
# Ensure trailing newline before appending
|
|
1204
|
-
ensure_trailing_newline "$gitignore"
|
|
1205
|
-
echo ".aidevops.json" >>"$gitignore"
|
|
1206
|
-
gitignore_updated=true
|
|
1207
|
-
fi
|
|
1208
|
-
|
|
1209
|
-
# Add .beads if beads is enabled
|
|
1210
|
-
if [[ "$enable_beads" == "true" ]]; then
|
|
1211
|
-
if ! grep -q "^\.beads$" "$gitignore" 2>/dev/null; then
|
|
1212
|
-
# Ensure trailing newline before appending
|
|
1213
|
-
ensure_trailing_newline "$gitignore"
|
|
1214
|
-
echo ".beads" >>"$gitignore"
|
|
1215
|
-
print_success "Added .beads to .gitignore"
|
|
1216
|
-
gitignore_updated=true
|
|
1217
|
-
fi
|
|
1218
|
-
fi
|
|
1219
|
-
|
|
1220
|
-
if [[ "$gitignore_updated" == "true" ]]; then
|
|
1221
|
-
print_info "Updated .gitignore"
|
|
1222
|
-
fi
|
|
1223
|
-
fi
|
|
1224
|
-
|
|
1225
|
-
# Scaffold optional files gated by init_scope (collaborator pointers,
|
|
1226
|
-
# DESIGN.md, courtesy files, MODELS.md). Extracted to reduce cmd_init
|
|
1227
|
-
# nesting depth and function length (t2265).
|
|
1228
|
-
_init_scaffold_scope_gated_files "$project_root" "$init_scope" "$repo_name"
|
|
1229
|
-
|
|
1230
|
-
# ─── Badge initialization (t2975) ────────────────────────────────────────
|
|
1231
|
-
# Install the loc-badge caller workflow and seed the canonical README badge
|
|
1232
|
-
# block in fresh repos. Both operations are idempotent. Skip for local_only
|
|
1233
|
-
# repos (no remote to host SVGs or run GitHub Actions).
|
|
1234
|
-
local _badges_helper="$AGENTS_DIR/scripts/readme-badges-helper.sh"
|
|
1235
|
-
local _wf_template="$AGENTS_DIR/templates/workflows/loc-badge-caller.yml"
|
|
1236
|
-
local _wf_dest="$project_root/.github/workflows/loc-badge.yml"
|
|
1237
|
-
|
|
1238
|
-
if [[ -n "$repo_slug" ]]; then
|
|
1239
|
-
# Install loc-badge caller workflow if template is available and file is absent
|
|
1240
|
-
if [[ -f "$_wf_template" && ! -f "$_wf_dest" ]]; then
|
|
1241
|
-
mkdir -p "$project_root/.github/workflows"
|
|
1242
|
-
cp "$_wf_template" "$_wf_dest"
|
|
1243
|
-
print_success "Installed .github/workflows/loc-badge.yml (LOC badge workflow)"
|
|
1244
|
-
elif [[ -f "$_wf_dest" ]]; then
|
|
1245
|
-
print_info ".github/workflows/loc-badge.yml already present"
|
|
1246
|
-
fi
|
|
1247
|
-
|
|
1248
|
-
# Seed the canonical badge block in README.md
|
|
1249
|
-
local _readme_path="$project_root/README.md"
|
|
1250
|
-
if [[ -f "$_badges_helper" && -f "$_readme_path" ]]; then
|
|
1251
|
-
if bash "$_badges_helper" inject "$_readme_path" "$repo_slug" 2>/dev/null; then
|
|
1252
|
-
print_success "Seeded canonical badge block in README.md"
|
|
1253
|
-
else
|
|
1254
|
-
print_warning "Badge block injection failed — run manually: aidevops badges sync --repo $repo_slug --apply"
|
|
1255
|
-
fi
|
|
1256
|
-
elif [[ ! -f "$_readme_path" ]]; then
|
|
1257
|
-
print_info "No README.md found — skipping badge block injection (create README.md first)"
|
|
1258
|
-
fi
|
|
1259
|
-
|
|
1260
|
-
# Remind about SYNC_PAT if the repo has a remote and isn't local_only
|
|
1261
|
-
local _is_local_only
|
|
1262
|
-
_is_local_only=$(jq -r --arg s "$repo_slug" \
|
|
1263
|
-
'.initialized_repos // [] | map(select(.slug == $s)) | if length > 0 then .[0].local_only // false else false end' \
|
|
1264
|
-
"$HOME/.config/aidevops/repos.json" 2>/dev/null || echo "false")
|
|
1265
|
-
if [[ "$_is_local_only" != "true" ]]; then
|
|
1266
|
-
print_info "Reminder: set SYNC_PAT secret so GitHub Actions can push badge SVGs — see: aidevops --help sync-pat"
|
|
1267
|
-
fi
|
|
1268
|
-
fi
|
|
1269
|
-
|
|
1270
|
-
# Run security posture assessment if enabled (t1412.11)
|
|
1271
|
-
if [[ "$enable_security" == "true" ]]; then
|
|
1272
|
-
local security_posture_script="$AGENTS_DIR/scripts/security-posture-helper.sh"
|
|
1273
|
-
if [[ -f "$security_posture_script" ]]; then
|
|
1274
|
-
print_info "Running security posture assessment..."
|
|
1275
|
-
if bash "$security_posture_script" store "$project_root"; then
|
|
1276
|
-
print_success "Security posture assessed and stored in .aidevops.json"
|
|
1277
|
-
else
|
|
1278
|
-
print_warning "Security posture assessment found issues (review with: aidevops security audit)"
|
|
1279
|
-
fi
|
|
1280
|
-
else
|
|
1281
|
-
print_info "Security posture check skipped (security-posture-helper.sh not available)"
|
|
1282
|
-
fi
|
|
1283
|
-
fi
|
|
1284
|
-
|
|
1285
|
-
# Build features string for registration
|
|
1286
|
-
local features_list=""
|
|
1287
|
-
[[ "$enable_planning" == "true" ]] && features_list="${features_list}planning,"
|
|
1288
|
-
[[ "$enable_git_workflow" == "true" ]] && features_list="${features_list}git-workflow,"
|
|
1289
|
-
[[ "$enable_code_quality" == "true" ]] && features_list="${features_list}code-quality,"
|
|
1290
|
-
[[ "$enable_time_tracking" == "true" ]] && features_list="${features_list}time-tracking,"
|
|
1291
|
-
[[ "$enable_database" == "true" ]] && features_list="${features_list}database,"
|
|
1292
|
-
[[ "$enable_beads" == "true" ]] && features_list="${features_list}beads,"
|
|
1293
|
-
[[ "$enable_sops" == "true" ]] && features_list="${features_list}sops,"
|
|
1294
|
-
[[ "$enable_security" == "true" ]] && features_list="${features_list}security,"
|
|
1295
|
-
features_list="${features_list%,}" # Remove trailing comma
|
|
1296
|
-
|
|
1297
|
-
# Register the *main* repo path (not the worktree path) in repos.json.
|
|
1298
|
-
# When check_protected_branch creates a worktree and cd's into it,
|
|
1299
|
-
# $project_root (resolved via git rev-parse --show-toplevel) points to the
|
|
1300
|
-
# worktree directory. We must register the canonical main worktree path so
|
|
1301
|
-
# that pulse and cleanup processes don't treat the worktree as a standalone repo.
|
|
1302
|
-
local register_path="$project_root"
|
|
1303
|
-
if [[ -n "${WORKTREE_PATH:-}" ]]; then
|
|
1304
|
-
# We're inside a worktree — resolve the main worktree path from git metadata
|
|
1305
|
-
local main_wt_path
|
|
1306
|
-
main_wt_path=$(git -C "$project_root" worktree list --porcelain 2>/dev/null | awk '/^worktree /{print $2; exit}')
|
|
1307
|
-
if [[ -n "$main_wt_path" ]] && [[ "$main_wt_path" != "$project_root" ]]; then
|
|
1308
|
-
register_path="$main_wt_path"
|
|
1309
|
-
fi
|
|
1310
|
-
fi
|
|
1311
|
-
register_repo "$register_path" "$aidevops_version" "$features_list"
|
|
1312
|
-
|
|
1313
|
-
# Auto-commit initialized files so they don't linger as mystery unstaged
|
|
1314
|
-
# changes (#2570 bug 2). Collect all files that cmd_init creates/modifies.
|
|
1315
|
-
local init_files=()
|
|
1316
|
-
[[ -f "$project_root/.gitattributes" ]] && init_files+=(".gitattributes")
|
|
1317
|
-
[[ -f "$project_root/.gitignore" ]] && init_files+=(".gitignore")
|
|
1318
|
-
[[ -d "$project_root/.agents" ]] && init_files+=(".agents/")
|
|
1319
|
-
[[ -f "$project_root/AGENTS.md" ]] && init_files+=("AGENTS.md")
|
|
1320
|
-
[[ -f "$project_root/DESIGN.md" ]] && init_files+=("DESIGN.md")
|
|
1321
|
-
[[ -f "$project_root/TODO.md" ]] && init_files+=("TODO.md")
|
|
1322
|
-
[[ -d "$project_root/todo" ]] && init_files+=("todo/")
|
|
1323
|
-
[[ -f "$project_root/MODELS.md" ]] && init_files+=("MODELS.md")
|
|
1324
|
-
[[ -f "$project_root/LICENCE" ]] && init_files+=("LICENCE")
|
|
1325
|
-
[[ -f "$project_root/CHANGELOG.md" ]] && init_files+=("CHANGELOG.md")
|
|
1326
|
-
[[ -f "$project_root/README.md" ]] && init_files+=("README.md")
|
|
1327
|
-
[[ -f "$project_root/.cursorrules" ]] && init_files+=(".cursorrules")
|
|
1328
|
-
[[ -f "$project_root/.windsurfrules" ]] && init_files+=(".windsurfrules")
|
|
1329
|
-
[[ -f "$project_root/.clinerules" ]] && init_files+=(".clinerules")
|
|
1330
|
-
[[ -d "$project_root/.github" ]] && init_files+=(".github/")
|
|
1331
|
-
[[ -f "$project_root/.sops.yaml" ]] && init_files+=(".sops.yaml")
|
|
1332
|
-
[[ -d "$project_root/schemas" ]] && init_files+=("schemas/")
|
|
1333
|
-
[[ -d "$project_root/migrations" ]] && init_files+=("migrations/")
|
|
1334
|
-
[[ -d "$project_root/seeds" ]] && init_files+=("seeds/")
|
|
1335
|
-
|
|
1336
|
-
local committed=false
|
|
1337
|
-
if [[ ${#init_files[@]} -gt 0 ]]; then
|
|
1338
|
-
# Stage all init files (--force not needed; .aidevops.json is gitignored above)
|
|
1339
|
-
if git -C "$project_root" add -- "${init_files[@]}" 2>/dev/null; then
|
|
1340
|
-
# Only commit if there are staged changes
|
|
1341
|
-
if ! git -C "$project_root" diff --cached --quiet 2>/dev/null; then
|
|
1342
|
-
if git -C "$project_root" commit -m "chore: initialize aidevops v${aidevops_version}" 2>/dev/null; then
|
|
1343
|
-
committed=true
|
|
1344
|
-
print_success "Committed initialized files"
|
|
1345
|
-
else
|
|
1346
|
-
print_warning "Auto-commit failed (pre-commit hook rejected?)"
|
|
1347
|
-
fi
|
|
1348
|
-
fi
|
|
1349
|
-
fi
|
|
1350
|
-
fi
|
|
1351
|
-
|
|
1352
|
-
echo ""
|
|
1353
|
-
print_success "AI DevOps initialized! (scope: $init_scope)"
|
|
1354
|
-
echo ""
|
|
1355
|
-
echo "Enabled features:"
|
|
1356
|
-
[[ "$enable_planning" == "true" ]] && echo " ✓ Planning (TODO.md, PLANS.md)"
|
|
1357
|
-
[[ "$enable_git_workflow" == "true" ]] && echo " ✓ Git workflow (branch management)"
|
|
1358
|
-
[[ "$enable_code_quality" == "true" ]] && echo " ✓ Code quality (linting, auditing)"
|
|
1359
|
-
[[ "$enable_time_tracking" == "true" ]] && echo " ✓ Time tracking (estimates, actuals)"
|
|
1360
|
-
[[ "$enable_database" == "true" ]] && echo " ✓ Database (schemas/, migrations/, seeds/)"
|
|
1361
|
-
[[ "$enable_beads" == "true" ]] && echo " ✓ Beads (task graph visualization)"
|
|
1362
|
-
[[ "$enable_sops" == "true" ]] && echo " ✓ SOPS (encrypted config files with age backend)"
|
|
1363
|
-
[[ "$enable_security" == "true" ]] && echo " ✓ Security (per-repo posture assessment)"
|
|
1364
|
-
[[ -f "$project_root/MODELS.md" ]] && echo " ✓ MODELS.md (per-repo model performance leaderboard)"
|
|
1365
|
-
echo ""
|
|
1366
|
-
# When init ran inside a worktree (check_protected_branch created one),
|
|
1367
|
-
# print explicit instructions so the user knows where to find their work.
|
|
1368
|
-
# Without this, the user's shell is back in the main repo after aidevops exits
|
|
1369
|
-
# and the worktree appears to have "disappeared".
|
|
1370
|
-
if [[ -n "${WORKTREE_PATH:-}" ]]; then
|
|
1371
|
-
local worktree_branch
|
|
1372
|
-
worktree_branch=$(git branch --show-current 2>/dev/null || echo "chore/aidevops-init")
|
|
1373
|
-
echo "Worktree location:"
|
|
1374
|
-
echo " $WORKTREE_PATH"
|
|
1375
|
-
echo ""
|
|
1376
|
-
echo "Your init commit is in the worktree above. To continue:"
|
|
1377
|
-
echo " cd $WORKTREE_PATH"
|
|
1378
|
-
echo " git push -u origin ${worktree_branch}"
|
|
1379
|
-
echo " gh pr create --fill" # aidevops-allow: raw-gh-wrapper
|
|
1380
|
-
echo ""
|
|
1381
|
-
fi
|
|
1382
|
-
echo "Next steps:"
|
|
1383
|
-
local step=1
|
|
1384
|
-
if [[ "$committed" != "true" ]]; then
|
|
1385
|
-
echo " ${step}. Commit the initialized files: git add -A && git commit -m 'chore: initialize aidevops'"
|
|
1386
|
-
((++step))
|
|
1387
|
-
fi
|
|
1388
|
-
if [[ "$enable_beads" == "true" ]]; then
|
|
1389
|
-
echo " ${step}. Add tasks to TODO.md with dependencies (blocked-by:t001)"
|
|
1390
|
-
((++step))
|
|
1391
|
-
echo " ${step}. Run /ready to see unblocked tasks"
|
|
1392
|
-
((++step))
|
|
1393
|
-
echo " ${step}. Run /sync-beads to sync with Beads graph"
|
|
1394
|
-
((++step))
|
|
1395
|
-
echo " ${step}. Use 'bd' CLI for graph visualization"
|
|
1396
|
-
elif [[ "$enable_database" == "true" ]]; then
|
|
1397
|
-
echo " ${step}. Add schema files to schemas/"
|
|
1398
|
-
((++step))
|
|
1399
|
-
echo " ${step}. Run diff to generate migrations"
|
|
1400
|
-
((++step))
|
|
1401
|
-
echo " ${step}. See .agents/workflows/sql-migrations.md"
|
|
1402
|
-
else
|
|
1403
|
-
echo " ${step}. Add tasks to TODO.md"
|
|
1404
|
-
((++step))
|
|
1405
|
-
echo " ${step}. Use /create-prd for complex features"
|
|
1406
|
-
((++step))
|
|
1407
|
-
echo " ${step}. Use /feature to start development"
|
|
1408
|
-
fi
|
|
1409
|
-
|
|
1410
|
-
return 0
|
|
1411
|
-
}
|