@jenga-ai/agent 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +340 -0
  3. package/agents/ai_engineer.md +113 -0
  4. package/agents/developer.md +236 -0
  5. package/agents/scrum-master.md +349 -0
  6. package/agents/scrutiny-agent.md +137 -0
  7. package/agents/solution-assessor.md +185 -0
  8. package/agents/tester.md +339 -0
  9. package/bin/jenga.js +70 -0
  10. package/hooks/copilot_session_end.sh +29 -0
  11. package/hooks/on_session_end.sh +238 -0
  12. package/hooks/prompt_router.sh +11 -0
  13. package/hooks/prompt_router_helper.js +52 -0
  14. package/hooks/session_end_helper.js +29 -0
  15. package/hooks/session_end_watcher.sh +24 -0
  16. package/lib/commands/attach.js +47 -0
  17. package/lib/commands/init.js +207 -0
  18. package/lib/commands/start.js +16 -0
  19. package/lib/commands/status.js +53 -0
  20. package/lib/config-schema.js +72 -0
  21. package/lib/inject-settings.js +61 -0
  22. package/lib/mirror.js +244 -0
  23. package/lib/resolve-project-dir.sh +47 -0
  24. package/mcp/execute-ticket/index.js +10 -0
  25. package/mcp/execute-ticket/package.json +5 -0
  26. package/mcp/help/index.js +79 -0
  27. package/mcp/help/package.json +14 -0
  28. package/mcp/router/README.md +19 -0
  29. package/mcp/router/embedder.js +23 -0
  30. package/mcp/router/index.js +204 -0
  31. package/mcp/router/matcher.js +87 -0
  32. package/mcp/router/package-lock.json +1048 -0
  33. package/mcp/router/package.json +11 -0
  34. package/mcp/router/skill-index.js +104 -0
  35. package/package.json +47 -0
  36. package/scripts/board_resolver.sh +46 -0
  37. package/scripts/e25_s01_extract_board_graph.py +292 -0
  38. package/scripts/e25_s01_generate_synthetic_board.py +90 -0
  39. package/scripts/measurement-10x.json +50 -0
  40. package/scripts/measurement-10x.txt +4 -0
  41. package/scripts/measurement-real.json +50 -0
  42. package/scripts/measurement-real.txt +4 -0
  43. package/scripts/postinstall.js +165 -0
  44. package/scripts/todo_cleanup.sh +22 -0
  45. package/scripts/todo_manager.sh +86 -0
  46. package/scripts/validate-board.sh +190 -0
  47. package/scripts/validate-story-format.sh +53 -0
  48. package/skills/brainstorm/SKILL.md +47 -0
  49. package/skills/btw/SKILL.md +42 -0
  50. package/skills/commit/SKILL.md +29 -0
  51. package/skills/commit/assets/user_instructions_template.md +22 -0
  52. package/skills/continue/SKILL.md +29 -0
  53. package/skills/convert/SKILL.md +124 -0
  54. package/skills/convert/convert_cli.py +235 -0
  55. package/skills/convert/tests/sample.csv +4 -0
  56. package/skills/convert/tests/sample.json +5 -0
  57. package/skills/convert/tests/sample.jsonl +3 -0
  58. package/skills/convert/tests/sample.yaml +18 -0
  59. package/skills/convert/tests/sample_obj.csv +2 -0
  60. package/skills/convert/tests/sample_obj.json +9 -0
  61. package/skills/deep-dive/SKILL.md +167 -0
  62. package/skills/do/SKILL.md +88 -0
  63. package/skills/do/assets/sender_template.json +12 -0
  64. package/skills/doc/SKILL.md +314 -0
  65. package/skills/doc/assets/path-objectives.yaml +38 -0
  66. package/skills/doc-sync/SKILL.md +167 -0
  67. package/skills/doc-sync/assets/default_excludes.txt +21 -0
  68. package/skills/doc-sync/assets/doc_targets.md +14 -0
  69. package/skills/dooo/SKILL.md +60 -0
  70. package/skills/error/SKILL.md +29 -0
  71. package/skills/evaluate/SKILL.md +45 -0
  72. package/skills/evaluate/assets/evaluation_invokation_template.yml +3 -0
  73. package/skills/evaluate/assets/evaluation_rapport_template.md +24 -0
  74. package/skills/examplify/SKILL.md +42 -0
  75. package/skills/help/SKILL.md +36 -0
  76. package/skills/improve/SKILL.md +55 -0
  77. package/skills/index/scripts/board-index +4 -0
  78. package/skills/index/scripts/board_index.py +615 -0
  79. package/skills/index/scripts/smoke_test.sh +86 -0
  80. package/skills/init/SKILL.md +44 -0
  81. package/skills/init/assets/.gitignore_template +15 -0
  82. package/skills/init/assets/PROJECT_SUMMARY_template.md +13 -0
  83. package/skills/init/assets/directory_structure.txt +13 -0
  84. package/skills/init/assets/test-config_template.json +4 -0
  85. package/skills/init/assets/workflow_template.json +30 -0
  86. package/skills/init/scripts/init.sh +48 -0
  87. package/skills/jbp/SKILL.md +25 -0
  88. package/skills/jenga/SKILL.md +68 -0
  89. package/skills/lgtm/SKILL.md +21 -0
  90. package/skills/mirror-public/SKILL.md +237 -0
  91. package/skills/mirror-public/assets/config.json +5 -0
  92. package/skills/mirror-public/scripts/mirror.sh +374 -0
  93. package/skills/pi-plan/SKILL.md +62 -0
  94. package/skills/pi-plan/assets/epic.json +7 -0
  95. package/skills/pi-plan/assets/story_template.md +18 -0
  96. package/skills/proceed/SKILL.md +29 -0
  97. package/skills/publish/SKILL.md +351 -0
  98. package/skills/publish/adapters/droplet.md +200 -0
  99. package/skills/publish/adapters/mobile-ios.md +114 -0
  100. package/skills/publish/adapters/npm-ci.md +223 -0
  101. package/skills/publish/adapters/npm.md +121 -0
  102. package/skills/publish/assets/ExportOptions.plist.template +19 -0
  103. package/skills/publish/assets/ci-contract.md +111 -0
  104. package/skills/publish/assets/ownership-matrix.md +17 -0
  105. package/skills/publish/assets/publish.example.json +85 -0
  106. package/skills/publish/assets/publish.example.npm-ci.json +40 -0
  107. package/skills/publish/assets/publish.example.npm.json +41 -0
  108. package/skills/publish/assets/secrets-guide.md +104 -0
  109. package/skills/publish/schemas/fixtures/npm-ci-minimal.json +17 -0
  110. package/skills/publish/schemas/fixtures/npm-ci-with-empty-secrets.json +18 -0
  111. package/skills/publish/schemas/fixtures/npm-ci-with-workflow-path.json +18 -0
  112. package/skills/publish/schemas/publish.schema.json +428 -0
  113. package/skills/publish/scripts/check_target_config.sh +96 -0
  114. package/skills/publish/scripts/droplet_pipeline.sh +208 -0
  115. package/skills/publish/scripts/generate_release_notes.sh +200 -0
  116. package/skills/publish/scripts/ios_pipeline.sh +486 -0
  117. package/skills/publish/scripts/npm_ci_pipeline.sh +225 -0
  118. package/skills/publish/scripts/npm_pipeline.sh +249 -0
  119. package/skills/publish/scripts/publish_common.sh +253 -0
  120. package/skills/publish/scripts/publish_deploy.sh +538 -0
  121. package/skills/publish/scripts/reconcile_tags.sh +135 -0
  122. package/skills/publish/scripts/run_gates.sh +616 -0
  123. package/skills/publish/scripts/setup_wizard.sh +394 -0
  124. package/skills/publish/scripts/show_history.sh +95 -0
  125. package/skills/publish/scripts/suggest_semver_bump.sh +105 -0
  126. package/skills/publish/scripts/validate_config.sh +163 -0
  127. package/skills/publish/scripts/validate_droplet_env.sh +45 -0
  128. package/skills/publish/scripts/validate_ios_env.sh +68 -0
  129. package/skills/publish/scripts/validate_npm_ci_env.sh +71 -0
  130. package/skills/publish/scripts/validate_npm_env.sh +22 -0
  131. package/skills/publish/scripts/write_ledger_entry.sh +126 -0
  132. package/skills/publish/wizards/droplet.md +275 -0
  133. package/skills/publish/wizards/mobile-ios.md +157 -0
  134. package/skills/publish/wizards/npm-ci.md +240 -0
  135. package/skills/publish/wizards/npm.md +224 -0
  136. package/skills/reconcile/SKILL.md +93 -0
  137. package/skills/reconcile/assets/report_format.md +44 -0
  138. package/skills/reconcile-origin/SKILL.md +75 -0
  139. package/skills/reconcile-origin/scripts/reconcile-origin.sh +372 -0
  140. package/skills/redo/SKILL.md +70 -0
  141. package/skills/route/SKILL.md +180 -0
  142. package/skills/self-sync/SKILL.md +73 -0
  143. package/skills/self-sync/scripts/run.js +136 -0
  144. package/skills/skillify/SKILL.md +68 -0
  145. package/skills/skillify/assets/init-new/SKILL.md +35 -0
  146. package/skills/skillify/assets/init-new/assets/.gitignore_template +15 -0
  147. package/skills/skillify/assets/init-new/assets/PROJECT_SUMMARY_template.md +13 -0
  148. package/skills/skillify/assets/init-new/assets/directory_structure.txt +10 -0
  149. package/skills/skillify/assets/init-new/assets/test-config_template.json +4 -0
  150. package/skills/skillify/assets/init-new/assets/workflow_template.json +17 -0
  151. package/skills/skillify/assets/init-new/scripts/init.sh +48 -0
  152. package/skills/skillify/assets/init-old/SKILL.md +124 -0
  153. package/skills/spinoff/SKILL.md +48 -0
  154. package/skills/status/SKILL.md +33 -0
  155. package/skills/status/assets/output_format.md +41 -0
  156. package/skills/todo/SKILL.md +46 -0
  157. package/skills/todo/assets/todo_handoff_template.md +22 -0
  158. package/skills/todo/assets/todo_template.md +3 -0
  159. package/skills/train/SKILL.md +116 -0
  160. package/skills/train/assets/dashboard-templates/classifiers.html +106 -0
  161. package/skills/train/assets/dashboard-templates/nlp.html +102 -0
  162. package/skills/train/assets/dashboard-templates/transformers.html +98 -0
  163. package/skills/train/assets/results-parsers/__init__.py +9 -0
  164. package/skills/train/assets/results-parsers/classifiers.py +84 -0
  165. package/skills/train/assets/results-parsers/nlp.py +88 -0
  166. package/skills/train/assets/results-parsers/reporter.py +154 -0
  167. package/skills/train/assets/results-parsers/transformers.py +120 -0
  168. package/skills/train/train_cli.py +786 -0
  169. package/templates/EXECUTION_PLAN_TEMPLATE.md +43 -0
  170. package/templates/EXECUTION_SUMMARY_TEMPLATE.md +50 -0
  171. package/templates/JENGA_CONFIG_TEMPLATE.json +23 -0
  172. package/templates/PROBLEM_RAPPORT_TEMPLATE.md +88 -0
  173. package/templates/SCRUM_BOARD_SCHEMA.md +311 -0
  174. package/templates/SKILL.md +16 -0
  175. package/templates/SKILL_TEMPLATE.md +28 -0
  176. package/templates/USER_INSTRUCTIONS_TEMPLATE.md +22 -0
  177. package/templates/copilot-instructions.md.tpl +55 -0
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: init
3
+ description: Initialize a new project with the standard directory structure, PROJECT_SUMMARY.md, workflow.json, git repo, and gitignore. Follows a defined ordered onboarding sequence. Use when setting up a new or empty project.
4
+ keywords:
5
+ - init
6
+ - initialize
7
+ - setup
8
+ - new project
9
+ - scaffold
10
+ examples:
11
+ - "initialize a new project"
12
+ - "set up a new workspace"
13
+ ---
14
+
15
+ # Init — Project Setup
16
+
17
+ ## Instructions
18
+
19
+ Follow these steps in order. Do not skip steps — the sequence matters.
20
+
21
+ ### 1–9. Run the scaffold script
22
+
23
+ Execute the init script from the project root:
24
+
25
+ ```bash
26
+ chmod +x ./scripts/init.sh && ./scripts/init.sh
27
+ ```
28
+
29
+ This script handles all scaffolding in one step:
30
+ 1. Initializes the git repository
31
+ 2. Creates `.gitignore`
32
+ 3. Creates the full directory structure under `project/`
33
+ 4. Creates `project/PROJECT_SUMMARY.md` with placeholder content
34
+ 5. Creates `project/configs/workflow.json` with shared constants
35
+ 6. Creates `project/configs/test-config.json` stub
36
+ 7. Creates `project/data/baselines.json`
37
+ 8. Creates `project/logs/events.json`
38
+ 9. Stages and commits all files with the message `init: scaffold project structure and workflow config`
39
+
40
+ If the script fails, check that you are in the project root and that git is available.
41
+
42
+ ### 10. Prompt next step
43
+
44
+ Inform the user that setup is complete and suggest running `/pi-plan` to define project goals and epics.
@@ -0,0 +1,15 @@
1
+ # macOS & Windows system files
2
+ .DS_Store
3
+ Thumbs.db
4
+ Desktop.ini
5
+
6
+ # Environment files
7
+ .env
8
+ .env.*
9
+ *.local
10
+
11
+ # Dependency directories
12
+ node_modules/
13
+ vendor/
14
+ .venv/
15
+ EOF
@@ -0,0 +1,13 @@
1
+ # Project Summary
2
+
3
+ ## Overview
4
+ _To be completed._
5
+
6
+ ## Architecture & Structure
7
+ _To be completed._
8
+
9
+ ## Epics
10
+ _To be completed via /pi-plan._
11
+
12
+ ## Conventions
13
+ _To be completed._
@@ -0,0 +1,13 @@
1
+ project/board/epics
2
+ project/board/stories
3
+ project/board/tasks
4
+ project/configs
5
+ project/data
6
+ project/queue
7
+ project/rapports/problems
8
+ project/rapports/analysis
9
+ project/logs
10
+ project/documentation
11
+ project/documentation/plans
12
+ project/documentation/summaries
13
+ project/documentation/examples
@@ -0,0 +1,4 @@
1
+ {
2
+ "_note": "Run the tester agent to configure tools for this project.",
3
+ "tools": []
4
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "statuses": ["Pending", "In Progress", "Passed", "Passed with remarks", "Failed", "Rejected", "Blocked"],
3
+ "rapport_types": ["conflict", "implementation_blocker", "security_concern", "test_failure", "analysis"],
4
+ "paths": {
5
+ "board": "project/board",
6
+ "epics": "project/board/epics",
7
+ "stories": "project/board/stories",
8
+ "tasks": "project/board/tasks",
9
+ "rapports_problems": "project/rapports/problems",
10
+ "rapports_analysis": "project/rapports/analysis",
11
+ "queue": "project/queue",
12
+ "scrum_triggers": "project/queue/scrum_triggers.jsonl",
13
+ "developer_triggers": "project/queue/developer_triggers.jsonl",
14
+ "tester_triggers": "project/queue/tester_triggers.jsonl",
15
+ "session_handoff": "project/queue/.session_handoff.json",
16
+ "logs": "project/logs",
17
+ "data": "project/data",
18
+ "configs": "project/configs",
19
+ "documentation": "project/documentation",
20
+ "documentation_plans": "project/documentation/plans",
21
+ "documentation_summaries": "project/documentation/summaries"
22
+ },
23
+ "agents": ["developer", "tester", "scrum-master"],
24
+ "pipeline": [
25
+ {"step": 1, "agent": "scrum-master", "phase": "planning", "on_complete": "write planning_complete handoff → developer_triggers.jsonl"},
26
+ {"step": 2, "agent": "developer", "phase": "implementation", "on_complete": "write implementation_complete handoff → tester_triggers.jsonl"},
27
+ {"step": 3, "agent": "tester", "phase": "verification", "on_complete": "write test status handoff → scrum_triggers.jsonl (+ developer_triggers.jsonl if failed)"},
28
+ {"step": 4, "agent": "scrum-master", "phase": "rollup_review", "on_complete": "update board statuses, close epic/story if all done"}
29
+ ]
30
+ }
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ ASSETS_DIR="$SCRIPT_DIR/../assets"
6
+
7
+ # ─── 1. Initialize git repository ────────────────────────────────────────────
8
+ echo "→ Initializing git repository..."
9
+ git init
10
+
11
+ # ─── 2. Create .gitignore ─────────────────────────────────────────────────────
12
+ echo "→ Copying .gitignore from template..."
13
+ cp "$ASSETS_DIR/.gitignore_template" .gitignore
14
+
15
+ # ─── 3. Scaffold directory structure ──────────────────────────────────────────
16
+ echo "→ Scaffolding directory structure..."
17
+ while IFS= read -r dir || [[ -n "$dir" ]]; do
18
+ [[ -z "$dir" || "$dir" == \#* ]] && continue
19
+ mkdir -p "$dir"
20
+ done < "$ASSETS_DIR/directory_structure.txt"
21
+
22
+ # ─── 4. Create project/PROJECT_SUMMARY.md ────────────────────────────────────
23
+ echo "→ Copying PROJECT_SUMMARY.md from template..."
24
+ cp "$ASSETS_DIR/PROJECT_SUMMARY_template.md" project/PROJECT_SUMMARY.md
25
+
26
+ # ─── 5. Create project/configs/workflow.json ─────────────────────────────────
27
+ echo "→ Copying workflow.json from template..."
28
+ cp "$ASSETS_DIR/workflow_template.json" project/configs/workflow.json
29
+
30
+ # ─── 6. Create project/configs/test-config.json stub ─────────────────────────
31
+ echo "→ Copying test-config.json from template..."
32
+ cp "$ASSETS_DIR/test-config_template.json" project/configs/test-config.json
33
+
34
+ # ─── 7. Create project/data/baselines.json ───────────────────────────────────
35
+ echo "→ Creating baselines.json..."
36
+ echo '{}' > project/data/baselines.json
37
+
38
+ # ─── 8. Create project/logs/events.json ──────────────────────────────────────
39
+ echo "→ Creating events.json..."
40
+ echo '[]' > project/logs/events.json
41
+
42
+ # ─── 9. Initial commit ───────────────────────────────────────────────────────
43
+ echo "→ Staging and committing scaffolded files..."
44
+ git add -A
45
+ git commit -m "init: scaffold project structure and workflow config"
46
+
47
+ echo ""
48
+ echo "✓ Project scaffold complete."
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: jbp
3
+ description: Scaffold the project using the JengaBasePlate boilerplate repo template from https://github.com/samwelmunga/JengaBasePlate.git
4
+ keywords:
5
+ - jbp
6
+ - boilerplate
7
+ - scaffold
8
+ - template
9
+ - jenga base
10
+ examples:
11
+ - "scaffold with JengaBasePlate"
12
+ - "set up the base template"
13
+ ---
14
+
15
+ # JBP — JengaBasePlate Scaffold
16
+
17
+ ## Instructions
18
+
19
+ Use the following repo as the boilerplate framework for this project:
20
+
21
+ ```
22
+ https://github.com/samwelmunga/JengaBasePlate.git
23
+ ```
24
+
25
+ Clone or pull the template and apply its structure to the current project.
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: jenga
3
+ description: Fully automated board orchestrator. Decomposes any unbroken Epics into Stories, any unbroken Stories into Tasks, queues all unqueued Tasks into todo.md, then executes every eligible item — no user prompts — until the board is fully started.
4
+ keywords:
5
+ - jenga
6
+ - orchestrate
7
+ - auto implement
8
+ - full board
9
+ - automated
10
+ examples:
11
+ - "run jenga to implement everything"
12
+ - "start the full automation"
13
+ metadata:
14
+ prefered_agent: scrum-master
15
+ ---
16
+
17
+ # Jenga — Auto-Implementation Orchestrator
18
+
19
+ ## Purpose
20
+
21
+ `/jenga` is a hands-free "commit to everything on the board" pipeline. It ensures the entire board is fully decomposed, fully queued, and fully executing — without any user interaction. It runs in four phases: **decompose → queue → execute → loop**.
22
+
23
+ ## Instructions
24
+
25
+ ### Phase 1 — Decompose Epics into Stories
26
+
27
+ Read all files in `project/board/epics/`. For each Epic that has no corresponding story files in `project/board/stories/` (i.e. no files whose name starts with that Epic's ID), invoke `/do` via a **scrum-master sub-agent** to break it down into Stories.
28
+
29
+ Repeat until every Epic has at least one Story on the board.
30
+
31
+ ### Phase 2 — Decompose Stories into Tasks
32
+
33
+ Read all files in `project/board/stories/`. For each Story that has no corresponding task files in `project/board/tasks/` (i.e. no files whose name starts with that Story's ID), invoke `/do` via a **scrum-master sub-agent** to break it down into Tasks.
34
+
35
+ Repeat until every Story has at least one Task on the board.
36
+
37
+ ### Phase 3 — Queue all Tasks into `todo.md`
38
+
39
+ Read all files in `project/board/tasks/`. For every Task not already listed in `project/todo.md`, append its ID (and title as a comment) to `project/todo.md`.
40
+
41
+ After this phase, `todo.md` reflects the full set of work on the board.
42
+
43
+ ### Phase 4 — Execute
44
+
45
+ Loop through `todo.md` and execute all eligible items, running independent ones in parallel:
46
+
47
+ 1. **Collect eligible items** — from `todo.md`, find all items whose board file has `status: Pending` and no unresolved dependencies. A dependency is resolved if the blocking item's status is at least `Running` or `Passed`.
48
+ 2. **Group by parallelism** — items with no shared dependencies and no overlapping output files can run concurrently. Items that depend on each other must be sequenced.
49
+ 3. **Invoke `/do` in parallel** — launch each independent item as a **background sub-agent** simultaneously. Do not wait for one to finish before starting another if they are independent.
50
+ 4. **Mark Running** — update `status: Running` in each launched item's board file (YAML front-matter) immediately after launch.
51
+ 5. **Wait and loop** — once all active background agents have completed, return to step 1 of this phase to pick up any newly unblocked items.
52
+
53
+ ### Exit condition
54
+
55
+ When no eligible candidates remain in Phase 4, exit and output:
56
+
57
+ ```
58
+ ✅ Jenga complete. All eligible tasks have been started.
59
+ ```
60
+
61
+ ## Edge Cases
62
+
63
+ - **Epic with no stories after breakdown** — log a warning and continue to the next Epic; do not block the pipeline.
64
+ - **Story with no tasks after breakdown** — log a warning and continue to the next Story.
65
+ - **Task already in `todo.md`** — skip; do not duplicate.
66
+ - **All tasks in `todo.md` already Running/Passed** — exits cleanly with the completion message.
67
+ - **Unresolved dependencies** — item is skipped in Phase 4 until its blockers are at least `Running`.
68
+ - **`/do` failure (background agent)** — treated as a skip; mark the item's status back to `Pending` and continue the loop with remaining candidates.
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: lgtm
3
+ description: Approve and commit the current work, then continue to the next task. Shortcut that chains /commit followed by /continue.
4
+ keywords:
5
+ - lgtm
6
+ - approve
7
+ - looks good
8
+ - done
9
+ - commit and continue
10
+ examples:
11
+ - "lgtm, commit this"
12
+ - "looks good, move on"
13
+ ---
14
+
15
+ # LGTM — Approve, Commit, and Continue
16
+
17
+ ## Instructions
18
+
19
+ 1. Invoke the `/commit` skill and wait for it to finish.
20
+
21
+ 2. If the current workflow is part of a `/do` execution, return to that workflow. Otherwise, invoke the `/continue` skill.
@@ -0,0 +1,237 @@
1
+ ---
2
+ name: mirror-public
3
+ description: Mirror this private repo one-way to its public counterpart (`https://github.com/samwelmunga/jenga-npm`), applying a `.publicignore` blocklist and producing a single squash commit per run so private board, queue, log, and rapport artefacts never leak downstream.
4
+ keywords:
5
+ - mirror public
6
+ - public mirror
7
+ - jenga-npm
8
+ - one-way sync
9
+ - publicignore
10
+ - squash mirror
11
+ examples:
12
+ - "/mirror-public --dry-run"
13
+ - "/mirror-public"
14
+ - "/mirror-public --force"
15
+ - "preview what would ship to the public repo"
16
+ - "sync the public mirror"
17
+ ---
18
+
19
+ # Mirror-Public
20
+
21
+ Push a curated subset of this private repo to the public counterpart at `https://github.com/samwelmunga/jenga-npm` as a **one-way**, **squash-committed** mirror. Private is always the source of truth; the public repo is a downstream shadow that this skill can rebuild at any time.
22
+
23
+ Distinct from `/publish` (which ships the `jenga-agent` npm package to npmjs.com) and from `/self-sync` (which mirrors root → in-repo `.claude/.agents/`). This skill is the third distribution surface: the public GitHub repo.
24
+
25
+ ## Direction & Contract
26
+
27
+ - **Direction:** private → public, one-way. Public never merges back. If a public commit exists that this skill did not create, the run aborts by default (see [Safety](#safety-model)).
28
+ - **Trigger:** manual only. There is no CI hook, no git hook, no on-commit automation. The risk of leaking WIP outweighs the convenience.
29
+ - **Commit model:** each real run produces exactly **one squash commit** on the public side. Private commit messages, authors, and history never appear on the public repo.
30
+ - **Scope model:** ships everything **except** what `.publicignore` blocks. Additive-by-default; the blocklist is the single source of truth for what stays private.
31
+
32
+ ## Invocation
33
+
34
+ | Command | Effect |
35
+ |---------|--------|
36
+ | `/mirror-public --dry-run` | Read-only preview. Prints the exact "would ship" and "would be blocked" file lists plus totals. No fetch-write, no commit, no push. |
37
+ | `/mirror-public` | Real run. Fetches the public tip, runs the safety check, rsyncs, squash-commits, pushes. Aborts if the public repo is ahead of the last mirror marker. |
38
+ | `/mirror-public --force` | Same as the real run, but overrides the safety abort — the public branch is force-updated to match private (blocklist applied). Use only for recovery; see [Recovery flow](#recovery-when-the-public-repo-is-ahead). |
39
+
40
+ All three delegate to `skills/mirror-public/scripts/mirror.sh`. This SKILL body never inlines mirror logic — everything lives in the script.
41
+
42
+ ### How it is actually run
43
+
44
+ ```bash
45
+ # preview
46
+ bash skills/mirror-public/scripts/mirror.sh --dry-run
47
+
48
+ # real run
49
+ bash skills/mirror-public/scripts/mirror.sh
50
+
51
+ # force overwrite when the public repo is ahead
52
+ bash skills/mirror-public/scripts/mirror.sh --force
53
+ ```
54
+
55
+ ## Blocklist — `.publicignore`
56
+
57
+ `.publicignore` lives at the **repo root** (discoverable like `.gitignore`). It is a `.gitignore`-style pattern file consumed by `rsync --exclude-from=<repo-root>/.publicignore`. Every line is a path relative to the repo root that MUST NEVER ship to the public mirror.
58
+
59
+ Current baseline (see the file for the authoritative list) blocks:
60
+
61
+ - `project/board/`, `project/queue/`, `project/logs/`, `project/rapports/`, `project/todo.md`
62
+ - `project/documentation/plans/`, `project/documentation/summaries/`, `project/instructions/`
63
+ - `.env`, `.env.*`, `*.local`, `*.pid`, `.claude/settings.local.json`
64
+ - `.claude/worktrees/`, `.agents/worktrees/`, `.mirror-worktrees/`
65
+ - `node_modules/`, `**/__pycache__/`, `*.pyc`, `.DS_Store`, `.git/`
66
+
67
+ ### Adding new entries
68
+
69
+ Edit `.publicignore` and add the path(s). Rules:
70
+
71
+ - **Over-block, don't under-block.** Anything missed is a private leak; anything extra is merely an unshipped file.
72
+ - **Verify with `--dry-run` before pushing.** The dry-run "Files that would ship" section is the honest answer to "did my new pattern match?".
73
+ - Directory-globs must end in `/` (e.g. `project/scratch/`), otherwise rsync only matches a file of that exact name.
74
+
75
+ ## Config — `skills/mirror-public/assets/config.json`
76
+
77
+ ```json
78
+ {
79
+ "publicRepoUrl": "https://github.com/samwelmunga/jenga-npm.git",
80
+ "defaultBranch": "main",
81
+ "worktreePath": ".mirror-worktrees/public"
82
+ }
83
+ ```
84
+
85
+ | Field | Purpose |
86
+ |-------|---------|
87
+ | `publicRepoUrl` | URL of the public downstream repo. Can be overridden at runtime by `MIRROR_PUBLIC_URL_OVERRIDE` (used by tests and by anyone rehearsing against a local bare remote). |
88
+ | `defaultBranch` | Branch mirrored on the public side. `main` in production. |
89
+ | `worktreePath` | Relative path (from the private repo root) where the script keeps its scratch clone of the public repo. Blocked from itself via `.publicignore` (`.mirror-worktrees/`). |
90
+
91
+ ## Safety model
92
+
93
+ The script maintains a **local `last-mirror-sync` tag** inside its scratch clone that records the public SHA it last produced. Before every real run it compares that tag to the current tip of `origin/<defaultBranch>`:
94
+
95
+ - **Tag missing** (first ever run, or scratch clone was wiped): proceed.
96
+ - **Tag matches remote tip:** proceed — the public repo has not moved since our last mirror.
97
+ - **Tag lags remote tip:** the public repo has commits the mirror did not create. **Abort with a non-zero exit** unless `--force` is passed.
98
+
99
+ `--force` skips the abort and overwrites the public branch with the private state (post-blocklist). It is a **destructive-by-default** flag: any commit that landed on the public repo outside this skill will disappear from the public branch. Rescue that work first (see [Recovery flow](#recovery-when-the-public-repo-is-ahead)) before using `--force`.
100
+
101
+ ## Squash-commit model
102
+
103
+ Every real run creates exactly one commit on the public branch:
104
+
105
+ ```
106
+ chore(mirror): sync from private at <short-sha> <UTC-timestamp>
107
+
108
+ Source-Commit: <full-private-sha>
109
+ ```
110
+
111
+ - **Subject** identifies the private HEAD at the time of the mirror.
112
+ - **`Source-Commit:` trailer** carries the full private SHA so anyone reading the public commit can cross-reference (against a private clone they have access to) which private state produced this snapshot.
113
+ - **No private log leaks.** Individual private commits, author names, and messages are never replayed on the public side.
114
+ - **Idempotent.** If the working tree matches the public tip post-blocklist, the script exits without creating a commit and prints `nothing to mirror — public tree already matches private (post-blocklist)`.
115
+
116
+ ## Examples
117
+
118
+ All example outputs below were captured against a local bare remote (`/tmp/mirror-t04-test.git`) using `MIRROR_PUBLIC_URL_OVERRIDE`, so the same shape reproduces without touching the real `jenga-npm.git`.
119
+
120
+ ### Dry-run
121
+
122
+ ```bash
123
+ bash skills/mirror-public/scripts/mirror.sh --dry-run
124
+ ```
125
+
126
+ Tail of the output:
127
+
128
+ ```
129
+ === Files that would ship (607) ===
130
+ ...
131
+ === Files that would be blocked (599) ===
132
+ project/board/...
133
+ project/queue/...
134
+ project/logs/...
135
+ project/rapports/...
136
+ project/todo.md
137
+ ...
138
+
139
+ ================ mirror-public dry-run summary ================
140
+ would ship : 607 files
141
+ would block : 599 files
142
+ public URL : https://github.com/samwelmunga/jenga-npm.git
143
+ remote branch : main
144
+ dry run — no push, no commit, no remote mutation
145
+ ===============================================================
146
+ ```
147
+
148
+ Zero remote mutation. Use this before every real run when the mirror state is uncertain, and after every `.publicignore` edit to confirm the pattern matched.
149
+
150
+ ### Real run
151
+
152
+ ```bash
153
+ bash skills/mirror-public/scripts/mirror.sh
154
+ ```
155
+
156
+ Tail of the output (first mirror against a fresh remote):
157
+
158
+ ```
159
+ mirror.sh: no last-mirror-sync tag found (first mirror or wiped scratch) — proceeding
160
+ mirror.sh: rsync <repo>/ -> <repo>/.mirror-worktrees/public/ (excludes: .publicignore + .git)
161
+ mirror.sh: pushing b955ae3380a5d562799b0e8a75bbb758f8773104 -> origin/main
162
+
163
+ ================ mirror-public summary ================
164
+ files changed : 607
165
+ new commit : b955ae3380a5d562799b0e8a75bbb758f8773104
166
+ remote branch : main
167
+ public URL : https://github.com/samwelmunga/jenga-npm.git
168
+ =======================================================
169
+ ```
170
+
171
+ A second consecutive run reports `nothing to mirror — public tree already matches private (post-blocklist)` and exits `0`.
172
+
173
+ ### `--force`
174
+
175
+ Use only when the safety abort fires and you have already rescued any external work off the public branch.
176
+
177
+ ```bash
178
+ bash skills/mirror-public/scripts/mirror.sh --force
179
+ ```
180
+
181
+ Look for the WARNING line — it names the divergent SHAs so you can verify you meant to overwrite them:
182
+
183
+ ```
184
+ mirror.sh: WARNING: origin/main (7524425...) has moved past last-mirror-sync (b955ae3...); proceeding due to --force
185
+ mirror.sh: rsync ...
186
+ mirror.sh: pushing 154f7b7... -> origin/main
187
+ ```
188
+
189
+ ### Recovery when the public repo is ahead
190
+
191
+ If the safety abort fires:
192
+
193
+ ```
194
+ mirror.sh: error: origin/main (<remote-sha>) has moved past last-mirror-sync (<marker-sha>).
195
+ The public repo has commits the mirror did not create. Re-run with --force to overwrite.
196
+ ```
197
+
198
+ Do this before you reach for `--force`:
199
+
200
+ 1. **Inspect the divergent commits.** In a separate clone of the public repo:
201
+ ```bash
202
+ git clone https://github.com/samwelmunga/jenga-npm.git /tmp/jenga-npm-rescue
203
+ git -C /tmp/jenga-npm-rescue log <marker-sha>..origin/main
204
+ ```
205
+ 2. **Decide what to keep.** For each divergent commit that has value:
206
+ - If the change belongs in the private repo, port it into this repo (as a regular commit under normal review) so the next mirror carries it forward.
207
+ - If the change should live only on the public repo, save the patches somewhere durable (`git format-patch <marker-sha>..origin/main -o /tmp/rescue-patches`) — they will not survive `--force`.
208
+ 3. **Re-run with `--force`** once you are confident the public branch can be overwritten:
209
+ ```bash
210
+ bash skills/mirror-public/scripts/mirror.sh --force
211
+ ```
212
+
213
+ The marker tag advances on success, and subsequent normal runs resume the abort-by-default behaviour.
214
+
215
+ ## Environment overrides
216
+
217
+ | Variable | Purpose |
218
+ |----------|---------|
219
+ | `MIRROR_PUBLIC_URL_OVERRIDE` | Replaces `publicRepoUrl` from `config.json` at runtime. Used to rehearse against a local bare remote (e.g. `git init --bare /tmp/test-mirror.git`) without touching the real public repo. |
220
+
221
+ ## Out of Scope
222
+
223
+ Per epic **E28 — Public Mirror**, these are explicitly not part of this skill:
224
+
225
+ - **Two-way sync** (PR back-flow from public → private)
226
+ - **Content-level scrubbing** (regex redactions inside file bodies)
227
+ - **Automated triggers** (git hooks, CI, watch-mode)
228
+ - **Cross-linking issues/PRs** between the two repos
229
+
230
+ See `project/board/epics/E28_public-mirror.md` → *Out of Scope* for the authoritative list.
231
+
232
+ ## Guard rails
233
+
234
+ - **Never inline mirror logic in this SKILL body.** All filesystem, git, rsync, and push work goes through `skills/mirror-public/scripts/mirror.sh`.
235
+ - **Never push to `jenga-npm.git` from an ad-hoc script.** Only this skill should push. Ad-hoc pushes bypass the blocklist and the safety marker.
236
+ - **Never commit `.mirror-worktrees/`.** It is already in `.publicignore` and should also stay untracked in the private repo. If you see it in `git status`, the scratch clone was accidentally seeded outside the configured path — investigate before mirroring.
237
+ - **Never `git add` `.publicignore` matches on the private side to "hide" them from the mirror.** The private repo tracks whatever it needs to; the blocklist is the single filter and must remain the single filter.
@@ -0,0 +1,5 @@
1
+ {
2
+ "publicRepoUrl": "https://github.com/samwelmunga/jenga-npm.git",
3
+ "defaultBranch": "main",
4
+ "worktreePath": ".mirror-worktrees/public"
5
+ }