@hegemonart/get-design-done 1.14.5 → 1.14.8
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 +7 -2
- package/.claude-plugin/plugin.json +11 -2
- package/CHANGELOG.md +109 -0
- package/README.md +28 -0
- package/SKILL.md +2 -0
- package/agents/design-executor.md +41 -0
- package/agents/design-figma-writer.md +61 -1
- package/agents/design-start-writer.md +221 -0
- package/connections/figma.md +10 -0
- package/hooks/first-run-nudge.sh +82 -0
- package/hooks/gdd-bash-guard.js +49 -0
- package/hooks/gdd-decision-injector.js +196 -0
- package/hooks/gdd-mcp-circuit-breaker.js +140 -0
- package/hooks/gdd-protected-paths.js +114 -0
- package/hooks/hooks.json +44 -0
- package/package.json +1 -1
- package/reference/cycle-handoff-preamble.md +22 -0
- package/reference/figma-sandbox.md +19 -0
- package/reference/mcp-budget.default.json +13 -0
- package/reference/meta-rules.md +66 -0
- package/reference/protected-paths.default.json +18 -0
- package/reference/registry.json +35 -0
- package/reference/registry.schema.json +52 -0
- package/reference/retrieval-contract.md +30 -0
- package/reference/schemas/mcp-budget.schema.json +21 -0
- package/reference/schemas/protected-paths.schema.json +19 -0
- package/reference/shared-preamble.md +6 -57
- package/reference/start-interview.md +84 -0
- package/scripts/build-intel.cjs +20 -0
- package/scripts/injection-patterns.cjs +42 -1
- package/scripts/lib/blast-radius.cjs +97 -0
- package/scripts/lib/dangerous-patterns.cjs +118 -0
- package/scripts/lib/detect-ui-root.cjs +187 -0
- package/scripts/lib/glob-match.cjs +57 -0
- package/scripts/lib/reference-registry.cjs +101 -0
- package/scripts/lib/start-findings-engine.cjs +405 -0
- package/skills/pause/SKILL.md +3 -0
- package/skills/progress/SKILL.md +2 -0
- package/skills/reflect/SKILL.md +2 -0
- package/skills/resume/SKILL.md +3 -0
- package/skills/start/SKILL.md +166 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Figma Plugin Sandbox — Hard Rules
|
|
2
|
+
|
|
3
|
+
The `use_figma` MCP runs every script inside the Figma plugin sandbox with a **~5–10s per-call budget**. Four pitfalls repeatedly burn quota in docs-authoring loops. Treat these as hard rules.
|
|
4
|
+
|
|
5
|
+
**Rule 1 — `loadFontAsync` does NOT cache across `use_figma` calls.** Every new call re-fetches font metadata. Preload every style ONCE at the top of a script; clone existing text nodes via `node.clone()` or `figma.createText().fontName = ...` rather than calling `loadFontAsync` again.
|
|
6
|
+
|
|
7
|
+
**Rule 2 — `figma.root.findOne()` is O(tree-size) per call.** On a real file with thousands of frames this alone eats the budget. When you already know the node you want to act on, pass the node ID directly and call `figma.getNodeById(id)`. Never call `findOne` in a loop.
|
|
8
|
+
|
|
9
|
+
**Rule 3 — `appendChild` on a large attached tree triggers full AutoLayout recomputation.** Build subtrees off-tree (on a detached parent) and attach the completed subtree once at the end. This avoids N + N-1 + ... + 1 full layout passes.
|
|
10
|
+
|
|
11
|
+
**Rule 4 — per-call timeout is ~5–10s.** For docs-authoring (multi-row layouts populating from a library), budget **≤2 row-equivalents per `use_figma` call**. Exceeding this puts the script in the hill-climb-against-timeout failure mode: you retry with less content per call, each retry wastes another 5–10s, and MCP quota vanishes.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## When to skip `use_figma` entirely
|
|
16
|
+
|
|
17
|
+
For **authoring new content** — creating pages, populating with library components, building documentation layouts from scratch — prefer `figma:figma-generate-design` from the Figma plugin. It runs outside the sandbox and has no per-call timeout.
|
|
18
|
+
|
|
19
|
+
`use_figma` (and `/gdd:figma-write`) remain the right tools for **decision-writing**: attaching annotations, binding local-style tokens, registering Code Connect mappings, writing back implementation-status. Small, bounded, read-then-write operations where the four pitfalls don't apply.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./schemas/mcp-budget.schema.json",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"description": "Default per-task MCP ceilings enforced by hooks/gdd-mcp-circuit-breaker.js. User overrides merge from .design/config.json.mcp_budget.",
|
|
5
|
+
"max_calls_per_task": 30,
|
|
6
|
+
"max_consecutive_timeouts": 3,
|
|
7
|
+
"reset_on_success": true,
|
|
8
|
+
"tracked_tools": [
|
|
9
|
+
"mcp__.*use_figma$",
|
|
10
|
+
"mcp__.*use_paper$",
|
|
11
|
+
"mcp__.*use_pencil$"
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Meta-Rules (L0)
|
|
2
|
+
|
|
3
|
+
These rules are framework-invariant across the GDD pipeline. They do not change between cycles, phases, or tasks. Every agent imports `reference/shared-preamble.md`, which aggregates `reference/meta-rules.md` first.
|
|
4
|
+
|
|
5
|
+
**Tier: L0** (frozen prefix; stabilizes the Anthropic 5-min prompt-cache prefix across agent spawns — the churning L2 body below does NOT invalidate this).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Required Reading Discipline
|
|
10
|
+
|
|
11
|
+
When the orchestrator's prompt contains a `<required_reading>` block, you MUST read every file it lists with the `Read` tool before taking any other action. Paths prefixed with `@` are file paths — pass them directly to `Read`. Skipping required reading is a hard violation: you will produce stale output that the downstream verifier catches, wasting a full spawn cycle.
|
|
12
|
+
|
|
13
|
+
## Writes Protocol
|
|
14
|
+
|
|
15
|
+
Only write files declared in your frontmatter `writes:` list. Agents with `reads-only: true` must never call `Write` or `Edit` on any file. If the task appears to require writing outside your declared scope, stop and return a `<blocker>` in STATE.md rather than expanding your write surface.
|
|
16
|
+
|
|
17
|
+
If your agent runs in a phase that enforces atomic commits (most do), commit only files in your declared `writes:` list. Use the repo commit convention: `docs(phase-N-P): short imperative description` for documentation-class changes, `feat(phase-N-P): ...` for new capability, `fix(phase-N-P): ...` for bug fixes. Phase and plan numbers come from `.design/STATE.md` `phase:` and the invoking plan's frontmatter.
|
|
18
|
+
|
|
19
|
+
## Deviation Handling
|
|
20
|
+
|
|
21
|
+
If an expected file is missing, a required reading entry fails to load, or the prompt references an artifact that contradicts STATE.md, **stop** before taking any destructive action. Return a structured blocker to STATE.md and terminate your response with your completion marker:
|
|
22
|
+
|
|
23
|
+
```markdown
|
|
24
|
+
<blocker>
|
|
25
|
+
type: missing-artifact | stale-state | contract-violation
|
|
26
|
+
detail: <one sentence>
|
|
27
|
+
suggested-fix: <one sentence or leave blank>
|
|
28
|
+
</blocker>
|
|
29
|
+
|
|
30
|
+
## {STAGE} COMPLETE
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Completion Markers
|
|
34
|
+
|
|
35
|
+
Valid completion markers per agent class (from `agents/README.md` §Completion Markers):
|
|
36
|
+
- Research agent → `## RESEARCH COMPLETE`
|
|
37
|
+
- Planning agent → `## PLANNING COMPLETE`
|
|
38
|
+
- Execution agent → `## EXECUTION COMPLETE`
|
|
39
|
+
- Verification agent → `## VERIFICATION COMPLETE`
|
|
40
|
+
- Stage-specific agents → the stage name: `## SCAN COMPLETE`, `## DISCOVER COMPLETE`, `## PLAN COMPLETE`, `## DESIGN COMPLETE`, `## VERIFY COMPLETE`.
|
|
41
|
+
|
|
42
|
+
The orchestrator detects failure by reading STATE.md for a `<blocker>`, not by the absence of a marker. Always emit the marker.
|
|
43
|
+
|
|
44
|
+
## Context-Exhaustion & Budget Awareness
|
|
45
|
+
|
|
46
|
+
A PostToolUse hook at `hooks/context-exhaustion.js` watches your tool output for the string `<context-exhaustion>` in your response. If you determine you cannot finish the task in the remaining context, emit:
|
|
47
|
+
|
|
48
|
+
```xml
|
|
49
|
+
<context-exhaustion>
|
|
50
|
+
reason: <one-sentence cause — e.g., "required_reading totals 47KB exceeding remaining context">
|
|
51
|
+
resume-hint: <one-sentence instruction for a resumption spawn>
|
|
52
|
+
</context-exhaustion>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
…before your completion marker. The hook captures this into STATE.md so the orchestrator can re-spawn you with a narrower scope. Do not guess when you're near exhaustion — only emit when a concrete obstacle (file too large to read, required diff too wide) forced the call.
|
|
56
|
+
|
|
57
|
+
A PreToolUse hook at `hooks/budget-enforcer.js` intercepts every `Task` spawn (including the one that invoked you). The hook may:
|
|
58
|
+
- **Short-circuit** your spawn with a cached result from `.design/cache-manifest.json` (transparent — you never run).
|
|
59
|
+
- **Downgrade** your tier to Haiku at the 80% per-task cap soft-threshold, silently (`auto_downgrade_on_cap: true` in `.design/budget.json`, D-03).
|
|
60
|
+
- **Hard-block** your spawn at the 100% per-task or per-phase cap with an actionable error (D-02).
|
|
61
|
+
|
|
62
|
+
Implication for you as the agent: **do not assume a specific model tier is live.** Your output must be correct whether you run on Haiku, Sonnet, or Opus. If a task genuinely requires reasoning density beyond Haiku, the `size_budget` + `default-tier` combination should have been set at authoring time so the router routes it correctly — the remedy is a frontmatter update (a Phase 11 reflector proposal), not a mid-run assumption.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
*Framework-invariant meta-rules. Aggregated by `reference/shared-preamble.md`.*
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./schemas/protected-paths.schema.json",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"description": "Paths the plugin refuses to Edit/Write or mutate via Bash without an explicit user override. User additions in .design/config.json.protected_paths MERGE into this list — they can extend but cannot reduce the default set.",
|
|
5
|
+
"protected_paths": [
|
|
6
|
+
"reference/**",
|
|
7
|
+
".design/archive/**",
|
|
8
|
+
".design/config.json",
|
|
9
|
+
".design/telemetry/**",
|
|
10
|
+
"skills/**",
|
|
11
|
+
"commands/**",
|
|
12
|
+
"hooks/**",
|
|
13
|
+
".git/**",
|
|
14
|
+
".claude/settings.json",
|
|
15
|
+
".claude-plugin/plugin.json",
|
|
16
|
+
".claude-plugin/marketplace.json"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./schemas/registry.schema.json",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"generated_at": "2026-04-24T00:00:00.000Z",
|
|
5
|
+
"entries": [
|
|
6
|
+
{ "name": "shared-preamble", "path": "reference/shared-preamble.md", "type": "preamble", "tier": "L0", "description": "L0 aggregator — imports meta-rules first and exposes Framework Identity + Ordering Convention + Pre-Warming" },
|
|
7
|
+
{ "name": "meta-rules", "path": "reference/meta-rules.md", "type": "meta-rules", "tier": "L0", "description": "Framework-invariant rules: Required Reading Discipline, Writes Protocol, Deviation Handling, Completion Markers, Context-Exhaustion & Budget awareness" },
|
|
8
|
+
{ "name": "cycle-handoff-preamble", "path": "reference/cycle-handoff-preamble.md", "type": "preamble", "description": "Framing prose for archived cycle material — read as reference, not as current request" },
|
|
9
|
+
{ "name": "retrieval-contract", "path": "reference/retrieval-contract.md", "type": "preamble", "description": "3-layer search → metadata → full-doc protocol with per-row token-cost labels" },
|
|
10
|
+
{ "name": "heuristics", "path": "reference/heuristics.md", "type": "heuristic", "tier": "L2", "description": "NNG heuristics + GDD-specific rubrics used by auditor / verifier" },
|
|
11
|
+
{ "name": "anti-patterns", "path": "reference/anti-patterns.md", "type": "heuristic", "tier": "L2", "description": "Catalog of design anti-patterns surfaced during audit" },
|
|
12
|
+
{ "name": "checklists", "path": "reference/checklists.md", "type": "heuristic", "tier": "L2", "description": "Per-category gate checklists" },
|
|
13
|
+
{ "name": "accessibility", "path": "reference/accessibility.md", "type": "heuristic", "description": "WCAG + focus + keyboard-nav heuristics referenced by a11y-mapper" },
|
|
14
|
+
{ "name": "debugger-philosophy", "path": "reference/debugger-philosophy.md", "type": "heuristic", "description": "Philosophy guide for systematic-debugging agents" },
|
|
15
|
+
{ "name": "parallelism-rules", "path": "reference/parallelism-rules.md", "type": "heuristic", "description": "Rules for serial/parallel agent dispatch and Touches conflict detection" },
|
|
16
|
+
{ "name": "priority-matrix", "path": "reference/priority-matrix.md", "type": "heuristic", "description": "Severity × Effort priority-score formula used by gap prioritizers" },
|
|
17
|
+
{ "name": "project-skills-guide", "path": "reference/project-skills-guide.md", "type": "heuristic", "description": "Guide for authoring project-skill artifacts (sketch/spike wrap-ups)" },
|
|
18
|
+
{ "name": "audit-scoring", "path": "reference/audit-scoring.md", "type": "output-contract", "description": "6-pillar + 7-category audit score rubric and output schema" },
|
|
19
|
+
{ "name": "review-format", "path": "reference/review-format.md", "type": "output-contract", "description": "Format of code-review output documents" },
|
|
20
|
+
{ "name": "authority-feeds", "path": "reference/authority-feeds.md", "type": "authority-feed", "description": "Whitelist of design-authority feed sources for the watcher" },
|
|
21
|
+
{ "name": "motion", "path": "reference/motion.md", "type": "motion", "description": "Motion vocabulary seed (extended in Phase 18)" },
|
|
22
|
+
{ "name": "typography", "path": "reference/typography.md", "type": "heuristic", "description": "Typography-system heuristics used by visual-hierarchy-mapper" },
|
|
23
|
+
{ "name": "ai-native-tool-interface","path": "reference/ai-native-tool-interface.md","type": "surfaces", "description": "AI-native tool interface contract used across connections" },
|
|
24
|
+
{ "name": "intel-schema", "path": "reference/intel-schema.md", "type": "schema", "description": "Schema documentation for .design/intel/ files" },
|
|
25
|
+
{ "name": "config-schema", "path": "reference/config-schema.md", "type": "schema", "description": "Schema documentation for .design/config.json" },
|
|
26
|
+
{ "name": "DEPRECATIONS", "path": "reference/DEPRECATIONS.md", "type": "schema", "description": "Deprecation records and redirect mappings" },
|
|
27
|
+
{ "name": "STATE-TEMPLATE", "path": "reference/STATE-TEMPLATE.md", "type": "schema", "description": "Template for .design/STATE.md scaffolded by /gdd:scan" },
|
|
28
|
+
{ "name": "model-prices", "path": "reference/model-prices.md", "type": "data", "description": "Anthropic model pricing table used by the telemetry aggregator" },
|
|
29
|
+
{ "name": "model-tiers", "path": "reference/model-tiers.md", "type": "data", "description": "Per-agent default tier map and rationale" },
|
|
30
|
+
{ "name": "figma-sandbox", "path": "reference/figma-sandbox.md", "type": "defaults", "description": "Four Figma plugin-sandbox pitfalls encoded as hard rules" },
|
|
31
|
+
{ "name": "mcp-budget.default", "path": "reference/mcp-budget.default.json", "type": "defaults", "description": "Default MCP per-task budget (calls + consecutive-timeout thresholds)" },
|
|
32
|
+
{ "name": "protected-paths.default", "path": "reference/protected-paths.default.json","type": "defaults", "description": "Default glob list the plugin refuses to Edit/Write/mutate-via-Bash without override" },
|
|
33
|
+
{ "name": "start-interview", "path": "reference/start-interview.md", "type": "preamble", "description": "Locked 5-question interview consumed by /gdd:start (Phase 14.7) — pain, target-area, budget, framework/DS confirmation, Figma/canvas workflow" }
|
|
34
|
+
]
|
|
35
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/hegemonart/get-design-done/reference/schemas/registry.schema.json",
|
|
4
|
+
"title": "Reference Registry",
|
|
5
|
+
"description": "Typed index of every reference/*.md (and reference/*.json default) in the plugin. Enables agents to query by type instead of grep-hunting import strings. Round-trip enforced: every reference/*.md must appear in entries[], every entry must resolve to an existing file.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["version", "generated_at", "entries"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"$schema": { "type": "string" },
|
|
10
|
+
"version": { "const": 1 },
|
|
11
|
+
"generated_at": { "type": "string", "format": "date-time" },
|
|
12
|
+
"entries": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"items": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"required": ["name", "path", "type"],
|
|
17
|
+
"properties": {
|
|
18
|
+
"name": { "type": "string", "minLength": 1, "pattern": "^[a-z0-9][a-z0-9-._]*$" },
|
|
19
|
+
"path": { "type": "string", "minLength": 1 },
|
|
20
|
+
"type": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"enum": [
|
|
23
|
+
"easing",
|
|
24
|
+
"taxonomy",
|
|
25
|
+
"preamble",
|
|
26
|
+
"output-contract",
|
|
27
|
+
"heuristic",
|
|
28
|
+
"schema",
|
|
29
|
+
"defaults",
|
|
30
|
+
"surfaces",
|
|
31
|
+
"motion",
|
|
32
|
+
"influences",
|
|
33
|
+
"meta-rules",
|
|
34
|
+
"data",
|
|
35
|
+
"authority-feed",
|
|
36
|
+
"principles",
|
|
37
|
+
"emotional-design",
|
|
38
|
+
"experience",
|
|
39
|
+
"palette",
|
|
40
|
+
"style-vocabulary"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"tier": { "enum": ["L0", "L1", "L2"] },
|
|
44
|
+
"description": { "type": "string" },
|
|
45
|
+
"registered_at": { "type": "string", "format": "date-time" }
|
|
46
|
+
},
|
|
47
|
+
"additionalProperties": false
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"additionalProperties": false
|
|
52
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Retrieval Contract — 3-Layer Search
|
|
2
|
+
|
|
3
|
+
When an agent or skill needs information from `.design/` artifacts or the `reference/` library, apply this protocol in order. Token economy matters — ladder from cheapest to most expensive.
|
|
4
|
+
|
|
5
|
+
## Layer 1 — Search (~50–100 tokens per hit)
|
|
6
|
+
|
|
7
|
+
Open `reference/registry.json` or the intel index at `.design/intel/`. Read one row per candidate: `{name, path, type, tier, description}`. This is enough to decide whether to descend further.
|
|
8
|
+
|
|
9
|
+
- `list({type})` — everything tagged with a given type (e.g., `"heuristic"`, `"motion"`, `"preamble"`).
|
|
10
|
+
- `find(name)` — direct lookup for a specific reference file by short name.
|
|
11
|
+
|
|
12
|
+
## Layer 2 — Metadata (~200–300 tokens per hit)
|
|
13
|
+
|
|
14
|
+
Open the candidate file's frontmatter + first 20 lines + heading outline. Decide if the full body is on the critical path.
|
|
15
|
+
|
|
16
|
+
## Layer 3 — Full document (~500–1000+ tokens)
|
|
17
|
+
|
|
18
|
+
Read the file with the `Read` tool. Only do this when layers 1 and 2 confirmed the doc is load-bearing for the current task.
|
|
19
|
+
|
|
20
|
+
## Token economy
|
|
21
|
+
|
|
22
|
+
A `/gdd:recall "term"` query that returns 5 Layer-1 hits ≈ 400 tokens. Opening all 5 full docs blind ≈ 4000 tokens. **Always ladder.** Skipping to Layer 3 is the #1 cause of context exhaustion in long pipeline runs.
|
|
23
|
+
|
|
24
|
+
## FTS5 backend (Phase 19.5)
|
|
25
|
+
|
|
26
|
+
Layer 1 becomes `scripts/lib/design-search.cjs` — same protocol, same output shape, but backed by `.design/search.db` instead of grep. Agents do not need to change anything; the backend swap is transparent.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
*Imported by every skill that reads `.design/` artifacts: `/gdd:progress`, `/gdd:resume`, `/gdd:reflect`, `/gdd:pause`, `/gdd:recall` (Phase 19.5+), `/gdd:timeline` (Phase 19.5+). Tier: preamble. Phase: 14.5.*
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/hegemonart/get-design-done/reference/schemas/mcp-budget.schema.json",
|
|
4
|
+
"title": "MCP Budget",
|
|
5
|
+
"description": "Thresholds for the MCP circuit-breaker hook. Applies to use_figma / use_paper / use_pencil and any tracked_tools entry the user extends.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["version", "max_calls_per_task", "max_consecutive_timeouts"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"$schema": { "type": "string" },
|
|
10
|
+
"version": { "const": 1 },
|
|
11
|
+
"description": { "type": "string" },
|
|
12
|
+
"max_calls_per_task": { "type": "integer", "minimum": 0, "maximum": 10000 },
|
|
13
|
+
"max_consecutive_timeouts": { "type": "integer", "minimum": 0, "maximum": 1000 },
|
|
14
|
+
"reset_on_success": { "type": "boolean" },
|
|
15
|
+
"tracked_tools": {
|
|
16
|
+
"type": "array",
|
|
17
|
+
"items": { "type": "string", "minLength": 1 }
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"additionalProperties": false
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://github.com/hegemonart/get-design-done/reference/schemas/protected-paths.schema.json",
|
|
4
|
+
"title": "Protected Paths",
|
|
5
|
+
"description": "Glob list describing paths the plugin refuses to Edit/Write or mutate via destructive Bash. User additions MERGE with this default list; users cannot reduce the default set.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["version", "protected_paths"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"$schema": { "type": "string" },
|
|
10
|
+
"version": { "const": 1 },
|
|
11
|
+
"description": { "type": "string" },
|
|
12
|
+
"protected_paths": {
|
|
13
|
+
"type": "array",
|
|
14
|
+
"minItems": 1,
|
|
15
|
+
"items": { "type": "string", "minLength": 1 }
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"additionalProperties": false
|
|
19
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# GSD Agent Shared Preamble
|
|
2
2
|
|
|
3
|
-
> **This file is imported via `@reference/shared-preamble.md` as the first line of every agent body in `agents/*.md`. Its placement is load-bearing for Anthropic's 5-minute prompt cache (see `reference/model-tiers.md` and
|
|
3
|
+
> **This file is imported via `@reference/shared-preamble.md` as the first line of every agent body in `agents/*.md`. Its placement is load-bearing for Anthropic's 5-minute prompt cache (see `reference/model-tiers.md` and Phase 10.1 decision D-08 Layer A): because every agent opens with the identical preamble prefix, the second and subsequent agent spawns in a session pay `cached_input_per_1m` rates rather than full `input_per_1m` rates for these bytes. Do not inline this content into agent bodies — always import.**
|
|
4
|
+
>
|
|
5
|
+
> **As of Phase 14.5 this file is an aggregator.** The framework-invariant subsections (Required Reading Discipline, Writes Protocol, Deviation Handling, Completion Markers, Context-Exhaustion & Budget awareness) live in `reference/meta-rules.md` (tier L0) so the L2 heuristics/anti-patterns/checklists churn never invalidates the L0 prefix.
|
|
6
|
+
|
|
7
|
+
@reference/meta-rules.md
|
|
4
8
|
|
|
5
9
|
## Framework Identity
|
|
6
10
|
|
|
@@ -8,61 +12,6 @@ You are a GSD agent operating under the `get-design-done` plugin contract (see `
|
|
|
8
12
|
|
|
9
13
|
You are one step in a pipeline. You do not own the pipeline. The orchestrator decides what runs next based on your output.
|
|
10
14
|
|
|
11
|
-
## Required Reading Discipline
|
|
12
|
-
|
|
13
|
-
When the orchestrator's prompt contains a `<required_reading>` block, you MUST read every file it lists with the `Read` tool before taking any other action. Paths prefixed with `@` are file paths — pass them directly to `Read`. Skipping required reading is a hard violation: you will produce stale output that the downstream verifier catches, wasting a full spawn cycle.
|
|
14
|
-
|
|
15
|
-
## Writes Protocol
|
|
16
|
-
|
|
17
|
-
Only write files declared in your frontmatter `writes:` list. Agents with `reads-only: true` must never call `Write` or `Edit` on any file. If the task appears to require writing outside your declared scope, stop and return a `<blocker>` in STATE.md rather than expanding your write surface.
|
|
18
|
-
|
|
19
|
-
If your agent runs in a phase that enforces atomic commits (most do), commit only files in your declared `writes:` list. Use the repo commit convention: `docs(phase-N-P): short imperative description` for documentation-class changes, `feat(phase-N-P): ...` for new capability, `fix(phase-N-P): ...` for bug fixes. Phase and plan numbers come from `.design/STATE.md` `phase:` and the invoking plan's frontmatter.
|
|
20
|
-
|
|
21
|
-
## Deviation Handling
|
|
22
|
-
|
|
23
|
-
If an expected file is missing, a required reading entry fails to load, or the prompt references an artifact that contradicts STATE.md, **stop** before taking any destructive action. Return a structured blocker to STATE.md and terminate your response with your completion marker:
|
|
24
|
-
|
|
25
|
-
```markdown
|
|
26
|
-
<blocker>
|
|
27
|
-
type: missing-artifact | stale-state | contract-violation
|
|
28
|
-
detail: <one sentence>
|
|
29
|
-
suggested-fix: <one sentence or leave blank>
|
|
30
|
-
</blocker>
|
|
31
|
-
|
|
32
|
-
## {STAGE} COMPLETE
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Valid completion markers per agent class (from `agents/README.md` §Completion Markers):
|
|
36
|
-
- Research agent → `## RESEARCH COMPLETE`
|
|
37
|
-
- Planning agent → `## PLANNING COMPLETE`
|
|
38
|
-
- Execution agent → `## EXECUTION COMPLETE`
|
|
39
|
-
- Verification agent → `## VERIFICATION COMPLETE`
|
|
40
|
-
- Stage-specific agents → the stage name: `## SCAN COMPLETE`, `## DISCOVER COMPLETE`, `## PLAN COMPLETE`, `## DESIGN COMPLETE`, `## VERIFY COMPLETE`.
|
|
41
|
-
|
|
42
|
-
The orchestrator detects failure by reading STATE.md for a `<blocker>`, not by the absence of a marker. Always emit the marker.
|
|
43
|
-
|
|
44
|
-
## Context-Exhaustion Hook Awareness
|
|
45
|
-
|
|
46
|
-
A PostToolUse hook at `hooks/context-exhaustion.js` watches your tool output for the string `<context-exhaustion>` in your response. If you determine you cannot finish the task in the remaining context, emit:
|
|
47
|
-
|
|
48
|
-
```xml
|
|
49
|
-
<context-exhaustion>
|
|
50
|
-
reason: <one-sentence cause — e.g., "required_reading totals 47KB exceeding remaining context">
|
|
51
|
-
resume-hint: <one-sentence instruction for a resumption spawn>
|
|
52
|
-
</context-exhaustion>
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
…before your completion marker. The hook captures this into STATE.md so the orchestrator can re-spawn you with a narrower scope. Do not guess when you're near exhaustion — only emit when a concrete obstacle (file too large to read, required diff too wide) forced the call.
|
|
56
|
-
|
|
57
|
-
## Budget-Enforcer Hook Awareness (Phase 10.1)
|
|
58
|
-
|
|
59
|
-
A PreToolUse hook at `hooks/budget-enforcer.js` intercepts every `Task` spawn (including the one that invoked you). The hook may:
|
|
60
|
-
- **Short-circuit** your spawn with a cached result from `.design/cache-manifest.json` (transparent — you never run).
|
|
61
|
-
- **Downgrade** your tier to Haiku at the 80% per-task cap soft-threshold, silently (`auto_downgrade_on_cap: true` in `.design/budget.json`, D-03).
|
|
62
|
-
- **Hard-block** your spawn at the 100% per-task or per-phase cap with an actionable error (D-02).
|
|
63
|
-
|
|
64
|
-
Implication for you as the agent: **do not assume a specific model tier is live.** Your output must be correct whether you run on Haiku, Sonnet, or Opus. If a task genuinely requires reasoning density beyond Haiku, the `size_budget` + `default-tier` combination should have been set at authoring time so the router routes it correctly — the remedy is a frontmatter update (a Phase 11 reflector proposal), not a mid-run assumption.
|
|
65
|
-
|
|
66
15
|
## Ordering Convention (D-17)
|
|
67
16
|
|
|
68
17
|
Your agent body is structured in this exact order so the cache prefix stays stable:
|
|
@@ -79,4 +28,4 @@ The `/gdd:warm-cache` command (ships in Plan 10.1-02) pre-warms this identical p
|
|
|
79
28
|
|
|
80
29
|
---
|
|
81
30
|
|
|
82
|
-
*Imported by: every file under `agents/*.md` (except `agents/README.md`). Maintained as part of Phase 10.1 (OPT-07). Edits to this file affect every agent simultaneously — verify across the full agent suite before committing.*
|
|
31
|
+
*Imported by: every file under `agents/*.md` (except `agents/README.md`). Maintained as part of Phase 10.1 (OPT-07) and Phase 14.5 (L0/L2 split). Edits to this file affect every agent simultaneously — verify across the full agent suite before committing.*
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# /gdd:start — Locked 5-Question Interview
|
|
2
|
+
|
|
3
|
+
**Purpose:** collect the minimum signal needed to steer the findings engine without slowing first-run completion past 30 seconds of interview wall-clock. Autodetectable dimensions collapse to a one-key confirmation; genuinely non-derivable dimensions are asked explicitly.
|
|
4
|
+
|
|
5
|
+
**Hard constraint:** v1.14.7 ships this fixed question set. Do not branch, re-order, or insert new questions without an explicit `/gsd-discuss-phase` override captured in a future DISCUSSION.md.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Q1 — Pain point *(required, free text)*
|
|
10
|
+
|
|
11
|
+
**Prompt:**
|
|
12
|
+
|
|
13
|
+
> What's the one design issue you'd most like to see fixed first? One line, ≤120 chars. Examples: "buttons feel jittery on hover", "colors are inconsistent across forms", "mobile nav breaks on the hero", "a11y on our modal".
|
|
14
|
+
|
|
15
|
+
**Default when `--skip-interview`:** empty string (pain hint disabled; scorer runs without boost).
|
|
16
|
+
|
|
17
|
+
**Validation:** max 120 chars; trim leading/trailing whitespace. No charset restrictions — the hint is free text.
|
|
18
|
+
|
|
19
|
+
**Failure posture:** if the user aborts at Q1, abort the whole skill with a one-line pointer to `/gdd:scan`.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Q2 — Target area confirmation *(autodetected, single select)*
|
|
24
|
+
|
|
25
|
+
**Prompt:**
|
|
26
|
+
|
|
27
|
+
> I detected your UI code at `<detected.path>` (`<detected.kind>`, confidence=`<detected.confidence>`). Use this surface? [Yes / choose another path / skip demo]
|
|
28
|
+
|
|
29
|
+
**Default when `--skip-interview`:** accept detected path as-is.
|
|
30
|
+
|
|
31
|
+
**Validation:** if the user picks "choose another path", ask for a repo-relative path and validate that at least one UI-extension file (.tsx/.jsx/.ts/.js/.svelte/.vue) exists inside. Re-prompt on failure. Two rejections → fall back to detected path with a warning.
|
|
32
|
+
|
|
33
|
+
**Failure posture:** if the user picks "skip demo", exit clean with `## START COMPLETE` and no `.design/` footprint.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Q3 — Budget / latency preference *(enum, default=balanced)*
|
|
38
|
+
|
|
39
|
+
**Prompt:**
|
|
40
|
+
|
|
41
|
+
> Report speed vs thoroughness? [fast (≤90s, tokens-only) / balanced (≤3min, default) / thorough (≤5min, includes a11y + motion checks)]
|
|
42
|
+
|
|
43
|
+
**Default when `--skip-interview`:** `balanced`.
|
|
44
|
+
|
|
45
|
+
**Validation:** must be one of `fast | balanced | thorough`. Any other input → re-prompt once, then default to `balanced`.
|
|
46
|
+
|
|
47
|
+
**Failure posture:** n/a — this is non-blocking; defaulting is safe.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Q4 — Framework + design-system confirmation *(autodetected, combined)*
|
|
52
|
+
|
|
53
|
+
**Prompt:**
|
|
54
|
+
|
|
55
|
+
> Detected framework=`<framework>`, design-system=`<design_system>`. Correct? [Yes / override framework / override design-system / skip (use detected)]
|
|
56
|
+
|
|
57
|
+
**Default when `--skip-interview`:** accept both detected values.
|
|
58
|
+
|
|
59
|
+
**Validation:** "override framework" → ask for a free-text label (`next | remix | vite | cra | svelte | vue | astro | solid | unknown`). "override design-system" → same, values (`tailwind | css-modules | vanilla-extract | styled | linaria | emotion | panda | unknown`). Invalid → default to `unknown`.
|
|
60
|
+
|
|
61
|
+
**Failure posture:** n/a — detection output is always available even if the user skips.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Q5 — Figma / canvas workflow *(enum, non-blocking)*
|
|
66
|
+
|
|
67
|
+
**Prompt:**
|
|
68
|
+
|
|
69
|
+
> Do you work with visual references? [Figma / .canvas file / neither / skip]
|
|
70
|
+
|
|
71
|
+
**Default when `--skip-interview`:** `skip`.
|
|
72
|
+
|
|
73
|
+
**Validation:** enum; any other → `skip`.
|
|
74
|
+
|
|
75
|
+
**Effect:** result steers the `Visual Proof Readiness` section only — never gates the happy path or the findings engine. The writer uses this to decide whether to surface `/gdd:connections figma` or `.canvas` guidance.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Do Not
|
|
80
|
+
|
|
81
|
+
- Do not ask for email, org name, team size, or any identity data.
|
|
82
|
+
- Do not ask more than five questions.
|
|
83
|
+
- Do not branch based on Q1 content (adaptive branching is explicitly deferred per the ROADMAP).
|
|
84
|
+
- Do not write STATE.md, config.json, or any source file as a side effect of the interview.
|
package/scripts/build-intel.cjs
CHANGED
|
@@ -435,6 +435,26 @@ async function main() {
|
|
|
435
435
|
|
|
436
436
|
console.log(` discovered ${allFiles.length} files, ${changed.length} changed`);
|
|
437
437
|
|
|
438
|
+
// Phase 14.5: validate reference registry round-trip whenever any reference/*
|
|
439
|
+
// changed (or on --force). Fail the build on dangling/missing/duplicate.
|
|
440
|
+
const referenceChanged = FORCE || changed.some(f => f.startsWith('reference/'));
|
|
441
|
+
if (referenceChanged) {
|
|
442
|
+
try {
|
|
443
|
+
const { validateRegistry } = require(path.join(__dirname, 'lib', 'reference-registry.cjs'));
|
|
444
|
+
const v = validateRegistry({ cwd: ROOT });
|
|
445
|
+
if (!v.ok) {
|
|
446
|
+
console.error('build-intel: reference registry validation failed:');
|
|
447
|
+
if (v.missingInRegistry.length) console.error(' missing in registry:', v.missingInRegistry);
|
|
448
|
+
if (v.danglingInRegistry.length) console.error(' dangling entries:', v.danglingInRegistry);
|
|
449
|
+
if (v.duplicates.length) console.error(' duplicate entries:', v.duplicates);
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
console.log(' reference registry: ok');
|
|
453
|
+
} catch (err) {
|
|
454
|
+
if (err && err.code !== 'MODULE_NOT_FOUND') throw err;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
438
458
|
// Always rebuild files slice when any file changed
|
|
439
459
|
const filesSlice = buildFilesSlice(allFiles);
|
|
440
460
|
writeSlice('files.json', filesSlice);
|
|
@@ -3,15 +3,56 @@
|
|
|
3
3
|
// hooks/gdd-read-injection-scanner.js (runtime hook) and
|
|
4
4
|
// scripts/run-injection-scanner-ci.cjs (CI scanner).
|
|
5
5
|
// Add new patterns here; both consumers pick them up automatically.
|
|
6
|
+
//
|
|
7
|
+
// Phase 14.5 adds three new families: invisible-Unicode obfuscation,
|
|
8
|
+
// HTML-comment instruction hijacks, and secret-exfil trigger patterns.
|
|
9
|
+
|
|
10
|
+
// Zero-width + word-joiner + BOM + bidi overrides. Used for detection
|
|
11
|
+
// AND as a normalization stripper for hooks that run scan after NFKC.
|
|
12
|
+
const _CONTEXT_INVISIBLE_CHARS = /[\u200B-\u200D\u2060\uFEFF\u202A-\u202E]/;
|
|
6
13
|
|
|
7
14
|
const INJECTION_PATTERNS = [
|
|
15
|
+
// ── classic prompt-injection verbs ──────────────────────────────────
|
|
8
16
|
{ name: 'ignore previous', re: /ignore\s+(all\s+)?(previous|prior|above)\s+instructions?/i },
|
|
9
17
|
{ name: 'disregard previous', re: /disregard\s+(all\s+)?(previous|prior|above)\s+instructions?/i },
|
|
18
|
+
{ name: 'forget previous', re: /forget\s+(the\s+|all\s+)?(previous|prior|above)/i },
|
|
10
19
|
{ name: 'you are now a different', re: /you\s+are\s+now\s+a\s+different/i },
|
|
11
20
|
{ name: 'system: you are', re: /system\s*:\s*you\s+are/i },
|
|
12
21
|
{ name: 'role tag injection', re: /<\s*\/?\s*(system|assistant|human)\s*>/i },
|
|
13
22
|
{ name: '[INST] fragment', re: /\[INST\]/i },
|
|
14
23
|
{ name: '### instruction fragment',re: /###\s*instruction/i },
|
|
24
|
+
|
|
25
|
+
// ── invisible-Unicode obfuscation (14.5 new family) ─────────────────
|
|
26
|
+
{ name: 'invisible-unicode chars', re: _CONTEXT_INVISIBLE_CHARS },
|
|
27
|
+
{ name: 'bidi-override instruction', re: /[\u202A-\u202E][^\n]*(ignore|disregard|forget|system\s*:)/i },
|
|
28
|
+
|
|
29
|
+
// ── HTML-comment / hidden-element instruction hijack (14.5 new) ─────
|
|
30
|
+
{ name: 'html-comment system', re: /<!--\s*system\s*:/i },
|
|
31
|
+
{ name: 'html-comment assistant', re: /<!--\s*assistant\s*:/i },
|
|
32
|
+
{ name: 'html-comment ignore', re: /<!--\s*(ignore|disregard|forget)\b/i },
|
|
33
|
+
{ name: 'hidden div system', re: /<div\s+[^>]*style\s*=\s*["'][^"']*display\s*:\s*none[^"']*["'][^>]*>\s*(system|ignore|disregard)/i },
|
|
34
|
+
{ name: 'hidden span system', re: /<span\s+[^>]*style\s*=\s*["'][^"']*visibility\s*:\s*hidden[^"']*["'][^>]*>\s*(system|ignore|disregard)/i },
|
|
35
|
+
{ name: 'zero-font-size trick', re: /style\s*=\s*["'][^"']*font-size\s*:\s*0[^"']*["'][^>]*>\s*(ignore|system|disregard)/i },
|
|
36
|
+
|
|
37
|
+
// ── secret-exfil trigger patterns (14.5 new) ─────────────────────────
|
|
38
|
+
{ name: 'curl-with-api-key-env', re: /curl\s+[^|\n]*\$\{?[A-Z][A-Z0-9_]*_(KEY|TOKEN|SECRET|PASSWORD|AUTH)\}?/ },
|
|
39
|
+
{ name: 'cat-dotenv', re: /\bcat\s+\.env(\.[a-z]+)?\b/ },
|
|
40
|
+
{ name: 'printenv-leak', re: /\bprintenv\b[^\n]{0,80}\|\s*(curl|wget|nc|ssh)/ },
|
|
41
|
+
{ name: 'tar-home-netcat', re: /\btar\s+c[fzvj]+\s+-\s+~[^\n]*\|\s*(nc|ssh|curl)/ },
|
|
42
|
+
{ name: 'env-dot-leak', re: /process\.env\.[A-Z][A-Z0-9_]*_(KEY|TOKEN|SECRET)\s*[^;,\n]*(fetch|axios|XMLHttpRequest|http\.request)/ },
|
|
43
|
+
{ name: 'ssh-key-cat', re: /\bcat\s+~?\/?\.ssh\/id_(rsa|ed25519|ecdsa|dsa)\b/ },
|
|
15
44
|
];
|
|
16
45
|
|
|
17
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Apply patterns to content and return matched pattern names (deduped).
|
|
48
|
+
*/
|
|
49
|
+
function scan(content) {
|
|
50
|
+
if (typeof content !== 'string' || !content) return [];
|
|
51
|
+
const hits = [];
|
|
52
|
+
for (const { name, re } of INJECTION_PATTERNS) {
|
|
53
|
+
if (re.test(content)) hits.push(name);
|
|
54
|
+
}
|
|
55
|
+
return hits;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = { INJECTION_PATTERNS, _CONTEXT_INVISIBLE_CHARS, scan };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* scripts/lib/blast-radius.cjs — per-task blast-radius preflight.
|
|
4
|
+
*
|
|
5
|
+
* Exports:
|
|
6
|
+
* estimate({touchedPaths, diffStats, config?}) →
|
|
7
|
+
* { files, lines, exceeds, overBy, limit: {files, lines} }
|
|
8
|
+
* estimateMCPCalls({toolCalls, config?}) →
|
|
9
|
+
* { count, exceeds, overBy, limit }
|
|
10
|
+
* formatDiffSummary({touchedPaths, diffStats, result}) → string
|
|
11
|
+
* loadConfig(cwd?) → { max_files_per_task, max_lines_per_task, max_mcp_calls_per_task }
|
|
12
|
+
*
|
|
13
|
+
* Config precedence: .design/config.json.blast_radius.{max_files_per_task, max_lines_per_task, max_mcp_calls_per_task}
|
|
14
|
+
* then .design/config.json top-level same keys, then built-in defaults.
|
|
15
|
+
*
|
|
16
|
+
* Zero-value limits DISABLE that dimension.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const fs = require('fs');
|
|
20
|
+
const path = require('path');
|
|
21
|
+
|
|
22
|
+
const DEFAULTS = {
|
|
23
|
+
max_files_per_task: 10,
|
|
24
|
+
max_lines_per_task: 400,
|
|
25
|
+
max_mcp_calls_per_task: 30,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function loadConfig(cwd) {
|
|
29
|
+
const configPath = path.join(cwd || process.cwd(), '.design', 'config.json');
|
|
30
|
+
let cfg = {};
|
|
31
|
+
try { cfg = JSON.parse(fs.readFileSync(configPath, 'utf8')); } catch { cfg = {}; }
|
|
32
|
+
const br = (cfg && typeof cfg === 'object' && cfg.blast_radius) || {};
|
|
33
|
+
return {
|
|
34
|
+
max_files_per_task: numberOr(br.max_files_per_task, cfg.max_files_per_task, DEFAULTS.max_files_per_task),
|
|
35
|
+
max_lines_per_task: numberOr(br.max_lines_per_task, cfg.max_lines_per_task, DEFAULTS.max_lines_per_task),
|
|
36
|
+
max_mcp_calls_per_task: numberOr(br.max_mcp_calls_per_task, cfg.max_mcp_calls_per_task, DEFAULTS.max_mcp_calls_per_task),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function numberOr(...candidates) {
|
|
41
|
+
for (const v of candidates) {
|
|
42
|
+
if (typeof v === 'number' && Number.isFinite(v) && v >= 0) return v;
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function estimate({ touchedPaths = [], diffStats = {}, config } = {}) {
|
|
48
|
+
const cfg = config || loadConfig();
|
|
49
|
+
const files = new Set((touchedPaths || []).filter(Boolean)).size;
|
|
50
|
+
const lines = (diffStats.insertions || 0) + (diffStats.deletions || 0);
|
|
51
|
+
const fileLimit = cfg.max_files_per_task;
|
|
52
|
+
const lineLimit = cfg.max_lines_per_task;
|
|
53
|
+
const fileExceeds = fileLimit > 0 && files > fileLimit;
|
|
54
|
+
const lineExceeds = lineLimit > 0 && lines > lineLimit;
|
|
55
|
+
return {
|
|
56
|
+
files,
|
|
57
|
+
lines,
|
|
58
|
+
exceeds: fileExceeds || lineExceeds,
|
|
59
|
+
overBy: {
|
|
60
|
+
files: fileExceeds ? files - fileLimit : 0,
|
|
61
|
+
lines: lineExceeds ? lines - lineLimit : 0,
|
|
62
|
+
},
|
|
63
|
+
limit: { files: fileLimit, lines: lineLimit },
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function estimateMCPCalls({ toolCalls = [], config } = {}) {
|
|
68
|
+
const cfg = config || loadConfig();
|
|
69
|
+
const count = Array.isArray(toolCalls) ? toolCalls.length : 0;
|
|
70
|
+
const limit = cfg.max_mcp_calls_per_task;
|
|
71
|
+
const exceeds = limit > 0 && count > limit;
|
|
72
|
+
return {
|
|
73
|
+
count,
|
|
74
|
+
exceeds,
|
|
75
|
+
overBy: exceeds ? count - limit : 0,
|
|
76
|
+
limit,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function formatDiffSummary({ touchedPaths = [], diffStats = {}, result }) {
|
|
81
|
+
const r = result || estimate({ touchedPaths, diffStats });
|
|
82
|
+
const lines = [];
|
|
83
|
+
lines.push('## Blast-Radius Preflight — Over Limit');
|
|
84
|
+
lines.push('');
|
|
85
|
+
lines.push(`Files touched: ${r.files} (limit ${r.limit.files || 'disabled'})`);
|
|
86
|
+
lines.push(`Lines changed: ${r.lines} (limit ${r.limit.lines || 'disabled'})`);
|
|
87
|
+
if (r.overBy.files) lines.push(`Over by: +${r.overBy.files} files`);
|
|
88
|
+
if (r.overBy.lines) lines.push(`Over by: +${r.overBy.lines} lines`);
|
|
89
|
+
lines.push('');
|
|
90
|
+
lines.push('Touched paths:');
|
|
91
|
+
for (const p of touchedPaths) lines.push(` - ${p}`);
|
|
92
|
+
lines.push('');
|
|
93
|
+
lines.push('To proceed: split the task into ≤-limit chunks, or raise the ceiling in `.design/config.json.blast_radius.{max_files_per_task,max_lines_per_task}`.');
|
|
94
|
+
return lines.join('\n');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { estimate, estimateMCPCalls, formatDiffSummary, loadConfig, DEFAULTS };
|