quiver-cli 0.1.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 +188 -0
- package/bin/quiver-cli.mjs +2 -0
- package/dist/cli.js +3074 -0
- package/package.json +55 -0
- package/template/.agents/AGENTS.md +25 -0
- package/template/.agents/commands/cp.md +116 -0
- package/template/.agents/commands/next-setup.md +1064 -0
- package/template/.agents/commands/tf-readme.md +38 -0
- package/template/.agents/config.json +60 -0
- package/template/.agents/skills/agent-browser/SKILL.md +55 -0
- package/template/.agents/skills/apps/skybridge/SKILL.md +46 -0
- package/template/.agents/skills/apps/skybridge/references/architecture.md +175 -0
- package/template/.agents/skills/apps/skybridge/references/copy-template.md +24 -0
- package/template/.agents/skills/apps/skybridge/references/csp.md +33 -0
- package/template/.agents/skills/apps/skybridge/references/deploy.md +33 -0
- package/template/.agents/skills/apps/skybridge/references/discover.md +84 -0
- package/template/.agents/skills/apps/skybridge/references/download-file.md +77 -0
- package/template/.agents/skills/apps/skybridge/references/fetch-and-render-data.md +151 -0
- package/template/.agents/skills/apps/skybridge/references/oauth.md +115 -0
- package/template/.agents/skills/apps/skybridge/references/open-external-links.md +71 -0
- package/template/.agents/skills/apps/skybridge/references/prompt-llm.md +20 -0
- package/template/.agents/skills/apps/skybridge/references/publish.md +19 -0
- package/template/.agents/skills/apps/skybridge/references/run-locally.md +51 -0
- package/template/.agents/skills/apps/skybridge/references/state-and-context.md +151 -0
- package/template/.agents/skills/apps/skybridge/references/ui-guidelines.md +205 -0
- package/template/.agents/skills/code/cleanup/SKILL.md +26 -0
- package/template/.agents/skills/code/vercel-react-best-practices/AGENTS.md +3810 -0
- package/template/.agents/skills/code/vercel-react-best-practices/README.md +123 -0
- package/template/.agents/skills/code/vercel-react-best-practices/SKILL.md +149 -0
- package/template/.agents/skills/code/vercel-react-best-practices/metadata.json +15 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/_sections.md +46 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/_template.md +28 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/advanced-effect-event-deps.md +56 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/advanced-init-once.md +42 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/advanced-use-latest.md +39 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/async-cheap-condition-before-await.md +37 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/async-defer-await.md +82 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/async-dependencies.md +51 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-analyzable-paths.md +63 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-barrel-imports.md +60 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-batch-dom-css.md +107 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-flatmap-filter.md +60 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-request-idle-callback.md +105 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-resource-hints.md +85 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-script-defer-async.md +68 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rendering-usetransition-loading.md +75 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-no-inline-components.md +82 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-split-combined-hooks.md +64 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-use-deferred-value.md +59 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/server-auth-actions.md +96 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/server-cache-react.md +76 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/server-dedup-props.md +65 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/server-hoist-static-io.md +149 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/server-no-shared-module-state.md +50 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/server-parallel-nested-fetching.md +34 -0
- package/template/.agents/skills/code/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/template/.agents/skills/data/prisma-cli/SKILL.md +247 -0
- package/template/.agents/skills/data/prisma-cli/references/db-execute.md +78 -0
- package/template/.agents/skills/data/prisma-cli/references/db-pull.md +185 -0
- package/template/.agents/skills/data/prisma-cli/references/db-push.md +148 -0
- package/template/.agents/skills/data/prisma-cli/references/db-seed.md +188 -0
- package/template/.agents/skills/data/prisma-cli/references/debug.md +46 -0
- package/template/.agents/skills/data/prisma-cli/references/dev.md +157 -0
- package/template/.agents/skills/data/prisma-cli/references/format.md +48 -0
- package/template/.agents/skills/data/prisma-cli/references/generate.md +173 -0
- package/template/.agents/skills/data/prisma-cli/references/init.md +136 -0
- package/template/.agents/skills/data/prisma-cli/references/mcp.md +38 -0
- package/template/.agents/skills/data/prisma-cli/references/migrate-deploy.md +127 -0
- package/template/.agents/skills/data/prisma-cli/references/migrate-dev.md +145 -0
- package/template/.agents/skills/data/prisma-cli/references/migrate-diff.md +89 -0
- package/template/.agents/skills/data/prisma-cli/references/migrate-reset.md +78 -0
- package/template/.agents/skills/data/prisma-cli/references/migrate-resolve.md +57 -0
- package/template/.agents/skills/data/prisma-cli/references/migrate-status.md +65 -0
- package/template/.agents/skills/data/prisma-cli/references/studio.md +137 -0
- package/template/.agents/skills/data/prisma-cli/references/validate.md +53 -0
- package/template/.agents/skills/data/prisma-client-api/SKILL.md +216 -0
- package/template/.agents/skills/data/prisma-client-api/references/client-methods.md +223 -0
- package/template/.agents/skills/data/prisma-client-api/references/constructor.md +208 -0
- package/template/.agents/skills/data/prisma-client-api/references/filters.md +256 -0
- package/template/.agents/skills/data/prisma-client-api/references/model-queries.md +281 -0
- package/template/.agents/skills/data/prisma-client-api/references/query-options.md +276 -0
- package/template/.agents/skills/data/prisma-client-api/references/raw-queries.md +194 -0
- package/template/.agents/skills/data/prisma-client-api/references/relations.md +308 -0
- package/template/.agents/skills/data/prisma-client-api/references/transactions.md +184 -0
- package/template/.agents/skills/design/impeccable/SKILL.md +176 -0
- package/template/.agents/skills/design/impeccable/reference/adapt.md +311 -0
- package/template/.agents/skills/design/impeccable/reference/animate.md +201 -0
- package/template/.agents/skills/design/impeccable/reference/audit.md +133 -0
- package/template/.agents/skills/design/impeccable/reference/bolder.md +113 -0
- package/template/.agents/skills/design/impeccable/reference/brand.md +108 -0
- package/template/.agents/skills/design/impeccable/reference/clarify.md +288 -0
- package/template/.agents/skills/design/impeccable/reference/codex.md +105 -0
- package/template/.agents/skills/design/impeccable/reference/colorize.md +257 -0
- package/template/.agents/skills/design/impeccable/reference/craft.md +123 -0
- package/template/.agents/skills/design/impeccable/reference/critique.md +767 -0
- package/template/.agents/skills/design/impeccable/reference/delight.md +302 -0
- package/template/.agents/skills/design/impeccable/reference/distill.md +111 -0
- package/template/.agents/skills/design/impeccable/reference/document.md +429 -0
- package/template/.agents/skills/design/impeccable/reference/extract.md +69 -0
- package/template/.agents/skills/design/impeccable/reference/harden.md +347 -0
- package/template/.agents/skills/design/impeccable/reference/init.md +172 -0
- package/template/.agents/skills/design/impeccable/reference/interaction-design.md +189 -0
- package/template/.agents/skills/design/impeccable/reference/layout.md +161 -0
- package/template/.agents/skills/design/impeccable/reference/live.md +718 -0
- package/template/.agents/skills/design/impeccable/reference/onboard.md +234 -0
- package/template/.agents/skills/design/impeccable/reference/optimize.md +258 -0
- package/template/.agents/skills/design/impeccable/reference/overdrive.md +130 -0
- package/template/.agents/skills/design/impeccable/reference/polish.md +241 -0
- package/template/.agents/skills/design/impeccable/reference/product.md +60 -0
- package/template/.agents/skills/design/impeccable/reference/quieter.md +99 -0
- package/template/.agents/skills/design/impeccable/reference/shape.md +165 -0
- package/template/.agents/skills/design/impeccable/reference/typeset.md +279 -0
- package/template/.agents/skills/design/impeccable/scripts/cleanup-deprecated.mjs +284 -0
- package/template/.agents/skills/design/impeccable/scripts/command-metadata.json +94 -0
- package/template/.agents/skills/design/impeccable/scripts/context-signals.mjs +225 -0
- package/template/.agents/skills/design/impeccable/scripts/context.mjs +270 -0
- package/template/.agents/skills/design/impeccable/scripts/critique-storage.mjs +242 -0
- package/template/.agents/skills/design/impeccable/scripts/design-parser.mjs +835 -0
- package/template/.agents/skills/design/impeccable/scripts/detect-csp.mjs +198 -0
- package/template/.agents/skills/design/impeccable/scripts/detect.mjs +21 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/browser/injected/index.mjs +1733 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/cli/main.mjs +244 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns-browser.js +4551 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/detect-antipatterns.mjs +43 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/browser/detect-url.mjs +252 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/regex/detect-text.mjs +535 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +986 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/static-html/detect-html.mjs +208 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/engines/visual/screenshot-contrast.mjs +189 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/findings.mjs +12 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/node/file-system.mjs +198 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/profile/profiler.mjs +166 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/registry/antipatterns.mjs +419 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/rules/checks.mjs +2316 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/color.mjs +124 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/constants.mjs +101 -0
- package/template/.agents/skills/design/impeccable/scripts/detector/shared/page.mjs +7 -0
- package/template/.agents/skills/design/impeccable/scripts/impeccable-paths.mjs +126 -0
- package/template/.agents/skills/design/impeccable/scripts/is-generated.mjs +69 -0
- package/template/.agents/skills/design/impeccable/scripts/live-accept.mjs +812 -0
- package/template/.agents/skills/design/impeccable/scripts/live-browser-session.js +123 -0
- package/template/.agents/skills/design/impeccable/scripts/live-browser.js +10316 -0
- package/template/.agents/skills/design/impeccable/scripts/live-commit-manual-edits.mjs +1241 -0
- package/template/.agents/skills/design/impeccable/scripts/live-complete.mjs +75 -0
- package/template/.agents/skills/design/impeccable/scripts/live-completion.mjs +19 -0
- package/template/.agents/skills/design/impeccable/scripts/live-copy-edit-agent.mjs +683 -0
- package/template/.agents/skills/design/impeccable/scripts/live-discard-manual-edits.mjs +51 -0
- package/template/.agents/skills/design/impeccable/scripts/live-event-validation.mjs +136 -0
- package/template/.agents/skills/design/impeccable/scripts/live-inject.mjs +557 -0
- package/template/.agents/skills/design/impeccable/scripts/live-insert-ui.mjs +458 -0
- package/template/.agents/skills/design/impeccable/scripts/live-insert.mjs +272 -0
- package/template/.agents/skills/design/impeccable/scripts/live-manual-edit-evidence.mjs +363 -0
- package/template/.agents/skills/design/impeccable/scripts/live-manual-edits-buffer.mjs +152 -0
- package/template/.agents/skills/design/impeccable/scripts/live-poll.mjs +379 -0
- package/template/.agents/skills/design/impeccable/scripts/live-resume.mjs +94 -0
- package/template/.agents/skills/design/impeccable/scripts/live-server.mjs +2322 -0
- package/template/.agents/skills/design/impeccable/scripts/live-session-store.mjs +289 -0
- package/template/.agents/skills/design/impeccable/scripts/live-status.mjs +61 -0
- package/template/.agents/skills/design/impeccable/scripts/live-svelte-component.mjs +826 -0
- package/template/.agents/skills/design/impeccable/scripts/live-sveltekit-adapter.mjs +274 -0
- package/template/.agents/skills/design/impeccable/scripts/live-ui-core.mjs +179 -0
- package/template/.agents/skills/design/impeccable/scripts/live-wrap.mjs +894 -0
- package/template/.agents/skills/design/impeccable/scripts/live.mjs +246 -0
- package/template/.agents/skills/design/impeccable/scripts/modern-screenshot.umd.js +14 -0
- package/template/.agents/skills/design/impeccable/scripts/palette.mjs +633 -0
- package/template/.agents/skills/design/impeccable/scripts/pin.mjs +214 -0
- package/template/.agents/skills/design/shadcn/SKILL.md +242 -0
- package/template/.agents/skills/design/shadcn/agents/openai.yml +5 -0
- package/template/.agents/skills/design/shadcn/assets/shadcn-small.png +0 -0
- package/template/.agents/skills/design/shadcn/assets/shadcn.png +0 -0
- package/template/.agents/skills/design/shadcn/cli.md +257 -0
- package/template/.agents/skills/design/shadcn/customization.md +202 -0
- package/template/.agents/skills/design/shadcn/evals/evals.json +47 -0
- package/template/.agents/skills/design/shadcn/mcp.md +94 -0
- package/template/.agents/skills/design/shadcn/rules/base-vs-radix.md +306 -0
- package/template/.agents/skills/design/shadcn/rules/composition.md +195 -0
- package/template/.agents/skills/design/shadcn/rules/forms.md +192 -0
- package/template/.agents/skills/design/shadcn/rules/icons.md +101 -0
- package/template/.agents/skills/design/shadcn/rules/styling.md +162 -0
- package/template/.agents/skills/find-skills/SKILL.md +142 -0
- package/template/.agents/skills/integrations/langfuse/SKILL.md +142 -0
- package/template/.agents/skills/integrations/langfuse/references/cli.md +52 -0
- package/template/.agents/skills/integrations/langfuse/references/error-analysis.md +100 -0
- package/template/.agents/skills/integrations/langfuse/references/instrumentation.md +134 -0
- package/template/.agents/skills/integrations/langfuse/references/judge-calibration.md +288 -0
- package/template/.agents/skills/integrations/langfuse/references/prompt-migration.md +234 -0
- package/template/.agents/skills/integrations/langfuse/references/sdk-upgrade.md +175 -0
- package/template/.agents/skills/integrations/langfuse/references/skill-feedback.md +52 -0
- package/template/.agents/skills/integrations/langfuse/references/user-feedback.md +88 -0
- package/template/.agents/skills/integrations/posthog/SKILL.md +102 -0
- package/template/.agents/skills/integrations/posthog/references/error-tracking-alerts.md +63 -0
- package/template/.agents/skills/integrations/posthog/references/error-tracking-assigning-issues.md +77 -0
- package/template/.agents/skills/integrations/posthog/references/error-tracking-fingerprints.md +57 -0
- package/template/.agents/skills/integrations/posthog/references/error-tracking-monitoring.md +140 -0
- package/template/.agents/skills/integrations/posthog/references/error-tracking-nextjs.md +490 -0
- package/template/.agents/skills/integrations/posthog/references/error-tracking-source-maps.md +45 -0
- package/template/.agents/skills/integrations/posthog/references/feature-flags-best-practices.md +139 -0
- package/template/.agents/skills/integrations/posthog/references/feature-flags-react.md +302 -0
- package/template/.agents/skills/integrations/posthog/references/identify-users.md +202 -0
- package/template/.agents/skills/integrations/posthog/references/integration-example.md +706 -0
- package/template/.agents/skills/integrations/posthog/references/integration-nextjs.md +385 -0
- package/template/.agents/skills/integrations/posthog/references/integration-step-1-begin.md +43 -0
- package/template/.agents/skills/integrations/posthog/references/integration-step-2-edit.md +37 -0
- package/template/.agents/skills/integrations/posthog/references/integration-step-3-revise.md +22 -0
- package/template/.agents/skills/integrations/posthog/references/integration-step-4-conclude.md +38 -0
- package/template/.agents/skills/integrations/posthog/references/llm-analytics-anthropic.md +200 -0
- package/template/.agents/skills/integrations/posthog/references/llm-analytics-basics.md +62 -0
- package/template/.agents/skills/integrations/posthog/references/llm-analytics-costs.md +197 -0
- package/template/.agents/skills/integrations/posthog/references/llm-analytics-manual-capture.md +397 -0
- package/template/.agents/skills/integrations/posthog/references/llm-analytics-traces.md +98 -0
- package/template/.agents/skills/integrations/posthog/references/llm-analytics-vercel-ai.md +120 -0
- package/template/.agents/skills/repo/repo-ci/SKILL.md +265 -0
- package/template/.agents/skills/repo/repo-init-next-js/SKILL.md +129 -0
- package/template/.agents/skills/repo/repo-init-next-js/references/file-contents.md +800 -0
- package/template/.agents/skills/repo/repo-init-next-js/scripts/setup.sh +47 -0
- package/template/.agents/skills/repo/repo-init-node/SKILL.md +196 -0
- package/template/.agents/skills/skill-creator/LICENSE.txt +202 -0
- package/template/.agents/skills/skill-creator/SKILL.md +485 -0
- package/template/.agents/skills/skill-creator/agents/analyzer.md +274 -0
- package/template/.agents/skills/skill-creator/agents/comparator.md +202 -0
- package/template/.agents/skills/skill-creator/agents/grader.md +223 -0
- package/template/.agents/skills/skill-creator/assets/eval_review.html +146 -0
- package/template/.agents/skills/skill-creator/eval-viewer/generate_review.py +471 -0
- package/template/.agents/skills/skill-creator/eval-viewer/viewer.html +1325 -0
- package/template/.agents/skills/skill-creator/references/schemas.md +430 -0
- package/template/.agents/skills/skill-creator/scripts/__init__.py +0 -0
- package/template/.agents/skills/skill-creator/scripts/aggregate_benchmark.py +401 -0
- package/template/.agents/skills/skill-creator/scripts/generate_report.py +326 -0
- package/template/.agents/skills/skill-creator/scripts/improve_description.py +247 -0
- package/template/.agents/skills/skill-creator/scripts/package_skill.py +136 -0
- package/template/.agents/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/template/.agents/skills/skill-creator/scripts/run_eval.py +310 -0
- package/template/.agents/skills/skill-creator/scripts/run_loop.py +328 -0
- package/template/.agents/skills/skill-creator/scripts/utils.py +47 -0
- package/template/.agents/upstreams.json +80 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Context-signals gatherer for the bare `{{command_prefix}}impeccable`
|
|
4
|
+
* (no-argument) path. Collects cheap, deterministic signals about the current
|
|
5
|
+
* project and emits them as JSON.
|
|
6
|
+
*
|
|
7
|
+
* It does NOT score or rank. The agent reasons over the raw signals using its
|
|
8
|
+
* knowledge of the command catalog (see SKILL.md routing rule 1). Deliberately
|
|
9
|
+
* light: no LLM calls, no detector run (`npx impeccable detect` is heavier and
|
|
10
|
+
* opt-in), no file writes. Every probe is best-effort and never throws; the
|
|
11
|
+
* output is always valid JSON.
|
|
12
|
+
*
|
|
13
|
+
* Signals:
|
|
14
|
+
* - setup: PRODUCT.md / DESIGN.md presence, register, whether code exists
|
|
15
|
+
* - critique: the latest cached critique score (.impeccable/critique)
|
|
16
|
+
* - git: branch + files changed vs the default branch (a scope hint)
|
|
17
|
+
* - devServer: whether a local dev server answers on a common port (gates live)
|
|
18
|
+
*/
|
|
19
|
+
import fs from 'node:fs';
|
|
20
|
+
import net from 'node:net';
|
|
21
|
+
import path from 'node:path';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
23
|
+
import { execFileSync } from 'node:child_process';
|
|
24
|
+
import { loadContext, extractRegister } from './context.mjs';
|
|
25
|
+
import { getCritiqueDir } from './impeccable-paths.mjs';
|
|
26
|
+
|
|
27
|
+
/** Is there code here at all, or just context files / an empty repo? */
|
|
28
|
+
function hasCode(cwd) {
|
|
29
|
+
if (fs.existsSync(path.join(cwd, 'package.json'))) return true;
|
|
30
|
+
for (const d of ['src', 'app', 'pages', 'site', 'public', 'components', 'lib']) {
|
|
31
|
+
if (fs.existsSync(path.join(cwd, d))) return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The most recent critique snapshot across all targets. Filenames are
|
|
38
|
+
* timestamp-prefixed (`<iso>__<slug>.md`), so a lexical sort is chronological.
|
|
39
|
+
* Parses the small frontmatter for score + P0/P1 counts.
|
|
40
|
+
*/
|
|
41
|
+
function latestCritique(cwd) {
|
|
42
|
+
try {
|
|
43
|
+
const dir = getCritiqueDir(cwd);
|
|
44
|
+
if (!fs.existsSync(dir)) return null;
|
|
45
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md')).sort();
|
|
46
|
+
if (!files.length) return null;
|
|
47
|
+
const newest = files[files.length - 1];
|
|
48
|
+
const text = fs.readFileSync(path.join(dir, newest), 'utf-8');
|
|
49
|
+
const front = text.split('---')[1] || '';
|
|
50
|
+
const get = (k) => {
|
|
51
|
+
const m = front.match(new RegExp(`^${k}:\\s*(.+)$`, 'm'));
|
|
52
|
+
return m ? m[1].trim() : null;
|
|
53
|
+
};
|
|
54
|
+
const num = (v) => {
|
|
55
|
+
const n = Number(v);
|
|
56
|
+
return Number.isFinite(n) ? n : null;
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
slug: get('slug'),
|
|
60
|
+
score: num(get('score')),
|
|
61
|
+
p0: num(get('p0')),
|
|
62
|
+
p1: num(get('p1')),
|
|
63
|
+
timestamp: get('timestamp'),
|
|
64
|
+
file: path.relative(cwd, path.join(dir, newest)),
|
|
65
|
+
};
|
|
66
|
+
} catch {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Branch + a scope hint: files changed vs the default branch, else working tree. */
|
|
72
|
+
function gitSignals(cwd) {
|
|
73
|
+
const run = (args, { trim = true } = {}) => {
|
|
74
|
+
try {
|
|
75
|
+
const out = execFileSync('git', args, {
|
|
76
|
+
cwd,
|
|
77
|
+
encoding: 'utf-8',
|
|
78
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
79
|
+
});
|
|
80
|
+
return trim ? out.trim() : out;
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
if (run(['rev-parse', '--is-inside-work-tree']) !== 'true') {
|
|
86
|
+
return { isRepo: false, branch: null, base: null, changedFiles: [], changedCount: 0 };
|
|
87
|
+
}
|
|
88
|
+
const branch = run(['rev-parse', '--abbrev-ref', 'HEAD']);
|
|
89
|
+
let base = null;
|
|
90
|
+
for (const b of ['main', 'master']) {
|
|
91
|
+
if (run(['rev-parse', '--verify', '--quiet', b]) !== null) {
|
|
92
|
+
base = b;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const diffBase = base && branch && branch !== base ? base : null;
|
|
97
|
+
const fromDiff = diffBase ? run(['diff', '--name-only', `${diffBase}...HEAD`]) : null;
|
|
98
|
+
// porcelain lines are `XY PATH`: a 2-char status + a space, then the path.
|
|
99
|
+
// Don't trim the combined output — an unstaged-modified line starts with a
|
|
100
|
+
// leading space (` M path`), and a global trim would eat the first line's
|
|
101
|
+
// status column and shift the slice. Renames render as `old -> new`.
|
|
102
|
+
const fromStatus = run(['-c', 'core.quotepath=false', 'status', '--porcelain'], { trim: false });
|
|
103
|
+
let changed = [];
|
|
104
|
+
if (fromDiff) {
|
|
105
|
+
changed = fromDiff.split('\n').filter(Boolean);
|
|
106
|
+
} else if (fromStatus) {
|
|
107
|
+
changed = fromStatus.split(/\r?\n/).filter(Boolean).map((l) => {
|
|
108
|
+
const p = l.slice(3);
|
|
109
|
+
const arrow = p.indexOf(' -> ');
|
|
110
|
+
return arrow === -1 ? p : p.slice(arrow + 4);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
isRepo: true,
|
|
115
|
+
branch,
|
|
116
|
+
base: diffBase,
|
|
117
|
+
changedFiles: changed.slice(0, 50),
|
|
118
|
+
changedCount: changed.length,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const COMMON_DEV_PORTS = [4321, 3000, 5173, 5174, 8080, 8000, 4200];
|
|
123
|
+
|
|
124
|
+
function probePort(port, timeout = 250) {
|
|
125
|
+
return new Promise((resolve) => {
|
|
126
|
+
const sock = new net.Socket();
|
|
127
|
+
let settled = false;
|
|
128
|
+
const finish = (ok) => {
|
|
129
|
+
if (settled) return;
|
|
130
|
+
settled = true;
|
|
131
|
+
try { sock.destroy(); } catch { /* ignore */ }
|
|
132
|
+
resolve(ok);
|
|
133
|
+
};
|
|
134
|
+
sock.setTimeout(timeout);
|
|
135
|
+
sock.once('connect', () => finish(true));
|
|
136
|
+
sock.once('timeout', () => finish(false));
|
|
137
|
+
sock.once('error', () => finish(false));
|
|
138
|
+
sock.connect(port, '127.0.0.1');
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function devServerSignals() {
|
|
143
|
+
const open = [];
|
|
144
|
+
await Promise.all(
|
|
145
|
+
COMMON_DEV_PORTS.map(async (p) => {
|
|
146
|
+
if (await probePort(p)) open.push(p);
|
|
147
|
+
}),
|
|
148
|
+
);
|
|
149
|
+
open.sort((a, b) => a - b);
|
|
150
|
+
return { running: open.length > 0, ports: open };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Extensions the detector scans (mirrors the engine's walkDir set + HTML).
|
|
154
|
+
const SCANNABLE_EXT = new Set([
|
|
155
|
+
'.html', '.htm', '.css', '.scss',
|
|
156
|
+
'.jsx', '.tsx', '.js', '.ts', '.vue', '.svelte', '.astro',
|
|
157
|
+
]);
|
|
158
|
+
// Where UI source typically lives. The detector walks these and skips
|
|
159
|
+
// node_modules / dist / build / .next / .nuxt automatically.
|
|
160
|
+
const SOURCE_DIRS = ['src', 'app', 'components', 'pages', 'public'];
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Local paths the agent should point the bundled detector at — never a URL.
|
|
164
|
+
* A URL means a costly Puppeteer browser render, and a probed dev-server port
|
|
165
|
+
* may not even belong to this project. An HTML *file* or a source tree is
|
|
166
|
+
* scanned by the cheap, jsdom-free static engine. This script does NOT run the
|
|
167
|
+
* detector; it just surfaces the target(s) so the agent can run
|
|
168
|
+
* `node <scripts>/detect.mjs --json <targets>` and fold the hits in.
|
|
169
|
+
*/
|
|
170
|
+
function scanTargets(cwd, git) {
|
|
171
|
+
// 1. Dirty tree wins: scan exactly the markup/style files in flight. It's
|
|
172
|
+
// what the user is working on, it's a small set, and it's local.
|
|
173
|
+
if (git.isRepo && git.changedFiles.length) {
|
|
174
|
+
const changed = git.changedFiles
|
|
175
|
+
.filter((f) => SCANNABLE_EXT.has(path.extname(f).toLowerCase()))
|
|
176
|
+
.filter((f) => fs.existsSync(path.join(cwd, f)));
|
|
177
|
+
if (changed.length) return { targets: changed.slice(0, 50), via: 'git-changes' };
|
|
178
|
+
}
|
|
179
|
+
// 2. Otherwise scan the local source dirs that exist.
|
|
180
|
+
const dirs = SOURCE_DIRS.filter((d) => fs.existsSync(path.join(cwd, d)));
|
|
181
|
+
if (dirs.length) return { targets: dirs, via: 'source-dir' };
|
|
182
|
+
// 3. A root HTML entry, or the project root as a last resort when there's
|
|
183
|
+
// code but no conventional source dir (walkDir still skips heavy dirs).
|
|
184
|
+
if (fs.existsSync(path.join(cwd, 'index.html'))) return { targets: ['index.html'], via: 'html' };
|
|
185
|
+
if (hasCode(cwd)) return { targets: ['.'], via: 'root' };
|
|
186
|
+
return { targets: [], via: null };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export async function gatherSignals(cwd = process.cwd()) {
|
|
190
|
+
const ctx = loadContext(cwd);
|
|
191
|
+
const git = gitSignals(cwd);
|
|
192
|
+
return {
|
|
193
|
+
setup: {
|
|
194
|
+
hasProduct: ctx.hasProduct,
|
|
195
|
+
productPath: ctx.productPath,
|
|
196
|
+
hasDesign: ctx.hasDesign,
|
|
197
|
+
designPath: ctx.designPath,
|
|
198
|
+
hasCode: hasCode(cwd),
|
|
199
|
+
register: extractRegister(ctx.product),
|
|
200
|
+
},
|
|
201
|
+
critique: { latest: latestCritique(cwd) },
|
|
202
|
+
git,
|
|
203
|
+
devServer: await devServerSignals(),
|
|
204
|
+
scan: scanTargets(cwd, git),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function cli() {
|
|
209
|
+
const signals = await gatherSignals(process.cwd());
|
|
210
|
+
process.stdout.write(`${JSON.stringify(signals, null, 2)}\n`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function invokedAsScript() {
|
|
214
|
+
const arg = process.argv[1];
|
|
215
|
+
if (!arg) return false;
|
|
216
|
+
try {
|
|
217
|
+
return fs.realpathSync(arg) === fs.realpathSync(fileURLToPath(import.meta.url));
|
|
218
|
+
} catch {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (invokedAsScript()) {
|
|
224
|
+
cli();
|
|
225
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context loader: prints PRODUCT.md (and DESIGN.md if present) as one
|
|
3
|
+
* markdown block on stdout, or exits with empty stdout when no PRODUCT.md
|
|
4
|
+
* is found anywhere. The skill keys off "empty stdout" to branch into the
|
|
5
|
+
* init flow.
|
|
6
|
+
*
|
|
7
|
+
* Path resolution (first match wins):
|
|
8
|
+
* 1. cwd, if PRODUCT.md or DESIGN.md is there
|
|
9
|
+
* 2. .agents/context/ then docs/
|
|
10
|
+
* 3. $IMPECCABLE_CONTEXT_DIR (absolute or cwd-relative) — power-user
|
|
11
|
+
* escape hatch, only consulted when defaults are empty
|
|
12
|
+
* 4. cwd as a "nothing found" default
|
|
13
|
+
*
|
|
14
|
+
* `resolveContextDir()` and `loadContext()` are also exported for the
|
|
15
|
+
* server-side scripts (live.mjs, live-server.mjs) that need the structured
|
|
16
|
+
* shape rather than the markdown block.
|
|
17
|
+
*/
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import os from 'node:os';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { fileURLToPath } from 'node:url';
|
|
22
|
+
|
|
23
|
+
const PRODUCT_NAMES = ['PRODUCT.md', 'Product.md', 'product.md'];
|
|
24
|
+
const DESIGN_NAMES = ['DESIGN.md', 'Design.md', 'design.md'];
|
|
25
|
+
const FALLBACK_DIRS = ['.agents/context', 'docs'];
|
|
26
|
+
|
|
27
|
+
// ─── Update check ──────────────────────────────────────────────────────────
|
|
28
|
+
// Piggyback a lightweight skill-version check on the once-per-session boot.
|
|
29
|
+
// When a newer skill ships, append an UPDATE_AVAILABLE directive so the agent
|
|
30
|
+
// can offer `npx impeccable skills update`. Everything here is best-effort and
|
|
31
|
+
// silent on failure: a network problem, sandbox, or missing cache must never
|
|
32
|
+
// block context output or print an error.
|
|
33
|
+
|
|
34
|
+
const UPDATE_HOST = (process.env.IMPECCABLE_UPDATE_HOST || 'https://impeccable.style').replace(/\/$/, '');
|
|
35
|
+
const UPDATE_CACHE_PATH =
|
|
36
|
+
process.env.IMPECCABLE_UPDATE_CACHE || path.join(os.homedir(), '.impeccable', 'update-check.json');
|
|
37
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // throttle the network poll to once a day
|
|
38
|
+
const RENOTIFY_INTERVAL_MS = 7 * 24 * 60 * 60 * 1000; // don't re-surface the same version for a week
|
|
39
|
+
const FETCH_TIMEOUT_MS = 1200;
|
|
40
|
+
|
|
41
|
+
export function resolveContextDir(cwd = process.cwd()) {
|
|
42
|
+
if (firstExisting(cwd, [...PRODUCT_NAMES, ...DESIGN_NAMES])) {
|
|
43
|
+
return cwd;
|
|
44
|
+
}
|
|
45
|
+
for (const rel of FALLBACK_DIRS) {
|
|
46
|
+
const candidate = path.resolve(cwd, rel);
|
|
47
|
+
if (firstExisting(candidate, [...PRODUCT_NAMES, ...DESIGN_NAMES])) {
|
|
48
|
+
return candidate;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const envDir = process.env.IMPECCABLE_CONTEXT_DIR;
|
|
52
|
+
if (envDir && envDir.trim()) {
|
|
53
|
+
const trimmed = envDir.trim();
|
|
54
|
+
return path.isAbsolute(trimmed) ? trimmed : path.resolve(cwd, trimmed);
|
|
55
|
+
}
|
|
56
|
+
return cwd;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function loadContext(cwd = process.cwd()) {
|
|
60
|
+
const contextDir = resolveContextDir(cwd);
|
|
61
|
+
const productPath = firstExisting(contextDir, PRODUCT_NAMES);
|
|
62
|
+
const designPath = firstExisting(contextDir, DESIGN_NAMES);
|
|
63
|
+
const product = productPath ? safeRead(productPath) : null;
|
|
64
|
+
const design = designPath ? safeRead(designPath) : null;
|
|
65
|
+
return {
|
|
66
|
+
hasProduct: !!product,
|
|
67
|
+
product,
|
|
68
|
+
productPath: productPath ? path.relative(cwd, productPath) : null,
|
|
69
|
+
hasDesign: !!design,
|
|
70
|
+
design,
|
|
71
|
+
designPath: designPath ? path.relative(cwd, designPath) : null,
|
|
72
|
+
contextDir,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function firstExisting(dir, names) {
|
|
77
|
+
for (const name of names) {
|
|
78
|
+
const abs = path.join(dir, name);
|
|
79
|
+
if (fs.existsSync(abs)) return abs;
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function safeRead(p) {
|
|
85
|
+
try {
|
|
86
|
+
return fs.readFileSync(p, 'utf-8');
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Pull the register (`brand` or `product`) out of PRODUCT.md by looking
|
|
94
|
+
* for a `## Register` section and reading the first non-empty line that
|
|
95
|
+
* follows it. Returns null when the file is legacy / register-less.
|
|
96
|
+
*/
|
|
97
|
+
export function extractRegister(product) {
|
|
98
|
+
if (!product) return null;
|
|
99
|
+
const lines = product.split('\n');
|
|
100
|
+
for (let i = 0; i < lines.length; i++) {
|
|
101
|
+
if (/^##\s+Register\b/i.test(lines[i].trim())) {
|
|
102
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
103
|
+
const next = lines[j].trim();
|
|
104
|
+
if (!next) continue;
|
|
105
|
+
const word = next.toLowerCase();
|
|
106
|
+
if (word === 'brand' || word === 'product') return word;
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Read the installed skill's own version from the sibling SKILL.md frontmatter
|
|
116
|
+
* (this file lives at `<skill>/scripts/context.mjs`). Returns null when the
|
|
117
|
+
* frontmatter is missing or unreadable.
|
|
118
|
+
*/
|
|
119
|
+
function readLocalSkillVersion() {
|
|
120
|
+
try {
|
|
121
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
122
|
+
const skillMd = path.join(here, '..', 'SKILL.md');
|
|
123
|
+
const content = fs.readFileSync(skillMd, 'utf-8');
|
|
124
|
+
const match = content.match(/^version:\s*(.+)$/m);
|
|
125
|
+
return match ? match[1].trim().replace(/^["']|["']$/g, '') : null;
|
|
126
|
+
} catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function readUpdateCache() {
|
|
132
|
+
try {
|
|
133
|
+
return JSON.parse(fs.readFileSync(UPDATE_CACHE_PATH, 'utf-8'));
|
|
134
|
+
} catch {
|
|
135
|
+
return {};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function writeUpdateCache(cache) {
|
|
140
|
+
try {
|
|
141
|
+
fs.mkdirSync(path.dirname(UPDATE_CACHE_PATH), { recursive: true });
|
|
142
|
+
fs.writeFileSync(UPDATE_CACHE_PATH, JSON.stringify(cache));
|
|
143
|
+
} catch {
|
|
144
|
+
// Best-effort: a read-only home dir just means we re-poll next session.
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Compare dotted numeric versions. Returns >0 when a is newer than b. */
|
|
149
|
+
function compareSemver(a, b) {
|
|
150
|
+
const pa = String(a).split('.').map(n => parseInt(n, 10) || 0);
|
|
151
|
+
const pb = String(b).split('.').map(n => parseInt(n, 10) || 0);
|
|
152
|
+
for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
|
|
153
|
+
const diff = (pa[i] || 0) - (pb[i] || 0);
|
|
154
|
+
if (diff !== 0) return diff;
|
|
155
|
+
}
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function fetchLatestSkillVersion() {
|
|
160
|
+
try {
|
|
161
|
+
const res = await fetch(`${UPDATE_HOST}/api/version`, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
|
|
162
|
+
if (!res.ok) return null;
|
|
163
|
+
const data = await res.json();
|
|
164
|
+
return typeof data?.skills === 'string' ? data.skills : null;
|
|
165
|
+
} catch {
|
|
166
|
+
return null; // offline, sandboxed, timed out, or bad JSON: all non-fatal
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function buildUpdateDirective(localVersion, latestVersion) {
|
|
171
|
+
return (
|
|
172
|
+
`UPDATE_AVAILABLE: A newer Impeccable skill is available ` +
|
|
173
|
+
`(installed v${localVersion}, latest v${latestVersion}). ` +
|
|
174
|
+
`Before continuing, ask the user once: "A newer Impeccable (v${latestVersion}) is available. ` +
|
|
175
|
+
`Update now? It runs \`npx impeccable skills update\`." ` +
|
|
176
|
+
`If they agree, run \`npx impeccable skills update\` (the update applies to the next session, not this one). ` +
|
|
177
|
+
`Either way, continue the current task without waiting, and do not raise this again.`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Best-effort update directive for the boot output. Returns a string to append
|
|
183
|
+
* or null. Polls the version endpoint at most once per day (cached globally in
|
|
184
|
+
* the user's home dir) and re-surfaces a given version at most once per week so
|
|
185
|
+
* the agent never nags. Opt out entirely with IMPECCABLE_NO_UPDATE_CHECK=1.
|
|
186
|
+
*/
|
|
187
|
+
async function computeUpdateDirective(now = Date.now()) {
|
|
188
|
+
try {
|
|
189
|
+
// Distributed via nb-agents: updates flow through `npx nb-agents update`,
|
|
190
|
+
// not `npx impeccable skills update`. Suppress the official update check so
|
|
191
|
+
// the two delivery mechanisms don't fight over the same skill directory.
|
|
192
|
+
return null;
|
|
193
|
+
if (process.env.IMPECCABLE_NO_UPDATE_CHECK) return null;
|
|
194
|
+
const localVersion = readLocalSkillVersion();
|
|
195
|
+
if (!localVersion) return null;
|
|
196
|
+
|
|
197
|
+
const cache = readUpdateCache();
|
|
198
|
+
|
|
199
|
+
// Poll the network only when the throttle window has elapsed. Stamp
|
|
200
|
+
// lastCheck even on failure so an offline machine doesn't poll every boot.
|
|
201
|
+
if (!cache.lastCheck || now - cache.lastCheck > CHECK_INTERVAL_MS) {
|
|
202
|
+
const latest = await fetchLatestSkillVersion();
|
|
203
|
+
cache.lastCheck = now;
|
|
204
|
+
if (latest) cache.latestVersion = latest;
|
|
205
|
+
writeUpdateCache(cache);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const latest = cache.latestVersion;
|
|
209
|
+
if (!latest || compareSemver(latest, localVersion) <= 0) return null;
|
|
210
|
+
|
|
211
|
+
// Anti-nag: surface a given version at most once per RENOTIFY window.
|
|
212
|
+
if (cache.notifiedVersion === latest && cache.notifiedAt && now - cache.notifiedAt < RENOTIFY_INTERVAL_MS) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
cache.notifiedVersion = latest;
|
|
216
|
+
cache.notifiedAt = now;
|
|
217
|
+
writeUpdateCache(cache);
|
|
218
|
+
|
|
219
|
+
return buildUpdateDirective(localVersion, latest);
|
|
220
|
+
} catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function cli() {
|
|
226
|
+
const ctx = loadContext(process.cwd());
|
|
227
|
+
const updateDirective = await computeUpdateDirective();
|
|
228
|
+
|
|
229
|
+
if (!ctx.hasProduct) {
|
|
230
|
+
// Direct stdout message instead of relying on empty output as a signal
|
|
231
|
+
// — cheap models miss the empty case more often than the explicit one.
|
|
232
|
+
const parts = [
|
|
233
|
+
'NO_PRODUCT_MD: This project has no PRODUCT.md yet. ' +
|
|
234
|
+
'Stop the current task, load reference/init.md, and follow its ' +
|
|
235
|
+
'instructions to write PRODUCT.md before resuming.',
|
|
236
|
+
];
|
|
237
|
+
if (updateDirective) parts.push(updateDirective);
|
|
238
|
+
process.stdout.write(parts.join('\n\n---\n\n') + '\n');
|
|
239
|
+
process.exit(0);
|
|
240
|
+
}
|
|
241
|
+
const parts = [`# PRODUCT.md\n\n${ctx.product.trim()}`];
|
|
242
|
+
if (ctx.hasDesign) {
|
|
243
|
+
parts.push(`# DESIGN.md\n\n${ctx.design.trim()}`);
|
|
244
|
+
}
|
|
245
|
+
const register = extractRegister(ctx.product);
|
|
246
|
+
const next = register
|
|
247
|
+
? `NEXT STEP: This project's register is \`${register}\`. You MUST now read \`reference/${register}.md\` before producing any design output.`
|
|
248
|
+
: `NEXT STEP: You MUST now read the matching register reference (\`reference/brand.md\` or \`reference/product.md\`) before producing any design output. Pick based on PRODUCT.md above.`;
|
|
249
|
+
parts.push(next);
|
|
250
|
+
if (updateDirective) parts.push(updateDirective);
|
|
251
|
+
process.stdout.write(parts.join('\n\n---\n\n') + '\n');
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Run cli() only when this module is the entry point. Compare realpaths
|
|
255
|
+
// rather than endsWith(): a loose suffix match also fires for unrelated
|
|
256
|
+
// scripts like `load-context.mjs`, and realpath tolerates symlinked
|
|
257
|
+
// invocation (the test harness symlinks the skill dir).
|
|
258
|
+
function invokedAsScript() {
|
|
259
|
+
const arg = process.argv[1];
|
|
260
|
+
if (!arg) return false;
|
|
261
|
+
try {
|
|
262
|
+
return fs.realpathSync(arg) === fs.realpathSync(fileURLToPath(import.meta.url));
|
|
263
|
+
} catch {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (invokedAsScript()) {
|
|
269
|
+
cli();
|
|
270
|
+
}
|