@minhduydev/mdpi 0.3.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.
- package/README.md +31 -0
- package/dist/index.js +1187 -0
- package/dist/template/.pi/.env.example +28 -0
- package/dist/template/.pi/AGENTS.md +226 -0
- package/dist/template/.pi/QUALITY.md +13 -0
- package/dist/template/.pi/README.md +452 -0
- package/dist/template/.pi/VERSION +1 -0
- package/dist/template/.pi/agents/INDEX.md +54 -0
- package/dist/template/.pi/agents/build.md +194 -0
- package/dist/template/.pi/agents/explore.md +85 -0
- package/dist/template/.pi/agents/general.md +189 -0
- package/dist/template/.pi/agents/plan.md +407 -0
- package/dist/template/.pi/agents/review.md +198 -0
- package/dist/template/.pi/agents/scout.md +142 -0
- package/dist/template/.pi/agents/vision.md +157 -0
- package/dist/template/.pi/artifacts/example/plan.md +12 -0
- package/dist/template/.pi/artifacts/example/progress.md +4 -0
- package/dist/template/.pi/artifacts/example/research.md +4 -0
- package/dist/template/.pi/artifacts/example/spec.md +16 -0
- package/dist/template/.pi/context/architecture.md +141 -0
- package/dist/template/.pi/context/fallow.md +137 -0
- package/dist/template/.pi/extensions/templates-injector.ts +76 -0
- package/dist/template/.pi/extensions/workflows-runner.ts +301 -0
- package/dist/template/.pi/guard.example.json +75 -0
- package/dist/template/.pi/prompts/INDEX.md +78 -0
- package/dist/template/.pi/prompts/audit.md +109 -0
- package/dist/template/.pi/prompts/close.md +88 -0
- package/dist/template/.pi/prompts/create.md +197 -0
- package/dist/template/.pi/prompts/fix.md +117 -0
- package/dist/template/.pi/prompts/gc.md +112 -0
- package/dist/template/.pi/prompts/init.md +206 -0
- package/dist/template/.pi/prompts/loop-check.md +87 -0
- package/dist/template/.pi/prompts/loop-init.md +157 -0
- package/dist/template/.pi/prompts/loop-review.md +90 -0
- package/dist/template/.pi/prompts/plan.md +254 -0
- package/dist/template/.pi/prompts/research.md +136 -0
- package/dist/template/.pi/prompts/ship.md +219 -0
- package/dist/template/.pi/prompts/status.md +92 -0
- package/dist/template/.pi/prompts/verify.md +154 -0
- package/dist/template/.pi/scripts/gc-check.sh +86 -0
- package/dist/template/.pi/settings.json +15 -0
- package/dist/template/.pi/skills/INDEX.md +331 -0
- package/dist/template/.pi/skills/accessibility-audit/SKILL.md +205 -0
- package/dist/template/.pi/skills/accessibility-audit/references/accessibility-checklist.md +109 -0
- package/dist/template/.pi/skills/agent-code-quality-gate/SKILL.md +131 -0
- package/dist/template/.pi/skills/api-and-interface-design/SKILL.md +159 -0
- package/dist/template/.pi/skills/behavioral-kernel/SKILL.md +92 -0
- package/dist/template/.pi/skills/brainstorming/SKILL.md +138 -0
- package/dist/template/.pi/skills/browser-testing-with-devtools/SKILL.md +90 -0
- package/dist/template/.pi/skills/chrome-devtools/SKILL.md +129 -0
- package/dist/template/.pi/skills/ci-cd-and-automation/SKILL.md +199 -0
- package/dist/template/.pi/skills/cloudflare/SKILL.md +271 -0
- package/dist/template/.pi/skills/cloudflare/references/agents-sdk/README.md +35 -0
- package/dist/template/.pi/skills/cloudflare/references/agents-sdk/api.md +100 -0
- package/dist/template/.pi/skills/cloudflare/references/agents-sdk/configuration.md +99 -0
- package/dist/template/.pi/skills/cloudflare/references/agents-sdk/gotchas.md +59 -0
- package/dist/template/.pi/skills/cloudflare/references/agents-sdk/patterns.md +89 -0
- package/dist/template/.pi/skills/cloudflare/references/ai-gateway/README.md +695 -0
- package/dist/template/.pi/skills/cloudflare/references/ai-search/README.md +14 -0
- package/dist/template/.pi/skills/cloudflare/references/ai-search/api.md +38 -0
- package/dist/template/.pi/skills/cloudflare/references/ai-search/configuration.md +52 -0
- package/dist/template/.pi/skills/cloudflare/references/ai-search/gotchas.md +41 -0
- package/dist/template/.pi/skills/cloudflare/references/ai-search/patterns.md +45 -0
- package/dist/template/.pi/skills/cloudflare/references/analytics-engine/README.md +14 -0
- package/dist/template/.pi/skills/cloudflare/references/analytics-engine/api.md +27 -0
- package/dist/template/.pi/skills/cloudflare/references/analytics-engine/configuration.md +45 -0
- package/dist/template/.pi/skills/cloudflare/references/analytics-engine/gotchas.md +3 -0
- package/dist/template/.pi/skills/cloudflare/references/analytics-engine/patterns.md +36 -0
- package/dist/template/.pi/skills/cloudflare/references/api/README.md +21 -0
- package/dist/template/.pi/skills/cloudflare/references/api/api.md +31 -0
- package/dist/template/.pi/skills/cloudflare/references/api/configuration.md +20 -0
- package/dist/template/.pi/skills/cloudflare/references/api/gotchas.md +28 -0
- package/dist/template/.pi/skills/cloudflare/references/api/patterns.md +47 -0
- package/dist/template/.pi/skills/cloudflare/references/api-shield/README.md +20 -0
- package/dist/template/.pi/skills/cloudflare/references/api-shield/api.md +78 -0
- package/dist/template/.pi/skills/cloudflare/references/api-shield/configuration.md +128 -0
- package/dist/template/.pi/skills/cloudflare/references/api-shield/gotchas.md +51 -0
- package/dist/template/.pi/skills/cloudflare/references/api-shield/patterns.md +145 -0
- package/dist/template/.pi/skills/cloudflare/references/argo-smart-routing/README.md +16 -0
- package/dist/template/.pi/skills/cloudflare/references/argo-smart-routing/api.md +50 -0
- package/dist/template/.pi/skills/cloudflare/references/argo-smart-routing/configuration.md +53 -0
- package/dist/template/.pi/skills/cloudflare/references/argo-smart-routing/gotchas.md +16 -0
- package/dist/template/.pi/skills/cloudflare/references/argo-smart-routing/patterns.md +45 -0
- package/dist/template/.pi/skills/cloudflare/references/bindings/README.md +14 -0
- package/dist/template/.pi/skills/cloudflare/references/bindings/api.md +3 -0
- package/dist/template/.pi/skills/cloudflare/references/bindings/configuration.md +58 -0
- package/dist/template/.pi/skills/cloudflare/references/bindings/gotchas.md +35 -0
- package/dist/template/.pi/skills/cloudflare/references/bindings/patterns.md +37 -0
- package/dist/template/.pi/skills/cloudflare/references/bot-management/README.md +71 -0
- package/dist/template/.pi/skills/cloudflare/references/bot-management/api.md +168 -0
- package/dist/template/.pi/skills/cloudflare/references/bot-management/configuration.md +114 -0
- package/dist/template/.pi/skills/cloudflare/references/bot-management/gotchas.md +99 -0
- package/dist/template/.pi/skills/cloudflare/references/bot-management/patterns.md +125 -0
- package/dist/template/.pi/skills/cloudflare/references/browser-rendering/README.md +16 -0
- package/dist/template/.pi/skills/cloudflare/references/browser-rendering/api.md +54 -0
- package/dist/template/.pi/skills/cloudflare/references/browser-rendering/configuration.md +47 -0
- package/dist/template/.pi/skills/cloudflare/references/browser-rendering/gotchas.md +29 -0
- package/dist/template/.pi/skills/cloudflare/references/browser-rendering/patterns.md +29 -0
- package/dist/template/.pi/skills/cloudflare/references/c3/README.md +264 -0
- package/dist/template/.pi/skills/cloudflare/references/cache-reserve/README.md +93 -0
- package/dist/template/.pi/skills/cloudflare/references/cache-reserve/api.md +176 -0
- package/dist/template/.pi/skills/cloudflare/references/cache-reserve/configuration.md +164 -0
- package/dist/template/.pi/skills/cloudflare/references/cache-reserve/gotchas.md +203 -0
- package/dist/template/.pi/skills/cloudflare/references/cache-reserve/patterns.md +180 -0
- package/dist/template/.pi/skills/cloudflare/references/containers/README.md +16 -0
- package/dist/template/.pi/skills/cloudflare/references/containers/api.md +43 -0
- package/dist/template/.pi/skills/cloudflare/references/containers/configuration.md +56 -0
- package/dist/template/.pi/skills/cloudflare/references/containers/gotchas.md +21 -0
- package/dist/template/.pi/skills/cloudflare/references/containers/patterns.md +40 -0
- package/dist/template/.pi/skills/cloudflare/references/cron-triggers/README.md +85 -0
- package/dist/template/.pi/skills/cloudflare/references/cron-triggers/api.md +198 -0
- package/dist/template/.pi/skills/cloudflare/references/cron-triggers/configuration.md +151 -0
- package/dist/template/.pi/skills/cloudflare/references/cron-triggers/gotchas.md +129 -0
- package/dist/template/.pi/skills/cloudflare/references/cron-triggers/patterns.md +122 -0
- package/dist/template/.pi/skills/cloudflare/references/d1/README.md +92 -0
- package/dist/template/.pi/skills/cloudflare/references/d1/api.md +141 -0
- package/dist/template/.pi/skills/cloudflare/references/d1/configuration.md +127 -0
- package/dist/template/.pi/skills/cloudflare/references/d1/gotchas.md +70 -0
- package/dist/template/.pi/skills/cloudflare/references/d1/patterns.md +144 -0
- package/dist/template/.pi/skills/cloudflare/references/ddos/README.md +34 -0
- package/dist/template/.pi/skills/cloudflare/references/ddos/api.md +136 -0
- package/dist/template/.pi/skills/cloudflare/references/ddos/configuration.md +67 -0
- package/dist/template/.pi/skills/cloudflare/references/ddos/gotchas.md +114 -0
- package/dist/template/.pi/skills/cloudflare/references/ddos/patterns.md +158 -0
- package/dist/template/.pi/skills/cloudflare/references/do-storage/README.md +62 -0
- package/dist/template/.pi/skills/cloudflare/references/do-storage/api.md +89 -0
- package/dist/template/.pi/skills/cloudflare/references/do-storage/configuration.md +116 -0
- package/dist/template/.pi/skills/cloudflare/references/do-storage/gotchas.md +93 -0
- package/dist/template/.pi/skills/cloudflare/references/do-storage/patterns.md +112 -0
- package/dist/template/.pi/skills/cloudflare/references/durable-objects/README.md +125 -0
- package/dist/template/.pi/skills/cloudflare/references/durable-objects/api.md +152 -0
- package/dist/template/.pi/skills/cloudflare/references/durable-objects/configuration.md +148 -0
- package/dist/template/.pi/skills/cloudflare/references/durable-objects/gotchas.md +158 -0
- package/dist/template/.pi/skills/cloudflare/references/durable-objects/patterns.md +255 -0
- package/dist/template/.pi/skills/cloudflare/references/email-routing/README.md +18 -0
- package/dist/template/.pi/skills/cloudflare/references/email-routing/api.md +46 -0
- package/dist/template/.pi/skills/cloudflare/references/email-routing/configuration.md +63 -0
- package/dist/template/.pi/skills/cloudflare/references/email-routing/gotchas.md +16 -0
- package/dist/template/.pi/skills/cloudflare/references/email-routing/patterns.md +46 -0
- package/dist/template/.pi/skills/cloudflare/references/email-workers/README.md +598 -0
- package/dist/template/.pi/skills/cloudflare/references/hyperdrive/README.md +62 -0
- package/dist/template/.pi/skills/cloudflare/references/hyperdrive/api.md +137 -0
- package/dist/template/.pi/skills/cloudflare/references/hyperdrive/configuration.md +133 -0
- package/dist/template/.pi/skills/cloudflare/references/hyperdrive/gotchas.md +184 -0
- package/dist/template/.pi/skills/cloudflare/references/hyperdrive/patterns.md +176 -0
- package/dist/template/.pi/skills/cloudflare/references/images/README.md +14 -0
- package/dist/template/.pi/skills/cloudflare/references/images/api.md +3 -0
- package/dist/template/.pi/skills/cloudflare/references/images/configuration.md +45 -0
- package/dist/template/.pi/skills/cloudflare/references/images/gotchas.md +23 -0
- package/dist/template/.pi/skills/cloudflare/references/images/patterns.md +31 -0
- package/dist/template/.pi/skills/cloudflare/references/kv/README.md +60 -0
- package/dist/template/.pi/skills/cloudflare/references/kv/api.md +114 -0
- package/dist/template/.pi/skills/cloudflare/references/kv/configuration.md +92 -0
- package/dist/template/.pi/skills/cloudflare/references/kv/gotchas.md +117 -0
- package/dist/template/.pi/skills/cloudflare/references/kv/patterns.md +139 -0
- package/dist/template/.pi/skills/cloudflare/references/miniflare/README.md +64 -0
- package/dist/template/.pi/skills/cloudflare/references/miniflare/api.md +144 -0
- package/dist/template/.pi/skills/cloudflare/references/miniflare/configuration.md +203 -0
- package/dist/template/.pi/skills/cloudflare/references/miniflare/gotchas.md +187 -0
- package/dist/template/.pi/skills/cloudflare/references/miniflare/patterns.md +211 -0
- package/dist/template/.pi/skills/cloudflare/references/network-interconnect/README.md +60 -0
- package/dist/template/.pi/skills/cloudflare/references/network-interconnect/api.md +240 -0
- package/dist/template/.pi/skills/cloudflare/references/network-interconnect/configuration.md +127 -0
- package/dist/template/.pi/skills/cloudflare/references/network-interconnect/gotchas.md +171 -0
- package/dist/template/.pi/skills/cloudflare/references/network-interconnect/patterns.md +171 -0
- package/dist/template/.pi/skills/cloudflare/references/observability/README.md +18 -0
- package/dist/template/.pi/skills/cloudflare/references/observability/api.md +51 -0
- package/dist/template/.pi/skills/cloudflare/references/observability/configuration.md +60 -0
- package/dist/template/.pi/skills/cloudflare/references/observability/gotchas.md +36 -0
- package/dist/template/.pi/skills/cloudflare/references/observability/patterns.md +42 -0
- package/dist/template/.pi/skills/cloudflare/references/pages/README.md +76 -0
- package/dist/template/.pi/skills/cloudflare/references/pages/api.md +200 -0
- package/dist/template/.pi/skills/cloudflare/references/pages/configuration.md +228 -0
- package/dist/template/.pi/skills/cloudflare/references/pages/gotchas.md +161 -0
- package/dist/template/.pi/skills/cloudflare/references/pages/patterns.md +145 -0
- package/dist/template/.pi/skills/cloudflare/references/pages-functions/README.md +57 -0
- package/dist/template/.pi/skills/cloudflare/references/pages-functions/api.md +201 -0
- package/dist/template/.pi/skills/cloudflare/references/pages-functions/configuration.md +159 -0
- package/dist/template/.pi/skills/cloudflare/references/pages-functions/gotchas.md +151 -0
- package/dist/template/.pi/skills/cloudflare/references/pages-functions/patterns.md +190 -0
- package/dist/template/.pi/skills/cloudflare/references/pipelines/README.md +664 -0
- package/dist/template/.pi/skills/cloudflare/references/pulumi/README.md +107 -0
- package/dist/template/.pi/skills/cloudflare/references/pulumi/api.md +194 -0
- package/dist/template/.pi/skills/cloudflare/references/pulumi/configuration.md +216 -0
- package/dist/template/.pi/skills/cloudflare/references/pulumi/gotchas.md +223 -0
- package/dist/template/.pi/skills/cloudflare/references/pulumi/patterns.md +139 -0
- package/dist/template/.pi/skills/cloudflare/references/queues/README.md +69 -0
- package/dist/template/.pi/skills/cloudflare/references/queues/api.md +138 -0
- package/dist/template/.pi/skills/cloudflare/references/queues/configuration.md +125 -0
- package/dist/template/.pi/skills/cloudflare/references/queues/gotchas.md +112 -0
- package/dist/template/.pi/skills/cloudflare/references/queues/patterns.md +155 -0
- package/dist/template/.pi/skills/cloudflare/references/r2/README.md +61 -0
- package/dist/template/.pi/skills/cloudflare/references/r2/api.md +127 -0
- package/dist/template/.pi/skills/cloudflare/references/r2/configuration.md +76 -0
- package/dist/template/.pi/skills/cloudflare/references/r2/gotchas.md +94 -0
- package/dist/template/.pi/skills/cloudflare/references/r2/patterns.md +127 -0
- package/dist/template/.pi/skills/cloudflare/references/r2-data-catalog/README.md +18 -0
- package/dist/template/.pi/skills/cloudflare/references/r2-data-catalog/api.md +29 -0
- package/dist/template/.pi/skills/cloudflare/references/r2-data-catalog/configuration.md +39 -0
- package/dist/template/.pi/skills/cloudflare/references/r2-data-catalog/gotchas.md +20 -0
- package/dist/template/.pi/skills/cloudflare/references/r2-data-catalog/patterns.md +46 -0
- package/dist/template/.pi/skills/cloudflare/references/r2-sql/README.md +512 -0
- package/dist/template/.pi/skills/cloudflare/references/realtime-sfu/README.md +21 -0
- package/dist/template/.pi/skills/cloudflare/references/realtime-sfu/api.md +135 -0
- package/dist/template/.pi/skills/cloudflare/references/realtime-sfu/configuration.md +63 -0
- package/dist/template/.pi/skills/cloudflare/references/realtime-sfu/gotchas.md +75 -0
- package/dist/template/.pi/skills/cloudflare/references/realtime-sfu/patterns.md +102 -0
- package/dist/template/.pi/skills/cloudflare/references/realtimekit/README.md +81 -0
- package/dist/template/.pi/skills/cloudflare/references/realtimekit/api.md +164 -0
- package/dist/template/.pi/skills/cloudflare/references/realtimekit/configuration.md +147 -0
- package/dist/template/.pi/skills/cloudflare/references/realtimekit/gotchas.md +172 -0
- package/dist/template/.pi/skills/cloudflare/references/realtimekit/patterns.md +155 -0
- package/dist/template/.pi/skills/cloudflare/references/sandbox/README.md +90 -0
- package/dist/template/.pi/skills/cloudflare/references/sandbox/api.md +178 -0
- package/dist/template/.pi/skills/cloudflare/references/sandbox/configuration.md +131 -0
- package/dist/template/.pi/skills/cloudflare/references/sandbox/gotchas.md +156 -0
- package/dist/template/.pi/skills/cloudflare/references/sandbox/patterns.md +203 -0
- package/dist/template/.pi/skills/cloudflare/references/secrets-store/README.md +58 -0
- package/dist/template/.pi/skills/cloudflare/references/secrets-store/api.md +182 -0
- package/dist/template/.pi/skills/cloudflare/references/secrets-store/configuration.md +140 -0
- package/dist/template/.pi/skills/cloudflare/references/secrets-store/gotchas.md +129 -0
- package/dist/template/.pi/skills/cloudflare/references/secrets-store/patterns.md +218 -0
- package/dist/template/.pi/skills/cloudflare/references/smart-placement/README.md +91 -0
- package/dist/template/.pi/skills/cloudflare/references/smart-placement/api.md +139 -0
- package/dist/template/.pi/skills/cloudflare/references/smart-placement/configuration.md +129 -0
- package/dist/template/.pi/skills/cloudflare/references/smart-placement/gotchas.md +87 -0
- package/dist/template/.pi/skills/cloudflare/references/smart-placement/patterns.md +135 -0
- package/dist/template/.pi/skills/cloudflare/references/snippets/README.md +15 -0
- package/dist/template/.pi/skills/cloudflare/references/snippets/api.md +47 -0
- package/dist/template/.pi/skills/cloudflare/references/snippets/configuration.md +33 -0
- package/dist/template/.pi/skills/cloudflare/references/snippets/gotchas.md +21 -0
- package/dist/template/.pi/skills/cloudflare/references/snippets/patterns.md +34 -0
- package/dist/template/.pi/skills/cloudflare/references/spectrum/README.md +16 -0
- package/dist/template/.pi/skills/cloudflare/references/spectrum/api.md +24 -0
- package/dist/template/.pi/skills/cloudflare/references/spectrum/configuration.md +43 -0
- package/dist/template/.pi/skills/cloudflare/references/spectrum/gotchas.md +42 -0
- package/dist/template/.pi/skills/cloudflare/references/spectrum/patterns.md +40 -0
- package/dist/template/.pi/skills/cloudflare/references/static-assets/README.md +14 -0
- package/dist/template/.pi/skills/cloudflare/references/static-assets/api.md +3 -0
- package/dist/template/.pi/skills/cloudflare/references/static-assets/configuration.md +47 -0
- package/dist/template/.pi/skills/cloudflare/references/static-assets/gotchas.md +44 -0
- package/dist/template/.pi/skills/cloudflare/references/static-assets/patterns.md +42 -0
- package/dist/template/.pi/skills/cloudflare/references/stream/README.md +103 -0
- package/dist/template/.pi/skills/cloudflare/references/stream/api.md +204 -0
- package/dist/template/.pi/skills/cloudflare/references/stream/configuration.md +127 -0
- package/dist/template/.pi/skills/cloudflare/references/stream/gotchas.md +131 -0
- package/dist/template/.pi/skills/cloudflare/references/stream/patterns.md +152 -0
- package/dist/template/.pi/skills/cloudflare/references/tail-workers/README.md +640 -0
- package/dist/template/.pi/skills/cloudflare/references/terraform/README.md +76 -0
- package/dist/template/.pi/skills/cloudflare/references/terraform/api.md +159 -0
- package/dist/template/.pi/skills/cloudflare/references/terraform/configuration.md +156 -0
- package/dist/template/.pi/skills/cloudflare/references/terraform/gotchas.md +207 -0
- package/dist/template/.pi/skills/cloudflare/references/terraform/patterns.md +135 -0
- package/dist/template/.pi/skills/cloudflare/references/tunnel/README.md +82 -0
- package/dist/template/.pi/skills/cloudflare/references/tunnel/api.md +105 -0
- package/dist/template/.pi/skills/cloudflare/references/tunnel/configuration.md +113 -0
- package/dist/template/.pi/skills/cloudflare/references/tunnel/gotchas.md +115 -0
- package/dist/template/.pi/skills/cloudflare/references/tunnel/patterns.md +157 -0
- package/dist/template/.pi/skills/cloudflare/references/turn/README.md +699 -0
- package/dist/template/.pi/skills/cloudflare/references/turnstile/README.md +14 -0
- package/dist/template/.pi/skills/cloudflare/references/turnstile/api.md +3 -0
- package/dist/template/.pi/skills/cloudflare/references/turnstile/configuration.md +19 -0
- package/dist/template/.pi/skills/cloudflare/references/turnstile/gotchas.md +27 -0
- package/dist/template/.pi/skills/cloudflare/references/turnstile/patterns.md +41 -0
- package/dist/template/.pi/skills/cloudflare/references/vectorize/README.md +682 -0
- package/dist/template/.pi/skills/cloudflare/references/waf/README.md +14 -0
- package/dist/template/.pi/skills/cloudflare/references/waf/api.md +3 -0
- package/dist/template/.pi/skills/cloudflare/references/waf/configuration.md +44 -0
- package/dist/template/.pi/skills/cloudflare/references/waf/gotchas.md +24 -0
- package/dist/template/.pi/skills/cloudflare/references/waf/patterns.md +29 -0
- package/dist/template/.pi/skills/cloudflare/references/web-analytics/README.md +19 -0
- package/dist/template/.pi/skills/cloudflare/references/web-analytics/api.md +52 -0
- package/dist/template/.pi/skills/cloudflare/references/web-analytics/configuration.md +31 -0
- package/dist/template/.pi/skills/cloudflare/references/web-analytics/gotchas.md +28 -0
- package/dist/template/.pi/skills/cloudflare/references/web-analytics/patterns.md +52 -0
- package/dist/template/.pi/skills/cloudflare/references/workerd/README.md +47 -0
- package/dist/template/.pi/skills/cloudflare/references/workerd/api.md +199 -0
- package/dist/template/.pi/skills/cloudflare/references/workerd/configuration.md +185 -0
- package/dist/template/.pi/skills/cloudflare/references/workerd/gotchas.md +203 -0
- package/dist/template/.pi/skills/cloudflare/references/workerd/patterns.md +216 -0
- package/dist/template/.pi/skills/cloudflare/references/workers/README.md +96 -0
- package/dist/template/.pi/skills/cloudflare/references/workers/api.md +137 -0
- package/dist/template/.pi/skills/cloudflare/references/workers/configuration.md +147 -0
- package/dist/template/.pi/skills/cloudflare/references/workers/gotchas.md +99 -0
- package/dist/template/.pi/skills/cloudflare/references/workers/patterns.md +149 -0
- package/dist/template/.pi/skills/cloudflare/references/workers-ai/README.md +116 -0
- package/dist/template/.pi/skills/cloudflare/references/workers-for-platforms/README.md +48 -0
- package/dist/template/.pi/skills/cloudflare/references/workers-for-platforms/api.md +169 -0
- package/dist/template/.pi/skills/cloudflare/references/workers-for-platforms/configuration.md +136 -0
- package/dist/template/.pi/skills/cloudflare/references/workers-for-platforms/gotchas.md +130 -0
- package/dist/template/.pi/skills/cloudflare/references/workers-for-platforms/patterns.md +170 -0
- package/dist/template/.pi/skills/cloudflare/references/workers-playground/README.md +16 -0
- package/dist/template/.pi/skills/cloudflare/references/workers-playground/api.md +20 -0
- package/dist/template/.pi/skills/cloudflare/references/workers-playground/configuration.md +3 -0
- package/dist/template/.pi/skills/cloudflare/references/workers-playground/gotchas.md +35 -0
- package/dist/template/.pi/skills/cloudflare/references/workers-playground/patterns.md +42 -0
- package/dist/template/.pi/skills/cloudflare/references/workers-vpc/README.md +579 -0
- package/dist/template/.pi/skills/cloudflare/references/workflows/README.md +62 -0
- package/dist/template/.pi/skills/cloudflare/references/workflows/api.md +125 -0
- package/dist/template/.pi/skills/cloudflare/references/workflows/configuration.md +177 -0
- package/dist/template/.pi/skills/cloudflare/references/workflows/gotchas.md +136 -0
- package/dist/template/.pi/skills/cloudflare/references/workflows/patterns.md +132 -0
- package/dist/template/.pi/skills/cloudflare/references/wrangler/README.md +90 -0
- package/dist/template/.pi/skills/cloudflare/references/wrangler/api.md +140 -0
- package/dist/template/.pi/skills/cloudflare/references/wrangler/configuration.md +128 -0
- package/dist/template/.pi/skills/cloudflare/references/wrangler/gotchas.md +93 -0
- package/dist/template/.pi/skills/cloudflare/references/wrangler/patterns.md +150 -0
- package/dist/template/.pi/skills/cloudflare/references/zaraz/README.md +360 -0
- package/dist/template/.pi/skills/code-cleanup/SKILL.md +232 -0
- package/dist/template/.pi/skills/code-review-and-quality/SKILL.md +344 -0
- package/dist/template/.pi/skills/code-simplification/SKILL.md +348 -0
- package/dist/template/.pi/skills/context-engineering/SKILL.md +296 -0
- package/dist/template/.pi/skills/core-data-expert/SKILL.md +117 -0
- package/dist/template/.pi/skills/core-data-expert/references/batch-operations.md +543 -0
- package/dist/template/.pi/skills/core-data-expert/references/cloudkit-integration.md +259 -0
- package/dist/template/.pi/skills/core-data-expert/references/concurrency.md +522 -0
- package/dist/template/.pi/skills/core-data-expert/references/fetch-requests.md +643 -0
- package/dist/template/.pi/skills/core-data-expert/references/glossary.md +233 -0
- package/dist/template/.pi/skills/core-data-expert/references/migration.md +393 -0
- package/dist/template/.pi/skills/core-data-expert/references/model-configuration.md +597 -0
- package/dist/template/.pi/skills/core-data-expert/references/performance.md +300 -0
- package/dist/template/.pi/skills/core-data-expert/references/persistent-history.md +553 -0
- package/dist/template/.pi/skills/core-data-expert/references/project-audit.md +60 -0
- package/dist/template/.pi/skills/core-data-expert/references/saving.md +574 -0
- package/dist/template/.pi/skills/core-data-expert/references/stack-setup.md +625 -0
- package/dist/template/.pi/skills/core-data-expert/references/testing.md +300 -0
- package/dist/template/.pi/skills/core-data-expert/references/threading.md +589 -0
- package/dist/template/.pi/skills/debugging-and-error-recovery/SKILL.md +274 -0
- package/dist/template/.pi/skills/deep-module-design/SKILL.md +218 -0
- package/dist/template/.pi/skills/defense-in-depth/SKILL.md +161 -0
- package/dist/template/.pi/skills/deprecation-and-migration/SKILL.md +186 -0
- package/dist/template/.pi/skills/design-system-audit/SKILL.md +176 -0
- package/dist/template/.pi/skills/design-taste-frontend/SKILL.md +255 -0
- package/dist/template/.pi/skills/development-lifecycle/SKILL.md +285 -0
- package/dist/template/.pi/skills/documentation-and-adrs/SKILL.md +217 -0
- package/dist/template/.pi/skills/doubt-driven-development/SKILL.md +207 -0
- package/dist/template/.pi/skills/fallow/SKILL.md +457 -0
- package/dist/template/.pi/skills/fallow/references/cli-reference.md +1905 -0
- package/dist/template/.pi/skills/fallow/references/gotchas.md +644 -0
- package/dist/template/.pi/skills/fallow/references/patterns.md +791 -0
- package/dist/template/.pi/skills/figma/SKILL.md +241 -0
- package/dist/template/.pi/skills/frontend-design/SKILL.md +262 -0
- package/dist/template/.pi/skills/frontend-design/references/animation/motion-advanced.md +224 -0
- package/dist/template/.pi/skills/frontend-design/references/animation/motion-core.md +181 -0
- package/dist/template/.pi/skills/frontend-design/references/canvas/execution.md +90 -0
- package/dist/template/.pi/skills/frontend-design/references/canvas/philosophy.md +94 -0
- package/dist/template/.pi/skills/frontend-design/references/design/color-system.md +111 -0
- package/dist/template/.pi/skills/frontend-design/references/design/interaction.md +149 -0
- package/dist/template/.pi/skills/frontend-design/references/design/typography-rules.md +106 -0
- package/dist/template/.pi/skills/frontend-design/references/design/ux-writing.md +99 -0
- package/dist/template/.pi/skills/frontend-design/references/shadcn/accessibility.md +132 -0
- package/dist/template/.pi/skills/frontend-design/references/shadcn/core-components.md +153 -0
- package/dist/template/.pi/skills/frontend-design/references/shadcn/form-components.md +158 -0
- package/dist/template/.pi/skills/frontend-design/references/shadcn/setup.md +69 -0
- package/dist/template/.pi/skills/frontend-design/references/shadcn/theming.md +152 -0
- package/dist/template/.pi/skills/frontend-design/references/tailwind/responsive.md +112 -0
- package/dist/template/.pi/skills/frontend-design/references/tailwind/utilities-layout.md +134 -0
- package/dist/template/.pi/skills/frontend-design/references/tailwind/utilities-styling.md +165 -0
- package/dist/template/.pi/skills/frontend-design/references/tailwind/v4-config.md +147 -0
- package/dist/template/.pi/skills/frontend-design/references/tailwind/v4-features.md +128 -0
- package/dist/template/.pi/skills/frontend-ui-engineering/SKILL.md +217 -0
- package/dist/template/.pi/skills/gemini-large-context/SKILL.md +238 -0
- package/dist/template/.pi/skills/git-workflow-and-versioning/SKILL.md +217 -0
- package/dist/template/.pi/skills/grill-me/SKILL.md +162 -0
- package/dist/template/.pi/skills/high-end-visual-design/SKILL.md +128 -0
- package/dist/template/.pi/skills/idea-refine/SKILL.md +195 -0
- package/dist/template/.pi/skills/idea-refine/examples.md +62 -0
- package/dist/template/.pi/skills/idea-refine/frameworks.md +63 -0
- package/dist/template/.pi/skills/idea-refine/refinement-criteria.md +56 -0
- package/dist/template/.pi/skills/incremental-implementation/SKILL.md +190 -0
- package/dist/template/.pi/skills/industrial-brutalist-ui/SKILL.md +131 -0
- package/dist/template/.pi/skills/interview-me/SKILL.md +227 -0
- package/dist/template/.pi/skills/jira/SKILL.md +306 -0
- package/dist/template/.pi/skills/loop-audit/SKILL.md +141 -0
- package/dist/template/.pi/skills/loop-cost/SKILL.md +130 -0
- package/dist/template/.pi/skills/loop-engineering/SKILL.md +175 -0
- package/dist/template/.pi/skills/minimalist-ui/SKILL.md +124 -0
- package/dist/template/.pi/skills/mockup-to-code/SKILL.md +197 -0
- package/dist/template/.pi/skills/observability-and-instrumentation/SKILL.md +202 -0
- package/dist/template/.pi/skills/opensrc/SKILL.md +297 -0
- package/dist/template/.pi/skills/opensrc/references/architecture.md +176 -0
- package/dist/template/.pi/skills/opensrc/references/cli-usage.md +176 -0
- package/dist/template/.pi/skills/opensrc/references/registry-support.md +137 -0
- package/dist/template/.pi/skills/pdf-extract/SKILL.md +461 -0
- package/dist/template/.pi/skills/performance-optimization/SKILL.md +233 -0
- package/dist/template/.pi/skills/performance-optimization/references/performance-checklist.md +85 -0
- package/dist/template/.pi/skills/planning-and-task-breakdown/SKILL.md +204 -0
- package/dist/template/.pi/skills/playwright/SKILL.md +404 -0
- package/dist/template/.pi/skills/playwright/references/agent-browser-cli.md +405 -0
- package/dist/template/.pi/skills/polar/SKILL.md +125 -0
- package/dist/template/.pi/skills/react-best-practices/SKILL.md +156 -0
- package/dist/template/.pi/skills/react-best-practices/rules/_sections.md +46 -0
- package/dist/template/.pi/skills/react-best-practices/rules/_template.md +28 -0
- package/dist/template/.pi/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/dist/template/.pi/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/dist/template/.pi/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/dist/template/.pi/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/dist/template/.pi/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/dist/template/.pi/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/dist/template/.pi/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/dist/template/.pi/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/dist/template/.pi/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/dist/template/.pi/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/dist/template/.pi/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/dist/template/.pi/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/dist/template/.pi/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/dist/template/.pi/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/dist/template/.pi/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/dist/template/.pi/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/dist/template/.pi/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/dist/template/.pi/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/dist/template/.pi/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/dist/template/.pi/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/dist/template/.pi/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/dist/template/.pi/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/dist/template/.pi/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/dist/template/.pi/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/dist/template/.pi/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/dist/template/.pi/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/dist/template/.pi/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/dist/template/.pi/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/dist/template/.pi/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/dist/template/.pi/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/dist/template/.pi/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/dist/template/.pi/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/dist/template/.pi/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/dist/template/.pi/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/dist/template/.pi/skills/redesign-existing-projects/SKILL.md +217 -0
- package/dist/template/.pi/skills/resend/SKILL.md +196 -0
- package/dist/template/.pi/skills/resend/references/react-email.md +287 -0
- package/dist/template/.pi/skills/resend/references/receive-email.md +248 -0
- package/dist/template/.pi/skills/resend/references/send-email.md +318 -0
- package/dist/template/.pi/skills/root-cause-tracing/SKILL.md +216 -0
- package/dist/template/.pi/skills/security-and-hardening/SKILL.md +293 -0
- package/dist/template/.pi/skills/security-and-hardening/references/security-checklist.md +105 -0
- package/dist/template/.pi/skills/shipping-and-launch/SKILL.md +101 -0
- package/dist/template/.pi/skills/source-driven-development/SKILL.md +108 -0
- package/dist/template/.pi/skills/spec-driven-development/SKILL.md +177 -0
- package/dist/template/.pi/skills/srcwalk/SKILL.md +182 -0
- package/dist/template/.pi/skills/subagent-driven-development/SKILL.md +253 -0
- package/dist/template/.pi/skills/supabase/SKILL.md +153 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/SKILL.md +88 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/advanced-full-text-search.md +55 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/advanced-jsonb-indexing.md +49 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/conn-idle-timeout.md +46 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/conn-limits.md +44 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/conn-pooling.md +41 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/conn-prepared-statements.md +46 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/data-batch-inserts.md +54 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/data-n-plus-one.md +53 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/data-pagination.md +50 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/data-upsert.md +50 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/lock-advisory.md +56 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/lock-deadlock-prevention.md +68 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/lock-short-transactions.md +50 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/lock-skip-locked.md +54 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/monitor-explain-analyze.md +45 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/monitor-pg-stat-statements.md +55 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/monitor-vacuum-analyze.md +55 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/query-composite-indexes.md +44 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/query-covering-indexes.md +40 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/query-index-types.md +45 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/query-missing-indexes.md +43 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/query-partial-indexes.md +45 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/schema-data-types.md +46 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/schema-foreign-key-indexes.md +59 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/schema-lowercase-identifiers.md +55 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/schema-partitioning.md +55 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/schema-primary-keys.md +61 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/security-privileges.md +54 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/security-rls-basics.md +50 -0
- package/dist/template/.pi/skills/supabase-postgres-best-practices/rules/security-rls-performance.md +57 -0
- package/dist/template/.pi/skills/swift-concurrency/SKILL.md +290 -0
- package/dist/template/.pi/skills/swift-concurrency/references/actors.md +640 -0
- package/dist/template/.pi/skills/swift-concurrency/references/async-algorithms.md +822 -0
- package/dist/template/.pi/skills/swift-concurrency/references/async-await-basics.md +249 -0
- package/dist/template/.pi/skills/swift-concurrency/references/async-sequences.md +670 -0
- package/dist/template/.pi/skills/swift-concurrency/references/core-data.md +533 -0
- package/dist/template/.pi/skills/swift-concurrency/references/glossary.md +128 -0
- package/dist/template/.pi/skills/swift-concurrency/references/linting.md +142 -0
- package/dist/template/.pi/skills/swift-concurrency/references/memory-management.md +542 -0
- package/dist/template/.pi/skills/swift-concurrency/references/migration.md +1076 -0
- package/dist/template/.pi/skills/swift-concurrency/references/performance.md +574 -0
- package/dist/template/.pi/skills/swift-concurrency/references/sendable.md +578 -0
- package/dist/template/.pi/skills/swift-concurrency/references/tasks.md +604 -0
- package/dist/template/.pi/skills/swift-concurrency/references/testing.md +565 -0
- package/dist/template/.pi/skills/swift-concurrency/references/threading.md +452 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/SKILL.md +343 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/animation-advanced.md +351 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/animation-basics.md +284 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/animation-transitions.md +326 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/image-optimization.md +286 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/layout-best-practices.md +312 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/liquid-glass.md +377 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/list-patterns.md +153 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/modern-apis.md +400 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/performance-patterns.md +377 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/scroll-patterns.md +305 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/sheet-navigation-patterns.md +292 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/state-management.md +447 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/text-formatting.md +285 -0
- package/dist/template/.pi/skills/swiftui-expert-skill/references/view-structure.md +276 -0
- package/dist/template/.pi/skills/test-driven-development/SKILL.md +400 -0
- package/dist/template/.pi/skills/test-driven-development/references/testing-patterns.md +155 -0
- package/dist/template/.pi/skills/testing-anti-patterns/SKILL.md +339 -0
- package/dist/template/.pi/skills/using-agent-skills/SKILL.md +371 -0
- package/dist/template/.pi/skills/using-git-worktrees/SKILL.md +275 -0
- package/dist/template/.pi/skills/vercel-deploy-claimable/SKILL.md +144 -0
- package/dist/template/.pi/skills/vercel-deploy-claimable/scripts/deploy.sh +249 -0
- package/dist/template/.pi/skills/verification-before-completion/SKILL.md +348 -0
- package/dist/template/.pi/skills/webclaw/SKILL.md +188 -0
- package/dist/template/.pi/skills/writing-skills/SKILL.md +329 -0
- package/dist/template/.pi/skills/writing-skills/references/anti-patterns.md +25 -0
- package/dist/template/.pi/skills/writing-skills/references/claude-search-optimization.md +140 -0
- package/dist/template/.pi/skills/writing-skills/references/discovery-workflow.md +11 -0
- package/dist/template/.pi/skills/writing-skills/references/file-organization.md +32 -0
- package/dist/template/.pi/skills/writing-skills/references/flowcharts-and-examples.md +57 -0
- package/dist/template/.pi/skills/writing-skills/references/rationalization-hardening.md +75 -0
- package/dist/template/.pi/skills/writing-skills/references/testing-methodology.md +397 -0
- package/dist/template/.pi/skills/writing-skills/references/testing-skill-types.md +52 -0
- package/dist/template/.pi/subagents.json +10 -0
- package/dist/template/.pi/templates/adr.md +47 -0
- package/dist/template/.pi/templates/design.md +61 -0
- package/dist/template/.pi/templates/loop-github-action.yml +162 -0
- package/dist/template/.pi/templates/loop-orchestrator.sh +514 -0
- package/dist/template/.pi/templates/loop-orchestrator.test.ts +332 -0
- package/dist/template/.pi/templates/loop-orchestrator.ts +936 -0
- package/dist/template/.pi/templates/loop-state.json +24 -0
- package/dist/template/.pi/templates/loop-state.md +98 -0
- package/dist/template/.pi/templates/loop-vision.md +110 -0
- package/dist/template/.pi/templates/prd.md +204 -0
- package/dist/template/.pi/templates/progress.md +55 -0
- package/dist/template/.pi/templates/project.md +58 -0
- package/dist/template/.pi/templates/proposal.md +40 -0
- package/dist/template/.pi/templates/review-state.json +20 -0
- package/dist/template/.pi/templates/roadmap.md +93 -0
- package/dist/template/.pi/templates/state.md +97 -0
- package/dist/template/.pi/templates/tasks.md +202 -0
- package/dist/template/.pi/templates/tech-stack.md +85 -0
- package/dist/template/.pi/templates/user.md +26 -0
- package/dist/template/.pi/workflows/INDEX.md +63 -0
- package/dist/template/.pi/workflows/audit-pattern.md +84 -0
- package/dist/template/.pi/workflows/batch-implement.md +121 -0
- package/dist/template/.pi/workflows/deep-research.md +89 -0
- package/dist/template/.pi/workflows/development-lifecycle-workflow.md +175 -0
- package/dist/template/.pi/workflows/garbage-collection.md +144 -0
- package/dist/template/.pi/workflows/quality-loop.md +211 -0
- package/package.json +57 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { cac } from "cac";
|
|
3
|
+
import { dirname, join, relative } from "node:path";
|
|
4
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
5
|
+
import * as p from "@clack/prompts";
|
|
6
|
+
import color from "picocolors";
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { mkdir, readdir } from "node:fs/promises";
|
|
10
|
+
//#region package.json
|
|
11
|
+
var version = "0.3.0";
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/utils/manifest.ts
|
|
14
|
+
/**
|
|
15
|
+
* Template manifest — SHA-256 hash map of every installed kit file.
|
|
16
|
+
*
|
|
17
|
+
* Generated by `mdpi init` after copying the kit into a project's .pi/.
|
|
18
|
+
* Used by `mdpi upgrade` (future) to detect user-modified files:
|
|
19
|
+
* - "unmodified" → file matches template hash, safe to overwrite on upgrade
|
|
20
|
+
* - "modified" → user edited it, preserve on upgrade
|
|
21
|
+
* - "unknown" → not a template file (user-added) or no manifest present
|
|
22
|
+
*
|
|
23
|
+
* Ported from OpenCodeKit's utils/manifest.ts (paradigm-agnostic — file/hash
|
|
24
|
+
* ops only, no OpenCode-specific coupling).
|
|
25
|
+
*/
|
|
26
|
+
const MANIFEST_FILE = ".template-manifest.json";
|
|
27
|
+
/** Compute SHA-256 hash of a file's content. */
|
|
28
|
+
function hashFile(filePath) {
|
|
29
|
+
return createHash("sha256").update(readFileSync(filePath, "utf-8")).digest("hex");
|
|
30
|
+
}
|
|
31
|
+
/** Walk a directory recursively → {relativePath: sha256}. */
|
|
32
|
+
function buildManifestFromDir(dir, skipDirs = [
|
|
33
|
+
"node_modules",
|
|
34
|
+
".git",
|
|
35
|
+
"dist",
|
|
36
|
+
"coverage"
|
|
37
|
+
]) {
|
|
38
|
+
const files = {};
|
|
39
|
+
function walk(currentDir) {
|
|
40
|
+
for (const entry of readdirSync(currentDir, { withFileTypes: true })) if (entry.isDirectory()) {
|
|
41
|
+
if (skipDirs.includes(entry.name)) continue;
|
|
42
|
+
walk(join(currentDir, entry.name));
|
|
43
|
+
} else if (entry.isFile()) {
|
|
44
|
+
if (entry.name === ".template-manifest.json") continue;
|
|
45
|
+
const fullPath = join(currentDir, entry.name);
|
|
46
|
+
files[relative(dir, fullPath)] = hashFile(fullPath);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
walk(dir);
|
|
50
|
+
return files;
|
|
51
|
+
}
|
|
52
|
+
/** Generate + write the manifest into a kit directory. */
|
|
53
|
+
function generateManifest(piDir, version) {
|
|
54
|
+
const manifest = {
|
|
55
|
+
version,
|
|
56
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
57
|
+
files: buildManifestFromDir(piDir)
|
|
58
|
+
};
|
|
59
|
+
writeFileSync(join(piDir, MANIFEST_FILE), JSON.stringify(manifest, null, 2));
|
|
60
|
+
return manifest;
|
|
61
|
+
}
|
|
62
|
+
/** Load an existing manifest, or null if none. */
|
|
63
|
+
function loadManifest(piDir) {
|
|
64
|
+
const manifestPath = join(piDir, MANIFEST_FILE);
|
|
65
|
+
if (!existsSync(manifestPath)) return null;
|
|
66
|
+
try {
|
|
67
|
+
return JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
68
|
+
} catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Has the user modified a template file since install? */
|
|
73
|
+
function fileModificationStatus(filePath, relativePath, manifest) {
|
|
74
|
+
if (!manifest) return "unknown";
|
|
75
|
+
const templateHash = manifest.files[relativePath];
|
|
76
|
+
if (!templateHash) return "unknown";
|
|
77
|
+
if (!existsSync(filePath)) return "unmodified";
|
|
78
|
+
return hashFile(filePath) === templateHash ? "unmodified" : "modified";
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/utils/template.ts
|
|
82
|
+
/** Shared template + version helpers used by init / upgrade / doctor. */
|
|
83
|
+
const DEFAULT_SKIP_DIRS = [
|
|
84
|
+
"node_modules",
|
|
85
|
+
".git",
|
|
86
|
+
"dist",
|
|
87
|
+
"coverage",
|
|
88
|
+
".next",
|
|
89
|
+
".turbo"
|
|
90
|
+
];
|
|
91
|
+
/** Resolve the bundled template root: dist/template (published) or repo root (dev tsx). */
|
|
92
|
+
function getTemplateRoot() {
|
|
93
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
94
|
+
const candidates = [join(__dirname, "template"), join(__dirname, "..", "..")];
|
|
95
|
+
for (const candidate of candidates) if (existsSync(join(candidate, ".pi"))) return candidate;
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
/** Resolve the mdpi package version (inlined by tsdown from package.json at build). */
|
|
99
|
+
function getPackageVersion() {
|
|
100
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
101
|
+
const pkgPaths = [join(__dirname, "..", "..", "package.json"), join(__dirname, "..", "package.json")];
|
|
102
|
+
for (const pkgPath of pkgPaths) {
|
|
103
|
+
if (!existsSync(pkgPath)) continue;
|
|
104
|
+
try {
|
|
105
|
+
return JSON.parse(readFileSync(pkgPath, "utf-8")).version ?? "unknown";
|
|
106
|
+
} catch {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return "unknown";
|
|
111
|
+
}
|
|
112
|
+
/** Walk a directory recursively → relative file paths (excludes skipDirs + skipFiles). */
|
|
113
|
+
function listFilesRel(dir, skipDirs = DEFAULT_SKIP_DIRS, skipFiles = []) {
|
|
114
|
+
const out = [];
|
|
115
|
+
function walk(d) {
|
|
116
|
+
for (const e of readdirSync(d, { withFileTypes: true })) if (e.isDirectory()) {
|
|
117
|
+
if (skipDirs.includes(e.name)) continue;
|
|
118
|
+
walk(join(d, e.name));
|
|
119
|
+
} else if (e.isFile()) {
|
|
120
|
+
if (skipFiles.includes(e.name)) continue;
|
|
121
|
+
out.push(relative(dir, join(d, e.name)));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
walk(dir);
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
//#endregion
|
|
128
|
+
//#region src/commands/lint.ts
|
|
129
|
+
/**
|
|
130
|
+
* mdpi lint — governance checks for the .pi/ kit.
|
|
131
|
+
*
|
|
132
|
+
* Adapted from OpenCodeKit's validation/ suite to pi conventions:
|
|
133
|
+
* - skill-lint: pi frontmatter (name + description required, NOT version/tags/
|
|
134
|
+
* dependencies which pi doesn't use), H1, When to Use / NOT to Use, line
|
|
135
|
+
* limits, references/ depth, and DEAD CROSS-REF detection (the bug class
|
|
136
|
+
* fixed manually in commit 0a20e46 — skills referencing non-existent skills).
|
|
137
|
+
* - docs-drift: .pi/README.md counts + slash-command refs vs actual content.
|
|
138
|
+
*
|
|
139
|
+
* Usage: mdpi lint [skills|docs|all] [--json] [--fix]
|
|
140
|
+
* --fix auto-applies the safely-fixable rules:
|
|
141
|
+
* - name-match → set frontmatter `name:` to the skill directory name
|
|
142
|
+
* - count-* → rewrite the README kit-summary counts to match reality
|
|
143
|
+
* Non-fixable rules (dead-cross-ref, missing H1/frontmatter, missing/unknown
|
|
144
|
+
* prompt refs, max-lines, refs-depth) are still reported.
|
|
145
|
+
* Exit code: 1 if any error-severity issue remains, else 0.
|
|
146
|
+
*/
|
|
147
|
+
const REQUIRED_FRONTMATTER = ["name", "description"];
|
|
148
|
+
const MAX_LINES = 500;
|
|
149
|
+
const MAX_LINES_WARNING = 400;
|
|
150
|
+
/** Kit categories whose README "(N)" counts are verified + auto-fixable. */
|
|
151
|
+
const COUNT_LABELS = [
|
|
152
|
+
"skill",
|
|
153
|
+
"prompt",
|
|
154
|
+
"agent",
|
|
155
|
+
"workflow",
|
|
156
|
+
"template"
|
|
157
|
+
];
|
|
158
|
+
const KNOWN_PI_BUILTINS = /* @__PURE__ */ new Set([
|
|
159
|
+
"reload",
|
|
160
|
+
"skill",
|
|
161
|
+
"memory",
|
|
162
|
+
"agent",
|
|
163
|
+
"agents",
|
|
164
|
+
"clear",
|
|
165
|
+
"compact",
|
|
166
|
+
"new",
|
|
167
|
+
"help",
|
|
168
|
+
"exit",
|
|
169
|
+
"quit",
|
|
170
|
+
"model",
|
|
171
|
+
"theme",
|
|
172
|
+
"tools",
|
|
173
|
+
"keys",
|
|
174
|
+
"config",
|
|
175
|
+
"ask",
|
|
176
|
+
"dcp",
|
|
177
|
+
"vcc",
|
|
178
|
+
"observation",
|
|
179
|
+
"session"
|
|
180
|
+
]);
|
|
181
|
+
function parseFrontmatter(content) {
|
|
182
|
+
const m = content.match(/^---\n([\s\S]*?)\n---/);
|
|
183
|
+
if (!m) return null;
|
|
184
|
+
const fields = {};
|
|
185
|
+
for (const line of m[1].split("\n")) {
|
|
186
|
+
const kv = line.match(/^(\w[\w-]*):\s*(.*)$/);
|
|
187
|
+
if (kv) {
|
|
188
|
+
const [, key, value] = kv;
|
|
189
|
+
fields[key] = value.replace(/^['"]|['"]$/g, "").trim();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return fields;
|
|
193
|
+
}
|
|
194
|
+
function skillDirNames(skillsDir) {
|
|
195
|
+
if (!existsSync(skillsDir)) return /* @__PURE__ */ new Set();
|
|
196
|
+
return new Set(readdirSync(skillsDir).filter((n) => statSync(join(skillsDir, n)).isDirectory() && !n.startsWith(".")));
|
|
197
|
+
}
|
|
198
|
+
/** Extract skill names referenced in body: `/skill:<n>`, `skill:<n>`, `` `<n>` skill ``. */
|
|
199
|
+
function findCrossRefs(content) {
|
|
200
|
+
const refs = /* @__PURE__ */ new Set();
|
|
201
|
+
for (const m of content.matchAll(/(?:\/skill:|skill:)([a-z0-9][a-z0-9-]*)/g)) refs.add(m[1]);
|
|
202
|
+
for (const m of content.matchAll(/`([a-z0-9][a-z0-9-]*)`\s+skill\b/g)) refs.add(m[1]);
|
|
203
|
+
return [...refs];
|
|
204
|
+
}
|
|
205
|
+
/** Lint one skill. With `fix=true`, rewrites the frontmatter `name:` to match the dir. */
|
|
206
|
+
function lintSkill(skillsDir, name, valid, fix) {
|
|
207
|
+
const issues = [];
|
|
208
|
+
let fixed = 0;
|
|
209
|
+
const skillPath = join(skillsDir, name, "SKILL.md");
|
|
210
|
+
if (!existsSync(skillPath)) {
|
|
211
|
+
issues.push({
|
|
212
|
+
scope: name,
|
|
213
|
+
rule: "file-exists",
|
|
214
|
+
severity: "error",
|
|
215
|
+
message: "Missing SKILL.md",
|
|
216
|
+
fix: "Create SKILL.md with name + description frontmatter"
|
|
217
|
+
});
|
|
218
|
+
return {
|
|
219
|
+
issues,
|
|
220
|
+
fixed
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const content = readFileSync(skillPath, "utf-8");
|
|
224
|
+
const lineCount = content.split("\n").length;
|
|
225
|
+
if (lineCount > MAX_LINES) issues.push({
|
|
226
|
+
scope: name,
|
|
227
|
+
rule: "max-lines",
|
|
228
|
+
severity: "error",
|
|
229
|
+
message: `${lineCount} lines (max ${MAX_LINES}); move detail to references/`,
|
|
230
|
+
fix: "Extract examples/patterns to references/"
|
|
231
|
+
});
|
|
232
|
+
else if (lineCount > MAX_LINES_WARNING) issues.push({
|
|
233
|
+
scope: name,
|
|
234
|
+
rule: "max-lines-warn",
|
|
235
|
+
severity: "warning",
|
|
236
|
+
message: `${lineCount} lines (warn ${MAX_LINES_WARNING})`
|
|
237
|
+
});
|
|
238
|
+
const fm = parseFrontmatter(content);
|
|
239
|
+
if (!fm) issues.push({
|
|
240
|
+
scope: name,
|
|
241
|
+
rule: "frontmatter",
|
|
242
|
+
severity: "error",
|
|
243
|
+
message: "Missing YAML frontmatter (--- … ---)",
|
|
244
|
+
fix: "Add frontmatter with name + description"
|
|
245
|
+
});
|
|
246
|
+
else {
|
|
247
|
+
for (const f of REQUIRED_FRONTMATTER) if (!(f in fm) || !fm[f]) issues.push({
|
|
248
|
+
scope: name,
|
|
249
|
+
rule: `frontmatter-${f}`,
|
|
250
|
+
severity: "error",
|
|
251
|
+
message: `Missing frontmatter field: ${f}`,
|
|
252
|
+
fix: `Add '${f}:'`
|
|
253
|
+
});
|
|
254
|
+
const nm = fm.name;
|
|
255
|
+
if (typeof nm === "string" && nm !== name) if (fix) {
|
|
256
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
257
|
+
if (fmMatch) {
|
|
258
|
+
const fmBody = fmMatch[1].replace(/^(\s*name:\s*).*$/m, `$1${name}`);
|
|
259
|
+
if (fmBody !== fmMatch[1]) {
|
|
260
|
+
writeFileSync(skillPath, content.replace(fmMatch[0], `---\n${fmBody}\n---`));
|
|
261
|
+
fixed++;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
} else issues.push({
|
|
265
|
+
scope: name,
|
|
266
|
+
rule: "name-match",
|
|
267
|
+
severity: "warning",
|
|
268
|
+
message: `frontmatter name '${nm}' ≠ directory '${name}'`,
|
|
269
|
+
fix: `Set frontmatter name: ${name} (or run: mdpi lint --fix)`
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
if (!/^#\s+\S/m.test(content)) issues.push({
|
|
273
|
+
scope: name,
|
|
274
|
+
rule: "h1",
|
|
275
|
+
severity: "error",
|
|
276
|
+
message: "Missing H1 title",
|
|
277
|
+
fix: "Add '# Title' after frontmatter"
|
|
278
|
+
});
|
|
279
|
+
if (!/^##\s+When\s+to\s+Use/im.test(content)) issues.push({
|
|
280
|
+
scope: name,
|
|
281
|
+
rule: "when-to-use",
|
|
282
|
+
severity: "warning",
|
|
283
|
+
message: "Missing '## When to Use'"
|
|
284
|
+
});
|
|
285
|
+
if (!/^##\s+When\s+NOT\s+to\s+Use/im.test(content)) issues.push({
|
|
286
|
+
scope: name,
|
|
287
|
+
rule: "when-not-to-use",
|
|
288
|
+
severity: "warning",
|
|
289
|
+
message: "Missing '## When NOT to Use'"
|
|
290
|
+
});
|
|
291
|
+
for (const ref of findCrossRefs(content)) if (!valid.has(ref)) issues.push({
|
|
292
|
+
scope: name,
|
|
293
|
+
rule: "dead-cross-ref",
|
|
294
|
+
severity: "error",
|
|
295
|
+
message: `references skill '${ref}' which does not exist in .pi/skills/`,
|
|
296
|
+
fix: `Remove the reference or add skill '${ref}'`
|
|
297
|
+
});
|
|
298
|
+
const refsDir = join(skillsDir, name, "references");
|
|
299
|
+
if (existsSync(refsDir)) {
|
|
300
|
+
const nested = readdirSync(refsDir).filter((e) => statSync(join(refsDir, e)).isDirectory());
|
|
301
|
+
if (nested.length) issues.push({
|
|
302
|
+
scope: name,
|
|
303
|
+
rule: "refs-depth",
|
|
304
|
+
severity: "warning",
|
|
305
|
+
message: `${nested.length} nested dir(s) in references/ — prefer flat`,
|
|
306
|
+
fix: "Flatten references/ to one level"
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
issues,
|
|
311
|
+
fixed
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
function lintSkills(piDir, fix = false) {
|
|
315
|
+
const skillsDir = join(piDir, "skills");
|
|
316
|
+
if (!existsSync(skillsDir)) return {
|
|
317
|
+
ok: false,
|
|
318
|
+
issues: [{
|
|
319
|
+
scope: "(root)",
|
|
320
|
+
rule: "skills-dir",
|
|
321
|
+
severity: "error",
|
|
322
|
+
message: `skills/ not found: ${skillsDir}`
|
|
323
|
+
}],
|
|
324
|
+
stats: {
|
|
325
|
+
total: 0,
|
|
326
|
+
passed: 0,
|
|
327
|
+
failed: 0,
|
|
328
|
+
warnings: 0,
|
|
329
|
+
fixed: 0
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
const valid = skillDirNames(skillsDir);
|
|
333
|
+
const names = [...valid].sort();
|
|
334
|
+
const issues = [];
|
|
335
|
+
let failed = 0;
|
|
336
|
+
let warns = 0;
|
|
337
|
+
let fixed = 0;
|
|
338
|
+
for (const n of names) {
|
|
339
|
+
const si = lintSkill(skillsDir, n, valid, fix);
|
|
340
|
+
issues.push(...si.issues);
|
|
341
|
+
if (si.issues.some((i) => i.severity === "error")) failed++;
|
|
342
|
+
warns += si.issues.filter((i) => i.severity === "warning").length;
|
|
343
|
+
fixed += si.fixed;
|
|
344
|
+
}
|
|
345
|
+
return {
|
|
346
|
+
ok: failed === 0,
|
|
347
|
+
issues,
|
|
348
|
+
stats: {
|
|
349
|
+
total: names.length,
|
|
350
|
+
passed: names.length - failed,
|
|
351
|
+
failed,
|
|
352
|
+
warnings: warns,
|
|
353
|
+
fixed
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Rewrite the README kit-summary counts to match reality. For each label, finds
|
|
359
|
+
* the first `` `label/` (N…) `` form and replaces N with the actual count,
|
|
360
|
+
* preserving any suffix (e.g. ` + INDEX`). Only matches the backtick-wrapped
|
|
361
|
+
* kit-summary form — leaves prose mentions of counts untouched.
|
|
362
|
+
*/
|
|
363
|
+
function fixReadmeCounts(readme, counts) {
|
|
364
|
+
let out = readme;
|
|
365
|
+
for (const [label, actual] of Object.entries(counts)) {
|
|
366
|
+
const re = new RegExp("(`" + label + "s?/?`\\s*\\()(\\d+)([^)]*\\))");
|
|
367
|
+
out = out.replace(re, (_m, pre, _n, suf) => `${pre}${actual}${suf}`);
|
|
368
|
+
}
|
|
369
|
+
return out;
|
|
370
|
+
}
|
|
371
|
+
function lintDocs(piDir, fix = false) {
|
|
372
|
+
const readmePath = join(piDir, "README.md");
|
|
373
|
+
if (!existsSync(readmePath)) return {
|
|
374
|
+
ok: true,
|
|
375
|
+
issues: [],
|
|
376
|
+
stats: {
|
|
377
|
+
total: 0,
|
|
378
|
+
passed: 0,
|
|
379
|
+
failed: 0,
|
|
380
|
+
warnings: 0,
|
|
381
|
+
fixed: 0
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
const readme = readFileSync(readmePath, "utf-8");
|
|
385
|
+
const issues = [];
|
|
386
|
+
const list = (sub) => existsSync(join(piDir, sub)) ? readdirSync(join(piDir, sub)).filter((f) => f.endsWith(".md")).map((f) => f.replace(/\.md$/, "")) : [];
|
|
387
|
+
const actualPrompts = list("prompts").filter((n) => n !== "INDEX").sort();
|
|
388
|
+
const actualAgents = list("agents").filter((n) => n !== "INDEX").length;
|
|
389
|
+
const actualSkills = existsSync(join(piDir, "skills")) ? readdirSync(join(piDir, "skills")).filter((n) => statSync(join(piDir, "skills", n)).isDirectory()).length : 0;
|
|
390
|
+
const actualWorkflows = list("workflows").filter((n) => n !== "INDEX").length;
|
|
391
|
+
const actualTemplates = list("templates").filter((n) => n !== "INDEX").length;
|
|
392
|
+
const counts = {
|
|
393
|
+
skill: actualSkills,
|
|
394
|
+
prompt: actualPrompts.length,
|
|
395
|
+
agent: actualAgents,
|
|
396
|
+
workflow: actualWorkflows,
|
|
397
|
+
template: actualTemplates
|
|
398
|
+
};
|
|
399
|
+
const documented = /* @__PURE__ */ new Set();
|
|
400
|
+
for (const m of readme.matchAll(/`\/([a-z0-9-]+)`/g)) documented.add(m[1]);
|
|
401
|
+
for (const cmd of actualPrompts) if (!documented.has(cmd)) issues.push({
|
|
402
|
+
scope: "README",
|
|
403
|
+
rule: "readme-missing-prompt",
|
|
404
|
+
severity: "warning",
|
|
405
|
+
message: `README missing '/${cmd}' (prompt exists)`
|
|
406
|
+
});
|
|
407
|
+
for (const cmd of documented) if (!actualPrompts.includes(cmd) && !KNOWN_PI_BUILTINS.has(cmd)) issues.push({
|
|
408
|
+
scope: "README",
|
|
409
|
+
rule: "readme-unknown-prompt",
|
|
410
|
+
severity: "warning",
|
|
411
|
+
message: `README references unknown '/${cmd}'`
|
|
412
|
+
});
|
|
413
|
+
const drift = (label) => {
|
|
414
|
+
const actual = counts[label];
|
|
415
|
+
const m = readme.match(new RegExp(`\\b${label}s?[^\\(\\n]*\\((\\d+)[^)]*\\)`));
|
|
416
|
+
if (!m) return null;
|
|
417
|
+
const doc = Number.parseInt(m[1], 10);
|
|
418
|
+
return doc === actual ? null : {
|
|
419
|
+
doc,
|
|
420
|
+
actual
|
|
421
|
+
};
|
|
422
|
+
};
|
|
423
|
+
const driftedLabeled = COUNT_LABELS.map((label) => ({
|
|
424
|
+
label,
|
|
425
|
+
d: drift(label)
|
|
426
|
+
})).filter((x) => x.d);
|
|
427
|
+
let fixed = 0;
|
|
428
|
+
if (fix) {
|
|
429
|
+
if (driftedLabeled.length) {
|
|
430
|
+
const fixedReadme = fixReadmeCounts(readme, counts);
|
|
431
|
+
if (fixedReadme !== readme) {
|
|
432
|
+
writeFileSync(readmePath, fixedReadme);
|
|
433
|
+
fixed = driftedLabeled.length;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
} else for (const { label, d } of driftedLabeled) issues.push({
|
|
437
|
+
scope: "README",
|
|
438
|
+
rule: `count-${label}`,
|
|
439
|
+
severity: "warning",
|
|
440
|
+
message: `${label} count (${d.doc}) ≠ actual (${d.actual})`,
|
|
441
|
+
fix: `Update README ${label} count to ${d.actual} (or run: mdpi lint --fix)`
|
|
442
|
+
});
|
|
443
|
+
return {
|
|
444
|
+
ok: issues.length === 0,
|
|
445
|
+
issues,
|
|
446
|
+
stats: {
|
|
447
|
+
total: 1,
|
|
448
|
+
passed: issues.length === 0 ? 1 : 0,
|
|
449
|
+
failed: 0,
|
|
450
|
+
warnings: issues.length,
|
|
451
|
+
fixed
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
function lintAll(piDir, opts = {}) {
|
|
456
|
+
const target = opts.target ?? "all";
|
|
457
|
+
const fix = opts.fix ?? false;
|
|
458
|
+
const results = [];
|
|
459
|
+
if (target === "skills" || target === "all") results.push({
|
|
460
|
+
name: "skills",
|
|
461
|
+
r: lintSkills(piDir, fix)
|
|
462
|
+
});
|
|
463
|
+
if (target === "docs" || target === "all") results.push({
|
|
464
|
+
name: "docs",
|
|
465
|
+
r: lintDocs(piDir, fix)
|
|
466
|
+
});
|
|
467
|
+
if (opts.json) {
|
|
468
|
+
console.log(JSON.stringify(results, null, 2));
|
|
469
|
+
const anyFail = results.some(({ r }) => !r.ok);
|
|
470
|
+
process.exitCode = anyFail ? 1 : 0;
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const totalFixed = results.reduce((s, { r }) => s + r.stats.fixed, 0);
|
|
474
|
+
for (const { name, r } of results) {
|
|
475
|
+
const fixedNote = r.stats.fixed ? color.green(` · ${r.stats.fixed} fixed`) : "";
|
|
476
|
+
console.log(`\n${color.bold(color.cyan(name))}: ${r.stats.total} total · ${r.stats.passed} passed · ${r.stats.failed} failed · ${r.stats.warnings} warnings${fixedNote}`);
|
|
477
|
+
const byScope = /* @__PURE__ */ new Map();
|
|
478
|
+
for (const i of r.issues) {
|
|
479
|
+
const a = byScope.get(i.scope) ?? [];
|
|
480
|
+
a.push(i);
|
|
481
|
+
byScope.set(i.scope, a);
|
|
482
|
+
}
|
|
483
|
+
for (const [scope, iss] of byScope) {
|
|
484
|
+
const hasErr = iss.some((i) => i.severity === "error");
|
|
485
|
+
console.log(` ${hasErr ? color.red("✗") : color.yellow("⚠")} ${scope}`);
|
|
486
|
+
for (const i of iss) {
|
|
487
|
+
console.log(` ${i.severity === "error" ? color.red("ERR ") : color.yellow("WARN")} [${i.rule}] ${i.message}`);
|
|
488
|
+
if (i.fix) console.log(` fix: ${i.fix}`);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (totalFixed) console.log(`\n${color.green("✓")} auto-fixed ${totalFixed} issue(s)`);
|
|
493
|
+
const anyFail = results.some(({ r }) => !r.ok);
|
|
494
|
+
process.exitCode = anyFail ? 1 : 0;
|
|
495
|
+
}
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/commands/doctor.ts
|
|
498
|
+
/**
|
|
499
|
+
* mdpi doctor — .pi/ health check.
|
|
500
|
+
*
|
|
501
|
+
* Verifies: mdpi version match, manifest valid, settings.json valid JSON, kit
|
|
502
|
+
* dirs present (skills/prompts/agents/templates/workflows/extensions), orphan
|
|
503
|
+
* detection (manifest files no longer in template), and a lint summary.
|
|
504
|
+
*/
|
|
505
|
+
function row(ok, label, detail) {
|
|
506
|
+
console.log(` ${ok ? color.green("✓") : color.red("✗")} ${label}: ${detail}`);
|
|
507
|
+
}
|
|
508
|
+
async function doctorCommand(options = {}) {
|
|
509
|
+
const quiet = process.argv.includes("--quiet");
|
|
510
|
+
if (!quiet) p.intro(color.bgCyan(color.black(" mdpi doctor ")));
|
|
511
|
+
const piDir = join(process.cwd(), ".pi");
|
|
512
|
+
if (!existsSync(piDir)) {
|
|
513
|
+
if (!quiet) {
|
|
514
|
+
p.log.error(".pi/ not found — run `mdpi init`");
|
|
515
|
+
p.outro(color.red("Failed"));
|
|
516
|
+
}
|
|
517
|
+
process.exitCode = 1;
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
const mdpiVersion = getPackageVersion();
|
|
521
|
+
const installedVersion = existsSync(join(piDir, ".version")) ? readFileSync(join(piDir, ".version"), "utf-8").trim() : "(none)";
|
|
522
|
+
let manifest = loadManifest(piDir);
|
|
523
|
+
let manifestOk = !!manifest && typeof manifest.version === "string" && !!manifest.files;
|
|
524
|
+
let manifestFixed = false;
|
|
525
|
+
if (!manifestOk && options.fix) {
|
|
526
|
+
manifest = generateManifest(piDir, installedVersion !== "(none)" ? installedVersion : mdpiVersion);
|
|
527
|
+
manifestOk = !!manifest && !!manifest.files;
|
|
528
|
+
manifestFixed = manifestOk;
|
|
529
|
+
}
|
|
530
|
+
let settingsOk = false;
|
|
531
|
+
try {
|
|
532
|
+
JSON.parse(readFileSync(join(piDir, "settings.json"), "utf-8"));
|
|
533
|
+
settingsOk = true;
|
|
534
|
+
} catch {
|
|
535
|
+
settingsOk = false;
|
|
536
|
+
}
|
|
537
|
+
const countMd = (sub) => existsSync(join(piDir, sub)) ? readdirSync(join(piDir, sub)).filter((f) => f.endsWith(".md") && f !== "INDEX.md").length : 0;
|
|
538
|
+
const countExt = (sub, ext) => existsSync(join(piDir, sub)) ? readdirSync(join(piDir, sub)).filter((f) => f.endsWith(ext)).length : 0;
|
|
539
|
+
const skillCount = existsSync(join(piDir, "skills")) ? readdirSync(join(piDir, "skills")).filter((n) => statSync(join(piDir, "skills", n)).isDirectory()).length : 0;
|
|
540
|
+
const templateRoot = getTemplateRoot();
|
|
541
|
+
let orphans = 0;
|
|
542
|
+
if (templateRoot && manifest) {
|
|
543
|
+
const srcPi = join(templateRoot, ".pi");
|
|
544
|
+
const newFiles = new Set(listFilesRel(srcPi, [
|
|
545
|
+
"node_modules",
|
|
546
|
+
".git",
|
|
547
|
+
"dist",
|
|
548
|
+
"coverage"
|
|
549
|
+
], [".template-manifest.json"]));
|
|
550
|
+
orphans = Object.keys(manifest.files).filter((f) => !newFiles.has(f) && f !== ".version").length;
|
|
551
|
+
}
|
|
552
|
+
console.log(`\n${color.bold("mdpi doctor")} — .pi/ health\n`);
|
|
553
|
+
row(true, "mdpi version", `installed ${installedVersion} · cli ${mdpiVersion}` + (installedVersion !== mdpiVersion ? color.yellow(" (out of date — run mdpi upgrade)") : ""));
|
|
554
|
+
row(manifestOk, "manifest", manifestOk ? `${Object.keys(manifest.files).length} files tracked (v${manifest.version})` + (manifestFixed ? color.green(" (regenerated by --fix)") : "") : "missing or invalid .template-manifest.json" + (options.fix ? "" : " (run: mdpi doctor --fix)"));
|
|
555
|
+
row(settingsOk, "settings.json", settingsOk ? "valid JSON" : "INVALID JSON");
|
|
556
|
+
row(skillCount > 0, "skills", `${skillCount} skill dirs`);
|
|
557
|
+
row(countMd("prompts") > 0, "prompts", `${countMd("prompts")} prompts`);
|
|
558
|
+
row(countMd("agents") > 0, "agents", `${countMd("agents")} agents`);
|
|
559
|
+
row(countMd("templates") > 0, "templates", `${countMd("templates")} templates`);
|
|
560
|
+
row(countMd("workflows") > 0, "workflows", `${countMd("workflows")} workflows`);
|
|
561
|
+
row(countExt("extensions", ".ts") > 0, "extensions", `${countExt("extensions", ".ts")} extensions`);
|
|
562
|
+
if (templateRoot) row(orphans === 0, "orphans", orphans === 0 ? "none (template-removed files)" : `${orphans} orphan(s) — run mdpi upgrade --prune-all`);
|
|
563
|
+
const lr = lintSkills(piDir);
|
|
564
|
+
console.log(` ${lr.ok ? color.green("✓") : color.red("✗")} lint: ${lr.stats.failed} errors · ${lr.stats.warnings} warnings (run ${color.cyan("mdpi lint")} for detail)`);
|
|
565
|
+
const healthy = manifestOk && settingsOk && skillCount > 0 && lr.stats.failed === 0;
|
|
566
|
+
if (!quiet) p.outro(healthy ? color.green("Healthy") : color.yellow("Issues found"));
|
|
567
|
+
process.exitCode = healthy ? 0 : 1;
|
|
568
|
+
}
|
|
569
|
+
//#endregion
|
|
570
|
+
//#region src/commands/init.ts
|
|
571
|
+
/**
|
|
572
|
+
* mdpi init — scaffold a Pi coding-agent kit (.pi/) into the current repo.
|
|
573
|
+
*
|
|
574
|
+
* Mirrors OpenCodeKit's `ock init` but pi-native:
|
|
575
|
+
* - No license gate (open MIT).
|
|
576
|
+
* - No --global (pi global dir ~/.pi/agent/ is pi's own config; installing the
|
|
577
|
+
* full kit there is a footgun). Project-local only.
|
|
578
|
+
* - Bundled template is the CURATED kit (runtime dirs already stripped at
|
|
579
|
+
* build time by scripts/bundle-template.mjs).
|
|
580
|
+
*
|
|
581
|
+
* After copy, writes .pi/.version and generates .pi/.template-manifest.json
|
|
582
|
+
* (SHA-256 map) so a future `mdpi upgrade` can detect user modifications.
|
|
583
|
+
*
|
|
584
|
+
* --only <cats> installs only the listed category dirs (plus the always-on kit
|
|
585
|
+
* config/docs) and rewrites settings.json to drop references to the excluded
|
|
586
|
+
* `skills`/`prompts`/`extensions` dirs so pi doesn't resolve dangling paths.
|
|
587
|
+
* Caveat: `mdpi upgrade` always compares against the FULL template — a subset
|
|
588
|
+
* install is not remembered as partial, so upgrade will offer the missing
|
|
589
|
+
* categories (use `--check` first, or re-run `init` without `--only`).
|
|
590
|
+
*
|
|
591
|
+
* --quiet suppresses the @clack UI but still performs the install (emits one
|
|
592
|
+
* machine-readable line). --force overwrites template files but preserves any
|
|
593
|
+
* extra user-created files under .pi/ (safe, non-destructive to custom work).
|
|
594
|
+
*/
|
|
595
|
+
const EXCLUDED_DIRS = [
|
|
596
|
+
"node_modules",
|
|
597
|
+
".git",
|
|
598
|
+
"dist",
|
|
599
|
+
".DS_Store",
|
|
600
|
+
"coverage",
|
|
601
|
+
".next",
|
|
602
|
+
".turbo"
|
|
603
|
+
];
|
|
604
|
+
const EXCLUDED_FILES = [
|
|
605
|
+
"bun.lock",
|
|
606
|
+
"package-lock.json",
|
|
607
|
+
"yarn.lock",
|
|
608
|
+
"pnpm-lock.yaml"
|
|
609
|
+
];
|
|
610
|
+
/** Selectable kit categories for `--only`. */
|
|
611
|
+
const CATEGORIES = [
|
|
612
|
+
"agents",
|
|
613
|
+
"prompts",
|
|
614
|
+
"skills",
|
|
615
|
+
"templates",
|
|
616
|
+
"workflows",
|
|
617
|
+
"context",
|
|
618
|
+
"extensions"
|
|
619
|
+
];
|
|
620
|
+
/** Kit infrastructure always installed (not selectable via --only). */
|
|
621
|
+
const ALWAYS_ENTRIES = [
|
|
622
|
+
"scripts",
|
|
623
|
+
"artifacts/example",
|
|
624
|
+
"settings.json",
|
|
625
|
+
"AGENTS.md",
|
|
626
|
+
"README.md",
|
|
627
|
+
"QUALITY.md",
|
|
628
|
+
"VERSION",
|
|
629
|
+
".env.example",
|
|
630
|
+
"guard.example.json",
|
|
631
|
+
"subagents.json"
|
|
632
|
+
];
|
|
633
|
+
/** Recursively copy a directory, skipping runtime/lockfile noise. */
|
|
634
|
+
async function copyDir(src, dest) {
|
|
635
|
+
await mkdir(dest, { recursive: true });
|
|
636
|
+
for (const entry of await readdir(src, { withFileTypes: true })) {
|
|
637
|
+
if (EXCLUDED_DIRS.includes(entry.name)) continue;
|
|
638
|
+
if (!entry.isDirectory() && EXCLUDED_FILES.includes(entry.name)) continue;
|
|
639
|
+
const srcPath = join(src, entry.name);
|
|
640
|
+
const destPath = join(dest, entry.name);
|
|
641
|
+
if (entry.isSymbolicLink()) continue;
|
|
642
|
+
if (entry.isDirectory()) await copyDir(srcPath, destPath);
|
|
643
|
+
else writeFileSync(destPath, readFileSync(srcPath, "utf-8"));
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
/** Copy one template entry (dir or file) into the kit, creating parent dirs. */
|
|
647
|
+
async function copyEntry(srcPi, piDir, rel) {
|
|
648
|
+
const from = join(srcPi, rel);
|
|
649
|
+
if (!existsSync(from)) return;
|
|
650
|
+
const to = join(piDir, rel);
|
|
651
|
+
if (statSync(from).isDirectory()) await copyDir(from, to);
|
|
652
|
+
else {
|
|
653
|
+
await mkdir(join(to, ".."), { recursive: true });
|
|
654
|
+
writeFileSync(to, readFileSync(from, "utf-8"));
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
/** Parse `--only cats` into a Set. Throws on an unknown category. */
|
|
658
|
+
function parseOnly(only) {
|
|
659
|
+
if (!only) return null;
|
|
660
|
+
const cats = /* @__PURE__ */ new Set();
|
|
661
|
+
for (const raw of only.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
662
|
+
if (!CATEGORIES.includes(raw)) throw new Error(`unknown --only category '${raw}'. Valid: ${CATEGORIES.join(", ")}`);
|
|
663
|
+
cats.add(raw);
|
|
664
|
+
}
|
|
665
|
+
return cats;
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Drop settings.json keys that reference excluded category dirs so pi doesn't
|
|
669
|
+
* resolve dangling `./skills`, `./prompts`, `./extensions` paths. `packages`
|
|
670
|
+
* (general pi runtime deps) is left untouched — agents/skills may still use
|
|
671
|
+
* those tools even when a category dir is absent.
|
|
672
|
+
*/
|
|
673
|
+
function adaptSettingsJson(piDir, only) {
|
|
674
|
+
const path = join(piDir, "settings.json");
|
|
675
|
+
if (!existsSync(path)) return false;
|
|
676
|
+
let json;
|
|
677
|
+
try {
|
|
678
|
+
json = JSON.parse(readFileSync(path, "utf-8"));
|
|
679
|
+
} catch {
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
let changed = false;
|
|
683
|
+
for (const key of [
|
|
684
|
+
"skills",
|
|
685
|
+
"prompts",
|
|
686
|
+
"extensions"
|
|
687
|
+
]) if (!only.has(key) && key in json) {
|
|
688
|
+
delete json[key];
|
|
689
|
+
changed = true;
|
|
690
|
+
}
|
|
691
|
+
if (changed) writeFileSync(path, JSON.stringify(json, null, 2) + "\n");
|
|
692
|
+
return changed;
|
|
693
|
+
}
|
|
694
|
+
async function initCommand(options = {}) {
|
|
695
|
+
const quiet = process.argv.includes("--quiet");
|
|
696
|
+
if (!quiet) p.intro(color.bgCyan(color.black(" mdpi ")));
|
|
697
|
+
let only = null;
|
|
698
|
+
if (options.only) try {
|
|
699
|
+
only = parseOnly(options.only);
|
|
700
|
+
} catch (error) {
|
|
701
|
+
if (!quiet) {
|
|
702
|
+
p.log.error(error instanceof Error ? error.message : String(error));
|
|
703
|
+
p.outro(color.red("Failed"));
|
|
704
|
+
}
|
|
705
|
+
process.exitCode = 1;
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
const piDir = join(process.cwd(), ".pi");
|
|
709
|
+
if (!quiet) p.log.info(`Installing Pi kit to: ${color.cyan(piDir)}`);
|
|
710
|
+
if (existsSync(piDir) && !options.force) {
|
|
711
|
+
if (!quiet) {
|
|
712
|
+
p.log.warn(".pi/ already exists in this project");
|
|
713
|
+
p.log.info(`Use ${color.cyan("--force")} to overwrite template files (preserves extra user files)`);
|
|
714
|
+
p.outro("Nothing to do");
|
|
715
|
+
}
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
const templateRoot = getTemplateRoot();
|
|
719
|
+
if (!templateRoot) {
|
|
720
|
+
if (!quiet) {
|
|
721
|
+
p.log.error("Template not found. Please reinstall mdpi.");
|
|
722
|
+
p.outro(color.red("Failed"));
|
|
723
|
+
}
|
|
724
|
+
process.exitCode = 1;
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
const srcPi = join(templateRoot, ".pi");
|
|
728
|
+
if (!existsSync(srcPi)) {
|
|
729
|
+
if (!quiet) {
|
|
730
|
+
p.log.error("Template .pi/ not found");
|
|
731
|
+
p.outro(color.red("Failed"));
|
|
732
|
+
}
|
|
733
|
+
process.exitCode = 1;
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
const spinner = quiet ? null : p.spinner();
|
|
737
|
+
spinner?.start(only ? `Copying subset (${[...only].join(",")}) to .pi/` : "Copying kit to .pi/");
|
|
738
|
+
try {
|
|
739
|
+
if (only) {
|
|
740
|
+
for (const entry of ALWAYS_ENTRIES) await copyEntry(srcPi, piDir, entry);
|
|
741
|
+
for (const cat of CATEGORIES) if (only.has(cat)) await copyEntry(srcPi, piDir, cat);
|
|
742
|
+
} else await copyDir(srcPi, piDir);
|
|
743
|
+
} catch (error) {
|
|
744
|
+
spinner?.stop("Failed");
|
|
745
|
+
if (!quiet) {
|
|
746
|
+
p.log.error(error instanceof Error ? error.message : String(error));
|
|
747
|
+
p.outro(color.red("Failed"));
|
|
748
|
+
}
|
|
749
|
+
process.exitCode = 1;
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
spinner?.stop("Done");
|
|
753
|
+
const version = getPackageVersion();
|
|
754
|
+
writeFileSync(join(piDir, ".version"), version);
|
|
755
|
+
if (only) adaptSettingsJson(piDir, only);
|
|
756
|
+
const manifest = generateManifest(piDir, version);
|
|
757
|
+
const fileCount = Object.keys(manifest.files).length;
|
|
758
|
+
if (quiet) {
|
|
759
|
+
const subset = only ? ` subset=[${[...only].join(",")}]` : "";
|
|
760
|
+
console.log(`mdpi: installed ${fileCount} files to ${piDir} (v${version})${subset}`);
|
|
761
|
+
} else {
|
|
762
|
+
const subsetNote = only ? `Subset: ${[...only].join(", ")} (+ always-on config).\n\nsettings.json trimmed to drop references to excluded\nskills/prompts/extensions dirs.\n\nNote: mdpi upgrade compares against the FULL template —\nrun \`mdpi upgrade --check\` before applying.\n\n` : "";
|
|
763
|
+
p.note(`Pi kit installed at:\n${piDir}\n\n${fileCount} template files tracked via manifest.\n\n` + subsetNote + `Next: open pi in this repo to use the kit.`, "Installation complete");
|
|
764
|
+
p.outro(color.green("Ready!"));
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
//#endregion
|
|
768
|
+
//#region src/commands/new.ts
|
|
769
|
+
/**
|
|
770
|
+
* mdpi new — scaffold a new kit component from a lint-passing skeleton.
|
|
771
|
+
*
|
|
772
|
+
* mdpi new <kind> <name> [--description <text>] [--force]
|
|
773
|
+
*
|
|
774
|
+
* Kinds + destinations:
|
|
775
|
+
* skill → .pi/skills/<name>/SKILL.md
|
|
776
|
+
* prompt → .pi/prompts/<name>.md
|
|
777
|
+
* agent → .pi/agents/<name>.md
|
|
778
|
+
* workflow → .pi/workflows/<name>.md
|
|
779
|
+
* template → .pi/templates/<name>.md
|
|
780
|
+
*
|
|
781
|
+
* Skeletons are pre-shaped to pass `mdpi lint` immediately (skills get
|
|
782
|
+
* frontmatter + H1 + When to Use / NOT; workflows get the DAG phase format;
|
|
783
|
+
* prompts get the $ARGUMENTS + argument table). Idempotent: refuses to
|
|
784
|
+
* overwrite an existing file unless --force.
|
|
785
|
+
*/
|
|
786
|
+
const KINDS = [
|
|
787
|
+
"skill",
|
|
788
|
+
"prompt",
|
|
789
|
+
"agent",
|
|
790
|
+
"workflow",
|
|
791
|
+
"template"
|
|
792
|
+
];
|
|
793
|
+
/** Title-case a kebab name for H1s: "my-skill" → "My Skill". */
|
|
794
|
+
function titleCase(name) {
|
|
795
|
+
return name.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
796
|
+
}
|
|
797
|
+
/** Validate a kebab-case file/dir name (lowercase letters, digits, hyphens). */
|
|
798
|
+
function validName(name) {
|
|
799
|
+
return /^[a-z][a-z0-9-]*[a-z0-9]$|^[a-z]$/.test(name) && !name.includes("--");
|
|
800
|
+
}
|
|
801
|
+
/** Build the skeleton body for a kind, substituting name + description. */
|
|
802
|
+
function skeleton(kind, name, desc) {
|
|
803
|
+
const title = titleCase(name);
|
|
804
|
+
const description = desc || `TODO: when to use this ${kind}`;
|
|
805
|
+
switch (kind) {
|
|
806
|
+
case "skill": return `---
|
|
807
|
+
name: ${name}
|
|
808
|
+
description: ${description}
|
|
809
|
+
---
|
|
810
|
+
|
|
811
|
+
# ${title}
|
|
812
|
+
|
|
813
|
+
TODO: what this skill does and when to reach for it.
|
|
814
|
+
|
|
815
|
+
## When to Use
|
|
816
|
+
|
|
817
|
+
- TODO: trigger conditions and boundaries
|
|
818
|
+
|
|
819
|
+
## When NOT to Use
|
|
820
|
+
|
|
821
|
+
- TODO: when to avoid this skill (overlapping skills, anti-patterns)
|
|
822
|
+
|
|
823
|
+
## Procedure
|
|
824
|
+
|
|
825
|
+
1. TODO: first concrete step
|
|
826
|
+
2. TODO: second step
|
|
827
|
+
|
|
828
|
+
## Pitfalls
|
|
829
|
+
|
|
830
|
+
- TODO: common mistake / caveat
|
|
831
|
+
|
|
832
|
+
## Verification
|
|
833
|
+
|
|
834
|
+
- TODO: how to confirm success
|
|
835
|
+
`;
|
|
836
|
+
case "prompt": return `---
|
|
837
|
+
description: ${description}
|
|
838
|
+
argument-hint: "[<args>] [--help]"
|
|
839
|
+
---
|
|
840
|
+
|
|
841
|
+
# ${title}: $ARGUMENTS
|
|
842
|
+
|
|
843
|
+
TODO: what this slash command does.
|
|
844
|
+
|
|
845
|
+
## Parse Arguments
|
|
846
|
+
|
|
847
|
+
| Argument | Default | Description |
|
|
848
|
+
| ------------ | -------- | -------------------------- |
|
|
849
|
+
| \`<args>\` | required | TODO: what the user passes |
|
|
850
|
+
| \`--help\` | false | Show this usage |
|
|
851
|
+
|
|
852
|
+
## Phase 1: TODO
|
|
853
|
+
|
|
854
|
+
TODO: first step.
|
|
855
|
+
`;
|
|
856
|
+
case "agent": return `---
|
|
857
|
+
name: ${name}
|
|
858
|
+
description: ${description}
|
|
859
|
+
tools: read, grep, find, ls, bash, edit, write
|
|
860
|
+
model: ollama-cloud/glm-5.2
|
|
861
|
+
---
|
|
862
|
+
|
|
863
|
+
You are a Pi subagent.
|
|
864
|
+
|
|
865
|
+
# ${title}
|
|
866
|
+
|
|
867
|
+
**Purpose**: TODO — one line on what this agent is for.
|
|
868
|
+
|
|
869
|
+
## When to dispatch
|
|
870
|
+
|
|
871
|
+
TODO: trigger conditions.
|
|
872
|
+
|
|
873
|
+
## Constraints
|
|
874
|
+
|
|
875
|
+
- TODO: scope boundaries (files touched, tools allowed)
|
|
876
|
+
`;
|
|
877
|
+
case "workflow": return `---
|
|
878
|
+
description: ${description}
|
|
879
|
+
---
|
|
880
|
+
|
|
881
|
+
# ${name}
|
|
882
|
+
|
|
883
|
+
TODO: what this workflow does. Use when ...
|
|
884
|
+
|
|
885
|
+
## Args
|
|
886
|
+
|
|
887
|
+
- \`arg1\` (required) — TODO: what the caller passes
|
|
888
|
+
|
|
889
|
+
## Phases
|
|
890
|
+
|
|
891
|
+
### Phase 1: TODO
|
|
892
|
+
|
|
893
|
+
- **Agent:** general
|
|
894
|
+
- **Concurrency:** 1
|
|
895
|
+
- **Depends on:** —
|
|
896
|
+
- **Tool:** \`subagent\`
|
|
897
|
+
|
|
898
|
+
**Prompt:**
|
|
899
|
+
|
|
900
|
+
\`\`\`
|
|
901
|
+
TODO: prompt for phase 1.
|
|
902
|
+
\`\`\`
|
|
903
|
+
`;
|
|
904
|
+
case "template": return `# ${title} Template
|
|
905
|
+
|
|
906
|
+
> **Template Instructions:**
|
|
907
|
+
>
|
|
908
|
+
> - Replace ALL bracketed placeholders with real content
|
|
909
|
+
> - If you cannot fill a section, use \`[NEEDS CLARIFICATION: reason]\` instead of guessing
|
|
910
|
+
|
|
911
|
+
## Section 1
|
|
912
|
+
|
|
913
|
+
TODO: content.
|
|
914
|
+
`;
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
/** Resolve the destination path for a kind + name. */
|
|
918
|
+
function destPath(piDir, kind, name) {
|
|
919
|
+
switch (kind) {
|
|
920
|
+
case "skill": return join(piDir, "skills", name, "SKILL.md");
|
|
921
|
+
case "prompt": return join(piDir, "prompts", `${name}.md`);
|
|
922
|
+
case "agent": return join(piDir, "agents", `${name}.md`);
|
|
923
|
+
case "workflow": return join(piDir, "workflows", `${name}.md`);
|
|
924
|
+
case "template": return join(piDir, "templates", `${name}.md`);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
async function newCommand(kindArg, name, options = {}) {
|
|
928
|
+
const quiet = process.argv.includes("--quiet");
|
|
929
|
+
if (!quiet) p.intro(color.bgCyan(color.black(" mdpi new ")));
|
|
930
|
+
if (!KINDS.includes(kindArg)) {
|
|
931
|
+
if (!quiet) {
|
|
932
|
+
p.log.error(`unknown kind '${kindArg}'. Valid: ${KINDS.join(", ")}`);
|
|
933
|
+
p.outro(color.red("Failed"));
|
|
934
|
+
}
|
|
935
|
+
process.exitCode = 1;
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
const kind = kindArg;
|
|
939
|
+
if (!name || !validName(name)) {
|
|
940
|
+
if (!quiet) {
|
|
941
|
+
p.log.error(`invalid name '${name ?? ""}'. Use kebab-case (lowercase letters, digits, hyphens; no leading/trailing/double hyphen).`);
|
|
942
|
+
p.outro(color.red("Failed"));
|
|
943
|
+
}
|
|
944
|
+
process.exitCode = 1;
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
const piDir = join(process.cwd(), ".pi");
|
|
948
|
+
if (!existsSync(piDir)) {
|
|
949
|
+
if (!quiet) {
|
|
950
|
+
p.log.error(".pi/ not found — run `mdpi init` first");
|
|
951
|
+
p.outro(color.red("Failed"));
|
|
952
|
+
}
|
|
953
|
+
process.exitCode = 1;
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
const dest = destPath(piDir, kind, name);
|
|
957
|
+
if (existsSync(dest) && !options.force) {
|
|
958
|
+
if (!quiet) {
|
|
959
|
+
p.log.warn(`${dest} already exists`);
|
|
960
|
+
p.log.info(`Use ${color.cyan("--force")} to overwrite`);
|
|
961
|
+
p.outro("Nothing to do");
|
|
962
|
+
}
|
|
963
|
+
return;
|
|
964
|
+
}
|
|
965
|
+
mkdirSync(join(dest, ".."), { recursive: true });
|
|
966
|
+
writeFileSync(dest, skeleton(kind, name, options.description ?? ""));
|
|
967
|
+
if (quiet) console.log(`mdpi: created ${kind} '${name}' → ${dest}`);
|
|
968
|
+
else {
|
|
969
|
+
p.note(`Created: ${color.cyan(dest)}\n\nEdit the skeleton, then run ${color.cyan("mdpi lint")} to verify.`, "Scaffolded");
|
|
970
|
+
p.outro(color.green("Ready!"));
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
//#endregion
|
|
974
|
+
//#region src/commands/upgrade.ts
|
|
975
|
+
/**
|
|
976
|
+
* mdpi upgrade — bring an installed .pi/ kit up to the bundled template version.
|
|
977
|
+
*
|
|
978
|
+
* Manifest-based (no patch files): for each template file, compare the user's
|
|
979
|
+
* current file hash against the install-time manifest:
|
|
980
|
+
* - "unmodified" → safe to overwrite with the new template version
|
|
981
|
+
* - "modified" → user edited it → preserve (skip) unless --force
|
|
982
|
+
* - "unknown" → not in manifest (new template file, or no manifest) → add
|
|
983
|
+
* (or, with no manifest, preserve unless --force)
|
|
984
|
+
*
|
|
985
|
+
* Orphans = files in the manifest but no longer in the new template
|
|
986
|
+
* (template-removed). Listed by default; --prune-all deletes them.
|
|
987
|
+
*
|
|
988
|
+
* Flags: --check (dry-run), --force (overwrite modified), --prune-all (delete
|
|
989
|
+
* orphans). --quiet → 1-line machine output.
|
|
990
|
+
*/
|
|
991
|
+
const SKIP_DIRS = [
|
|
992
|
+
"node_modules",
|
|
993
|
+
".git",
|
|
994
|
+
"dist",
|
|
995
|
+
"coverage",
|
|
996
|
+
".next",
|
|
997
|
+
".turbo"
|
|
998
|
+
];
|
|
999
|
+
/**
|
|
1000
|
+
* Classify each template file against the install-time manifest.
|
|
1001
|
+
* Pure logic (no fs writes) — extracted so it can be unit-tested.
|
|
1002
|
+
*
|
|
1003
|
+
* unmodified → toUpdate (hash matches manifest)
|
|
1004
|
+
* modified → preserved unless force (user edited it)
|
|
1005
|
+
* unknown → toAdd if a manifest exists (new template file); with no
|
|
1006
|
+
* manifest, preserved unless force (can't detect edits)
|
|
1007
|
+
*/
|
|
1008
|
+
function classifyFiles(newFiles, piDir, manifest, force) {
|
|
1009
|
+
const toUpdate = [];
|
|
1010
|
+
const toAdd = [];
|
|
1011
|
+
const preserved = [];
|
|
1012
|
+
for (const rel of newFiles) {
|
|
1013
|
+
const status = fileModificationStatus(join(piDir, rel), rel, manifest);
|
|
1014
|
+
if (status === "unmodified") toUpdate.push(rel);
|
|
1015
|
+
else if (status === "modified") if (force) toUpdate.push(rel);
|
|
1016
|
+
else preserved.push(rel);
|
|
1017
|
+
else if (manifest) toAdd.push(rel);
|
|
1018
|
+
else if (force) toAdd.push(rel);
|
|
1019
|
+
else preserved.push(rel);
|
|
1020
|
+
}
|
|
1021
|
+
return {
|
|
1022
|
+
toUpdate,
|
|
1023
|
+
toAdd,
|
|
1024
|
+
preserved
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Orphans: files in the manifest but no longer in the new template
|
|
1029
|
+
* (template-removed since install). Excludes `.version` — it is mdpi-managed
|
|
1030
|
+
* (regenerated each init/upgrade), not a template file, so it must never be a
|
|
1031
|
+
* prune candidate. Returns [] when there is no manifest.
|
|
1032
|
+
*/
|
|
1033
|
+
function findOrphans(installedFiles, newFiles, manifest) {
|
|
1034
|
+
if (!manifest) return [];
|
|
1035
|
+
const newSet = new Set(newFiles);
|
|
1036
|
+
return installedFiles.filter((f) => !newSet.has(f) && f in manifest.files && f !== ".version");
|
|
1037
|
+
}
|
|
1038
|
+
async function upgradeCommand(options = {}) {
|
|
1039
|
+
const quiet = process.argv.includes("--quiet");
|
|
1040
|
+
if (!quiet) p.intro(color.bgCyan(color.black(" mdpi upgrade ")));
|
|
1041
|
+
const piDir = join(process.cwd(), ".pi");
|
|
1042
|
+
if (!existsSync(piDir)) {
|
|
1043
|
+
if (!quiet) {
|
|
1044
|
+
p.log.error(".pi/ not found — run `mdpi init` first");
|
|
1045
|
+
p.outro(color.red("Failed"));
|
|
1046
|
+
}
|
|
1047
|
+
process.exitCode = 1;
|
|
1048
|
+
return;
|
|
1049
|
+
}
|
|
1050
|
+
const templateRoot = getTemplateRoot();
|
|
1051
|
+
if (!templateRoot) {
|
|
1052
|
+
if (!quiet) {
|
|
1053
|
+
p.log.error("Template not found. Reinstall mdpi.");
|
|
1054
|
+
p.outro(color.red("Failed"));
|
|
1055
|
+
}
|
|
1056
|
+
process.exitCode = 1;
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
const srcPi = join(templateRoot, ".pi");
|
|
1060
|
+
const currentVersion = existsSync(join(piDir, ".version")) ? readFileSync(join(piDir, ".version"), "utf-8").trim() : "unknown";
|
|
1061
|
+
const targetVersion = getPackageVersion();
|
|
1062
|
+
const manifest = loadManifest(piDir);
|
|
1063
|
+
const newFiles = listFilesRel(srcPi, SKIP_DIRS, [MANIFEST_FILE]);
|
|
1064
|
+
const installedFiles = listFilesRel(piDir, SKIP_DIRS, [MANIFEST_FILE]);
|
|
1065
|
+
const { toUpdate, toAdd, preserved } = classifyFiles(newFiles, piDir, manifest, !!options.force);
|
|
1066
|
+
const orphans = findOrphans(installedFiles, newFiles, manifest);
|
|
1067
|
+
if (!quiet) {
|
|
1068
|
+
p.log.info(`v${currentVersion} → v${targetVersion}`);
|
|
1069
|
+
p.log.info(`update ${toUpdate.length} · add ${toAdd.length} · preserve ${preserved.length} (user-modified) · orphans ${orphans.length}`);
|
|
1070
|
+
if (preserved.length) p.log.warn(`preserved: ${preserved.slice(0, 5).join(", ")}${preserved.length > 5 ? " …" : ""}`);
|
|
1071
|
+
if (orphans.length && !options.pruneAll) p.log.warn(`orphans kept: ${orphans.slice(0, 5).join(", ")}${orphans.length > 5 ? " …" : ""} — use --prune-all to delete`);
|
|
1072
|
+
}
|
|
1073
|
+
if (currentVersion === targetVersion && toUpdate.length === 0 && toAdd.length === 0 && !options.force) {
|
|
1074
|
+
if (!quiet) p.outro(color.green("Already up to date"));
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
if (options.check) {
|
|
1078
|
+
if (!quiet) p.outro("dry-run (--check), no changes written");
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
if (!manifest && !options.force) {
|
|
1082
|
+
if (!quiet) {
|
|
1083
|
+
p.log.error("no manifest — cannot safely detect user modifications. Use --force to overwrite everything.");
|
|
1084
|
+
p.outro(color.red("Aborted"));
|
|
1085
|
+
}
|
|
1086
|
+
process.exitCode = 1;
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
const spinner = quiet ? null : p.spinner();
|
|
1090
|
+
spinner?.start("Applying upgrade");
|
|
1091
|
+
try {
|
|
1092
|
+
for (const rel of [...toUpdate, ...toAdd]) {
|
|
1093
|
+
const dest = join(piDir, rel);
|
|
1094
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
1095
|
+
cpSync(join(srcPi, rel), dest);
|
|
1096
|
+
}
|
|
1097
|
+
if (options.pruneAll) for (const rel of orphans) rmSync(join(piDir, rel));
|
|
1098
|
+
} catch (error) {
|
|
1099
|
+
spinner?.stop("Failed");
|
|
1100
|
+
if (!quiet) {
|
|
1101
|
+
p.log.error(error instanceof Error ? error.message : String(error));
|
|
1102
|
+
p.outro(color.red("Failed"));
|
|
1103
|
+
}
|
|
1104
|
+
process.exitCode = 1;
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
spinner?.stop("Done");
|
|
1108
|
+
writeFileSync(join(piDir, ".version"), targetVersion);
|
|
1109
|
+
const newManifest = generateManifest(piDir, targetVersion);
|
|
1110
|
+
const total = Object.keys(newManifest.files).length;
|
|
1111
|
+
const prunedNote = options.pruneAll ? ` · pruned ${orphans.length}` : "";
|
|
1112
|
+
if (quiet) console.log(`mdpi: upgraded v${currentVersion}→v${targetVersion} (${toUpdate.length} updated, ${toAdd.length} added${prunedNote}, ${total} tracked)`);
|
|
1113
|
+
else {
|
|
1114
|
+
p.note(`Upgraded v${currentVersion} → v${targetVersion}\n\n${toUpdate.length} updated · ${toAdd.length} added · ${preserved.length} preserved${prunedNote}\n${total} files now tracked.`, "Upgrade complete");
|
|
1115
|
+
p.outro(color.green("Ready!"));
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
//#endregion
|
|
1119
|
+
//#region src/utils/logger.ts
|
|
1120
|
+
const WEIGHT = {
|
|
1121
|
+
debug: 10,
|
|
1122
|
+
info: 20,
|
|
1123
|
+
warn: 30,
|
|
1124
|
+
error: 40
|
|
1125
|
+
};
|
|
1126
|
+
let threshold = "info";
|
|
1127
|
+
function setLogLevel(level) {
|
|
1128
|
+
threshold = level;
|
|
1129
|
+
}
|
|
1130
|
+
function emit(level, message) {
|
|
1131
|
+
if (WEIGHT[level] < WEIGHT[threshold]) return;
|
|
1132
|
+
const tag = level === "error" ? "error:" : level === "warn" ? "warn:" : level === "debug" ? "debug:" : "";
|
|
1133
|
+
if (level === "error") console.error(tag ? `${tag} ${message}` : message);
|
|
1134
|
+
else console.log(tag ? `${tag} ${message}` : message);
|
|
1135
|
+
}
|
|
1136
|
+
const logger = {
|
|
1137
|
+
debug: (msg) => emit("debug", msg),
|
|
1138
|
+
info: (msg) => emit("info", msg),
|
|
1139
|
+
warn: (msg) => emit("warn", msg),
|
|
1140
|
+
error: (msg) => emit("error", msg)
|
|
1141
|
+
};
|
|
1142
|
+
//#endregion
|
|
1143
|
+
//#region src/index.ts
|
|
1144
|
+
if (process.stdout.setEncoding) process.stdout.setEncoding("utf8");
|
|
1145
|
+
if (process.stderr.setEncoding) process.stderr.setEncoding("utf8");
|
|
1146
|
+
const packageVersion = version;
|
|
1147
|
+
const cli = cac("mdpi");
|
|
1148
|
+
cli.option("--verbose", "Enable verbose (debug) logging");
|
|
1149
|
+
cli.option("--quiet", "Suppress UI output");
|
|
1150
|
+
cli.version(`${packageVersion}`);
|
|
1151
|
+
cli.help();
|
|
1152
|
+
cli.command("init", "Scaffold a Pi coding-agent kit (.pi/) into the current repo").option("--force", "Overwrite existing .pi/ template files (preserves extra user files)").option("-y, --yes", "Skip prompts, use defaults (for CI)").option("--only <cats>", "Install only these categories (comma-sep: agents,prompts,skills,templates,workflows,context,extensions)").action(async (options) => {
|
|
1153
|
+
await initCommand(options);
|
|
1154
|
+
});
|
|
1155
|
+
cli.command("upgrade", "Bring .pi/ up to the bundled template version").option("--force", "Overwrite user-modified files (default: preserve them)").option("--check", "Dry-run: report what would change without writing").option("--prune-all", "Delete orphan files (template-removed since install)").action(async (options) => {
|
|
1156
|
+
await upgradeCommand(options);
|
|
1157
|
+
});
|
|
1158
|
+
cli.command("new <kind> [name]", "Scaffold a new kit component (skill|prompt|agent|workflow|template)").option("-d, --description <text>", "Set the frontmatter description (default: TODO placeholder)").option("--force", "Overwrite an existing file at the destination").action(async (kind, name, options) => {
|
|
1159
|
+
await newCommand(kind, name, {
|
|
1160
|
+
force: options?.force,
|
|
1161
|
+
description: options?.description
|
|
1162
|
+
});
|
|
1163
|
+
});
|
|
1164
|
+
cli.command("lint [target]", "Governance checks (skills|docs|all)").option("--json", "Output machine-readable JSON").option("--fix", "Auto-fix fixable issues (name-match, README counts)").action(async (target, options) => {
|
|
1165
|
+
lintAll(join(process.cwd(), ".pi"), {
|
|
1166
|
+
target: target ?? "all",
|
|
1167
|
+
json: options?.json,
|
|
1168
|
+
fix: options?.fix
|
|
1169
|
+
});
|
|
1170
|
+
});
|
|
1171
|
+
cli.command("doctor", "Check .pi/ health").option("--fix", "Auto-fix: regenerate manifest if missing/invalid").action(async (options) => {
|
|
1172
|
+
await doctorCommand({ fix: options?.fix });
|
|
1173
|
+
});
|
|
1174
|
+
cli.command("", "Show help").action(() => {
|
|
1175
|
+
cli.outputHelp();
|
|
1176
|
+
});
|
|
1177
|
+
try {
|
|
1178
|
+
const argv = process.argv.slice(2);
|
|
1179
|
+
if (argv.includes("--verbose")) setLogLevel("debug");
|
|
1180
|
+
if (argv.includes("--quiet")) setLogLevel("error");
|
|
1181
|
+
cli.parse(process.argv);
|
|
1182
|
+
} catch (error) {
|
|
1183
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
1184
|
+
process.exit(1);
|
|
1185
|
+
}
|
|
1186
|
+
//#endregion
|
|
1187
|
+
export { cli };
|