@hegemonart/get-design-done 1.57.1 → 1.57.3
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/.claude-plugin/marketplace.json +26 -41
- package/.claude-plugin/plugin.json +23 -48
- package/CHANGELOG.md +139 -0
- package/README.md +166 -511
- package/SKILL.md +4 -6
- package/agents/README.md +33 -36
- package/agents/a11y-mapper.md +3 -3
- package/agents/component-benchmark-harvester.md +6 -6
- package/agents/component-benchmark-synthesizer.md +3 -3
- package/agents/compose-executor.md +3 -3
- package/agents/cost-forecaster.md +2 -2
- package/agents/design-auditor.md +7 -7
- package/agents/design-authority-watcher.md +15 -15
- package/agents/design-context-builder.md +4 -4
- package/agents/design-context-checker-gate.md +1 -1
- package/agents/design-discussant.md +2 -2
- package/agents/design-doc-writer.md +1 -1
- package/agents/design-executor.md +2 -2
- package/agents/design-figma-writer.md +2 -2
- package/agents/design-fixer.md +7 -7
- package/agents/design-integration-checker-gate.md +1 -1
- package/agents/design-integration-checker.md +1 -1
- package/agents/design-paper-writer.md +3 -3
- package/agents/design-pencil-writer.md +1 -1
- package/agents/design-planner.md +21 -0
- package/agents/design-reflector.md +39 -39
- package/agents/design-research-synthesizer.md +1 -0
- package/agents/design-start-writer.md +1 -1
- package/agents/design-update-checker.md +5 -5
- package/agents/design-verifier-gate.md +1 -1
- package/agents/design-verifier.md +52 -48
- package/agents/ds-generator.md +2 -2
- package/agents/ds-migration-planner.md +4 -4
- package/agents/email-executor.md +9 -9
- package/agents/experiment-result-ingester.md +3 -3
- package/agents/flutter-executor.md +5 -5
- package/agents/gdd-graph-refresh.md +3 -3
- package/agents/gdd-intel-updater.md +2 -2
- package/agents/motion-mapper.md +2 -2
- package/agents/motion-verifier.md +4 -4
- package/agents/pdf-executor.md +8 -8
- package/agents/perf-analyzer.md +17 -17
- package/agents/pr-commenter.md +9 -9
- package/agents/prototype-gate.md +2 -2
- package/agents/quality-gate-runner.md +1 -1
- package/agents/rollout-coordinator.md +3 -3
- package/agents/swift-executor.md +4 -4
- package/agents/ticket-sync-agent.md +6 -6
- package/agents/user-research-synthesizer.md +2 -2
- package/connections/connections.md +44 -45
- package/connections/cursor.md +72 -0
- package/connections/preview.md +3 -3
- package/hooks/first-run-nudge.cjs +171 -0
- package/hooks/gdd-intel-trigger.js +243 -0
- package/hooks/gdd-mcp-circuit-breaker.js +62 -7
- package/hooks/gdd-precompact-snapshot.js +50 -29
- package/hooks/gdd-protected-paths.js +150 -18
- package/hooks/gdd-risk-gate.js +93 -1
- package/hooks/gdd-sessionstart-recap.js +59 -24
- package/hooks/hooks.json +13 -4
- package/hooks/inject-using-gdd.cjs +188 -0
- package/hooks/update-check.cjs +511 -0
- package/package.json +9 -3
- package/reference/STATE-TEMPLATE.md +10 -13
- package/reference/audit-scoring.md +1 -1
- package/reference/cache-tier-doctrine.md +46 -0
- package/reference/config-schema.md +9 -9
- package/reference/i18n.md +1 -1
- package/reference/intel-schema.md +37 -2
- package/reference/meta-rules.md +4 -4
- package/reference/model-tiers.md +2 -2
- package/reference/registry.json +101 -94
- package/reference/runtime-models.md +11 -1
- package/reference/shared-preamble.md +13 -14
- package/reference/skill-graph.md +22 -3
- package/scripts/bootstrap.cjs +373 -0
- package/scripts/injection-patterns.cjs +58 -0
- package/scripts/lib/apply-reflections/incubator-proposals.cjs +57 -26
- package/scripts/lib/install/converters/codex-plugin.cjs +5 -2
- package/scripts/lib/install/converters/cursor.cjs +20 -0
- package/scripts/lib/issue-reporter/report-flow.cjs +1 -1
- package/scripts/lib/manifest/skills.json +75 -28
- package/scripts/lib/state/query-surface.cjs +67 -9
- package/scripts/lib/state/state-store.cjs +68 -26
- package/scripts/lib/worktree-resolve.cjs +4 -16
- package/sdk/cli/commands/stage.ts +17 -0
- package/sdk/cli/index.js +14 -0
- package/skills/README.md +46 -0
- package/skills/bootstrap-ds/SKILL.md +1 -1
- package/skills/cache-manager/SKILL.md +3 -3
- package/skills/cache-manager/cache-policy.md +1 -1
- package/skills/compare/SKILL.md +1 -1
- package/skills/design/SKILL.md +19 -0
- package/skills/explore/SKILL.md +11 -0
- package/skills/figma-write/SKILL.md +13 -2
- package/skills/new-cycle/SKILL.md +1 -1
- package/skills/paper-write/SKILL.md +54 -0
- package/skills/peer-cli-customize/SKILL.md +0 -1
- package/skills/peers/SKILL.md +1 -1
- package/skills/pencil-write/SKILL.md +54 -0
- package/skills/reflect/procedures/capability-gap-scan.md +0 -1
- package/skills/report-issue/SKILL.md +2 -2
- package/skills/report-issue/report-issue-procedure.md +0 -1
- package/skills/router/SKILL.md +2 -2
- package/skills/synthesize/SKILL.md +1 -1
- package/skills/turn-closeout/SKILL.md +1 -1
- package/skills/verify/verify-procedure.md +10 -11
- package/skills/warm-cache/SKILL.md +1 -1
- package/dist/claude-code/.claude/skills/add-backlog/SKILL.md +0 -48
- package/dist/claude-code/.claude/skills/analyze-dependencies/SKILL.md +0 -95
- package/dist/claude-code/.claude/skills/apply-reflections/SKILL.md +0 -109
- package/dist/claude-code/.claude/skills/apply-reflections/apply-reflections-procedure.md +0 -170
- package/dist/claude-code/.claude/skills/audit/SKILL.md +0 -79
- package/dist/claude-code/.claude/skills/bandit-status/SKILL.md +0 -94
- package/dist/claude-code/.claude/skills/benchmark/SKILL.md +0 -65
- package/dist/claude-code/.claude/skills/bootstrap-ds/SKILL.md +0 -43
- package/dist/claude-code/.claude/skills/brief/SKILL.md +0 -145
- package/dist/claude-code/.claude/skills/budget/SKILL.md +0 -45
- package/dist/claude-code/.claude/skills/cache-manager/SKILL.md +0 -66
- package/dist/claude-code/.claude/skills/cache-manager/cache-policy.md +0 -126
- package/dist/claude-code/.claude/skills/check-update/SKILL.md +0 -98
- package/dist/claude-code/.claude/skills/compare/SKILL.md +0 -82
- package/dist/claude-code/.claude/skills/compare/compare-rubric.md +0 -171
- package/dist/claude-code/.claude/skills/complete-cycle/SKILL.md +0 -81
- package/dist/claude-code/.claude/skills/connections/SKILL.md +0 -71
- package/dist/claude-code/.claude/skills/connections/connections-onboarding.md +0 -608
- package/dist/claude-code/.claude/skills/context/SKILL.md +0 -137
- package/dist/claude-code/.claude/skills/continue/SKILL.md +0 -24
- package/dist/claude-code/.claude/skills/darkmode/SKILL.md +0 -76
- package/dist/claude-code/.claude/skills/darkmode/darkmode-audit-procedure.md +0 -258
- package/dist/claude-code/.claude/skills/debug/SKILL.md +0 -41
- package/dist/claude-code/.claude/skills/debug/debug-feedback-loops.md +0 -119
- package/dist/claude-code/.claude/skills/design/SKILL.md +0 -99
- package/dist/claude-code/.claude/skills/design/design-procedure.md +0 -304
- package/dist/claude-code/.claude/skills/discover/SKILL.md +0 -78
- package/dist/claude-code/.claude/skills/discover/discover-procedure.md +0 -222
- package/dist/claude-code/.claude/skills/discuss/SKILL.md +0 -96
- package/dist/claude-code/.claude/skills/do/SKILL.md +0 -45
- package/dist/claude-code/.claude/skills/explore/SKILL.md +0 -107
- package/dist/claude-code/.claude/skills/explore/explore-procedure.md +0 -267
- package/dist/claude-code/.claude/skills/export/SKILL.md +0 -30
- package/dist/claude-code/.claude/skills/extract-learnings/SKILL.md +0 -114
- package/dist/claude-code/.claude/skills/fast/SKILL.md +0 -91
- package/dist/claude-code/.claude/skills/figma-extract/SKILL.md +0 -64
- package/dist/claude-code/.claude/skills/figma-write/SKILL.md +0 -39
- package/dist/claude-code/.claude/skills/graphify/SKILL.md +0 -49
- package/dist/claude-code/.claude/skills/health/SKILL.md +0 -99
- package/dist/claude-code/.claude/skills/health/health-mcp-detection.md +0 -44
- package/dist/claude-code/.claude/skills/health/health-skill-length-report.md +0 -69
- package/dist/claude-code/.claude/skills/help/SKILL.md +0 -87
- package/dist/claude-code/.claude/skills/instinct/SKILL.md +0 -111
- package/dist/claude-code/.claude/skills/list-assumptions/SKILL.md +0 -61
- package/dist/claude-code/.claude/skills/list-pins/SKILL.md +0 -27
- package/dist/claude-code/.claude/skills/live/SKILL.md +0 -98
- package/dist/claude-code/.claude/skills/locale/SKILL.md +0 -51
- package/dist/claude-code/.claude/skills/map/SKILL.md +0 -89
- package/dist/claude-code/.claude/skills/migrate/SKILL.md +0 -70
- package/dist/claude-code/.claude/skills/migrate-context/SKILL.md +0 -123
- package/dist/claude-code/.claude/skills/new-addendum/SKILL.md +0 -81
- package/dist/claude-code/.claude/skills/new-cycle/SKILL.md +0 -37
- package/dist/claude-code/.claude/skills/new-cycle/milestone-completeness-rubric.md +0 -87
- package/dist/claude-code/.claude/skills/new-project/SKILL.md +0 -53
- package/dist/claude-code/.claude/skills/new-skill/SKILL.md +0 -90
- package/dist/claude-code/.claude/skills/next/SKILL.md +0 -68
- package/dist/claude-code/.claude/skills/note/SKILL.md +0 -48
- package/dist/claude-code/.claude/skills/openrouter-status/SKILL.md +0 -86
- package/dist/claude-code/.claude/skills/optimize/SKILL.md +0 -97
- package/dist/claude-code/.claude/skills/override/SKILL.md +0 -86
- package/dist/claude-code/.claude/skills/pause/SKILL.md +0 -77
- package/dist/claude-code/.claude/skills/peer-cli-add/SKILL.md +0 -88
- package/dist/claude-code/.claude/skills/peer-cli-add/peer-cli-protocol.md +0 -161
- package/dist/claude-code/.claude/skills/peer-cli-customize/SKILL.md +0 -90
- package/dist/claude-code/.claude/skills/peers/SKILL.md +0 -96
- package/dist/claude-code/.claude/skills/pin/SKILL.md +0 -37
- package/dist/claude-code/.claude/skills/plan/SKILL.md +0 -105
- package/dist/claude-code/.claude/skills/plan/plan-procedure.md +0 -278
- package/dist/claude-code/.claude/skills/plant-seed/SKILL.md +0 -48
- package/dist/claude-code/.claude/skills/pr-branch/SKILL.md +0 -32
- package/dist/claude-code/.claude/skills/progress/SKILL.md +0 -107
- package/dist/claude-code/.claude/skills/quality-gate/SKILL.md +0 -90
- package/dist/claude-code/.claude/skills/quality-gate/threat-modeling.md +0 -101
- package/dist/claude-code/.claude/skills/quick/SKILL.md +0 -44
- package/dist/claude-code/.claude/skills/reapply-patches/SKILL.md +0 -32
- package/dist/claude-code/.claude/skills/recall/SKILL.md +0 -75
- package/dist/claude-code/.claude/skills/reflect/SKILL.md +0 -85
- package/dist/claude-code/.claude/skills/reflect/procedures/capability-gap-scan.md +0 -120
- package/dist/claude-code/.claude/skills/report-issue/SKILL.md +0 -53
- package/dist/claude-code/.claude/skills/report-issue/report-issue-procedure.md +0 -120
- package/dist/claude-code/.claude/skills/resume/SKILL.md +0 -93
- package/dist/claude-code/.claude/skills/review-backlog/SKILL.md +0 -46
- package/dist/claude-code/.claude/skills/review-decisions/SKILL.md +0 -42
- package/dist/claude-code/.claude/skills/roi/SKILL.md +0 -54
- package/dist/claude-code/.claude/skills/rollout-status/SKILL.md +0 -35
- package/dist/claude-code/.claude/skills/router/SKILL.md +0 -89
- package/dist/claude-code/.claude/skills/router/capability-gap-emitter.md +0 -65
- package/dist/claude-code/.claude/skills/router/router-pick-emitter.md +0 -78
- package/dist/claude-code/.claude/skills/router/router-rules.md +0 -84
- package/dist/claude-code/.claude/skills/scan/SKILL.md +0 -92
- package/dist/claude-code/.claude/skills/scan/scan-procedure.md +0 -732
- package/dist/claude-code/.claude/skills/settings/SKILL.md +0 -87
- package/dist/claude-code/.claude/skills/ship/SKILL.md +0 -48
- package/dist/claude-code/.claude/skills/sketch/SKILL.md +0 -78
- package/dist/claude-code/.claude/skills/sketch-wrap-up/SKILL.md +0 -92
- package/dist/claude-code/.claude/skills/skill-manifest/SKILL.md +0 -79
- package/dist/claude-code/.claude/skills/spike/SKILL.md +0 -67
- package/dist/claude-code/.claude/skills/spike-wrap-up/SKILL.md +0 -86
- package/dist/claude-code/.claude/skills/start/SKILL.md +0 -67
- package/dist/claude-code/.claude/skills/start/start-procedure.md +0 -115
- package/dist/claude-code/.claude/skills/state/SKILL.md +0 -106
- package/dist/claude-code/.claude/skills/stats/SKILL.md +0 -51
- package/dist/claude-code/.claude/skills/style/SKILL.md +0 -71
- package/dist/claude-code/.claude/skills/style/style-doc-procedure.md +0 -150
- package/dist/claude-code/.claude/skills/synthesize/SKILL.md +0 -94
- package/dist/claude-code/.claude/skills/timeline/SKILL.md +0 -66
- package/dist/claude-code/.claude/skills/todo/SKILL.md +0 -64
- package/dist/claude-code/.claude/skills/turn-closeout/SKILL.md +0 -95
- package/dist/claude-code/.claude/skills/undo/SKILL.md +0 -31
- package/dist/claude-code/.claude/skills/unlock-decision/SKILL.md +0 -54
- package/dist/claude-code/.claude/skills/unpin/SKILL.md +0 -31
- package/dist/claude-code/.claude/skills/update/SKILL.md +0 -56
- package/dist/claude-code/.claude/skills/using-gdd/SKILL.md +0 -78
- package/dist/claude-code/.claude/skills/verify/SKILL.md +0 -113
- package/dist/claude-code/.claude/skills/verify/verify-procedure.md +0 -512
- package/dist/claude-code/.claude/skills/warm-cache/SKILL.md +0 -81
- package/dist/claude-code/.claude/skills/watch-authorities/SKILL.md +0 -82
- package/dist/claude-code/.claude/skills/zoom-out/SKILL.md +0 -26
- package/hooks/first-run-nudge.sh +0 -82
- package/hooks/inject-using-gdd.sh +0 -72
- package/hooks/run-hook.cmd +0 -35
- package/hooks/update-check.sh +0 -251
- package/scripts/lib/audit-aggregator/index.cjs +0 -219
- package/scripts/lib/hedge-ensemble.cjs +0 -217
- package/skills/discover/SKILL.md +0 -78
- package/skills/discover/discover-procedure.md +0 -222
- package/skills/new-cycle/milestone-completeness-rubric.md +0 -87
- package/skills/scan/SKILL.md +0 -92
- package/skills/scan/scan-procedure.md +0 -732
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: gdd-watch-authorities
|
|
3
|
-
description: "Fetches the design-authority feed whitelist, diffs against .design/authority-snapshot.json, and writes .design/authority-report.md (consumed by /gdd:reflect). Authority monitoring only - no trend-watching."
|
|
4
|
-
argument-hint: "[--refresh] [--since <date>] [--feed <name>] [--schedule <weekly|daily|monthly>]"
|
|
5
|
-
tools: Read, Write, Task, Bash
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
# /gdd:watch-authorities
|
|
9
|
-
|
|
10
|
-
Runs `design-authority-watcher` on demand. Fetches the curated design-authority feed whitelist, diffs against the prior snapshot, classifies new entries into five buckets, and writes `.design/authority-report.md`. Phase 11's reflector picks up the report automatically when you next run `/gdd:reflect`.
|
|
11
|
-
|
|
12
|
-
Authority-monitoring only. Not trend-watching. See `reference/authority-feeds.md` §"Rejected kinds" for what this skill will never fetch.
|
|
13
|
-
|
|
14
|
-
## Steps
|
|
15
|
-
|
|
16
|
-
1. **Parse args.** Extract optional flags: `--refresh`, `--since <date>`, `--feed <name>`, `--schedule <cadence>`. Anything that doesn't match one of these is an error - print `Unknown flag: <arg>. Valid flags: --refresh --since <date> --feed <name> --schedule <weekly|daily|monthly>.` and STOP.
|
|
17
|
-
|
|
18
|
-
Mutual exclusion rules:
|
|
19
|
-
- `--schedule` is handled entirely by this skill - it does not combine with the other three. If `--schedule` is present alongside any of `--refresh | --since | --feed`, print `--schedule cannot combine with other flags. Schedule registration runs this skill with no flags at the configured cadence.` and STOP.
|
|
20
|
-
- `--refresh` and `--since` are mutually exclusive - print `--refresh and --since are mutually exclusive. --refresh re-seeds the snapshot silently; --since surfaces a backlog from a boundary date. Pick one.` and STOP.
|
|
21
|
-
|
|
22
|
-
2. **Handle `--schedule <cadence>` branch** (early-return).
|
|
23
|
-
|
|
24
|
-
If `--schedule` is set:
|
|
25
|
-
- Validate cadence ∈ {`weekly`, `daily`, `monthly`}; else print `Unknown cadence: <value>. Use one of: weekly, daily, monthly.` and STOP.
|
|
26
|
-
- Probe for the scheduled-tasks MCP via ToolSearch:
|
|
27
|
-
|
|
28
|
-
```
|
|
29
|
-
ToolSearch({ query: "scheduled-tasks", max_results: 3 })
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
- If the probe returns an empty result set: print `scheduled-tasks MCP not connected. Install it with: claude mcp add scheduled-tasks ... then retry with --schedule.` - this is a documented fallback (not an error). Terminate with `## WATCH COMPLETE` and exit 0.
|
|
33
|
-
- If the probe returns one or more `scheduled-tasks` tools: register the cron. Discover the MCP's registration tool name at runtime from the ToolSearch result and follow its schema. Target command: `/gdd:watch-authorities` with NO flags (the cron invokes the default diff-and-report behavior). Cadence → cron expression mapping:
|
|
34
|
-
- `weekly` → `0 9 * * 1` (Mondays 09:00 local)
|
|
35
|
-
- `daily` → `0 9 * * *` (every day 09:00 local)
|
|
36
|
-
- `monthly` → `0 9 1 * *` (1st of each month 09:00 local)
|
|
37
|
-
- After registration: print `Scheduled /gdd:watch-authorities to run <cadence>.` and terminate with `## WATCH COMPLETE`.
|
|
38
|
-
|
|
39
|
-
3. **Validate `--since <date>`** (if present).
|
|
40
|
-
|
|
41
|
-
Accept ISO8601 (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SSZ`). Sanity-check via Bash `date -d "<value>" +%s` (GNU) or the POSIX equivalent `python3 -c "from datetime import datetime; datetime.fromisoformat('<value>'.replace('Z','+00:00'))"`. On parse failure: print `Invalid --since value: <value>. Use ISO8601 (YYYY-MM-DD or YYYY-MM-DDTHH:MM:SSZ).` and STOP.
|
|
42
|
-
|
|
43
|
-
If the parsed date is earlier than `2020-01-01`, ask: `Very old --since value: <value>. Did you mean something more recent? Proceed? [y/N]`. On anything other than `y`/`Y`, STOP.
|
|
44
|
-
|
|
45
|
-
4. **Spawn the watcher.**
|
|
46
|
-
|
|
47
|
-
Build the `Task(subagent_type="design-authority-watcher", ...)` prompt. The prompt supplies the agent's required-reading block (watcher step 0), echoes the invocation flags verbatim (watcher Flags section), and instructs the agent to follow its own fetch/diff/classify/write loop:
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
Task("design-authority-watcher", """
|
|
51
|
-
<required_reading>
|
|
52
|
-
@reference/authority-feeds.md
|
|
53
|
-
@.design/authority-snapshot.json
|
|
54
|
-
@.design/STATE.md
|
|
55
|
-
</required_reading>
|
|
56
|
-
|
|
57
|
-
Invocation flags: <joined flag list or "none">
|
|
58
|
-
|
|
59
|
-
Fetch the feeds listed in reference/authority-feeds.md, diff against .design/authority-snapshot.json,
|
|
60
|
-
classify new entries per the D-17 decision table, write .design/authority-snapshot.json and
|
|
61
|
-
.design/authority-report.md.
|
|
62
|
-
|
|
63
|
-
Terminate with ## WATCH COMPLETE.
|
|
64
|
-
""")
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
`<joined flag list>` is the subset of `--refresh | --since <date> | --feed <name>` actually passed - e.g., `--refresh`, `--since 2026-03-01`, `--feed wai-aria-apg`, `--refresh --feed radix-ui-releases`, or literally `none` when no flags were supplied.
|
|
68
|
-
|
|
69
|
-
5. **Print summary.**
|
|
70
|
-
|
|
71
|
-
After the agent returns:
|
|
72
|
-
- If STATE.md gained a `<blocker type="contract-violation">` on this run (snapshot version mismatch, hash-format violation, or over-200 entries per feed), surface the blocker verbatim and stop - do not print the default "review and reflect" line.
|
|
73
|
-
- Otherwise print the agent's one-line stdout summary (normal mode: `Surfaced N entries across M feeds. K skipped. See .design/authority-report.md.`; first-run / refresh mode: `Seeded snapshot for N feeds — next run will surface new entries.`) followed by: `Review and reflect: /gdd:reflect`.
|
|
74
|
-
|
|
75
|
-
6. **Terminate with `## WATCH COMPLETE`.**
|
|
76
|
-
|
|
77
|
-
## Do Not
|
|
78
|
-
|
|
79
|
-
- Do not modify `agents/design-authority-watcher.md`.
|
|
80
|
-
- Do not modify `agents/design-reflector.md` - Phase 13.2 does not touch the reflector agent (CONTEXT.md D-25).
|
|
81
|
-
- Do not write to `.design/authority-snapshot.json` or `.design/authority-report.md` directly - those are the agent's writes.
|
|
82
|
-
- Do not fetch URLs outside `reference/authority-feeds.md`. The whitelist is the allow-list.
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: zoom-out
|
|
3
|
-
description: "Asks the agent to go up a layer of abstraction and map the relevant modules and callers using the project's CONTEXT.md vocabulary. Use when the user is working in an unfamiliar area of code and needs orientation before deep work."
|
|
4
|
-
disable-model-invocation: true
|
|
5
|
-
argument-hint: "[scope]"
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
Source: mattpocock/skills (MIT) - adapted with permission. See `../NOTICE` for the full attribution block.
|
|
9
|
-
|
|
10
|
-
# Zoom Out
|
|
11
|
-
|
|
12
|
-
**Role:** Give the user a map, not a fix.
|
|
13
|
-
|
|
14
|
-
I don't know this area of code well. Go up a layer of abstraction. Give me a map of all the relevant modules and callers, using the project's domain glossary (`CONTEXT.md`) vocabulary.
|
|
15
|
-
|
|
16
|
-
When invoked, produce a one-screen map that names:
|
|
17
|
-
|
|
18
|
-
1. **Modules in scope** - one-line description of each, using terms from `CONTEXT.md` (see `./../reference/context-md-format.md` for the schema). Do not invent terms.
|
|
19
|
-
2. **Callers** - who calls these modules from elsewhere, with file paths.
|
|
20
|
-
3. **Seams** - where data crosses module boundaries, named per `./../reference/architecture-vocabulary.md`.
|
|
21
|
-
|
|
22
|
-
Do not propose fixes. Do not write code. The output is a map.
|
|
23
|
-
|
|
24
|
-
If `CONTEXT.md` is absent, suggest `/gdd:discuss` to start one, but still produce the map using basenames and inferred terms.
|
|
25
|
-
|
|
26
|
-
## ZOOM-OUT COMPLETE
|
package/hooks/first-run-nudge.sh
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# get-design-done — first-run nudge (Phase 14.7)
|
|
3
|
-
# SessionStart hook. Silent-on-failure by policy: exits 0 on every error path.
|
|
4
|
-
# Prints exactly one restrained line pointing at /gdd:start when all gates pass,
|
|
5
|
-
# and nothing otherwise.
|
|
6
|
-
|
|
7
|
-
set -u # intentionally no -e: we want to fall through to exit 0
|
|
8
|
-
|
|
9
|
-
# Silent logger — writes nothing by default. Set GDD_NUDGE_DEBUG=1 to enable stderr.
|
|
10
|
-
log() {
|
|
11
|
-
if [ "${GDD_NUDGE_DEBUG:-0}" = "1" ]; then
|
|
12
|
-
printf '[gdd first-run-nudge] %s\n' "$*" >&2
|
|
13
|
-
fi
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
DESIGN_DIR="$(pwd)/.design"
|
|
17
|
-
STATE="${DESIGN_DIR}/STATE.md"
|
|
18
|
-
CONFIG="${DESIGN_DIR}/config.json"
|
|
19
|
-
DISMISS_FLAG="${HOME:-$USERPROFILE}/.claude/gdd-nudge-dismissed"
|
|
20
|
-
|
|
21
|
-
# Gate 1 — repo already has GDD state, suppress.
|
|
22
|
-
has_design_state() {
|
|
23
|
-
[ -f "${CONFIG}" ] || [ -f "${STATE}" ]
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
# Gate 2 — per-install dismissal flag.
|
|
27
|
-
is_dismissed() {
|
|
28
|
-
[ -f "${DISMISS_FLAG}" ]
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
# Gate 3 — STATE.md stage belongs to an active pipeline window.
|
|
32
|
-
# Inherits the shape used by Phase 13.3 update-check.sh.
|
|
33
|
-
read_state_stage() {
|
|
34
|
-
[ -f "${STATE}" ] || { printf ''; return; }
|
|
35
|
-
grep -E '^stage:' "${STATE}" 2>/dev/null | head -n1 | \
|
|
36
|
-
sed -E 's/^stage:[[:space:]]*"?([^"[:space:]]+)"?.*/\1/'
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
is_active_stage() {
|
|
40
|
-
local s
|
|
41
|
-
s="$(read_state_stage)"
|
|
42
|
-
case "${s}" in
|
|
43
|
-
plan|design|verify|executing|discussing) return 0 ;;
|
|
44
|
-
*) return 1 ;;
|
|
45
|
-
esac
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
# Gate 4 — recent session history has a gdd:* command. We cannot reliably read
|
|
49
|
-
# session history from a hook in all runtimes; when the signal is unavailable,
|
|
50
|
-
# treat it as "unknown → not suppressed". This preserves the nudge's
|
|
51
|
-
# usefulness without creating false suppression.
|
|
52
|
-
has_recent_gdd_command() {
|
|
53
|
-
# Placeholder: no portable transcript path exposed to SessionStart hooks today.
|
|
54
|
-
# Keep the function for future wiring; for now always returns non-zero (unknown).
|
|
55
|
-
return 1
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
# MANDATORY sourcing guard: unit tests source this script to test the helper
|
|
59
|
-
# functions without executing the main flow. Non-negotiable.
|
|
60
|
-
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
|
61
|
-
if has_design_state; then
|
|
62
|
-
log "design state present — suppress"
|
|
63
|
-
exit 0
|
|
64
|
-
fi
|
|
65
|
-
if is_dismissed; then
|
|
66
|
-
log "dismissal flag present — suppress"
|
|
67
|
-
exit 0
|
|
68
|
-
fi
|
|
69
|
-
if is_active_stage; then
|
|
70
|
-
log "active stage — suppress"
|
|
71
|
-
exit 0
|
|
72
|
-
fi
|
|
73
|
-
if has_recent_gdd_command; then
|
|
74
|
-
log "recent gdd:* command detected — suppress"
|
|
75
|
-
exit 0
|
|
76
|
-
fi
|
|
77
|
-
# All gates passed — emit the locked one-line nudge.
|
|
78
|
-
printf 'Tip: run /gdd:start to let GDD inspect this codebase and suggest one first fix.\n'
|
|
79
|
-
exit 0
|
|
80
|
-
fi
|
|
81
|
-
# When sourced (BASH_SOURCE != $0), fall through with function definitions loaded
|
|
82
|
-
# and without side effects.
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# hooks/inject-using-gdd.sh — SessionStart per-harness context injector (D-07).
|
|
3
|
-
#
|
|
4
|
-
# The forcing function GDD lacked: on every session start / /clear / compact this
|
|
5
|
-
# reads skills/using-gdd/SKILL.md (the bootstrap discipline contract) and emits it
|
|
6
|
-
# as the host harness's SessionStart "additionalContext" shape so the agent is
|
|
7
|
-
# primed with the 1%-rule + red-flags + skill-priority before it acts.
|
|
8
|
-
#
|
|
9
|
-
# Ported MECHANISM (not content) from obra/superpowers (MIT): one polyglot script,
|
|
10
|
-
# env-var branch, pure-bash escape_for_json (no jq/python dependency). See NOTICE.
|
|
11
|
-
#
|
|
12
|
-
# Three emitted shapes (ONE JSON object on stdout, nothing else):
|
|
13
|
-
# Cursor (CURSOR_PLUGIN_ROOT set) -> {"additional_context": "<escaped>"}
|
|
14
|
-
# Claude Code (CLAUDE_PLUGIN_ROOT set, no Cursor)
|
|
15
|
-
# -> {"hookSpecificOutput":
|
|
16
|
-
# {"hookEventName":"SessionStart",
|
|
17
|
-
# "additionalContext":"<escaped>"}}
|
|
18
|
-
# SDK-standard (neither; e.g. COPILOT_CLI) -> {"additionalContext": "<escaped>"}
|
|
19
|
-
#
|
|
20
|
-
# Branch order: check Cursor BEFORE Claude Code — a Cursor session may also export
|
|
21
|
-
# CLAUDE_PLUGIN_ROOT, and Cursor's own var must win.
|
|
22
|
-
#
|
|
23
|
-
# NO-CASCADE (D-06): this script is wired ONLY under the SessionStart hook event in
|
|
24
|
-
# hooks/hooks.json. Subagent spawns do not fire SessionStart, so the inject cannot
|
|
25
|
-
# cascade into a subagent's context. (Structural guarantee; behavioral proof = P33.)
|
|
26
|
-
|
|
27
|
-
set -u
|
|
28
|
-
|
|
29
|
-
# --- Resolve the plugin root so we can locate skills/using-gdd/SKILL.md ---------
|
|
30
|
-
# Prefer the harness-provided roots; fall back to this script's parent dir so the
|
|
31
|
-
# emitter is runnable straight from hooks/ in tests and in bare shells.
|
|
32
|
-
SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
33
|
-
ROOT="${CURSOR_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-${SELF_DIR}/..}}"
|
|
34
|
-
ROOT="${ROOT//\\//}" # normalize Windows backslashes to forward slashes
|
|
35
|
-
SKILL="${ROOT}/skills/using-gdd/SKILL.md"
|
|
36
|
-
|
|
37
|
-
# Defensive: if the skill file is missing we must STILL emit a syntactically valid
|
|
38
|
-
# JSON object (an empty additionalContext) so the SessionStart pipeline never
|
|
39
|
-
# breaks on a partial install. Never crash the session start.
|
|
40
|
-
if [[ -r "${SKILL}" ]]; then
|
|
41
|
-
CONTENT="$(cat "${SKILL}")"
|
|
42
|
-
else
|
|
43
|
-
CONTENT=""
|
|
44
|
-
fi
|
|
45
|
-
|
|
46
|
-
# --- escape_for_json (superpowers pattern; pure bash param-substitution) --------
|
|
47
|
-
# Order matters: backslash FIRST (so escapes we add next aren't re-escaped), then
|
|
48
|
-
# double-quote, then the control chars newline / tab / carriage-return. Emits the
|
|
49
|
-
# value WITH surrounding double-quotes so callers can splice it directly.
|
|
50
|
-
escape_for_json() {
|
|
51
|
-
local s="$1"
|
|
52
|
-
s="${s//\\/\\\\}" # \ -> \\
|
|
53
|
-
s="${s//\"/\\\"}" # " -> \"
|
|
54
|
-
s="${s//$'\t'/\\t}" # tab -> \t
|
|
55
|
-
s="${s//$'\r'/\\r}" # CR -> \r
|
|
56
|
-
s="${s//$'\n'/\\n}" # LF -> \n (do last: newlines are the record separator)
|
|
57
|
-
printf '"%s"' "$s"
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
ESCAPED="$(escape_for_json "${CONTENT}")"
|
|
61
|
-
|
|
62
|
-
# --- Branch on harness env vars and emit the matching single JSON object --------
|
|
63
|
-
if [[ -n "${CURSOR_PLUGIN_ROOT:-}" ]]; then
|
|
64
|
-
# Cursor: top-level additional_context.
|
|
65
|
-
printf '{"additional_context": %s}\n' "${ESCAPED}"
|
|
66
|
-
elif [[ -n "${CLAUDE_PLUGIN_ROOT:-}" ]]; then
|
|
67
|
-
# Claude Code: hookSpecificOutput envelope (mirrors hooks/gdd-decision-injector.js).
|
|
68
|
-
printf '{"hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": %s}}\n' "${ESCAPED}"
|
|
69
|
-
else
|
|
70
|
-
# SDK-standard (COPILOT_CLI or none): top-level additionalContext.
|
|
71
|
-
printf '{"additionalContext": %s}\n' "${ESCAPED}"
|
|
72
|
-
fi
|
package/hooks/run-hook.cmd
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
REM hooks/run-hook.cmd — Windows polyglot wrapper that invokes a GDD .sh hook
|
|
3
|
-
REM through bash.
|
|
4
|
-
REM
|
|
5
|
-
REM Workaround for Claude Code's Windows auto-bash bug: CC can mis-handle a
|
|
6
|
-
REM SessionStart `command` that points directly at a `.sh` file on Windows
|
|
7
|
-
REM shells. This .cmd shim locates bash and runs the script explicitly, so the
|
|
8
|
-
REM SessionStart inject (hooks/inject-using-gdd.sh) fires on Windows too.
|
|
9
|
-
REM
|
|
10
|
-
REM Usage: run-hook.cmd <script-name.sh> [args...]
|
|
11
|
-
REM Default (no arg): inject-using-gdd.sh — the SessionStart using-gdd injector.
|
|
12
|
-
REM The host harness's env (CLAUDE_PLUGIN_ROOT / CURSOR_PLUGIN_ROOT / COPILOT_CLI)
|
|
13
|
-
REM is inherited by bash and drives the emitter's per-harness branch.
|
|
14
|
-
setlocal
|
|
15
|
-
|
|
16
|
-
REM Script to run, relative to this .cmd's own directory (%~dp0 ends with a backslash).
|
|
17
|
-
set "HOOK_SCRIPT=%~1"
|
|
18
|
-
if "%HOOK_SCRIPT%"=="" set "HOOK_SCRIPT=inject-using-gdd.sh"
|
|
19
|
-
if not "%~1"=="" shift
|
|
20
|
-
|
|
21
|
-
set "HOOK_PATH=%~dp0%HOOK_SCRIPT%"
|
|
22
|
-
|
|
23
|
-
REM Prefer bash on PATH; fall back to a typical Git-for-Windows install location.
|
|
24
|
-
where bash >nul 2>nul
|
|
25
|
-
if %ERRORLEVEL%==0 (
|
|
26
|
-
bash "%HOOK_PATH%" %*
|
|
27
|
-
) else if exist "%ProgramFiles%\Git\bin\bash.exe" (
|
|
28
|
-
"%ProgramFiles%\Git\bin\bash.exe" "%HOOK_PATH%" %*
|
|
29
|
-
) else (
|
|
30
|
-
REM No bash available: emit a valid empty SDK-shape JSON object so the
|
|
31
|
-
REM SessionStart pipeline still receives parseable output and never breaks.
|
|
32
|
-
echo {"additionalContext": ""}
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
endlocal
|
package/hooks/update-check.sh
DELETED
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# get-design-done — update check (Phase 13.3)
|
|
3
|
-
# SessionStart hook. Silent-on-failure by policy (D-04): exits 0 on every error path.
|
|
4
|
-
# 24h-cached unauthenticated GET of /releases/latest. Renders .design/update-available.md
|
|
5
|
-
# only when a newer version exists AND it is not dismissed AND stage-guard allows.
|
|
6
|
-
|
|
7
|
-
set -u # intentionally no -e: we want to fall through to exit 0
|
|
8
|
-
|
|
9
|
-
PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-$(cd "$(dirname "$0")/.." && pwd)}"
|
|
10
|
-
PLUGIN_ROOT="${PLUGIN_ROOT//\\//}" # Windows → POSIX slashes
|
|
11
|
-
|
|
12
|
-
DESIGN_DIR="$(pwd)/.design"
|
|
13
|
-
CACHE="${DESIGN_DIR}/update-cache.json"
|
|
14
|
-
BANNER="${DESIGN_DIR}/update-available.md"
|
|
15
|
-
CONFIG="${DESIGN_DIR}/config.json"
|
|
16
|
-
STATE="${DESIGN_DIR}/STATE.md"
|
|
17
|
-
CACHE_TTL_SECONDS=86400 # 24h
|
|
18
|
-
|
|
19
|
-
# Silent logger — writes nothing by default. Set GDD_UPDATE_DEBUG=1 to enable stderr.
|
|
20
|
-
log() {
|
|
21
|
-
if [ "${GDD_UPDATE_DEBUG:-0}" = "1" ]; then
|
|
22
|
-
printf '[gdd update-check] %s\n' "$*" >&2
|
|
23
|
-
fi
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
# Ensure .design/ exists (bootstrap normally creates it; belt+suspenders).
|
|
27
|
-
mkdir -p "${DESIGN_DIR}" 2>/dev/null || exit 0
|
|
28
|
-
|
|
29
|
-
# ---- Read current plugin version (no jq) ----
|
|
30
|
-
PLUGIN_JSON="${PLUGIN_ROOT}/.claude-plugin/plugin.json"
|
|
31
|
-
|
|
32
|
-
read_current_tag() {
|
|
33
|
-
[ -f "${PLUGIN_JSON}" ] || return 1
|
|
34
|
-
grep -E '^[[:space:]]*"version"[[:space:]]*:' "${PLUGIN_JSON}" | head -n1 | \
|
|
35
|
-
sed -E 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/'
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
# ---- Semver normalizer: "v1.0.7" -> "1 0 7 0"; "v1.0.7.3" -> "1 0 7 3" ----
|
|
39
|
-
normalize_semver() {
|
|
40
|
-
local t="${1#v}"
|
|
41
|
-
# strip any -pre/-beta suffix after first hyphen (unauth'd API rarely surfaces these, best-effort)
|
|
42
|
-
t="${t%%-*}"
|
|
43
|
-
# Replace dots with spaces; pad to 4 segments
|
|
44
|
-
# shellcheck disable=SC2086
|
|
45
|
-
set -- $(printf '%s' "${t}" | tr '.' ' ')
|
|
46
|
-
local a="${1:-0}" b="${2:-0}" c="${3:-0}" d="${4:-0}"
|
|
47
|
-
# Sanitize to digits only (POSIX: tr -cd 0-9 — BSD+GNU safe)
|
|
48
|
-
a="$(printf '%s' "$a" | tr -cd '0-9')"; a="${a:-0}"
|
|
49
|
-
b="$(printf '%s' "$b" | tr -cd '0-9')"; b="${b:-0}"
|
|
50
|
-
c="$(printf '%s' "$c" | tr -cd '0-9')"; c="${c:-0}"
|
|
51
|
-
d="$(printf '%s' "$d" | tr -cd '0-9')"; d="${d:-0}"
|
|
52
|
-
printf '%s %s %s %s' "$a" "$b" "$c" "$d"
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
# ---- Classify delta: compare 4-segment tuples ----
|
|
56
|
-
# Args: current_tag latest_tag
|
|
57
|
-
# Prints: "newer|same|older|invalid" + "major|minor|patch|off-cadence|none"
|
|
58
|
-
classify_delta() {
|
|
59
|
-
local cur lat
|
|
60
|
-
cur="$(normalize_semver "$1")" || { printf 'invalid none'; return; }
|
|
61
|
-
lat="$(normalize_semver "$2")" || { printf 'invalid none'; return; }
|
|
62
|
-
# shellcheck disable=SC2086
|
|
63
|
-
set -- $cur; local ca="$1" cb="$2" cc="$3" cd="$4"
|
|
64
|
-
# shellcheck disable=SC2086
|
|
65
|
-
set -- $lat; local la="$1" lb="$2" lc="$3" ld="$4"
|
|
66
|
-
|
|
67
|
-
# Per-segment integer compare (lexicographic per segment by numeric value)
|
|
68
|
-
if [ "$la" -gt "$ca" ]; then printf 'newer major'; return
|
|
69
|
-
elif [ "$la" -lt "$ca" ]; then printf 'older major'; return
|
|
70
|
-
fi
|
|
71
|
-
if [ "$lb" -gt "$cb" ]; then printf 'newer minor'; return
|
|
72
|
-
elif [ "$lb" -lt "$cb" ]; then printf 'older minor'; return
|
|
73
|
-
fi
|
|
74
|
-
if [ "$lc" -gt "$cc" ]; then printf 'newer patch'; return
|
|
75
|
-
elif [ "$lc" -lt "$cc" ]; then printf 'older patch'; return
|
|
76
|
-
fi
|
|
77
|
-
if [ "$ld" -gt "$cd" ]; then printf 'newer off-cadence'; return
|
|
78
|
-
elif [ "$ld" -lt "$cd" ]; then printf 'older off-cadence'; return
|
|
79
|
-
fi
|
|
80
|
-
printf 'same none'
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
# ---- Cache freshness check: returns 0 if fresh (<24h old), 1 if stale or missing ----
|
|
84
|
-
is_cache_fresh() {
|
|
85
|
-
[ -f "${CACHE}" ] || return 1
|
|
86
|
-
local now mtime age
|
|
87
|
-
now="$(date +%s)"
|
|
88
|
-
# BSD date -r on macOS; GNU stat -c on Linux; fall back to perl then python.
|
|
89
|
-
if mtime="$(date -r "${CACHE}" +%s 2>/dev/null)"; then :
|
|
90
|
-
elif mtime="$(stat -c %Y "${CACHE}" 2>/dev/null)"; then :
|
|
91
|
-
elif mtime="$(perl -e 'print((stat shift)[9])' "${CACHE}" 2>/dev/null)"; then :
|
|
92
|
-
else return 1
|
|
93
|
-
fi
|
|
94
|
-
[ -n "${mtime:-}" ] || return 1
|
|
95
|
-
age=$((now - mtime))
|
|
96
|
-
[ "${age}" -lt "${CACHE_TTL_SECONDS}" ]
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
# ---- Fetch latest release. Writes raw body to stdout on success, nothing on failure. ----
|
|
100
|
-
fetch_latest() {
|
|
101
|
-
command -v curl >/dev/null 2>&1 || { log "no curl"; return 1; }
|
|
102
|
-
local url="https://api.github.com/repos/hegemonart/get-design-done/releases/latest"
|
|
103
|
-
curl -sf --max-time 3 -H 'Accept: application/vnd.github+json' "${url}" 2>/dev/null || return 1
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
# ---- Extract fields from the release JSON (no jq). Robust to whitespace; fails soft. ----
|
|
107
|
-
extract_tag() {
|
|
108
|
-
grep -E '"tag_name"[[:space:]]*:' | head -n1 | sed -E 's/.*"tag_name"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/'
|
|
109
|
-
}
|
|
110
|
-
# Body extraction: python3-only. If python3 is absent, we intentionally return empty
|
|
111
|
-
# (D-04 silent-on-failure posture). No awk/sed fallback — JSON string decoding in pure
|
|
112
|
-
# bash is fragile and untested; empty excerpt is the correct degraded state.
|
|
113
|
-
extract_body() {
|
|
114
|
-
command -v python3 >/dev/null 2>&1 || return 0
|
|
115
|
-
python3 -c 'import json,sys
|
|
116
|
-
try:
|
|
117
|
-
d=json.load(sys.stdin)
|
|
118
|
-
b=d.get("body","") or ""
|
|
119
|
-
print(b[:500])
|
|
120
|
-
except Exception:
|
|
121
|
-
pass' 2>/dev/null
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
# ---- Read .design/STATE.md stage field. Returns "brief"|"explore"|"plan"|"design"|"verify"|"" ----
|
|
125
|
-
# Schema source: reference/STATE-TEMPLATE.md — `stage:` lives in both the frontmatter
|
|
126
|
-
# and the <position> block with identical values per the write contract. We take the
|
|
127
|
-
# first occurrence (head -n1), which is the frontmatter line.
|
|
128
|
-
read_state_stage() {
|
|
129
|
-
[ -f "${STATE}" ] || { printf ''; return; }
|
|
130
|
-
grep -E '^stage:' "${STATE}" 2>/dev/null | head -n1 | sed -E 's/^stage:[[:space:]]*"?([^"[:space:]]+)"?.*/\1/'
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
# ---- Read .design/config.json for update_dismissed. Returns tag or empty. ----
|
|
134
|
-
read_dismissed() {
|
|
135
|
-
[ -f "${CONFIG}" ] || { printf ''; return; }
|
|
136
|
-
grep -E '"update_dismissed"[[:space:]]*:' "${CONFIG}" 2>/dev/null | head -n1 | \
|
|
137
|
-
sed -E 's/.*"update_dismissed"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/'
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
# ---- Main control flow ----
|
|
141
|
-
# MANDATORY sourcing guard: wrap the entire main flow so that `source update-check.sh`
|
|
142
|
-
# (used by unit tests and interactive debugging) loads the function definitions without
|
|
143
|
-
# executing steps 1-6 and exiting the sourcing shell. This is non-negotiable — the
|
|
144
|
-
# semver self-test acceptance criterion sources this script.
|
|
145
|
-
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
|
|
146
|
-
|
|
147
|
-
CURRENT_TAG="$(read_current_tag)" || { log "no plugin.json"; exit 0; }
|
|
148
|
-
[ -n "${CURRENT_TAG:-}" ] || { log "no current version parsed"; exit 0; }
|
|
149
|
-
# Normalize to "vX.Y.Z" shape for display (plugin.json stores bare "1.0.7")
|
|
150
|
-
DISPLAY_CURRENT="v${CURRENT_TAG#v}"
|
|
151
|
-
|
|
152
|
-
# Optional --refresh forces a fresh fetch (called by plan 13.3-04's /gdd:check-update --refresh).
|
|
153
|
-
FORCE_REFRESH=0
|
|
154
|
-
for arg in "$@"; do
|
|
155
|
-
case "$arg" in
|
|
156
|
-
--refresh) FORCE_REFRESH=1 ;;
|
|
157
|
-
esac
|
|
158
|
-
done
|
|
159
|
-
|
|
160
|
-
# 1. Populate cache if missing/stale or forced.
|
|
161
|
-
if [ "${FORCE_REFRESH}" -eq 1 ] || ! is_cache_fresh; then
|
|
162
|
-
RAW="$(fetch_latest)" || RAW=""
|
|
163
|
-
if [ -n "${RAW}" ]; then
|
|
164
|
-
LATEST_TAG="$(printf '%s' "${RAW}" | extract_tag)"
|
|
165
|
-
BODY_EXCERPT="$(printf '%s' "${RAW}" | extract_body)"
|
|
166
|
-
# Strip control chars defensively (T-13.3-03)
|
|
167
|
-
BODY_EXCERPT="$(printf '%s' "${BODY_EXCERPT}" | tr -d '\000-\010\013\014\016-\037')"
|
|
168
|
-
# Strip double-quotes so the JSON round-trip sed read-back cannot be injected via a
|
|
169
|
-
# crafted release body. Body is display-only — losing quotes is acceptable.
|
|
170
|
-
BODY_EXCERPT="$(printf '%s' "${BODY_EXCERPT}" | tr -d '"')"
|
|
171
|
-
# Validate LATEST_TAG is a safe semver string before trusting it (CR-02).
|
|
172
|
-
if ! printf '%s' "${LATEST_TAG}" | grep -qE '^v?[0-9]+\.[0-9]+(\.[0-9]+)*$'; then
|
|
173
|
-
log "LATEST_TAG '${LATEST_TAG}' failed semver safety check — aborting cache write"
|
|
174
|
-
LATEST_TAG=""
|
|
175
|
-
fi
|
|
176
|
-
if [ -n "${LATEST_TAG}" ]; then
|
|
177
|
-
read -r DELTA_STATE DELTA_KIND <<EOF
|
|
178
|
-
$(classify_delta "${DISPLAY_CURRENT}" "${LATEST_TAG}")
|
|
179
|
-
EOF
|
|
180
|
-
IS_NEWER=false
|
|
181
|
-
[ "${DELTA_STATE}" = "newer" ] && IS_NEWER=true
|
|
182
|
-
CHECKED_AT="$(date +%s)"
|
|
183
|
-
# Write cache atomically (write-to-tmp + rename) — T-13.3-04 mitigation
|
|
184
|
-
TMP="${CACHE}.tmp.$$"
|
|
185
|
-
{
|
|
186
|
-
printf '{\n'
|
|
187
|
-
printf ' "checked_at": %s,\n' "${CHECKED_AT}"
|
|
188
|
-
printf ' "current_tag": "%s",\n' "${DISPLAY_CURRENT}"
|
|
189
|
-
printf ' "latest_tag": "%s",\n' "${LATEST_TAG}"
|
|
190
|
-
printf ' "delta": "%s",\n' "${DELTA_KIND}"
|
|
191
|
-
printf ' "is_newer": %s,\n' "${IS_NEWER}"
|
|
192
|
-
# Escape the body for JSON — backslashes first, then quotes, then newlines.
|
|
193
|
-
ESC="$(printf '%s' "${BODY_EXCERPT}" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' | awk '{printf "%s\\n", $0}')"
|
|
194
|
-
printf ' "changelog_excerpt": "%s"\n' "${ESC}"
|
|
195
|
-
printf '}\n'
|
|
196
|
-
} > "${TMP}" 2>/dev/null && mv "${TMP}" "${CACHE}" 2>/dev/null || rm -f "${TMP}" 2>/dev/null
|
|
197
|
-
fi
|
|
198
|
-
fi
|
|
199
|
-
fi
|
|
200
|
-
|
|
201
|
-
# 2. Read cache (whether freshly written or still valid).
|
|
202
|
-
[ -f "${CACHE}" ] || exit 0 # no cache, nothing to do — silent exit
|
|
203
|
-
|
|
204
|
-
C_LATEST="$(grep -E '"latest_tag"' "${CACHE}" 2>/dev/null | head -n1 | sed -E 's/.*"latest_tag"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')"
|
|
205
|
-
C_DELTA="$(grep -E '"delta"' "${CACHE}" 2>/dev/null | head -n1 | sed -E 's/.*"delta"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/')"
|
|
206
|
-
# Allowlist-gate C_DELTA before it reaches any shell context (WR-04).
|
|
207
|
-
case "${C_DELTA:-}" in
|
|
208
|
-
major|minor|patch|off-cadence|none) : ;;
|
|
209
|
-
*) C_DELTA="unknown" ;;
|
|
210
|
-
esac
|
|
211
|
-
C_NEWER="$(grep -E '"is_newer"' "${CACHE}" 2>/dev/null | head -n1 | sed -E 's/.*"is_newer"[[:space:]]*:[[:space:]]*(true|false).*/\1/')"
|
|
212
|
-
C_BODY="$(grep -E '"changelog_excerpt"' "${CACHE}" 2>/dev/null | head -n1 | sed -E 's/.*"changelog_excerpt"[[:space:]]*:[[:space:]]*"(.*)".*/\1/' | sed -E 's/\\n/\n/g')"
|
|
213
|
-
|
|
214
|
-
# 3. Gate: if cache says not newer, remove any stale banner and exit.
|
|
215
|
-
if [ "${C_NEWER:-false}" != "true" ]; then
|
|
216
|
-
rm -f "${BANNER}" 2>/dev/null
|
|
217
|
-
exit 0
|
|
218
|
-
fi
|
|
219
|
-
|
|
220
|
-
# 4. Dismissal gate (D-13): if user already dismissed this exact tag, suppress.
|
|
221
|
-
DISMISSED="$(read_dismissed)"
|
|
222
|
-
if [ -n "${DISMISSED}" ] && [ "${DISMISSED}" = "${C_LATEST}" ]; then
|
|
223
|
-
rm -f "${BANNER}" 2>/dev/null
|
|
224
|
-
exit 0
|
|
225
|
-
fi
|
|
226
|
-
|
|
227
|
-
# 5. State-machine guard (D-11/D-12): suppress during plan|design|verify.
|
|
228
|
-
STAGE="$(read_state_stage)"
|
|
229
|
-
case "${STAGE}" in
|
|
230
|
-
plan|design|verify)
|
|
231
|
-
rm -f "${BANNER}" 2>/dev/null
|
|
232
|
-
exit 0
|
|
233
|
-
;;
|
|
234
|
-
esac
|
|
235
|
-
|
|
236
|
-
# 6. All gates passed — render the banner atomically.
|
|
237
|
-
TMP="${BANNER}.tmp.$$"
|
|
238
|
-
{
|
|
239
|
-
printf '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
|
|
240
|
-
printf ' 📦 Plugin update: %s → %s (%s)\n' "${DISPLAY_CURRENT}" "${C_LATEST}" "${C_DELTA}"
|
|
241
|
-
if [ -n "${C_BODY}" ]; then
|
|
242
|
-
printf '%s\n' "${C_BODY}"
|
|
243
|
-
fi
|
|
244
|
-
printf ' Install: /gdd:update Dismiss: /gdd:check-update --dismiss\n'
|
|
245
|
-
printf '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
|
|
246
|
-
} > "${TMP}" 2>/dev/null && mv "${TMP}" "${BANNER}" 2>/dev/null || rm -f "${TMP}" 2>/dev/null
|
|
247
|
-
|
|
248
|
-
exit 0
|
|
249
|
-
fi
|
|
250
|
-
# When sourced (BASH_SOURCE != $0), fall through with function definitions loaded
|
|
251
|
-
# and without side effects. Sourcing callers must invoke functions explicitly.
|