@litfamily/litopencode 1.0.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/.gitattributes +8 -0
- package/ATTRIBUTION.md +56 -0
- package/CHANGELOG.md +267 -0
- package/CODE_OF_CONDUCT.md +30 -0
- package/CONTRIBUTING.md +82 -0
- package/LICENSE +21 -0
- package/README-Ko-KR.md +252 -0
- package/README.md +252 -0
- package/SECURITY.md +36 -0
- package/SUPPORT.md +37 -0
- package/bin/litopencode +3 -0
- package/bin/litopencode.cjs +10 -0
- package/dist/activation-managed-prompts.d.ts +2 -0
- package/dist/activation-managed-prompts.js +51 -0
- package/dist/activation-primary-prompts.d.ts +2 -0
- package/dist/activation-primary-prompts.js +176 -0
- package/dist/activation-probe.d.ts +3 -0
- package/dist/activation-probe.js +19 -0
- package/dist/activation-prompt-utils.d.ts +6 -0
- package/dist/activation-prompt-utils.js +36 -0
- package/dist/activation-routing.d.ts +5 -0
- package/dist/activation-routing.js +340 -0
- package/dist/activation-workflow-prompts.d.ts +26 -0
- package/dist/activation-workflow-prompts.js +295 -0
- package/dist/activation.d.ts +24 -0
- package/dist/activation.js +152 -0
- package/dist/agents/defaults.d.ts +15 -0
- package/dist/agents/defaults.js +112 -0
- package/dist/agents/registry.d.ts +65 -0
- package/dist/agents/registry.js +319 -0
- package/dist/agents/specialists.d.ts +49 -0
- package/dist/agents/specialists.js +307 -0
- package/dist/agents/types.d.ts +55 -0
- package/dist/agents/types.js +4 -0
- package/dist/agents.d.ts +4 -0
- package/dist/agents.js +3 -0
- package/dist/benchmark.d.ts +94 -0
- package/dist/benchmark.js +172 -0
- package/dist/bounded-authority-hooks.d.ts +19 -0
- package/dist/bounded-authority-hooks.js +157 -0
- package/dist/bounded-authority.d.ts +184 -0
- package/dist/bounded-authority.js +825 -0
- package/dist/cache-metrics.d.ts +54 -0
- package/dist/cache-metrics.js +81 -0
- package/dist/cli/args.d.ts +5 -0
- package/dist/cli/args.js +415 -0
- package/dist/cli/auto-update.d.ts +117 -0
- package/dist/cli/auto-update.js +604 -0
- package/dist/cli/command-alias-ownership.d.ts +2 -0
- package/dist/cli/command-alias-ownership.js +14 -0
- package/dist/cli/command-aliases.d.ts +12 -0
- package/dist/cli/command-aliases.js +112 -0
- package/dist/cli/doctor.d.ts +2 -0
- package/dist/cli/doctor.js +175 -0
- package/dist/cli/host-capabilities.d.ts +12 -0
- package/dist/cli/host-capabilities.js +25 -0
- package/dist/cli/host-limits.d.ts +21 -0
- package/dist/cli/host-limits.js +83 -0
- package/dist/cli/install-config.d.ts +13 -0
- package/dist/cli/install-config.js +201 -0
- package/dist/cli/install-report.d.ts +2 -0
- package/dist/cli/install-report.js +180 -0
- package/dist/cli/install-tui.d.ts +12 -0
- package/dist/cli/install-tui.js +174 -0
- package/dist/cli/install.d.ts +2 -0
- package/dist/cli/install.js +264 -0
- package/dist/cli/json.d.ts +4 -0
- package/dist/cli/json.js +31 -0
- package/dist/cli/loop.d.ts +3 -0
- package/dist/cli/loop.js +135 -0
- package/dist/cli/lsp-capability.d.ts +17 -0
- package/dist/cli/lsp-capability.js +92 -0
- package/dist/cli/managed-skill-assets.d.ts +23 -0
- package/dist/cli/managed-skill-assets.js +117 -0
- package/dist/cli/model-catalog.d.ts +21 -0
- package/dist/cli/model-catalog.js +107 -0
- package/dist/cli/model-policy.d.ts +3 -0
- package/dist/cli/model-policy.js +143 -0
- package/dist/cli/model-routing.d.ts +5 -0
- package/dist/cli/model-routing.js +208 -0
- package/dist/cli/native-canonical-backup.d.ts +9 -0
- package/dist/cli/native-canonical-backup.js +96 -0
- package/dist/cli/native-canonical-cache.d.ts +9 -0
- package/dist/cli/native-canonical-cache.js +37 -0
- package/dist/cli/native-skill-install.d.ts +2 -0
- package/dist/cli/native-skill-install.js +128 -0
- package/dist/cli/native-skill-integrity.d.ts +25 -0
- package/dist/cli/native-skill-integrity.js +178 -0
- package/dist/cli/native-skill-tree.d.ts +12 -0
- package/dist/cli/native-skill-tree.js +104 -0
- package/dist/cli/native-skills.d.ts +22 -0
- package/dist/cli/native-skills.js +105 -0
- package/dist/cli/plugin-mutation.d.ts +4 -0
- package/dist/cli/plugin-mutation.js +89 -0
- package/dist/cli/scientific-visualization-dependencies.d.ts +18 -0
- package/dist/cli/scientific-visualization-dependencies.js +91 -0
- package/dist/cli/skill-loop.d.ts +3 -0
- package/dist/cli/skill-loop.js +649 -0
- package/dist/cli/types.d.ts +188 -0
- package/dist/cli/types.js +1 -0
- package/dist/cli/update-check.d.ts +2 -0
- package/dist/cli/update-check.js +15 -0
- package/dist/cli/update-notifier.d.ts +70 -0
- package/dist/cli/update-notifier.js +717 -0
- package/dist/cli/vendor-path-migration.d.ts +9 -0
- package/dist/cli/vendor-path-migration.js +96 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +250 -0
- package/dist/commands.d.ts +331 -0
- package/dist/commands.js +600 -0
- package/dist/config-parser.d.ts +5 -0
- package/dist/config-parser.js +274 -0
- package/dist/config.d.ts +72 -0
- package/dist/config.js +157 -0
- package/dist/deliverable-hedge-guard.d.ts +26 -0
- package/dist/deliverable-hedge-guard.js +302 -0
- package/dist/durable-plan.d.ts +23 -0
- package/dist/durable-plan.js +349 -0
- package/dist/features.d.ts +828 -0
- package/dist/features.js +1109 -0
- package/dist/hooks.d.ts +17 -0
- package/dist/hooks.js +83 -0
- package/dist/ignition.d.ts +14 -0
- package/dist/ignition.js +33 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.js +30 -0
- package/dist/inert-data.d.ts +2 -0
- package/dist/inert-data.js +13 -0
- package/dist/knowledge.d.ts +98 -0
- package/dist/knowledge.js +961 -0
- package/dist/ledger.d.ts +181 -0
- package/dist/ledger.js +1179 -0
- package/dist/lit-fetch-classify.d.ts +8 -0
- package/dist/lit-fetch-classify.js +107 -0
- package/dist/lit-fetch-content.d.ts +2 -0
- package/dist/lit-fetch-content.js +43 -0
- package/dist/lit-fetch-http.d.ts +10 -0
- package/dist/lit-fetch-http.js +82 -0
- package/dist/lit-fetch-result.d.ts +2 -0
- package/dist/lit-fetch-result.js +65 -0
- package/dist/lit-fetch-ssrf.d.ts +9 -0
- package/dist/lit-fetch-ssrf.js +77 -0
- package/dist/lit-fetch-url.d.ts +3 -0
- package/dist/lit-fetch-url.js +28 -0
- package/dist/lit-fetch.d.ts +69 -0
- package/dist/lit-fetch.js +84 -0
- package/dist/lit-mark.d.ts +17 -0
- package/dist/lit-mark.js +129 -0
- package/dist/logger.d.ts +9 -0
- package/dist/logger.js +21 -0
- package/dist/model-route-policy.d.ts +25 -0
- package/dist/model-route-policy.js +129 -0
- package/dist/reader-facing-output.d.ts +3 -0
- package/dist/reader-facing-output.js +28 -0
- package/dist/rules/discovery.d.ts +36 -0
- package/dist/rules/discovery.js +270 -0
- package/dist/rules/engine.d.ts +43 -0
- package/dist/rules/engine.js +236 -0
- package/dist/rules/frontmatter.d.ts +16 -0
- package/dist/rules/frontmatter.js +179 -0
- package/dist/rules/glob.d.ts +12 -0
- package/dist/rules/glob.js +233 -0
- package/dist/rules/hooks.d.ts +25 -0
- package/dist/rules/hooks.js +75 -0
- package/dist/rules/output-style.d.ts +1 -0
- package/dist/rules/output-style.js +20 -0
- package/dist/scientific-visualization-banner.d.ts +1 -0
- package/dist/scientific-visualization-banner.js +2 -0
- package/dist/search-workflow-ideas.d.ts +67 -0
- package/dist/search-workflow-ideas.js +128 -0
- package/dist/secret-shapes.d.ts +9 -0
- package/dist/secret-shapes.js +63 -0
- package/dist/server.d.ts +6 -0
- package/dist/server.js +129 -0
- package/dist/session-lineage.d.ts +8 -0
- package/dist/session-lineage.js +12 -0
- package/dist/skill-loop/apply.d.ts +19 -0
- package/dist/skill-loop/apply.js +483 -0
- package/dist/skill-loop/config.d.ts +50 -0
- package/dist/skill-loop/config.js +215 -0
- package/dist/skill-loop/curator.d.ts +18 -0
- package/dist/skill-loop/curator.js +232 -0
- package/dist/skill-loop/ledger.d.ts +39 -0
- package/dist/skill-loop/ledger.js +344 -0
- package/dist/skill-loop/proposals.d.ts +48 -0
- package/dist/skill-loop/proposals.js +290 -0
- package/dist/skill-loop/storage.d.ts +72 -0
- package/dist/skill-loop/storage.js +817 -0
- package/dist/skill-loop/time.d.ts +2 -0
- package/dist/skill-loop/time.js +23 -0
- package/dist/skill-loop/transaction.d.ts +62 -0
- package/dist/skill-loop/transaction.js +836 -0
- package/dist/skill-loop/usage.d.ts +31 -0
- package/dist/skill-loop/usage.js +146 -0
- package/dist/skill-observer.d.ts +22 -0
- package/dist/skill-observer.js +1154 -0
- package/dist/skill-renames.d.ts +5 -0
- package/dist/skill-renames.js +8 -0
- package/dist/skills.d.ts +307 -0
- package/dist/skills.js +450 -0
- package/dist/stable-identity.d.ts +14 -0
- package/dist/stable-identity.js +15 -0
- package/dist/state.d.ts +25 -0
- package/dist/state.js +38 -0
- package/dist/strict-json.d.ts +2 -0
- package/dist/strict-json.js +94 -0
- package/dist/tool-guards.d.ts +34 -0
- package/dist/tool-guards.js +210 -0
- package/dist/tool-kit.d.ts +32 -0
- package/dist/tool-kit.js +75 -0
- package/dist/tools.d.ts +43 -0
- package/dist/tools.js +381 -0
- package/dist/uiux-visual-catalog.d.ts +50 -0
- package/dist/uiux-visual-catalog.js +103 -0
- package/dist/user-facing-markdown.d.ts +5 -0
- package/dist/user-facing-markdown.js +93 -0
- package/dist/workflow-families.d.ts +16 -0
- package/dist/workflow-families.js +95 -0
- package/docs/assets/cover.webp +0 -0
- package/docs/assets/litopencode-continuity-1600.webp +0 -0
- package/docs/assets/litopencode-ignition-1600.webp +0 -0
- package/docs/assets/readme/badge-license.svg +1 -0
- package/docs/assets/readme/badge-version.svg +1 -0
- package/docs/assets/readme/litopencode-clay-icon.png +0 -0
- package/docs/assets/readme/litopencode-wordmark.svg +5 -0
- package/docs/lit-mark.md +46 -0
- package/docs/migration.md +162 -0
- package/docs/privacy.md +82 -0
- package/docs/reference-Ko-KR.md +309 -0
- package/docs/reference.md +444 -0
- package/output-styles/asd-ste100-ko.md +41 -0
- package/output-styles/asd-ste100.md +40 -0
- package/output-styles/eli5-ko.md +13 -0
- package/output-styles/eli5.md +11 -0
- package/package.json +57 -0
- package/qa/fixtures/litfamily-harness-speed-v1.json +38 -0
- package/qa/harness-speed-contract.mjs +209 -0
- package/qa/harness-speed-v2-contract.mjs +213 -0
- package/qa/harness-speed-verdict.mjs +105 -0
- package/skills/agent-roster/SKILL.md +262 -0
- package/skills/autoconference/LICENSE +21 -0
- package/skills/autoconference/PROVENANCE.md +20 -0
- package/skills/autoconference/SKILL.md +125 -0
- package/skills/autoconference/assets/conference_template.md +76 -0
- package/skills/autoconference/assets/report_template.md +53 -0
- package/skills/autoconference/assets/synthesis_template.md +39 -0
- package/skills/autoconference/modes/analyze.md +255 -0
- package/skills/autoconference/modes/core.md +112 -0
- package/skills/autoconference/modes/debate.md +373 -0
- package/skills/autoconference/modes/plan.md +310 -0
- package/skills/autoconference/modes/resume.md +48 -0
- package/skills/autoconference/modes/ship.md +57 -0
- package/skills/autoconference/modes/survey.md +109 -0
- package/skills/autoconference/references/agent-prompts.md +134 -0
- package/skills/autoconference/references/conference-protocol.md +102 -0
- package/skills/autoconference/references/convergence-guide.md +167 -0
- package/skills/autoconference/references/core-principles.md +83 -0
- package/skills/autoconference/references/crash-recovery.md +104 -0
- package/skills/autoconference/references/results-logging.md +198 -0
- package/skills/autoconference/references/upstream-family-contract.md +130 -0
- package/skills/autoconference/references/visualization-guide.md +105 -0
- package/skills/autoconference/scripts/check_conference.sh +371 -0
- package/skills/autoconference/scripts/init_conference.py +625 -0
- package/skills/autoconference/scripts/style_presets.py +104 -0
- package/skills/autoresearch/LICENSE +21 -0
- package/skills/autoresearch/PROVENANCE.md +20 -0
- package/skills/autoresearch/SKILL.md +131 -0
- package/skills/autoresearch/assets/report_template.md +50 -0
- package/skills/autoresearch/assets/research_template.md +38 -0
- package/skills/autoresearch/assets/results_template.tsv +1 -0
- package/skills/autoresearch/modes/core.md +216 -0
- package/skills/autoresearch/modes/debug.md +235 -0
- package/skills/autoresearch/modes/fix.md +173 -0
- package/skills/autoresearch/modes/learn.md +91 -0
- package/skills/autoresearch/modes/plan.md +291 -0
- package/skills/autoresearch/modes/predict.md +266 -0
- package/skills/autoresearch/modes/reason.md +170 -0
- package/skills/autoresearch/modes/scenario.md +164 -0
- package/skills/autoresearch/modes/security.md +282 -0
- package/skills/autoresearch/modes/ship.md +49 -0
- package/skills/autoresearch/references/core-principles.md +80 -0
- package/skills/autoresearch/references/evaluator-contract.md +126 -0
- package/skills/autoresearch/references/investigation-techniques.md +205 -0
- package/skills/autoresearch/references/owasp-checklist.md +161 -0
- package/skills/autoresearch/references/persona-templates.md +232 -0
- package/skills/autoresearch/references/results-logging.md +96 -0
- package/skills/autoresearch/references/scenario-dimensions.md +178 -0
- package/skills/autoresearch/references/stride-model.md +196 -0
- package/skills/autoresearch/references/stuck-detection.md +107 -0
- package/skills/autoresearch/references/type-checklists.md +163 -0
- package/skills/autoresearch/references/upstream-family-contract.md +125 -0
- package/skills/autoresearch/references/visualization-guide.md +95 -0
- package/skills/autoresearch/scripts/check_progress.sh +272 -0
- package/skills/autoresearch/scripts/init_research.py +330 -0
- package/skills/autoresearch/scripts/run_with_deadline.py +164 -0
- package/skills/autoresearch/scripts/style_presets.py +104 -0
- package/skills/browser-drive/SKILL.md +133 -0
- package/skills/browser-drive/references/snapshot-act-loop.md +37 -0
- package/skills/browser-drive/scripts/capability-probe.mjs +262 -0
- package/skills/comment-checker/SKILL.md +171 -0
- package/skills/debugging/SKILL.md +202 -0
- package/skills/debugging/references/README.md +15 -0
- package/skills/debugging/references/escalation.md +43 -0
- package/skills/debugging/references/runtimes/README.md +17 -0
- package/skills/debugging/references/runtimes/bundled-js-binary.md +44 -0
- package/skills/debugging/references/runtimes/go.md +37 -0
- package/skills/debugging/references/runtimes/native-binary.md +44 -0
- package/skills/debugging/references/runtimes/node.md +43 -0
- package/skills/debugging/references/runtimes/python.md +41 -0
- package/skills/debugging/references/runtimes/rust.md +37 -0
- package/skills/debugging/references/tools.md +72 -0
- package/skills/deep-interview/SKILL.md +201 -0
- package/skills/doctor-installer/SKILL.md +252 -0
- package/skills/durable-litgoal/SKILL.md +248 -0
- package/skills/frontend-ui-ux/SKILL.md +70 -0
- package/skills/frontend-ui-ux/data/LICENSE +21 -0
- package/skills/frontend-ui-ux/data/PROVENANCE.json +1088 -0
- package/skills/frontend-ui-ux/data/THIRD-PARTY-NOTICE.txt +14 -0
- package/skills/frontend-ui-ux/data/design-intelligence.json +1 -0
- package/skills/frontend-ui-ux/references/_canonical-corpus/ATTRIBUTION.md +217 -0
- package/skills/frontend-ui-ux/references/_canonical-corpus/LICENSE +21 -0
- package/skills/frontend-ui-ux/references/_canonical-corpus/LICENSE-Apache-2.0.txt +201 -0
- package/skills/frontend-ui-ux/references/_canonical-corpus/manifest.json +867 -0
- package/skills/frontend-ui-ux/references/adaptive-layout.md +85 -0
- package/skills/frontend-ui-ux/references/brand-and-imagery.md +79 -0
- package/skills/frontend-ui-ux/references/complete-contract.md +282 -0
- package/skills/frontend-ui-ux/references/composition.md +87 -0
- package/skills/frontend-ui-ux/references/creative-directions.md +83 -0
- package/skills/frontend-ui-ux/references/design/README.md +248 -0
- package/skills/frontend-ui-ux/references/design/_INDEX.md +191 -0
- package/skills/frontend-ui-ux/references/design/airbnb.md +393 -0
- package/skills/frontend-ui-ux/references/design/airtable.md +92 -0
- package/skills/frontend-ui-ux/references/design/apple.md +250 -0
- package/skills/frontend-ui-ux/references/design/aside.md +209 -0
- package/skills/frontend-ui-ux/references/design/binance.md +348 -0
- package/skills/frontend-ui-ux/references/design/bmw.md +183 -0
- package/skills/frontend-ui-ux/references/design/brutalist-skill.md +92 -0
- package/skills/frontend-ui-ux/references/design/bugatti.md +271 -0
- package/skills/frontend-ui-ux/references/design/cal.md +262 -0
- package/skills/frontend-ui-ux/references/design/claude.md +315 -0
- package/skills/frontend-ui-ux/references/design/clay.md +307 -0
- package/skills/frontend-ui-ux/references/design/clickhouse.md +284 -0
- package/skills/frontend-ui-ux/references/design/clone-from-url.md +65 -0
- package/skills/frontend-ui-ux/references/design/cohere.md +269 -0
- package/skills/frontend-ui-ux/references/design/coinbase.md +132 -0
- package/skills/frontend-ui-ux/references/design/composio.md +310 -0
- package/skills/frontend-ui-ux/references/design/cursor.md +312 -0
- package/skills/frontend-ui-ux/references/design/design-system-architecture.md +244 -0
- package/skills/frontend-ui-ux/references/design/elevenlabs.md +268 -0
- package/skills/frontend-ui-ux/references/design/expo.md +284 -0
- package/skills/frontend-ui-ux/references/design/ferrari.md +317 -0
- package/skills/frontend-ui-ux/references/design/figma.md +223 -0
- package/skills/frontend-ui-ux/references/design/framer.md +249 -0
- package/skills/frontend-ui-ux/references/design/gpt-tasteskill.md +74 -0
- package/skills/frontend-ui-ux/references/design/hashicorp.md +281 -0
- package/skills/frontend-ui-ux/references/design/ibm.md +335 -0
- package/skills/frontend-ui-ux/references/design/image-to-code-skill.md +1228 -0
- package/skills/frontend-ui-ux/references/design/imagegen-brandkit.md +798 -0
- package/skills/frontend-ui-ux/references/design/imagegen-frontend-mobile.md +1465 -0
- package/skills/frontend-ui-ux/references/design/imagegen-frontend-web.md +987 -0
- package/skills/frontend-ui-ux/references/design/intercom.md +149 -0
- package/skills/frontend-ui-ux/references/design/kraken.md +128 -0
- package/skills/frontend-ui-ux/references/design/lamborghini.md +291 -0
- package/skills/frontend-ui-ux/references/design/layout-skill.md +107 -0
- package/skills/frontend-ui-ux/references/design/lazyweb.md +77 -0
- package/skills/frontend-ui-ux/references/design/linear.app.md +370 -0
- package/skills/frontend-ui-ux/references/design/lovable.md +301 -0
- package/skills/frontend-ui-ux/references/design/mastercard.md +368 -0
- package/skills/frontend-ui-ux/references/design/meta.md +369 -0
- package/skills/frontend-ui-ux/references/design/minimalist-skill.md +85 -0
- package/skills/frontend-ui-ux/references/design/minimax.md +260 -0
- package/skills/frontend-ui-ux/references/design/mintlify.md +329 -0
- package/skills/frontend-ui-ux/references/design/miro.md +111 -0
- package/skills/frontend-ui-ux/references/design/mistral.ai.md +264 -0
- package/skills/frontend-ui-ux/references/design/mongodb.md +269 -0
- package/skills/frontend-ui-ux/references/design/nike.md +366 -0
- package/skills/frontend-ui-ux/references/design/notion.md +312 -0
- package/skills/frontend-ui-ux/references/design/nvidia.md +296 -0
- package/skills/frontend-ui-ux/references/design/ollama.md +270 -0
- package/skills/frontend-ui-ux/references/design/opencode.ai.md +284 -0
- package/skills/frontend-ui-ux/references/design/output-skill.md +49 -0
- package/skills/frontend-ui-ux/references/design/pinterest.md +233 -0
- package/skills/frontend-ui-ux/references/design/playstation.md +367 -0
- package/skills/frontend-ui-ux/references/design/posthog.md +259 -0
- package/skills/frontend-ui-ux/references/design/raycast.md +271 -0
- package/skills/frontend-ui-ux/references/design/react-dev-tooling-skill.md +230 -0
- package/skills/frontend-ui-ux/references/design/redesign-skill.md +178 -0
- package/skills/frontend-ui-ux/references/design/renault.md +314 -0
- package/skills/frontend-ui-ux/references/design/replicate.md +264 -0
- package/skills/frontend-ui-ux/references/design/resend.md +306 -0
- package/skills/frontend-ui-ux/references/design/revolut.md +188 -0
- package/skills/frontend-ui-ux/references/design/runwayml.md +247 -0
- package/skills/frontend-ui-ux/references/design/sanity.md +360 -0
- package/skills/frontend-ui-ux/references/design/sentry.md +265 -0
- package/skills/frontend-ui-ux/references/design/shopify.md +353 -0
- package/skills/frontend-ui-ux/references/design/soft-skill.md +98 -0
- package/skills/frontend-ui-ux/references/design/spacex.md +197 -0
- package/skills/frontend-ui-ux/references/design/spotify.md +249 -0
- package/skills/frontend-ui-ux/references/design/starbucks.md +583 -0
- package/skills/frontend-ui-ux/references/design/stitch-design-example.md +121 -0
- package/skills/frontend-ui-ux/references/design/stitch-skill.md +184 -0
- package/skills/frontend-ui-ux/references/design/stripe.md +325 -0
- package/skills/frontend-ui-ux/references/design/supabase.md +258 -0
- package/skills/frontend-ui-ux/references/design/superhuman.md +255 -0
- package/skills/frontend-ui-ux/references/design/taste-skill.md +1206 -0
- package/skills/frontend-ui-ux/references/design/tesla.md +289 -0
- package/skills/frontend-ui-ux/references/design/theverge.md +342 -0
- package/skills/frontend-ui-ux/references/design/together.ai.md +266 -0
- package/skills/frontend-ui-ux/references/design/uber.md +298 -0
- package/skills/frontend-ui-ux/references/design/vercel.md +313 -0
- package/skills/frontend-ui-ux/references/design/vodafone.md +426 -0
- package/skills/frontend-ui-ux/references/design/voltagent.md +326 -0
- package/skills/frontend-ui-ux/references/design/warp.md +256 -0
- package/skills/frontend-ui-ux/references/design/webflow.md +95 -0
- package/skills/frontend-ui-ux/references/design/wired.md +281 -0
- package/skills/frontend-ui-ux/references/design/wise.md +176 -0
- package/skills/frontend-ui-ux/references/design/x.ai.md +260 -0
- package/skills/frontend-ui-ux/references/design/zapier.md +331 -0
- package/skills/frontend-ui-ux/references/designpowers/EVIDENCE.md +97 -0
- package/skills/frontend-ui-ux/references/designpowers/README.md +48 -0
- package/skills/frontend-ui-ux/references/designpowers/UPSTREAM.md +80 -0
- package/skills/frontend-ui-ux/references/designpowers/lane-a-direction.md +64 -0
- package/skills/frontend-ui-ux/references/designpowers/lane-b-execution.md +65 -0
- package/skills/frontend-ui-ux/references/designpowers/lane-c-review.md +65 -0
- package/skills/frontend-ui-ux/references/designpowers/lane-d-memory.md +83 -0
- package/skills/frontend-ui-ux/references/designpowers/orchestration.md +80 -0
- package/skills/frontend-ui-ux/references/designpowers/routing.md +79 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/LICENSE +21 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/agents/accessibility-reviewer.md +83 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/agents/content-writer.md +132 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-builder.md +109 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-critic.md +89 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-lead.md +113 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-scout.md +78 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/agents/design-strategist.md +121 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/agents/heuristic-evaluator.md +268 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/agents/inspiration-scout.md +107 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/agents/motion-designer.md +120 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/accessible-content/reference.md +101 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/adaptive-interfaces/reference.md +109 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/cognitive-accessibility/reference.md +107 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-debate/reference.md +199 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-debt-tracker/reference.md +174 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-handoff/reference.md +125 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-md/reference.md +106 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-retrospective/reference.md +266 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-review/reference.md +123 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/design-system-alignment/reference.md +120 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/designpowers-critique/reference.md +164 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/heuristic-evaluation/reference.md +85 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/inclusive-personas/reference.md +98 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/inspiration-scouting/reference.md +165 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/interaction-design/reference.md +122 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/motion-choreography/reference.md +81 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/research-planning/reference.md +96 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/responsive-patterns/reference.md +77 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/synthetic-user-testing/reference.md +192 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/taste-feedback/reference.md +165 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/taste-report/reference.md +78 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/token-architecture/reference.md +75 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/ui-composition/reference.md +117 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/usability-testing/reference.md +78 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/verification-before-shipping/reference.md +125 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/voice-and-tone/reference.md +79 -0
- package/skills/frontend-ui-ux/references/designpowers/vendor/skills/writing-design-plans/reference.md +119 -0
- package/skills/frontend-ui-ux/references/evidence-review.md +103 -0
- package/skills/frontend-ui-ux/references/implementation-platforms.md +94 -0
- package/skills/frontend-ui-ux/references/inclusive-interface.md +85 -0
- package/skills/frontend-ui-ux/references/interaction-motion.md +93 -0
- package/skills/frontend-ui-ux/references/operating-lanes.md +80 -0
- package/skills/frontend-ui-ux/references/perfection/README.md +160 -0
- package/skills/frontend-ui-ux/references/perfection/react-perf-tooling.md +127 -0
- package/skills/frontend-ui-ux/references/performance-delivery.md +77 -0
- package/skills/frontend-ui-ux/references/product-direction.md +84 -0
- package/skills/frontend-ui-ux/references/redesign-playbook.md +88 -0
- package/skills/frontend-ui-ux/references/system-foundations.md +86 -0
- package/skills/frontend-ui-ux/references/taste-direction.md +55 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/README.md +659 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/charts.csv +26 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/colors.csv +162 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/icons.csv +106 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/landing.csv +35 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/products.csv +162 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/react-performance.csv +45 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/astro.csv +54 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/flutter.csv +53 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/html-tailwind.csv +56 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/jetpack-compose.csv +53 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/nextjs.csv +53 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/nuxt-ui.csv +51 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/nuxtjs.csv +59 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/react-native.csv +52 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/react.csv +54 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/shadcn.csv +61 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/svelte.csv +54 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/swiftui.csv +51 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/stacks/vue.csv +50 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/styles.csv +85 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/typography.csv +74 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/ui-reasoning.csv +162 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/ux-guidelines.csv +100 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/data/web-interface.csv +31 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/scripts/core.py +262 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/scripts/design_system.py +1148 -0
- package/skills/frontend-ui-ux/references/ui-ux-db/scripts/search.py +114 -0
- package/skills/frontend-ui-ux/references/visual-language.md +84 -0
- package/skills/frontend-ui-ux/references/visual-reconstruction.md +87 -0
- package/skills/frontend-ui-ux/schemas/design-contract-v1alpha1.json +570 -0
- package/skills/frontend-ui-ux/schemas/design-contract-v1beta1.json +32 -0
- package/skills/frontend-ui-ux/schemas/design-contract-v1beta2.json +34 -0
- package/skills/frontend-ui-ux/scripts/bounded-json.mjs +45 -0
- package/skills/frontend-ui-ux/scripts/canonical-json.mjs +26 -0
- package/skills/frontend-ui-ux/scripts/contract-error.mjs +15 -0
- package/skills/frontend-ui-ux/scripts/csv.mjs +64 -0
- package/skills/frontend-ui-ux/scripts/dataset.mjs +65 -0
- package/skills/frontend-ui-ux/scripts/design-contract.mjs +609 -0
- package/skills/frontend-ui-ux/scripts/import-design-intelligence.mjs +193 -0
- package/skills/frontend-ui-ux/scripts/retrieval.mjs +92 -0
- package/skills/frontend-ui-ux/scripts/stable-file-read.mjs +237 -0
- package/skills/frontend-ui-ux/scripts/stdin-json.mjs +16 -0
- package/skills/frontend-ui-ux/scripts/strict-json.mjs +102 -0
- package/skills/frontend-ui-ux/scripts/uiux.mjs +86 -0
- package/skills/frontend-ui-ux/scripts/verify-canonical-corpus.mjs +266 -0
- package/skills/lit-burnoff/SKILL.md +227 -0
- package/skills/lit-burnoff-file/SKILL.md +175 -0
- package/skills/lit-code/SKILL.md +266 -0
- package/skills/lit-code/references/README.md +18 -0
- package/skills/lit-code/references/go/README.md +12 -0
- package/skills/lit-code/references/go/concurrency.md +42 -0
- package/skills/lit-code/references/go/error-handling.md +47 -0
- package/skills/lit-code/references/go/testing.md +55 -0
- package/skills/lit-code/references/go/tooling.md +35 -0
- package/skills/lit-code/references/go/type-patterns.md +50 -0
- package/skills/lit-code/references/python/README.md +12 -0
- package/skills/lit-code/references/python/async.md +50 -0
- package/skills/lit-code/references/python/error-handling.md +44 -0
- package/skills/lit-code/references/python/testing.md +50 -0
- package/skills/lit-code/references/python/tooling.md +38 -0
- package/skills/lit-code/references/python/type-patterns.md +46 -0
- package/skills/lit-code/references/rust/README.md +12 -0
- package/skills/lit-code/references/rust/concurrency.md +45 -0
- package/skills/lit-code/references/rust/error-handling.md +43 -0
- package/skills/lit-code/references/rust/tooling.md +36 -0
- package/skills/lit-code/references/rust/type-patterns.md +46 -0
- package/skills/lit-code/references/rust/unsafe.md +47 -0
- package/skills/lit-code/references/typescript/README.md +11 -0
- package/skills/lit-code/references/typescript/error-handling.md +56 -0
- package/skills/lit-code/references/typescript/testing.md +42 -0
- package/skills/lit-code/references/typescript/tsconfig-strict.md +40 -0
- package/skills/lit-code/references/typescript/type-patterns.md +60 -0
- package/skills/lit-commit/SKILL.md +222 -0
- package/skills/lit-comprehend/SKILL.md +278 -0
- package/skills/lit-comprehend/assets/explainer-scaffold.html +104 -0
- package/skills/lit-comprehend/references/artifact-template.md +114 -0
- package/skills/lit-comprehend/references/micro-worlds.md +115 -0
- package/skills/lit-comprehend/scripts/verify-explainer.ts +339 -0
- package/skills/lit-crucible/SKILL.md +212 -0
- package/skills/lit-fetch/SKILL.md +242 -0
- package/skills/lit-handoff/SKILL.md +199 -0
- package/skills/lit-init/SKILL.md +214 -0
- package/skills/lit-korean/SKILL.md +231 -0
- package/skills/lit-plan/SKILL.md +351 -0
- package/skills/lit-plan/scripts/scaffold-plan.mjs +275 -0
- package/skills/lit-recap/SKILL.md +233 -0
- package/skills/lit-scientific-visualization/SKILL.md +175 -0
- package/skills/litresearch/SKILL.md +436 -0
- package/skills/litwork/SKILL.md +227 -0
- package/skills/lsp/SKILL.md +186 -0
- package/skills/lsp-setup/SKILL.md +187 -0
- package/skills/lsp-setup/references/README.md +40 -0
- package/skills/lsp-setup/references/bash.md +31 -0
- package/skills/lsp-setup/references/c-cpp.md +43 -0
- package/skills/lsp-setup/references/csharp.md +29 -0
- package/skills/lsp-setup/references/dart.md +25 -0
- package/skills/lsp-setup/references/elixir.md +30 -0
- package/skills/lsp-setup/references/go.md +33 -0
- package/skills/lsp-setup/references/haskell.md +27 -0
- package/skills/lsp-setup/references/java.md +28 -0
- package/skills/lsp-setup/references/julia.md +28 -0
- package/skills/lsp-setup/references/kotlin.md +25 -0
- package/skills/lsp-setup/references/lua.md +26 -0
- package/skills/lsp-setup/references/php.md +30 -0
- package/skills/lsp-setup/references/python.md +33 -0
- package/skills/lsp-setup/references/ruby.md +34 -0
- package/skills/lsp-setup/references/rust.md +34 -0
- package/skills/lsp-setup/references/swift.md +26 -0
- package/skills/lsp-setup/references/terraform.md +26 -0
- package/skills/lsp-setup/references/typescript.md +35 -0
- package/skills/lsp-setup/references/yaml.md +27 -0
- package/skills/lsp-setup/references/zig.md +28 -0
- package/skills/managed-skill-manifest.json +1386 -0
- package/skills/native-goal-verdict/SKILL.md +216 -0
- package/skills/refactor/SKILL.md +221 -0
- package/skills/reference-benchmark-claims/SKILL.md +204 -0
- package/skills/release-guardrails/SKILL.md +245 -0
- package/skills/review-work/SKILL.md +301 -0
- package/skills/rules/SKILL.md +196 -0
- package/skills/search-workflow-ideas/SKILL.md +223 -0
- package/skills/skill-observer/SKILL.md +148 -0
- package/skills/skill-observer/references/review-contract.md +95 -0
- package/skills/skill-rename-aliases.json +10 -0
- package/skills/start-work/SKILL.md +334 -0
- package/skills/structural-search/SKILL.md +234 -0
- package/skills/tool-guards/SKILL.md +223 -0
- package/skills/visual-qa/SKILL.md +61 -0
- package/skills/visual-qa/references/capture-playbook.md +105 -0
- package/skills/visual-qa/references/complete-contract.md +411 -0
- package/skills/visual-qa/schemas/evidence-manifest-v1alpha1.json +201 -0
- package/skills/visual-qa/schemas/evidence-manifest-v1beta1.json +141 -0
- package/skills/visual-qa/schemas/review-receipt-v1alpha1.json +113 -0
- package/skills/visual-qa/scripts/artifact.mjs +123 -0
- package/skills/visual-qa/scripts/bounded-json.mjs +47 -0
- package/skills/visual-qa/scripts/canonical-json.mjs +28 -0
- package/skills/visual-qa/scripts/capabilities.mjs +66 -0
- package/skills/visual-qa/scripts/contract-text.mjs +15 -0
- package/skills/visual-qa/scripts/design-contract.mjs +611 -0
- package/skills/visual-qa/scripts/evidence-evaluate.mjs +521 -0
- package/skills/visual-qa/scripts/evidence.mjs +271 -0
- package/skills/visual-qa/scripts/png-decode.mjs +146 -0
- package/skills/visual-qa/scripts/png.mjs +154 -0
- package/skills/visual-qa/scripts/review.mjs +188 -0
- package/skills/visual-qa/scripts/stdin-json.mjs +16 -0
- package/skills/visual-qa/scripts/strict-json.mjs +102 -0
- package/skills/visual-qa/scripts/tui.mjs +149 -0
- package/skills/visual-qa/scripts/visual-qa.mjs +90 -0
- package/skills/wikify/LICENSE +21 -0
- package/skills/wikify/PROVENANCE.md +18 -0
- package/skills/wikify/SKILL.md +176 -0
- package/skills/wikify/assets/home-template.md +44 -0
- package/skills/wikify/assets/maintenance-report-template.md +46 -0
- package/skills/wikify/assets/paper-source-note-template.md +137 -0
- package/skills/wikify/assets/source-note-template.md +45 -0
- package/skills/wikify/assets/wiki-rules-template.md +117 -0
- package/skills/wikify/modes/ingest.md +57 -0
- package/skills/wikify/modes/init.md +44 -0
- package/skills/wikify/modes/lint.md +60 -0
- package/skills/wikify/modes/query.md +38 -0
- package/skills/wikify/modes/save.md +45 -0
- package/skills/wikify/references-upstream-contract.md +758 -0
- package/skills/workflow-loop/SKILL.md +387 -0
- package/tools/check-pack-payload.mjs +410 -0
- package/tools/check-payload-substance.mjs +738 -0
- package/tools/check-version-lockstep.mjs +238 -0
- package/tools/gen-canonical-frontend-manifest.mjs +66 -0
- package/tools/gen-managed-skill-manifest.mjs +135 -0
- package/tools/harness-speed-local.mjs +261 -0
- package/tools/payload-reference-exemptions.json +49 -0
- package/tools/payload-substance-allowlist.json +46 -0
- package/tools/payload-substance-parity.json +571 -0
- package/tools/qa-real-surface-fixtures.mjs +428 -0
- package/tools/qa-real-surface-harness.mjs +173 -0
- package/tools/run-behavior-replacement-probes.mjs +296 -0
- package/tools/run-build.mjs +101 -0
- package/tools/run-harness-speed-local.mjs +52 -0
- package/tools/run-harness-speed-v2.mjs +36 -0
- package/tools/run-installed-resource-tamper-probe.mjs +62 -0
- package/tools/run-negative-gate-matrix.mjs +516 -0
- package/tools/run-rules-glob-differential.mjs +232 -0
- package/tools/run-typecheck.mjs +25 -0
- package/tools/run-uiux-visual-qa-scenarios.mjs +234 -0
- package/tools/run-wikify-surface-probe.mjs +170 -0
- package/tools/scan-legacy-tokens.mjs +431 -0
- package/tools/version-manifests.json +60 -0
- package/tsconfig.build.json +12 -0
- package/tsconfig.json +13 -0
- package/vendor/NOTICE.md +21 -0
- package/vendor/handoff/SKILL.md +199 -0
- package/vendor/handoff/evals/evals.json +154 -0
- package/vendor/handoff/examples/HANDOFF-example-generic-auth-refactor.md +97 -0
- package/vendor/handoff/templates/HANDOFF.md +121 -0
- package/vendor/licenses/022_handoff-MIT.txt +21 -0
- package/vendor/licenses/045_scientific-visualization-MIT.txt +21 -0
- package/vendor/provenance/022_handoff.md +19 -0
- package/vendor/provenance/045_scientific-visualization.md +36 -0
- package/vendor/scientific-visualization/SKILL.md +283 -0
- package/vendor/scientific-visualization/assets/color_palettes.py +197 -0
- package/vendor/scientific-visualization/assets/nature.mplstyle +75 -0
- package/vendor/scientific-visualization/assets/presentation.mplstyle +74 -0
- package/vendor/scientific-visualization/assets/publication.mplstyle +78 -0
- package/vendor/scientific-visualization/evals/evals.json +158 -0
- package/vendor/scientific-visualization/references/color_palettes.md +380 -0
- package/vendor/scientific-visualization/references/journal_requirements.md +359 -0
- package/vendor/scientific-visualization/references/matplotlib_examples.md +608 -0
- package/vendor/scientific-visualization/references/mdanalysis_martini_visualization.md +85 -0
- package/vendor/scientific-visualization/references/publication_guidelines.md +217 -0
- package/vendor/scientific-visualization/references/seaborn_for_publications.md +293 -0
- package/vendor/scientific-visualization/scripts/figure_export.py +238 -0
- package/vendor/scientific-visualization/scripts/style_presets.py +467 -0
- package/vendor/scientific-visualization/tests/test_figure_export.py +51 -0
- package/vendor/scientific-visualization/tests/test_style_presets.py +114 -0
|
@@ -0,0 +1,961 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import fsSync from "node:fs";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { decodeStrictUtf8, parseStrictJson } from "./strict-json.js";
|
|
6
|
+
import { containsSecret } from "./secret-shapes.js";
|
|
7
|
+
import { sameStableDirectoryIdentity, sameStableStatIdentity, stableStatIdentity } from "./stable-identity.js";
|
|
8
|
+
export const knowledgeKinds = ["fact", "decision", "failure", "risk", "rule", "checkpoint"];
|
|
9
|
+
export const knowledgeStates = ["review-needed", "accepted", "rejected", "stale"];
|
|
10
|
+
export class KnowledgeStoreError extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "KnowledgeStoreError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
class KnowledgeFileDisappearedError extends KnowledgeStoreError {
|
|
17
|
+
code = "ENOENT";
|
|
18
|
+
}
|
|
19
|
+
const normalQueryBudgetBytes = 2048;
|
|
20
|
+
const hardQueryBudgetBytes = 4096;
|
|
21
|
+
const maximumTextBytes = 320;
|
|
22
|
+
const maximumTextWords = 56;
|
|
23
|
+
const maximumEvidenceBytes = 192;
|
|
24
|
+
const maximumAuthorityBytes = 8 * 1024 * 1024;
|
|
25
|
+
const maximumStructuredStringScanCharacters = 512;
|
|
26
|
+
const safeIdentifier = /^[a-z0-9][a-z0-9._/-]{0,63}$/u;
|
|
27
|
+
const evidenceReference = /^(?!\/)(?!.*(?:^|\/)\.\.(?:\/|$))[A-Za-z0-9._@+/-]+(?:(?::\d+(?:-\d+)?)|(?:#[A-Za-z0-9._-]+))?$/u;
|
|
28
|
+
const controlCharacterShape = /[\u0000-\u001F\u007F-\u009F\p{Cf}]/u;
|
|
29
|
+
const instructionShape = /(?:```|<\/?(?:system|assistant|developer|user|tool)\b|\b(?:ignore|disregard)\s+(?:(?:all|any|every|the)\s+)?(?:previous|prior)\s+instructions?\b|\bsystem prompt\b|\bdeveloper message\b|^(?:system|assistant|developer|user|tool)\s*:|(?:^|\n)\s*#{1,6}\s*(?:system|assistant|developer|user|tool)\s*:)/iu;
|
|
30
|
+
const stopWords = new Set(["a", "an", "and", "are", "as", "at", "be", "by", "does", "for", "from", "how", "in", "is", "it", "of", "on", "or", "the", "this", "to", "what", "where", "which", "with"]);
|
|
31
|
+
const noFollowReadFlags = fsSync.constants.O_RDONLY | fsSync.constants.O_NOFOLLOW;
|
|
32
|
+
const noFollowCreateFlags = fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL | fsSync.constants.O_NOFOLLOW;
|
|
33
|
+
const noFollowDirectoryFlags = fsSync.constants.O_RDONLY | (fsSync.constants.O_DIRECTORY ?? 0);
|
|
34
|
+
function isRecord(value) {
|
|
35
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36
|
+
}
|
|
37
|
+
function hasErrorCode(error, code) {
|
|
38
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
39
|
+
}
|
|
40
|
+
function parseLockOwner(value, filePath) {
|
|
41
|
+
if (!isRecord(value) ||
|
|
42
|
+
Object.keys(value).some((key) => key !== "pid" && key !== "nonce") ||
|
|
43
|
+
typeof value.pid !== "number" ||
|
|
44
|
+
!Number.isSafeInteger(value.pid) ||
|
|
45
|
+
value.pid <= 0 ||
|
|
46
|
+
typeof value.nonce !== "string" ||
|
|
47
|
+
value.nonce.length === 0) {
|
|
48
|
+
throw new KnowledgeStoreError(`Malformed knowledge lock owner at ${filePath}.`);
|
|
49
|
+
}
|
|
50
|
+
return { pid: value.pid, nonce: value.nonce };
|
|
51
|
+
}
|
|
52
|
+
function identityOf(stat) {
|
|
53
|
+
return stableStatIdentity(stat);
|
|
54
|
+
}
|
|
55
|
+
function fileIdentityOf(stat) {
|
|
56
|
+
return Object.freeze({ ...stableStatIdentity(stat), nlink: stat.nlink });
|
|
57
|
+
}
|
|
58
|
+
function sameIdentity(left, right) {
|
|
59
|
+
return sameStableDirectoryIdentity(stableStatIdentity(left), stableStatIdentity(right));
|
|
60
|
+
}
|
|
61
|
+
function sameFileIdentity(left, right) {
|
|
62
|
+
return sameStableStatIdentity(stableStatIdentity(left), stableStatIdentity(right)) && left.nlink === right.nlink;
|
|
63
|
+
}
|
|
64
|
+
function isParentIdentityRace(error, parentPath) {
|
|
65
|
+
return error instanceof KnowledgeStoreError &&
|
|
66
|
+
error.message === `Knowledge parent identity changed before I/O: ${parentPath}.`;
|
|
67
|
+
}
|
|
68
|
+
function assertDirectoryStat(stat, filePath) {
|
|
69
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
70
|
+
throw new KnowledgeStoreError(`Unsafe knowledge directory at ${filePath}: symbolic links and non-directories are not allowed.`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function syncDirectoryIdentity(filePath) {
|
|
74
|
+
try {
|
|
75
|
+
const stat = fsSync.lstatSync(filePath);
|
|
76
|
+
assertDirectoryStat(stat, filePath);
|
|
77
|
+
return identityOf(stat);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
if (hasErrorCode(error, "ENOENT"))
|
|
81
|
+
return undefined;
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function utf8Bytes(value) {
|
|
86
|
+
return Buffer.byteLength(value, "utf8");
|
|
87
|
+
}
|
|
88
|
+
function normalizeText(value) {
|
|
89
|
+
return value.trim().replace(/\s+/gu, " ");
|
|
90
|
+
}
|
|
91
|
+
function isAsciiLetter(code) {
|
|
92
|
+
return (code >= 0x41 && code <= 0x5a) || (code >= 0x61 && code <= 0x7a);
|
|
93
|
+
}
|
|
94
|
+
function isUriSchemeCharacter(code) {
|
|
95
|
+
return isAsciiLetter(code) || (code >= 0x30 && code <= 0x39) || code === 0x2b || code === 0x2d || code === 0x2e;
|
|
96
|
+
}
|
|
97
|
+
function isUriAuthorityDelimiter(code) {
|
|
98
|
+
return code <= 0x20 || code === 0x23 || code === 0x2f || code === 0x3f || code === 0x85 || code === 0xa0 ||
|
|
99
|
+
code === 0x1680 || (code >= 0x2000 && code <= 0x200a) || code === 0x2028 || code === 0x2029 ||
|
|
100
|
+
code === 0x202f || code === 0x205f || code === 0x3000 || code === 0xfeff;
|
|
101
|
+
}
|
|
102
|
+
function containsCredentialUriUserinfo(value) {
|
|
103
|
+
const limit = Math.min(value.length, maximumStructuredStringScanCharacters);
|
|
104
|
+
let index = 0;
|
|
105
|
+
while (index < limit) {
|
|
106
|
+
if (!isAsciiLetter(value.charCodeAt(index))) {
|
|
107
|
+
index += 1;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
let cursor = index + 1;
|
|
111
|
+
while (cursor < limit && isUriSchemeCharacter(value.charCodeAt(cursor)))
|
|
112
|
+
cursor += 1;
|
|
113
|
+
if (value.charCodeAt(cursor) !== 0x3a || value.charCodeAt(cursor + 1) !== 0x2f || value.charCodeAt(cursor + 2) !== 0x2f) {
|
|
114
|
+
index = cursor > index ? cursor : index + 1;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const authorityStart = cursor + 3;
|
|
118
|
+
let authorityEnd = authorityStart;
|
|
119
|
+
while (authorityEnd < limit && !isUriAuthorityDelimiter(value.charCodeAt(authorityEnd)))
|
|
120
|
+
authorityEnd += 1;
|
|
121
|
+
for (let authorityIndex = authorityStart + 1; authorityIndex < authorityEnd; authorityIndex += 1) {
|
|
122
|
+
if (value.charCodeAt(authorityIndex) === 0x40)
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
index = authorityEnd;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
function structuredStringReason(value) {
|
|
130
|
+
if (controlCharacterShape.test(value))
|
|
131
|
+
return "UNSAFE_CONTROL_CHARACTERS";
|
|
132
|
+
if (containsCredentialUriUserinfo(value))
|
|
133
|
+
return "UNSAFE_URI_USERINFO";
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
function validSurface(value) {
|
|
137
|
+
return safeIdentifier.test(value) && !value.includes("..") && utf8Bytes(value) <= 64;
|
|
138
|
+
}
|
|
139
|
+
function parseKnowledgeEvent(value) {
|
|
140
|
+
if (!isRecord(value))
|
|
141
|
+
return { reason: "INVALID_EVENT_OBJECT" };
|
|
142
|
+
const supported = new Set(["kind", "text", "evidenceRef", "source"]);
|
|
143
|
+
if (Object.keys(value).some((key) => !supported.has(key)))
|
|
144
|
+
return { reason: "INVALID_EVENT_FIELDS" };
|
|
145
|
+
if (typeof value.kind !== "string" || !knowledgeKinds.includes(value.kind)) {
|
|
146
|
+
return { reason: "INVALID_KIND" };
|
|
147
|
+
}
|
|
148
|
+
if (typeof value.text !== "string")
|
|
149
|
+
return { reason: "INVALID_TEXT" };
|
|
150
|
+
const textReason = structuredStringReason(value.text);
|
|
151
|
+
if (textReason !== undefined)
|
|
152
|
+
return { reason: textReason };
|
|
153
|
+
const text = normalizeText(value.text);
|
|
154
|
+
if (text.length === 0 || utf8Bytes(text) > maximumTextBytes || text.split(" ").length > maximumTextWords) {
|
|
155
|
+
return { reason: "INVALID_TEXT_BOUNDS" };
|
|
156
|
+
}
|
|
157
|
+
if (instructionShape.test(text) || /[<>]/u.test(text))
|
|
158
|
+
return { reason: "UNSAFE_INSTRUCTION_SHAPE" };
|
|
159
|
+
if (containsSecret(text))
|
|
160
|
+
return { reason: "UNSAFE_SECRET" };
|
|
161
|
+
if (typeof value.evidenceRef !== "string")
|
|
162
|
+
return { reason: "INVALID_EVIDENCE_REFERENCE" };
|
|
163
|
+
const evidenceRef = value.evidenceRef.trim();
|
|
164
|
+
const evidenceReason = structuredStringReason(evidenceRef);
|
|
165
|
+
if (evidenceReason !== undefined || utf8Bytes(evidenceRef) > maximumEvidenceBytes || !evidenceReference.test(evidenceRef) || containsSecret(evidenceRef)) {
|
|
166
|
+
return { reason: "INVALID_EVIDENCE_REFERENCE" };
|
|
167
|
+
}
|
|
168
|
+
if (typeof value.source !== "string" || structuredStringReason(value.source) !== undefined || !validSurface(value.source) || containsSecret(value.source)) {
|
|
169
|
+
return { reason: "INVALID_SOURCE" };
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
event: {
|
|
173
|
+
kind: value.kind,
|
|
174
|
+
text,
|
|
175
|
+
evidenceRef,
|
|
176
|
+
source: value.source
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function stableKnowledgeId(event) {
|
|
181
|
+
const identity = JSON.stringify([event.kind, event.text.toLocaleLowerCase("en-US"), event.evidenceRef, event.source]);
|
|
182
|
+
return `kn_${createHash("sha256").update(identity).digest("hex").slice(0, 24)}`;
|
|
183
|
+
}
|
|
184
|
+
function isIsoTimestamp(value) {
|
|
185
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{3}))?Z$/u.exec(value);
|
|
186
|
+
if (match === null)
|
|
187
|
+
return false;
|
|
188
|
+
const [, yearText, monthText, dayText, hourText, minuteText, secondText, millisecondsText] = match;
|
|
189
|
+
const year = Number(yearText);
|
|
190
|
+
const month = Number(monthText);
|
|
191
|
+
const day = Number(dayText);
|
|
192
|
+
const hour = Number(hourText);
|
|
193
|
+
const minute = Number(minuteText);
|
|
194
|
+
const second = Number(secondText);
|
|
195
|
+
const milliseconds = millisecondsText === undefined ? 0 : Number(millisecondsText);
|
|
196
|
+
if (month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 || minute > 59 || second > 59 || milliseconds > 999) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
const date = new Date(0);
|
|
200
|
+
date.setUTCFullYear(year, month - 1, day);
|
|
201
|
+
date.setUTCHours(hour, minute, second, milliseconds);
|
|
202
|
+
return date.getUTCFullYear() === year &&
|
|
203
|
+
date.getUTCMonth() === month - 1 &&
|
|
204
|
+
date.getUTCDate() === day &&
|
|
205
|
+
date.getUTCHours() === hour &&
|
|
206
|
+
date.getUTCMinutes() === minute &&
|
|
207
|
+
date.getUTCSeconds() === second &&
|
|
208
|
+
date.getUTCMilliseconds() === milliseconds;
|
|
209
|
+
}
|
|
210
|
+
function parseKnowledgeRecord(value, line) {
|
|
211
|
+
if (!isRecord(value))
|
|
212
|
+
throw new KnowledgeStoreError(`Malformed knowledge claim at line ${line}: expected an object.`);
|
|
213
|
+
const supported = new Set(["id", "text", "kind", "state", "timestamp", "provenance", "evidence"]);
|
|
214
|
+
const provenanceFields = new Set(["product", "surface", "source"]);
|
|
215
|
+
const evidenceFields = new Set(["ref"]);
|
|
216
|
+
if (Object.keys(value).some((key) => !supported.has(key))) {
|
|
217
|
+
throw new KnowledgeStoreError(`Malformed knowledge claim at line ${line}: unknown fields.`);
|
|
218
|
+
}
|
|
219
|
+
const provenance = value.provenance;
|
|
220
|
+
const evidence = value.evidence;
|
|
221
|
+
if (typeof value.id !== "string" || !/^kn_[a-f0-9]{24}$/u.test(value.id) ||
|
|
222
|
+
typeof value.text !== "string" || structuredStringReason(value.text) !== undefined || value.text.length === 0 || normalizeText(value.text) !== value.text || utf8Bytes(value.text) > maximumTextBytes || value.text.split(" ").length > maximumTextWords ||
|
|
223
|
+
typeof value.kind !== "string" || !knowledgeKinds.includes(value.kind) ||
|
|
224
|
+
typeof value.state !== "string" || !knowledgeStates.includes(value.state) ||
|
|
225
|
+
typeof value.timestamp !== "string" || !isIsoTimestamp(value.timestamp) ||
|
|
226
|
+
!isRecord(provenance) || Object.keys(provenance).some((key) => !provenanceFields.has(key)) || provenance.product !== "litopencode" ||
|
|
227
|
+
typeof provenance.surface !== "string" || structuredStringReason(provenance.surface) !== undefined || !validSurface(provenance.surface) ||
|
|
228
|
+
typeof provenance.source !== "string" || structuredStringReason(provenance.source) !== undefined || !validSurface(provenance.source) ||
|
|
229
|
+
!isRecord(evidence) || Object.keys(evidence).some((key) => !evidenceFields.has(key)) ||
|
|
230
|
+
typeof evidence.ref !== "string" || structuredStringReason(evidence.ref) !== undefined || utf8Bytes(evidence.ref) > maximumEvidenceBytes || !evidenceReference.test(evidence.ref)) {
|
|
231
|
+
throw new KnowledgeStoreError(`Malformed knowledge claim at line ${line}: invalid bounded fields.`);
|
|
232
|
+
}
|
|
233
|
+
if (instructionShape.test(value.text) || containsSecret(value.text) || /[<>]/u.test(value.text) ||
|
|
234
|
+
containsSecret(provenance.surface) || containsSecret(provenance.source) || containsSecret(evidence.ref)) {
|
|
235
|
+
throw new KnowledgeStoreError(`Malformed knowledge claim at line ${line}: unsafe text.`);
|
|
236
|
+
}
|
|
237
|
+
const expectedId = stableKnowledgeId({
|
|
238
|
+
kind: value.kind,
|
|
239
|
+
text: value.text,
|
|
240
|
+
evidenceRef: evidence.ref,
|
|
241
|
+
source: provenance.source
|
|
242
|
+
});
|
|
243
|
+
if (value.id !== expectedId) {
|
|
244
|
+
throw new KnowledgeStoreError(`Malformed knowledge claim at line ${line}: stable id does not match the record fields.`);
|
|
245
|
+
}
|
|
246
|
+
return value;
|
|
247
|
+
}
|
|
248
|
+
function parseClaims(raw) {
|
|
249
|
+
if (raw === "")
|
|
250
|
+
return [];
|
|
251
|
+
const lines = (raw.endsWith("\n") ? raw.slice(0, -1) : raw).split("\n");
|
|
252
|
+
return Object.freeze(lines.map((line, index) => {
|
|
253
|
+
try {
|
|
254
|
+
return parseKnowledgeRecord(parseStrictJson(line), index + 1);
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
if (error instanceof KnowledgeStoreError)
|
|
258
|
+
throw error;
|
|
259
|
+
throw new KnowledgeStoreError(`Malformed knowledge claim at line ${index + 1}: invalid JSON.`);
|
|
260
|
+
}
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
263
|
+
async function assertPinnedDirectory(filePath, expected) {
|
|
264
|
+
let stat;
|
|
265
|
+
try {
|
|
266
|
+
stat = await fs.lstat(filePath);
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
if (hasErrorCode(error, "ENOENT") && expected === undefined)
|
|
270
|
+
return;
|
|
271
|
+
if (hasErrorCode(error, "ENOENT")) {
|
|
272
|
+
throw new KnowledgeStoreError(`Knowledge ancestor identity changed before I/O: ${filePath} is missing.`);
|
|
273
|
+
}
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
assertDirectoryStat(stat, filePath);
|
|
277
|
+
if (expected === undefined || !sameIdentity(identityOf(stat), expected)) {
|
|
278
|
+
throw new KnowledgeStoreError(`Knowledge ancestor identity changed before I/O: ${filePath}.`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async function assertPinnedDirectories(paths) {
|
|
282
|
+
await assertPinnedDirectory(paths.projectRoot, paths.projectRootIdentity);
|
|
283
|
+
await assertPinnedDirectory(paths.runtimeDirectory, paths.runtimeDirectoryIdentity);
|
|
284
|
+
await assertPinnedDirectory(paths.directory, paths.directoryIdentity);
|
|
285
|
+
}
|
|
286
|
+
async function pinParent(filePath, paths) {
|
|
287
|
+
await assertPinnedDirectories(paths);
|
|
288
|
+
const parentPath = path.dirname(filePath);
|
|
289
|
+
const stat = await fs.lstat(parentPath);
|
|
290
|
+
assertDirectoryStat(stat, parentPath);
|
|
291
|
+
const identity = identityOf(stat);
|
|
292
|
+
if (parentPath === paths.directory && paths.directoryIdentity !== undefined && !sameIdentity(identity, paths.directoryIdentity)) {
|
|
293
|
+
throw new KnowledgeStoreError(`Knowledge parent identity changed before I/O: ${parentPath}.`);
|
|
294
|
+
}
|
|
295
|
+
return Object.freeze({ path: parentPath, identity });
|
|
296
|
+
}
|
|
297
|
+
async function assertParentStable(parent, paths) {
|
|
298
|
+
await assertPinnedDirectories(paths);
|
|
299
|
+
const stat = await fs.lstat(parent.path);
|
|
300
|
+
assertDirectoryStat(stat, parent.path);
|
|
301
|
+
if (!sameIdentity(identityOf(stat), parent.identity)) {
|
|
302
|
+
throw new KnowledgeStoreError(`Knowledge parent identity changed before I/O: ${parent.path}.`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async function readRegularFileIfPresent(filePath, paths, options = {}) {
|
|
306
|
+
const allowHardlinks = options.allowHardlinks === true;
|
|
307
|
+
const parent = await pinParent(filePath, paths);
|
|
308
|
+
await assertParentStable(parent, paths);
|
|
309
|
+
let pathStat;
|
|
310
|
+
try {
|
|
311
|
+
pathStat = await fs.lstat(filePath);
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
if (hasErrorCode(error, "ENOENT")) {
|
|
315
|
+
if (options.allowMissing !== false)
|
|
316
|
+
return { text: "" };
|
|
317
|
+
throw new KnowledgeFileDisappearedError(`Knowledge file disappeared during observation at ${filePath}.`);
|
|
318
|
+
}
|
|
319
|
+
throw error;
|
|
320
|
+
}
|
|
321
|
+
if (pathStat.isSymbolicLink() || !pathStat.isFile()) {
|
|
322
|
+
throw new KnowledgeStoreError(`Unsafe knowledge file type at ${filePath}.`);
|
|
323
|
+
}
|
|
324
|
+
if (!allowHardlinks && pathStat.nlink !== 1)
|
|
325
|
+
throw new KnowledgeStoreError(`Unsafe hardlinked knowledge file at ${filePath}: multiple links are not allowed.`);
|
|
326
|
+
if (pathStat.size > maximumAuthorityBytes)
|
|
327
|
+
throw new KnowledgeStoreError("Knowledge authority exceeds its local byte limit.");
|
|
328
|
+
let handle;
|
|
329
|
+
try {
|
|
330
|
+
handle = await fs.open(filePath, noFollowReadFlags);
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
if (hasErrorCode(error, "ENOENT")) {
|
|
334
|
+
throw new KnowledgeFileDisappearedError(`Knowledge file disappeared after observation at ${filePath}.`);
|
|
335
|
+
}
|
|
336
|
+
if (hasErrorCode(error, "ELOOP"))
|
|
337
|
+
throw new KnowledgeStoreError(`Unsafe knowledge file type at ${filePath}: symbolic links are not allowed.`);
|
|
338
|
+
throw error;
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
await assertParentStable(parent, paths);
|
|
342
|
+
const descriptorStat = await handle.stat();
|
|
343
|
+
if (!descriptorStat.isFile() || !sameFileIdentity(fileIdentityOf(descriptorStat), fileIdentityOf(pathStat))) {
|
|
344
|
+
throw new KnowledgeStoreError(`Unsafe knowledge file replacement at ${filePath}.`);
|
|
345
|
+
}
|
|
346
|
+
if (!allowHardlinks && descriptorStat.nlink !== 1)
|
|
347
|
+
throw new KnowledgeStoreError(`Unsafe hardlinked knowledge file at ${filePath}: multiple links are not allowed.`);
|
|
348
|
+
if (descriptorStat.size > maximumAuthorityBytes)
|
|
349
|
+
throw new KnowledgeStoreError("Knowledge authority exceeds its local byte limit.");
|
|
350
|
+
await assertParentStable(parent, paths);
|
|
351
|
+
const text = decodeStrictUtf8(await handle.readFile());
|
|
352
|
+
if (utf8Bytes(text) > maximumAuthorityBytes)
|
|
353
|
+
throw new KnowledgeStoreError("Knowledge authority exceeds its local byte limit.");
|
|
354
|
+
await assertParentStable(parent, paths);
|
|
355
|
+
let finalStat;
|
|
356
|
+
try {
|
|
357
|
+
finalStat = await fs.lstat(filePath);
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
if (hasErrorCode(error, "ENOENT"))
|
|
361
|
+
throw new KnowledgeFileDisappearedError(`Knowledge authority disappeared after descriptor read at ${filePath}.`);
|
|
362
|
+
throw error;
|
|
363
|
+
}
|
|
364
|
+
if (finalStat.isSymbolicLink() || !finalStat.isFile() || !sameFileIdentity(fileIdentityOf(finalStat), fileIdentityOf(descriptorStat))) {
|
|
365
|
+
throw new KnowledgeStoreError(`Knowledge file changed after descriptor read at ${filePath}.`);
|
|
366
|
+
}
|
|
367
|
+
if (!allowHardlinks && finalStat.nlink !== 1)
|
|
368
|
+
throw new KnowledgeStoreError(`Unsafe hardlinked knowledge file at ${filePath}: multiple links are not allowed.`);
|
|
369
|
+
if (finalStat.size > maximumAuthorityBytes)
|
|
370
|
+
throw new KnowledgeStoreError("Knowledge authority exceeds its local byte limit.");
|
|
371
|
+
return { text, identity: fileIdentityOf(finalStat) };
|
|
372
|
+
}
|
|
373
|
+
finally {
|
|
374
|
+
await handle.close().catch(() => undefined);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async function writeBufferFully(handle, bytes, filePath) {
|
|
378
|
+
let offset = 0;
|
|
379
|
+
while (offset < bytes.byteLength) {
|
|
380
|
+
const remaining = bytes.byteLength - offset;
|
|
381
|
+
const result = await handle.write(bytes, offset, remaining, null);
|
|
382
|
+
if (!Number.isSafeInteger(result.bytesWritten) || result.bytesWritten < 1 || result.bytesWritten > remaining) {
|
|
383
|
+
throw new KnowledgeStoreError(`Knowledge staged write made no progress at ${filePath}.`);
|
|
384
|
+
}
|
|
385
|
+
offset += result.bytesWritten;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
async function writePrivateFile(filePath, text, paths) {
|
|
389
|
+
const parent = await pinParent(filePath, paths);
|
|
390
|
+
try {
|
|
391
|
+
await assertParentStable(parent, paths);
|
|
392
|
+
const handle = await fs.open(filePath, noFollowCreateFlags, 0o600);
|
|
393
|
+
try {
|
|
394
|
+
await assertParentStable(parent, paths);
|
|
395
|
+
const stat = await handle.stat();
|
|
396
|
+
if (!stat.isFile() || stat.nlink !== 1)
|
|
397
|
+
throw new KnowledgeStoreError(`Unsafe staged knowledge file at ${filePath}.`);
|
|
398
|
+
await assertParentStable(parent, paths);
|
|
399
|
+
await writeBufferFully(handle, Buffer.from(text, "utf8"), filePath);
|
|
400
|
+
await assertParentStable(parent, paths);
|
|
401
|
+
await handle.sync();
|
|
402
|
+
}
|
|
403
|
+
finally {
|
|
404
|
+
await handle.close().catch(() => undefined);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
catch (error) {
|
|
408
|
+
if (hasErrorCode(error, "ELOOP"))
|
|
409
|
+
throw new KnowledgeStoreError(`Unsafe knowledge file type at ${filePath}: symbolic links are not allowed.`);
|
|
410
|
+
throw error;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
function unsupportedDirectorySync(error) {
|
|
414
|
+
return ["EBADF", "EISDIR", "EINVAL", "ENOTSUP", "EOPNOTSUPP", "EPERM"].some((code) => hasErrorCode(error, code));
|
|
415
|
+
}
|
|
416
|
+
async function syncDirectory(filePath, paths) {
|
|
417
|
+
await assertPinnedDirectories(paths);
|
|
418
|
+
let handle;
|
|
419
|
+
try {
|
|
420
|
+
handle = await fs.open(filePath, noFollowDirectoryFlags);
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
if (unsupportedDirectorySync(error))
|
|
424
|
+
return;
|
|
425
|
+
throw error;
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
428
|
+
try {
|
|
429
|
+
await handle.sync();
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
if (!unsupportedDirectorySync(error))
|
|
433
|
+
throw error;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
finally {
|
|
437
|
+
await handle.close().catch(() => undefined);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
async function publishStage(stagePath, finalPath, expectedAuthority, paths, finalParent) {
|
|
441
|
+
const stage = await readRegularFileIfPresent(stagePath, paths);
|
|
442
|
+
if (stage.identity === undefined)
|
|
443
|
+
throw new KnowledgeStoreError(`Knowledge staged snapshot disappeared at ${stagePath}.`);
|
|
444
|
+
parseClaims(stage.text);
|
|
445
|
+
const authority = await readAuthoritySnapshot(paths);
|
|
446
|
+
if (!sameAuthoritySnapshot(expectedAuthority, authority)) {
|
|
447
|
+
throw new KnowledgeStoreError(`Knowledge authority changed before snapshot publication at ${finalPath}.`);
|
|
448
|
+
}
|
|
449
|
+
await assertParentStable(finalParent, paths);
|
|
450
|
+
await fs.rename(stagePath, finalPath);
|
|
451
|
+
try {
|
|
452
|
+
await syncDirectory(finalParent.path, paths);
|
|
453
|
+
}
|
|
454
|
+
catch (error) {
|
|
455
|
+
try {
|
|
456
|
+
await writePrivateFile(stagePath, stage.text, paths);
|
|
457
|
+
}
|
|
458
|
+
catch {
|
|
459
|
+
// Keep the committed authority visible when the recovery copy cannot be recreated.
|
|
460
|
+
}
|
|
461
|
+
throw error;
|
|
462
|
+
}
|
|
463
|
+
const finalRead = await readAuthoritySnapshot(paths);
|
|
464
|
+
if (finalRead.raw !== stage.text)
|
|
465
|
+
throw new KnowledgeStoreError(`Knowledge final bytes differ from the staged snapshot at ${finalPath}.`);
|
|
466
|
+
parseClaims(finalRead.raw);
|
|
467
|
+
}
|
|
468
|
+
async function promoteStage(stagePath, finalPath, paths, expectedAuthority) {
|
|
469
|
+
const stageParent = await pinParent(stagePath, paths);
|
|
470
|
+
const finalParent = await pinParent(finalPath, paths);
|
|
471
|
+
await assertParentStable(stageParent, paths);
|
|
472
|
+
const stage = await readRegularFileIfPresent(stagePath, paths);
|
|
473
|
+
const authority = await readAuthoritySnapshot(paths);
|
|
474
|
+
if (!sameAuthoritySnapshot(expectedAuthority, authority)) {
|
|
475
|
+
throw new KnowledgeStoreError(`Knowledge authority changed before snapshot publication at ${finalPath}.`);
|
|
476
|
+
}
|
|
477
|
+
try {
|
|
478
|
+
parseClaims(authority.raw);
|
|
479
|
+
}
|
|
480
|
+
catch {
|
|
481
|
+
throw new KnowledgeStoreError(`Knowledge recovery is required before snapshot publication at ${finalPath}.`);
|
|
482
|
+
}
|
|
483
|
+
if (!stage.text.startsWith(authority.raw) || stage.text === authority.raw) {
|
|
484
|
+
throw new KnowledgeStoreError(`Knowledge staged snapshot diverges from the current authority at ${stagePath}.`);
|
|
485
|
+
}
|
|
486
|
+
await publishStage(stagePath, finalPath, expectedAuthority, paths, finalParent);
|
|
487
|
+
}
|
|
488
|
+
async function ensureDirectory(filePath, beforeCreate) {
|
|
489
|
+
try {
|
|
490
|
+
const existing = await fs.lstat(filePath);
|
|
491
|
+
assertDirectoryStat(existing, filePath);
|
|
492
|
+
return identityOf(existing);
|
|
493
|
+
}
|
|
494
|
+
catch (error) {
|
|
495
|
+
if (!hasErrorCode(error, "ENOENT"))
|
|
496
|
+
throw error;
|
|
497
|
+
}
|
|
498
|
+
await beforeCreate?.();
|
|
499
|
+
try {
|
|
500
|
+
await fs.mkdir(filePath, { mode: 0o700 });
|
|
501
|
+
}
|
|
502
|
+
catch (error) {
|
|
503
|
+
if (!hasErrorCode(error, "EEXIST")) {
|
|
504
|
+
if (hasErrorCode(error, "ENOENT"))
|
|
505
|
+
throw new KnowledgeStoreError(`Knowledge parent disappeared before creating ${filePath}.`);
|
|
506
|
+
throw error;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const stat = await fs.lstat(filePath);
|
|
510
|
+
assertDirectoryStat(stat, filePath);
|
|
511
|
+
return identityOf(stat);
|
|
512
|
+
}
|
|
513
|
+
async function ensureKnowledgeDirectory(paths) {
|
|
514
|
+
await assertPinnedDirectories(paths);
|
|
515
|
+
let current = paths;
|
|
516
|
+
if (current.runtimeDirectoryIdentity === undefined) {
|
|
517
|
+
await assertPinnedDirectories(current);
|
|
518
|
+
await ensureDirectory(current.runtimeDirectory, async () => assertPinnedDirectories(current));
|
|
519
|
+
current = knowledgePaths(current.projectRoot);
|
|
520
|
+
}
|
|
521
|
+
if (current.directoryIdentity === undefined) {
|
|
522
|
+
await assertPinnedDirectories(current);
|
|
523
|
+
await ensureDirectory(current.directory, async () => assertPinnedDirectories(current));
|
|
524
|
+
current = knowledgePaths(current.projectRoot);
|
|
525
|
+
}
|
|
526
|
+
await assertPinnedDirectories(current);
|
|
527
|
+
return current;
|
|
528
|
+
}
|
|
529
|
+
async function knowledgeDirectoryExists(paths) {
|
|
530
|
+
await assertPinnedDirectories(paths);
|
|
531
|
+
return paths.runtimeDirectoryIdentity !== undefined && paths.directoryIdentity !== undefined;
|
|
532
|
+
}
|
|
533
|
+
async function readAuthority(paths) {
|
|
534
|
+
return (await readAuthoritySnapshot(paths)).raw;
|
|
535
|
+
}
|
|
536
|
+
async function readAuthoritySnapshot(paths) {
|
|
537
|
+
if (!(await knowledgeDirectoryExists(paths)))
|
|
538
|
+
return { raw: "" };
|
|
539
|
+
const result = await readRegularFileIfPresent(paths.claimsFile, paths, { allowHardlinks: true });
|
|
540
|
+
return { raw: result.text, ...(result.identity === undefined ? {} : { identity: result.identity }) };
|
|
541
|
+
}
|
|
542
|
+
function sameAuthoritySnapshot(left, right) {
|
|
543
|
+
if (left.identity === undefined || right.identity === undefined) {
|
|
544
|
+
return left.identity === right.identity && left.raw === right.raw;
|
|
545
|
+
}
|
|
546
|
+
return sameFileIdentity(left.identity, right.identity) && left.raw === right.raw;
|
|
547
|
+
}
|
|
548
|
+
function recoveryResult(status, removed = [], preserved = []) {
|
|
549
|
+
return {
|
|
550
|
+
status,
|
|
551
|
+
recovered: status === "recovered",
|
|
552
|
+
recoveryRequired: status === "recovery-required",
|
|
553
|
+
removed: Object.freeze([...removed]),
|
|
554
|
+
preserved: Object.freeze([...preserved])
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
export function knowledgePaths(projectRoot) {
|
|
558
|
+
const root = path.resolve(projectRoot);
|
|
559
|
+
let projectRootIdentity;
|
|
560
|
+
try {
|
|
561
|
+
const stat = fsSync.lstatSync(root);
|
|
562
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
563
|
+
throw new KnowledgeStoreError(`Unsafe project root at ${root}: symbolic links and non-directories are not allowed.`);
|
|
564
|
+
projectRootIdentity = identityOf(stat);
|
|
565
|
+
}
|
|
566
|
+
catch (error) {
|
|
567
|
+
if (error instanceof KnowledgeStoreError || !hasErrorCode(error, "ENOENT"))
|
|
568
|
+
throw error;
|
|
569
|
+
}
|
|
570
|
+
const runtimeDirectory = path.join(root, ".litopencode");
|
|
571
|
+
const directory = path.join(runtimeDirectory, "knowledge");
|
|
572
|
+
return {
|
|
573
|
+
projectRoot: root,
|
|
574
|
+
runtimeDirectory,
|
|
575
|
+
directory,
|
|
576
|
+
claimsFile: path.join(directory, "claims.jsonl"),
|
|
577
|
+
lockDirectory: path.join(directory, ".claims.lock"),
|
|
578
|
+
projectRootIdentity,
|
|
579
|
+
runtimeDirectoryIdentity: syncDirectoryIdentity(runtimeDirectory),
|
|
580
|
+
directoryIdentity: syncDirectoryIdentity(directory)
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
async function removeSafeStage(filePath, expectedIdentity, paths) {
|
|
584
|
+
const parent = await pinParent(filePath, paths);
|
|
585
|
+
await assertParentStable(parent, paths);
|
|
586
|
+
let stat;
|
|
587
|
+
try {
|
|
588
|
+
stat = await fs.lstat(filePath);
|
|
589
|
+
}
|
|
590
|
+
catch (error) {
|
|
591
|
+
if (hasErrorCode(error, "ENOENT"))
|
|
592
|
+
return true;
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
if (stat.isSymbolicLink() || !stat.isFile() || stat.nlink !== 1 || !sameFileIdentity(fileIdentityOf(stat), expectedIdentity))
|
|
596
|
+
return false;
|
|
597
|
+
await fs.rm(filePath, { force: true });
|
|
598
|
+
return true;
|
|
599
|
+
}
|
|
600
|
+
async function recoverUnlocked(paths) {
|
|
601
|
+
await assertPinnedDirectories(paths);
|
|
602
|
+
let entries;
|
|
603
|
+
try {
|
|
604
|
+
entries = await fs.readdir(paths.directory);
|
|
605
|
+
}
|
|
606
|
+
catch (error) {
|
|
607
|
+
if (hasErrorCode(error, "ENOENT"))
|
|
608
|
+
return recoveryResult("clean");
|
|
609
|
+
throw error;
|
|
610
|
+
}
|
|
611
|
+
const prefix = `${path.basename(paths.claimsFile)}.tmp-`;
|
|
612
|
+
const candidates = entries.filter((entry) => entry.startsWith(prefix)).sort();
|
|
613
|
+
if (candidates.length === 0)
|
|
614
|
+
return recoveryResult("clean");
|
|
615
|
+
const authoritySnapshot = await readAuthoritySnapshot(paths);
|
|
616
|
+
let authorityReadable = true;
|
|
617
|
+
try {
|
|
618
|
+
parseClaims(authoritySnapshot.raw);
|
|
619
|
+
}
|
|
620
|
+
catch {
|
|
621
|
+
authorityReadable = false;
|
|
622
|
+
}
|
|
623
|
+
const removed = [];
|
|
624
|
+
const preserved = new Set();
|
|
625
|
+
const invalid = [];
|
|
626
|
+
const valid = [];
|
|
627
|
+
for (const name of candidates) {
|
|
628
|
+
const filePath = path.join(paths.directory, name);
|
|
629
|
+
let stage;
|
|
630
|
+
try {
|
|
631
|
+
stage = await readRegularFileIfPresent(filePath, paths);
|
|
632
|
+
}
|
|
633
|
+
catch {
|
|
634
|
+
preserved.add(name);
|
|
635
|
+
continue;
|
|
636
|
+
}
|
|
637
|
+
if (stage.identity === undefined) {
|
|
638
|
+
preserved.add(name);
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
try {
|
|
642
|
+
parseClaims(stage.text);
|
|
643
|
+
valid.push({ name, filePath, raw: stage.text, identity: stage.identity });
|
|
644
|
+
}
|
|
645
|
+
catch {
|
|
646
|
+
invalid.push({ name, filePath, identity: stage.identity });
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
for (const stage of invalid) {
|
|
650
|
+
if (await removeSafeStage(stage.filePath, stage.identity, paths))
|
|
651
|
+
removed.push(stage.name);
|
|
652
|
+
else
|
|
653
|
+
preserved.add(stage.name);
|
|
654
|
+
}
|
|
655
|
+
if (!authorityReadable || preserved.size > 0) {
|
|
656
|
+
for (const stage of valid)
|
|
657
|
+
preserved.add(stage.name);
|
|
658
|
+
return recoveryResult("recovery-required", removed, [...preserved].sort());
|
|
659
|
+
}
|
|
660
|
+
const groups = new Map();
|
|
661
|
+
for (const stage of valid)
|
|
662
|
+
groups.set(stage.raw, [...(groups.get(stage.raw) ?? []), stage]);
|
|
663
|
+
if (groups.size > 1)
|
|
664
|
+
return recoveryResult("recovery-required", removed, valid.map((stage) => stage.name).sort());
|
|
665
|
+
const group = groups.values().next().value;
|
|
666
|
+
if (group === undefined)
|
|
667
|
+
return recoveryResult("clean", removed);
|
|
668
|
+
const authority = authoritySnapshot.raw;
|
|
669
|
+
const selected = group[0];
|
|
670
|
+
if (selected.raw === authority) {
|
|
671
|
+
for (const stage of group) {
|
|
672
|
+
if (await removeSafeStage(stage.filePath, stage.identity, paths))
|
|
673
|
+
removed.push(stage.name);
|
|
674
|
+
else
|
|
675
|
+
preserved.add(stage.name);
|
|
676
|
+
}
|
|
677
|
+
return preserved.size === 0
|
|
678
|
+
? recoveryResult("clean", removed)
|
|
679
|
+
: recoveryResult("recovery-required", removed, [...preserved].sort());
|
|
680
|
+
}
|
|
681
|
+
if (!selected.raw.startsWith(authority) || selected.raw.length <= authority.length) {
|
|
682
|
+
return recoveryResult("recovery-required", removed, group.map((stage) => stage.name).sort());
|
|
683
|
+
}
|
|
684
|
+
await promoteStage(selected.filePath, paths.claimsFile, paths, authoritySnapshot);
|
|
685
|
+
removed.push(selected.name);
|
|
686
|
+
for (const duplicate of group.slice(1)) {
|
|
687
|
+
if (await removeSafeStage(duplicate.filePath, duplicate.identity, paths))
|
|
688
|
+
removed.push(duplicate.name);
|
|
689
|
+
else
|
|
690
|
+
preserved.add(duplicate.name);
|
|
691
|
+
}
|
|
692
|
+
return preserved.size === 0
|
|
693
|
+
? recoveryResult("recovered", removed)
|
|
694
|
+
: recoveryResult("recovery-required", removed, [...preserved].sort());
|
|
695
|
+
}
|
|
696
|
+
async function processExists(pid) {
|
|
697
|
+
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
698
|
+
return false;
|
|
699
|
+
try {
|
|
700
|
+
process.kill(pid, 0);
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
catch (error) {
|
|
704
|
+
return error instanceof Error && "code" in error && error.code === "EPERM";
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
async function acquireLock(paths) {
|
|
708
|
+
const currentPaths = await ensureKnowledgeDirectory(paths);
|
|
709
|
+
const nonce = randomUUID();
|
|
710
|
+
for (let attempt = 0; attempt < 80; attempt += 1) {
|
|
711
|
+
try {
|
|
712
|
+
const lockParent = await pinParent(currentPaths.lockDirectory, currentPaths);
|
|
713
|
+
await assertParentStable(lockParent, currentPaths);
|
|
714
|
+
await fs.mkdir(currentPaths.lockDirectory);
|
|
715
|
+
await writePrivateFile(path.join(currentPaths.lockDirectory, "owner.json"), JSON.stringify({ pid: process.pid, nonce }), currentPaths);
|
|
716
|
+
return {
|
|
717
|
+
paths: currentPaths,
|
|
718
|
+
release: async () => {
|
|
719
|
+
try {
|
|
720
|
+
const owner = JSON.parse((await readRegularFileIfPresent(path.join(currentPaths.lockDirectory, "owner.json"), currentPaths)).text);
|
|
721
|
+
if (owner.nonce === nonce) {
|
|
722
|
+
const releaseParent = await pinParent(currentPaths.lockDirectory, currentPaths);
|
|
723
|
+
await assertParentStable(releaseParent, currentPaths);
|
|
724
|
+
await fs.rm(currentPaths.lockDirectory, { recursive: true, force: true });
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
catch (error) {
|
|
728
|
+
if (!hasErrorCode(error, "ENOENT"))
|
|
729
|
+
throw error;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
catch (error) {
|
|
735
|
+
if (!hasErrorCode(error, "EEXIST"))
|
|
736
|
+
throw error;
|
|
737
|
+
let lockStat;
|
|
738
|
+
try {
|
|
739
|
+
await assertPinnedDirectories(currentPaths);
|
|
740
|
+
lockStat = await fs.lstat(currentPaths.lockDirectory);
|
|
741
|
+
}
|
|
742
|
+
catch (lockError) {
|
|
743
|
+
if (hasErrorCode(lockError, "ENOENT"))
|
|
744
|
+
continue;
|
|
745
|
+
throw lockError;
|
|
746
|
+
}
|
|
747
|
+
if (lockStat.isSymbolicLink() || !lockStat.isDirectory()) {
|
|
748
|
+
throw new KnowledgeStoreError(`Unsafe knowledge lock at ${paths.lockDirectory}.`);
|
|
749
|
+
}
|
|
750
|
+
try {
|
|
751
|
+
const ownerPath = path.join(currentPaths.lockDirectory, "owner.json");
|
|
752
|
+
const owner = parseLockOwner(JSON.parse((await readRegularFileIfPresent(ownerPath, currentPaths, { allowMissing: false })).text), ownerPath);
|
|
753
|
+
if (!(await processExists(owner.pid))) {
|
|
754
|
+
const staleParent = await pinParent(currentPaths.lockDirectory, currentPaths);
|
|
755
|
+
await assertParentStable(staleParent, currentPaths);
|
|
756
|
+
await fs.rm(currentPaths.lockDirectory, { recursive: true, force: true });
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
catch (ownerError) {
|
|
761
|
+
if (ownerError instanceof KnowledgeFileDisappearedError) {
|
|
762
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
763
|
+
continue;
|
|
764
|
+
}
|
|
765
|
+
if (isParentIdentityRace(ownerError, currentPaths.lockDirectory)) {
|
|
766
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
if (hasErrorCode(ownerError, "ENOENT")) {
|
|
770
|
+
try {
|
|
771
|
+
await assertPinnedDirectories(currentPaths);
|
|
772
|
+
const currentLockStat = await fs.lstat(currentPaths.lockDirectory);
|
|
773
|
+
if (currentLockStat.isSymbolicLink() || !currentLockStat.isDirectory()) {
|
|
774
|
+
throw new KnowledgeStoreError(`Unsafe knowledge lock at ${paths.lockDirectory}.`);
|
|
775
|
+
}
|
|
776
|
+
if (!sameIdentity(identityOf(currentLockStat), identityOf(lockStat))) {
|
|
777
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
778
|
+
continue;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
catch (lockError) {
|
|
782
|
+
if (hasErrorCode(lockError, "ENOENT")) {
|
|
783
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
784
|
+
continue;
|
|
785
|
+
}
|
|
786
|
+
throw lockError;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
throw ownerError;
|
|
790
|
+
}
|
|
791
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
throw new KnowledgeStoreError("Knowledge mutation lock timed out.");
|
|
795
|
+
}
|
|
796
|
+
async function appendRecord(paths, record, expectedAuthority) {
|
|
797
|
+
const lock = await acquireLock(paths);
|
|
798
|
+
try {
|
|
799
|
+
const currentPaths = lock.paths;
|
|
800
|
+
let authoritySnapshot = await readAuthoritySnapshot(currentPaths);
|
|
801
|
+
const authorityChangedAfterObservation = expectedAuthority !== undefined && !sameAuthoritySnapshot(expectedAuthority, authoritySnapshot);
|
|
802
|
+
const recovery = await recoverUnlocked(currentPaths);
|
|
803
|
+
if (recovery.recoveryRequired) {
|
|
804
|
+
throw new KnowledgeStoreError("Knowledge recovery is required before the next mutation.");
|
|
805
|
+
}
|
|
806
|
+
if (recovery.recovered)
|
|
807
|
+
authoritySnapshot = await readAuthoritySnapshot(currentPaths);
|
|
808
|
+
const authority = authoritySnapshot.raw;
|
|
809
|
+
const current = latestRecords(parseClaims(authority)).get(record.id);
|
|
810
|
+
if (current !== undefined && (record.state === "review-needed" || current.state === record.state))
|
|
811
|
+
return false;
|
|
812
|
+
if (authorityChangedAfterObservation)
|
|
813
|
+
throw new KnowledgeStoreError("Knowledge authority changed after the initial observation.");
|
|
814
|
+
const line = `${authority !== "" && !authority.endsWith("\n") ? "\n" : ""}${JSON.stringify(record)}\n`;
|
|
815
|
+
const stage = `${currentPaths.claimsFile}.tmp-${createHash("sha256").update(line).digest("hex").slice(0, 16)}`;
|
|
816
|
+
await writePrivateFile(stage, `${authority}${line}`, currentPaths);
|
|
817
|
+
await promoteStage(stage, currentPaths.claimsFile, currentPaths, authoritySnapshot);
|
|
818
|
+
return true;
|
|
819
|
+
}
|
|
820
|
+
finally {
|
|
821
|
+
await lock.release();
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
function latestRecords(claims) {
|
|
825
|
+
const latest = new Map();
|
|
826
|
+
for (const claim of claims)
|
|
827
|
+
latest.set(claim.id, claim);
|
|
828
|
+
return latest;
|
|
829
|
+
}
|
|
830
|
+
export async function readKnowledgeClaims(projectRoot) {
|
|
831
|
+
const raw = await readAuthority(knowledgePaths(projectRoot));
|
|
832
|
+
return parseClaims(raw);
|
|
833
|
+
}
|
|
834
|
+
export async function recoverKnowledge(projectRoot) {
|
|
835
|
+
const paths = knowledgePaths(projectRoot);
|
|
836
|
+
if (!(await knowledgeDirectoryExists(paths)))
|
|
837
|
+
return recoveryResult("clean");
|
|
838
|
+
const lock = await acquireLock(paths);
|
|
839
|
+
try {
|
|
840
|
+
return await recoverUnlocked(lock.paths);
|
|
841
|
+
}
|
|
842
|
+
finally {
|
|
843
|
+
await lock.release();
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
export async function captureKnowledgeEvent(projectRoot, value, options) {
|
|
847
|
+
if (options.captureEnabled === false)
|
|
848
|
+
return { status: "disabled" };
|
|
849
|
+
if (!validSurface(options.surface) || containsSecret(options.surface))
|
|
850
|
+
return { status: "rejected", reason: "INVALID_SURFACE" };
|
|
851
|
+
const parsed = parseKnowledgeEvent(value);
|
|
852
|
+
if (parsed.event === undefined)
|
|
853
|
+
return { status: "rejected", reason: parsed.reason ?? "INVALID_EVENT" };
|
|
854
|
+
const id = stableKnowledgeId(parsed.event);
|
|
855
|
+
const paths = knowledgePaths(projectRoot);
|
|
856
|
+
const authoritySnapshot = await readAuthoritySnapshot(paths);
|
|
857
|
+
const latest = latestRecords(parseClaims(authoritySnapshot.raw));
|
|
858
|
+
const existing = latest.get(id);
|
|
859
|
+
if (existing !== undefined)
|
|
860
|
+
return { status: "duplicate", record: existing };
|
|
861
|
+
const record = Object.freeze({
|
|
862
|
+
id,
|
|
863
|
+
text: parsed.event.text,
|
|
864
|
+
kind: parsed.event.kind,
|
|
865
|
+
state: "review-needed",
|
|
866
|
+
timestamp: (options.now?.() ?? new Date()).toISOString(),
|
|
867
|
+
provenance: Object.freeze({ product: "litopencode", surface: options.surface, source: parsed.event.source }),
|
|
868
|
+
evidence: Object.freeze({ ref: parsed.event.evidenceRef })
|
|
869
|
+
});
|
|
870
|
+
if (await appendRecord(paths, record, authoritySnapshot))
|
|
871
|
+
return { status: "captured", record };
|
|
872
|
+
const duplicate = latestRecords(parseClaims(await readAuthority(paths))).get(id);
|
|
873
|
+
return duplicate === undefined ? { status: "rejected", reason: "STORE_RACE" } : { status: "duplicate", record: duplicate };
|
|
874
|
+
}
|
|
875
|
+
export async function reviewKnowledgeRecord(projectRoot, input) {
|
|
876
|
+
if (!/^kn_[a-f0-9]{24}$/u.test(input.id))
|
|
877
|
+
return { status: "blocked", reason: "INVALID_ID" };
|
|
878
|
+
if (!["accepted", "rejected", "stale"].includes(input.state))
|
|
879
|
+
return { status: "blocked", reason: "INVALID_STATE" };
|
|
880
|
+
if (!validSurface(input.surface) || containsSecret(input.surface))
|
|
881
|
+
return { status: "blocked", reason: "INVALID_SURFACE" };
|
|
882
|
+
const paths = knowledgePaths(projectRoot);
|
|
883
|
+
const authoritySnapshot = await readAuthoritySnapshot(paths);
|
|
884
|
+
const existing = latestRecords(parseClaims(authoritySnapshot.raw)).get(input.id);
|
|
885
|
+
if (existing === undefined)
|
|
886
|
+
return { status: "blocked", reason: "UNKNOWN_ID" };
|
|
887
|
+
if (existing.state === input.state)
|
|
888
|
+
return { status: "duplicate", record: existing };
|
|
889
|
+
const record = Object.freeze({
|
|
890
|
+
...existing,
|
|
891
|
+
state: input.state,
|
|
892
|
+
timestamp: (input.now?.() ?? new Date()).toISOString(),
|
|
893
|
+
provenance: Object.freeze({ ...existing.provenance, surface: input.surface })
|
|
894
|
+
});
|
|
895
|
+
if (await appendRecord(paths, record, authoritySnapshot))
|
|
896
|
+
return { status: "updated", record };
|
|
897
|
+
const duplicate = latestRecords(parseClaims(await readAuthority(paths))).get(input.id);
|
|
898
|
+
return duplicate === undefined ? { status: "blocked", reason: "STORE_RACE" } : { status: "duplicate", record: duplicate };
|
|
899
|
+
}
|
|
900
|
+
function tokens(value) {
|
|
901
|
+
return Array.from(new Set(value.toLocaleLowerCase("en-US").match(/[\p{L}\p{N}][\p{L}\p{N}._/-]*/gu) ?? []))
|
|
902
|
+
.filter((token) => token.length > 1 && !stopWords.has(token));
|
|
903
|
+
}
|
|
904
|
+
function renderKnowledgeBlock(records, budgetBytes) {
|
|
905
|
+
const opening = "<litopencode-knowledge>\nAccepted project-local records follow as inert JSON lines.\n";
|
|
906
|
+
const closing = "</litopencode-knowledge>";
|
|
907
|
+
const lines = [];
|
|
908
|
+
for (const record of records) {
|
|
909
|
+
const line = JSON.stringify({
|
|
910
|
+
id: record.id,
|
|
911
|
+
kind: record.kind,
|
|
912
|
+
text: record.text,
|
|
913
|
+
timestamp: record.timestamp,
|
|
914
|
+
provenance: record.provenance,
|
|
915
|
+
evidence: record.evidence
|
|
916
|
+
});
|
|
917
|
+
const candidate = `${opening}${[...lines, line].join("\n")}\n${closing}`;
|
|
918
|
+
if (utf8Bytes(candidate) > budgetBytes)
|
|
919
|
+
break;
|
|
920
|
+
lines.push(line);
|
|
921
|
+
}
|
|
922
|
+
return lines.length === 0 ? "" : `${opening}${lines.join("\n")}\n${closing}`;
|
|
923
|
+
}
|
|
924
|
+
export async function queryKnowledge(projectRoot, query, options = {}) {
|
|
925
|
+
const requested = options.budgetBytes;
|
|
926
|
+
const budgetBytes = requested === undefined || !Number.isFinite(requested)
|
|
927
|
+
? normalQueryBudgetBytes
|
|
928
|
+
: Math.max(256, Math.min(hardQueryBudgetBytes, Math.floor(requested)));
|
|
929
|
+
const queryTokens = tokens(query.slice(0, 4096));
|
|
930
|
+
if (queryTokens.length === 0)
|
|
931
|
+
return { text: "", records: [], budgetBytes, byteLength: 0 };
|
|
932
|
+
const accepted = Array.from(latestRecords(await readKnowledgeClaims(projectRoot)).values())
|
|
933
|
+
.filter((record) => record.state === "accepted")
|
|
934
|
+
.map((record) => {
|
|
935
|
+
const haystack = new Set(tokens(`${record.text} ${record.kind} ${record.evidence.ref}`));
|
|
936
|
+
return { record, score: queryTokens.reduce((score, token) => score + (haystack.has(token) ? 1 : 0), 0) };
|
|
937
|
+
})
|
|
938
|
+
.filter((candidate) => candidate.score > 0)
|
|
939
|
+
.sort((left, right) => right.score - left.score || right.record.timestamp.localeCompare(left.record.timestamp) || left.record.id.localeCompare(right.record.id));
|
|
940
|
+
if (accepted.length === 0)
|
|
941
|
+
return { text: "", records: [], budgetBytes, byteLength: 0 };
|
|
942
|
+
const ordered = accepted.map((candidate) => candidate.record);
|
|
943
|
+
const text = renderKnowledgeBlock(ordered, budgetBytes);
|
|
944
|
+
if (text === "")
|
|
945
|
+
return { text: "", records: [], budgetBytes, byteLength: 0 };
|
|
946
|
+
const includedIds = new Set(Array.from(text.matchAll(/"id":"(kn_[a-f0-9]{24})"/gu), (match) => match[1]));
|
|
947
|
+
const records = ordered.filter((record) => includedIds.has(record.id));
|
|
948
|
+
return { text, records: Object.freeze(records), budgetBytes, byteLength: utf8Bytes(text) };
|
|
949
|
+
}
|
|
950
|
+
export async function inspectKnowledge(projectRoot) {
|
|
951
|
+
const claims = await readKnowledgeClaims(projectRoot);
|
|
952
|
+
const counts = { "review-needed": 0, accepted: 0, rejected: 0, stale: 0 };
|
|
953
|
+
for (const record of latestRecords(claims).values())
|
|
954
|
+
counts[record.state] += 1;
|
|
955
|
+
return {
|
|
956
|
+
authority: "claims.jsonl",
|
|
957
|
+
path: knowledgePaths(projectRoot).claimsFile,
|
|
958
|
+
counts,
|
|
959
|
+
derivedTruthFiles: 0
|
|
960
|
+
};
|
|
961
|
+
}
|