@kontourai/flow-agents 0.1.1
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/.githooks/pre-push +11 -0
- package/.github/workflows/ci.yml +210 -0
- package/.github/workflows/docs-pages.yml +52 -0
- package/.github/workflows/publish-npm.yml +104 -0
- package/AGENTS.md +26 -0
- package/CHANGELOG.md +66 -0
- package/CODE_OF_CONDUCT.md +25 -0
- package/CONTEXT.md +300 -0
- package/CONTRIBUTING.md +44 -0
- package/LICENSE +201 -0
- package/README.md +129 -0
- package/SECURITY.md +33 -0
- package/agent-cards/dev.json +19 -0
- package/agents/dev.json +127 -0
- package/agents/tool-code-reviewer.json +61 -0
- package/agents/tool-dependencies-updater.json +118 -0
- package/agents/tool-explore-config.json +92 -0
- package/agents/tool-explore-deps.json +92 -0
- package/agents/tool-explore-entry.json +92 -0
- package/agents/tool-explore-patterns.json +92 -0
- package/agents/tool-explore-structure.json +92 -0
- package/agents/tool-explore-tests.json +92 -0
- package/agents/tool-planner.json +57 -0
- package/agents/tool-playwright.json +145 -0
- package/agents/tool-security-reviewer.json +56 -0
- package/agents/tool-verifier.json +61 -0
- package/agents/tool-worker.json +58 -0
- package/build/src/cli/console-learning-projection.js +123 -0
- package/build/src/cli/docs-preview.js +39 -0
- package/build/src/cli/effective-backlog-settings.js +102 -0
- package/build/src/cli/export-bookmarks.js +38 -0
- package/build/src/cli/fixture-retirement-audit.js +140 -0
- package/build/src/cli/flow-kit.js +138 -0
- package/build/src/cli/import-bookmarks.js +50 -0
- package/build/src/cli/init.js +239 -0
- package/build/src/cli/instinct-cli.js +93 -0
- package/build/src/cli/promote-workflow-artifact.js +63 -0
- package/build/src/cli/publish-change-helper.js +154 -0
- package/build/src/cli/pull-work-provider.js +469 -0
- package/build/src/cli/runtime-adapter.js +23 -0
- package/build/src/cli/telemetry-doctor.js +221 -0
- package/build/src/cli/usage-feedback.js +443 -0
- package/build/src/cli/validate-hook-influence.js +152 -0
- package/build/src/cli/validate-source-tree.js +31 -0
- package/build/src/cli/validate-workflow-artifacts.js +486 -0
- package/build/src/cli/veritas-governance.js +262 -0
- package/build/src/cli/workflow-artifact-cleanup-audit.js +272 -0
- package/build/src/cli/workflow-sidecar.js +816 -0
- package/build/src/cli.js +89 -0
- package/build/src/flow-kit/validate.js +75 -0
- package/build/src/lib/args.js +45 -0
- package/build/src/lib/fs.js +62 -0
- package/build/src/lib/workflow-learning-projection.js +334 -0
- package/build/src/runtime-adapters.js +146 -0
- package/build/src/tools/build-universal-bundles.js +397 -0
- package/build/src/tools/common.js +56 -0
- package/build/src/tools/filter-installed-packs.js +132 -0
- package/build/src/tools/generate-context-map.js +198 -0
- package/build/src/tools/validate-package.js +64 -0
- package/build/src/tools/validate-source-tree.js +622 -0
- package/console.telemetry.json +176 -0
- package/context/base-rules.md +17 -0
- package/context/code-review-standards.md +62 -0
- package/context/coding-standards.md +42 -0
- package/context/common/orchestrators.md +12 -0
- package/context/common/subagents.md +28 -0
- package/context/contracts/artifact-contract.md +182 -0
- package/context/contracts/builder-kit-workflow-state-contract.md +319 -0
- package/context/contracts/delivery-contract.md +69 -0
- package/context/contracts/execution-contract.md +53 -0
- package/context/contracts/governance-adapter-contract.md +67 -0
- package/context/contracts/planning-contract.md +85 -0
- package/context/contracts/review-contract.md +104 -0
- package/context/contracts/sandbox-policy.md +52 -0
- package/context/contracts/verification-contract.md +134 -0
- package/context/contracts/work-item-contract.md +215 -0
- package/context/deferred/demo-mode.md +33 -0
- package/context/deferred/languages/go.md +31 -0
- package/context/deferred/languages/python.md +31 -0
- package/context/deferred/languages/typescript.md +34 -0
- package/context/deferred/parallelization.md +35 -0
- package/context/deferred/worktree-isolation.md +24 -0
- package/context/development-workflow.md +50 -0
- package/context/scripts/context-budget/budget-scan.sh +166 -0
- package/context/scripts/detect-tools.sh +3 -0
- package/context/scripts/discover-agents.sh +28 -0
- package/context/scripts/git-status.sh +49 -0
- package/context/scripts/hooks/config-protection.js +79 -0
- package/context/scripts/hooks/desktop-notify.sh +39 -0
- package/context/scripts/hooks/governance-audit.sh +135 -0
- package/context/scripts/hooks/lib/audit-transport.sh +40 -0
- package/context/scripts/hooks/lib/hook-flags.js +49 -0
- package/context/scripts/hooks/lib/patterns.sh +57 -0
- package/context/scripts/hooks/lib/resolve-formatter.js +80 -0
- package/context/scripts/hooks/post-edit-accumulator.js +66 -0
- package/context/scripts/hooks/pre-commit-quality.js +194 -0
- package/context/scripts/hooks/quality-gate.js +93 -0
- package/context/scripts/hooks/report-only-guard.js +21 -0
- package/context/scripts/hooks/run-hook.js +136 -0
- package/context/scripts/hooks/stop-format-typecheck.js +141 -0
- package/context/scripts/hooks/stop-goal-fit.js +337 -0
- package/context/scripts/hooks/workflow-steering.js +250 -0
- package/context/scripts/telemetry/console-presets.sh +14 -0
- package/context/scripts/telemetry/install-console-config.sh +214 -0
- package/context/scripts/telemetry/lib/config.sh +85 -0
- package/context/scripts/telemetry/lib/enrich.sh +115 -0
- package/context/scripts/telemetry/lib/redact.sh +22 -0
- package/context/scripts/telemetry/lib/session.sh +63 -0
- package/context/scripts/telemetry/lib/transport.sh +183 -0
- package/context/scripts/telemetry/lib/usage.sh +29 -0
- package/context/scripts/telemetry/sync-agents.sh +173 -0
- package/context/scripts/telemetry/telemetry.conf +23 -0
- package/context/scripts/telemetry/telemetry.sh +387 -0
- package/context/scripts/validate-package.sh +89 -0
- package/context/settings/backlog-provider-settings.json +54 -0
- package/context/templates/core/identity.md +26 -0
- package/context/templates/core/user.md +15 -0
- package/docs/_config.yml +15 -0
- package/docs/_layouts/default.html +87 -0
- package/docs/adr/0001-flow-agents-consumes-flow.md +77 -0
- package/docs/adr/0002-flow-kits-as-extension-unit.md +13 -0
- package/docs/adr/0003-flow-agents-coordinates-kits-and-adapters.md +13 -0
- package/docs/adr/0004-gates-expect-surface-claims.md +15 -0
- package/docs/adr/0005-kubernetes-inspired-resource-contracts.md +48 -0
- package/docs/adr/0006-typescript-first-source-policy.md +98 -0
- package/docs/agent-system-guidebook.md +391 -0
- package/docs/agent-usage-feedback-loop.md +351 -0
- package/docs/assets/favicon.svg +13 -0
- package/docs/assets/og-image.png +0 -0
- package/docs/assets/site.css +774 -0
- package/docs/assets/site.js +139 -0
- package/docs/configurable-workflow-routing.md +174 -0
- package/docs/context-map.md +145 -0
- package/docs/developer-architecture.md +145 -0
- package/docs/developer-hook-setup.md +61 -0
- package/docs/fixture-ownership.md +44 -0
- package/docs/flow-kit-repository-contract.md +180 -0
- package/docs/index.md +129 -0
- package/docs/kontour-resource-contract.md +358 -0
- package/docs/migrations.md +64 -0
- package/docs/north-star.md +322 -0
- package/docs/operating-layers.md +110 -0
- package/docs/repository-structure.md +132 -0
- package/docs/sandbox-policy.md +56 -0
- package/docs/skills-map.md +203 -0
- package/docs/standards-register.md +96 -0
- package/docs/veritas-integration.md +165 -0
- package/docs/work-item-adapters.md +72 -0
- package/docs/workflow-artifact-lifecycle.md +141 -0
- package/docs/workflow-eval-strategy.md +295 -0
- package/docs/workflow-shared-contracts.md +51 -0
- package/docs/workflow-usage-guide.md +443 -0
- package/evals/ARCHITECTURE.md +143 -0
- package/evals/CONVENTIONS.md +58 -0
- package/evals/README.md +128 -0
- package/evals/acceptance/run.sh +29 -0
- package/evals/acceptance/test_claude_harness.sh +242 -0
- package/evals/acceptance/test_codex_harness.sh +108 -0
- package/evals/acceptance/test_kiro_harness.sh +128 -0
- package/evals/cases/dev/404.html +97 -0
- package/evals/cases/dev/code-review.yaml +44 -0
- package/evals/cases/dev/dashboard.html +300 -0
- package/evals/cases/dev/deliver.yaml +66 -0
- package/evals/cases/dev/dependency-update.yaml +16 -0
- package/evals/cases/dev/explore.yaml +20 -0
- package/evals/cases/dev/index.html +370 -0
- package/evals/cases/dev/package-lock.json +28 -0
- package/evals/cases/dev/package.json +16 -0
- package/evals/cases/dev/plan-work.yaml +20 -0
- package/evals/cases/dev/promptfooconfig.yaml +666 -0
- package/evals/cases/dev/search-first.yaml +20 -0
- package/evals/cases/dev/tdd-workflow.yaml +48 -0
- package/evals/cases/dev/verify-work.yaml +44 -0
- package/evals/cases/dev/workflow.yaml +34 -0
- package/evals/ci/run-baseline.sh +283 -0
- package/evals/fixtures/backlog-provider-settings/global-default.json +44 -0
- package/evals/fixtures/backlog-provider-settings/project-override.json +53 -0
- package/evals/fixtures/builder-kit-workflow-state/baseline-freshness-resolution-hint.json +139 -0
- package/evals/fixtures/builder-kit-workflow-state/direct-primitive-stop.json +59 -0
- package/evals/fixtures/builder-kit-workflow-state/empty-board-route-shape.json +55 -0
- package/evals/fixtures/builder-kit-workflow-state/happy-path.json +71 -0
- package/evals/fixtures/builder-kit-workflow-state/mid-work-resume.json +80 -0
- package/evals/fixtures/builder-kit-workflow-state/missing-prestep-recovery.json +65 -0
- package/evals/fixtures/builder-kit-workflow-state/product-build-chaining.json +60 -0
- package/evals/fixtures/builder-kit-workflow-state/stale-continuation-requires-new-probe.json +57 -0
- package/evals/fixtures/console-learning-projection/artifacts/console-learning-correction/learning.json +50 -0
- package/evals/fixtures/console-learning-projection/artifacts/console-learning-open-route/learning.json +41 -0
- package/evals/fixtures/flow-kit-repository/invalid-absolute-path/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/invalid-asset-section/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-asset-section/kit.json +11 -0
- package/evals/fixtures/flow-kit-repository/invalid-duplicate-flow/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-duplicate-flow/kit.json +9 -0
- package/evals/fixtures/flow-kit-repository/invalid-id/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-id/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/invalid-malformed-json/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/invalid-missing-flow/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/invalid-missing-id/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-missing-id/kit.json +7 -0
- package/evals/fixtures/flow-kit-repository/invalid-missing-schema-version/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-missing-schema-version/kit.json +7 -0
- package/evals/fixtures/flow-kit-repository/invalid-name/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-name/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/invalid-schema-version/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-schema-version/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/invalid-traversal/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/adapters/example.json +3 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/assets/example.txt +1 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/docs/README.md +3 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/flows/runtime.flow.json +26 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/kit-evals/example.json +3 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/kit-skills/mixed/SKILL.md +3 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/kit.json +44 -0
- package/evals/fixtures/flow-kit-repository/valid-local-kit/docs/README.md +3 -0
- package/evals/fixtures/flow-kit-repository/valid-local-kit/flows/review.flow.json +26 -0
- package/evals/fixtures/flow-kit-repository/valid-local-kit/kit.json +20 -0
- package/evals/fixtures/hook-influence/cases.json +336 -0
- package/evals/fixtures/pull-work-provider/github-issues.json +170 -0
- package/evals/fixtures/pull-work-wip-shepherding/global-wip-informs.json +43 -0
- package/evals/fixtures/pull-work-wip-shepherding/personal-wip-blocks.json +42 -0
- package/evals/fixtures/surface-trust/accepted-claim-trust-report.json +31 -0
- package/evals/fixtures/surface-trust/artifact-absent.json +19 -0
- package/evals/fixtures/surface-trust/integrity-mismatch-trust-report.json +32 -0
- package/evals/fixtures/surface-trust/missing-authority-trust-report.json +27 -0
- package/evals/fixtures/surface-trust/provider-absent.json +19 -0
- package/evals/fixtures/surface-trust/rejected-claim-trust-report.json +30 -0
- package/evals/fixtures/surface-trust/stale-claim-trust-snapshot.json +31 -0
- package/evals/fixtures/usage-feedback/sample-full.jsonl +11 -0
- package/evals/fixtures/usage-feedback/sample-outcomes.jsonl +1 -0
- package/evals/fixtures/veritas-governance-adapter/fake-veritas-pass.sh +18 -0
- package/evals/fixtures/veritas-governance-adapter/fake-veritas-secret-fail.sh +10 -0
- package/evals/fixtures/veritas-governance-adapter/fake-veritas-unconfigured.sh +4 -0
- package/evals/integration/test_bundle_install.sh +541 -0
- package/evals/integration/test_console_learning_projection.sh +192 -0
- package/evals/integration/test_context_map.sh +65 -0
- package/evals/integration/test_effective_backlog_settings.sh +58 -0
- package/evals/integration/test_fixture_retirement_audit.sh +58 -0
- package/evals/integration/test_flow_agents_statusline.sh +93 -0
- package/evals/integration/test_flow_kit_repository.sh +90 -0
- package/evals/integration/test_goal_fit_hook.sh +482 -0
- package/evals/integration/test_hook_category_behaviors.sh +190 -0
- package/evals/integration/test_hook_influence_cases.sh +69 -0
- package/evals/integration/test_local_flow_kit_install.sh +145 -0
- package/evals/integration/test_publish_change_helper.sh +176 -0
- package/evals/integration/test_pull_work_provider.sh +140 -0
- package/evals/integration/test_runtime_adapter_activation.sh +106 -0
- package/evals/integration/test_telemetry.sh +485 -0
- package/evals/integration/test_telemetry_doctor.sh +193 -0
- package/evals/integration/test_usage_feedback_dashboard.sh +169 -0
- package/evals/integration/test_usage_feedback_global.sh +117 -0
- package/evals/integration/test_usage_feedback_import.sh +227 -0
- package/evals/integration/test_usage_feedback_outcomes.sh +165 -0
- package/evals/integration/test_usage_feedback_report.sh +263 -0
- package/evals/integration/test_veritas_governance_adapter.sh +235 -0
- package/evals/integration/test_workflow_artifact_cleanup_audit.sh +287 -0
- package/evals/integration/test_workflow_artifacts.sh +1247 -0
- package/evals/integration/test_workflow_sidecar_writer.sh +2112 -0
- package/evals/integration/test_workflow_steering_hook.sh +337 -0
- package/evals/lib/assertions/delegated-to.js +40 -0
- package/evals/lib/assertions/max-tool-calls.js +15 -0
- package/evals/lib/assertions/no-write-tools.js +27 -0
- package/evals/lib/assertions/pass-at-k.js +39 -0
- package/evals/lib/assertions/telemetry-utils.js +105 -0
- package/evals/lib/assertions/tool-called.js +39 -0
- package/evals/lib/assertions/verify-after-fix.js +61 -0
- package/evals/lib/claude-judge.sh +40 -0
- package/evals/lib/claude-provider.sh +74 -0
- package/evals/lib/codex-judge.sh +39 -0
- package/evals/lib/codex-provider.sh +81 -0
- package/evals/lib/eval-dev.sh +5 -0
- package/evals/lib/eval-judge.sh +22 -0
- package/evals/lib/eval-provider.sh +26 -0
- package/evals/lib/eval-report.sh +73 -0
- package/evals/lib/kiro-dev.sh +4 -0
- package/evals/lib/kiro-judge.sh +17 -0
- package/evals/lib/kiro-provider.sh +62 -0
- package/evals/lib/node.sh +111 -0
- package/evals/promptfooconfig.yaml +70 -0
- package/evals/run.sh +309 -0
- package/evals/static/test_evidence_refs.sh +141 -0
- package/evals/static/test_package.sh +407 -0
- package/evals/static/test_repo_hooks.sh +68 -0
- package/evals/static/test_universal_bundles.sh +274 -0
- package/evals/static/test_workflow_skills.sh +1207 -0
- package/install.sh +64 -0
- package/integrations/veritas/flow-agents.adapter.json +138 -0
- package/integrations/veritas/flow-agents.authority-settings.json +26 -0
- package/integrations/veritas/flow-agents.repo-standards.json +82 -0
- package/kits/builder/flows/build.flow.json +218 -0
- package/kits/builder/flows/shape.flow.json +127 -0
- package/kits/builder/kit.json +19 -0
- package/kits/catalog.json +11 -0
- package/package.json +130 -0
- package/packaging/README.md +60 -0
- package/packaging/manifest.json +173 -0
- package/packaging/packs.json +69 -0
- package/powers/dependency-checker/POWER.md +20 -0
- package/powers/dependency-checker/mcp.json +20 -0
- package/powers/playwright/POWER.md +25 -0
- package/powers/playwright/mcp.json +12 -0
- package/prompts/code-audit.md +123 -0
- package/prompts/kcommit.md +88 -0
- package/schemas/backlog-provider-settings.schema.json +138 -0
- package/schemas/workflow-acceptance.schema.json +216 -0
- package/schemas/workflow-critique.schema.json +113 -0
- package/schemas/workflow-evidence.schema.json +357 -0
- package/schemas/workflow-handoff.schema.json +52 -0
- package/schemas/workflow-learning.schema.json +223 -0
- package/schemas/workflow-release.schema.json +172 -0
- package/schemas/workflow-state.schema.json +80 -0
- package/scripts/README.md +111 -0
- package/scripts/build-universal-bundles.js +3 -0
- package/scripts/check-content-boundary.cjs +99 -0
- package/scripts/context-budget/budget-scan.sh +166 -0
- package/scripts/detect-tools.sh +3 -0
- package/scripts/discover-agents.sh +28 -0
- package/scripts/effective-backlog-settings.js +2 -0
- package/scripts/filter-installed-packs.js +2 -0
- package/scripts/flow-kit.js +2 -0
- package/scripts/generate-context-map.js +2 -0
- package/scripts/git-status.sh +49 -0
- package/scripts/hooks/claude-hook-adapter.js +174 -0
- package/scripts/hooks/claude-telemetry-hook.js +115 -0
- package/scripts/hooks/codex-hook-adapter.js +176 -0
- package/scripts/hooks/codex-telemetry-hook.js +95 -0
- package/scripts/hooks/config-protection.js +79 -0
- package/scripts/hooks/desktop-notify.sh +39 -0
- package/scripts/hooks/governance-audit.sh +135 -0
- package/scripts/hooks/lib/audit-transport.sh +40 -0
- package/scripts/hooks/lib/hook-flags.js +49 -0
- package/scripts/hooks/lib/patterns.sh +57 -0
- package/scripts/hooks/lib/resolve-formatter.js +80 -0
- package/scripts/hooks/post-edit-accumulator.js +66 -0
- package/scripts/hooks/pre-commit-quality.js +194 -0
- package/scripts/hooks/quality-gate.js +93 -0
- package/scripts/hooks/report-only-guard.js +21 -0
- package/scripts/hooks/run-hook.js +136 -0
- package/scripts/hooks/stop-format-typecheck.js +141 -0
- package/scripts/hooks/stop-goal-fit.js +337 -0
- package/scripts/hooks/workflow-steering.js +250 -0
- package/scripts/install-codex-home.sh +106 -0
- package/scripts/package.json +3 -0
- package/scripts/promote-workflow-artifact.js +2 -0
- package/scripts/publish-change-helper.js +2 -0
- package/scripts/pull-work-provider.js +2 -0
- package/scripts/setup-repo-hooks.sh +8 -0
- package/scripts/statusline/flow-agents-statusline.js +157 -0
- package/scripts/telemetry/console-presets.sh +14 -0
- package/scripts/telemetry/install-console-config.sh +214 -0
- package/scripts/telemetry/lib/config.sh +85 -0
- package/scripts/telemetry/lib/enrich.sh +115 -0
- package/scripts/telemetry/lib/redact.sh +22 -0
- package/scripts/telemetry/lib/session.sh +63 -0
- package/scripts/telemetry/lib/transport.sh +183 -0
- package/scripts/telemetry/lib/usage.sh +29 -0
- package/scripts/telemetry/sync-agents.sh +173 -0
- package/scripts/telemetry/telemetry.conf +23 -0
- package/scripts/telemetry/telemetry.sh +387 -0
- package/scripts/usage-feedback.js +2 -0
- package/scripts/validate-hook-influence-cases.js +2 -0
- package/scripts/validate-package.sh +89 -0
- package/scripts/validate-source-tree.js +9 -0
- package/skills/agentic-engineering/SKILL.md +62 -0
- package/skills/browser-test/SKILL.md +51 -0
- package/skills/builder-shape/SKILL.md +76 -0
- package/skills/context-budget/SKILL.md +40 -0
- package/skills/deliver/SKILL.md +241 -0
- package/skills/dependency-update/SKILL.md +68 -0
- package/skills/design-probe/SKILL.md +107 -0
- package/skills/eval-rebuild/SKILL.md +39 -0
- package/skills/evidence-gate/SKILL.md +186 -0
- package/skills/execute-plan/SKILL.md +110 -0
- package/skills/explore/SKILL.md +137 -0
- package/skills/feedback-loop/SKILL.md +87 -0
- package/skills/fix-bug/SKILL.md +133 -0
- package/skills/frontend-design/SKILL.md +80 -0
- package/skills/github-cli/SKILL.md +63 -0
- package/skills/idea-to-backlog/SKILL.md +267 -0
- package/skills/knowledge-capture/SKILL.md +55 -0
- package/skills/learning-review/SKILL.md +115 -0
- package/skills/pickup-probe/SKILL.md +114 -0
- package/skills/plan-work/SKILL.md +176 -0
- package/skills/pull-work/SKILL.md +309 -0
- package/skills/release-readiness/SKILL.md +121 -0
- package/skills/review-work/SKILL.md +161 -0
- package/skills/search-first/SKILL.md +66 -0
- package/skills/tdd-workflow/SKILL.md +140 -0
- package/skills/verify-work/SKILL.md +109 -0
- package/src/cli/console-learning-projection.ts +140 -0
- package/src/cli/effective-backlog-settings.ts +99 -0
- package/src/cli/fixture-retirement-audit.ts +154 -0
- package/src/cli/flow-kit.ts +139 -0
- package/src/cli/init.ts +248 -0
- package/src/cli/promote-workflow-artifact.ts +64 -0
- package/src/cli/publish-change-helper.ts +143 -0
- package/src/cli/pull-work-provider.ts +481 -0
- package/src/cli/runtime-adapter.ts +24 -0
- package/src/cli/telemetry-doctor.ts +243 -0
- package/src/cli/usage-feedback.ts +418 -0
- package/src/cli/validate-hook-influence.ts +119 -0
- package/src/cli/validate-source-tree.ts +30 -0
- package/src/cli/validate-workflow-artifacts.ts +411 -0
- package/src/cli/veritas-governance.ts +322 -0
- package/src/cli/workflow-artifact-cleanup-audit.ts +281 -0
- package/src/cli/workflow-sidecar.ts +676 -0
- package/src/cli.ts +95 -0
- package/src/flow-kit/validate.ts +74 -0
- package/src/lib/args.ts +43 -0
- package/src/lib/fs.ts +62 -0
- package/src/lib/workflow-learning-projection.ts +491 -0
- package/src/runtime-adapters.ts +154 -0
- package/src/tools/build-universal-bundles.ts +366 -0
- package/src/tools/common.ts +61 -0
- package/src/tools/filter-installed-packs.ts +129 -0
- package/src/tools/generate-context-map.ts +199 -0
- package/src/tools/validate-package.ts +57 -0
- package/src/tools/validate-source-tree.ts +488 -0
- package/tsconfig.json +19 -0
- package/veritas.claims.json +6 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { flagBool, flagString, parseArgs } from "../lib/args.js";
|
|
5
|
+
|
|
6
|
+
type EvidenceStatus = "pass" | "fail" | "not_verified" | "skip";
|
|
7
|
+
type EvidenceVerdict = "pass" | "partial" | "fail" | "not_verified";
|
|
8
|
+
|
|
9
|
+
type EvidenceCheck = {
|
|
10
|
+
id: string;
|
|
11
|
+
kind: "policy";
|
|
12
|
+
status: EvidenceStatus;
|
|
13
|
+
command?: string;
|
|
14
|
+
summary: string;
|
|
15
|
+
artifact_refs?: EvidenceRef[];
|
|
16
|
+
standard_refs?: Array<{ standard: "veritas"; ref: string; role: "native"; summary: string }>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type EvidencePayload = {
|
|
20
|
+
schema_version: "1.0";
|
|
21
|
+
task_slug: string;
|
|
22
|
+
verdict: EvidenceVerdict;
|
|
23
|
+
checks: EvidenceCheck[];
|
|
24
|
+
external_evidence?: Array<{ system: "veritas"; ref: EvidenceRef; summary: string; standard: "veritas" }>;
|
|
25
|
+
not_verified_gaps?: string[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type EvidenceRef = {
|
|
29
|
+
kind: "source" | "command" | "artifact" | "provider" | "external";
|
|
30
|
+
url?: string;
|
|
31
|
+
file?: string;
|
|
32
|
+
line_start?: number;
|
|
33
|
+
line_end?: number;
|
|
34
|
+
excerpt?: string;
|
|
35
|
+
summary?: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type EvidenceOptions = {
|
|
39
|
+
artifactDir: string;
|
|
40
|
+
evidencePath: string;
|
|
41
|
+
taskSlug: string;
|
|
42
|
+
veritasBin: string;
|
|
43
|
+
repoRoot: string;
|
|
44
|
+
veritasRoot?: string;
|
|
45
|
+
nativeArtifact?: string;
|
|
46
|
+
maxAgeSeconds?: number;
|
|
47
|
+
skip: boolean;
|
|
48
|
+
notConfigured: boolean;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const OUTPUT_SUMMARY_MAX_BYTES = 2048;
|
|
52
|
+
const OUTPUT_SUMMARY_MAX_LINES = 20;
|
|
53
|
+
const REDACTED = "[REDACTED]";
|
|
54
|
+
|
|
55
|
+
function usage(): void {
|
|
56
|
+
console.error(
|
|
57
|
+
[
|
|
58
|
+
"usage: flow-agents veritas-governance evidence --artifact-dir DIR [options]",
|
|
59
|
+
"",
|
|
60
|
+
"Options:",
|
|
61
|
+
" --artifact-dir DIR Workflow artifact directory containing sidecars.",
|
|
62
|
+
" --evidence-path FILE Evidence sidecar path. Defaults to DIR/evidence.json.",
|
|
63
|
+
" --task-slug SLUG Defaults to state.json task_slug or artifact dir name.",
|
|
64
|
+
" --veritas-bin PATH Veritas executable. Defaults to veritas.",
|
|
65
|
+
" --repo-root DIR Repository root used as Veritas working directory.",
|
|
66
|
+
" --veritas-root DIR Append --root DIR to the Veritas command.",
|
|
67
|
+
" --veritas-artifact FILE Native Veritas artifact to reference and freshness-check.",
|
|
68
|
+
" --max-age-seconds N Mark the check not_verified when the artifact is stale.",
|
|
69
|
+
" --skip Record a skipped policy check without invoking Veritas.",
|
|
70
|
+
" --not-configured Record not_verified for an intentionally unconfigured provider.",
|
|
71
|
+
].join("\n")
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function readTaskSlug(artifactDir: string, fallback?: string): string {
|
|
76
|
+
if (fallback) return fallback;
|
|
77
|
+
const statePath = path.join(artifactDir, "state.json");
|
|
78
|
+
if (fs.existsSync(statePath)) {
|
|
79
|
+
const parsed = JSON.parse(fs.readFileSync(statePath, "utf8")) as { task_slug?: string };
|
|
80
|
+
if (parsed.task_slug) return parsed.task_slug;
|
|
81
|
+
}
|
|
82
|
+
return path.basename(path.resolve(artifactDir));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function ensureParent(file: string): void {
|
|
86
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function commandString(veritasBin: string, veritasRoot?: string): string {
|
|
90
|
+
return ["veritas", "readiness", "--check", "evidence", "--working-tree", veritasRoot ? `--root ${veritasRoot}` : ""]
|
|
91
|
+
.filter(Boolean)
|
|
92
|
+
.join(" ")
|
|
93
|
+
.replace(/^veritas/, veritasBin);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function artifactEvidenceRef(file: string): EvidenceRef {
|
|
97
|
+
return { kind: "artifact", file, summary: file };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function resolveFromBase(file: string, base: string): string {
|
|
101
|
+
return path.isAbsolute(file) ? file : path.resolve(base, file);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function artifactRef(file: string, repoRoot: string): string {
|
|
105
|
+
return resolveFromBase(file, repoRoot);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function notVerifiedPayload(taskSlug: string, command: string, summary: string, repoRoot: string, artifact?: string): EvidencePayload {
|
|
109
|
+
return {
|
|
110
|
+
schema_version: "1.0",
|
|
111
|
+
task_slug: taskSlug,
|
|
112
|
+
verdict: "not_verified",
|
|
113
|
+
checks: [
|
|
114
|
+
{
|
|
115
|
+
id: "veritas-governance-evidence",
|
|
116
|
+
kind: "policy",
|
|
117
|
+
status: "not_verified",
|
|
118
|
+
command,
|
|
119
|
+
summary,
|
|
120
|
+
...(artifact ? { artifact_refs: [artifactEvidenceRef(artifactRef(artifact, repoRoot))] } : {}),
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
not_verified_gaps: [summary],
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function writeEvidence(file: string, payload: EvidencePayload): void {
|
|
128
|
+
ensureParent(file);
|
|
129
|
+
fs.writeFileSync(file, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function artifactProblem(nativeArtifact: string | undefined, maxAgeSeconds: number | undefined, repoRoot: string): string | undefined {
|
|
133
|
+
if (!nativeArtifact) return undefined;
|
|
134
|
+
const resolved = resolveFromBase(nativeArtifact, repoRoot);
|
|
135
|
+
let stat: fs.Stats;
|
|
136
|
+
try {
|
|
137
|
+
stat = fs.statSync(resolved);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
140
|
+
return code === "ENOENT"
|
|
141
|
+
? `Veritas readiness completed, but the expected native artifact is missing: ${resolved}.`
|
|
142
|
+
: `Veritas readiness completed, but the expected native artifact is unreadable: ${resolved}.`;
|
|
143
|
+
}
|
|
144
|
+
if (!stat.isFile()) return `Veritas readiness completed, but the expected native artifact is not a file: ${resolved}.`;
|
|
145
|
+
try {
|
|
146
|
+
fs.accessSync(resolved, fs.constants.R_OK);
|
|
147
|
+
} catch {
|
|
148
|
+
return `Veritas readiness completed, but the expected native artifact is unreadable: ${resolved}.`;
|
|
149
|
+
}
|
|
150
|
+
if (maxAgeSeconds !== undefined && maxAgeSeconds >= 0) {
|
|
151
|
+
const ageSeconds = (Date.now() - stat.mtimeMs) / 1000;
|
|
152
|
+
if (ageSeconds > maxAgeSeconds) {
|
|
153
|
+
return `Veritas native artifact is stale: ${resolved} is ${Math.floor(ageSeconds)}s old, max age is ${maxAgeSeconds}s.`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function successPayload(taskSlug: string, command: string, repoRoot: string, nativeArtifact?: string): EvidencePayload {
|
|
160
|
+
const ref = nativeArtifact ? artifactRef(nativeArtifact, repoRoot) : "veritas://readiness/evidence";
|
|
161
|
+
const evidenceRef: EvidenceRef = nativeArtifact ? artifactEvidenceRef(ref) : { kind: "external", url: ref, summary: "Veritas readiness evidence." };
|
|
162
|
+
return {
|
|
163
|
+
schema_version: "1.0",
|
|
164
|
+
task_slug: taskSlug,
|
|
165
|
+
verdict: "pass",
|
|
166
|
+
checks: [
|
|
167
|
+
{
|
|
168
|
+
id: "veritas-governance-evidence",
|
|
169
|
+
kind: "policy",
|
|
170
|
+
status: "pass",
|
|
171
|
+
command,
|
|
172
|
+
summary: "Veritas readiness evidence completed without blocking findings.",
|
|
173
|
+
artifact_refs: nativeArtifact ? [evidenceRef] : undefined,
|
|
174
|
+
standard_refs: [
|
|
175
|
+
{
|
|
176
|
+
standard: "veritas",
|
|
177
|
+
ref,
|
|
178
|
+
role: "native",
|
|
179
|
+
summary: "Native Veritas readiness evidence artifact.",
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
external_evidence: nativeArtifact
|
|
185
|
+
? [
|
|
186
|
+
{
|
|
187
|
+
system: "veritas",
|
|
188
|
+
ref: evidenceRef,
|
|
189
|
+
summary: "Native Veritas readiness evidence artifact.",
|
|
190
|
+
standard: "veritas",
|
|
191
|
+
},
|
|
192
|
+
]
|
|
193
|
+
: undefined,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function skipPayload(taskSlug: string, command: string, notConfigured: boolean): EvidencePayload {
|
|
198
|
+
const summary = notConfigured
|
|
199
|
+
? "Veritas governance evidence was requested, but Veritas is not configured for this repository."
|
|
200
|
+
: "Veritas governance evidence was explicitly skipped by caller request.";
|
|
201
|
+
return {
|
|
202
|
+
schema_version: "1.0",
|
|
203
|
+
task_slug: taskSlug,
|
|
204
|
+
verdict: notConfigured ? "not_verified" : "partial",
|
|
205
|
+
checks: [
|
|
206
|
+
{
|
|
207
|
+
id: "veritas-governance-evidence",
|
|
208
|
+
kind: "policy",
|
|
209
|
+
status: notConfigured ? "not_verified" : "skip",
|
|
210
|
+
command,
|
|
211
|
+
summary,
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
...(notConfigured ? { not_verified_gaps: [summary] } : {}),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function parseEvidenceOptions(argv: string[]): EvidenceOptions | undefined {
|
|
219
|
+
const [subcommand, ...rest] = argv;
|
|
220
|
+
if (subcommand !== "evidence") {
|
|
221
|
+
usage();
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
const { flags } = parseArgs(rest);
|
|
225
|
+
const artifactDirFlag = flagString(flags, "artifact-dir");
|
|
226
|
+
if (!artifactDirFlag) {
|
|
227
|
+
usage();
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
const artifactDir = path.resolve(artifactDirFlag);
|
|
231
|
+
const repoRoot = path.resolve(flagString(flags, "repo-root", ".") ?? ".");
|
|
232
|
+
const evidencePathFlag = flagString(flags, "evidence-path");
|
|
233
|
+
const maxAgeRaw = flagString(flags, "max-age-seconds");
|
|
234
|
+
const maxAgeSeconds = maxAgeRaw === undefined ? undefined : Number(maxAgeRaw);
|
|
235
|
+
if (maxAgeRaw !== undefined && !Number.isFinite(maxAgeSeconds)) throw new Error("--max-age-seconds must be a number");
|
|
236
|
+
return {
|
|
237
|
+
artifactDir,
|
|
238
|
+
evidencePath: path.resolve(evidencePathFlag ?? path.join(artifactDir, "evidence.json")),
|
|
239
|
+
taskSlug: readTaskSlug(artifactDir, flagString(flags, "task-slug")),
|
|
240
|
+
veritasBin: flagString(flags, "veritas-bin", "veritas") ?? "veritas",
|
|
241
|
+
repoRoot,
|
|
242
|
+
veritasRoot: flagString(flags, "veritas-root"),
|
|
243
|
+
nativeArtifact: flagString(flags, "veritas-artifact"),
|
|
244
|
+
maxAgeSeconds,
|
|
245
|
+
skip: flagBool(flags, "skip"),
|
|
246
|
+
notConfigured: flagBool(flags, "not-configured"),
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function outputSummary(result: ReturnType<typeof spawnSync>): string {
|
|
251
|
+
const raw = `${result.stdout ?? ""}${result.stderr ? `\n${result.stderr}` : ""}`.trim();
|
|
252
|
+
if (!raw) return "";
|
|
253
|
+
const redacted = redactSecrets(raw);
|
|
254
|
+
const bounded = boundOutput(redacted);
|
|
255
|
+
return bounded.truncated ? `${bounded.text}\n[Output truncated for evidence sidecar.]` : bounded.text;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function redactSecrets(value: string): string {
|
|
259
|
+
return value
|
|
260
|
+
.replace(/\b(?:api[_-]?key|token|secret|password|passwd|pwd|authorization)\s*[:=]\s*["']?[^"'\s]+/gi, (match) => {
|
|
261
|
+
const separator = match.includes("=") ? "=" : ":";
|
|
262
|
+
return `${match.slice(0, match.indexOf(separator) + 1)}${REDACTED}`;
|
|
263
|
+
})
|
|
264
|
+
.replace(/\b(?:sk|pk|ghp|github_pat|xox[baprs]|glpat)-[A-Za-z0-9_=-]{12,}\b/g, REDACTED)
|
|
265
|
+
.replace(/\b[A-Za-z0-9+/]{32,}={0,2}\b/g, REDACTED);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function boundOutput(value: string): { text: string; truncated: boolean } {
|
|
269
|
+
const lines = value.split(/\r?\n/);
|
|
270
|
+
const lineBounded = lines.slice(0, OUTPUT_SUMMARY_MAX_LINES).join("\n");
|
|
271
|
+
const bytes = Buffer.byteLength(lineBounded, "utf8");
|
|
272
|
+
if (lines.length <= OUTPUT_SUMMARY_MAX_LINES && bytes <= OUTPUT_SUMMARY_MAX_BYTES) {
|
|
273
|
+
return { text: lineBounded, truncated: false };
|
|
274
|
+
}
|
|
275
|
+
let text = lineBounded;
|
|
276
|
+
if (bytes > OUTPUT_SUMMARY_MAX_BYTES) {
|
|
277
|
+
text = Buffer.from(lineBounded, "utf8").subarray(0, OUTPUT_SUMMARY_MAX_BYTES).toString("utf8");
|
|
278
|
+
}
|
|
279
|
+
return { text, truncated: true };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function runEvidence(options: EvidenceOptions): number {
|
|
283
|
+
const commandText = commandString(options.veritasBin, options.veritasRoot);
|
|
284
|
+
|
|
285
|
+
if (options.skip || options.notConfigured) {
|
|
286
|
+
writeEvidence(options.evidencePath, skipPayload(options.taskSlug, commandText, options.notConfigured));
|
|
287
|
+
console.log(`Wrote Veritas governance evidence sidecar: ${options.evidencePath}`);
|
|
288
|
+
return 0;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const args = ["readiness", "--check", "evidence", "--working-tree", ...(options.veritasRoot ? ["--root", options.veritasRoot] : [])];
|
|
292
|
+
const result = spawnSync(options.veritasBin, args, { cwd: options.repoRoot, encoding: "utf8" });
|
|
293
|
+
if (result.error) {
|
|
294
|
+
const summary = `Unable to run Veritas executable '${options.veritasBin}': ${result.error.message}. Configure --veritas-bin or run with --not-configured when governance evidence is optional.`;
|
|
295
|
+
writeEvidence(options.evidencePath, notVerifiedPayload(options.taskSlug, commandText, summary, options.repoRoot, options.nativeArtifact));
|
|
296
|
+
console.log(`Wrote Veritas governance evidence sidecar: ${options.evidencePath}`);
|
|
297
|
+
return 0;
|
|
298
|
+
}
|
|
299
|
+
if ((result.status ?? 1) !== 0) {
|
|
300
|
+
const output = outputSummary(result);
|
|
301
|
+
const summary = `Veritas readiness returned exit status ${result.status ?? "unknown"}${output ? ` with summarized output:\n${output}` : "."}`;
|
|
302
|
+
writeEvidence(options.evidencePath, notVerifiedPayload(options.taskSlug, commandText, summary, options.repoRoot, options.nativeArtifact));
|
|
303
|
+
console.log(`Wrote Veritas governance evidence sidecar: ${options.evidencePath}`);
|
|
304
|
+
return 0;
|
|
305
|
+
}
|
|
306
|
+
const problem = artifactProblem(options.nativeArtifact, options.maxAgeSeconds, options.repoRoot);
|
|
307
|
+
if (problem) {
|
|
308
|
+
writeEvidence(options.evidencePath, notVerifiedPayload(options.taskSlug, commandText, problem, options.repoRoot, options.nativeArtifact));
|
|
309
|
+
console.log(`Wrote Veritas governance evidence sidecar: ${options.evidencePath}`);
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
writeEvidence(options.evidencePath, successPayload(options.taskSlug, commandText, options.repoRoot, options.nativeArtifact));
|
|
313
|
+
console.log(`Wrote Veritas governance evidence sidecar: ${options.evidencePath}`);
|
|
314
|
+
return 0;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function main(argv = process.argv.slice(2)): number {
|
|
318
|
+
const options = parseEvidenceOptions(argv);
|
|
319
|
+
return options ? runEvidence(options) : 2;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (import.meta.url === `file://${process.argv[1]}`) process.exit(main());
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { flagBool, flagString, parseArgs } from "../lib/args.js";
|
|
4
|
+
|
|
5
|
+
type Classification =
|
|
6
|
+
| "active_wip"
|
|
7
|
+
| "cleanup_candidate"
|
|
8
|
+
| "terminal_done"
|
|
9
|
+
| "active_learning_followup"
|
|
10
|
+
| "invalid";
|
|
11
|
+
|
|
12
|
+
type AuditItem = {
|
|
13
|
+
slug: string;
|
|
14
|
+
path: string;
|
|
15
|
+
classification: Classification;
|
|
16
|
+
state_status: string | null;
|
|
17
|
+
phase: string | null;
|
|
18
|
+
next_action_status: string | null;
|
|
19
|
+
reasons: string[];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type AuditResult = {
|
|
23
|
+
artifact_root: string;
|
|
24
|
+
buckets: Record<Classification, AuditItem[]>;
|
|
25
|
+
totals: Record<Classification | "scanned", number>;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const ACTIVE_STATUSES = new Set([
|
|
29
|
+
"planning",
|
|
30
|
+
"planned",
|
|
31
|
+
"in_progress",
|
|
32
|
+
"verifying",
|
|
33
|
+
"blocked",
|
|
34
|
+
"failed",
|
|
35
|
+
"not_verified",
|
|
36
|
+
"needs_decision",
|
|
37
|
+
]);
|
|
38
|
+
|
|
39
|
+
const KNOWN_LEARNING_STATUSES = new Set(["pending", "learned", "followup_required", "blocked"]);
|
|
40
|
+
const KNOWN_LEARNING_ROUTE_TARGETS = new Set(["rule", "skill", "power", "agent", "eval", "doc", "backlog", "knowledge", "none"]);
|
|
41
|
+
const KNOWN_LEARNING_ROUTE_STATUSES = new Set(["completed", "open", "deferred", "accepted", "rejected"]);
|
|
42
|
+
const SKIPPED_ROOT_ENTRIES = new Set(["archive", "changes", "delivery-history"]);
|
|
43
|
+
const MAX_SIDECAR_BYTES = 1024 * 1024;
|
|
44
|
+
|
|
45
|
+
function printHelp(): void {
|
|
46
|
+
console.log("Usage: flow-agents workflow-artifact-cleanup-audit [--artifact-root <path>] [--json]");
|
|
47
|
+
console.log("");
|
|
48
|
+
console.log("Read-only dry-run audit for local workflow artifact directories.");
|
|
49
|
+
console.log("");
|
|
50
|
+
console.log("Options:");
|
|
51
|
+
console.log(" --artifact-root <path> Local artifact root to scan (default: .flow-agents)");
|
|
52
|
+
console.log(" --json Print stable JSON buckets instead of text");
|
|
53
|
+
console.log(" --help Show this help");
|
|
54
|
+
console.log("");
|
|
55
|
+
console.log("The command classifies active WIP, cleanup candidates, terminal done records,");
|
|
56
|
+
console.log("active learning follow-ups, and invalid sidecars. It does not delete, archive,");
|
|
57
|
+
console.log("move, or rewrite workflow artifacts.");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function readJson(file: string, label: string): { ok: true; value: unknown } | { ok: false; reason: string } {
|
|
61
|
+
let fd: number | null = null;
|
|
62
|
+
try {
|
|
63
|
+
const noFollow = typeof fs.constants.O_NOFOLLOW === "number" ? fs.constants.O_NOFOLLOW : 0;
|
|
64
|
+
fd = fs.openSync(file, fs.constants.O_RDONLY | noFollow);
|
|
65
|
+
const stat = fs.fstatSync(fd);
|
|
66
|
+
if (!stat.isFile()) return { ok: false, reason: `${label} must be a regular file` };
|
|
67
|
+
if (stat.size > MAX_SIDECAR_BYTES) {
|
|
68
|
+
return { ok: false, reason: `${label} exceeds max size of ${MAX_SIDECAR_BYTES} bytes` };
|
|
69
|
+
}
|
|
70
|
+
const buffer = Buffer.alloc(stat.size);
|
|
71
|
+
const bytesRead = fs.readSync(fd, buffer, 0, stat.size, 0);
|
|
72
|
+
if (bytesRead !== stat.size) {
|
|
73
|
+
return { ok: false, reason: `${label} changed while being read` };
|
|
74
|
+
}
|
|
75
|
+
return { ok: true, value: JSON.parse(buffer.toString("utf8")) };
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ELOOP") {
|
|
78
|
+
return { ok: false, reason: `${label} must not be a symlink` };
|
|
79
|
+
}
|
|
80
|
+
return { ok: false, reason: `${label} is unreadable: ${error instanceof Error ? error.message : String(error)}` };
|
|
81
|
+
} finally {
|
|
82
|
+
if (fd !== null) fs.closeSync(fd);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function objectValue(value: unknown): Record<string, unknown> | null {
|
|
87
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record<string, unknown> : null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function stringField(record: Record<string, unknown>, key: string): string | null {
|
|
91
|
+
const value = record[key];
|
|
92
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function validateNextAction(state: Record<string, unknown>): { ok: true; status: string } | { ok: false; reason: string } {
|
|
96
|
+
const nextAction = objectValue(state.next_action);
|
|
97
|
+
if (!nextAction) return { ok: false, reason: "state.next_action is missing or invalid" };
|
|
98
|
+
const status = stringField(nextAction, "status");
|
|
99
|
+
if (!status) return { ok: false, reason: "state.next_action.status is missing or invalid" };
|
|
100
|
+
return { ok: true, status };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function routeStatuses(learning: Record<string, unknown>): { ok: true; statuses: string[] } | { ok: false; reason: string } {
|
|
104
|
+
if (!Array.isArray(learning.records)) return { ok: false, reason: "learning.records is missing or invalid" };
|
|
105
|
+
const statuses: string[] = [];
|
|
106
|
+
for (const record of learning.records) {
|
|
107
|
+
const item = objectValue(record);
|
|
108
|
+
if (!item) return { ok: false, reason: "learning record must be an object" };
|
|
109
|
+
if (!Array.isArray(item.routing)) return { ok: false, reason: "learning record routing is missing or invalid" };
|
|
110
|
+
const routing = item.routing;
|
|
111
|
+
for (const route of routing) {
|
|
112
|
+
const routeObject = objectValue(route);
|
|
113
|
+
if (!routeObject) return { ok: false, reason: "learning routing entry must be an object" };
|
|
114
|
+
const target = stringField(routeObject, "target");
|
|
115
|
+
if (!target) return { ok: false, reason: "learning routing target is missing or invalid" };
|
|
116
|
+
if (!KNOWN_LEARNING_ROUTE_TARGETS.has(target)) return { ok: false, reason: `learning routing has unknown target: ${target}` };
|
|
117
|
+
const action = stringField(routeObject, "action");
|
|
118
|
+
if (!action) return { ok: false, reason: "learning routing action is missing or invalid" };
|
|
119
|
+
const status = stringField(routeObject, "status");
|
|
120
|
+
if (!status) return { ok: false, reason: "learning routing status is missing or invalid" };
|
|
121
|
+
statuses.push(status);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { ok: true, statuses };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function learningSignals(workflowDir: string): { open: boolean; reasons: string[]; invalidReason?: string } {
|
|
128
|
+
const learningPath = path.join(workflowDir, "learning.json");
|
|
129
|
+
if (!fs.existsSync(learningPath)) return { open: false, reasons: [] };
|
|
130
|
+
const parsed = readJson(learningPath, "learning.json");
|
|
131
|
+
if (!parsed.ok) return { open: false, reasons: [], invalidReason: parsed.reason };
|
|
132
|
+
const learning = objectValue(parsed.value);
|
|
133
|
+
if (!learning) return { open: false, reasons: [], invalidReason: "learning.json must be an object" };
|
|
134
|
+
|
|
135
|
+
const reasons: string[] = [];
|
|
136
|
+
const status = stringField(learning, "status");
|
|
137
|
+
if (!status) return { open: false, reasons: [], invalidReason: "learning.status is missing or invalid" };
|
|
138
|
+
if (!KNOWN_LEARNING_STATUSES.has(status)) return { open: false, reasons: [], invalidReason: `learning.status is unknown: ${status}` };
|
|
139
|
+
if (status === "followup_required") reasons.push("learning.status is followup_required");
|
|
140
|
+
const routesResult = routeStatuses(learning);
|
|
141
|
+
if (!routesResult.ok) return { open: false, reasons: [], invalidReason: routesResult.reason };
|
|
142
|
+
const routes = routesResult.statuses;
|
|
143
|
+
if (routes.includes("open")) reasons.push("learning routing has an open route");
|
|
144
|
+
const unknown = routes.filter((routeStatus) => !KNOWN_LEARNING_ROUTE_STATUSES.has(routeStatus));
|
|
145
|
+
if (unknown.length) return { open: false, reasons: [], invalidReason: `learning routing has unknown status: ${unknown.join(", ")}` };
|
|
146
|
+
return { open: reasons.length > 0, reasons };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function invalidItem(slug: string, workflowPath: string, reason: string): AuditItem {
|
|
150
|
+
return {
|
|
151
|
+
slug,
|
|
152
|
+
path: workflowPath,
|
|
153
|
+
classification: "invalid",
|
|
154
|
+
state_status: null,
|
|
155
|
+
phase: null,
|
|
156
|
+
next_action_status: null,
|
|
157
|
+
reasons: [reason],
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function classifyWorkflow(slug: string, workflowPath: string): AuditItem {
|
|
162
|
+
const statePath = path.join(workflowPath, "state.json");
|
|
163
|
+
if (!fs.existsSync(statePath)) return invalidItem(slug, workflowPath, "missing state.json");
|
|
164
|
+
const parsed = readJson(statePath, "state.json");
|
|
165
|
+
if (!parsed.ok) return invalidItem(slug, workflowPath, parsed.reason);
|
|
166
|
+
const state = objectValue(parsed.value);
|
|
167
|
+
if (!state) return invalidItem(slug, workflowPath, "state.json must be an object");
|
|
168
|
+
|
|
169
|
+
const status = stringField(state, "status");
|
|
170
|
+
const phase = stringField(state, "phase");
|
|
171
|
+
if (!status) return invalidItem(slug, workflowPath, "state.status is missing or invalid");
|
|
172
|
+
if (!phase) return invalidItem(slug, workflowPath, "state.phase is missing or invalid");
|
|
173
|
+
const nextAction = validateNextAction(state);
|
|
174
|
+
if (!nextAction.ok) return invalidItem(slug, workflowPath, nextAction.reason);
|
|
175
|
+
const nextStatus = nextAction.status;
|
|
176
|
+
|
|
177
|
+
const base = { slug, path: workflowPath, state_status: status, phase, next_action_status: nextStatus };
|
|
178
|
+
const learning = learningSignals(workflowPath);
|
|
179
|
+
if (learning.invalidReason) return { ...base, classification: "invalid", reasons: [learning.invalidReason] };
|
|
180
|
+
if (learning.open) {
|
|
181
|
+
return { ...base, classification: "active_learning_followup", reasons: learning.reasons };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (ACTIVE_STATUSES.has(status)) {
|
|
185
|
+
return { ...base, classification: "active_wip", reasons: [`state.status is active: ${status}`] };
|
|
186
|
+
}
|
|
187
|
+
if (status === "verified" && nextStatus === "continue") {
|
|
188
|
+
return { ...base, classification: "active_wip", reasons: ["verified workflow still has next_action.status continue"] };
|
|
189
|
+
}
|
|
190
|
+
if (status === "verified" && nextStatus === "done") {
|
|
191
|
+
return { ...base, classification: "cleanup_candidate", reasons: ["verified workflow has next_action.status done"] };
|
|
192
|
+
}
|
|
193
|
+
if (["delivered", "accepted", "archived"].includes(status) && phase === "done") {
|
|
194
|
+
return { ...base, classification: "terminal_done", reasons: [`${status} workflow is in phase done`] };
|
|
195
|
+
}
|
|
196
|
+
if ((status === "accepted" || status === "archived") && learning.reasons.length === 0) {
|
|
197
|
+
return { ...base, classification: "terminal_done", reasons: [`${status} workflow has no open learning routing`] };
|
|
198
|
+
}
|
|
199
|
+
return { ...base, classification: "invalid", reasons: [`unrecognized lifecycle shape: status=${status}, phase=${phase}, next_action.status=${nextStatus ?? "missing"}`] };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function childWorkflowDirs(root: string): string[] {
|
|
203
|
+
return fs.readdirSync(root, { withFileTypes: true })
|
|
204
|
+
.filter((entry) => entry.isDirectory())
|
|
205
|
+
.map((entry) => entry.name)
|
|
206
|
+
.filter((name) => !name.startsWith(".") && !SKIPPED_ROOT_ENTRIES.has(name))
|
|
207
|
+
.sort();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function emptyBuckets(): Record<Classification, AuditItem[]> {
|
|
211
|
+
return {
|
|
212
|
+
active_wip: [],
|
|
213
|
+
cleanup_candidate: [],
|
|
214
|
+
terminal_done: [],
|
|
215
|
+
active_learning_followup: [],
|
|
216
|
+
invalid: [],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function audit(root: string): AuditResult {
|
|
221
|
+
const artifactRoot = path.resolve(root);
|
|
222
|
+
const stat = fs.statSync(artifactRoot);
|
|
223
|
+
if (!stat.isDirectory()) throw new Error(`artifact root is not a directory: ${artifactRoot}`);
|
|
224
|
+
const buckets = emptyBuckets();
|
|
225
|
+
for (const slug of childWorkflowDirs(artifactRoot)) {
|
|
226
|
+
const item = classifyWorkflow(slug, path.join(artifactRoot, slug));
|
|
227
|
+
buckets[item.classification].push(item);
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
artifact_root: artifactRoot,
|
|
231
|
+
buckets,
|
|
232
|
+
totals: {
|
|
233
|
+
scanned: Object.values(buckets).reduce((sum, items) => sum + items.length, 0),
|
|
234
|
+
active_wip: buckets.active_wip.length,
|
|
235
|
+
cleanup_candidate: buckets.cleanup_candidate.length,
|
|
236
|
+
terminal_done: buckets.terminal_done.length,
|
|
237
|
+
active_learning_followup: buckets.active_learning_followup.length,
|
|
238
|
+
invalid: buckets.invalid.length,
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function printBucket(title: string, items: AuditItem[]): void {
|
|
244
|
+
console.log(`${title}: ${items.length}`);
|
|
245
|
+
for (const item of items) {
|
|
246
|
+
console.log(` - ${item.slug} (${item.state_status ?? "unknown"} / ${item.phase ?? "unknown"} / next=${item.next_action_status ?? "missing"})`);
|
|
247
|
+
for (const reason of item.reasons) console.log(` reason: ${reason}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function printText(result: AuditResult): void {
|
|
252
|
+
console.log("Workflow artifact cleanup audit (dry run, read-only)");
|
|
253
|
+
console.log(`Artifact root: ${result.artifact_root}`);
|
|
254
|
+
console.log(`Scanned workflow directories: ${result.totals.scanned}`);
|
|
255
|
+
console.log("");
|
|
256
|
+
printBucket("Active WIP", result.buckets.active_wip);
|
|
257
|
+
printBucket("Active learning follow-ups", result.buckets.active_learning_followup);
|
|
258
|
+
printBucket("Cleanup candidates", result.buckets.cleanup_candidate);
|
|
259
|
+
printBucket("Terminal done", result.buckets.terminal_done);
|
|
260
|
+
printBucket("Invalid sidecars", result.buckets.invalid);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function main(argv = process.argv.slice(2)): number {
|
|
264
|
+
const args = parseArgs(argv);
|
|
265
|
+
if (flagBool(args.flags, "help") || flagBool(args.flags, "h")) {
|
|
266
|
+
printHelp();
|
|
267
|
+
return 0;
|
|
268
|
+
}
|
|
269
|
+
let result: AuditResult;
|
|
270
|
+
try {
|
|
271
|
+
result = audit(flagString(args.flags, "artifact-root", ".flow-agents") ?? ".flow-agents");
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.error(`workflow-artifact-cleanup-audit: ${error instanceof Error ? error.message : String(error)}`);
|
|
274
|
+
return 1;
|
|
275
|
+
}
|
|
276
|
+
if (flagBool(args.flags, "json")) console.log(JSON.stringify(result, null, 2));
|
|
277
|
+
else printText(result);
|
|
278
|
+
return 0;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (import.meta.url === `file://${process.argv[1]}`) process.exit(main());
|