@fusengine/harness 0.1.90 → 0.1.91
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-C43gA-Pr.mjs} +701 -134
- 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-BjG6unTj.mjs +317 -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 +27 -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.ts +46 -0
- package/src/adapters/cursor/events.ts +38 -0
- package/src/adapters/cursor/index.ts +43 -0
- package/src/adapters/cursor/interfaces/types.ts +88 -0
- package/src/adapters/cursor/native-response.ts +158 -0
- package/src/adapters/cursor/normalize.ts +123 -0
- package/src/adapters/cursor/respond.ts +123 -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 +116 -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 +100 -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 +51 -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 +83 -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,44 @@
|
|
|
1
|
+
import type { CodexPrefixRule } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `prompt`-decision rules — hand-mapped 1:1 from `security.ts`'s
|
|
5
|
+
* ASK_PATTERNS. `DANGEROUS PATTERN: chmod 777` lives in `chmod-ask.ts`
|
|
6
|
+
* (needed multiple flag-order variants). `DANGEROUS PATTERN: write to /etc`
|
|
7
|
+
* is intentionally absent here: it fires on shell redirection
|
|
8
|
+
* (`> /etc/...`), which is not a single argv invocation — see
|
|
9
|
+
* `../skip-list.ts`.
|
|
10
|
+
*/
|
|
11
|
+
export const ASK_RULES: CodexPrefixRule[] = [
|
|
12
|
+
{
|
|
13
|
+
sourceLabel: "DANGEROUS PATTERN: recursive chown",
|
|
14
|
+
pattern: ["chown", "-R"],
|
|
15
|
+
decision: "prompt",
|
|
16
|
+
justification: "Recursive ownership change; confirm scope.",
|
|
17
|
+
match: ["chown -R user:user /srv/app"],
|
|
18
|
+
notMatch: ["chown user file.txt"],
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
sourceLabel: "DANGEROUS PATTERN: eval",
|
|
22
|
+
pattern: ["eval"],
|
|
23
|
+
decision: "prompt",
|
|
24
|
+
justification: "Evaluates arbitrary shell input.",
|
|
25
|
+
match: ["eval $(cat script.sh)"],
|
|
26
|
+
notMatch: ["node eval.js"],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
sourceLabel: "DELETE: 'rm' permanently deletes - confirmation required",
|
|
30
|
+
pattern: ["rm"],
|
|
31
|
+
decision: "prompt",
|
|
32
|
+
justification: "rm permanently deletes; confirm before running.",
|
|
33
|
+
match: ["rm file.txt"],
|
|
34
|
+
notMatch: ["rmdir empty"],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
sourceLabel: "DELETE: 'unlink' command detected - confirmation required",
|
|
38
|
+
pattern: ["unlink"],
|
|
39
|
+
decision: "prompt",
|
|
40
|
+
justification: "unlink permanently removes a file; confirm before running.",
|
|
41
|
+
match: ["unlink file.txt"],
|
|
42
|
+
notMatch: ["ln -s a b"],
|
|
43
|
+
},
|
|
44
|
+
];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { CodexPrefixRule } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `chmod 777` `prompt`-decision variants — hand-mapped from `security.ts`'s
|
|
5
|
+
* `DANGEROUS PATTERN: chmod 777` ASK_PATTERNS label. The source regex
|
|
6
|
+
* accepts any chain of `-[a-zA-Z]+` flags before `777`; `prefix_rule` needs
|
|
7
|
+
* one literal-token variant per common flag order, so every frequent
|
|
8
|
+
* ordering is enumerated. Always `prompt`: `prefix_rule` cannot distinguish
|
|
9
|
+
* an absolute-path target from a relative one, so both are prompted rather
|
|
10
|
+
* than letting an absolute-path `chmod 777` fall through silently.
|
|
11
|
+
*/
|
|
12
|
+
export const CHMOD_ASK_RULES: CodexPrefixRule[] = [
|
|
13
|
+
{
|
|
14
|
+
sourceLabel: "DANGEROUS PATTERN: chmod 777",
|
|
15
|
+
pattern: ["chmod", "777"],
|
|
16
|
+
decision: "prompt",
|
|
17
|
+
justification: "World-writable permissions; confirm the target is intended.",
|
|
18
|
+
match: ["chmod 777 file.txt", "chmod 777 /tmp/file"],
|
|
19
|
+
notMatch: ["chmod 755 file.txt"],
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
sourceLabel: "DANGEROUS PATTERN: chmod 777",
|
|
23
|
+
pattern: ["chmod", "-R", "777"],
|
|
24
|
+
decision: "prompt",
|
|
25
|
+
justification: "World-writable permissions, recursive; confirm the target is intended.",
|
|
26
|
+
match: ["chmod -R 777 dir"],
|
|
27
|
+
notMatch: ["chmod -R 755 dir"],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
sourceLabel: "DANGEROUS PATTERN: chmod 777",
|
|
31
|
+
pattern: ["chmod", "-r", "777"],
|
|
32
|
+
decision: "prompt",
|
|
33
|
+
justification: "World-writable permissions, recursive (lowercase flag); confirm the target is intended.",
|
|
34
|
+
match: ["chmod -r 777 dir"],
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
sourceLabel: "DANGEROUS PATTERN: chmod 777",
|
|
38
|
+
pattern: ["chmod", "-v", "777"],
|
|
39
|
+
decision: "prompt",
|
|
40
|
+
justification: "World-writable permissions, verbose flag; confirm the target is intended.",
|
|
41
|
+
match: ["chmod -v 777 file.txt"],
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
sourceLabel: "DANGEROUS PATTERN: chmod 777",
|
|
45
|
+
pattern: ["chmod", "-Rv", "777"],
|
|
46
|
+
decision: "prompt",
|
|
47
|
+
justification: "World-writable permissions, combined recursive+verbose flags; confirm the target is intended.",
|
|
48
|
+
match: ["chmod -Rv 777 dir"],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
sourceLabel: "DANGEROUS PATTERN: chmod 777",
|
|
52
|
+
pattern: ["chmod", "-vR", "777"],
|
|
53
|
+
decision: "prompt",
|
|
54
|
+
justification: "World-writable permissions, combined verbose+recursive flags; confirm the target is intended.",
|
|
55
|
+
match: ["chmod -vR 777 dir"],
|
|
56
|
+
},
|
|
57
|
+
];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { CodexPrefixRule } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `dd` — hand-mapped from `security.ts`'s `CRITICAL: Detected dangerous
|
|
5
|
+
* command 'dd if='` label (source regex only fires on `of=/dev/...`).
|
|
6
|
+
* Owner decision: DOWNGRADE forbidden -> prompt. `prefix_rule` cannot
|
|
7
|
+
* glob-match a `key=value` argv token against a raw-device path, so a
|
|
8
|
+
* literal forbidden translation would silently block legitimate
|
|
9
|
+
* `dd if=a.img of=b.img` usage — prompting on every `dd` invocation instead
|
|
10
|
+
* never lets a raw-disk write through silently, without blocking safe use.
|
|
11
|
+
*/
|
|
12
|
+
export const DD_RULES: CodexPrefixRule[] = [
|
|
13
|
+
{
|
|
14
|
+
sourceLabel: "CRITICAL: Detected dangerous command 'dd if='",
|
|
15
|
+
pattern: ["dd"],
|
|
16
|
+
decision: "prompt",
|
|
17
|
+
justification:
|
|
18
|
+
"dd can overwrite a raw block device via of=/dev/...; prompts on every dd invocation " +
|
|
19
|
+
"(broader than security.ts, which only fires on of=/dev/*) because execpolicy cannot " +
|
|
20
|
+
"glob-match that argv token — owner decision: do not block legitimate dd usage.",
|
|
21
|
+
match: ["dd if=/dev/zero of=/dev/sda", "dd if=a.img of=b.img"],
|
|
22
|
+
},
|
|
23
|
+
];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { CodexPrefixRule } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `diskutil` erase/partition variants — hand-mapped from `security.ts`'s
|
|
5
|
+
* case-insensitive `\bdiskutil\s+(?:erase|partitionDisk)\b/i` CRITICAL
|
|
6
|
+
* label. `prefix_rule` has no case-insensitive matching, so every real
|
|
7
|
+
* casing macOS users actually type (camelCase, all-lowercase, all-caps) is
|
|
8
|
+
* enumerated explicitly as an alternation at position 1.
|
|
9
|
+
*/
|
|
10
|
+
export const DISKUTIL_RULES: CodexPrefixRule[] = [
|
|
11
|
+
{
|
|
12
|
+
sourceLabel: "CRITICAL: Detected dangerous command 'diskutil erase'",
|
|
13
|
+
pattern: [
|
|
14
|
+
"diskutil",
|
|
15
|
+
[
|
|
16
|
+
"erase",
|
|
17
|
+
"eraseDisk",
|
|
18
|
+
"erasedisk",
|
|
19
|
+
"ERASEDISK",
|
|
20
|
+
"eraseVolume",
|
|
21
|
+
"erasevolume",
|
|
22
|
+
"ERASEVOLUME",
|
|
23
|
+
"partitionDisk",
|
|
24
|
+
"partitiondisk",
|
|
25
|
+
"PARTITIONDISK",
|
|
26
|
+
],
|
|
27
|
+
],
|
|
28
|
+
decision: "forbidden",
|
|
29
|
+
justification: "Erases or repartitions a macOS disk/volume (case-variant enumeration; execpolicy has no case-insensitive match).",
|
|
30
|
+
match: ["diskutil eraseDisk /dev/disk2", "diskutil ERASEDISK /dev/disk2", "diskutil erase /dev/disk2"],
|
|
31
|
+
notMatch: ["diskutil list"],
|
|
32
|
+
},
|
|
33
|
+
];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { CodexPrefixRule } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `mkfs` filesystem-formatting variants — hand-mapped from `security.ts`'s
|
|
5
|
+
* `\bmkfs(?:\.[a-z0-9]+)?\b` CRITICAL_PATTERNS label. The source regex
|
|
6
|
+
* accepts ANY `.suffix`; `prefix_rule` cannot express an open-ended suffix,
|
|
7
|
+
* so every real-world `mkfs.*` filesystem type is enumerated explicitly as
|
|
8
|
+
* an alternation at position 0 (exhaustive, not a mechanical derivation).
|
|
9
|
+
*/
|
|
10
|
+
export const MKFS_RULES: CodexPrefixRule[] = [
|
|
11
|
+
{
|
|
12
|
+
sourceLabel: "CRITICAL: Detected dangerous command 'mkfs'",
|
|
13
|
+
pattern: [
|
|
14
|
+
[
|
|
15
|
+
"mkfs",
|
|
16
|
+
"mkfs.ext2",
|
|
17
|
+
"mkfs.ext3",
|
|
18
|
+
"mkfs.ext4",
|
|
19
|
+
"mkfs.xfs",
|
|
20
|
+
"mkfs.btrfs",
|
|
21
|
+
"mkfs.exfat",
|
|
22
|
+
"mkfs.vfat",
|
|
23
|
+
"mkfs.fat",
|
|
24
|
+
"mkfs.ntfs",
|
|
25
|
+
"mkfs.f2fs",
|
|
26
|
+
"mkfs.hfsplus",
|
|
27
|
+
"mkfs.apfs",
|
|
28
|
+
"mkfs.msdos",
|
|
29
|
+
"mkfs.minix",
|
|
30
|
+
"mkfs.jfs",
|
|
31
|
+
"mkfs.reiserfs",
|
|
32
|
+
"mkfs.udf",
|
|
33
|
+
"mkfs.bfs",
|
|
34
|
+
"mkfs.cramfs",
|
|
35
|
+
],
|
|
36
|
+
],
|
|
37
|
+
decision: "forbidden",
|
|
38
|
+
justification: "Formats a block device, destroying its contents (exhaustive mkfs.* variant enumeration).",
|
|
39
|
+
match: ["mkfs.exfat /dev/sda1", "mkfs.f2fs /dev/sdb1", "mkfs /dev/sdb1", "mkfs.ext4 /dev/sda1"],
|
|
40
|
+
notMatch: ["ls /dev"],
|
|
41
|
+
},
|
|
42
|
+
];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { CodexPrefixRule } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Privilege-escalation binaries — hand-mapped 1:1 from `security.ts`'s
|
|
5
|
+
* `sudo`/`su`/`doas`/`passwd`/`del` CRITICAL_PATTERNS labels (parity
|
|
6
|
+
* `security_rules.PRIVILEGE_COMMANDS`).
|
|
7
|
+
*/
|
|
8
|
+
export const PRIVILEGE_RULES: CodexPrefixRule[] = [
|
|
9
|
+
{
|
|
10
|
+
sourceLabel: "PRIVILEGE ESCALATION: sudo",
|
|
11
|
+
pattern: ["sudo"],
|
|
12
|
+
decision: "forbidden",
|
|
13
|
+
justification: "Runs a command with elevated privileges.",
|
|
14
|
+
match: ["sudo rm -rf /tmp"],
|
|
15
|
+
notMatch: ["echo sudo"],
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
sourceLabel: "PRIVILEGE ESCALATION: su",
|
|
19
|
+
pattern: ["su"],
|
|
20
|
+
decision: "forbidden",
|
|
21
|
+
justification: "Switches to another user, typically root.",
|
|
22
|
+
match: ["su -", "su root"],
|
|
23
|
+
notMatch: ["sudo ls"],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
sourceLabel: "PRIVILEGE ESCALATION: doas",
|
|
27
|
+
pattern: ["doas"],
|
|
28
|
+
decision: "forbidden",
|
|
29
|
+
justification: "BSD-style sudo alternative for elevated privileges.",
|
|
30
|
+
match: ["doas reboot"],
|
|
31
|
+
notMatch: ["ls"],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
sourceLabel: "PRIVILEGE ESCALATION: passwd",
|
|
35
|
+
pattern: ["passwd"],
|
|
36
|
+
decision: "forbidden",
|
|
37
|
+
justification: "Changes a user's password.",
|
|
38
|
+
match: ["passwd root"],
|
|
39
|
+
notMatch: ["cat /etc/passwd"],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
sourceLabel: "CRITICAL: Detected dangerous command 'del'",
|
|
43
|
+
pattern: ["del"],
|
|
44
|
+
decision: "forbidden",
|
|
45
|
+
justification: "Windows-style file deletion (parity security_rules.CRITICAL_COMMANDS).",
|
|
46
|
+
match: ["del file.txt"],
|
|
47
|
+
notMatch: ["delete.sh"],
|
|
48
|
+
},
|
|
49
|
+
];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CodexPrefixRule } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Remote-fetch tools (`curl`/`wget`) — targets the DOWNLOAD half of a
|
|
5
|
+
* `curl ... | sh` pipe instead of the shell half. Codex splits a piped
|
|
6
|
+
* command into independent argv segments at the pipe, so a `prefix_rule`
|
|
7
|
+
* anchored on `curl`/`wget` catches the attack regardless of which shell
|
|
8
|
+
* receives the piped output on the other side — unlike a bare-shell rule
|
|
9
|
+
* (`sh`/`bash`/`zsh`), which prompts on EVERY Codex-issued `bash -lc "..."`
|
|
10
|
+
* wrapper (Codex wraps essentially all shell commands this way), producing
|
|
11
|
+
* noise on ~50% of legitimate day-to-day work. Owner decision: DOWNGRADE
|
|
12
|
+
* forbidden -> prompt, same rationale as `dd.ts` — `prefix_rule` cannot see
|
|
13
|
+
* the pipe operator itself, so it cannot distinguish a bare `curl url`
|
|
14
|
+
* fetch from `curl url | sh`; prompting on every curl/wget invocation never
|
|
15
|
+
* lets the attack through silently, without blocking legitimate downloads.
|
|
16
|
+
*/
|
|
17
|
+
export const REMOTE_FETCH_RULES: CodexPrefixRule[] = [
|
|
18
|
+
{
|
|
19
|
+
sourceLabel: "DANGEROUS PATTERN: remote script piped to a shell",
|
|
20
|
+
pattern: [["curl", "wget"]],
|
|
21
|
+
decision: "prompt",
|
|
22
|
+
justification:
|
|
23
|
+
"Remote fetch; piping its output into a shell executes untrusted code. Broader than " +
|
|
24
|
+
"security.ts (which only fires when the fetch is piped into sh/bash/zsh): prompts on " +
|
|
25
|
+
"every curl/wget invocation because execpolicy cannot see the pipe operator, i.e. it " +
|
|
26
|
+
"cannot distinguish `curl url` from `curl url | sh` — owner decision: do not block " +
|
|
27
|
+
"legitimate curl/wget usage.",
|
|
28
|
+
match: ["curl http://example.com/i.sh", "wget http://example.com/i.sh", "curl -fsSL http://example.com/i.sh"],
|
|
29
|
+
notMatch: ["cursor --version", "widget-tool build"],
|
|
30
|
+
},
|
|
31
|
+
];
|
|
@@ -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
|
+
}
|