codetrellis 0.0.0-stage → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +27 -2
- package/bin/codetrellis.mjs +4 -0
- package/package.json +41 -4
- package/reader/material-reader.mjs +130346 -0
- package/resources/tree-sitter/README.md +150 -0
- package/resources/tree-sitter/tree-sitter-c-sharp.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-go.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-java.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-javascript.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-kotlin.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-php.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-python.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-ruby.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-rust.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-swift.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-tsx.wasm +0 -0
- package/resources/tree-sitter/tree-sitter-typescript.wasm +0 -0
- package/resources/tree-sitter/tree-sitter.wasm +0 -0
- package/src/backend/agent/claude-code-watcher.js +270 -0
- package/src/backend/index.js +8 -0
- package/src/backend/lib/sha256-file.js +39 -0
- package/src/backend/lib/zip-entries.js +98 -0
- package/src/backend/lifecycle.js +102 -0
- package/src/backend/mcp/binding-headers.js +54 -0
- package/src/backend/mcp/client-identity.js +44 -0
- package/src/backend/mcp/connector/command.js +110 -0
- package/src/backend/mcp/connector/core.js +278 -0
- package/src/backend/mcp/connector/files.js +121 -0
- package/src/backend/mcp/connector/hook.js +284 -0
- package/src/backend/mcp/connector/main.js +126 -0
- package/src/backend/mcp/connector/sse-upstream.js +152 -0
- package/src/backend/mcp/helpers.js +43 -0
- package/src/backend/mcp/prompt-builders.js +104 -0
- package/src/backend/mcp/resources.js +197 -0
- package/src/backend/mcp/server.js +813 -0
- package/src/backend/mcp/skill-guide.js +1528 -0
- package/src/backend/mcp/tools/architecture-tools.js +273 -0
- package/src/backend/mcp/tools/audio-tools.js +117 -0
- package/src/backend/mcp/tools/awareness-tools.js +623 -0
- package/src/backend/mcp/tools/budget-tools.js +185 -0
- package/src/backend/mcp/tools/channel-tools.js +205 -0
- package/src/backend/mcp/tools/contribution-tools.js +181 -0
- package/src/backend/mcp/tools/drift-tools.js +252 -0
- package/src/backend/mcp/tools/git-tools.js +316 -0
- package/src/backend/mcp/tools/governance-tools.js +175 -0
- package/src/backend/mcp/tools/graph-tools.js +181 -0
- package/src/backend/mcp/tools/intake-tools.js +244 -0
- package/src/backend/mcp/tools/mobile-tools.js +118 -0
- package/src/backend/mcp/tools/peer-tools.js +330 -0
- package/src/backend/mcp/tools/plan-item-tools.js +1601 -0
- package/src/backend/mcp/tools/plan-tools.js +680 -0
- package/src/backend/mcp/tools/presence-tools.js +185 -0
- package/src/backend/mcp/tools/project-config-tools.js +150 -0
- package/src/backend/mcp/tools/review-tools.js +172 -0
- package/src/backend/mcp/tools/session-tools.js +554 -0
- package/src/backend/mcp/tools/system-docs-tools.js +168 -0
- package/src/backend/mcp/tools/terminal-tools.js +186 -0
- package/src/backend/mcp/tools/test-tools.js +117 -0
- package/src/backend/mcp/tools/ui-tools.js +356 -0
- package/src/backend/mcp/types.js +15 -0
- package/src/backend/middleware/local-auth.js +116 -0
- package/src/backend/server.js +5951 -0
- package/src/backend/services/agent-event-log.js +350 -0
- package/src/backend/services/architecture-rule.js +584 -0
- package/src/backend/services/architecture-rules.js +275 -0
- package/src/backend/services/artefact-content-service.js +181 -0
- package/src/backend/services/artefact-service.js +305 -0
- package/src/backend/services/artefact-watcher.js +167 -0
- package/src/backend/services/ast-parser.js +327 -0
- package/src/backend/services/audio-buffer-service.js +152 -0
- package/src/backend/services/awareness-notices.js +139 -0
- package/src/backend/services/awareness-replies.js +194 -0
- package/src/backend/services/awareness-service.js +344 -0
- package/src/backend/services/awareness-signals.js +267 -0
- package/src/backend/services/baseline-store.js +83 -0
- package/src/backend/services/branch-workstreams.js +341 -0
- package/src/backend/services/breakpoint-service.js +584 -0
- package/src/backend/services/brief-service.js +299 -0
- package/src/backend/services/budget-service.js +528 -0
- package/src/backend/services/callsites/base.js +15 -0
- package/src/backend/services/callsites/csharp.js +148 -0
- package/src/backend/services/callsites/go.js +163 -0
- package/src/backend/services/callsites/index.js +53 -0
- package/src/backend/services/callsites/kotlin.js +123 -0
- package/src/backend/services/callsites/process-env.js +125 -0
- package/src/backend/services/callsites/python.js +91 -0
- package/src/backend/services/callsites/ruby.js +164 -0
- package/src/backend/services/callsites/shared.js +198 -0
- package/src/backend/services/callsites/swift.js +115 -0
- package/src/backend/services/callsites/typescript.js +87 -0
- package/src/backend/services/capability-token.js +108 -0
- package/src/backend/services/change-check.js +156 -0
- package/src/backend/services/channel-dispatcher-service.js +235 -0
- package/src/backend/services/channel-event-file-service.js +125 -0
- package/src/backend/services/channel-event-service.js +297 -0
- package/src/backend/services/check-runs.js +177 -0
- package/src/backend/services/checkout-identity.js +73 -0
- package/src/backend/services/claude-code-parallel.js +221 -0
- package/src/backend/services/claude-desktop-config.js +186 -0
- package/src/backend/services/cli-install.js +249 -0
- package/src/backend/services/clock.js +57 -0
- package/src/backend/services/cloud-files.js +191 -0
- package/src/backend/services/coalesce.js +50 -0
- package/src/backend/services/code-breakpoints.js +291 -0
- package/src/backend/services/comment-service.js +168 -0
- package/src/backend/services/commit-attribution.js +134 -0
- package/src/backend/services/commit-edges.js +99 -0
- package/src/backend/services/confined-fs.js +241 -0
- package/src/backend/services/conformity-gate.js +132 -0
- package/src/backend/services/contribution-service.js +409 -0
- package/src/backend/services/coverage-service.js +173 -0
- package/src/backend/services/criteria-service.js +721 -0
- package/src/backend/services/criterion-checks.js +387 -0
- package/src/backend/services/criterion-loop-service.js +411 -0
- package/src/backend/services/cross-system-service.js +231 -0
- package/src/backend/services/database.js +916 -0
- package/src/backend/services/db-schema.js +1358 -0
- package/src/backend/services/deviation-service.js +344 -0
- package/src/backend/services/diff-engine.js +168 -0
- package/src/backend/services/evidence.js +363 -0
- package/src/backend/services/external-intake-service.js +269 -0
- package/src/backend/services/external-pointer-service.js +195 -0
- package/src/backend/services/external-refs-service.js +214 -0
- package/src/backend/services/file-history.js +158 -0
- package/src/backend/services/file-watcher.js +193 -0
- package/src/backend/services/folder-requests.js +118 -0
- package/src/backend/services/freeze-service.js +189 -0
- package/src/backend/services/gemini-cli-hook.js +172 -0
- package/src/backend/services/git-activity-service.js +325 -0
- package/src/backend/services/git-blobs.js +70 -0
- package/src/backend/services/git-branches.js +357 -0
- package/src/backend/services/git-checkout.js +153 -0
- package/src/backend/services/git-commit-service.js +91 -0
- package/src/backend/services/git-env.js +121 -0
- package/src/backend/services/git-identity.js +84 -0
- package/src/backend/services/git-refs.js +386 -0
- package/src/backend/services/git-safety.js +58 -0
- package/src/backend/services/grant-guard.js +67 -0
- package/src/backend/services/html-view-policy.js +135 -0
- package/src/backend/services/human-decision.js +55 -0
- package/src/backend/services/importers.js +159 -0
- package/src/backend/services/intent-service.js +105 -0
- package/src/backend/services/ipc-dispatcher.js +164 -0
- package/src/backend/services/item-git-state.js +208 -0
- package/src/backend/services/line-changes.js +232 -0
- package/src/backend/services/line-history.js +174 -0
- package/src/backend/services/local-api-changes.js +40 -0
- package/src/backend/services/logger.js +217 -0
- package/src/backend/services/material-footprints.js +203 -0
- package/src/backend/services/material-place.js +82 -0
- package/src/backend/services/material-reader/docx-markdown.js +198 -0
- package/src/backend/services/material-reader/fixtures.test-helper.js +169 -0
- package/src/backend/services/material-reader/quote.js +44 -0
- package/src/backend/services/material-reader/reader-host.js +202 -0
- package/src/backend/services/material-signals.js +141 -0
- package/src/backend/services/mcp-capabilities.js +390 -0
- package/src/backend/services/mdns-service.js +304 -0
- package/src/backend/services/mobile-api-server.js +281 -0
- package/src/backend/services/mobile-approvals.js +306 -0
- package/src/backend/services/mobile-awareness.js +180 -0
- package/src/backend/services/mobile-breakpoints.js +115 -0
- package/src/backend/services/mobile-budget.js +98 -0
- package/src/backend/services/mobile-freeze.js +106 -0
- package/src/backend/services/mobile-proposals.js +100 -0
- package/src/backend/services/mobile-rpc-service.js +1289 -0
- package/src/backend/services/mobile-workstreams.js +160 -0
- package/src/backend/services/monorepo-detector.js +131 -0
- package/src/backend/services/other-work.js +45 -0
- package/src/backend/services/pack-seal.js +129 -0
- package/src/backend/services/paired-device-service.js +192 -0
- package/src/backend/services/pairing-server.js +323 -0
- package/src/backend/services/pairing-service.js +200 -0
- package/src/backend/services/pantry-resolution-service.js +166 -0
- package/src/backend/services/parsers/base.js +124 -0
- package/src/backend/services/parsers/csharp.js +224 -0
- package/src/backend/services/parsers/go.js +180 -0
- package/src/backend/services/parsers/index.js +101 -0
- package/src/backend/services/parsers/java.js +103 -0
- package/src/backend/services/parsers/kotlin.js +252 -0
- package/src/backend/services/parsers/php.js +113 -0
- package/src/backend/services/parsers/python.js +189 -0
- package/src/backend/services/parsers/ruby.js +212 -0
- package/src/backend/services/parsers/rust.js +97 -0
- package/src/backend/services/parsers/swift.js +221 -0
- package/src/backend/services/parsers/typescript.js +235 -0
- package/src/backend/services/pattern-scan.js +65 -0
- package/src/backend/services/patterns.js +281 -0
- package/src/backend/services/peer-audit-service.js +123 -0
- package/src/backend/services/peer-auth.js +153 -0
- package/src/backend/services/peer-capabilities.js +209 -0
- package/src/backend/services/peer-connection-service.js +450 -0
- package/src/backend/services/peer-grants.js +33 -0
- package/src/backend/services/persistence.js +116 -0
- package/src/backend/services/personal-sync-service.js +232 -0
- package/src/backend/services/pipeline-approvals.js +72 -0
- package/src/backend/services/pipeline.js +232 -0
- package/src/backend/services/plan-arrivals.js +68 -0
- package/src/backend/services/plan-changes-service.js +273 -0
- package/src/backend/services/plan-conflict-service.js +288 -0
- package/src/backend/services/plan-dependencies.js +91 -0
- package/src/backend/services/plan-doc-guard.js +90 -0
- package/src/backend/services/plan-documents-service.js +275 -0
- package/src/backend/services/plan-event-service.js +154 -0
- package/src/backend/services/plan-file-refs.js +106 -0
- package/src/backend/services/plan-file-service.js +1564 -0
- package/src/backend/services/plan-history-service.js +156 -0
- package/src/backend/services/plan-import-service.js +261 -0
- package/src/backend/services/plan-item-service.js +1223 -0
- package/src/backend/services/plan-migrate-service.js +395 -0
- package/src/backend/services/plan-overlay-service.js +173 -0
- package/src/backend/services/plan-phases-service.js +182 -0
- package/src/backend/services/plan-progress-service.js +134 -0
- package/src/backend/services/plan-review-service.js +262 -0
- package/src/backend/services/plan-service.js +682 -0
- package/src/backend/services/plan-status.js +113 -0
- package/src/backend/services/plan-template-publish-service.js +217 -0
- package/src/backend/services/plan-templates-service.js +242 -0
- package/src/backend/services/plan-templates.js +1164 -0
- package/src/backend/services/planned-overlap-actions.js +200 -0
- package/src/backend/services/plans-home.js +224 -0
- package/src/backend/services/play-forward.js +256 -0
- package/src/backend/services/playback-service.js +117 -0
- package/src/backend/services/power-service.js +167 -0
- package/src/backend/services/power-signals.js +81 -0
- package/src/backend/services/pr-draft-service.js +142 -0
- package/src/backend/services/presence-service.js +122 -0
- package/src/backend/services/pricing.js +60 -0
- package/src/backend/services/project-config-service.js +474 -0
- package/src/backend/services/project-scanner.js +184 -0
- package/src/backend/services/projection-service.js +69 -0
- package/src/backend/services/push-notification-service.js +288 -0
- package/src/backend/services/recent-projects-service.js +204 -0
- package/src/backend/services/record-chain.js +162 -0
- package/src/backend/services/recurrence-rule.js +63 -0
- package/src/backend/services/recurring-agent.js +89 -0
- package/src/backend/services/recurring-scheduler.js +77 -0
- package/src/backend/services/recurring-service.js +276 -0
- package/src/backend/services/reference-service.js +169 -0
- package/src/backend/services/release-public-key.js +30 -0
- package/src/backend/services/release-signature.js +64 -0
- package/src/backend/services/remote-audio-service.js +224 -0
- package/src/backend/services/remote-interaction-service.js +275 -0
- package/src/backend/services/remote-terminal-service.js +324 -0
- package/src/backend/services/rendition/child/main.js +76 -0
- package/src/backend/services/rendition/engine-host.js +249 -0
- package/src/backend/services/rendition/engine-lock.js +39 -0
- package/src/backend/services/rendition/engine-lock.json +14 -0
- package/src/backend/services/rendition/engine-manifest.js +97 -0
- package/src/backend/services/rendition/network-proof.js +73 -0
- package/src/backend/services/rendition/rendition-service.js +175 -0
- package/src/backend/services/replay-frames.js +387 -0
- package/src/backend/services/replay-state.js +201 -0
- package/src/backend/services/resolvers/base.js +108 -0
- package/src/backend/services/resolvers/csharp.js +109 -0
- package/src/backend/services/resolvers/go.js +105 -0
- package/src/backend/services/resolvers/index.js +60 -0
- package/src/backend/services/resolvers/java.js +63 -0
- package/src/backend/services/resolvers/kotlin.js +109 -0
- package/src/backend/services/resolvers/php.js +89 -0
- package/src/backend/services/resolvers/python.js +154 -0
- package/src/backend/services/resolvers/ruby.js +103 -0
- package/src/backend/services/resolvers/rust.js +77 -0
- package/src/backend/services/resolvers/swift.js +98 -0
- package/src/backend/services/resolvers/typescript.js +71 -0
- package/src/backend/services/retention.js +54 -0
- package/src/backend/services/review-architecture.js +236 -0
- package/src/backend/services/review-attest.js +68 -0
- package/src/backend/services/review-bundle.js +226 -0
- package/src/backend/services/review-graduation.js +93 -0
- package/src/backend/services/review-host/bitbucket.js +79 -0
- package/src/backend/services/review-host/detect.js +80 -0
- package/src/backend/services/review-host/github.js +97 -0
- package/src/backend/services/review-host/gitlab.js +77 -0
- package/src/backend/services/review-host/host-state.js +103 -0
- package/src/backend/services/review-host/http.js +58 -0
- package/src/backend/services/review-host/switch.js +120 -0
- package/src/backend/services/review-marks.js +72 -0
- package/src/backend/services/review-notes.js +169 -0
- package/src/backend/services/review-other-work.js +90 -0
- package/src/backend/services/review-queue-service.js +147 -0
- package/src/backend/services/review-risk.js +127 -0
- package/src/backend/services/review-task.js +121 -0
- package/src/backend/services/rule-approvals.js +275 -0
- package/src/backend/services/rule-baseline.js +134 -0
- package/src/backend/services/rule-changes.js +162 -0
- package/src/backend/services/rule-preview.js +47 -0
- package/src/backend/services/rule-proposals.js +104 -0
- package/src/backend/services/rule-scope.js +76 -0
- package/src/backend/services/rulebook.js +286 -0
- package/src/backend/services/rules-at.js +56 -0
- package/src/backend/services/rules-overview.js +88 -0
- package/src/backend/services/schema-reconciler.js +250 -0
- package/src/backend/services/secret-store.js +119 -0
- package/src/backend/services/section-workstreams.js +95 -0
- package/src/backend/services/self-write-tracker.js +75 -0
- package/src/backend/services/sensor-bridge-service.js +285 -0
- package/src/backend/services/session-service.js +192 -0
- package/src/backend/services/settings-service.js +324 -0
- package/src/backend/services/signal-breakpoints.js +172 -0
- package/src/backend/services/signed-approval-record.js +86 -0
- package/src/backend/services/signed-approvals.js +244 -0
- package/src/backend/services/signoff-pack.js +328 -0
- package/src/backend/services/signoff-rows.js +114 -0
- package/src/backend/services/skill-arrival-service.js +138 -0
- package/src/backend/services/skill-model.js +193 -0
- package/src/backend/services/skill-use-service.js +140 -0
- package/src/backend/services/skills-service.js +150 -0
- package/src/backend/services/snapshot-compare-service.js +397 -0
- package/src/backend/services/source-control.js +189 -0
- package/src/backend/services/spec-links-service.js +123 -0
- package/src/backend/services/spec-proposal-withdraw.js +50 -0
- package/src/backend/services/spec-proposals-service.js +470 -0
- package/src/backend/services/sql/embedded.js +95 -0
- package/src/backend/services/sql/index.js +63 -0
- package/src/backend/services/sql/refs.js +357 -0
- package/src/backend/services/sql/schema.js +312 -0
- package/src/backend/services/sql/tokenizer.js +195 -0
- package/src/backend/services/stack-overlaps.js +117 -0
- package/src/backend/services/stack-service.js +225 -0
- package/src/backend/services/state-sync-service.js +384 -0
- package/src/backend/services/stuck-sensor-service.js +200 -0
- package/src/backend/services/system-discovery.js +481 -0
- package/src/backend/services/system-docs-service.js +701 -0
- package/src/backend/services/task-attachments-service.js +340 -0
- package/src/backend/services/task-grounding.js +60 -0
- package/src/backend/services/task-records/check-run-record.js +146 -0
- package/src/backend/services/task-records/check-runs.js +115 -0
- package/src/backend/services/task-records/heads.js +88 -0
- package/src/backend/services/task-records/material-reads.js +225 -0
- package/src/backend/services/task-records/read-record.js +100 -0
- package/src/backend/services/task-records/record.js +173 -0
- package/src/backend/services/task-records/run-record.js +136 -0
- package/src/backend/services/task-records/shared-state.js +548 -0
- package/src/backend/services/task-records/signing.js +140 -0
- package/src/backend/services/task-records/split-signals.js +52 -0
- package/src/backend/services/task-records/test-runs.js +164 -0
- package/src/backend/services/task-records/trust.js +243 -0
- package/src/backend/services/task-rules.js +81 -0
- package/src/backend/services/task-workstreams.js +90 -0
- package/src/backend/services/terminal-history-service.js +266 -0
- package/src/backend/services/terminal-service.js +267 -0
- package/src/backend/services/test-clock.js +53 -0
- package/src/backend/services/tests/grounding.js +266 -0
- package/src/backend/services/tests/junit.js +104 -0
- package/src/backend/services/tests/teammate-runs.js +134 -0
- package/src/backend/services/tests/test-results.js +234 -0
- package/src/backend/services/tree-watcher.js +204 -0
- package/src/backend/services/trellis-service.js +204 -0
- package/src/backend/services/trusted-roots.js +186 -0
- package/src/backend/services/update-download-service.js +342 -0
- package/src/backend/services/update-service.js +266 -0
- package/src/backend/services/watch-ignore.js +76 -0
- package/src/backend/services/webhook-egress.js +206 -0
- package/src/backend/services/webrtc-service.js +413 -0
- package/src/backend/services/work-changes.js +59 -0
- package/src/backend/services/workstream-binding.js +82 -0
- package/src/backend/services/workstream-commits.js +116 -0
- package/src/backend/services/workstream-imports.js +177 -0
- package/src/backend/services/workstream-service.js +335 -0
- package/src/backend/services/workstream-symbols.js +223 -0
- package/src/backend/services/workstream-watch-service.js +391 -0
- package/src/backend/services/worktree-service.js +235 -0
- package/src/cli/agent.js +100 -0
- package/src/cli/args.js +209 -0
- package/src/cli/conformity.js +106 -0
- package/src/cli/desktop.js +91 -0
- package/src/cli/main.js +411 -0
- package/src/cli/pipeline.js +109 -0
- package/src/cli/plan-verbs.js +259 -0
- package/src/cli/review-adapters.js +220 -0
- package/src/cli/review-output.js +93 -0
- package/src/cli/review-post.js +89 -0
- package/src/cli/review-sink.js +183 -0
- package/src/cli/review-skill.js +44 -0
- package/src/cli/review-verify.js +104 -0
- package/src/cli/review.js +386 -0
- package/src/cli/sarif.js +148 -0
- package/src/cli/verbs.js +360 -0
- package/src/shared/build-info.js +39 -0
- package/src/shared/lib/agent-review.js +199 -0
- package/src/shared/lib/agent-turns.js +140 -0
- package/src/shared/lib/awareness-digest.js +120 -0
- package/src/shared/lib/branch-name.js +45 -0
- package/src/shared/lib/breakpoint-words.js +118 -0
- package/src/shared/lib/budget-words.js +56 -0
- package/src/shared/lib/burst.js +69 -0
- package/src/shared/lib/call-entry.js +105 -0
- package/src/shared/lib/check-compare.js +42 -0
- package/src/shared/lib/check-words.js +216 -0
- package/src/shared/lib/csv.js +62 -0
- package/src/shared/lib/folder-entry.js +87 -0
- package/src/shared/lib/freeze-words.js +60 -0
- package/src/shared/lib/fuzzy.js +84 -0
- package/src/shared/lib/git-state-words.js +80 -0
- package/src/shared/lib/grep-entry.js +86 -0
- package/src/shared/lib/grounding-line.js +61 -0
- package/src/shared/lib/import-line.js +71 -0
- package/src/shared/lib/item-status.js +249 -0
- package/src/shared/lib/line-changes.js +53 -0
- package/src/shared/lib/locator.js +71 -0
- package/src/shared/lib/matcher.js +120 -0
- package/src/shared/lib/merge-order.js +65 -0
- package/src/shared/lib/open-findings.js +68 -0
- package/src/shared/lib/other-work.js +108 -0
- package/src/shared/lib/package-entry.js +142 -0
- package/src/shared/lib/plan-vocab.js +40 -0
- package/src/shared/lib/pptx-xml.js +86 -0
- package/src/shared/lib/proposal-words.js +61 -0
- package/src/shared/lib/recurrence.js +183 -0
- package/src/shared/lib/references.js +72 -0
- package/src/shared/lib/rule-pattern.js +56 -0
- package/src/shared/lib/sdp-fingerprint.js +115 -0
- package/src/shared/lib/sdp-minimal.js +123 -0
- package/src/shared/lib/secret-paths.js +34 -0
- package/src/shared/lib/signal-words.js +172 -0
- package/src/shared/lib/signoff.js +56 -0
- package/src/shared/lib/skills-note.js +60 -0
- package/src/shared/lib/spec-sections.js +78 -0
- package/src/shared/lib/symbol-entry.js +60 -0
- package/src/shared/lib/tool-phrasing.js +338 -0
- package/src/shared/lib/turn-gap.js +27 -0
- package/src/shared/lib/workstream-words.js +38 -0
- package/src/shared/lib/xlsx-xml.js +144 -0
- package/src/shared/lib/zip-reader.js +99 -0
- package/src/shared/types/agent.js +27 -0
- package/src/shared/types/architecture-rules.js +30 -0
- package/src/shared/types/ast.js +15 -0
- package/src/shared/types/breakpoint.js +33 -0
- package/src/shared/types/channel.js +41 -0
- package/src/shared/types/criteria.js +15 -0
- package/src/shared/types/graph.js +15 -0
- package/src/shared/types/index.js +53 -0
- package/src/shared/types/peer.js +36 -0
- package/src/shared/types/pipeline.js +15 -0
- package/src/shared/types/plan.js +15 -0
- package/src/shared/types/play-forward.js +15 -0
- package/src/shared/types/power.js +15 -0
- package/src/shared/types/presence.js +15 -0
- package/src/shared/types/project-config.js +35 -0
- package/src/shared/types/project.js +15 -0
- package/src/shared/types/record.js +15 -0
- package/src/shared/types/recurring.js +15 -0
- package/src/shared/types/review.js +15 -0
- package/src/shared/types/settings.js +114 -0
- package/src/shared/types/stack.js +15 -0
- package/src/shared/types/system-doc.js +15 -0
- package/src/shared/types/system.js +15 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
var signed_approvals_exports = {};
|
|
29
|
+
__export(signed_approvals_exports, {
|
|
30
|
+
importApprovals: () => importApprovals,
|
|
31
|
+
listSignedApprovals: () => listSignedApprovals,
|
|
32
|
+
signApproval: () => signApproval,
|
|
33
|
+
signingSetup: () => signingSetup,
|
|
34
|
+
verifyRecord: () => verifyRecord
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(signed_approvals_exports);
|
|
37
|
+
var import_node_fs = __toESM(require("node:fs"));
|
|
38
|
+
var import_node_os = __toESM(require("node:os"));
|
|
39
|
+
var import_node_path = __toESM(require("node:path"));
|
|
40
|
+
var import_node_child_process = require("node:child_process");
|
|
41
|
+
var import_database = require("./database");
|
|
42
|
+
var import_persistence = require("./persistence");
|
|
43
|
+
var import_confined_fs = require("./confined-fs");
|
|
44
|
+
var import_self_write_tracker = require("./self-write-tracker");
|
|
45
|
+
var import_signed_approval_record = require("./signed-approval-record");
|
|
46
|
+
const MAX_RECORDS = 500;
|
|
47
|
+
const SSH_KEYGEN = "ssh-keygen";
|
|
48
|
+
function gitConfig(repo, key) {
|
|
49
|
+
try {
|
|
50
|
+
const v = (0, import_node_child_process.execFileSync)("git", ["-C", repo, "config", "--get", key], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 5e3 }).trim();
|
|
51
|
+
return v || null;
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const expandHome = (p) => p.startsWith("~/") ? import_node_path.default.join(import_node_os.default.homedir(), p.slice(2)) : p;
|
|
57
|
+
function signingSetup(repo) {
|
|
58
|
+
const signer = gitConfig(repo, "user.email");
|
|
59
|
+
const format = gitConfig(repo, "gpg.format");
|
|
60
|
+
const key = gitConfig(repo, "user.signingkey");
|
|
61
|
+
const allowed = gitConfig(repo, "gpg.ssh.allowedSignersFile");
|
|
62
|
+
const allowedSigners = allowed ? import_node_path.default.resolve(repo, expandHome(allowed)) : null;
|
|
63
|
+
const no = (why) => ({ canSign: false, why, signer, keyFile: null, allowedSigners });
|
|
64
|
+
if (format !== "ssh") return no("git signing is not set up with an SSH key (gpg.format ssh)");
|
|
65
|
+
if (!key) return no("git has no signing key (user.signingkey)");
|
|
66
|
+
if (!signer) return no("git has no user.email to sign as");
|
|
67
|
+
let keyFile;
|
|
68
|
+
if (key.startsWith("key::")) {
|
|
69
|
+
keyFile = import_node_path.default.join(import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os.default.tmpdir(), "codetrellis-signing-")), "key.pub");
|
|
70
|
+
import_node_fs.default.writeFileSync(keyFile, `${key.slice(5).trim()}
|
|
71
|
+
`, { mode: 384, flag: "wx" });
|
|
72
|
+
} else {
|
|
73
|
+
keyFile = import_node_path.default.resolve(repo, expandHome(key));
|
|
74
|
+
if (!import_node_fs.default.existsSync(keyFile)) return no(`the signing key ${key} is not on this machine`);
|
|
75
|
+
}
|
|
76
|
+
return { canSign: true, why: null, signer, keyFile, allowedSigners };
|
|
77
|
+
}
|
|
78
|
+
function insertRow(r) {
|
|
79
|
+
(0, import_database.getDb)().run(
|
|
80
|
+
`INSERT OR REPLACE INTO signed_approvals (uid, plan_uid, item_uid, criterion_uid, signer, origin, state, reason, file, at)
|
|
81
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
82
|
+
[r.uid, r.planUid, r.itemUid, r.criterionUid, r.signer, r.origin, r.state, r.reason, r.file ?? null, r.at]
|
|
83
|
+
);
|
|
84
|
+
(0, import_persistence.markDirty)();
|
|
85
|
+
}
|
|
86
|
+
function rowToApproval(r) {
|
|
87
|
+
return {
|
|
88
|
+
uid: r[0],
|
|
89
|
+
planUid: r[1],
|
|
90
|
+
itemUid: r[2],
|
|
91
|
+
criterionUid: r[3],
|
|
92
|
+
signer: r[4] ?? null,
|
|
93
|
+
origin: r[5],
|
|
94
|
+
state: r[6],
|
|
95
|
+
reason: r[7] ?? null,
|
|
96
|
+
at: r[8]
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function listSignedApprovals(itemUid) {
|
|
100
|
+
const res = (0, import_database.getDb)().exec(
|
|
101
|
+
`SELECT uid, plan_uid, item_uid, criterion_uid, signer, origin, state, reason, at FROM signed_approvals WHERE item_uid = ? ORDER BY at DESC`,
|
|
102
|
+
[itemUid]
|
|
103
|
+
);
|
|
104
|
+
return (res[0]?.values ?? []).map(rowToApproval);
|
|
105
|
+
}
|
|
106
|
+
function signApproval(input) {
|
|
107
|
+
const base = { uid: input.signoffUid, planUid: input.planUid, itemUid: input.itemUid, criterionUid: input.criterionUid, origin: "here", at: input.at };
|
|
108
|
+
const local = (why, signer = null) => {
|
|
109
|
+
const row2 = { ...base, signer, state: "local", reason: why };
|
|
110
|
+
insertRow(row2);
|
|
111
|
+
return row2;
|
|
112
|
+
};
|
|
113
|
+
if (!input.repo || !input.planDir) return local("the plan is not shared through a folder, so the approval stays on this machine");
|
|
114
|
+
if (process.env.CODETRELLIS_SIGN_APPROVALS === "0") return local("signing approvals is turned off on this machine, so the approval stays on it");
|
|
115
|
+
const setup = signingSetup(input.repo);
|
|
116
|
+
if (!setup.canSign) return local(`${setup.why}, so the approval stays on this machine`, setup.signer);
|
|
117
|
+
const statement = {
|
|
118
|
+
uid: input.signoffUid,
|
|
119
|
+
planUid: input.planUid,
|
|
120
|
+
itemUid: input.itemUid,
|
|
121
|
+
criterionUid: input.criterionUid,
|
|
122
|
+
criterionSha256: (0, import_signed_approval_record.sha256)(input.criterionText),
|
|
123
|
+
decision: "approved",
|
|
124
|
+
evidence: input.evidence,
|
|
125
|
+
signer: setup.signer,
|
|
126
|
+
at: new Date(input.at).toISOString()
|
|
127
|
+
};
|
|
128
|
+
let signature;
|
|
129
|
+
try {
|
|
130
|
+
signature = (0, import_node_child_process.execFileSync)(SSH_KEYGEN, ["-Y", "sign", "-f", setup.keyFile, "-n", import_signed_approval_record.NAMESPACE], {
|
|
131
|
+
input: (0, import_signed_approval_record.canonicalStatement)(statement),
|
|
132
|
+
encoding: "utf-8",
|
|
133
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
134
|
+
timeout: 15e3
|
|
135
|
+
});
|
|
136
|
+
} catch (err) {
|
|
137
|
+
const why = err.stderr?.toString().trim().split("\n").pop() || err.message;
|
|
138
|
+
return local(`git's SSH key could not sign it (${why.slice(0, 160)}), so the approval stays on this machine`, setup.signer);
|
|
139
|
+
}
|
|
140
|
+
const rel = import_node_path.default.join("approvals", `${input.signoffUid}.yaml`);
|
|
141
|
+
try {
|
|
142
|
+
const written = (0, import_confined_fs.writeFileWithin)(input.planDir, rel, (0, import_signed_approval_record.recordYaml)(statement, signature), "approval record");
|
|
143
|
+
(0, import_self_write_tracker.stampSelfWrite)(written);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
return local(`the record could not be written (${err.message}), so the approval stays on this machine`, setup.signer);
|
|
146
|
+
}
|
|
147
|
+
const row = { ...base, signer: setup.signer, state: "signed", reason: null };
|
|
148
|
+
insertRow({ ...row, file: rel });
|
|
149
|
+
return row;
|
|
150
|
+
}
|
|
151
|
+
function verifyRecord(text, signature, signer, allowedSigners, namespace = import_signed_approval_record.NAMESPACE) {
|
|
152
|
+
if (!allowedSigners) return { ok: false, reason: "git has no allowed signers to check it against (gpg.ssh.allowedSignersFile)" };
|
|
153
|
+
if (!import_node_fs.default.existsSync(allowedSigners)) return { ok: false, reason: `the allowed signers file ${allowedSigners} is not on this machine` };
|
|
154
|
+
const dir = import_node_fs.default.mkdtempSync(import_node_path.default.join(import_node_os.default.tmpdir(), "codetrellis-verify-"));
|
|
155
|
+
try {
|
|
156
|
+
const sig = import_node_path.default.join(dir, "approval.sig");
|
|
157
|
+
import_node_fs.default.writeFileSync(sig, signature, { mode: 384 });
|
|
158
|
+
(0, import_node_child_process.execFileSync)(SSH_KEYGEN, ["-Y", "verify", "-f", allowedSigners, "-I", signer, "-n", namespace, "-s", sig], {
|
|
159
|
+
input: text,
|
|
160
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
161
|
+
timeout: 15e3
|
|
162
|
+
});
|
|
163
|
+
return { ok: true };
|
|
164
|
+
} catch (err) {
|
|
165
|
+
const e = err;
|
|
166
|
+
if (e.code === "ENOENT") return { ok: false, reason: "ssh-keygen is not installed, so the signature cannot be checked" };
|
|
167
|
+
const said = e.stderr?.toString().trim().split("\n").pop() ?? "";
|
|
168
|
+
return { ok: false, reason: `the signature does not verify for ${signer}${said ? ` (${said.slice(0, 160)})` : ""}` };
|
|
169
|
+
} finally {
|
|
170
|
+
import_node_fs.default.rmSync(dir, { recursive: true, force: true });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function importApprovals(planDir, planUid, repo, lookup) {
|
|
174
|
+
const dir = import_node_path.default.join(planDir, "approvals");
|
|
175
|
+
let names;
|
|
176
|
+
try {
|
|
177
|
+
names = import_node_fs.default.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".yaml")).map((e) => e.name).slice(0, MAX_RECORDS);
|
|
178
|
+
} catch {
|
|
179
|
+
return { verified: 0, unverified: 0 };
|
|
180
|
+
}
|
|
181
|
+
const known = new Set(((0, import_database.getDb)().exec(
|
|
182
|
+
`SELECT uid FROM signed_approvals WHERE plan_uid = ? AND (origin = 'here' OR state = 'verified')`,
|
|
183
|
+
[planUid]
|
|
184
|
+
)[0]?.values ?? []).map((r) => r[0]));
|
|
185
|
+
const allowed = signingSetup(repo).allowedSigners;
|
|
186
|
+
let verified = 0;
|
|
187
|
+
let unverified = 0;
|
|
188
|
+
for (const name of names) {
|
|
189
|
+
const rel = import_node_path.default.join("approvals", name);
|
|
190
|
+
let content;
|
|
191
|
+
try {
|
|
192
|
+
content = (0, import_confined_fs.readTextWithin)(planDir, rel, "approval record");
|
|
193
|
+
} catch {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const parsed = (0, import_signed_approval_record.parseRecord)(content);
|
|
197
|
+
const uidFromName = name.slice(0, -".yaml".length);
|
|
198
|
+
if (known.has(uidFromName)) continue;
|
|
199
|
+
const at = parsed.ok ? Date.parse(parsed.statement.at) : Date.now();
|
|
200
|
+
const unverifiable = (reason, item, criterion, signer) => {
|
|
201
|
+
insertRow({ uid: uidFromName, planUid, itemUid: item, criterionUid: criterion, signer, origin: "file", state: "unverified", reason, at, file: rel });
|
|
202
|
+
unverified++;
|
|
203
|
+
};
|
|
204
|
+
if (!parsed.ok) {
|
|
205
|
+
unverifiable(parsed.reason, "", "", null);
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const s = parsed.statement;
|
|
209
|
+
if (s.uid !== uidFromName) {
|
|
210
|
+
unverifiable("the file is named for another approval", s.itemUid, s.criterionUid, s.signer);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (s.planUid !== planUid) {
|
|
214
|
+
unverifiable("the record is for another plan", s.itemUid, s.criterionUid, s.signer);
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const c = lookup.criterion(s.criterionUid);
|
|
218
|
+
if (!c || c.itemUid !== s.itemUid || c.planUid !== planUid) {
|
|
219
|
+
unverifiable("the criterion it approves is not in this plan", s.itemUid, s.criterionUid, s.signer);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if ((0, import_signed_approval_record.sha256)(c.text) !== s.criterionSha256) {
|
|
223
|
+
unverifiable("the criterion has been reworded since it was approved", s.itemUid, s.criterionUid, s.signer);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const check = verifyRecord(parsed.text, parsed.signature, s.signer, allowed);
|
|
227
|
+
if (!check.ok) {
|
|
228
|
+
unverifiable(check.reason, s.itemUid, s.criterionUid, s.signer);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
lookup.addSignoff({ uid: s.uid, criterionUid: s.criterionUid, actor: s.signer, evidence: s.evidence, at });
|
|
232
|
+
insertRow({ uid: s.uid, planUid, itemUid: s.itemUid, criterionUid: s.criterionUid, signer: s.signer, origin: "file", state: "verified", reason: null, at, file: rel });
|
|
233
|
+
verified++;
|
|
234
|
+
}
|
|
235
|
+
return { verified, unverified };
|
|
236
|
+
}
|
|
237
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
238
|
+
0 && (module.exports = {
|
|
239
|
+
importApprovals,
|
|
240
|
+
listSignedApprovals,
|
|
241
|
+
signApproval,
|
|
242
|
+
signingSetup,
|
|
243
|
+
verifyRecord
|
|
244
|
+
});
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var signoff_pack_exports = {};
|
|
19
|
+
__export(signoff_pack_exports, {
|
|
20
|
+
PACK_FORMAT: () => PACK_FORMAT,
|
|
21
|
+
PACK_VERSION: () => PACK_VERSION,
|
|
22
|
+
PackError: () => PackError,
|
|
23
|
+
buildSignoffPack: () => buildSignoffPack,
|
|
24
|
+
esc: () => esc,
|
|
25
|
+
packFilesOf: () => packFilesOf,
|
|
26
|
+
packFromText: () => packFromText,
|
|
27
|
+
packSignals: () => packSignals,
|
|
28
|
+
packSkills: () => packSkills,
|
|
29
|
+
renderPackHtml: () => renderPackHtml,
|
|
30
|
+
scriptJson: () => scriptJson,
|
|
31
|
+
verifyPack: () => verifyPack
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(signoff_pack_exports);
|
|
34
|
+
var import_plan_service = require("./plan-service");
|
|
35
|
+
var import_trusted_roots = require("./trusted-roots");
|
|
36
|
+
var import_sha256_file = require("../lib/sha256-file");
|
|
37
|
+
var import_signoff_rows = require("./signoff-rows");
|
|
38
|
+
var import_plan_item_service = require("./plan-item-service");
|
|
39
|
+
var import_skill_use_service = require("./skill-use-service");
|
|
40
|
+
var import_signoff = require("../../shared/lib/signoff");
|
|
41
|
+
var import_awareness_service = require("./awareness-service");
|
|
42
|
+
var import_task_workstreams = require("./task-workstreams");
|
|
43
|
+
var import_other_work = require("../../shared/lib/other-work");
|
|
44
|
+
var import_signal_words = require("../../shared/lib/signal-words");
|
|
45
|
+
var import_material_place = require("./material-place");
|
|
46
|
+
const PACK_FORMAT = "codetrellis-signoff-pack";
|
|
47
|
+
const PACK_VERSION = 1;
|
|
48
|
+
function packSignals(planUid, projectPath) {
|
|
49
|
+
if (!projectPath) return [];
|
|
50
|
+
let signals;
|
|
51
|
+
try {
|
|
52
|
+
signals = (0, import_awareness_service.loadSignals)(projectPath).filter((s) => s.subject.material);
|
|
53
|
+
} catch {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
if (signals.length === 0) return [];
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const item of (0, import_plan_item_service.listAllItems)(planUid)) {
|
|
59
|
+
if (item.kind !== "action") continue;
|
|
60
|
+
const root = (0, import_task_workstreams.taskWorkstreamId)(item.uid);
|
|
61
|
+
const touched = signals.filter((s) => s.workstreams.includes(root));
|
|
62
|
+
if (touched.length === 0) continue;
|
|
63
|
+
const byId = new Map(touched.map((s) => [s.id, s]));
|
|
64
|
+
const label = (r) => touched.find((s) => s.subject.labels?.[r])?.subject.labels?.[r] ?? r;
|
|
65
|
+
for (const e of (0, import_other_work.otherWorkInFlight)({ root, name: item.title }, touched, label).entries) {
|
|
66
|
+
out.push({
|
|
67
|
+
itemUid: item.uid,
|
|
68
|
+
itemTitle: item.title,
|
|
69
|
+
signalId: e.signalId,
|
|
70
|
+
severity: e.severity,
|
|
71
|
+
heading: e.heading,
|
|
72
|
+
says: (0, import_signal_words.briefLine)(byId.get(e.signalId), root),
|
|
73
|
+
outcome: e.outcome,
|
|
74
|
+
outcomeWords: e.outcomeWords,
|
|
75
|
+
notes: e.notes
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
function packSkills(planUid) {
|
|
82
|
+
const out = [];
|
|
83
|
+
for (const item of (0, import_plan_item_service.listAllItems)(planUid)) {
|
|
84
|
+
if (item.kind !== "action") continue;
|
|
85
|
+
const wanted = (0, import_plan_item_service.resolveSkills)(item).filter((s) => s.required || s.use === "recommended");
|
|
86
|
+
if (wanted.length === 0) continue;
|
|
87
|
+
const proof = (0, import_skill_use_service.skillProof)(item, wanted);
|
|
88
|
+
const sources = (0, import_skill_use_service.skillUseSources)(item.uid);
|
|
89
|
+
for (const s of wanted) {
|
|
90
|
+
const p = proof?.get(s.name) ?? null;
|
|
91
|
+
out.push({
|
|
92
|
+
itemUid: item.uid,
|
|
93
|
+
itemTitle: item.title,
|
|
94
|
+
name: s.name,
|
|
95
|
+
use: s.required ? "required" : "recommended",
|
|
96
|
+
proof: p,
|
|
97
|
+
proofSource: p === "used" ? (0, import_skill_use_service.sourceOf)(sources, s.name) : null
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
function buildSignoffPack(planUid, now = /* @__PURE__ */ new Date()) {
|
|
104
|
+
const plan = (0, import_plan_service.getPlan)(planUid);
|
|
105
|
+
if (!plan) throw new Error(`Plan ${planUid} not found`);
|
|
106
|
+
const rows = (0, import_signoff_rows.signoffRows)(planUid);
|
|
107
|
+
const files = /* @__PURE__ */ new Map();
|
|
108
|
+
const claim = (path, sha256, takenAt) => {
|
|
109
|
+
if (!path || !sha256) return;
|
|
110
|
+
const key = `${path}\0${sha256}`;
|
|
111
|
+
if (!files.has(key)) files.set(key, { path, sha256, takenAt });
|
|
112
|
+
};
|
|
113
|
+
for (const r of rows) {
|
|
114
|
+
const approved = r.decision?.decision === "approved";
|
|
115
|
+
for (const e of r.evidence) {
|
|
116
|
+
const atApproval = approved && e.attachmentUid ? r.decision.evidenceHashes[e.attachmentUid] : void 0;
|
|
117
|
+
if (atApproval) claim(e.path, atApproval, "approval");
|
|
118
|
+
else claim(e.path, e.sha256AtSubmit, "submission");
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
format: PACK_FORMAT,
|
|
123
|
+
version: PACK_VERSION,
|
|
124
|
+
plan: { uid: plan.uid, title: plan.title },
|
|
125
|
+
generatedAt: now.toISOString(),
|
|
126
|
+
rows,
|
|
127
|
+
files: [...files.values()].sort((a, b) => a.path.localeCompare(b.path)),
|
|
128
|
+
skills: packSkills(planUid),
|
|
129
|
+
signals: packSignals(planUid, plan.projectPath ?? null)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
class PackError extends Error {
|
|
133
|
+
}
|
|
134
|
+
const SHA256 = /^[a-f0-9]{64}$/;
|
|
135
|
+
const MAX_PACK_FILES = 5e3;
|
|
136
|
+
function packFilesOf(raw) {
|
|
137
|
+
if (!raw || typeof raw !== "object") throw new PackError("That is not a sign-off pack");
|
|
138
|
+
const p = raw;
|
|
139
|
+
if (p.format !== PACK_FORMAT) throw new PackError("That is not a CodeTrellis sign-off pack");
|
|
140
|
+
if (p.version !== PACK_VERSION) throw new PackError(`This pack is version ${String(p.version)}; this app reads version ${PACK_VERSION}`);
|
|
141
|
+
if (!Array.isArray(p.files)) throw new PackError("The pack names no files");
|
|
142
|
+
if (p.files.length > MAX_PACK_FILES) throw new PackError(`The pack names more than ${MAX_PACK_FILES} files`);
|
|
143
|
+
return p.files.flatMap((f) => {
|
|
144
|
+
if (!f || typeof f !== "object") return [];
|
|
145
|
+
const { path, sha256, takenAt } = f;
|
|
146
|
+
if (typeof path !== "string" || !path || path.length > 1024 || typeof sha256 !== "string" || !SHA256.test(sha256)) return [];
|
|
147
|
+
return [{ path, sha256, takenAt: takenAt === "approval" ? "approval" : "submission" }];
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
async function verifyPack(planUid, raw, now = /* @__PURE__ */ new Date()) {
|
|
151
|
+
const files = packFilesOf(raw);
|
|
152
|
+
const packPlan = raw.plan?.uid;
|
|
153
|
+
if (packPlan !== planUid) throw new PackError("This pack is for a different plan \u2014 open that plan to verify it");
|
|
154
|
+
const plan = (0, import_plan_service.getPlan)(planUid);
|
|
155
|
+
if (!plan?.projectPath) throw new PackError("This plan is not associated with a project");
|
|
156
|
+
const root = (0, import_trusted_roots.resolveTrustedProjectRoot)(plan.projectPath, "plan.projectPath");
|
|
157
|
+
const results = [];
|
|
158
|
+
for (const f of files) {
|
|
159
|
+
let current = null;
|
|
160
|
+
try {
|
|
161
|
+
const at = (0, import_material_place.locateStored)(f.path, root);
|
|
162
|
+
if (!at) throw new Error("not placed on this device");
|
|
163
|
+
current = (await (0, import_sha256_file.sha256FileWithin)(at.root, at.rel)).sha256;
|
|
164
|
+
} catch {
|
|
165
|
+
current = null;
|
|
166
|
+
}
|
|
167
|
+
results.push({
|
|
168
|
+
path: f.path,
|
|
169
|
+
sha256: f.sha256,
|
|
170
|
+
now: current,
|
|
171
|
+
verdict: current === null ? "missing" : current === f.sha256 ? "matches" : "changed"
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
planUid,
|
|
176
|
+
checkedAt: now.toISOString(),
|
|
177
|
+
files: results,
|
|
178
|
+
matches: results.filter((r) => r.verdict === "matches").length,
|
|
179
|
+
changed: results.filter((r) => r.verdict === "changed").length,
|
|
180
|
+
missing: results.filter((r) => r.verdict === "missing").length
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function esc(s) {
|
|
184
|
+
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
185
|
+
}
|
|
186
|
+
function scriptJson(value) {
|
|
187
|
+
return JSON.stringify(value).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
188
|
+
}
|
|
189
|
+
const GLYPH = {
|
|
190
|
+
met: "\u2713",
|
|
191
|
+
submitted: "\u25D0",
|
|
192
|
+
sent_back: "\u21A9",
|
|
193
|
+
stale: "\u26A0",
|
|
194
|
+
open: "\u25CB"
|
|
195
|
+
};
|
|
196
|
+
function rowHtml(r) {
|
|
197
|
+
const evidence = r.evidence.length === 0 ? '<p class="muted">No evidence was offered.</p>' : `<ul class="evidence">${r.evidence.map((e) => `<li><span class="path">${esc(e.path ?? "note")}</span>${e.where ? ` \u2014 ${esc(e.where)}` : ""}${e.sha256AtSubmit ? `<br><code>sha256 ${esc(e.sha256AtSubmit)}</code>` : ""}</li>`).join("")}</ul>`;
|
|
198
|
+
const notes = [
|
|
199
|
+
r.submissionNote ? `<p><strong>${esc(r.submittedBy ?? "The agent")} said:</strong> ${esc(r.submissionNote)}</p>` : "",
|
|
200
|
+
r.decision?.note ? `<p><strong>Decision note:</strong> ${esc(r.decision.note)}</p>` : "",
|
|
201
|
+
r.changedFiles.length ? `<p class="warn">Changed since it was approved: ${esc(r.changedFiles.join(", "))}</p>` : ""
|
|
202
|
+
].join("");
|
|
203
|
+
return `<section class="criterion">
|
|
204
|
+
<h3><span class="glyph">${GLYPH[r.state]}</span> ${esc(r.text)}</h3>
|
|
205
|
+
<p class="state">${esc((0, import_signoff.stateWords)(r))} \xB7 ${esc(r.kind)} \xB7 ${esc(r.policy === "agent" ? "the agent may approve" : r.policy === "propose" ? "the agent proposes, a person decides" : "a person decides")}</p>
|
|
206
|
+
<p class="decision">${esc((0, import_signoff.decisionWords)(r))}</p>
|
|
207
|
+
${evidence}${notes}
|
|
208
|
+
</section>`;
|
|
209
|
+
}
|
|
210
|
+
function groupByItem(rows) {
|
|
211
|
+
const groups = /* @__PURE__ */ new Map();
|
|
212
|
+
for (const r of rows) {
|
|
213
|
+
const g = groups.get(r.itemUid) ?? { title: r.itemTitle, ref: r.itemRef, rows: [] };
|
|
214
|
+
g.rows.push(r);
|
|
215
|
+
groups.set(r.itemUid, g);
|
|
216
|
+
}
|
|
217
|
+
return [...groups.values()];
|
|
218
|
+
}
|
|
219
|
+
function summaryApart(self, unverified) {
|
|
220
|
+
const parts = [
|
|
221
|
+
self ? `${self} of them by the agent's own checks` : "",
|
|
222
|
+
unverified ? `${unverified}${self ? "" : " of them"} unverified (local API)` : ""
|
|
223
|
+
].filter(Boolean);
|
|
224
|
+
return parts.length ? ` \u2014 ${parts.join(" and ")}, listed separately` : "";
|
|
225
|
+
}
|
|
226
|
+
function renderPackHtml(pack) {
|
|
227
|
+
const unverifiedApproval = (r) => r.unverified && r.decision?.decision === "approved";
|
|
228
|
+
const byPerson = pack.rows.filter((r) => !r.selfApproved && !unverifiedApproval(r));
|
|
229
|
+
const selfApproved = pack.rows.filter((r) => r.selfApproved);
|
|
230
|
+
const unverified = pack.rows.filter(unverifiedApproval);
|
|
231
|
+
const met = pack.rows.filter((r) => r.state === "met").length;
|
|
232
|
+
const title = `Sign-off pack \u2014 ${pack.plan.title}`;
|
|
233
|
+
const sections = groupByItem(byPerson).map((g) => `<h2>${esc(g.title)} <span class="ref">${esc(g.ref)}</span></h2>
|
|
234
|
+
${g.rows.map(rowHtml).join("\n")}`).join("\n");
|
|
235
|
+
const self = selfApproved.length === 0 ? "" : `
|
|
236
|
+
<h2 class="self">Approved by the agent's own checks</h2>
|
|
237
|
+
<p class="muted">These criteria were left to the agent (agent policy), and it approved them itself when it submitted. No person signed them.</p>
|
|
238
|
+
${groupByItem(selfApproved).map((g) => `<h3 class="item">${esc(g.title)} <span class="ref">${esc(g.ref)}</span></h3>
|
|
239
|
+
${g.rows.map(rowHtml).join("\n")}`).join("\n")}`;
|
|
240
|
+
const unverifiedSection = unverified.length === 0 ? "" : `
|
|
241
|
+
<h2 class="self">Approved through the local API (unverified)</h2>
|
|
242
|
+
<p class="muted">These approvals came over the local HTTP API with this machine's token, not from the CodeTrellis app or a paired phone. A person in a browser or a script holding the token could have made them; nothing records which.</p>
|
|
243
|
+
${groupByItem(unverified).map((g) => `<h3 class="item">${esc(g.title)} <span class="ref">${esc(g.ref)}</span></h3>
|
|
244
|
+
${g.rows.map(rowHtml).join("\n")}`).join("\n")}`;
|
|
245
|
+
const fileRows = pack.files.map((f) => `<tr><td class="path">${esc(f.path)}</td><td><code>${esc(f.sha256)}</code></td><td>${f.takenAt === "approval" ? "at approval" : "when offered"}</td></tr>`).join("");
|
|
246
|
+
return `<!doctype html>
|
|
247
|
+
<html lang="en">
|
|
248
|
+
<head>
|
|
249
|
+
<meta charset="utf-8">
|
|
250
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'">
|
|
251
|
+
<title>${esc(title)}</title>
|
|
252
|
+
<style>
|
|
253
|
+
body { font: 13px/1.5 -apple-system, "Segoe UI", Helvetica, Arial, sans-serif; color: #111; max-width: 820px; margin: 32px auto; padding: 0 24px; }
|
|
254
|
+
h1 { font-size: 22px; margin: 0 0 4px; }
|
|
255
|
+
h2 { font-size: 16px; margin: 28px 0 8px; border-bottom: 1px solid #ddd; padding-bottom: 4px; }
|
|
256
|
+
h2.self { color: #7a4d00; }
|
|
257
|
+
h3 { font-size: 14px; margin: 0 0 2px; }
|
|
258
|
+
h3.item { margin-top: 16px; }
|
|
259
|
+
.ref, .muted, .state { color: #666; font-weight: normal; font-size: 12px; }
|
|
260
|
+
.criterion { border: 1px solid #e3e3e3; border-radius: 6px; padding: 10px 12px; margin: 8px 0; break-inside: avoid; }
|
|
261
|
+
.decision { margin: 2px 0 6px; }
|
|
262
|
+
.glyph { display: inline-block; width: 1.2em; }
|
|
263
|
+
.evidence { margin: 4px 0; padding-left: 18px; }
|
|
264
|
+
.path { font-family: ui-monospace, Menlo, Consolas, monospace; }
|
|
265
|
+
code { font: 11px ui-monospace, Menlo, Consolas, monospace; color: #444; word-break: break-all; }
|
|
266
|
+
.warn { color: #8a5a00; }
|
|
267
|
+
table { border-collapse: collapse; width: 100%; font-size: 12px; }
|
|
268
|
+
td, th { border-bottom: 1px solid #eee; padding: 4px 6px; text-align: left; vertical-align: top; }
|
|
269
|
+
.summary { margin: 8px 0 0; color: #333; }
|
|
270
|
+
</style>
|
|
271
|
+
</head>
|
|
272
|
+
<body>
|
|
273
|
+
<h1>${esc(title)}</h1>
|
|
274
|
+
<p class="muted">Generated ${esc(pack.generatedAt.replace("T", " ").slice(0, 16))} UTC by CodeTrellis \xB7 plan ${esc(pack.plan.uid)}</p>
|
|
275
|
+
${pack.seal ? `<p class="muted" id="seal">Signed by the computer with key <code>${esc(pack.seal.key)}</code>, with its record at entry #${esc(String(pack.sealedRecord?.seq ?? 0))} (<code>${esc((pack.sealedRecord?.hash ?? "").slice(0, 16))}\u2026</code>). Verify it in CodeTrellis: a changed byte, or a changed record, shows.</p>` : ""}
|
|
276
|
+
<p class="summary">${met} of ${pack.rows.length} criteria met${summaryApart(selfApproved.length, unverified.length)}.</p>
|
|
277
|
+
${sections || (pack.rows.length ? "" : '<p class="muted">This plan has no acceptance criteria.</p>')}
|
|
278
|
+
${unverifiedSection}
|
|
279
|
+
${self}
|
|
280
|
+
${skillsSection(pack.skills ?? [])}
|
|
281
|
+
${signalsSection(pack.signals ?? [])}
|
|
282
|
+
<h2>Files and hashes</h2>
|
|
283
|
+
<p class="muted">Every file this pack vouches for, with the sha256 it had when it was judged. "Verify a pack" in CodeTrellis re-hashes each one and says which still match.</p>
|
|
284
|
+
<table><thead><tr><th>File</th><th>sha256</th><th>Taken</th></tr></thead><tbody>${fileRows || '<tr><td colspan="3" class="muted">No files.</td></tr>'}</tbody></table>
|
|
285
|
+
<script type="application/json" id="codetrellis-signoff-pack">${scriptJson(pack)}</script>
|
|
286
|
+
</body>
|
|
287
|
+
</html>
|
|
288
|
+
`;
|
|
289
|
+
}
|
|
290
|
+
function signalsSection(signals) {
|
|
291
|
+
if (signals.length === 0) return "";
|
|
292
|
+
const rows = signals.map((s) => `<tr><td>${esc(s.itemTitle)}</td><td><b>${esc(s.heading)}</b> <span class="muted">${esc(s.severity)}</span><br>${esc(s.says)}${s.notes.map((n) => `<br><span class="muted">${esc(n)}</span>`).join("")}</td><td>${esc(s.outcomeWords)}</td></tr>`).join("");
|
|
293
|
+
return `
|
|
294
|
+
<h2>Other work that touched these tasks</h2>
|
|
295
|
+
<p class="muted">Files these tasks shared with other tasks that changed, versions they disagreed on, or outputs two tasks wrote, and how each ended.</p>
|
|
296
|
+
<table><thead><tr><th>Task</th><th>What happened</th><th>How it ended</th></tr></thead><tbody>${rows}</tbody></table>`;
|
|
297
|
+
}
|
|
298
|
+
function skillsSection(skills) {
|
|
299
|
+
if (skills.length === 0) return "";
|
|
300
|
+
const words = (p, src) => p === "used" ? `\u2713 used${src === "mcp" ? " (read through CodeTrellis)" : src === "session_log" ? " (Claude Code session log)" : ""}` : p === "not_used" ? "\u25CB not used" : p === "unknown" ? "unknown (nothing seen: the agent may have read the file itself)" : "not started";
|
|
301
|
+
const rows = skills.map((s) => `<tr><td>${esc(s.itemTitle)}</td><td>${esc(s.name)}</td><td>${s.use}</td><td>${esc(words(s.proof, s.proofSource))}</td></tr>`).join("");
|
|
302
|
+
return `
|
|
303
|
+
<h2>Skills</h2>
|
|
304
|
+
<p class="muted">The skills each task asked the agent to use, and whether the agent loaded them: from Claude Code's session log, or from the agent reading it through CodeTrellis (any client). Unknown means nothing was seen.</p>
|
|
305
|
+
<table><thead><tr><th>Task</th><th>Skill</th><th>Asked as</th><th>Used</th></tr></thead><tbody>${rows}</tbody></table>`;
|
|
306
|
+
}
|
|
307
|
+
function packFromText(text) {
|
|
308
|
+
const trimmed = text.trim();
|
|
309
|
+
if (trimmed.startsWith("{")) return JSON.parse(trimmed);
|
|
310
|
+
const m = trimmed.match(/<script type="application\/json" id="codetrellis-signoff-pack">([\s\S]*?)<\/script>/);
|
|
311
|
+
if (!m) throw new PackError("No sign-off pack data in that file");
|
|
312
|
+
return JSON.parse(m[1]);
|
|
313
|
+
}
|
|
314
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
315
|
+
0 && (module.exports = {
|
|
316
|
+
PACK_FORMAT,
|
|
317
|
+
PACK_VERSION,
|
|
318
|
+
PackError,
|
|
319
|
+
buildSignoffPack,
|
|
320
|
+
esc,
|
|
321
|
+
packFilesOf,
|
|
322
|
+
packFromText,
|
|
323
|
+
packSignals,
|
|
324
|
+
packSkills,
|
|
325
|
+
renderPackHtml,
|
|
326
|
+
scriptJson,
|
|
327
|
+
verifyPack
|
|
328
|
+
});
|