@tyroneross/build-loop 0.36.1 → 0.43.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 (270) hide show
  1. package/.agents/plugins/marketplace.json +2 -2
  2. package/.claude-plugin/marketplace.json +3 -3
  3. package/.claude-plugin/plugin.json +1 -1
  4. package/.codex-plugin/plugin.json +1 -1
  5. package/.cursor/rules/build-loop-surface.mdc +12 -11
  6. package/.cursor/rules/skill-index.mdc +33 -0
  7. package/AGENTS.md +213 -34
  8. package/README.md +99 -31
  9. package/agents/advisor.md +4 -4
  10. package/agents/alignment-checker.md +2 -2
  11. package/agents/architecture-scout.md +4 -4
  12. package/agents/build-orchestrator.md +38 -36
  13. package/agents/database-assessor.md +10 -4
  14. package/agents/design-contract-specialist.md +8 -8
  15. package/agents/fact-checker.md +13 -3
  16. package/agents/fix-critique.md +2 -2
  17. package/agents/independent-auditor.md +60 -7
  18. package/agents/leak-scanner.md +82 -0
  19. package/agents/overfitting-reviewer.md +2 -2
  20. package/agents/plan-critic.md +1 -1
  21. package/agents/promotion-reviewer.md +5 -5
  22. package/agents/retrospective-synthesizer.md +138 -35
  23. package/agents/scope-auditor.md +82 -11
  24. package/agents/security-reviewer.md +55 -1
  25. package/agents/self-improvement-architect.md +17 -3
  26. package/agents/transcript-pattern-miner.md +5 -5
  27. package/agents/ui-validator.md +1 -1
  28. package/bin/build-loop-debugger.js +143 -0
  29. package/bin/build-loop-install.js +1 -4
  30. package/bin/build-loop-load-probe.js +345 -0
  31. package/codex-skills/build-loop/SKILL.md +28 -6
  32. package/commands/feedback.md +37 -0
  33. package/dist/src/interactive-verifier.d.ts +1 -14
  34. package/dist/src/interactive-verifier.d.ts.map +1 -1
  35. package/dist/src/interactive-verifier.js +6 -113
  36. package/dist/src/interactive-verifier.js.map +1 -1
  37. package/dist/src/quality.d.ts +5 -0
  38. package/dist/src/quality.d.ts.map +1 -0
  39. package/dist/src/quality.js +81 -0
  40. package/dist/src/quality.js.map +1 -0
  41. package/dist/src/storage.d.ts.map +1 -1
  42. package/dist/src/storage.js +37 -3
  43. package/dist/src/storage.js.map +1 -1
  44. package/docs/agent-surface-policy.md +35 -31
  45. package/docs/memory-setup.md +19 -0
  46. package/hooks/git/pre-push +65 -4
  47. package/hooks/hooks.json +95 -38
  48. package/hooks/pre-commit +20 -1
  49. package/hooks/pre-edit-rally-point.sh +10 -3
  50. package/hooks/session-start-codex-hook-trust.sh +30 -0
  51. package/hooks/session-start-git-hooks.sh +3 -1
  52. package/hooks/session-start-rally-point.sh +52 -4
  53. package/hooks/session-start-worktree-gc.sh +47 -94
  54. package/hooks/stop-transcript-sweep.sh +173 -0
  55. package/hooks/test_closeout.sh +14 -2
  56. package/package.json +8 -7
  57. package/scripts/README.md +1 -1
  58. package/scripts/_paths.py +65 -0
  59. package/scripts/groundwork_exchange.py +1012 -0
  60. package/scripts/install_memory.py +33 -1
  61. package/scripts/lessons_index/ingest.py +13 -2
  62. package/scripts/lessons_index/query.py +36 -13
  63. package/scripts/memory_context/__init__.py +108 -14
  64. package/scripts/memory_graph/__init__.py +5 -1
  65. package/scripts/project_resolver.py +42 -36
  66. package/scripts/sync_plugin_cache.py +37 -2
  67. package/skills/agent-rally-point/SKILL.md +46 -0
  68. package/skills/api-registry-bridge/SKILL.md +1 -1
  69. package/skills/architecture/dead/SKILL.md +1 -1
  70. package/skills/architecture/impact/SKILL.md +1 -1
  71. package/skills/architecture/review/SKILL.md +1 -1
  72. package/skills/architecture/rules/SKILL.md +3 -3
  73. package/skills/architecture/scan/SKILL.md +1 -1
  74. package/skills/architecture/trace/SKILL.md +1 -1
  75. package/skills/attribution-standard/SKILL.md +6 -6
  76. package/skills/auto-decision-capture/SKILL.md +31 -2
  77. package/skills/auto-finding-capture/SKILL.md +28 -1
  78. package/skills/build-loop/SKILL.md +131 -23
  79. package/skills/build-loop/fallbacks.md +16 -21
  80. package/skills/build-loop/phases/ui-validation.md +2 -2
  81. package/skills/build-loop/references/advisor-dispatch-ladder.md +1 -1
  82. package/skills/build-loop/references/apple-native-planning.md +1 -1
  83. package/skills/build-loop/references/autonomous-and-per-commit-modes.md +11 -5
  84. package/skills/build-loop/references/autonomy-dashboard.md +115 -0
  85. package/skills/build-loop/references/capability-routing.md +24 -2
  86. package/skills/build-loop/references/coordination.md +24 -6
  87. package/skills/build-loop/references/experiment-results-template.md +15 -3
  88. package/skills/build-loop/references/leadership.md +1 -1
  89. package/skills/build-loop/references/memory.md +14 -3
  90. package/skills/build-loop/references/modular-systems-pack.md +8 -0
  91. package/skills/build-loop/references/output-style.md +86 -0
  92. package/skills/build-loop/references/phase-1-assess.md +102 -2
  93. package/skills/build-loop/references/phase-2-plan.md +9 -1
  94. package/skills/build-loop/references/phase-3-execute.md +5 -2
  95. package/skills/build-loop/references/phase-4-review.md +85 -8
  96. package/skills/build-loop/references/phase-5-iterate.md +76 -8
  97. package/skills/build-loop/references/phase-6-learn.md +10 -17
  98. package/skills/build-loop/references/privileged-request-broker.md +254 -0
  99. package/skills/build-loop/references/resource-aware-execution.md +183 -0
  100. package/skills/build-loop/references/self-recursive-dev.md +2 -2
  101. package/skills/build-loop/references/status-output-format.md +207 -0
  102. package/skills/build-loop/references/verify-dispatch.md +56 -2
  103. package/skills/building-with-deepagents/SKILL.md +1 -1
  104. package/skills/claim-scope/SKILL.md +185 -0
  105. package/skills/color-engine/SKILL.md +103 -0
  106. package/skills/color-engine/_core.py +464 -0
  107. package/skills/color-engine/color_engine.py +175 -0
  108. package/skills/cost-rca/SKILL.md +61 -0
  109. package/skills/data-plane-worktrees/SKILL.md +139 -0
  110. package/skills/data-plane-worktrees/agents/openai.yaml +4 -0
  111. package/skills/database-practice/SKILL.md +200 -0
  112. package/skills/database-practice/references/diagnostic-queries.sql +126 -0
  113. package/skills/database-practice/references/vector-and-graph-tuning.md +208 -0
  114. package/skills/database-practice/scripts/db_table_map.py +1244 -0
  115. package/skills/database-practice/scripts/test_db_table_map.py +514 -0
  116. package/skills/debug-loop/SKILL.md +36 -6
  117. package/skills/debugging-memory/SKILL.md +32 -430
  118. package/skills/debugging-memory/references/pattern-extraction.md +4 -4
  119. package/skills/debugging-memory/references/search.md +32 -120
  120. package/skills/debugging-memory/references/store.md +32 -126
  121. package/skills/debugging-memory/references/subagent-integration.md +1 -1
  122. package/skills/decision-queue/SKILL.md +251 -0
  123. package/skills/decision-queue/assets/template.html +1242 -0
  124. package/skills/decision-queue/references/example-large-queue-batching.md +164 -0
  125. package/skills/decision-queue/scripts/regen_template_constants.py +160 -0
  126. package/skills/defenseclaw-bridge/SKILL.md +2 -2
  127. package/skills/defenseclaw-bridge/references/dc-config-mapping.md +2 -9
  128. package/skills/drain-proposals/SKILL.md +53 -0
  129. package/skills/focused-loop-builder/SKILL.md +31 -0
  130. package/skills/focused-loop-builder/references/spec-format.md +27 -0
  131. package/skills/handoff/SKILL.md +169 -8
  132. package/skills/ibr-bridge/SKILL.md +4 -1
  133. package/skills/knowledge/SKILL.md +26 -14
  134. package/skills/knowledge/references/review-mode.md +2 -3
  135. package/skills/knowledge/templates/madr-minimal.md +1 -1
  136. package/skills/mcp-builder/SKILL.md +1 -1
  137. package/skills/model-bakeoff/SKILL.md +48 -10
  138. package/skills/model-tiering/SKILL.md +92 -31
  139. package/skills/native-ax-driver/SKILL.md +38 -5
  140. package/skills/native-ax-driver/scripts/native_driver.py +278 -22
  141. package/skills/native-ax-driver/scripts/test_native_driver.py +227 -0
  142. package/skills/optimize/SKILL.md +1 -1
  143. package/skills/plugin-builder/SKILL.md +48 -1
  144. package/skills/plugin-builder/references/build-loop-phase-guidance.md +3 -4
  145. package/skills/plugin-builder/references/distribution.md +13 -2
  146. package/skills/plugin-builder/references/plugin-hygiene-lessons.md +2 -2
  147. package/skills/plugin-tests/SKILL.md +2 -2
  148. package/skills/recursive-retrospective/SKILL.md +1 -1
  149. package/skills/repo-closeout/SKILL.md +17 -0
  150. package/skills/repo-closeout/agents/openai.yaml +4 -0
  151. package/skills/repo-maintenance/SKILL.md +179 -0
  152. package/skills/repo-maintenance/agents/openai.yaml +4 -0
  153. package/skills/repo-maintenance/references/pre-public-hygiene.md +134 -0
  154. package/skills/repo-maintenance/references/repository-taxonomy.md +161 -0
  155. package/skills/repo-maintenance/references/safety-protocol.md +106 -0
  156. package/skills/repo-maintenance/references/stack-profiles.md +138 -0
  157. package/skills/repo-maintenance/scripts/audit_repo_maintenance.py +1198 -0
  158. package/skills/repo-maintenance/scripts/test_audit_repo_maintenance.py +506 -0
  159. package/skills/repository-intelligence/SKILL.md +189 -0
  160. package/skills/repository-intelligence/agents/openai.yaml +4 -0
  161. package/skills/repository-intelligence/references/assessment-rubric.md +88 -0
  162. package/skills/repository-intelligence/scripts/repository_inventory.py +347 -0
  163. package/skills/research/SKILL.md +12 -2
  164. package/skills/root-cause-analysis/SKILL.md +1 -1
  165. package/skills/runtime-parity-verification/SKILL.md +36 -1
  166. package/skills/security-methodology/SKILL.md +22 -9
  167. package/skills/security-methodology/references/agentic-handoff-templates.md +220 -0
  168. package/skills/security-methodology/references/cross-source-matrix.md +1 -1
  169. package/skills/security-methodology/references/owasp-agentic-top-10.md +1 -1
  170. package/skills/security-scan/SKILL.md +55 -15
  171. package/skills/self-improve/SKILL.md +70 -50
  172. package/skills/silent-assumptions/SKILL.md +341 -0
  173. package/skills/silent-assumptions/references/elicitation-detectors.md +342 -0
  174. package/skills/spec-writing/SKILL.md +128 -24
  175. package/skills/spec-writing/scripts/check_checklist.py +114 -15
  176. package/skills/ui-design/SKILL.md +6 -4
  177. package/skills/ui-design/references/color-engine.md +132 -0
  178. package/skills/ui-design/references/design-preferences-from-owned-apps.md +8 -8
  179. package/skills/ui-design/references/ui-guidance-sources.md +1 -1
  180. package/skills/ui-design/references/universal-design-principles.alt.md +2 -2
  181. package/plugin-artifacts/codex/.codex-plugin/plugin.json +0 -41
  182. package/plugin-artifacts/codex/AGENTS.md +0 -560
  183. package/plugin-artifacts/codex/BUILD-ARTIFACT.md +0 -5
  184. package/plugin-artifacts/codex/LICENSE +0 -202
  185. package/plugin-artifacts/codex/README.md +0 -313
  186. package/plugin-artifacts/codex/assets/build-loop-plugin-icon.png +0 -0
  187. package/plugin-artifacts/codex/docs/agent-surface-policy.md +0 -63
  188. package/plugin-artifacts/codex/references/advisor-dispatch-ladder.md +0 -62
  189. package/plugin-artifacts/codex/references/agent-role-taxonomy.md +0 -135
  190. package/plugin-artifacts/codex/references/autonomous-and-per-commit-modes.md +0 -161
  191. package/plugin-artifacts/codex/references/autonomy-config.md +0 -231
  192. package/plugin-artifacts/codex/references/backlog-system.md +0 -285
  193. package/plugin-artifacts/codex/references/capability-routing.md +0 -231
  194. package/plugin-artifacts/codex/references/codex-subagents.md +0 -106
  195. package/plugin-artifacts/codex/references/coordination-file-template.md +0 -181
  196. package/plugin-artifacts/codex/references/coordination-rules.md +0 -552
  197. package/plugin-artifacts/codex/references/dogfood-reload-checkpoint.md +0 -112
  198. package/plugin-artifacts/codex/references/halt-and-ask-protocol.md +0 -102
  199. package/plugin-artifacts/codex/references/implementer-envelope-schema.md +0 -302
  200. package/plugin-artifacts/codex/references/intent-capability-pack.md +0 -257
  201. package/plugin-artifacts/codex/references/intent-exploration-prompts.md +0 -96
  202. package/plugin-artifacts/codex/references/leadership.md +0 -72
  203. package/plugin-artifacts/codex/references/memory-systems.md +0 -261
  204. package/plugin-artifacts/codex/references/memory.md +0 -313
  205. package/plugin-artifacts/codex/references/model-tier-mapping.md +0 -296
  206. package/plugin-artifacts/codex/references/modular-systems-pack.md +0 -96
  207. package/plugin-artifacts/codex/references/phase-1-assess.md +0 -249
  208. package/plugin-artifacts/codex/references/phase-2-plan.md +0 -86
  209. package/plugin-artifacts/codex/references/phase-3-execute.md +0 -49
  210. package/plugin-artifacts/codex/references/phase-4-review.md +0 -341
  211. package/plugin-artifacts/codex/references/phase-5-iterate.md +0 -72
  212. package/plugin-artifacts/codex/references/phase-6-learn.md +0 -58
  213. package/plugin-artifacts/codex/references/recent-design-structures.md +0 -274
  214. package/plugin-artifacts/codex/references/research-trigger-policy.md +0 -140
  215. package/plugin-artifacts/codex/references/runtime-smoke-triggers.md +0 -42
  216. package/plugin-artifacts/codex/references/self-review.md +0 -234
  217. package/plugin-artifacts/codex/references/single-writer-commit-protocol.md +0 -90
  218. package/plugin-artifacts/codex/references/task-capture-policy.md +0 -68
  219. package/plugin-artifacts/codex/references/ui-io-contract.md +0 -116
  220. package/plugin-artifacts/codex/references/ui-spotcheck-protocol.md +0 -65
  221. package/plugin-artifacts/codex/references/verify-dispatch.md +0 -85
  222. package/plugin-artifacts/codex/skills/build-loop/SKILL.md +0 -381
  223. package/plugin-artifacts/codex/skills/build-loop/detect-plugins.mjs +0 -82
  224. package/plugin-artifacts/codex/skills/build-loop/eval-guide.md +0 -65
  225. package/plugin-artifacts/codex/skills/build-loop/fallbacks.md +0 -549
  226. package/plugin-artifacts/codex/skills/build-loop/phases/fact-check.md +0 -42
  227. package/plugin-artifacts/codex/skills/build-loop/phases/ui-validation.md +0 -267
  228. package/plugin-artifacts/codex/skills/build-loop/references/advisor-dispatch-ladder.md +0 -62
  229. package/plugin-artifacts/codex/skills/build-loop/references/apple-native-planning.md +0 -439
  230. package/plugin-artifacts/codex/skills/build-loop/references/autonomous-and-per-commit-modes.md +0 -161
  231. package/plugin-artifacts/codex/skills/build-loop/references/capability-routing.md +0 -231
  232. package/plugin-artifacts/codex/skills/build-loop/references/codex-subagents.md +0 -106
  233. package/plugin-artifacts/codex/skills/build-loop/references/coordination.md +0 -161
  234. package/plugin-artifacts/codex/skills/build-loop/references/correction-aware-capture.md +0 -177
  235. package/plugin-artifacts/codex/skills/build-loop/references/experiment-results-template.md +0 -101
  236. package/plugin-artifacts/codex/skills/build-loop/references/independent-auditor.md +0 -72
  237. package/plugin-artifacts/codex/skills/build-loop/references/intent-capability-pack.md +0 -257
  238. package/plugin-artifacts/codex/skills/build-loop/references/intent-exploration-prompts.md +0 -96
  239. package/plugin-artifacts/codex/skills/build-loop/references/leadership.md +0 -72
  240. package/plugin-artifacts/codex/skills/build-loop/references/memory.md +0 -313
  241. package/plugin-artifacts/codex/skills/build-loop/references/modular-systems-pack.md +0 -96
  242. package/plugin-artifacts/codex/skills/build-loop/references/output-style.md +0 -222
  243. package/plugin-artifacts/codex/skills/build-loop/references/pay-it-forward-arch.md +0 -98
  244. package/plugin-artifacts/codex/skills/build-loop/references/phase-1-assess.md +0 -249
  245. package/plugin-artifacts/codex/skills/build-loop/references/phase-2-plan.md +0 -86
  246. package/plugin-artifacts/codex/skills/build-loop/references/phase-3-execute.md +0 -49
  247. package/plugin-artifacts/codex/skills/build-loop/references/phase-4-review.md +0 -341
  248. package/plugin-artifacts/codex/skills/build-loop/references/phase-5-iterate.md +0 -72
  249. package/plugin-artifacts/codex/skills/build-loop/references/phase-6-learn.md +0 -58
  250. package/plugin-artifacts/codex/skills/build-loop/references/recent-design-structures.md +0 -274
  251. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/ASSESSMENT.md +0 -85
  252. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/STANDALONE_TEST_RUN.md +0 -149
  253. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/01-simple-bugfix.md +0 -32
  254. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/02-ui-build-with-iteration.md +0 -48
  255. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/03-multi-failure-escalation.md +0 -60
  256. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/04-ui-build-ibr-absent.md +0 -51
  257. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/05-refactor-navgator-absent.md +0 -71
  258. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/scenarios/06-recurring-bug-debugger-absent.md +0 -52
  259. package/plugin-artifacts/codex/skills/build-loop/references/refactor-history/trace-comparison.md +0 -202
  260. package/plugin-artifacts/codex/skills/build-loop/references/self-recursive-dev.md +0 -77
  261. package/plugin-artifacts/codex/skills/build-loop/references/self-review.md +0 -234
  262. package/plugin-artifacts/codex/skills/build-loop/references/ui-io-contract.md +0 -116
  263. package/plugin-artifacts/codex/skills/build-loop/references/verify-dispatch.md +0 -85
  264. package/plugin-artifacts/codex/skills/build-loop/scanners/audit-design-rules.mjs +0 -476
  265. package/plugin-artifacts/codex/skills/build-loop/scanners/require-visual-evidence.mjs +0 -239
  266. package/plugin-artifacts/codex/skills/build-loop/templates/backlog-item.md +0 -35
  267. package/plugin-artifacts/codex/skills/build-loop/templates/codex-worker-prompt.md +0 -100
  268. package/plugin-artifacts/codex/skills/build-loop/templates/ui-subagent-prompt.md +0 -179
  269. package/plugin-artifacts/codex/skills/build-loop/templates/ux-fix-plan.md +0 -40
  270. package/scripts/build_codex_plugin_artifact.py +0 -321
@@ -14,10 +14,10 @@ synthetic mouse or keyboard events — the user's hardware cursor is never moved
14
14
  Usage (from a build-loop run, project cwd):
15
15
 
16
16
  # Resolve a running app to its pid (no AX permission needed for this)
17
- python3 native_driver.py resolve --app "Secrets Vault"
17
+ python3 native_driver.py resolve --app "MyApp"
18
18
 
19
19
  # Scan AX tree of running app
20
- python3 native_driver.py scan --app "Secrets Vault" --json
20
+ python3 native_driver.py scan --app "MyApp" --json
21
21
  python3 native_driver.py scan --pid 12345 --json
22
22
 
23
23
  # Perform an AX action on a specific element
@@ -34,6 +34,11 @@ Usage (from a build-loop run, project cwd):
34
34
  # List running regular GUI apps (no AX permission needed)
35
35
  python3 native_driver.py apps
36
36
 
37
+ # Launch an isolated instance under a private state dir and capture its pid
38
+ # (see SKILL.md "Single-instance PID-scoped verification mode")
39
+ python3 native_driver.py launch --app-path /Applications/MyApp.app \\
40
+ --state-env-var ET_STATE_DIR --state-dir /tmp/myapp-verify --fresh
41
+
37
42
  # Analyze a captured AX tree for centered-narrow layout gaps
38
43
  python3 native_driver.py analyze-layout --stdin < ax-tree.json
39
44
 
@@ -49,6 +54,7 @@ import os
49
54
  import shutil
50
55
  import subprocess
51
56
  import sys
57
+ import time
52
58
  from pathlib import Path
53
59
 
54
60
  # ─── Paths ────────────────────────────────────────────────────────────────────
@@ -180,28 +186,33 @@ def cmd_preflight(_args: argparse.Namespace) -> int:
180
186
  return 2
181
187
 
182
188
 
183
- def cmd_apps(_args: argparse.Namespace) -> int:
184
- """List regular GUI apps with their pid and bundle identifier (no AX needed)."""
189
+ def _query_gui_processes() -> list[dict]:
190
+ """Query System Events for every foreground GUI process: name, pid, bundle id.
191
+
192
+ Raises FileNotFoundError (no osascript) or subprocess.CalledProcessError
193
+ (AppleScript failed) — callers decide how to surface those.
194
+ """
195
+ # Fetch each property as its own list and concatenate — a record
196
+ # (`{name, unix id, ...} of ...`) or a bound variable holding the filtered
197
+ # collection both raise -1728 on current macOS; the direct per-property form
198
+ # is the reliable one. Restrict to `application processes` (not `every
199
+ # process`): background-only daemons lack a `background only` property and
200
+ # error the filter. Order (names, pids, bundles) matches the flat-thirds
201
+ # parser below.
202
+ proc_set = "(application processes whose background only is false)"
185
203
  script = (
186
- 'tell application "System Events" to '
187
- 'get {name, unix id, bundle identifier} of every process '
188
- "whose background only is false"
204
+ 'tell application "System Events" to return '
205
+ f"(name of {proc_set}) & "
206
+ f"(unix id of {proc_set}) & "
207
+ f"(bundle identifier of {proc_set})"
208
+ )
209
+ result = subprocess.run(
210
+ ["osascript", "-e", script],
211
+ capture_output=True,
212
+ text=True,
213
+ timeout=10,
214
+ check=True,
189
215
  )
190
- try:
191
- result = subprocess.run(
192
- ["osascript", "-e", script],
193
- capture_output=True,
194
- text=True,
195
- timeout=10,
196
- check=True,
197
- )
198
- except FileNotFoundError:
199
- print(json.dumps({"error": "osascript missing"}), file=sys.stderr)
200
- return 1
201
- except subprocess.CalledProcessError as e:
202
- print(json.dumps({"error": e.stderr.strip() or "osascript failed"}), file=sys.stderr)
203
- return 1
204
-
205
216
  raw = result.stdout.strip()
206
217
  # AppleScript returns a flat list: name1, name2, ..., pid1, pid2, ..., bundle1, ...
207
218
  parts = [p.strip() for p in raw.split(",")]
@@ -215,6 +226,41 @@ def cmd_apps(_args: argparse.Namespace) -> int:
215
226
  "bundleIdentifier": parts[2 * n + i] if parts[2 * n + i] != "missing value" else None,
216
227
  }
217
228
  )
229
+ return apps
230
+
231
+
232
+ def _gui_pids(bundle_id: str | None = None) -> set[int]:
233
+ """Pid set of running GUI processes, optionally filtered to a bundle id.
234
+
235
+ Swallows osascript failures and returns an empty set — callers that need
236
+ to distinguish "no processes" from "query failed" should call
237
+ `_query_gui_processes()` directly instead.
238
+ """
239
+ try:
240
+ apps = _query_gui_processes()
241
+ except (FileNotFoundError, subprocess.CalledProcessError, subprocess.TimeoutExpired):
242
+ return set()
243
+ pids: set[int] = set()
244
+ for app in apps:
245
+ if app["pid"] is None:
246
+ continue
247
+ if bundle_id is not None and app.get("bundleIdentifier") != bundle_id:
248
+ continue
249
+ pids.add(app["pid"])
250
+ return pids
251
+
252
+
253
+ def cmd_apps(_args: argparse.Namespace) -> int:
254
+ """List regular GUI apps with their pid and bundle identifier (no AX needed)."""
255
+ try:
256
+ apps = _query_gui_processes()
257
+ except FileNotFoundError:
258
+ print(json.dumps({"error": "osascript missing"}), file=sys.stderr)
259
+ return 1
260
+ except subprocess.CalledProcessError as e:
261
+ print(json.dumps({"error": e.stderr.strip() or "osascript failed"}), file=sys.stderr)
262
+ return 1
263
+
218
264
  print(json.dumps(apps, indent=2))
219
265
  return 0
220
266
 
@@ -234,6 +280,185 @@ def cmd_resolve(args: argparse.Namespace) -> int:
234
280
  return 0
235
281
 
236
282
 
283
+ def select_new_pid(before: set[int], after: set[int]) -> int | None:
284
+ """Return the single new pid in `after - before`, or None if 0 or >1.
285
+
286
+ Pure and deterministic — the caller must treat None as "ambiguous, do
287
+ not guess" rather than picking an arbitrary candidate.
288
+ """
289
+ new = after - before
290
+ if len(new) == 1:
291
+ return next(iter(new))
292
+ return None
293
+
294
+
295
+ def build_launch_env(base_env: dict, state_env_var: str | None, state_dir: str | None) -> dict:
296
+ """Return a COPY of base_env with state_env_var=state_dir set, iff both given.
297
+
298
+ Never mutates base_env.
299
+ """
300
+ env = dict(base_env)
301
+ if state_env_var and state_dir:
302
+ env[state_env_var] = state_dir
303
+ return env
304
+
305
+
306
+ def build_open_command(
307
+ target: str, *, by_bundle_id: bool, fresh: bool, args: list[str]
308
+ ) -> list[str]:
309
+ """Build the macOS `open` argv for an isolated-instance launch.
310
+
311
+ Always forces a new instance (`-n`). Adds `-F` (ignore saved window/scene
312
+ state) when `fresh` — never wedge into the app's saved state from a prior
313
+ run. Targets by bundle id (`-b`) or by `.app` path. `--args` is only
314
+ appended when there are extra argv to pass through.
315
+ """
316
+ cmd = ["open", "-n"]
317
+ if fresh:
318
+ cmd.append("-F")
319
+ if by_bundle_id:
320
+ cmd += ["-b", target]
321
+ else:
322
+ cmd.append(target)
323
+ if args:
324
+ cmd.append("--args")
325
+ cmd += list(args)
326
+ return cmd
327
+
328
+
329
+ def _derive_bundle_id(app_path: str) -> str | None:
330
+ """Best-effort CFBundleIdentifier lookup from an .app's Info.plist.
331
+
332
+ Used only to filter candidate pids and for the launch report — never
333
+ raises; returns None on any failure (missing `defaults`, malformed
334
+ bundle, timeout).
335
+ """
336
+ info_plist_prefix = str(Path(app_path) / "Contents" / "Info")
337
+ try:
338
+ result = subprocess.run(
339
+ ["defaults", "read", info_plist_prefix, "CFBundleIdentifier"],
340
+ capture_output=True,
341
+ text=True,
342
+ timeout=5,
343
+ )
344
+ except (FileNotFoundError, subprocess.TimeoutExpired):
345
+ return None
346
+ if result.returncode != 0:
347
+ return None
348
+ bundle_id = result.stdout.strip()
349
+ return bundle_id or None
350
+
351
+
352
+ def cmd_launch(args: argparse.Namespace) -> int:
353
+ """Launch an isolated app instance under a private state dir and capture its pid.
354
+
355
+ This is the deterministic launch + pid-capture half of the "single-instance
356
+ PID-scoped verification mode" (see SKILL.md). It never assumes which pid
357
+ belongs to the new instance — it snapshots the running GUI pid set before
358
+ launch, launches with `open -n` (always a new instance), and diffs the
359
+ pid set after launch. Ambiguous results (zero or more than one new pid)
360
+ are reported as failure rather than guessed.
361
+ """
362
+ if bool(args.app_path) == bool(args.bundle_id):
363
+ # The argparse mutually-exclusive required group should prevent this;
364
+ # guard defensively so a programmatic caller gets a clean error too.
365
+ print(
366
+ json.dumps(
367
+ {
368
+ "success": False,
369
+ "pid": None,
370
+ "error": "exactly one of --app-path/--bundle-id required",
371
+ }
372
+ ),
373
+ file=sys.stderr,
374
+ )
375
+ return 2
376
+
377
+ if args.bundle_id:
378
+ target = args.bundle_id
379
+ by_bundle_id = True
380
+ bundle_id = args.bundle_id
381
+ else:
382
+ target = str(args.app_path)
383
+ by_bundle_id = False
384
+ bundle_id = _derive_bundle_id(target)
385
+
386
+ def _fail(error: str) -> int:
387
+ print(
388
+ json.dumps(
389
+ {
390
+ "success": False,
391
+ "pid": None,
392
+ "bundle_id": bundle_id,
393
+ "state_dir": args.state_dir,
394
+ "fresh": args.fresh,
395
+ "error": error,
396
+ "next": None,
397
+ }
398
+ )
399
+ )
400
+ return 1
401
+
402
+ # A failed before-snapshot is a HARD launch failure, not an empty baseline.
403
+ # _gui_pids() swallows query errors into an empty set; if we used it here and
404
+ # the osascript query failed while the user had one ambient instance running,
405
+ # the after-poll would surface that ambient pid as "new" and we would drive
406
+ # the user's live window — the exact failure this mode exists to prevent.
407
+ try:
408
+ before = {
409
+ app["pid"]
410
+ for app in _query_gui_processes()
411
+ if app["pid"] is not None
412
+ }
413
+ except (FileNotFoundError, subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
414
+ return _fail(f"could not snapshot GUI pids before launch: {e}")
415
+ env = build_launch_env(dict(os.environ), args.state_env_var, args.state_dir)
416
+ open_argv = build_open_command(
417
+ target, by_bundle_id=by_bundle_id, fresh=args.fresh, args=args.args or []
418
+ )
419
+
420
+ try:
421
+ proc = subprocess.run(open_argv, env=env, capture_output=True, text=True, timeout=15)
422
+ except (subprocess.TimeoutExpired, FileNotFoundError) as e:
423
+ return _fail(f"`open` failed to run: {e}")
424
+
425
+ if proc.returncode != 0:
426
+ return _fail(proc.stderr.strip() or f"open exited {proc.returncode}")
427
+
428
+ deadline = time.monotonic() + args.timeout
429
+ pid: int | None = None
430
+ while time.monotonic() < deadline:
431
+ after = _gui_pids(bundle_id=bundle_id)
432
+ pid = select_new_pid(before, after)
433
+ if pid is not None:
434
+ break
435
+ time.sleep(0.25)
436
+
437
+ if pid is None:
438
+ return _fail(
439
+ "could not capture a single new pid (zero or ambiguous candidates) "
440
+ "within timeout"
441
+ )
442
+
443
+ print(
444
+ json.dumps(
445
+ {
446
+ "success": True,
447
+ "pid": pid,
448
+ "bundle_id": bundle_id,
449
+ "state_dir": args.state_dir,
450
+ "fresh": args.fresh,
451
+ "error": None,
452
+ "next": (
453
+ f"drive AX scoped to this pid: native_driver.py scan --pid {pid} "
454
+ f"/ action --pid {pid} ..."
455
+ ),
456
+ }
457
+ )
458
+ )
459
+ return 0
460
+
461
+
237
462
  def cmd_scan(args: argparse.Namespace) -> int:
238
463
  binary = ensure_binary()
239
464
  cmd: list[str] = [str(binary)]
@@ -440,6 +665,36 @@ def build_parser() -> argparse.ArgumentParser:
440
665
  p_resolve = sub.add_parser("resolve", help="Resolve an app name to its pid")
441
666
  p_resolve.add_argument("--app", required=True)
442
667
 
668
+ p_launch = sub.add_parser(
669
+ "launch",
670
+ help="Launch an isolated app instance under a private state dir and capture its pid",
671
+ )
672
+ launch_target = p_launch.add_mutually_exclusive_group(required=True)
673
+ launch_target.add_argument("--app-path", help="Path to a .app bundle")
674
+ launch_target.add_argument("--bundle-id", help="Bundle identifier, e.g. com.example.myapp")
675
+ p_launch.add_argument("--state-dir", help="Isolated state directory for this instance")
676
+ p_launch.add_argument(
677
+ "--state-env-var",
678
+ help="Env var name to point at --state-dir, e.g. ET_STATE_DIR",
679
+ )
680
+ p_launch.add_argument(
681
+ "--fresh",
682
+ action="store_true",
683
+ help="Pass -F to `open` (ignore saved window/scene state)",
684
+ )
685
+ p_launch.add_argument(
686
+ "--arg",
687
+ action="append",
688
+ dest="args",
689
+ help="Extra argv passed through to the launched app (repeatable)",
690
+ )
691
+ p_launch.add_argument(
692
+ "--timeout",
693
+ type=float,
694
+ default=10.0,
695
+ help="Seconds to wait for the new pid to appear (default 10.0)",
696
+ )
697
+
443
698
  p_scan = sub.add_parser("scan", help="Scan AX tree of running app")
444
699
  p_scan.add_argument("--pid", type=int)
445
700
  p_scan.add_argument("--app")
@@ -479,6 +734,7 @@ def main(argv: list[str] | None = None) -> int:
479
734
  "preflight": cmd_preflight,
480
735
  "apps": cmd_apps,
481
736
  "resolve": cmd_resolve,
737
+ "launch": cmd_launch,
482
738
  "scan": cmd_scan,
483
739
  "analyze-layout": cmd_analyze_layout,
484
740
  "action": cmd_action,
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env python3
2
+ # SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com>
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ """
5
+ Tests for the pure launch / pid-capture / scope-selection helpers in
6
+ native_driver.py.
7
+
8
+ Honest scope: the live path — `launch` actually invoking macOS `open`,
9
+ snapshotting real GUI process pids via osascript, and driving the Swift AX
10
+ binary against the captured pid — requires a running macOS GUI session and
11
+ Accessibility permission. That path cannot be exercised headlessly and is
12
+ NOT unit-tested here; it is covered by the SKILL.md manual self-test
13
+ ("Single-instance PID-scoped verification mode").
14
+
15
+ What IS tested here is the pure, deterministic logic that decides which pid
16
+ belongs to the freshly-launched instance and how the `open` invocation is
17
+ built — the part that must never guess. These functions take no OS action
18
+ and have no side effects, so they run anywhere pytest runs.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import json
24
+ import shutil
25
+
26
+ import pytest
27
+
28
+ import native_driver
29
+
30
+
31
+ # ─── select_new_pid ────────────────────────────────────────────────────────
32
+
33
+
34
+ def test_select_new_pid_single_new_pid():
35
+ assert native_driver.select_new_pid({1, 2}, {1, 2, 3}) == 3
36
+
37
+
38
+ def test_select_new_pid_identical_sets_returns_none():
39
+ assert native_driver.select_new_pid({1, 2}, {1, 2}) is None
40
+
41
+
42
+ def test_select_new_pid_two_new_pids_is_ambiguous():
43
+ assert native_driver.select_new_pid({1, 2}, {1, 2, 3, 4}) is None
44
+
45
+
46
+ def test_select_new_pid_empty_before_one_after():
47
+ assert native_driver.select_new_pid(set(), {42}) == 42
48
+
49
+
50
+ def test_select_new_pid_empty_before_empty_after():
51
+ assert native_driver.select_new_pid(set(), set()) is None
52
+
53
+
54
+ def test_select_new_pid_pid_disappeared_is_not_new():
55
+ # before has a pid that's gone in after; no new pid appeared.
56
+ assert native_driver.select_new_pid({1, 2, 3}, {1, 2}) is None
57
+
58
+
59
+ # ─── build_launch_env ──────────────────────────────────────────────────────
60
+
61
+
62
+ def test_build_launch_env_sets_key_when_both_given():
63
+ base = {"PATH": "/usr/bin"}
64
+ env = native_driver.build_launch_env(base, "ET_STATE_DIR", "/tmp/state")
65
+ assert env["ET_STATE_DIR"] == "/tmp/state"
66
+ assert env["PATH"] == "/usr/bin"
67
+
68
+
69
+ def test_build_launch_env_does_not_mutate_input():
70
+ base = {"PATH": "/usr/bin"}
71
+ native_driver.build_launch_env(base, "ET_STATE_DIR", "/tmp/state")
72
+ assert base == {"PATH": "/usr/bin"}
73
+ assert "ET_STATE_DIR" not in base
74
+
75
+
76
+ def test_build_launch_env_no_state_env_var_returns_unchanged_copy():
77
+ base = {"PATH": "/usr/bin"}
78
+ env = native_driver.build_launch_env(base, None, "/tmp/state")
79
+ assert env == base
80
+ assert env is not base
81
+
82
+
83
+ def test_build_launch_env_no_state_dir_returns_unchanged_copy():
84
+ base = {"PATH": "/usr/bin"}
85
+ env = native_driver.build_launch_env(base, "ET_STATE_DIR", None)
86
+ assert env == base
87
+ assert env is not base
88
+
89
+
90
+ def test_build_launch_env_both_none_returns_unchanged_copy():
91
+ base = {"PATH": "/usr/bin"}
92
+ env = native_driver.build_launch_env(base, None, None)
93
+ assert env == base
94
+ assert env is not base
95
+
96
+
97
+ # ─── build_open_command ────────────────────────────────────────────────────
98
+
99
+
100
+ def test_build_open_command_always_includes_force_new_instance():
101
+ cmd = native_driver.build_open_command(
102
+ "/Applications/MyApp.app", by_bundle_id=False, fresh=False, args=[]
103
+ )
104
+ assert "-n" in cmd
105
+
106
+
107
+ def test_build_open_command_fresh_true_includes_dash_F():
108
+ cmd = native_driver.build_open_command(
109
+ "/Applications/MyApp.app", by_bundle_id=False, fresh=True, args=[]
110
+ )
111
+ assert "-F" in cmd
112
+
113
+
114
+ def test_build_open_command_fresh_false_excludes_dash_F():
115
+ cmd = native_driver.build_open_command(
116
+ "/Applications/MyApp.app", by_bundle_id=False, fresh=False, args=[]
117
+ )
118
+ assert "-F" not in cmd
119
+
120
+
121
+ def test_build_open_command_by_bundle_id_uses_dash_b():
122
+ cmd = native_driver.build_open_command(
123
+ "com.example.myapp", by_bundle_id=True, fresh=False, args=[]
124
+ )
125
+ assert "-b" in cmd
126
+ assert cmd[cmd.index("-b") + 1] == "com.example.myapp"
127
+ assert "com.example.myapp" not in cmd[: cmd.index("-b")]
128
+
129
+
130
+ def test_build_open_command_by_app_path_uses_raw_path():
131
+ cmd = native_driver.build_open_command(
132
+ "/Applications/MyApp.app", by_bundle_id=False, fresh=False, args=[]
133
+ )
134
+ assert "-b" not in cmd
135
+ assert "/Applications/MyApp.app" in cmd
136
+
137
+
138
+ def test_build_open_command_appends_args_when_nonempty():
139
+ cmd = native_driver.build_open_command(
140
+ "/Applications/MyApp.app",
141
+ by_bundle_id=False,
142
+ fresh=False,
143
+ args=["--flag", "value"],
144
+ )
145
+ assert "--args" in cmd
146
+ idx = cmd.index("--args")
147
+ assert cmd[idx + 1 :] == ["--flag", "value"]
148
+
149
+
150
+ def test_build_open_command_no_args_omits_dash_dash_args():
151
+ cmd = native_driver.build_open_command(
152
+ "/Applications/MyApp.app", by_bundle_id=False, fresh=False, args=[]
153
+ )
154
+ assert "--args" not in cmd
155
+
156
+
157
+ def test_build_open_command_full_shape_fresh_bundle_with_args():
158
+ cmd = native_driver.build_open_command(
159
+ "com.example.myapp",
160
+ by_bundle_id=True,
161
+ fresh=True,
162
+ args=["--seed", "123"],
163
+ )
164
+ assert cmd == ["open", "-n", "-F", "-b", "com.example.myapp", "--args", "--seed", "123"]
165
+
166
+
167
+ # ─── cmd_launch before-snapshot safety (auditor finding f1) ─────────────────
168
+ #
169
+ # The one path where this mode could hand back the USER's pid: if the
170
+ # before-snapshot query fails, an empty baseline makes a lone ambient pid look
171
+ # "new". cmd_launch must treat a failed before-snapshot as a HARD launch
172
+ # failure and NEVER invoke `open`.
173
+
174
+
175
+ def test_cmd_launch_hard_fails_when_before_snapshot_query_errors(monkeypatch, capsys):
176
+ import argparse
177
+ import subprocess as _subprocess
178
+
179
+ def _raise(*_a, **_k):
180
+ raise _subprocess.CalledProcessError(1, ["osascript"], stderr="boom")
181
+
182
+ monkeypatch.setattr(native_driver, "_query_gui_processes", _raise)
183
+
184
+ # If `open` (or any subprocess) is invoked, the guard failed — fail loudly.
185
+ def _forbidden(*_a, **_k):
186
+ raise AssertionError("cmd_launch invoked a subprocess despite a failed before-snapshot")
187
+
188
+ monkeypatch.setattr(native_driver.subprocess, "run", _forbidden)
189
+
190
+ args = argparse.Namespace(
191
+ app_path=None,
192
+ bundle_id="com.example.myapp",
193
+ state_dir=None,
194
+ state_env_var=None,
195
+ fresh=False,
196
+ args=[],
197
+ timeout=1.0,
198
+ )
199
+ rc = native_driver.cmd_launch(args)
200
+ out = capsys.readouterr().out
201
+ assert rc == 1
202
+ payload = json.loads(out)
203
+ assert payload["success"] is False
204
+ assert "snapshot" in payload["error"].lower()
205
+
206
+
207
+ @pytest.mark.skipif(
208
+ shutil.which("osascript") is None,
209
+ reason="live osascript smoke — requires a macOS GUI session; skipped headless",
210
+ )
211
+ def test_query_gui_processes_live_smoke():
212
+ """Invoke the REAL osascript query, not a mock.
213
+
214
+ The mocked unit tests above cannot validate the AppleScript STRING — which
215
+ is exactly how a malformed multi-property query (`{name, unix id, bundle
216
+ identifier} of every process whose ...`) shipped and raised -1728 at
217
+ runtime (2026-07-19), leaving `_gui_pids()` empty and the PID-scoped launch
218
+ non-functional while every unit test stayed green. This asserts the query
219
+ actually parses and returns a plausible process list on a real macOS
220
+ session; it skips cleanly in headless CI.
221
+ """
222
+ apps = native_driver._query_gui_processes()
223
+ assert isinstance(apps, list)
224
+ assert apps, "no GUI processes returned — the AppleScript query is likely malformed"
225
+ for a in apps:
226
+ assert {"name", "pid", "bundleIdentifier"} <= set(a)
227
+ assert any(a["pid"] for a in apps), "no real pids captured — launch pid-capture would fail"
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: optimize
3
3
  description: Use when the user says "optimize this", "optimization", "make X faster", "reduce <metric>", or "speed up my app". Runs a Design of Experiments test matrix — up to 6 variables in one pass — to find which variable actually moves the number. Falls back to one-at-a-time loop for single variables.
4
- user-invocable: true
4
+ user-invocable: false
5
5
  ---
6
6
 
7
7
  <!-- SPDX-FileCopyrightText: 2025-2026 Tyrone Ross, Jr <46267523+tyroneross@users.noreply.github.com> | SPDX-License-Identifier: Apache-2.0 -->
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: plugin-builder
3
- description: Use when the user asks to create, build, scaffold, convert, or migrate a Claude Code plugin, or needs guidance on plugin.json, directory layout, hooks, MCP servers, agents, LSP, or distribution.
3
+ description: Create, scaffold, or migrate a Claude Code plugin — manifest, directory layout, hooks, agents, distribution. Use when the user asks to create/build/scaffold/convert a plugin, or needs plugin.json/layout guidance. Not for building the MCP server itself once the shell exists (use `mcp-builder`; pair the two).
4
4
  user-invocable: false
5
5
  ---
6
6
 
@@ -327,3 +327,50 @@ Core references (load as needed — don't pre-load all):
327
327
  Related skills:
328
328
  - **`mcp-builder`** — MCP server implementation companion
329
329
  - **`skill-builder`** — SKILL.md authoring (for skills shipped inside a plugin)
330
+
331
+ ## Distribution & versioning — DETECT, don't mandate
332
+
333
+ Before touching any `version` field, run the detector:
334
+
335
+ ```bash
336
+ python3 scripts/detect_plugin_distribution.py <repo> --hub <fleet marketplace.json>
337
+ ```
338
+
339
+ The install SOURCE decides the policy (auto-SHA vs semver) — a blanket rule gets it wrong.
340
+ What is NOT negotiable is *consistency*: the version state must agree across `plugin.json`,
341
+ the codex manifest, and the marketplace entry, or one silently masks the others. Enforce that
342
+ invariant in CI with either of two equivalent options:
343
+
344
+ **Option A — local check (default; no external dependency).** `detect_plugin_distribution.py`
345
+ itself is advisory-only (always exits 0, prints a recommendation) — it is not a gate as
346
+ shipped. Vendor the script into the generated repo and wrap its `--json` output in a CI step
347
+ that fails on the `consistent` field:
348
+
349
+ ```yaml
350
+ jobs:
351
+ verify-manifests:
352
+ runs-on: ubuntu-latest
353
+ steps:
354
+ - uses: actions/checkout@v4
355
+ - run: |
356
+ python3 scripts/detect_plugin_distribution.py . --json > /tmp/dist.json
357
+ python3 -c "import json,sys; d=json.load(open('/tmp/dist.json')); sys.exit(0 if d['consistent'] else 1)"
358
+ ```
359
+
360
+ (Adjust the script path to wherever `detect_plugin_distribution.py` lands in the generated
361
+ repo — vendor the script itself, not just the CI call.)
362
+
363
+ **Option B — reuse the maintainer's shared workflow (opt-in, explicit dependency).** build-loop's
364
+ own CI calls a reusable workflow hosted in the maintainer's `tyroneross/RossLabs-AI-Toolkit` repo
365
+ at the `plugin-ci-v1` tag. This is convenient but puts the generated repo's CI at the mercy of a
366
+ third-party org: if that repo goes private, is renamed, or the tag is force-moved/deleted, the
367
+ generated repo's CI breaks with no local fallback. Only wire this in if the user explicitly wants
368
+ to track the maintainer's shared workflow and understands that risk:
369
+
370
+ ```yaml
371
+ jobs:
372
+ verify-manifests:
373
+ uses: tyroneross/RossLabs-AI-Toolkit/.github/workflows/verify-plugin-manifests.yml@plugin-ci-v1
374
+ ```
375
+
376
+ Full decision table + the failure that motivated it: `references/plugin-distribution.md`.
@@ -73,10 +73,10 @@ The rename is a full migration, not a metadata change. Partial renames produce p
73
73
  ## Source-of-truth flow
74
74
 
75
75
  ```
76
- ~/Desktop/git-folder/<plugin-repo>/ ← author-owned, edit here, commit
76
+ <your-local-checkout>/<plugin-repo>/ ← author-owned, edit here, commit
77
77
 
78
78
  ▼ (git push)
79
- GitHub: tyroneross/<marketplace>/ ← marketplace pulls from here
79
+ GitHub: <your-github-org>/<marketplace>/ ← marketplace pulls from here
80
80
 
81
81
  ▼ (plugin sync)
82
82
  ~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/ ← generated, never edit long-term
@@ -90,5 +90,4 @@ Build-loop must edit the source repo, not the cache. Cache edits survive until t
90
90
  ## References
91
91
 
92
92
  - `~/.claude/skills/plugin-builder/references/plugin-hygiene-lessons.md` — full incident log
93
- - `~/.claude/projects/-Users-tyroneross/memory/feedback_hook_design.md` — hook design rules
94
- - `~/.claude/projects/-Users-tyroneross/memory/feedback_rosslabs_toolkit_sync.md` — marketplace rename sync
93
+ - Per-project Claude Code memory (`feedback_hook_design.md`, `feedback_rosslabs_toolkit_sync.md`) — hook design rules and marketplace-rename-sync notes, when present. These live under a project-slug directory in `~/.claude/projects/` derived from your home path (glob `~/.claude/projects/-Users-*/memory/` to find yours); they are optional, personal notes, not part of this plugin.