@svayam-opensource/prj 0.5.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/LICENSE +21 -0
- package/README.md +123 -0
- package/agent/harness-manifest.yaml +225 -0
- package/agent/session-protocol.md +116 -0
- package/bin/prj +21 -0
- package/package.json +41 -0
- package/prj +2381 -0
- package/scripts/add-repo.sh +126 -0
- package/scripts/cancel.sh +157 -0
- package/scripts/close-knowledge.sh +250 -0
- package/scripts/close-project.sh +233 -0
- package/scripts/create-task.sh +226 -0
- package/scripts/install-deps.sh +292 -0
- package/scripts/join.sh +89 -0
- package/scripts/lib.sh +841 -0
- package/scripts/merge-task.sh +163 -0
- package/scripts/onboard-repo.sh +275 -0
- package/scripts/pause.sh +80 -0
- package/scripts/project-access.sh +34 -0
- package/scripts/propose-knowledge.sh +168 -0
- package/scripts/release-to-public.sh +185 -0
- package/scripts/render-harness.sh +151 -0
- package/scripts/resume.sh +103 -0
- package/scripts/seed.sh +774 -0
- package/scripts/sync-from-publish.sh +193 -0
- package/scripts/sync.sh +90 -0
- package/scripts/test-merge.sh +100 -0
- package/scripts/validate/check_knowledge.py +158 -0
- package/scripts/validate/check_privacy.py +211 -0
- package/scripts/validate/check_protocol.py +117 -0
- package/scripts/validate/check_secrets.py +175 -0
- package/scripts/validate/run.py +391 -0
- package/setup.sh +529 -0
package/setup.sh
ADDED
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Agentic Development Framework — Organization Setup Script
|
|
3
|
+
#
|
|
4
|
+
# One-time interactive setup for your organization. Writes org-specific values
|
|
5
|
+
# to org-config.yaml and configures git remotes so future framework upgrades
|
|
6
|
+
# can be pulled from the upstream TEMPLATE.
|
|
7
|
+
#
|
|
8
|
+
# Framework files (CLAUDE.md, AGENTS.md, knowledge/policies/, etc.) are NEVER
|
|
9
|
+
# modified by this script — they reference values from org-config.yaml at
|
|
10
|
+
# runtime. This keeps `git pull template main` conflict-free forever.
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# bash setup.sh # interactive (default)
|
|
14
|
+
# bash setup.sh --non-interactive # re-use existing org-config.yaml values
|
|
15
|
+
# # (for CI / re-runs)
|
|
16
|
+
#
|
|
17
|
+
# Env escape hatches (testing):
|
|
18
|
+
# SETUP_SKIP_GITHUB_VERIFY=1 Skip the gh / scope checks.
|
|
19
|
+
# SETUP_SKIP_REMOTE_CONFIG=1 Skip rename/add of origin/template remotes.
|
|
20
|
+
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
NON_INTERACTIVE=false
|
|
24
|
+
[[ "${1:-}" == "--non-interactive" ]] && NON_INTERACTIVE=true
|
|
25
|
+
|
|
26
|
+
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
27
|
+
CONFIG="$REPO_ROOT/org-config.yaml"
|
|
28
|
+
|
|
29
|
+
# The framework's canonical upstream — used to seed the `template` remote.
|
|
30
|
+
TEMPLATE_REPO_URL="git@github.com:svayam-opensource/governed-agentic-dev-framework.git"
|
|
31
|
+
TEMPLATE_OWNER="svayam-opensource"
|
|
32
|
+
TEMPLATE_REPO="governed-agentic-dev-framework"
|
|
33
|
+
|
|
34
|
+
# ── Output helpers ────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
BOLD='\033[1m'; DIM='\033[2m'
|
|
37
|
+
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
|
|
38
|
+
|
|
39
|
+
ok() { echo -e "${GREEN} ✓${NC} $*"; }
|
|
40
|
+
warn() { echo -e "${YELLOW} !${NC} $*"; }
|
|
41
|
+
err() { echo -e "${RED} ✗${NC} $*" >&2; }
|
|
42
|
+
info() { echo -e "${CYAN} →${NC} $*"; }
|
|
43
|
+
header() { echo ""; echo -e "${BOLD}${CYAN}$*${NC}"; }
|
|
44
|
+
hard_stop() { echo ""; err "$*"; echo ""; exit 1; }
|
|
45
|
+
|
|
46
|
+
# ── Read with EOF handling ────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
# Helpers below use unique internal variable names (__rabort_val, __ask_val,
|
|
49
|
+
# __validated_val) to avoid shadowing the caller's __val via bash's dynamic
|
|
50
|
+
# scoping. printf -v writes to the closest local in scope — if both inner
|
|
51
|
+
# and outer scopes declare `local __val`, the inner wins and the value
|
|
52
|
+
# never propagates back to the caller.
|
|
53
|
+
|
|
54
|
+
_read_or_abort() {
|
|
55
|
+
local __varname="$1" __rabort_val
|
|
56
|
+
if ! IFS= read -r __rabort_val; then
|
|
57
|
+
echo ""
|
|
58
|
+
err "Aborted (no input)."
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
printf -v "$__varname" '%s' "$__rabort_val"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
ask() {
|
|
65
|
+
local __var="$1" __prompt="$2" __default="${3:-}" __ask_val
|
|
66
|
+
if [[ -n "$__default" ]]; then
|
|
67
|
+
printf " ${BOLD}%s${NC} ${DIM}[%s]${NC}: " "$__prompt" "$__default"
|
|
68
|
+
else
|
|
69
|
+
printf " ${BOLD}%s${NC}: " "$__prompt"
|
|
70
|
+
fi
|
|
71
|
+
_read_or_abort __ask_val
|
|
72
|
+
printf -v "$__var" '%s' "${__ask_val:-$__default}"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
ask_required() {
|
|
76
|
+
local __var="$1" __prompt="$2" __default="${3:-}" __validated_val
|
|
77
|
+
while true; do
|
|
78
|
+
ask __validated_val "$__prompt" "$__default"
|
|
79
|
+
if [[ -n "$__validated_val" ]]; then
|
|
80
|
+
printf -v "$__var" '%s' "$__validated_val"
|
|
81
|
+
return
|
|
82
|
+
fi
|
|
83
|
+
err "Required."
|
|
84
|
+
done
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
ask_slug() {
|
|
88
|
+
local __var="$1" __prompt="$2" __default="${3:-}" __validated_val
|
|
89
|
+
while true; do
|
|
90
|
+
ask __validated_val "$__prompt" "$__default"
|
|
91
|
+
if [[ "$__validated_val" =~ ^[A-Z][A-Z0-9]{1,5}$ ]]; then
|
|
92
|
+
printf -v "$__var" '%s' "$__validated_val"
|
|
93
|
+
return
|
|
94
|
+
fi
|
|
95
|
+
err "Must be 2-6 uppercase letters/digits, starting with a letter (e.g. ACME, NORDIC, SVM2)."
|
|
96
|
+
done
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
ask_date() {
|
|
100
|
+
local __var="$1" __prompt="$2" __default="${3:-}" __validated_val
|
|
101
|
+
while true; do
|
|
102
|
+
ask __validated_val "$__prompt" "$__default"
|
|
103
|
+
if [[ "$__validated_val" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
|
|
104
|
+
printf -v "$__var" '%s' "$__validated_val"
|
|
105
|
+
return
|
|
106
|
+
fi
|
|
107
|
+
err "Must be YYYY-MM-DD format."
|
|
108
|
+
done
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# Parse a GitHub URL into owner/repo. Accepts ssh and https forms (with or
|
|
112
|
+
# without .git suffix). Returns 0 on success and sets the two named vars.
|
|
113
|
+
parse_github_url() {
|
|
114
|
+
local url="$1" __owner_var="$2" __repo_var="$3"
|
|
115
|
+
local normalized="${url%.git}"
|
|
116
|
+
if [[ "$normalized" =~ github\.com[:/]([^/]+)/(.+)$ ]]; then
|
|
117
|
+
printf -v "$__owner_var" '%s' "${BASH_REMATCH[1]}"
|
|
118
|
+
printf -v "$__repo_var" '%s' "${BASH_REMATCH[2]}"
|
|
119
|
+
return 0
|
|
120
|
+
fi
|
|
121
|
+
return 1
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
ask_github_url() {
|
|
125
|
+
local __var="$1" __prompt="$2" __default="${3:-}" __validated_val __owner __repo
|
|
126
|
+
while true; do
|
|
127
|
+
ask __validated_val "$__prompt" "$__default"
|
|
128
|
+
if [[ -z "$__validated_val" ]]; then
|
|
129
|
+
err "Required."
|
|
130
|
+
continue
|
|
131
|
+
fi
|
|
132
|
+
if parse_github_url "$__validated_val" __owner __repo; then
|
|
133
|
+
printf -v "$__var" '%s' "$__validated_val"
|
|
134
|
+
return
|
|
135
|
+
fi
|
|
136
|
+
err "Expected a GitHub URL (git@github.com:owner/repo[.git] or https://github.com/owner/repo[.git])."
|
|
137
|
+
done
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
# ── Read existing org-config.yaml values ──────────────────────────────────────
|
|
141
|
+
|
|
142
|
+
read_yaml_field() {
|
|
143
|
+
local key="$1"
|
|
144
|
+
if command -v yq &>/dev/null; then
|
|
145
|
+
local v
|
|
146
|
+
v=$(yq ".$key" "$CONFIG" 2>/dev/null)
|
|
147
|
+
[[ "$v" == "null" ]] && echo "" || echo "$v"
|
|
148
|
+
else
|
|
149
|
+
python3 -c "import yaml; v = yaml.safe_load(open('$CONFIG')).get('$key', ''); print(v if v is not None else '')" 2>/dev/null
|
|
150
|
+
fi
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# ── Step 1: Pre-conditions ────────────────────────────────────────────────────
|
|
154
|
+
|
|
155
|
+
[[ -f "$CONFIG" ]] || hard_stop "org-config.yaml not found at $CONFIG"
|
|
156
|
+
|
|
157
|
+
cd "$REPO_ROOT"
|
|
158
|
+
|
|
159
|
+
if ! git rev-parse --git-dir &>/dev/null; then
|
|
160
|
+
hard_stop "Not in a git repository. Initialize first: git init"
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
ORIGIN_URL=$(git remote get-url origin 2>/dev/null || echo "")
|
|
164
|
+
if [[ -z "$ORIGIN_URL" ]]; then
|
|
165
|
+
hard_stop "No 'origin' remote configured. Set one up first:
|
|
166
|
+
git remote add origin <YOUR_ORG_REPO_URL>
|
|
167
|
+
Then re-run setup.sh."
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
ORIGIN_OWNER=""
|
|
171
|
+
ORIGIN_REPO=""
|
|
172
|
+
parse_github_url "$ORIGIN_URL" ORIGIN_OWNER ORIGIN_REPO \
|
|
173
|
+
|| hard_stop "Could not parse owner/repo from origin URL: $ORIGIN_URL"
|
|
174
|
+
|
|
175
|
+
# Detect "origin still points at the framework TEMPLATE" — adopter cloned
|
|
176
|
+
# TEMPLATE directly. We re-point origin at their org repo during setup
|
|
177
|
+
# (not a hard stop the way it used to be — setup itself does the fix).
|
|
178
|
+
ORIGIN_IS_TEMPLATE=false
|
|
179
|
+
if [[ "$ORIGIN_OWNER" == "$TEMPLATE_OWNER" && "$ORIGIN_REPO" == "$TEMPLATE_REPO" ]]; then
|
|
180
|
+
ORIGIN_IS_TEMPLATE=true
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
# ── Step 2: Read existing config + runtime detections ────────────────────────
|
|
184
|
+
|
|
185
|
+
CURRENT_ORG_NAME=$(read_yaml_field org_name)
|
|
186
|
+
CURRENT_ORG_SHORT=$(read_yaml_field org_short_name)
|
|
187
|
+
CURRENT_ORG_SLUG=$(read_yaml_field org_slug)
|
|
188
|
+
CURRENT_ORG_REPO_URL=$(read_yaml_field org_repo_url)
|
|
189
|
+
CURRENT_DEFAULT_BRANCH=$(read_yaml_field default_branch)
|
|
190
|
+
CURRENT_DEFAULT_CODE_BRANCH=$(read_yaml_field default_code_branch)
|
|
191
|
+
CURRENT_AGENT_WORK_ROOT=$(read_yaml_field agent_work_root)
|
|
192
|
+
CURRENT_POLICY_OWNER_EMAIL=$(read_yaml_field policy_owner_email)
|
|
193
|
+
CURRENT_POLICY_OWNER_GITHUB=$(read_yaml_field policy_owner_github)
|
|
194
|
+
CURRENT_LEGAL=$(read_yaml_field legal_owner_github)
|
|
195
|
+
CURRENT_INFRA=$(read_yaml_field infra_owner_github)
|
|
196
|
+
CURRENT_SYS_ARCH=$(read_yaml_field system_arch_owner_github)
|
|
197
|
+
CURRENT_DATA_ARCH=$(read_yaml_field data_arch_owner_github)
|
|
198
|
+
CURRENT_POLICY_DATE=$(read_yaml_field policy_effective_date)
|
|
199
|
+
|
|
200
|
+
GH_USER=$(gh api user --jq .login 2>/dev/null || echo "")
|
|
201
|
+
GIT_EMAIL=$(git config user.email 2>/dev/null || echo "")
|
|
202
|
+
TODAY=$(date +%Y-%m-%d)
|
|
203
|
+
|
|
204
|
+
# ── Step 3: Prompt (or skip in --non-interactive) ─────────────────────────────
|
|
205
|
+
|
|
206
|
+
if $NON_INTERACTIVE; then
|
|
207
|
+
warn "--non-interactive: using existing org-config.yaml values as-is."
|
|
208
|
+
[[ -n "$CURRENT_ORG_NAME" ]] \
|
|
209
|
+
|| hard_stop "org-config.yaml has no org_name. Run setup.sh interactively first."
|
|
210
|
+
ORG_NAME="$CURRENT_ORG_NAME"
|
|
211
|
+
ORG_SHORT_NAME="$CURRENT_ORG_SHORT"
|
|
212
|
+
ORG_SLUG="$CURRENT_ORG_SLUG"
|
|
213
|
+
ORG_SLUG_LOWER=$(echo "$ORG_SLUG" | tr '[:upper:]' '[:lower:]')
|
|
214
|
+
ORG_REPO_URL="${CURRENT_ORG_REPO_URL:-$ORIGIN_URL}"
|
|
215
|
+
parse_github_url "$ORG_REPO_URL" ORIGIN_OWNER ORIGIN_REPO || true
|
|
216
|
+
GITHUB_ORG="$ORIGIN_OWNER"
|
|
217
|
+
WORKSPACE_REPO="$ORIGIN_REPO"
|
|
218
|
+
DEFAULT_BRANCH="$CURRENT_DEFAULT_BRANCH"
|
|
219
|
+
DEFAULT_CODE_BRANCH="$CURRENT_DEFAULT_CODE_BRANCH"
|
|
220
|
+
AGENT_WORK_ROOT="$CURRENT_AGENT_WORK_ROOT"
|
|
221
|
+
# Store a PORTABLE tilde path, not the expanded $HOME — this value is
|
|
222
|
+
# committed to org-config.yaml and shared with the whole team. lib.sh
|
|
223
|
+
# expands the leading ~ per-developer at runtime.
|
|
224
|
+
[[ -n "$AGENT_WORK_ROOT" ]] || AGENT_WORK_ROOT="~/.${ORG_SLUG_LOWER}/projects"
|
|
225
|
+
POLICY_OWNER_EMAIL="$CURRENT_POLICY_OWNER_EMAIL"
|
|
226
|
+
POLICY_OWNER_GITHUB="$CURRENT_POLICY_OWNER_GITHUB"
|
|
227
|
+
LEGAL_OWNER_GITHUB="$CURRENT_LEGAL"
|
|
228
|
+
INFRA_OWNER_GITHUB="$CURRENT_INFRA"
|
|
229
|
+
SYSTEM_ARCH_OWNER_GITHUB="$CURRENT_SYS_ARCH"
|
|
230
|
+
DATA_ARCH_OWNER_GITHUB="$CURRENT_DATA_ARCH"
|
|
231
|
+
POLICY_EFFECTIVE_DATE="$CURRENT_POLICY_DATE"
|
|
232
|
+
else
|
|
233
|
+
echo ""
|
|
234
|
+
echo -e "${BOLD}Agentic Development Framework — Setup${NC}"
|
|
235
|
+
echo "─────────────────────────────────────────"
|
|
236
|
+
echo ""
|
|
237
|
+
echo " Current 'origin': ${ORIGIN_OWNER}/${ORIGIN_REPO}"
|
|
238
|
+
[[ -n "$GH_USER" ]] && echo " Detected gh user: ${GH_USER}"
|
|
239
|
+
[[ -n "$GIT_EMAIL" ]] && echo " Git user.email: ${GIT_EMAIL}"
|
|
240
|
+
echo ""
|
|
241
|
+
echo "Press Enter to accept the [default] for any prompt."
|
|
242
|
+
|
|
243
|
+
if $ORIGIN_IS_TEMPLATE; then
|
|
244
|
+
header "Your organization's repository"
|
|
245
|
+
echo " Your 'origin' remote points at the framework TEMPLATE:"
|
|
246
|
+
echo " $ORIGIN_URL"
|
|
247
|
+
echo ""
|
|
248
|
+
echo " Setup will re-point origin at your org's own repo and keep TEMPLATE"
|
|
249
|
+
echo " as a separate remote called 'template' for future framework"
|
|
250
|
+
echo " upgrades (git pull template main)."
|
|
251
|
+
echo ""
|
|
252
|
+
ask_github_url ORG_REPO_URL "Your org's repo URL (git@... or https://...)" "${CURRENT_ORG_REPO_URL:-}"
|
|
253
|
+
parse_github_url "$ORG_REPO_URL" ORIGIN_OWNER ORIGIN_REPO \
|
|
254
|
+
|| hard_stop "Internal error: just-validated URL no longer parses."
|
|
255
|
+
else
|
|
256
|
+
ORG_REPO_URL="${CURRENT_ORG_REPO_URL:-$ORIGIN_URL}"
|
|
257
|
+
fi
|
|
258
|
+
GITHUB_ORG="$ORIGIN_OWNER"
|
|
259
|
+
WORKSPACE_REPO="$ORIGIN_REPO"
|
|
260
|
+
|
|
261
|
+
header "Organization"
|
|
262
|
+
ask_required ORG_NAME "Full legal name of your organization" "$CURRENT_ORG_NAME"
|
|
263
|
+
ask_required ORG_SHORT_NAME "Short display name (used in headings)" "$CURRENT_ORG_SHORT"
|
|
264
|
+
ask_slug ORG_SLUG "Org slug (uppercase, 2-6 chars; e.g. ACME)" "$CURRENT_ORG_SLUG"
|
|
265
|
+
ORG_SLUG_LOWER=$(echo "$ORG_SLUG" | tr '[:upper:]' '[:lower:]')
|
|
266
|
+
echo ""
|
|
267
|
+
ok "org_slug: $ORG_SLUG"
|
|
268
|
+
ok "org_slug_lower: $ORG_SLUG_LOWER (auto-derived)"
|
|
269
|
+
ok "github_org: $ORIGIN_OWNER (from org repo URL)"
|
|
270
|
+
ok "workspace_repo: $ORIGIN_REPO (from org repo URL)"
|
|
271
|
+
|
|
272
|
+
header "Branches"
|
|
273
|
+
ask DEFAULT_BRANCH "Default branch for this workspace repo" "${CURRENT_DEFAULT_BRANCH:-main}"
|
|
274
|
+
ask DEFAULT_CODE_BRANCH "Default base branch for code repositories" "${CURRENT_DEFAULT_CODE_BRANCH:-dev}"
|
|
275
|
+
|
|
276
|
+
header "Agent work root"
|
|
277
|
+
echo " Per-project workspaces are created under this path. Each project"
|
|
278
|
+
echo " gets its own folder containing a clone of this repo on the project"
|
|
279
|
+
echo " branch plus clones of each impacted code repo on the project branch."
|
|
280
|
+
echo ""
|
|
281
|
+
ask AGENT_WORK_ROOT "Agent work root path" "${CURRENT_AGENT_WORK_ROOT:-~/.${ORG_SLUG_LOWER}/projects}"
|
|
282
|
+
|
|
283
|
+
header "Policy Owner (initial holder of all policy roles)"
|
|
284
|
+
ask_required POLICY_OWNER_EMAIL "Policy Owner email" "${CURRENT_POLICY_OWNER_EMAIL:-$GIT_EMAIL}"
|
|
285
|
+
ask_required POLICY_OWNER_GITHUB "Policy Owner GitHub @-handle" "${CURRENT_POLICY_OWNER_GITHUB:-${GH_USER:+@$GH_USER}}"
|
|
286
|
+
|
|
287
|
+
header "Domain Owners"
|
|
288
|
+
echo " Per the default policy, all domain roles are held by the Policy Owner"
|
|
289
|
+
echo " at launch. Press Enter to accept '$POLICY_OWNER_GITHUB' for each."
|
|
290
|
+
ask LEGAL_OWNER_GITHUB "Legal Owner" "${CURRENT_LEGAL:-$POLICY_OWNER_GITHUB}"
|
|
291
|
+
ask INFRA_OWNER_GITHUB "Infrastructure Owner" "${CURRENT_INFRA:-$POLICY_OWNER_GITHUB}"
|
|
292
|
+
ask SYSTEM_ARCH_OWNER_GITHUB "System Architecture Owner" "${CURRENT_SYS_ARCH:-$POLICY_OWNER_GITHUB}"
|
|
293
|
+
ask DATA_ARCH_OWNER_GITHUB "Data Architecture Owner" "${CURRENT_DATA_ARCH:-$POLICY_OWNER_GITHUB}"
|
|
294
|
+
|
|
295
|
+
header "Policy"
|
|
296
|
+
ask_date POLICY_EFFECTIVE_DATE "Policy effective date (YYYY-MM-DD)" "${CURRENT_POLICY_DATE:-$TODAY}"
|
|
297
|
+
|
|
298
|
+
echo ""
|
|
299
|
+
echo "Configured values:"
|
|
300
|
+
echo " org_name: $ORG_NAME"
|
|
301
|
+
echo " org_short_name: $ORG_SHORT_NAME"
|
|
302
|
+
echo " org_slug: $ORG_SLUG"
|
|
303
|
+
echo " org_repo_url: $ORG_REPO_URL"
|
|
304
|
+
echo " github_org: $GITHUB_ORG"
|
|
305
|
+
echo " workspace_repo: $WORKSPACE_REPO"
|
|
306
|
+
echo " default_branch: $DEFAULT_BRANCH"
|
|
307
|
+
echo " default_code_branch: $DEFAULT_CODE_BRANCH"
|
|
308
|
+
echo " agent_work_root: $AGENT_WORK_ROOT"
|
|
309
|
+
echo " policy_owner_email: $POLICY_OWNER_EMAIL"
|
|
310
|
+
echo " policy_owner_github: $POLICY_OWNER_GITHUB"
|
|
311
|
+
echo " legal_owner_github: $LEGAL_OWNER_GITHUB"
|
|
312
|
+
echo " infra_owner_github: $INFRA_OWNER_GITHUB"
|
|
313
|
+
echo " system_arch_owner_github: $SYSTEM_ARCH_OWNER_GITHUB"
|
|
314
|
+
echo " data_arch_owner_github: $DATA_ARCH_OWNER_GITHUB"
|
|
315
|
+
echo " policy_effective_date: $POLICY_EFFECTIVE_DATE"
|
|
316
|
+
echo ""
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
# ── Step 4: Write org-config.yaml ─────────────────────────────────────────────
|
|
320
|
+
|
|
321
|
+
cat > "$CONFIG" <<EOF
|
|
322
|
+
# Agentic Development Framework — Organization Configuration
|
|
323
|
+
#
|
|
324
|
+
# Single source of truth for this organization's identity, defaults, and roles.
|
|
325
|
+
# Framework scripts and agents read these values at runtime — no placeholder
|
|
326
|
+
# substitution happens, so this file is the only thing that diverges from
|
|
327
|
+
# the upstream framework template.
|
|
328
|
+
#
|
|
329
|
+
# Re-run ./setup.sh to update. Do not edit by hand unless you know what you're
|
|
330
|
+
# doing (setup.sh overwrites the file).
|
|
331
|
+
|
|
332
|
+
# Full legal name of your organization
|
|
333
|
+
org_name: "$ORG_NAME"
|
|
334
|
+
|
|
335
|
+
# Short display name (used in headings and prose)
|
|
336
|
+
org_short_name: "$ORG_SHORT_NAME"
|
|
337
|
+
|
|
338
|
+
# Uppercase slug for human display and multi-org disambiguation (2-6 chars).
|
|
339
|
+
# Not used in project IDs (which use a literal PRJ- prefix).
|
|
340
|
+
org_slug: "$ORG_SLUG"
|
|
341
|
+
|
|
342
|
+
# Lowercase derivation of org_slug — used for filesystem paths under
|
|
343
|
+
# agent_work_root. Auto-derived from org_slug.
|
|
344
|
+
org_slug_lower: "$ORG_SLUG_LOWER"
|
|
345
|
+
|
|
346
|
+
# Full URL of this workspace repository. 'origin' will be set to this.
|
|
347
|
+
org_repo_url: "$ORG_REPO_URL"
|
|
348
|
+
|
|
349
|
+
# GitHub organization or username (derived from org_repo_url)
|
|
350
|
+
github_org: "$GITHUB_ORG"
|
|
351
|
+
|
|
352
|
+
# Name of this workspace repository (derived from org_repo_url)
|
|
353
|
+
workspace_repo: "$WORKSPACE_REPO"
|
|
354
|
+
|
|
355
|
+
# Default branch name for this workspace repo
|
|
356
|
+
default_branch: "$DEFAULT_BRANCH"
|
|
357
|
+
|
|
358
|
+
# Default base branch for code repositories (used by seed script)
|
|
359
|
+
default_code_branch: "$DEFAULT_CODE_BRANCH"
|
|
360
|
+
|
|
361
|
+
# Per-project workspaces are created under this path. Each gets its own
|
|
362
|
+
# folder containing a clone of this workspace repo on the project branch
|
|
363
|
+
# plus clones of each impacted code repo on the project branch.
|
|
364
|
+
agent_work_root: "$AGENT_WORK_ROOT"
|
|
365
|
+
|
|
366
|
+
# Policy Owner details (initial holder of all policy roles at launch)
|
|
367
|
+
policy_owner_email: "$POLICY_OWNER_EMAIL"
|
|
368
|
+
policy_owner_github: "$POLICY_OWNER_GITHUB"
|
|
369
|
+
|
|
370
|
+
# Other role GitHub handles (update as roles are formally assigned)
|
|
371
|
+
legal_owner_github: "$LEGAL_OWNER_GITHUB"
|
|
372
|
+
infra_owner_github: "$INFRA_OWNER_GITHUB"
|
|
373
|
+
system_arch_owner_github: "$SYSTEM_ARCH_OWNER_GITHUB"
|
|
374
|
+
data_arch_owner_github: "$DATA_ARCH_OWNER_GITHUB"
|
|
375
|
+
|
|
376
|
+
# Effective date of the policy (YYYY-MM-DD)
|
|
377
|
+
policy_effective_date: "$POLICY_EFFECTIVE_DATE"
|
|
378
|
+
EOF
|
|
379
|
+
|
|
380
|
+
ok "Wrote $CONFIG"
|
|
381
|
+
|
|
382
|
+
# ── Step 5: Configure git remotes ─────────────────────────────────────────────
|
|
383
|
+
|
|
384
|
+
if $NON_INTERACTIVE || [[ "${SETUP_SKIP_REMOTE_CONFIG:-}" == "1" ]]; then
|
|
385
|
+
:
|
|
386
|
+
else
|
|
387
|
+
header "Configuring git remotes"
|
|
388
|
+
|
|
389
|
+
CURRENT_ORIGIN_URL=$(git remote get-url origin 2>/dev/null || echo "")
|
|
390
|
+
if [[ "$CURRENT_ORIGIN_URL" != "$ORG_REPO_URL" ]]; then
|
|
391
|
+
if $ORIGIN_IS_TEMPLATE; then
|
|
392
|
+
info "Renaming current 'origin' (TEMPLATE) → 'template'"
|
|
393
|
+
git remote get-url template &>/dev/null && git remote remove template
|
|
394
|
+
git remote rename origin template
|
|
395
|
+
info "Setting new 'origin' → $ORG_REPO_URL"
|
|
396
|
+
git remote add origin "$ORG_REPO_URL"
|
|
397
|
+
else
|
|
398
|
+
info "Updating 'origin' → $ORG_REPO_URL"
|
|
399
|
+
git remote set-url origin "$ORG_REPO_URL"
|
|
400
|
+
fi
|
|
401
|
+
else
|
|
402
|
+
ok "origin → $ORG_REPO_URL"
|
|
403
|
+
fi
|
|
404
|
+
|
|
405
|
+
if git remote get-url template &>/dev/null; then
|
|
406
|
+
CURRENT_TEMPLATE_URL=$(git remote get-url template)
|
|
407
|
+
if [[ "$CURRENT_TEMPLATE_URL" != "$TEMPLATE_REPO_URL" ]]; then
|
|
408
|
+
info "Updating 'template' → $TEMPLATE_REPO_URL"
|
|
409
|
+
git remote set-url template "$TEMPLATE_REPO_URL"
|
|
410
|
+
else
|
|
411
|
+
ok "template → $TEMPLATE_REPO_URL"
|
|
412
|
+
fi
|
|
413
|
+
else
|
|
414
|
+
info "Adding 'template' remote → $TEMPLATE_REPO_URL"
|
|
415
|
+
git remote add template "$TEMPLATE_REPO_URL"
|
|
416
|
+
fi
|
|
417
|
+
|
|
418
|
+
ok "Remotes:"
|
|
419
|
+
git remote -v | sed 's/^/ /'
|
|
420
|
+
fi
|
|
421
|
+
|
|
422
|
+
# ── Step 6: GitHub identity & access verification ────────────────────────────
|
|
423
|
+
|
|
424
|
+
if $NON_INTERACTIVE || [[ "${SETUP_SKIP_GITHUB_VERIFY:-}" == "1" ]]; then
|
|
425
|
+
:
|
|
426
|
+
else
|
|
427
|
+
header "Identity & GitHub access"
|
|
428
|
+
|
|
429
|
+
GIT_EMAIL=$(git config user.email 2>/dev/null || echo "")
|
|
430
|
+
if [[ -z "$GIT_EMAIL" ]]; then
|
|
431
|
+
warn "git config user.email is not set"
|
|
432
|
+
ask GIT_EMAIL "Your email for git commits" ""
|
|
433
|
+
if [[ -n "$GIT_EMAIL" ]]; then
|
|
434
|
+
git config --global user.email "$GIT_EMAIL" && ok "Set git user.email: $GIT_EMAIL"
|
|
435
|
+
else
|
|
436
|
+
hard_stop "git user.email is required: git config --global user.email 'you@example.com'"
|
|
437
|
+
fi
|
|
438
|
+
else
|
|
439
|
+
ok "git user.email: $GIT_EMAIL"
|
|
440
|
+
fi
|
|
441
|
+
|
|
442
|
+
GH_USER=$(gh api user --jq .login 2>/dev/null || echo "")
|
|
443
|
+
if [[ -z "$GH_USER" ]]; then
|
|
444
|
+
warn "Not logged in to the GitHub CLI (gh)."
|
|
445
|
+
if confirm_yn "Run 'gh auth login' now (recommended scopes will be requested)?"; then
|
|
446
|
+
gh auth login -h github.com -s "$GOV_SCOPES_CSV" || true
|
|
447
|
+
GH_USER=$(gh api user --jq .login 2>/dev/null || echo "")
|
|
448
|
+
fi
|
|
449
|
+
[[ -n "$GH_USER" ]] || hard_stop "Still not logged in. Run 'gh auth login' and re-run setup.sh."
|
|
450
|
+
fi
|
|
451
|
+
ok "gh user: $GH_USER"
|
|
452
|
+
|
|
453
|
+
if gh api "orgs/$GITHUB_ORG" &>/dev/null; then
|
|
454
|
+
ok "Read access to org '$GITHUB_ORG'"
|
|
455
|
+
elif gh api "users/$GITHUB_ORG" &>/dev/null; then
|
|
456
|
+
ok "'$GITHUB_ORG' is a user account (not an org) — accessible"
|
|
457
|
+
else
|
|
458
|
+
err "Cannot read '$GITHUB_ORG' — not found, or no access"
|
|
459
|
+
hard_stop "Verify github_org in org-config.yaml is correct, you are a member, and gh has 'read:org' scope:
|
|
460
|
+
gh auth refresh -h github.com -s read:org"
|
|
461
|
+
fi
|
|
462
|
+
|
|
463
|
+
SCOPES=$(gh auth status 2>&1 | grep -i "Token scopes" | head -1 | sed -E 's/.*Token scopes:[[:space:]]*//' | tr -d "'\"" || echo "")
|
|
464
|
+
if [[ -n "$SCOPES" ]]; then
|
|
465
|
+
ok "Token scopes: $SCOPES"
|
|
466
|
+
if ! echo "$SCOPES" | grep -qw "repo"; then
|
|
467
|
+
err "Missing required scope: repo"
|
|
468
|
+
hard_stop "Refresh: gh auth refresh -h github.com -s repo"
|
|
469
|
+
fi
|
|
470
|
+
if [[ "$GITHUB_ORG_TYPE" == "org" ]] && ! echo "$SCOPES" | grep -qw "read:org"; then
|
|
471
|
+
warn "Scope 'read:org' not detected — some org operations may fail"
|
|
472
|
+
echo " Refresh: gh auth refresh -h github.com -s read:org"
|
|
473
|
+
fi
|
|
474
|
+
else
|
|
475
|
+
warn "Could not determine token scopes — assuming sufficient"
|
|
476
|
+
fi
|
|
477
|
+
|
|
478
|
+
if git ls-remote origin HEAD &>/dev/null; then
|
|
479
|
+
ok "origin reachable"
|
|
480
|
+
else
|
|
481
|
+
warn "Could not contact 'origin' remote — verify with: git remote -v"
|
|
482
|
+
fi
|
|
483
|
+
if git ls-remote template HEAD &>/dev/null; then
|
|
484
|
+
ok "template reachable"
|
|
485
|
+
else
|
|
486
|
+
warn "Could not contact 'template' remote — verify with: git remote -v"
|
|
487
|
+
fi
|
|
488
|
+
fi
|
|
489
|
+
|
|
490
|
+
# ── Step 7: Bootstrap current user's preferences file ────────────────────────
|
|
491
|
+
#
|
|
492
|
+
# Per-user preferences live at $AGENT_WORK_ROOT/preferences/<gh-login>.md
|
|
493
|
+
# (POL-127). Copy the template here so the developer has a starting point.
|
|
494
|
+
# Never overwrite an existing file. Skip silently if no gh login is available.
|
|
495
|
+
#
|
|
496
|
+
# NOTE (#65/H6): this previously referenced an undefined $PRJ_GOV_LOC, which
|
|
497
|
+
# aborted setup.sh under `set -u`. AGENT_WORK_ROOT is resolved above (with a
|
|
498
|
+
# default); expand a leading ~ against $HOME so prefs land outside the repo.
|
|
499
|
+
|
|
500
|
+
PREFS_LOGIN=$(gh api user --jq .login 2>/dev/null || echo "")
|
|
501
|
+
PREFS_TEMPLATE="$REPO_ROOT/knowledge/guidance/preferences-template.md"
|
|
502
|
+
if [[ -n "$PREFS_LOGIN" ]] && [[ -f "$PREFS_TEMPLATE" ]]; then
|
|
503
|
+
PREFS_DIR="${AGENT_WORK_ROOT/#\~/$HOME}/preferences"
|
|
504
|
+
PREFS_FILE="$PREFS_DIR/$PREFS_LOGIN.md"
|
|
505
|
+
mkdir -p "$PREFS_DIR"
|
|
506
|
+
if [[ -f "$PREFS_FILE" ]]; then
|
|
507
|
+
ok "Preferences: $PREFS_FILE (kept existing)"
|
|
508
|
+
else
|
|
509
|
+
cp "$PREFS_TEMPLATE" "$PREFS_FILE"
|
|
510
|
+
ok "Preferences: $PREFS_FILE (created from template)"
|
|
511
|
+
fi
|
|
512
|
+
else
|
|
513
|
+
warn "Could not bootstrap preferences file (gh login unavailable)."
|
|
514
|
+
warn "It will be auto-created on first prj write op once gh auth is configured."
|
|
515
|
+
fi
|
|
516
|
+
|
|
517
|
+
# ── Done ──────────────────────────────────────────────────────────────────────
|
|
518
|
+
|
|
519
|
+
echo ""
|
|
520
|
+
echo -e "${BOLD}${GREEN}Configured framework for: $ORG_NAME ($ORG_SLUG)${NC}"
|
|
521
|
+
echo ""
|
|
522
|
+
echo "Next steps:"
|
|
523
|
+
echo " 1. Review changes: git diff org-config.yaml"
|
|
524
|
+
echo " 2. Commit + push: git add org-config.yaml && git commit -m 'configure framework for $ORG_NAME' && git push origin $DEFAULT_BRANCH"
|
|
525
|
+
echo " 3. Edit preferences: ${PREFS_FILE:-<agent_work_root>/preferences/<your-gh-login>.md}"
|
|
526
|
+
echo " 4. Start using: ./prj"
|
|
527
|
+
echo ""
|
|
528
|
+
echo " Framework upgrades: git fetch template && git merge template/$DEFAULT_BRANCH"
|
|
529
|
+
echo ""
|