@fusengine/harness 0.1.90 → 0.1.92
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/dist/adapters/claude/index.d.mts +1 -1
- package/dist/adapters/claude/index.mjs +1 -1
- package/dist/adapters/cline/index.mjs +1 -1
- package/dist/adapters/codex/index.d.mts +2 -2
- package/dist/adapters/codex/index.mjs +3 -2
- package/dist/adapters/cursor/index.d.mts +15 -28
- package/dist/adapters/cursor/index.mjs +35 -46
- package/dist/adapters/gemini/index.mjs +1 -1
- package/dist/adapters/hermes/index.d.mts +1 -1
- package/dist/adapters/hermes/index.mjs +1 -1
- package/dist/adapters/kimi/index.d.mts +1 -1
- package/dist/adapters/kimi/index.mjs +1 -1
- package/dist/{claude-5bC8TDAz.mjs → claude-Ckv2_TgP.mjs} +3 -43
- package/dist/cli/bin.mjs +429 -16
- package/dist/cli/index.mjs +1 -1
- package/dist/command-string-CALMTnwN.mjs +42 -0
- package/dist/{evaluate-C1VSI5pb.mjs → evaluate-CMiIqHeH.mjs} +261 -13
- package/dist/{handle-CBaOcFZq.mjs → handle-BF1dZFjY.mjs} +1194 -144
- package/dist/{hermes-kP6NUQlG.mjs → hermes-B9-p_3IF.mjs} +3 -2
- package/dist/{index-DBgnIZn9.d.mts → index-B8PG82su.d.mts} +1 -1
- package/dist/{index-D4C2-IIc.d.mts → index-SmKYkk2N.d.mts} +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/init/index.d.mts +1 -1
- package/dist/init/index.mjs +1 -1
- package/dist/{kimi-D6X7b8wF.mjs → kimi-G2wcSh5-.mjs} +3 -2
- package/dist/normalize-Dy8g9Ybl.mjs +397 -0
- package/dist/policy/index.d.mts +1 -1
- package/dist/policy/index.mjs +2 -2
- package/dist/prompt/index.d.mts +1 -1
- package/dist/{run-CdIlUtjv.mjs → run-DkrzC0gb.mjs} +1 -1
- package/dist/{run-TAXNRSpD.mjs → run-GHAMqcOn.mjs} +26 -3
- package/dist/runtime/index.d.mts +37 -16
- package/dist/runtime/index.mjs +2 -2
- package/dist/{types-C0-igRKr.d.mts → types-Bh0jEF3_.d.mts} +2 -0
- package/dist/{validate-DZFtAbLP.mjs → validate-KjZ1X9tH.mjs} +1 -1
- package/package.json +3 -2
- package/src/adapters/claude/index.ts +92 -0
- package/src/adapters/cline/index.ts +36 -0
- package/src/adapters/codex/apply-patch.ts +94 -0
- package/src/adapters/codex/index.ts +84 -0
- package/src/adapters/codex/permission-mode.ts +16 -0
- package/src/adapters/cursor/context-budget.ts +144 -0
- package/src/adapters/cursor/context-limit.ts +115 -0
- package/src/adapters/cursor/context.ts +65 -0
- package/src/adapters/cursor/events.ts +38 -0
- package/src/adapters/cursor/index.ts +43 -0
- package/src/adapters/cursor/interfaces/context-budget.ts +25 -0
- package/src/adapters/cursor/interfaces/types.ts +88 -0
- package/src/adapters/cursor/native-response.ts +39 -0
- package/src/adapters/cursor/native-schemas.ts +161 -0
- package/src/adapters/cursor/normalize.ts +188 -0
- package/src/adapters/cursor/plugin-root.ts +103 -0
- package/src/adapters/cursor/respond.ts +170 -0
- package/src/adapters/gemini/index.ts +36 -0
- package/src/adapters/hermes/index.ts +55 -0
- package/src/adapters/hermes/interfaces/types.ts +26 -0
- package/src/adapters/kimi/index.ts +96 -0
- package/src/adapters/kimi/interfaces/types.ts +55 -0
- package/src/cache/compact.ts +55 -0
- package/src/cache/index.ts +5 -0
- package/src/cache/io.ts +38 -0
- package/src/cache/mcp-response.ts +39 -0
- package/src/cache/mcp-store.ts +70 -0
- package/src/cache/store.ts +93 -0
- package/src/changelog/fetch.ts +79 -0
- package/src/cli/bin.ts +100 -0
- package/src/cli/cursor-event-scanner.ts +175 -0
- package/src/cli/cursor-stdin-reader.ts +65 -0
- package/src/cli/doctor.ts +96 -0
- package/src/cli/hook-io.ts +130 -0
- package/src/cli/hook-sound.ts +49 -0
- package/src/cli/index.ts +1 -0
- package/src/cli/json-primitive-scanner.ts +83 -0
- package/src/cli/run.ts +36 -0
- package/src/cli/scope.ts +31 -0
- package/src/codex-rules/build.ts +63 -0
- package/src/codex-rules/index.ts +8 -0
- package/src/codex-rules/mapping.ts +42 -0
- package/src/codex-rules/rules/absolute-paths.ts +52 -0
- package/src/codex-rules/rules/ask.ts +44 -0
- package/src/codex-rules/rules/chmod-ask.ts +57 -0
- package/src/codex-rules/rules/dd.ts +23 -0
- package/src/codex-rules/rules/diskutil.ts +33 -0
- package/src/codex-rules/rules/mkfs.ts +42 -0
- package/src/codex-rules/rules/privilege.ts +49 -0
- package/src/codex-rules/rules/remote-fetch.ts +31 -0
- package/src/codex-rules/rules/rm-variants.ts +58 -0
- package/src/codex-rules/rules/single-commands.ts +57 -0
- package/src/codex-rules/skip-list.ts +25 -0
- package/src/codex-rules/starlark.ts +46 -0
- package/src/codex-rules/types.ts +36 -0
- package/src/config/dotenv.ts +55 -0
- package/src/config/env.ts +10 -0
- package/src/config/home-dir.ts +36 -0
- package/src/config/index.ts +5 -0
- package/src/config/layout.ts +52 -0
- package/src/config/limits.ts +56 -0
- package/src/config/ttl.ts +30 -0
- package/src/detect/harness.ts +73 -0
- package/src/detect/index.ts +1 -0
- package/src/detect/interfaces/types.ts +18 -0
- package/src/freshness/agent-evidence-record.ts +95 -0
- package/src/freshness/agent-evidence.ts +95 -0
- package/src/freshness/codex-spawn-evidence.ts +91 -0
- package/src/freshness/doc-helpers.ts +90 -0
- package/src/freshness/evidence-harvest-io.ts +72 -0
- package/src/freshness/evidence-harvest.ts +61 -0
- package/src/freshness/explore-tools.ts +95 -0
- package/src/freshness/index.ts +2 -0
- package/src/freshness/query-framework.ts +73 -0
- package/src/freshness/ref-evidence.ts +49 -0
- package/src/freshness/ref-journal.ts +98 -0
- package/src/freshness/trivial-edit-counter.ts +30 -0
- package/src/index.ts +11 -0
- package/src/init/index.ts +2 -0
- package/src/init/run.ts +32 -0
- package/src/init/templates.ts +75 -0
- package/src/memory/gitignore.ts +14 -0
- package/src/memory/index.ts +3 -0
- package/src/memory/registry.ts +39 -0
- package/src/memory/session-roots.ts +99 -0
- package/src/memory/state.ts +58 -0
- package/src/policy/apex-authorization.ts +100 -0
- package/src/policy/apex-gates.ts +91 -0
- package/src/policy/apex-target-steps.ts +73 -0
- package/src/policy/apex-target.ts +49 -0
- package/src/policy/apex-task-context.ts +81 -0
- package/src/policy/apex.ts +94 -0
- package/src/policy/cartographer/build-tree.ts +69 -0
- package/src/policy/cartographer/describe.ts +53 -0
- package/src/policy/cartographer/entry.ts +38 -0
- package/src/policy/cartographer/frontmatter.ts +48 -0
- package/src/policy/cartographer/indicators.ts +34 -0
- package/src/policy/claude-md-context.ts +66 -0
- package/src/policy/conventions/index.ts +11 -0
- package/src/policy/conventions/interfaces.ts +71 -0
- package/src/policy/conventions/langs.ts +53 -0
- package/src/policy/conventions/query.ts +49 -0
- package/src/policy/conventions/react-hooks.ts +18 -0
- package/src/policy/conventions/store-or-query.ts +21 -0
- package/src/policy/conventions/stores.ts +56 -0
- package/src/policy/conventions/strip.ts +87 -0
- package/src/policy/conventions/verdict.ts +38 -0
- package/src/policy/creation-intent.ts +11 -0
- package/src/policy/deny-loop.ts +88 -0
- package/src/policy/design/allowed-write.ts +28 -0
- package/src/policy/design/content-checks.ts +45 -0
- package/src/policy/design/corpus-fs.ts +21 -0
- package/src/policy/design/corpus-resolve-cache.ts +49 -0
- package/src/policy/design/corpus-resolve.ts +99 -0
- package/src/policy/design/corpus.ts +92 -0
- package/src/policy/design/design-system-rules.ts +45 -0
- package/src/policy/design/flag.ts +30 -0
- package/src/policy/design/gates-pipeline.ts +105 -0
- package/src/policy/design/gates.ts +87 -0
- package/src/policy/design/screenshot-tools.ts +24 -0
- package/src/policy/design/skill-evidence.ts +34 -0
- package/src/policy/design/skill-gate.ts +81 -0
- package/src/policy/design/skill-triggers.ts +51 -0
- package/src/policy/design/state.ts +92 -0
- package/src/policy/design/template-urls.ts +54 -0
- package/src/policy/design/transitions.ts +68 -0
- package/src/policy/detect-claude-md-project-type.ts +32 -0
- package/src/policy/detect-framework.ts +99 -0
- package/src/policy/detect-primitive-checks.ts +100 -0
- package/src/policy/detect-primitive-lib.ts +55 -0
- package/src/policy/detect-project.ts +99 -0
- package/src/policy/edit-outcome.ts +53 -0
- package/src/policy/evaluate.ts +81 -0
- package/src/policy/expert-agents.ts +60 -0
- package/src/policy/file-size-scope.ts +32 -0
- package/src/policy/file-size.ts +99 -0
- package/src/policy/framework-solid-exclude.ts +52 -0
- package/src/policy/framework-solid-extended.ts +93 -0
- package/src/policy/framework-solid-gates-systems.ts +99 -0
- package/src/policy/framework-solid-gates.ts +98 -0
- package/src/policy/framework-solid.ts +88 -0
- package/src/policy/gemini-mcp-gate.ts +83 -0
- package/src/policy/git-gates.ts +40 -0
- package/src/policy/guards/bash-command-anchor.ts +40 -0
- package/src/policy/guards/bash-write-patterns.ts +140 -0
- package/src/policy/guards/bash-write-redirects.ts +201 -0
- package/src/policy/guards/bash-write-safe-paths.ts +62 -0
- package/src/policy/guards/bash-write.ts +76 -0
- package/src/policy/guards/context.ts +12 -0
- package/src/policy/guards/index.ts +61 -0
- package/src/policy/guards/install.ts +22 -0
- package/src/policy/guards/interface-separation-ext.ts +41 -0
- package/src/policy/guards/interface-separation.ts +100 -0
- package/src/policy/guards/protected-path.ts +100 -0
- package/src/policy/guards/security.ts +97 -0
- package/src/policy/index.ts +19 -0
- package/src/policy/interfaces/types.ts +46 -0
- package/src/policy/js-gate-route.ts +51 -0
- package/src/policy/lessons/lesson-gate.ts +0 -0
- package/src/policy/lessons/trigger-index.ts +84 -0
- package/src/policy/lessons/types.ts +31 -0
- package/src/policy/module-layout.ts +31 -0
- package/src/policy/nearest-manifest.ts +75 -0
- package/src/policy/never-approval.ts +95 -0
- package/src/policy/patterns.ts +80 -0
- package/src/policy/seo/validate.ts +51 -0
- package/src/policy/shadcn-project.ts +22 -0
- package/src/policy/shadcn-skill-gate.ts +97 -0
- package/src/policy/shell-read-refs.ts +96 -0
- package/src/policy/skill-path.ts +46 -0
- package/src/policy/skill-patterns/laravel.ts +40 -0
- package/src/policy/skill-patterns/nextjs.ts +29 -0
- package/src/policy/skill-patterns/react.ts +27 -0
- package/src/policy/skill-patterns/shadcn-subskills.ts +33 -0
- package/src/policy/skill-patterns/shadcn.ts +66 -0
- package/src/policy/skill-patterns/swift.ts +30 -0
- package/src/policy/skill-patterns/tailwind.ts +40 -0
- package/src/policy/skill-source.ts +52 -0
- package/src/policy/skill-trigger-patterns.ts +33 -0
- package/src/policy/skill-triggers.ts +108 -0
- package/src/policy/tailwind-skill-gate.ts +63 -0
- package/src/policy/trivial-edits.ts +52 -0
- package/src/policy/verbosity.ts +39 -0
- package/src/prompt/index.ts +1 -0
- package/src/prompt/types.ts +36 -0
- package/src/refs/discover.ts +69 -0
- package/src/refs/frontmatter.ts +24 -0
- package/src/refs/index.ts +4 -0
- package/src/refs/loader.ts +59 -0
- package/src/refs/ref-key.ts +41 -0
- package/src/refs/router.ts +63 -0
- package/src/refs/types.ts +25 -0
- package/src/runtime/activity.ts +99 -0
- package/src/runtime/apply-patch-gate.ts +41 -0
- package/src/runtime/burst-window.ts +21 -0
- package/src/runtime/codex-shell-tool.ts +37 -0
- package/src/runtime/command-string.ts +37 -0
- package/src/runtime/confirm/codex-confirm.ts +126 -0
- package/src/runtime/confirm/codex-prompt-origin.ts +16 -0
- package/src/runtime/confirm/confirm-code.ts +33 -0
- package/src/runtime/confirm/confirm-gate.ts +65 -0
- package/src/runtime/confirm/confirm-irreversible.ts +32 -0
- package/src/runtime/confirm/confirm-pending.ts +34 -0
- package/src/runtime/confirm/confirm-state.ts +56 -0
- package/src/runtime/confirm/confirm-subagent.ts +112 -0
- package/src/runtime/confirm/confirm-submit.ts +38 -0
- package/src/runtime/core-guards-active.ts +84 -0
- package/src/runtime/core-guards-wired.ts +47 -0
- package/src/runtime/deny-loop-store.ts +94 -0
- package/src/runtime/deny-notice.ts +52 -0
- package/src/runtime/design-cache-resolve.ts +51 -0
- package/src/runtime/design-content-gate.ts +95 -0
- package/src/runtime/design-files-gate.ts +114 -0
- package/src/runtime/design-helpers.ts +93 -0
- package/src/runtime/design-lifecycle.ts +39 -0
- package/src/runtime/design-read-credit.ts +28 -0
- package/src/runtime/design.ts +116 -0
- package/src/runtime/dev-context.ts +43 -0
- package/src/runtime/dry-patterns.ts +32 -0
- package/src/runtime/dry.ts +96 -0
- package/src/runtime/fragment-registry.ts +33 -0
- package/src/runtime/framework-skill-gate.ts +79 -0
- package/src/runtime/fs-cleanup.ts +71 -0
- package/src/runtime/gate-apex.ts +81 -0
- package/src/runtime/gate-helpers.ts +52 -0
- package/src/runtime/gate-input.ts +26 -0
- package/src/runtime/gate.ts +128 -0
- package/src/runtime/handle-post.ts +104 -0
- package/src/runtime/handle-pre.ts +137 -0
- package/src/runtime/handle-scope-async.ts +29 -0
- package/src/runtime/handle-types.ts +27 -0
- package/src/runtime/handle.ts +197 -0
- package/src/runtime/home-state.ts +55 -0
- package/src/runtime/index.ts +18 -0
- package/src/runtime/inform.ts +28 -0
- package/src/runtime/inject-budget-recap.ts +47 -0
- package/src/runtime/inject-budget.ts +81 -0
- package/src/runtime/inject-context.ts +85 -0
- package/src/runtime/inject-dedup-exclusive.ts +98 -0
- package/src/runtime/inject-dedup.ts +89 -0
- package/src/runtime/is-agent-tool.ts +19 -0
- package/src/runtime/lifecycle/agent-files.ts +49 -0
- package/src/runtime/lifecycle/agent-memory.ts +82 -0
- package/src/runtime/lifecycle/agent-transcript.ts +80 -0
- package/src/runtime/lifecycle/aipilot/analytics.ts +99 -0
- package/src/runtime/lifecycle/aipilot/apex-task-store.ts +61 -0
- package/src/runtime/lifecycle/aipilot/cache-base.ts +48 -0
- package/src/runtime/lifecycle/aipilot/cache-doc.ts +100 -0
- package/src/runtime/lifecycle/aipilot/cache-lessons.ts +58 -0
- package/src/runtime/lifecycle/aipilot/cache-test.ts +70 -0
- package/src/runtime/lifecycle/aipilot/curate-lessons.ts +64 -0
- package/src/runtime/lifecycle/aipilot/dispatch-aipilot.ts +106 -0
- package/src/runtime/lifecycle/aipilot/doc-cache-gate.ts +69 -0
- package/src/runtime/lifecycle/aipilot/inject-apex.ts +98 -0
- package/src/runtime/lifecycle/aipilot/inject-doc.ts +62 -0
- package/src/runtime/lifecycle/aipilot/inject-explore.ts +64 -0
- package/src/runtime/lifecycle/aipilot/inject-lessons.ts +61 -0
- package/src/runtime/lifecycle/aipilot/inject-test.ts +48 -0
- package/src/runtime/lifecycle/aipilot/lesson-archive.ts +60 -0
- package/src/runtime/lifecycle/aipilot/lesson-inject.ts +98 -0
- package/src/runtime/lifecycle/aipilot/lesson-parse.ts +74 -0
- package/src/runtime/lifecycle/aipilot/lessons.ts +72 -0
- package/src/runtime/lifecycle/aipilot/promote-global-lessons.ts +92 -0
- package/src/runtime/lifecycle/aipilot/solid-compliance.ts +69 -0
- package/src/runtime/lifecycle/aipilot/solid-notice.ts +47 -0
- package/src/runtime/lifecycle/aipilot/solid-transcript.ts +60 -0
- package/src/runtime/lifecycle/aipilot/source-scan.ts +56 -0
- package/src/runtime/lifecycle/aipilot/sync-task.ts +75 -0
- package/src/runtime/lifecycle/aipilot/transcript.ts +88 -0
- package/src/runtime/lifecycle/aipilot/types.ts +72 -0
- package/src/runtime/lifecycle/auto-document-reads.ts +73 -0
- package/src/runtime/lifecycle/cartographer/detect.ts +57 -0
- package/src/runtime/lifecycle/cartographer/ecosystem-map.ts +56 -0
- package/src/runtime/lifecycle/cartographer/fs-util.ts +64 -0
- package/src/runtime/lifecycle/cartographer/merge.ts +51 -0
- package/src/runtime/lifecycle/cartographer/project-map.ts +49 -0
- package/src/runtime/lifecycle/cartographer/scan-hooks.ts +48 -0
- package/src/runtime/lifecycle/cartographer/scan.ts +81 -0
- package/src/runtime/lifecycle/cartographer/session-start.ts +33 -0
- package/src/runtime/lifecycle/cartographer/track-enrichment.ts +45 -0
- package/src/runtime/lifecycle/cartographer/write-plugin-map.ts +43 -0
- package/src/runtime/lifecycle/cartographer/write-tree.ts +44 -0
- package/src/runtime/lifecycle/changelog-research.ts +43 -0
- package/src/runtime/lifecycle/check-file-size.ts +61 -0
- package/src/runtime/lifecycle/codex-resync/fingerprint.ts +63 -0
- package/src/runtime/lifecycle/codex-resync/lock.ts +46 -0
- package/src/runtime/lifecycle/codex-resync/managed-destination.ts +39 -0
- package/src/runtime/lifecycle/codex-resync/materialize-agents.ts +57 -0
- package/src/runtime/lifecycle/codex-resync/plugin-files.ts +65 -0
- package/src/runtime/lifecycle/codex-resync/plugin-roots.ts +62 -0
- package/src/runtime/lifecycle/codex-resync/resync.ts +45 -0
- package/src/runtime/lifecycle/codex-resync/symlink-commands.ts +25 -0
- package/src/runtime/lifecycle/dispatch.ts +109 -0
- package/src/runtime/lifecycle/failure-lesson.ts +55 -0
- package/src/runtime/lifecycle/index.ts +34 -0
- package/src/runtime/lifecycle/inject-rules.ts +50 -0
- package/src/runtime/lifecycle/instructions-loaded.ts +22 -0
- package/src/runtime/lifecycle/kimi-rules-native.ts +44 -0
- package/src/runtime/lifecycle/lessons/dispatch.ts +88 -0
- package/src/runtime/lifecycle/lessons/reminder.ts +89 -0
- package/src/runtime/lifecycle/lessons/state.ts +23 -0
- package/src/runtime/lifecycle/memory/agent-lesson.ts +35 -0
- package/src/runtime/lifecycle/memory/capture-error.ts +41 -0
- package/src/runtime/lifecycle/memory/client.ts +65 -0
- package/src/runtime/lifecycle/memory/dispatch.ts +42 -0
- package/src/runtime/lifecycle/memory/recall.ts +30 -0
- package/src/runtime/lifecycle/memory/salience.ts +31 -0
- package/src/runtime/lifecycle/memory/state.ts +49 -0
- package/src/runtime/lifecycle/memory/track-ops.ts +15 -0
- package/src/runtime/lifecycle/post-compact.ts +38 -0
- package/src/runtime/lifecycle/post-edit-ts.ts +49 -0
- package/src/runtime/lifecycle/post-tracking.ts +40 -0
- package/src/runtime/lifecycle/pre-compact.ts +38 -0
- package/src/runtime/lifecycle/rules-root.ts +99 -0
- package/src/runtime/lifecycle/security/check-skill.ts +63 -0
- package/src/runtime/lifecycle/security/scan-patterns.ts +53 -0
- package/src/runtime/lifecycle/security/scan.ts +76 -0
- package/src/runtime/lifecycle/security/skill-state.ts +47 -0
- package/src/runtime/lifecycle/security/track-mcp.ts +29 -0
- package/src/runtime/lifecycle/security/track-skill-read.ts +31 -0
- package/src/runtime/lifecycle/seo/post-tool-use.ts +45 -0
- package/src/runtime/lifecycle/session-end.ts +17 -0
- package/src/runtime/lifecycle/session-start.ts +57 -0
- package/src/runtime/lifecycle/snapshot/board.ts +27 -0
- package/src/runtime/lifecycle/snapshot/format.ts +60 -0
- package/src/runtime/lifecycle/snapshot/git.ts +71 -0
- package/src/runtime/lifecycle/snapshot/index.ts +52 -0
- package/src/runtime/lifecycle/snapshot/version.ts +34 -0
- package/src/runtime/lifecycle/solid-detect.ts +56 -0
- package/src/runtime/lifecycle/stop-core.ts +35 -0
- package/src/runtime/lifecycle/subagent-cache.ts +61 -0
- package/src/runtime/lifecycle/task-completed.ts +94 -0
- package/src/runtime/lifecycle/teammate-idle-check.ts +63 -0
- package/src/runtime/lifecycle/teammate-idle.ts +28 -0
- package/src/runtime/lifecycle/tool-failure.ts +24 -0
- package/src/runtime/lifecycle/track-changes.ts +61 -0
- package/src/runtime/lifecycle/validate-solid.ts +70 -0
- package/src/runtime/lifecycle/validate-tailwind.ts +80 -0
- package/src/runtime/lifecycle-bridge.ts +74 -0
- package/src/runtime/mcp-key.ts +44 -0
- package/src/runtime/mcp-tool-name.ts +92 -0
- package/src/runtime/mcp.ts +91 -0
- package/src/runtime/modular.ts +77 -0
- package/src/runtime/normalize.ts +107 -0
- package/src/runtime/notices.ts +91 -0
- package/src/runtime/notification-sound.ts +49 -0
- package/src/runtime/notifications.ts +77 -0
- package/src/runtime/paths.ts +59 -0
- package/src/runtime/post-fanout.ts +41 -0
- package/src/runtime/post-outcome.ts +61 -0
- package/src/runtime/pre-allow.ts +97 -0
- package/src/runtime/precommit.ts +60 -0
- package/src/runtime/prompt-text.ts +38 -0
- package/src/runtime/receipt-capture.ts +34 -0
- package/src/runtime/record.ts +26 -0
- package/src/runtime/respond.ts +92 -0
- package/src/runtime/solid-file-size-gate.ts +58 -0
- package/src/runtime/solid-pre.ts +53 -0
- package/src/runtime/storage.ts +9 -0
- package/src/statusline/colors.ts +31 -0
- package/src/statusline/constants.ts +17 -0
- package/src/statusline/formatters.ts +54 -0
- package/src/statusline/index.ts +4 -0
- package/src/statusline/progress-bar.ts +46 -0
- package/src/tracking/codex-post-failure.ts +73 -0
- package/src/tracking/index.ts +2 -0
- package/src/tracking/integrity.ts +99 -0
- package/src/tracking/one-shot-dedup.ts +34 -0
- package/src/tracking/one-shot-failure.ts +42 -0
- package/src/tracking/one-shot-store.ts +95 -0
- package/src/tracking/one-shot.ts +93 -0
- package/src/tracking/receipts.ts +95 -0
- package/src/tracking/session-state.ts +94 -0
- package/src/tracking/store.ts +96 -0
- package/src/tracking/track-compact.ts +100 -0
- package/src/tracking/track-diff.ts +50 -0
- package/src/tracking/track-journal.ts +97 -0
- package/src/tracking/track-lock-sync.ts +83 -0
- package/src/tracking/track-lock.ts +67 -0
- package/src/util/compact-json.ts +23 -0
- package/src/util/index.ts +2 -0
- package/src/util/json-io.ts +47 -0
- package/src/util/project-root.ts +41 -0
- package/src/util/runtime-io.ts +85 -0
- package/src/util/semver.ts +30 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { CodexPrefixRule } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `rm` prefix rules — hand-mapped from `security.ts`'s two `rm -rf`-family
|
|
5
|
+
* CRITICAL_PATTERNS labels. Both source regexes accept `-rf`/`-fr` combined
|
|
6
|
+
* or `-r -f`/`-f -r` split flags, so each label needs 3 prefix_rule variants
|
|
7
|
+
* (combined flag + 2 split-flag orders) to avoid a flag-order gap.
|
|
8
|
+
*/
|
|
9
|
+
export const RM_VARIANT_RULES: CodexPrefixRule[] = [
|
|
10
|
+
{
|
|
11
|
+
sourceLabel: "DANGEROUS PATTERN: recursive delete of / or ~",
|
|
12
|
+
pattern: ["rm", ["-rf", "-fr"], ["/", "~"]],
|
|
13
|
+
decision: "forbidden",
|
|
14
|
+
justification: "Recursive force-delete of the filesystem root or home directory.",
|
|
15
|
+
match: ["rm -rf /", "rm -fr ~"],
|
|
16
|
+
notMatch: ["rm -rf /tmp/build"],
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
sourceLabel: "DANGEROUS PATTERN: recursive delete of / or ~",
|
|
20
|
+
pattern: ["rm", "-r", "-f", ["/", "~"]],
|
|
21
|
+
decision: "forbidden",
|
|
22
|
+
justification: "Recursive force-delete of the filesystem root or home directory (split flags).",
|
|
23
|
+
match: ["rm -r -f /", "rm -r -f ~"],
|
|
24
|
+
notMatch: ["rm -r -f /tmp/build"],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
sourceLabel: "DANGEROUS PATTERN: recursive delete of / or ~",
|
|
28
|
+
pattern: ["rm", "-f", "-r", ["/", "~"]],
|
|
29
|
+
decision: "forbidden",
|
|
30
|
+
justification: "Recursive force-delete of the filesystem root or home directory (split flags, reversed).",
|
|
31
|
+
match: ["rm -f -r /", "rm -f -r ~"],
|
|
32
|
+
notMatch: ["rm -f -r /tmp/build"],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
sourceLabel: "DANGEROUS PATTERN: recursive delete of a system directory",
|
|
36
|
+
pattern: ["rm", ["-rf", "-fr"], ["/etc", "/usr", "/var", "/bin", "/sbin", "/boot", "/lib"]],
|
|
37
|
+
decision: "forbidden",
|
|
38
|
+
justification: "Recursive force-delete of a core system directory.",
|
|
39
|
+
match: ["rm -rf /etc", "rm -fr /usr"],
|
|
40
|
+
notMatch: ["rm -rf /home/user/project"],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
sourceLabel: "DANGEROUS PATTERN: recursive delete of a system directory",
|
|
44
|
+
pattern: ["rm", "-r", "-f", ["/etc", "/usr", "/var", "/bin", "/sbin", "/boot", "/lib"]],
|
|
45
|
+
decision: "forbidden",
|
|
46
|
+
justification: "Recursive force-delete of a core system directory (split flags).",
|
|
47
|
+
match: ["rm -r -f /var", "rm -r -f /bin"],
|
|
48
|
+
notMatch: ["rm -r -f /home/user"],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
sourceLabel: "DANGEROUS PATTERN: recursive delete of a system directory",
|
|
52
|
+
pattern: ["rm", "-f", "-r", ["/etc", "/usr", "/var", "/bin", "/sbin", "/boot", "/lib"]],
|
|
53
|
+
decision: "forbidden",
|
|
54
|
+
justification: "Recursive force-delete of a core system directory (split flags, reversed).",
|
|
55
|
+
match: ["rm -f -r /sbin", "rm -f -r /lib"],
|
|
56
|
+
notMatch: ["rm -f -r /tmp"],
|
|
57
|
+
},
|
|
58
|
+
];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { CodexPrefixRule } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Single-purpose destructive binaries — hand-mapped 1:1 from `security.ts`'s
|
|
5
|
+
* `chmod 777 on /`, `shred`, `fdisk` CRITICAL_PATTERNS labels. `mkfs`,
|
|
6
|
+
* `dd`, and `diskutil erase` live in their own files (`mkfs.ts`, `dd.ts`,
|
|
7
|
+
* `diskutil.ts`) — each needed a wider case/variant enumeration than fits
|
|
8
|
+
* this file's SRP scope. The root-chmod rule needs the same flag-order
|
|
9
|
+
* alternation as `chmod-ask.ts` — source security.ts accepts flags before
|
|
10
|
+
* `777` for the root case too (`chmod -R 777 /` is still CRITICAL there).
|
|
11
|
+
*/
|
|
12
|
+
export const SINGLE_COMMAND_RULES: CodexPrefixRule[] = [
|
|
13
|
+
{
|
|
14
|
+
sourceLabel: "DANGEROUS PATTERN: chmod 777 on /",
|
|
15
|
+
pattern: ["chmod", "777", "/"],
|
|
16
|
+
decision: "forbidden",
|
|
17
|
+
justification: "World-writable permissions on the filesystem root.",
|
|
18
|
+
match: ["chmod 777 /"],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
sourceLabel: "DANGEROUS PATTERN: chmod 777 on /",
|
|
22
|
+
pattern: ["chmod", "-R", "777", "/"],
|
|
23
|
+
decision: "forbidden",
|
|
24
|
+
justification: "World-writable permissions on the filesystem root, recursive.",
|
|
25
|
+
match: ["chmod -R 777 /"],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
sourceLabel: "DANGEROUS PATTERN: chmod 777 on /",
|
|
29
|
+
pattern: ["chmod", "-r", "777", "/"],
|
|
30
|
+
decision: "forbidden",
|
|
31
|
+
justification: "World-writable permissions on the filesystem root, recursive (lowercase flag).",
|
|
32
|
+
match: ["chmod -r 777 /"],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
sourceLabel: "DANGEROUS PATTERN: chmod 777 on /",
|
|
36
|
+
pattern: ["chmod", "-v", "777", "/"],
|
|
37
|
+
decision: "forbidden",
|
|
38
|
+
justification: "World-writable permissions on the filesystem root, verbose flag.",
|
|
39
|
+
match: ["chmod -v 777 /"],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
sourceLabel: "CRITICAL: Detected dangerous command 'shred'",
|
|
43
|
+
pattern: ["shred"],
|
|
44
|
+
decision: "forbidden",
|
|
45
|
+
justification: "Irreversibly overwrites file contents.",
|
|
46
|
+
match: ["shred -u secrets.txt"],
|
|
47
|
+
notMatch: ["cat secrets.txt"],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
sourceLabel: "CRITICAL: Detected dangerous command 'fdisk'",
|
|
51
|
+
pattern: ["fdisk"],
|
|
52
|
+
decision: "forbidden",
|
|
53
|
+
justification: "Edits disk partition tables.",
|
|
54
|
+
match: ["fdisk /dev/sda"],
|
|
55
|
+
notMatch: ["cat /proc/partitions"],
|
|
56
|
+
},
|
|
57
|
+
];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CodexSkipEntry } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `security.ts` labels with NO argv-prefix equivalent. Every entry here is
|
|
5
|
+
* shell syntax (pipes, redirection, function-definition syntax) that never
|
|
6
|
+
* appears as a plain argv token when a command is exec'd without a shell —
|
|
7
|
+
* Codex execpolicy's `prefix_rule` cannot express them. Covered instead by
|
|
8
|
+
* `sandbox_mode`/`approval_policy`. Never drop a label silently: it is
|
|
9
|
+
* either a prefix_rule (see `rules/`) or an entry here.
|
|
10
|
+
*/
|
|
11
|
+
export const SKIP_LIST: CodexSkipEntry[] = [
|
|
12
|
+
{
|
|
13
|
+
sourceLabel: "DANGEROUS PATTERN: fork bomb",
|
|
14
|
+
reason:
|
|
15
|
+
"Shell function-definition syntax `:(){ :|:& };:` is not a single argv invocation — no prefix_rule can express it.",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
sourceLabel: "DANGEROUS PATTERN: redirect to a raw disk device",
|
|
19
|
+
reason: "`>`/`>>` redirection to /dev/sda is shell syntax, invisible to argv-prefix matching.",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
sourceLabel: "DANGEROUS PATTERN: write to /etc",
|
|
23
|
+
reason: "`>`/`>>` redirection into /etc/* is shell syntax, invisible to argv-prefix matching.",
|
|
24
|
+
},
|
|
25
|
+
];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { CodexPrefixRule } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Neutralize characters that would break out of a Starlark `#` line comment
|
|
5
|
+
* (a bare `\n`/`\r` in an interpolated label/reason would start a new line
|
|
6
|
+
* no longer prefixed by `#`, corrupting the generated file).
|
|
7
|
+
*/
|
|
8
|
+
export function sanitizeComment(text: string): string {
|
|
9
|
+
return text.replace(/[\r\n]+/g, " ");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Render a string array as a Starlark list literal (double-quoted strings). */
|
|
13
|
+
function starlarkList(values: string[]): string {
|
|
14
|
+
return `[${values.map((v) => JSON.stringify(v)).join(", ")}]`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Render one pattern element: a bare token, or a `[...]` alternation. */
|
|
18
|
+
function starlarkPatternElement(el: string | string[]): string {
|
|
19
|
+
return Array.isArray(el) ? starlarkList(el) : JSON.stringify(el);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Render a single `CodexPrefixRule` as a Starlark `prefix_rule(...)` call,
|
|
24
|
+
* prefixed by a traceability comment pointing back at its `security.ts` label.
|
|
25
|
+
*/
|
|
26
|
+
export function renderPrefixRule(rule: CodexPrefixRule): string {
|
|
27
|
+
const pattern = `[${rule.pattern.map(starlarkPatternElement).join(", ")}]`;
|
|
28
|
+
const lines = [
|
|
29
|
+
`# source: security.ts label "${sanitizeComment(rule.sourceLabel)}"`,
|
|
30
|
+
"prefix_rule(",
|
|
31
|
+
` pattern = ${pattern},`,
|
|
32
|
+
` decision = "${rule.decision}",`,
|
|
33
|
+
` justification = ${JSON.stringify(rule.justification)},`,
|
|
34
|
+
` match = ${starlarkList(rule.match)},`,
|
|
35
|
+
];
|
|
36
|
+
if (rule.notMatch && rule.notMatch.length > 0) {
|
|
37
|
+
lines.push(` not_match = ${starlarkList(rule.notMatch)},`);
|
|
38
|
+
}
|
|
39
|
+
lines.push(")");
|
|
40
|
+
return lines.join("\n");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Render a list of rules, each block separated by a blank line. */
|
|
44
|
+
export function renderPrefixRules(rules: CodexPrefixRule[]): string {
|
|
45
|
+
return rules.map(renderPrefixRule).join("\n\n");
|
|
46
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the Codex execpolicy `.rules` (Starlark) generator.
|
|
3
|
+
* Verified contract (openai/codex `codex-rs/execpolicy/README.md` +
|
|
4
|
+
* learn.chatgpt.com/docs/agent-configuration/rules): `prefix_rule(pattern,
|
|
5
|
+
* decision, justification?, match?, not_match?)`; decisions are
|
|
6
|
+
* allow < prompt < forbidden (strictest wins across matching rules);
|
|
7
|
+
* `match`/`not_match` are validated as inline unit tests at file-load time.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Codex execpolicy decision severity. */
|
|
11
|
+
export type CodexDecision = "allow" | "prompt" | "forbidden";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* One argv-prefix rule, hand-mapped from a single `security.ts`
|
|
15
|
+
* `LabeledPattern`. `pattern` entries are exact argv tokens; a nested
|
|
16
|
+
* array element means "any of these tokens at this position" (Codex
|
|
17
|
+
* execpolicy alternation) — never a mechanical regex-to-prefix derivation.
|
|
18
|
+
*/
|
|
19
|
+
export interface CodexPrefixRule {
|
|
20
|
+
/** Exact `label` string from `CRITICAL_PATTERNS`/`ASK_PATTERNS` this rule maps to. */
|
|
21
|
+
sourceLabel: string;
|
|
22
|
+
/** Argv-prefix tokens; an element may be `string[]` for alternation. */
|
|
23
|
+
pattern: (string | string[])[];
|
|
24
|
+
decision: CodexDecision;
|
|
25
|
+
justification: string;
|
|
26
|
+
/** Example invocations that must satisfy this rule (validated at load time). */
|
|
27
|
+
match: string[];
|
|
28
|
+
/** Example invocations that must NOT satisfy this rule (validated at load time). */
|
|
29
|
+
notMatch?: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A `security.ts` label with no argv-prefix equivalent — named, never dropped silently. */
|
|
33
|
+
export interface CodexSkipEntry {
|
|
34
|
+
sourceLabel: string;
|
|
35
|
+
reason: string;
|
|
36
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native .env loader. Ports the claude-plugins `services/env-file.ts`
|
|
3
|
+
* (`loadEnvFile`) into the engine so a hook run hydrates `process.env` from the
|
|
4
|
+
* harness home `.env` (`~/.claude/.env`, `~/.codex/.env`, …) plus the project
|
|
5
|
+
* `<cwd>/.env`, instead of relying on Bun auto-dotenv or `BASH_ENV`. A value
|
|
6
|
+
* already present in the environment always wins (the file never overwrites it).
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { harnessHome } from "./home-dir";
|
|
12
|
+
import type { HarnessId } from "../detect/harness";
|
|
13
|
+
|
|
14
|
+
/** Home config dir holding the `.env` for each harness (defaults to `.claude`). */
|
|
15
|
+
export const HOME_DIR: Partial<Record<HarnessId, string>> = {
|
|
16
|
+
"claude-code": ".claude", codex: ".codex", cursor: ".cursor",
|
|
17
|
+
cline: ".clinerules", "gemini-cli": ".gemini", opencode: ".opencode",
|
|
18
|
+
hermes: ".hermes", kimi: ".kimi-code",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** Parse a `.env` file into a key→value map (`export KEY="v"` or `KEY=v`). */
|
|
22
|
+
export function parseEnvFile(path: string): Record<string, string> {
|
|
23
|
+
if (!existsSync(path)) return {};
|
|
24
|
+
let content = "";
|
|
25
|
+
try { content = readFileSync(path, "utf-8"); } catch { return {}; }
|
|
26
|
+
const out: Record<string, string> = {};
|
|
27
|
+
for (const line of content.split(/\r?\n/)) {
|
|
28
|
+
if (line.trimStart().startsWith("#")) continue;
|
|
29
|
+
const m = line.match(/^\s*(?:export\s+)?(\w+)\s*=\s*["']?([^"'\n]*)["']?\s*$/);
|
|
30
|
+
if (m?.[1]) out[m[1]] = m[2] ?? "";
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The `.env` paths probed for a harness: home `.env` then `<cwd>/.env`. */
|
|
36
|
+
export function envCandidates(id: HarnessId, home: string = homedir(), cwd: string = process.cwd()): string[] {
|
|
37
|
+
return [join(harnessHome(id, process.env, home), ".env"), join(cwd, ".env")];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Load `.env` files into `env` without overwriting existing keys. Reads the
|
|
42
|
+
* harness home `.env` then `<cwd>/.env`. Best-effort (missing/unreadable files
|
|
43
|
+
* are skipped) so a hook never fails on env loading.
|
|
44
|
+
* @param id - Detected harness id (selects the home dir).
|
|
45
|
+
* @param env - Target environment (defaults to `process.env`).
|
|
46
|
+
* @param home - Home dir.
|
|
47
|
+
* @param cwd - Project root.
|
|
48
|
+
*/
|
|
49
|
+
export function loadDotenv(id: HarnessId, env: NodeJS.ProcessEnv = process.env, home: string = homedir(), cwd: string = process.cwd()): void {
|
|
50
|
+
for (const path of envCandidates(id, home, cwd)) {
|
|
51
|
+
for (const [k, v] of Object.entries(parseEnvFile(path))) {
|
|
52
|
+
if (env[k] === undefined) env[k] = v;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Robust integer-from-env parser.
|
|
3
|
+
* undefined / empty / whitespace / NaN / float / <= 0 all fall back to `fallback`.
|
|
4
|
+
* `Number("")` is 0, so the empty guard is required before `Number()`.
|
|
5
|
+
*/
|
|
6
|
+
export function parseEnvInt(raw: string | undefined, fallback: number): number {
|
|
7
|
+
if (!raw?.trim()) return fallback;
|
|
8
|
+
const n = Number(raw.trim());
|
|
9
|
+
return Number.isInteger(n) && n > 0 ? n : fallback;
|
|
10
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness home-dir resolution with env overrides. `dotenv.ts`'s static
|
|
3
|
+
* `HOME_DIR` table knows every harness's default config dir but cannot move
|
|
4
|
+
* with the user's data root — Kimi Code CLI documents `KIMI_CODE_HOME` for
|
|
5
|
+
* exactly that (isolated data roots get isolated skills/agents/hooks), so the
|
|
6
|
+
* override is honored here instead of hardcoding `~/.kimi-code`.
|
|
7
|
+
*
|
|
8
|
+
* Deliberately consumed only where a USER-HOME path is built (`envCandidates`
|
|
9
|
+
* today): `harnessHomeSegment` (`policy/apex-target.ts`) also serves
|
|
10
|
+
* project-relative joins (`<project>/.kimi-code/apex`), which an absolute
|
|
11
|
+
* override would corrupt — those sites stay segment-based.
|
|
12
|
+
*/
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import type { HarnessId } from "../detect/harness";
|
|
16
|
+
import { HOME_DIR } from "./dotenv";
|
|
17
|
+
|
|
18
|
+
/** Documented env var relocating a harness home dir, when one exists. */
|
|
19
|
+
const HOME_ENV: Partial<Record<HarnessId, string>> = { kimi: "KIMI_CODE_HOME" };
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolve a harness's home config dir as an absolute path.
|
|
23
|
+
* @param id - Harness id.
|
|
24
|
+
* @param env - Environment (defaults to `process.env`).
|
|
25
|
+
* @param home - OS home dir (injectable for tests).
|
|
26
|
+
* @returns The override path when set, else `join(home, HOME_DIR[id])`.
|
|
27
|
+
*/
|
|
28
|
+
export function harnessHome(
|
|
29
|
+
id: string,
|
|
30
|
+
env: Record<string, string | undefined> = process.env,
|
|
31
|
+
home: string = homedir(),
|
|
32
|
+
): string {
|
|
33
|
+
const key = HOME_ENV[id as HarnessId];
|
|
34
|
+
const override = key ? env[key] : undefined;
|
|
35
|
+
return override ?? join(home, HOME_DIR[id as HarnessId] ?? ".claude");
|
|
36
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
|
|
3
|
+
/** Top-level project state dir — neutral + harness-agnostic (not `.claude/…`). */
|
|
4
|
+
export const STATE_ROOT = ".harness";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The selective `.gitignore` for `.harness/`: ignore the machine-local session
|
|
8
|
+
* state (track, cache, throttle) but keep the curated `memory/LESSON.md`.
|
|
9
|
+
*/
|
|
10
|
+
export const STATE_GITIGNORE = "track/\ncache/\nmemory/state.json\n";
|
|
11
|
+
|
|
12
|
+
/** Every project-state path the package uses, derived from one project root. */
|
|
13
|
+
export interface ProjectLayout {
|
|
14
|
+
root: string;
|
|
15
|
+
/** `<root>/.harness` — the single state dir. */
|
|
16
|
+
stateDir: string;
|
|
17
|
+
/** `<root>/.harness/track` — session track files. */
|
|
18
|
+
trackDir: string;
|
|
19
|
+
/** `<root>/.harness/cache` — MCP/WebFetch cache. */
|
|
20
|
+
cacheDir: string;
|
|
21
|
+
/** `<root>/.harness/memory` — lessons + throttle. */
|
|
22
|
+
memoryDir: string;
|
|
23
|
+
/** `<root>/.harness/memory/LESSON.md` — curated, committable. */
|
|
24
|
+
lessonsFile: string;
|
|
25
|
+
/** `<root>/.harness/memory/state.json` — machine-local throttle. */
|
|
26
|
+
memoryStateFile: string;
|
|
27
|
+
/** `<root>/.harness/track/solid-notice.json` — SubagentStop dedup sidecar. */
|
|
28
|
+
solidNoticeFile: string;
|
|
29
|
+
/** `<root>/.harness/.gitignore`. */
|
|
30
|
+
gitignoreFile: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The single source of truth for where every package file lives under a project
|
|
35
|
+
* root. All modules (runtime storage, memory, init) derive their paths here —
|
|
36
|
+
* nothing hardcodes a directory name elsewhere.
|
|
37
|
+
*/
|
|
38
|
+
export function projectLayout(root: string): ProjectLayout {
|
|
39
|
+
const stateDir = join(root, STATE_ROOT);
|
|
40
|
+
const memoryDir = join(stateDir, "memory");
|
|
41
|
+
return {
|
|
42
|
+
root,
|
|
43
|
+
stateDir,
|
|
44
|
+
trackDir: join(stateDir, "track"),
|
|
45
|
+
solidNoticeFile: join(stateDir, "track", "solid-notice.json"),
|
|
46
|
+
cacheDir: join(stateDir, "cache"),
|
|
47
|
+
memoryDir,
|
|
48
|
+
lessonsFile: join(memoryDir, "LESSON.md"),
|
|
49
|
+
memoryStateFile: join(memoryDir, "state.json"),
|
|
50
|
+
gitignoreFile: join(stateDir, ".gitignore"),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { parseEnvInt } from "./env";
|
|
2
|
+
|
|
3
|
+
/** Default SOLID max lines per file. */
|
|
4
|
+
export const DEFAULT_MAX_LINES = 100;
|
|
5
|
+
|
|
6
|
+
/** Default env var name carrying the max-lines override. */
|
|
7
|
+
export const MAX_LINES_ENV_KEY = "FUSE_SOLID_MAX_LINES";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Resolve the SOLID max-lines limit from an env map.
|
|
11
|
+
* @param env - environment map (defaults to `process.env`)
|
|
12
|
+
* @param key - env var name (defaults to `FUSE_SOLID_MAX_LINES`)
|
|
13
|
+
*/
|
|
14
|
+
export function resolveMaxLines(
|
|
15
|
+
env: Record<string, string | undefined> = process.env,
|
|
16
|
+
key: string = MAX_LINES_ENV_KEY,
|
|
17
|
+
): number {
|
|
18
|
+
return parseEnvInt(env[key], DEFAULT_MAX_LINES);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Advisory module-split headroom = `maxLines - 10` (never below 1). */
|
|
22
|
+
export function splitTarget(maxLines: number): number {
|
|
23
|
+
return Math.max(maxLines - 10, 1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Custom-hook line budget, derived from the global limit (ratio 0.3 — with
|
|
28
|
+
* the default 100 this yields the owner template's 30; one variable,
|
|
29
|
+
* `FUSE_SOLID_MAX_LINES`, drives every budget proportionally).
|
|
30
|
+
* @param maxLines - The global limit (from {@link resolveMaxLines}).
|
|
31
|
+
*/
|
|
32
|
+
export function hookBudget(maxLines: number): number {
|
|
33
|
+
return Math.max(Math.round(maxLines * 0.3), 1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Store line budget, derived from the global limit (ratio 0.4 — 40 at the
|
|
38
|
+
* default 100, per owner rules/07-state-management).
|
|
39
|
+
* @param maxLines - The global limit (from {@link resolveMaxLines}).
|
|
40
|
+
*/
|
|
41
|
+
export function storeBudget(maxLines: number): number {
|
|
42
|
+
return Math.max(Math.round(maxLines * 0.4), 1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Default stdin cap for hook payloads: 16 MiB. */
|
|
46
|
+
export const DEFAULT_STDIN_MAX_BYTES: number = 16 * 1024 * 1024;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Resolve the hook stdin cap (`FUSE_HOOK_STDIN_MAX_BYTES`, default 16 MiB;
|
|
50
|
+
* absent/invalid falls back to the default). The only new env var of the
|
|
51
|
+
* mission — owner-approved exception.
|
|
52
|
+
* @param env - environment map (defaults to `process.env`)
|
|
53
|
+
*/
|
|
54
|
+
export function resolveStdinMaxBytes(env: Record<string, string | undefined> = process.env): number {
|
|
55
|
+
return parseEnvInt(env.FUSE_HOOK_STDIN_MAX_BYTES, DEFAULT_STDIN_MAX_BYTES);
|
|
56
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { parseEnvInt } from "./env";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Default enforcement-freshness window, in seconds (2 minutes). Matches the
|
|
5
|
+
* plugin's original `FUSE_ENFORCE_TTL_SEC` default. This is the constant that
|
|
6
|
+
* actually reaches production (via `resolveTtlSec` -> `bin.ts`); `gate.ts`'s
|
|
7
|
+
* `DEFAULT_WINDOW_MS` is only a fallback for direct programmatic callers that
|
|
8
|
+
* omit `windowMs` (e.g. tests) and never applies on the real CLI path.
|
|
9
|
+
*/
|
|
10
|
+
export const DEFAULT_TTL_SEC = 120;
|
|
11
|
+
|
|
12
|
+
/** Default env var name carrying the TTL override. */
|
|
13
|
+
export const TTL_ENV_KEY = "FUSE_ENFORCE_TTL_SEC";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the enforcement TTL (seconds) from an env map.
|
|
17
|
+
* @param env - environment map (defaults to `process.env`)
|
|
18
|
+
* @param key - env var name (defaults to `FUSE_ENFORCE_TTL_SEC`)
|
|
19
|
+
*/
|
|
20
|
+
export function resolveTtlSec(
|
|
21
|
+
env: Record<string, string | undefined> = process.env,
|
|
22
|
+
key: string = TTL_ENV_KEY,
|
|
23
|
+
): number {
|
|
24
|
+
return parseEnvInt(env[key], DEFAULT_TTL_SEC);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Human label for a TTL: 120 -> "2min", 240 -> "4min", 90 -> "90s". */
|
|
28
|
+
export function ttlLabel(sec: number): string {
|
|
29
|
+
return sec % 60 === 0 ? `${sec / 60}min` : `${sec}s`;
|
|
30
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime detection of the AI coding harness, and its integration mode.
|
|
3
|
+
* Env-signal names verified 2026 (agentx, agents.md#136, @vercel/detect-agent,
|
|
4
|
+
* official Claude Code / Cursor / Gemini / Codex docs). Presence-based: the
|
|
5
|
+
* value is ignored except for the `AGENT` / `AI_AGENT` standards.
|
|
6
|
+
*/
|
|
7
|
+
import type { HarnessId, HarnessMode, HarnessInfo } from "./interfaces/types";
|
|
8
|
+
|
|
9
|
+
export type { HarnessId, HarnessMode, HarnessVia, HarnessInfo } from "./interfaces/types";
|
|
10
|
+
|
|
11
|
+
/** Tool-specific env var (presence) -> harness id. Order = detection priority. */
|
|
12
|
+
const ENV_SIGNALS: ReadonlyArray<readonly [string, HarnessId]> = [
|
|
13
|
+
["CLAUDECODE", "claude-code"], ["CODEX_SANDBOX", "codex"],
|
|
14
|
+
["CURSOR_AGENT", "cursor"], ["CLINE", "cline"], ["CLINE_AGENT", "cline"],
|
|
15
|
+
["GEMINI_CLI", "gemini-cli"], ["OPENCODE", "opencode"],
|
|
16
|
+
["WINDSURF_AGENT", "windsurf"], ["CODEIUM_AGENT", "windsurf"],
|
|
17
|
+
["COPILOT_AGENT", "copilot"], ["AIDER", "aider"], ["KIRO", "kiro"],
|
|
18
|
+
["GOOSE", "goose"], ["AMP", "amp"], ["HERMES_SESSION_ID", "hermes"],
|
|
19
|
+
// No documented session-marker env var for Kimi Code CLI (unlike CLAUDECODE,
|
|
20
|
+
// CURSOR_AGENT, ...). `KIMI_CODE_HOME`/`KIMI_PLUGIN_ROOT` are injected only
|
|
21
|
+
// into PLUGIN-declared hooks (customization/plugins.html: "The hook process
|
|
22
|
+
// receives two extra environment variables"), never into config.toml hooks —
|
|
23
|
+
// verified live against kimi-code v0.27.0: a config.toml hook process sees
|
|
24
|
+
// neither var, and no session marker at all. Detection therefore falls
|
|
25
|
+
// through to the `AGENT=kimi` / `AI_AGENT=kimi` standard above.
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
/** `AGENT=<name>` / `AI_AGENT=<name>` standard value -> harness id. */
|
|
29
|
+
const STD_NAMES: Record<string, HarnessId> = {
|
|
30
|
+
goose: "goose", amp: "amp", claude: "claude-code", "claude-code": "claude-code",
|
|
31
|
+
cursor: "cursor", codex: "codex", cline: "cline", aider: "aider",
|
|
32
|
+
opencode: "opencode", gemini: "gemini-cli", copilot: "copilot", kiro: "kiro",
|
|
33
|
+
hermes: "hermes", kimi: "kimi",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** Harnesses exposing a native hook system (vs CLI-only integration). */
|
|
37
|
+
const HOOK_CAPABLE: ReadonlySet<HarnessId> = new Set([
|
|
38
|
+
"claude-code", "codex", "cursor", "cline", "gemini-cli", "opencode", "hermes", "kimi",
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
/** Integration mode for a harness id. */
|
|
42
|
+
export function modeFor(id: HarnessId): HarnessMode {
|
|
43
|
+
return HOOK_CAPABLE.has(id) ? "hook" : "cli";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Detect the current AI coding harness from environment signals.
|
|
48
|
+
* Priority: `AGENT` standard -> `AI_AGENT` standard -> tool-specific vars -> unknown.
|
|
49
|
+
* @param env - environment map (defaults to `process.env`)
|
|
50
|
+
*/
|
|
51
|
+
export function detectHarness(
|
|
52
|
+
env: Record<string, string | undefined> = process.env,
|
|
53
|
+
): HarnessInfo {
|
|
54
|
+
const agent = env.AGENT?.trim().toLowerCase();
|
|
55
|
+
const agentId = agent ? STD_NAMES[agent] : undefined;
|
|
56
|
+
if (agentId) return { id: agentId, mode: modeFor(agentId), via: "agent-std" };
|
|
57
|
+
|
|
58
|
+
const ai = env.AI_AGENT?.trim().toLowerCase();
|
|
59
|
+
const aiId = ai ? STD_NAMES[ai] : undefined;
|
|
60
|
+
if (aiId) return { id: aiId, mode: modeFor(aiId), via: "ai-agent-std" };
|
|
61
|
+
|
|
62
|
+
for (const [key, id] of ENV_SIGNALS) {
|
|
63
|
+
if (env[key]?.trim()) return { id, mode: modeFor(id), via: "env" };
|
|
64
|
+
}
|
|
65
|
+
return { id: "unknown", mode: "cli", via: "fallback" };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Convenience: the integration mode of the current harness. */
|
|
69
|
+
export function detectMode(
|
|
70
|
+
env: Record<string, string | undefined> = process.env,
|
|
71
|
+
): HarnessMode {
|
|
72
|
+
return detectHarness(env).mode;
|
|
73
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./harness";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Known AI coding harnesses detectable at runtime. */
|
|
2
|
+
export type HarnessId =
|
|
3
|
+
| "claude-code" | "codex" | "cursor" | "cline" | "gemini-cli"
|
|
4
|
+
| "opencode" | "windsurf" | "copilot" | "aider" | "kiro"
|
|
5
|
+
| "goose" | "amp" | "hermes" | "kimi" | "unknown";
|
|
6
|
+
|
|
7
|
+
/** Integration mode: `hook` = native lifecycle hooks; `cli` = run as an external step. */
|
|
8
|
+
export type HarnessMode = "hook" | "cli";
|
|
9
|
+
|
|
10
|
+
/** How the harness was identified. */
|
|
11
|
+
export type HarnessVia = "agent-std" | "ai-agent-std" | "env" | "fallback";
|
|
12
|
+
|
|
13
|
+
/** Result of {@link detectHarness}. */
|
|
14
|
+
export interface HarnessInfo {
|
|
15
|
+
id: HarnessId;
|
|
16
|
+
mode: HarnessMode;
|
|
17
|
+
via: HarnessVia;
|
|
18
|
+
}
|