projecta-rrr 1.24.0 → 1.24.2
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/AGENTS.md +6 -1
- package/CHANGELOG.md +15 -0
- package/README.md +3 -0
- package/commands/rrr/coordinator-status.md +37 -0
- package/commands/rrr/help.md +5 -0
- package/commands/rrr/provision-coordinator.md +39 -0
- package/commands/rrr/provision-sprites.md +41 -0
- package/commands/rrr/runtime-init.md +39 -0
- package/commands/rrr/sprite-status.md +38 -0
- package/docs/team-mode-github-sprites.md +22 -5
- package/package.json +1 -1
- package/rrr/lib/team-mode/manager.js +478 -0
- package/scripts/rrr-team-mode.js +84 -1
package/AGENTS.md
CHANGED
|
@@ -41,6 +41,7 @@ The RRR loop follows five steps:
|
|
|
41
41
|
| `$rrr-check-version` | Check version consistency across package.json, CHANGELOG, and planning docs |
|
|
42
42
|
| `$rrr-complete-milestone` | Archive completed milestone and prepare for next version |
|
|
43
43
|
| `$rrr-coordinate-merge` | Generate a GitHub-native coordinator merge readiness report |
|
|
44
|
+
| `$rrr-coordinator-status` | Check Cloudflare coordinator readiness for RRR team mode |
|
|
44
45
|
| `$rrr-create-roadmap` | Create roadmap with phases for the project |
|
|
45
46
|
| `$rrr-debug` | Systematic debugging with persistent state across context resets |
|
|
46
47
|
| `$rrr-define-requirements` | Define what "done" looks like with checkable requirements |
|
|
@@ -69,13 +70,17 @@ The RRR loop follows five steps:
|
|
|
69
70
|
| `$rrr-plan-milestone-gaps` | Create phases to close all gaps identified by milestone audit |
|
|
70
71
|
| `$rrr-plan-phase` | Create detailed execution plan for a phase (PLAN.md) with verification loop |
|
|
71
72
|
| `$rrr-progress` | Check project progress, show context, and route to next action (execute or plan) |
|
|
73
|
+
| `$rrr-provision-coordinator` | Create the Cloudflare coordinator contract for RRR team mode integration review |
|
|
74
|
+
| `$rrr-provision-sprites` | Generate or apply Sprite provisioning for assigned RRR team workstreams |
|
|
72
75
|
| `$rrr-remove-phase` | Remove a future phase from roadmap and renumber subsequent phases |
|
|
73
76
|
| `$rrr-research-phase` | Research how to implement a phase (standalone - usually use /rrr:plan-phase instead) |
|
|
74
77
|
| `$rrr-research-project` | Research domain ecosystem before creating roadmap |
|
|
75
78
|
| `$rrr-resume-work` | Resume work from previous session with full context restoration |
|
|
79
|
+
| `$rrr-runtime-init` | Create a repo-specific runtime manifest for team mode setup, checks, and UAT |
|
|
76
80
|
| `$rrr-savings` | Show token-savings report (session + lifetime) with tier distribution and Opus-rate dashboard |
|
|
77
81
|
| `$rrr-search-skills` | Search for skills on skillsmp.com marketplace |
|
|
78
82
|
| `$rrr-ship` | Create PR from verified work with planning context, or generate SHIP-READY.md |
|
|
83
|
+
| `$rrr-sprite-status` | Check planned or live Sprite readiness for RRR team mode workstreams |
|
|
79
84
|
| `$rrr-submit-phase` | Submit a team-owned phase for coordinator integration review |
|
|
80
85
|
| `$rrr-team-init` | Initialize GitHub-native RRR team mode for a repo pilot |
|
|
81
86
|
| `$rrr-team-status` | Show RRR team-mode phase assignments and submission readiness |
|
|
@@ -91,4 +96,4 @@ The RRR loop follows five steps:
|
|
|
91
96
|
- Use `$rrr-help` for full skill catalogue
|
|
92
97
|
- Trigger phrases are case-sensitive: `$rrr-plan-phase` not `$rrr-PlanPhase`
|
|
93
98
|
|
|
94
|
-
<!-- generated: 2026-05-
|
|
99
|
+
<!-- generated: 2026-05-12T23:35:46.267Z | source: commands/rrr/*.md | count: 59 skills -->
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ All notable changes to RRR will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## [1.24.2] - 2026-05-12
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
- **Sprite exec apply path** — provisioning now inserts `--` before remote commands so Sprite does not parse `bash -lc`, `npx --global`, or setup flags as Sprite CLI flags.
|
|
11
|
+
|
|
12
|
+
## [1.24.1] - 2026-05-12
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- **Runtime manifest** — added `$rrr-runtime-init` to create `.planning/RUNTIME.json` with repo-local setup, check, browser UAT, and workdir commands for reusable sandboxes.
|
|
16
|
+
- **Sprite provisioning** — added `$rrr-provision-sprites` and `$rrr-sprite-status` to generate/apply per-team Sprite setup plans without repo-specific images.
|
|
17
|
+
- **Coordinator provisioning** — added `$rrr-provision-coordinator` and `$rrr-coordinator-status` for report-first Cloudflare Sandbox coordinator contracts and readiness checks.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
- **Team mode SOP** — documented the reusable org-level image model: one developer Sprite runtime plus one Cloudflare coordinator runtime, with repo specifics supplied by `.planning/RUNTIME.json`.
|
|
21
|
+
|
|
7
22
|
## [1.24.0] - 2026-05-12
|
|
8
23
|
|
|
9
24
|
### Added
|
package/README.md
CHANGED
|
@@ -113,7 +113,10 @@ RRR can pilot GitHub-native team execution where the coordinator assigns phases
|
|
|
113
113
|
|
|
114
114
|
```bash
|
|
115
115
|
/rrr:team-init --github-org PA-MATRIX --milestone v1.24 --runner sprites
|
|
116
|
+
/rrr:runtime-init
|
|
116
117
|
/rrr:assign-phases --team team-runtime --phases 92,95
|
|
118
|
+
/rrr:provision-sprites
|
|
119
|
+
/rrr:provision-coordinator
|
|
117
120
|
/rrr:dispatch-team --team team-runtime
|
|
118
121
|
/rrr:team-status
|
|
119
122
|
```
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rrr:coordinator-status
|
|
3
|
+
description: Check Cloudflare coordinator readiness for RRR team mode
|
|
4
|
+
argument-hint: ""
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Bash
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<objective>
|
|
12
|
+
Check whether the current repo has the coordinator contract and local deployment prerequisites needed for Cloudflare Sandbox-based integration review.
|
|
13
|
+
</objective>
|
|
14
|
+
|
|
15
|
+
<process>
|
|
16
|
+
|
|
17
|
+
1. Run:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
node ~/.claude/rrr/scripts/rrr-team-mode.js coordinator-status
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Development source repo fallback:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
node scripts/rrr-team-mode.js coordinator-status
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
2. Write `.planning/coordinator/STATUS.md`.
|
|
30
|
+
3. Report missing runtime manifest, missing coordinator config, missing Wrangler, and missing local secrets.
|
|
31
|
+
|
|
32
|
+
</process>
|
|
33
|
+
|
|
34
|
+
<constraints>
|
|
35
|
+
- Do not print secret values.
|
|
36
|
+
- Do not deploy or mutate Cloudflare resources from this status command.
|
|
37
|
+
</constraints>
|
package/commands/rrr/help.md
CHANGED
|
@@ -128,7 +128,10 @@ RRR team mode keeps collaboration GitHub-native:
|
|
|
128
128
|
|
|
129
129
|
```
|
|
130
130
|
/rrr:team-init --github-org PA-MATRIX --milestone v1.24 --runner sprites
|
|
131
|
+
/rrr:runtime-init
|
|
131
132
|
/rrr:assign-phases --team team-runtime --phases 92,95
|
|
133
|
+
/rrr:provision-sprites
|
|
134
|
+
/rrr:provision-coordinator
|
|
132
135
|
/rrr:dispatch-team --team team-runtime
|
|
133
136
|
/rrr:team-status
|
|
134
137
|
```
|
|
@@ -145,6 +148,8 @@ RRR team mode keeps collaboration GitHub-native:
|
|
|
145
148
|
**Coordinator integration:**
|
|
146
149
|
|
|
147
150
|
```
|
|
151
|
+
/rrr:sprite-status --live
|
|
152
|
+
/rrr:coordinator-status
|
|
148
153
|
/rrr:coordinate-merge
|
|
149
154
|
/rrr:integration-report
|
|
150
155
|
```
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rrr:provision-coordinator
|
|
3
|
+
description: Create the Cloudflare coordinator contract for RRR team mode integration review
|
|
4
|
+
argument-hint: "[--provider cloudflare-sandbox] [--name <coordinator-name>]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Bash
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<objective>
|
|
12
|
+
Prepare the repo's coordinator contract so a reusable Cloudflare Claude Code/Sandbox coordinator can clone, merge, test, and report without owning developer work state.
|
|
13
|
+
</objective>
|
|
14
|
+
|
|
15
|
+
<process>
|
|
16
|
+
|
|
17
|
+
1. Read `.planning/team-mode.json` and `.planning/RUNTIME.json`.
|
|
18
|
+
2. Run:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
node ~/.claude/rrr/scripts/rrr-team-mode.js provision-coordinator {{RRR_ARGS}}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Development source repo fallback:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
node scripts/rrr-team-mode.js provision-coordinator {{RRR_ARGS}}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
3. Write `.planning/coordinator/COORDINATOR.json` and `.planning/coordinator/README.md`.
|
|
31
|
+
4. Report required secrets, integration branch, runtime manifest, and report-first merge contract.
|
|
32
|
+
|
|
33
|
+
</process>
|
|
34
|
+
|
|
35
|
+
<constraints>
|
|
36
|
+
- Coordinator is report-first; never auto-merge to the base branch.
|
|
37
|
+
- Developer work remains in team Sprites and team branches.
|
|
38
|
+
- Do not store secrets in planning files.
|
|
39
|
+
</constraints>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rrr:provision-sprites
|
|
3
|
+
description: Generate or apply Sprite provisioning for assigned RRR team workstreams
|
|
4
|
+
argument-hint: "[--team <team>|--teams a,b] [--apply]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Bash
|
|
9
|
+
- AskUserQuestion
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<objective>
|
|
13
|
+
Prepare one persistent Sprite per assigned team/workstream using reusable org-level tooling plus the repo's `.planning/RUNTIME.json`.
|
|
14
|
+
</objective>
|
|
15
|
+
|
|
16
|
+
<process>
|
|
17
|
+
|
|
18
|
+
1. Read `.planning/team-mode.json` and `.planning/RUNTIME.json`.
|
|
19
|
+
2. Generate `.planning/sprites/PROVISIONING.md` and `.planning/sprites/PROVISIONING.json`.
|
|
20
|
+
3. If `--apply` is present, create/update the Sprites and run bootstrap commands:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
node ~/.claude/rrr/scripts/rrr-team-mode.js provision-sprites {{RRR_ARGS}}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Development source repo fallback:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
node scripts/rrr-team-mode.js provision-sprites {{RRR_ARGS}}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
4. Report each team Sprite, branch, setup status, and checkpoint status.
|
|
33
|
+
|
|
34
|
+
</process>
|
|
35
|
+
|
|
36
|
+
<constraints>
|
|
37
|
+
- One Sprite per team/workstream.
|
|
38
|
+
- Do not share a single Sprite across teams for implementation work.
|
|
39
|
+
- Do not auto-run assigned phases; provisioning stops at clean-ready setup.
|
|
40
|
+
- Do not push branches or create PRs without explicit coordinator approval.
|
|
41
|
+
</constraints>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rrr:runtime-init
|
|
3
|
+
description: Create a repo-specific runtime manifest for team mode setup, checks, and UAT
|
|
4
|
+
argument-hint: "[--force] [--package-manager npm|pnpm|yarn|bun] [--setup a,b] [--checks a,b]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Bash
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<objective>
|
|
12
|
+
Create `.planning/RUNTIME.json`, the repo-specific bootstrap contract used by reusable Sprites and Cloudflare coordinator sandboxes.
|
|
13
|
+
</objective>
|
|
14
|
+
|
|
15
|
+
<process>
|
|
16
|
+
|
|
17
|
+
1. Read `.planning/STATE.md` and inspect package manager files.
|
|
18
|
+
2. Infer setup/check commands from lockfiles and `package.json` scripts.
|
|
19
|
+
3. Run:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
node ~/.claude/rrr/scripts/rrr-team-mode.js runtime-init {{RRR_ARGS}}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Development source repo fallback:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
node scripts/rrr-team-mode.js runtime-init {{RRR_ARGS}}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
4. Report the manifest path, setup commands, checks, and browser UAT mode.
|
|
32
|
+
|
|
33
|
+
</process>
|
|
34
|
+
|
|
35
|
+
<constraints>
|
|
36
|
+
- Do not hardcode repository names.
|
|
37
|
+
- Do not put secrets in `.planning/RUNTIME.json`.
|
|
38
|
+
- Keep the manifest small and reviewable.
|
|
39
|
+
</constraints>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rrr:sprite-status
|
|
3
|
+
description: Check planned or live Sprite readiness for RRR team mode workstreams
|
|
4
|
+
argument-hint: "[--live] [--team <team>|--teams a,b]"
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- Read
|
|
7
|
+
- Write
|
|
8
|
+
- Bash
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
<objective>
|
|
12
|
+
Verify that assigned team Sprites exist and, with `--live`, that each Sprite has the expected repo checkout and branch.
|
|
13
|
+
</objective>
|
|
14
|
+
|
|
15
|
+
<process>
|
|
16
|
+
|
|
17
|
+
1. Read `.planning/team-mode.json`.
|
|
18
|
+
2. Run:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
node ~/.claude/rrr/scripts/rrr-team-mode.js sprite-status {{RRR_ARGS}}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Development source repo fallback:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
node scripts/rrr-team-mode.js sprite-status {{RRR_ARGS}}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
3. Write `.planning/sprites/STATUS.md`.
|
|
31
|
+
4. Report missing Sprites, wrong branches, missing Claude Code, missing GSD Browser, or missing repo checkout.
|
|
32
|
+
|
|
33
|
+
</process>
|
|
34
|
+
|
|
35
|
+
<constraints>
|
|
36
|
+
- `--live` can wake Sprites and run remote commands.
|
|
37
|
+
- Do not destroy or restore Sprites from this command.
|
|
38
|
+
</constraints>
|
|
@@ -60,13 +60,19 @@ RRR owns planning, execution guidance, verification artifacts, and coordinator r
|
|
|
60
60
|
$rrr-team-init --github-org PA-MATRIX --milestone v1.24 --runner sprites --teams team-platform,team-runtime,team-ux
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
3. Create
|
|
63
|
+
3. Create the repo runtime manifest:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
$rrr-runtime-init
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
4. Create or review the roadmap:
|
|
64
70
|
|
|
65
71
|
```bash
|
|
66
72
|
$rrr-create-roadmap
|
|
67
73
|
```
|
|
68
74
|
|
|
69
|
-
|
|
75
|
+
5. Assign phases:
|
|
70
76
|
|
|
71
77
|
```bash
|
|
72
78
|
$rrr-assign-phases --team team-platform --phases 91,94
|
|
@@ -74,7 +80,14 @@ RRR owns planning, execution guidance, verification artifacts, and coordinator r
|
|
|
74
80
|
$rrr-assign-phases --team team-ux --phases 93
|
|
75
81
|
```
|
|
76
82
|
|
|
77
|
-
|
|
83
|
+
6. Prepare reusable runtime endpoints:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
$rrr-provision-sprites
|
|
87
|
+
$rrr-provision-coordinator
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
7. Dispatch teams:
|
|
78
91
|
|
|
79
92
|
```bash
|
|
80
93
|
$rrr-dispatch-team --team team-platform
|
|
@@ -82,13 +95,15 @@ RRR owns planning, execution guidance, verification artifacts, and coordinator r
|
|
|
82
95
|
$rrr-dispatch-team --team team-ux
|
|
83
96
|
```
|
|
84
97
|
|
|
85
|
-
|
|
98
|
+
8. Monitor:
|
|
86
99
|
|
|
87
100
|
```bash
|
|
88
101
|
$rrr-team-status
|
|
102
|
+
$rrr-sprite-status --live
|
|
103
|
+
$rrr-coordinator-status
|
|
89
104
|
```
|
|
90
105
|
|
|
91
|
-
|
|
106
|
+
9. Coordinate integration:
|
|
92
107
|
|
|
93
108
|
```bash
|
|
94
109
|
$rrr-coordinate-merge
|
|
@@ -136,6 +151,8 @@ Use a base Sprite image/template with:
|
|
|
136
151
|
- GSD Browser
|
|
137
152
|
- Browser/system dependencies required by the repo
|
|
138
153
|
|
|
154
|
+
The base Sprite should be reusable across repos. Repo-specific setup belongs in `.planning/RUNTIME.json`, created by `$rrr-runtime-init`.
|
|
155
|
+
|
|
139
156
|
Recommended lifecycle:
|
|
140
157
|
|
|
141
158
|
1. Create one Sprite per team/workstream.
|
package/package.json
CHANGED
|
@@ -73,10 +73,477 @@ function spriteFor(repoName, milestone, team) {
|
|
|
73
73
|
return `rrr-${base}`;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
function normalizeList(value) {
|
|
77
|
+
if (!value) return [];
|
|
78
|
+
if (Array.isArray(value)) return value.map(String).map(s => s.trim()).filter(Boolean);
|
|
79
|
+
return String(value).split(',').map(s => s.trim()).filter(Boolean);
|
|
80
|
+
}
|
|
81
|
+
|
|
76
82
|
function teamSlug(raw) {
|
|
77
83
|
return requireTeamName(raw.replace(/^@?[^/]+\//, ''));
|
|
78
84
|
}
|
|
79
85
|
|
|
86
|
+
function runtimeManifestPath(projectRoot) {
|
|
87
|
+
return path.join(projectRoot, '.planning', 'RUNTIME.json');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function readPackageJson(projectRoot) {
|
|
91
|
+
const packagePath = path.join(projectRoot, 'package.json');
|
|
92
|
+
if (!fs.existsSync(packagePath)) return null;
|
|
93
|
+
return readJson(packagePath, null);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function detectPackageManager(projectRoot) {
|
|
97
|
+
if (fs.existsSync(path.join(projectRoot, 'pnpm-lock.yaml'))) return 'pnpm';
|
|
98
|
+
if (fs.existsSync(path.join(projectRoot, 'yarn.lock'))) return 'yarn';
|
|
99
|
+
if (fs.existsSync(path.join(projectRoot, 'bun.lockb')) || fs.existsSync(path.join(projectRoot, 'bun.lock'))) return 'bun';
|
|
100
|
+
if (fs.existsSync(path.join(projectRoot, 'package-lock.json')) || fs.existsSync(path.join(projectRoot, 'npm-shrinkwrap.json'))) return 'npm';
|
|
101
|
+
if (fs.existsSync(path.join(projectRoot, 'package.json'))) return 'npm';
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function packageCommand(packageManager, script) {
|
|
106
|
+
if (packageManager === 'pnpm') return `pnpm ${script}`;
|
|
107
|
+
if (packageManager === 'yarn') return `yarn ${script}`;
|
|
108
|
+
if (packageManager === 'bun') return `bun ${script}`;
|
|
109
|
+
return `npm ${script}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function installCommand(projectRoot, packageManager) {
|
|
113
|
+
if (!packageManager) return null;
|
|
114
|
+
if (packageManager === 'pnpm') return 'pnpm install --frozen-lockfile';
|
|
115
|
+
if (packageManager === 'yarn') return 'yarn install --frozen-lockfile';
|
|
116
|
+
if (packageManager === 'bun') return 'bun install --frozen-lockfile';
|
|
117
|
+
if (fs.existsSync(path.join(projectRoot, 'package-lock.json')) || fs.existsSync(path.join(projectRoot, 'npm-shrinkwrap.json'))) {
|
|
118
|
+
return 'npm ci';
|
|
119
|
+
}
|
|
120
|
+
return 'npm install';
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function inferRuntimeManifest(projectRoot, options) {
|
|
124
|
+
const opts = options || {};
|
|
125
|
+
const packageJson = readPackageJson(projectRoot);
|
|
126
|
+
const scripts = packageJson && packageJson.scripts ? packageJson.scripts : {};
|
|
127
|
+
const packageManager = opts.packageManager || detectPackageManager(projectRoot);
|
|
128
|
+
const setup = normalizeList(opts.setup);
|
|
129
|
+
const checks = normalizeList(opts.checks);
|
|
130
|
+
const install = installCommand(projectRoot, packageManager);
|
|
131
|
+
|
|
132
|
+
if (setup.length === 0 && install) setup.push(install);
|
|
133
|
+
if (checks.length === 0) {
|
|
134
|
+
for (const scriptName of ['lint', 'test', 'build']) {
|
|
135
|
+
if (scripts[scriptName]) checks.push(packageCommand(packageManager, `run ${scriptName}`));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const browserUatScript = scripts['browser:uat'] ? packageCommand(packageManager, 'run browser:uat') : null;
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
schema_version: 1,
|
|
143
|
+
generated_at: new Date().toISOString(),
|
|
144
|
+
package_manager: packageManager,
|
|
145
|
+
workdir: opts.workdir || null,
|
|
146
|
+
setup,
|
|
147
|
+
checks,
|
|
148
|
+
uat: {
|
|
149
|
+
browser: opts.browser || 'gsd',
|
|
150
|
+
command: opts.uatCommand || browserUatScript
|
|
151
|
+
},
|
|
152
|
+
tools: {
|
|
153
|
+
developer_sprite: ['git', 'gh', 'node', 'npm', 'claude', 'projecta-rrr', 'gsd-browser'],
|
|
154
|
+
coordinator: ['git', 'gh', 'node', 'npm', 'claude', 'projecta-rrr']
|
|
155
|
+
},
|
|
156
|
+
notes: [
|
|
157
|
+
'This manifest is repo-specific. Keep Sprite and Cloudflare coordinator images reusable.',
|
|
158
|
+
'Team/developer commands must pass --team <team> in team mode.'
|
|
159
|
+
]
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function writeRuntimeManifest(options) {
|
|
164
|
+
const opts = options || {};
|
|
165
|
+
const projectRoot = opts.projectRoot || process.cwd();
|
|
166
|
+
const filePath = runtimeManifestPath(projectRoot);
|
|
167
|
+
if (fs.existsSync(filePath) && !opts.force) {
|
|
168
|
+
return { filePath, manifest: readJson(filePath, null), created: false };
|
|
169
|
+
}
|
|
170
|
+
const manifest = inferRuntimeManifest(projectRoot, opts);
|
|
171
|
+
writeJson(filePath, manifest);
|
|
172
|
+
return { filePath, manifest, created: true };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function readRuntimeManifest(projectRoot) {
|
|
176
|
+
const filePath = runtimeManifestPath(projectRoot || process.cwd());
|
|
177
|
+
const manifest = readJson(filePath, null);
|
|
178
|
+
return manifest || inferRuntimeManifest(projectRoot || process.cwd(), {});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function repoNameFor(config) {
|
|
182
|
+
if (config.github && config.github.repo) return config.github.repo.split('/').pop();
|
|
183
|
+
return path.basename(config.project_root);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function repoCloneFor(config) {
|
|
187
|
+
if (config.github && config.github.repo) return config.github.repo;
|
|
188
|
+
return detectRepo(config.project_root);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function spriteWorkdir(config, runtime) {
|
|
192
|
+
return runtime.workdir || `/home/sprite/work/${repoNameFor(config)}`;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function buildSpriteProvisionPlan(projectRoot, options) {
|
|
196
|
+
const opts = options || {};
|
|
197
|
+
const config = loadConfig(projectRoot);
|
|
198
|
+
const runtime = readRuntimeManifest(config.project_root);
|
|
199
|
+
const repo = repoCloneFor(config);
|
|
200
|
+
const workdir = spriteWorkdir(config, runtime);
|
|
201
|
+
const selectedTeams = normalizeList(opts.teams || opts.team);
|
|
202
|
+
const teams = selectedTeams.length > 0 ? selectedTeams : Object.keys(config.teams || {});
|
|
203
|
+
const rows = [];
|
|
204
|
+
|
|
205
|
+
for (const rawTeam of teams) {
|
|
206
|
+
const team = requireTeamName(rawTeam);
|
|
207
|
+
const teamConfig = config.teams[team];
|
|
208
|
+
if (!teamConfig) throw new Error(`unknown team: ${team}`);
|
|
209
|
+
if (!teamConfig.sprite) throw new Error(`team ${team} does not have a Sprite name`);
|
|
210
|
+
|
|
211
|
+
const setup = runtime.setup || [];
|
|
212
|
+
const checks = runtime.checks || [];
|
|
213
|
+
const cloneCommand = repo
|
|
214
|
+
? `gh repo clone ${repo} ${workdir}`
|
|
215
|
+
: `git clone <repo-url> ${workdir}`;
|
|
216
|
+
const checkoutCommand = `git fetch origin && git checkout -B ${teamConfig.branch} origin/${config.github.base_branch}`;
|
|
217
|
+
|
|
218
|
+
rows.push({
|
|
219
|
+
team,
|
|
220
|
+
github_team: teamConfig.github_team,
|
|
221
|
+
phases: teamConfig.phases || [],
|
|
222
|
+
sprite: teamConfig.sprite,
|
|
223
|
+
branch: teamConfig.branch,
|
|
224
|
+
workdir,
|
|
225
|
+
repo,
|
|
226
|
+
setup,
|
|
227
|
+
checks,
|
|
228
|
+
commands: [
|
|
229
|
+
`sprite create ${teamConfig.sprite} --skip-console`,
|
|
230
|
+
`sprite exec -s ${teamConfig.sprite} -- mkdir -p /home/sprite/work`,
|
|
231
|
+
`sprite exec -s ${teamConfig.sprite} -- bash -lc 'test -d ${workdir}/.git || ${cloneCommand}'`,
|
|
232
|
+
`sprite exec -s ${teamConfig.sprite} --dir ${workdir} -- bash -lc '${checkoutCommand}'`,
|
|
233
|
+
`sprite exec -s ${teamConfig.sprite} --dir ${workdir} -- npx projecta-rrr@latest --global --yes`,
|
|
234
|
+
...setup.map(cmd => `sprite exec -s ${teamConfig.sprite} --dir ${workdir} -- bash -lc '${cmd}'`),
|
|
235
|
+
`sprite checkpoint create -s ${teamConfig.sprite} --comment "rrr clean-ready ${config.milestone} ${team}"`
|
|
236
|
+
]
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return { config, runtime, workdir, rows };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function renderSpriteProvisionPlan(plan) {
|
|
244
|
+
const lines = [
|
|
245
|
+
`# ${plan.config.milestone} Sprite Provisioning`,
|
|
246
|
+
'',
|
|
247
|
+
`Generated: ${new Date().toISOString()}`,
|
|
248
|
+
`Repo: ${plan.config.github.repo || '(unset)'}`,
|
|
249
|
+
`Base branch: ${plan.config.github.base_branch}`,
|
|
250
|
+
'',
|
|
251
|
+
'## Runtime',
|
|
252
|
+
'',
|
|
253
|
+
`Package manager: ${plan.runtime.package_manager || '(none detected)'}`,
|
|
254
|
+
`Workdir: ${plan.workdir}`,
|
|
255
|
+
`Browser UAT: ${plan.runtime.uat && plan.runtime.uat.browser ? plan.runtime.uat.browser : 'gsd'}`,
|
|
256
|
+
'',
|
|
257
|
+
'## Workstreams',
|
|
258
|
+
'',
|
|
259
|
+
'| Team | Phases | Sprite | Branch |',
|
|
260
|
+
'| ---- | ------ | ------ | ------ |'
|
|
261
|
+
];
|
|
262
|
+
|
|
263
|
+
for (const row of plan.rows) {
|
|
264
|
+
lines.push(`| ${row.team} | ${row.phases.join(', ') || '-'} | ${row.sprite} | ${row.branch} |`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
lines.push('', '## Commands', '');
|
|
268
|
+
for (const row of plan.rows) {
|
|
269
|
+
lines.push(`### ${row.team}`, '');
|
|
270
|
+
lines.push('```bash');
|
|
271
|
+
for (const command of row.commands) lines.push(command);
|
|
272
|
+
lines.push('```', '');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
lines.push(
|
|
276
|
+
'## Notes',
|
|
277
|
+
'',
|
|
278
|
+
'- One Sprite per team/workstream.',
|
|
279
|
+
'- The base Sprite tooling is repo-agnostic; this repo supplies setup/check commands through `.planning/RUNTIME.json`.',
|
|
280
|
+
'- Developers should run RRR with `--team <team>` inside the Sprite.'
|
|
281
|
+
);
|
|
282
|
+
return `${lines.join('\n')}\n`;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function writeSpriteProvisionPlan(projectRoot, options) {
|
|
286
|
+
const plan = buildSpriteProvisionPlan(projectRoot, options);
|
|
287
|
+
const dir = path.join(plan.config.project_root, '.planning', 'sprites');
|
|
288
|
+
ensureDir(dir);
|
|
289
|
+
const mdPath = path.join(dir, 'PROVISIONING.md');
|
|
290
|
+
const jsonPath = path.join(dir, 'PROVISIONING.json');
|
|
291
|
+
atomicWrite(mdPath, renderSpriteProvisionPlan(plan));
|
|
292
|
+
writeJson(jsonPath, {
|
|
293
|
+
schema_version: 1,
|
|
294
|
+
generated_at: new Date().toISOString(),
|
|
295
|
+
milestone: plan.config.milestone,
|
|
296
|
+
repo: plan.config.github.repo,
|
|
297
|
+
workdir: plan.workdir,
|
|
298
|
+
runtime: plan.runtime,
|
|
299
|
+
rows: plan.rows
|
|
300
|
+
});
|
|
301
|
+
return { plan, mdPath, jsonPath };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function execCapture(command, args, options) {
|
|
305
|
+
try {
|
|
306
|
+
const output = execFileSync(command, args, {
|
|
307
|
+
cwd: options && options.cwd ? options.cwd : process.cwd(),
|
|
308
|
+
encoding: 'utf8',
|
|
309
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
310
|
+
});
|
|
311
|
+
return { ok: true, output: output.trim() };
|
|
312
|
+
} catch (err) {
|
|
313
|
+
const stdout = err.stdout ? String(err.stdout) : '';
|
|
314
|
+
const stderr = err.stderr ? String(err.stderr) : '';
|
|
315
|
+
return { ok: false, output: `${stdout}${stderr}`.trim(), code: err.status || 1 };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function applySpriteProvisionPlan(projectRoot, options) {
|
|
320
|
+
const plan = buildSpriteProvisionPlan(projectRoot, options);
|
|
321
|
+
const results = [];
|
|
322
|
+
for (const row of plan.rows) {
|
|
323
|
+
const teamResults = [];
|
|
324
|
+
const create = execCapture('sprite', ['create', row.sprite, '--skip-console']);
|
|
325
|
+
teamResults.push({ step: 'create', ...create });
|
|
326
|
+
execCapture('sprite', ['exec', '-s', row.sprite, '--', 'mkdir', '-p', '/home/sprite/work']);
|
|
327
|
+
const clone = execCapture('sprite', [
|
|
328
|
+
'exec',
|
|
329
|
+
'-s',
|
|
330
|
+
row.sprite,
|
|
331
|
+
'--',
|
|
332
|
+
'bash',
|
|
333
|
+
'-lc',
|
|
334
|
+
`test -d ${row.workdir}/.git || gh repo clone ${row.repo} ${row.workdir}`
|
|
335
|
+
]);
|
|
336
|
+
teamResults.push({ step: 'clone', ...clone });
|
|
337
|
+
const checkout = execCapture('sprite', [
|
|
338
|
+
'exec',
|
|
339
|
+
'-s',
|
|
340
|
+
row.sprite,
|
|
341
|
+
'--dir',
|
|
342
|
+
row.workdir,
|
|
343
|
+
'--',
|
|
344
|
+
'bash',
|
|
345
|
+
'-lc',
|
|
346
|
+
`git fetch origin && git checkout -B ${row.branch} origin/${plan.config.github.base_branch}`
|
|
347
|
+
]);
|
|
348
|
+
teamResults.push({ step: 'checkout', ...checkout });
|
|
349
|
+
const rrr = execCapture('sprite', [
|
|
350
|
+
'exec',
|
|
351
|
+
'-s',
|
|
352
|
+
row.sprite,
|
|
353
|
+
'--dir',
|
|
354
|
+
row.workdir,
|
|
355
|
+
'--',
|
|
356
|
+
'npx',
|
|
357
|
+
'projecta-rrr@latest',
|
|
358
|
+
'--global',
|
|
359
|
+
'--yes'
|
|
360
|
+
]);
|
|
361
|
+
teamResults.push({ step: 'rrr-install', ...rrr });
|
|
362
|
+
for (const command of row.setup || []) {
|
|
363
|
+
const setup = execCapture('sprite', ['exec', '-s', row.sprite, '--dir', row.workdir, '--', 'bash', '-lc', command]);
|
|
364
|
+
teamResults.push({ step: `setup: ${command}`, ...setup });
|
|
365
|
+
}
|
|
366
|
+
const checkpoint = execCapture('sprite', [
|
|
367
|
+
'checkpoint',
|
|
368
|
+
'create',
|
|
369
|
+
'-s',
|
|
370
|
+
row.sprite,
|
|
371
|
+
'--comment',
|
|
372
|
+
`rrr clean-ready ${plan.config.milestone} ${row.team}`
|
|
373
|
+
]);
|
|
374
|
+
teamResults.push({ step: 'checkpoint', ...checkpoint });
|
|
375
|
+
results.push({ team: row.team, sprite: row.sprite, results: teamResults });
|
|
376
|
+
}
|
|
377
|
+
return { plan, results };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function readSpriteStatus(projectRoot, options) {
|
|
381
|
+
const opts = options || {};
|
|
382
|
+
const plan = buildSpriteProvisionPlan(projectRoot, opts);
|
|
383
|
+
const rows = [];
|
|
384
|
+
const cli = execCapture('sprite', ['--help']);
|
|
385
|
+
for (const row of plan.rows) {
|
|
386
|
+
const status = {
|
|
387
|
+
team: row.team,
|
|
388
|
+
sprite: row.sprite,
|
|
389
|
+
branch: row.branch,
|
|
390
|
+
workdir: row.workdir,
|
|
391
|
+
planned: true,
|
|
392
|
+
live_checked: !!opts.live,
|
|
393
|
+
ok: false,
|
|
394
|
+
details: []
|
|
395
|
+
};
|
|
396
|
+
if (!cli.ok) {
|
|
397
|
+
status.details.push('sprite CLI not available or not authenticated');
|
|
398
|
+
rows.push(status);
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (opts.live) {
|
|
402
|
+
const probe = execCapture('sprite', [
|
|
403
|
+
'exec',
|
|
404
|
+
'-s',
|
|
405
|
+
row.sprite,
|
|
406
|
+
'--dir',
|
|
407
|
+
row.workdir,
|
|
408
|
+
'--',
|
|
409
|
+
'bash',
|
|
410
|
+
'-lc',
|
|
411
|
+
'printf "branch="; git branch --show-current; printf "\\nhead="; git rev-parse --short HEAD; printf "\\nclaude="; claude --version 2>/dev/null || true; printf "\\ngsd="; gsd-browser --version 2>/dev/null || true'
|
|
412
|
+
]);
|
|
413
|
+
status.ok = probe.ok;
|
|
414
|
+
status.details.push(probe.output || (probe.ok ? 'ok' : 'probe failed'));
|
|
415
|
+
} else {
|
|
416
|
+
const list = execCapture('sprite', ['list', '--prefix', row.sprite]);
|
|
417
|
+
status.ok = list.ok && list.output.includes(row.sprite);
|
|
418
|
+
status.details.push(status.ok ? 'sprite listed' : 'sprite not listed');
|
|
419
|
+
}
|
|
420
|
+
rows.push(status);
|
|
421
|
+
}
|
|
422
|
+
return { plan, rows };
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function writeSpriteStatusReport(projectRoot, options) {
|
|
426
|
+
const status = readSpriteStatus(projectRoot, options);
|
|
427
|
+
const dir = path.join(status.plan.config.project_root, '.planning', 'sprites');
|
|
428
|
+
ensureDir(dir);
|
|
429
|
+
const lines = [
|
|
430
|
+
`# ${status.plan.config.milestone} Sprite Status`,
|
|
431
|
+
'',
|
|
432
|
+
`Generated: ${new Date().toISOString()}`,
|
|
433
|
+
'',
|
|
434
|
+
'| Team | Sprite | Branch | Live Checked | OK |',
|
|
435
|
+
'| ---- | ------ | ------ | ------------ | -- |'
|
|
436
|
+
];
|
|
437
|
+
for (const row of status.rows) {
|
|
438
|
+
lines.push(`| ${row.team} | ${row.sprite} | ${row.branch} | ${row.live_checked ? 'yes' : 'no'} | ${row.ok ? 'yes' : 'no'} |`);
|
|
439
|
+
}
|
|
440
|
+
lines.push('', '## Details', '');
|
|
441
|
+
for (const row of status.rows) {
|
|
442
|
+
lines.push(`### ${row.team}`, '');
|
|
443
|
+
lines.push('```text');
|
|
444
|
+
lines.push(...row.details);
|
|
445
|
+
lines.push('```', '');
|
|
446
|
+
}
|
|
447
|
+
const reportPath = path.join(dir, 'STATUS.md');
|
|
448
|
+
atomicWrite(reportPath, `${lines.join('\n')}\n`);
|
|
449
|
+
return { status, reportPath };
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function coordinatorConfigPath(projectRoot) {
|
|
453
|
+
return path.join(projectRoot, '.planning', 'coordinator', 'COORDINATOR.json');
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function writeCoordinatorProvision(options) {
|
|
457
|
+
const opts = options || {};
|
|
458
|
+
const projectRoot = opts.projectRoot || process.cwd();
|
|
459
|
+
const teamConfig = loadConfig(projectRoot);
|
|
460
|
+
const runtime = readRuntimeManifest(projectRoot);
|
|
461
|
+
const dir = path.join(projectRoot, '.planning', 'coordinator');
|
|
462
|
+
ensureDir(dir);
|
|
463
|
+
const config = {
|
|
464
|
+
schema_version: 1,
|
|
465
|
+
provider: opts.provider || 'cloudflare-sandbox',
|
|
466
|
+
name: opts.name || `rrr-coordinator-${repoNameFor(teamConfig).toLowerCase()}`,
|
|
467
|
+
generated_at: new Date().toISOString(),
|
|
468
|
+
repo: teamConfig.github.repo,
|
|
469
|
+
milestone: teamConfig.milestone,
|
|
470
|
+
base_branch: teamConfig.github.base_branch,
|
|
471
|
+
integration_branch: teamConfig.github.integration_branch,
|
|
472
|
+
runtime_manifest: path.relative(projectRoot, runtimeManifestPath(projectRoot)),
|
|
473
|
+
report_first: true,
|
|
474
|
+
required_secrets: ['ANTHROPIC_API_KEY', 'GITHUB_TOKEN'],
|
|
475
|
+
responsibilities: [
|
|
476
|
+
'clone repository into ephemeral sandbox',
|
|
477
|
+
'merge team branches into integration branch',
|
|
478
|
+
'run runtime checks from .planning/RUNTIME.json',
|
|
479
|
+
'run RRR integration report',
|
|
480
|
+
'return report; never auto-merge to base branch'
|
|
481
|
+
]
|
|
482
|
+
};
|
|
483
|
+
writeJson(coordinatorConfigPath(projectRoot), config);
|
|
484
|
+
const readme = [
|
|
485
|
+
`# ${config.name}`,
|
|
486
|
+
'',
|
|
487
|
+
'This is the RRR coordinator contract for Cloudflare Sandbox execution.',
|
|
488
|
+
'',
|
|
489
|
+
'## Runtime',
|
|
490
|
+
'',
|
|
491
|
+
`Repo: ${config.repo || '(unset)'}`,
|
|
492
|
+
`Milestone: ${config.milestone}`,
|
|
493
|
+
`Base branch: ${config.base_branch}`,
|
|
494
|
+
`Integration branch: ${config.integration_branch}`,
|
|
495
|
+
`Runtime manifest: ${config.runtime_manifest}`,
|
|
496
|
+
'',
|
|
497
|
+
'## Required Secrets',
|
|
498
|
+
'',
|
|
499
|
+
'- `ANTHROPIC_API_KEY` for Claude Code.',
|
|
500
|
+
'- `GITHUB_TOKEN` or GitHub App installation token for read/write repo access.',
|
|
501
|
+
'',
|
|
502
|
+
'## Execution Contract',
|
|
503
|
+
'',
|
|
504
|
+
'- Coordinator uses a fresh Cloudflare sandbox per integration run unless explicitly kept alive.',
|
|
505
|
+
'- GitHub remains the source of truth for branches, PRs, reviews, and checks.',
|
|
506
|
+
'- Coordinator is report-first: it writes reports and recommendations only; humans approve final merges.',
|
|
507
|
+
'- Developer work stays in Sprites/team branches.'
|
|
508
|
+
].join('\n');
|
|
509
|
+
atomicWrite(path.join(dir, 'README.md'), `${readme}\n`);
|
|
510
|
+
return { config, runtime, dir };
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function readCoordinatorStatus(projectRoot) {
|
|
514
|
+
const root = projectRoot || process.cwd();
|
|
515
|
+
const config = readJson(coordinatorConfigPath(root), null);
|
|
516
|
+
const checks = [];
|
|
517
|
+
checks.push({ name: 'coordinator config', ok: !!config, detail: config ? path.relative(root, coordinatorConfigPath(root)) : 'missing' });
|
|
518
|
+
checks.push({ name: 'runtime manifest', ok: fs.existsSync(runtimeManifestPath(root)), detail: path.relative(root, runtimeManifestPath(root)) });
|
|
519
|
+
const wrangler = execCapture('npx', ['wrangler', '--version'], { cwd: root });
|
|
520
|
+
checks.push({ name: 'wrangler available', ok: wrangler.ok, detail: wrangler.output || 'not available' });
|
|
521
|
+
checks.push({ name: 'ANTHROPIC_API_KEY present', ok: !!process.env.ANTHROPIC_API_KEY, detail: process.env.ANTHROPIC_API_KEY ? 'present' : 'missing locally' });
|
|
522
|
+
checks.push({ name: 'GITHUB_TOKEN present', ok: !!process.env.GITHUB_TOKEN, detail: process.env.GITHUB_TOKEN ? 'present' : 'missing locally' });
|
|
523
|
+
return { config, checks };
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function writeCoordinatorStatusReport(projectRoot) {
|
|
527
|
+
const root = projectRoot || process.cwd();
|
|
528
|
+
const status = readCoordinatorStatus(root);
|
|
529
|
+
const dir = path.join(root, '.planning', 'coordinator');
|
|
530
|
+
ensureDir(dir);
|
|
531
|
+
const lines = [
|
|
532
|
+
'# Coordinator Status',
|
|
533
|
+
'',
|
|
534
|
+
`Generated: ${new Date().toISOString()}`,
|
|
535
|
+
'',
|
|
536
|
+
'| Check | OK | Detail |',
|
|
537
|
+
'| ----- | -- | ------ |'
|
|
538
|
+
];
|
|
539
|
+
for (const check of status.checks) {
|
|
540
|
+
lines.push(`| ${check.name} | ${check.ok ? 'yes' : 'no'} | ${String(check.detail || '').replace(/\|/g, '\\|')} |`);
|
|
541
|
+
}
|
|
542
|
+
const reportPath = path.join(dir, 'STATUS.md');
|
|
543
|
+
atomicWrite(reportPath, `${lines.join('\n')}\n`);
|
|
544
|
+
return { status, reportPath };
|
|
545
|
+
}
|
|
546
|
+
|
|
80
547
|
function createTeamState(team, config) {
|
|
81
548
|
const ctx = resolvePlanningContext({ projectRoot: config.project_root, team });
|
|
82
549
|
ensureDir(ctx.workstreamDir);
|
|
@@ -415,6 +882,17 @@ module.exports = {
|
|
|
415
882
|
submitPhase,
|
|
416
883
|
readStatus,
|
|
417
884
|
writeIntegrationReport,
|
|
885
|
+
writeRuntimeManifest,
|
|
886
|
+
readRuntimeManifest,
|
|
887
|
+
inferRuntimeManifest,
|
|
888
|
+
buildSpriteProvisionPlan,
|
|
889
|
+
writeSpriteProvisionPlan,
|
|
890
|
+
applySpriteProvisionPlan,
|
|
891
|
+
readSpriteStatus,
|
|
892
|
+
writeSpriteStatusReport,
|
|
893
|
+
writeCoordinatorProvision,
|
|
894
|
+
readCoordinatorStatus,
|
|
895
|
+
writeCoordinatorStatusReport,
|
|
418
896
|
detectRepo,
|
|
419
897
|
branchFor,
|
|
420
898
|
spriteFor
|
package/scripts/rrr-team-mode.js
CHANGED
|
@@ -17,6 +17,12 @@ const {
|
|
|
17
17
|
submitPhase,
|
|
18
18
|
readStatus,
|
|
19
19
|
writeIntegrationReport,
|
|
20
|
+
writeRuntimeManifest,
|
|
21
|
+
writeSpriteProvisionPlan,
|
|
22
|
+
applySpriteProvisionPlan,
|
|
23
|
+
writeSpriteStatusReport,
|
|
24
|
+
writeCoordinatorProvision,
|
|
25
|
+
writeCoordinatorStatusReport,
|
|
20
26
|
loadConfig
|
|
21
27
|
} = loadManager();
|
|
22
28
|
|
|
@@ -64,10 +70,15 @@ Usage:
|
|
|
64
70
|
node scripts/rrr-team-mode.js status
|
|
65
71
|
node scripts/rrr-team-mode.js submit --team team-runtime --phase 92 --ready
|
|
66
72
|
node scripts/rrr-team-mode.js report
|
|
73
|
+
node scripts/rrr-team-mode.js runtime-init
|
|
74
|
+
node scripts/rrr-team-mode.js provision-sprites [--apply] [--teams team-runtime]
|
|
75
|
+
node scripts/rrr-team-mode.js sprite-status [--live]
|
|
76
|
+
node scripts/rrr-team-mode.js provision-coordinator
|
|
77
|
+
node scripts/rrr-team-mode.js coordinator-status
|
|
67
78
|
|
|
68
79
|
Notes:
|
|
69
80
|
- This helper writes RRR planning artifacts only.
|
|
70
|
-
- GitHub PR
|
|
81
|
+
- GitHub PR dispatch is intentionally report-first in the pilot.
|
|
71
82
|
- Developer execution commands must use --team so root STATE.md stays coordinator-owned.
|
|
72
83
|
`);
|
|
73
84
|
}
|
|
@@ -157,6 +168,78 @@ function main() {
|
|
|
157
168
|
return;
|
|
158
169
|
}
|
|
159
170
|
|
|
171
|
+
if (command === 'runtime-init') {
|
|
172
|
+
const result = writeRuntimeManifest({
|
|
173
|
+
projectRoot,
|
|
174
|
+
force: args.force === true || args.force === 'true',
|
|
175
|
+
packageManager: args['package-manager'],
|
|
176
|
+
setup: args.setup,
|
|
177
|
+
checks: args.checks,
|
|
178
|
+
browser: args.browser,
|
|
179
|
+
uatCommand: args['uat-command'],
|
|
180
|
+
workdir: args.workdir
|
|
181
|
+
});
|
|
182
|
+
console.log(`${result.created ? 'Wrote' : 'Already exists'} ${path.relative(projectRoot, result.filePath)}`);
|
|
183
|
+
console.log(`Package manager: ${result.manifest.package_manager || '(none detected)'}`);
|
|
184
|
+
console.log(`Setup: ${(result.manifest.setup || []).join(', ') || '-'}`);
|
|
185
|
+
console.log(`Checks: ${(result.manifest.checks || []).join(', ') || '-'}`);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (command === 'provision-sprites') {
|
|
190
|
+
const options = {
|
|
191
|
+
teams: args.teams,
|
|
192
|
+
team: args.team
|
|
193
|
+
};
|
|
194
|
+
if (args.apply === true || args.apply === 'true') {
|
|
195
|
+
const result = applySpriteProvisionPlan(projectRoot, options);
|
|
196
|
+
for (const teamResult of result.results) {
|
|
197
|
+
console.log(`${teamResult.team}: ${teamResult.sprite}`);
|
|
198
|
+
for (const step of teamResult.results) {
|
|
199
|
+
console.log(` ${step.ok ? 'ok' : 'failed'} ${step.step}`);
|
|
200
|
+
if (!step.ok && step.output) console.log(` ${step.output.split('\n')[0]}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const written = writeSpriteProvisionPlan(projectRoot, options);
|
|
205
|
+
console.log(`Wrote ${path.relative(projectRoot, written.mdPath)}`);
|
|
206
|
+
console.log(`Wrote ${path.relative(projectRoot, written.jsonPath)}`);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (command === 'sprite-status') {
|
|
211
|
+
const result = writeSpriteStatusReport(projectRoot, {
|
|
212
|
+
live: args.live === true || args.live === 'true',
|
|
213
|
+
teams: args.teams,
|
|
214
|
+
team: args.team
|
|
215
|
+
});
|
|
216
|
+
console.log(`Wrote ${path.relative(projectRoot, result.reportPath)}`);
|
|
217
|
+
for (const row of result.status.rows) {
|
|
218
|
+
console.log(`${row.team}: ${row.ok ? 'ok' : 'not-ready'} ${row.sprite}`);
|
|
219
|
+
}
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (command === 'provision-coordinator') {
|
|
224
|
+
const result = writeCoordinatorProvision({
|
|
225
|
+
projectRoot,
|
|
226
|
+
provider: args.provider,
|
|
227
|
+
name: args.name
|
|
228
|
+
});
|
|
229
|
+
console.log(`Wrote ${path.relative(projectRoot, path.join(result.dir, 'COORDINATOR.json'))}`);
|
|
230
|
+
console.log(`Wrote ${path.relative(projectRoot, path.join(result.dir, 'README.md'))}`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (command === 'coordinator-status') {
|
|
235
|
+
const result = writeCoordinatorStatusReport(projectRoot);
|
|
236
|
+
console.log(`Wrote ${path.relative(projectRoot, result.reportPath)}`);
|
|
237
|
+
for (const check of result.status.checks) {
|
|
238
|
+
console.log(`${check.ok ? 'ok' : 'missing'} ${check.name}: ${check.detail}`);
|
|
239
|
+
}
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
160
243
|
if (command === 'show-config') {
|
|
161
244
|
console.log(JSON.stringify(loadConfig(projectRoot), null, 2));
|
|
162
245
|
return;
|