@unbrained/pm-cli 2026.9.7 → 2026.9.8
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/.agents/skills/pm-developer/SKILL.md +12 -4
- package/.agents/skills/pm-developer/references/MULTI_AGENT_MERGE.md +19 -10
- package/.agents/skills/pm-extensions/SKILL.md +6 -0
- package/.agents/skills/pm-sdk/SKILL.md +11 -2
- package/.agents/skills/pm-user/SKILL.md +5 -0
- package/.agents/skills/pm-user/references/ECOSYSTEM_PLANNING.md +95 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +211 -181
- package/dist/cli/after-command-context-usage.js +1 -48
- package/dist/cli/argv-utils.js +1 -40
- package/dist/cli/bootstrap-args.js +1 -9
- package/dist/cli/bundle-integrity.js +2 -110
- package/dist/cli/commander-usage.js +11 -771
- package/dist/cli/commands/activity.js +1 -9
- package/dist/cli/commands/aggregate.js +1 -5
- package/dist/cli/commands/append.js +1 -5
- package/dist/cli/commands/claim.js +1 -5
- package/dist/cli/commands/close-many.js +1 -5
- package/dist/cli/commands/close.js +1 -9
- package/dist/cli/commands/comments.js +1 -5
- package/dist/cli/commands/config.js +1 -9
- package/dist/cli/commands/context.js +1 -9
- package/dist/cli/commands/contracts.js +1 -5
- package/dist/cli/commands/copy.js +1 -9
- package/dist/cli/commands/create.js +1 -9
- package/dist/cli/commands/delete.js +1 -9
- package/dist/cli/commands/deps.js +1 -9
- package/dist/cli/commands/docs.js +1 -9
- package/dist/cli/commands/duplicates.js +1 -9
- package/dist/cli/commands/eval.js +1 -9
- package/dist/cli/commands/extension/describe.js +1 -5
- package/dist/cli/commands/extension/scaffold.js +1 -5
- package/dist/cli/commands/extension/shared.js +1 -5
- package/dist/cli/commands/extension.js +1 -9
- package/dist/cli/commands/files.js +1 -9
- package/dist/cli/commands/focus.js +1 -9
- package/dist/cli/commands/gc.js +1 -9
- package/dist/cli/commands/get.js +1 -9
- package/dist/cli/commands/graph.js +1 -10
- package/dist/cli/commands/health.js +1 -9
- package/dist/cli/commands/history-compact.js +1 -10
- package/dist/cli/commands/history-redact.js +1 -11
- package/dist/cli/commands/history-repair.js +1 -10
- package/dist/cli/commands/history.js +1 -5
- package/dist/cli/commands/index.js +1 -58
- package/dist/cli/commands/init-agent-guidance.js +1 -9
- package/dist/cli/commands/init.js +1 -9
- package/dist/cli/commands/learnings.js +1 -5
- package/dist/cli/commands/lifecycle-transitions.js +1 -5
- package/dist/cli/commands/list.js +1 -9
- package/dist/cli/commands/merge.js +1 -11
- package/dist/cli/commands/next.js +1 -9
- package/dist/cli/commands/notes.js +1 -5
- package/dist/cli/commands/plan.js +1 -9
- package/dist/cli/commands/profile.js +1 -9
- package/dist/cli/commands/restore.js +1 -9
- package/dist/cli/commands/scheduling-shortcuts.js +1 -9
- package/dist/cli/commands/schema.js +1 -10
- package/dist/cli/commands/search.js +1 -9
- package/dist/cli/commands/stats.js +1 -9
- package/dist/cli/commands/telemetry.js +1 -9
- package/dist/cli/commands/test-all.js +1 -9
- package/dist/cli/commands/test-runs.js +1 -9
- package/dist/cli/commands/test.js +1 -9
- package/dist/cli/commands/update-many.js +1 -5
- package/dist/cli/commands/update.js +1 -9
- package/dist/cli/commands/upgrade.js +1 -5
- package/dist/cli/commands/validate.js +1 -15
- package/dist/cli/context-intent-invocation.js +1 -20
- package/dist/cli/description-stdin.js +1 -17
- package/dist/cli/error-guidance.js +1 -1353
- package/dist/cli/extension-command-help.js +5 -636
- package/dist/cli/extension-command-options.js +1 -427
- package/dist/cli/extension-recovery.js +1 -126
- package/dist/cli/help-content.js +2 -1001
- package/dist/cli/help-json-payload.js +4 -570
- package/dist/cli/main.d.ts +4 -0
- package/dist/cli/main.js +5 -2573
- package/dist/cli/migration-gates.js +1 -289
- package/dist/cli/public.js +1 -20
- package/dist/cli/register-annotations.js +1 -270
- package/dist/cli/register-assurance.js +1 -62
- package/dist/cli/register-files-lookup.js +1 -56
- package/dist/cli/register-history-activity.d.ts +8 -0
- package/dist/cli/register-history-activity.js +2 -0
- package/dist/cli/register-history-author.js +1 -38
- package/dist/cli/register-history-maintenance.d.ts +9 -0
- package/dist/cli/register-history-maintenance.js +2 -0
- package/dist/cli/register-list-query.js +10 -943
- package/dist/cli/register-mutation-options.js +1 -9
- package/dist/cli/register-mutation.js +19 -1893
- package/dist/cli/register-operations.js +1 -920
- package/dist/cli/register-setup.js +1 -716
- package/dist/cli/register-structured-mutation.js +1 -196
- package/dist/cli/registration-helpers.js +1 -5
- package/dist/cli/runtime-compatibility-boundary.js +4 -42
- package/dist/cli/schema-registration-helpers.js +1 -73
- package/dist/cli/search-refresh.js +1 -13
- package/dist/cli/stats-analytics-json.js +1 -68
- package/dist/cli/telemetry-flush.js +1 -10
- package/dist/cli-bundle/bundle-manifest.json +164 -156
- package/dist/cli-bundle/chunks/chunk-3BZZA6A3.js +5 -0
- package/dist/cli-bundle/chunks/chunk-AMRDVX36.js +235 -0
- package/dist/cli-bundle/chunks/chunk-DAM4CBBR.js +2202 -0
- package/dist/cli-bundle/chunks/{chunk-WYRZZ5ZE.js → chunk-DEZPZOQ7.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-FLDWYEND.js +77 -0
- package/dist/cli-bundle/chunks/{chunk-NPN5I67J.js → chunk-G5ADGQ2D.js} +3 -3
- package/dist/cli-bundle/chunks/chunk-JAAEYHUA.js +2 -0
- package/dist/cli-bundle/chunks/{chunk-JX4DH4WC.js → chunk-KR3RP4FH.js} +3 -3
- package/dist/cli-bundle/chunks/chunk-LEZOUXG6.js +2 -0
- package/dist/cli-bundle/chunks/chunk-NSAPNEYH.js +2 -0
- package/dist/cli-bundle/chunks/chunk-QOEKOECV.js +29 -0
- package/dist/cli-bundle/chunks/chunk-RHSFOMHU.js +2 -0
- package/dist/cli-bundle/chunks/chunk-XOD2PCB6.js +20 -0
- package/dist/cli-bundle/chunks/{chunk-JT6XF6GE.js → chunk-YVEQ2AMO.js} +4 -4
- package/dist/cli-bundle/chunks/register-list-query-KSK3R63M.js +11 -0
- package/dist/cli-bundle/chunks/register-mutation-IQ5C7U6O.js +20 -0
- package/dist/cli-bundle/chunks/{register-operations-I5W3KMGB.js → register-operations-DWVHR2NC.js} +2 -2
- package/dist/cli-bundle/chunks/register-setup-BQ5SVD4B.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-3WRPWLA7.js +3 -0
- package/dist/cli-bundle/focused-chunks/chunk-7MBUII7U.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-COPBO5HW.js +8 -0
- package/dist/cli-bundle/focused-chunks/chunk-DUN4IXYN.js +20 -0
- package/dist/cli-bundle/focused-chunks/{chunk-LIJIA4TY.js → chunk-EE5QSM4Z.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-HJVARYM3.js +6 -0
- package/dist/cli-bundle/focused-chunks/chunk-IAUOUGVR.js +177 -0
- package/dist/cli-bundle/focused-chunks/chunk-KOOMJ54F.js +2 -0
- package/dist/cli-bundle/focused-chunks/chunk-MZDXX7JY.js +56 -0
- package/dist/cli-bundle/focused-chunks/{chunk-M6PBRFSH.js → chunk-OPFHFGDD.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-QHNETP77.js +9 -0
- package/dist/cli-bundle/focused-chunks/{chunk-3BZEOXLT.js → chunk-RMWES4M6.js} +2 -2
- package/dist/cli-bundle/focused-chunks/chunk-RXT4P5QH.js +10 -0
- package/dist/cli-bundle/focused-chunks/chunk-SDOL6FM2.js +12 -0
- package/dist/cli-bundle/focused-chunks/chunk-SQV4AAUC.js +2 -0
- package/dist/cli-bundle/focused-chunks/{chunk-PEFABF7E.js → chunk-WE3IUNIW.js} +3 -3
- package/dist/cli-bundle/focused-chunks/chunk-YALOUBWJ.js +2 -0
- package/dist/cli-bundle/main.js +20 -20
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +2 -2
- package/dist/cli-bundle/sdk-core.js +29 -29
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +42 -42
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +11 -11
- package/dist/cli.js +1 -104
- package/dist/core/checkpoint/checkpoint-gc.js +1 -169
- package/dist/core/checkpoint/mutation-checkpoint.js +2 -133
- package/dist/core/config/nested-settings.js +1 -534
- package/dist/core/config/positional-value.js +1 -109
- package/dist/core/diagnostics/remediation.js +1 -506
- package/dist/core/extensions/activation-summary-markdown.js +2 -145
- package/dist/core/extensions/activation-summary.js +1 -92
- package/dist/core/extensions/activation-transaction.js +1 -46
- package/dist/core/extensions/capability-usage.js +1 -173
- package/dist/core/extensions/command-hook-context.js +1 -13
- package/dist/core/extensions/command-metadata-contract.js +1 -22
- package/dist/core/extensions/command-visibility-tier.js +1 -29
- package/dist/core/extensions/contribution-inventory.js +1 -173
- package/dist/core/extensions/exporter-output-contract.js +1 -66
- package/dist/core/extensions/extension-candidate.js +1 -18
- package/dist/core/extensions/extension-capability-aliases.js +1 -158
- package/dist/core/extensions/extension-hook-runtime.js +1 -699
- package/dist/core/extensions/extension-policy.js +1 -599
- package/dist/core/extensions/extension-registries.js +1 -62
- package/dist/core/extensions/extension-runtime-helpers.js +1 -33
- package/dist/core/extensions/extension-types.js +1 -102
- package/dist/core/extensions/flag-definition-validation.js +1 -52
- package/dist/core/extensions/flag-value-types.js +1 -64
- package/dist/core/extensions/index.js +1 -526
- package/dist/core/extensions/item-field-types.js +1 -39
- package/dist/core/extensions/item-fields.js +1 -261
- package/dist/core/extensions/loader.js +1 -2223
- package/dist/core/extensions/manifest-schema.js +1 -69
- package/dist/core/extensions/module-graph-snapshot.js +1 -33
- package/dist/core/extensions/preflight-ownership.js +1 -41
- package/dist/core/extensions/registration-validation.js +1 -82
- package/dist/core/extensions/registration-values.js +1 -209
- package/dist/core/extensions/reserved-host-flags.js +1 -36
- package/dist/core/extensions/runtime-registrations.js +1 -67
- package/dist/core/extensions/service-ownership.js +1 -19
- package/dist/core/extensions/version-compat.js +1 -216
- package/dist/core/fs/fs-utils.js +2 -158
- package/dist/core/fs/host-environment-errors.js +1 -141
- package/dist/core/fs/index.js +1 -9
- package/dist/core/fs/path-utils.js +1 -17
- package/dist/core/governance/content-fields.js +1 -160
- package/dist/core/governance/issue-codes.js +1 -151
- package/dist/core/governance/metadata-coverage.js +1 -358
- package/dist/core/history/drift-cache.js +3 -72
- package/dist/core/history/drift-scan.js +1 -342
- package/dist/core/history/event-classification.js +1 -109
- package/dist/core/history/event-index.js +8 -784
- package/dist/core/history/history-compact-bulk.js +1 -104
- package/dist/core/history/history-diff.js +1 -158
- package/dist/core/history/history-rewrite.d.ts +6 -1
- package/dist/core/history/history-rewrite.js +1 -107
- package/dist/core/history/history-storage-stats.js +1 -102
- package/dist/core/history/history-stream-policy.js +1 -62
- package/dist/core/history/history.js +2 -617
- package/dist/core/history/identity.js +1 -30
- package/dist/core/history/index.js +1 -9
- package/dist/core/history/projection.js +1 -196
- package/dist/core/history/read.d.ts +9 -0
- package/dist/core/history/read.js +1 -57
- package/dist/core/history/replay.js +2 -439
- package/dist/core/history/version-address.js +1 -32
- package/dist/core/history/workspace-history.js +2 -433
- package/dist/core/io/body-file.js +1 -62
- package/dist/core/io/bulk-ids-input.js +1 -112
- package/dist/core/item/actionability.js +1 -327
- package/dist/core/item/dependency-reference.js +1 -20
- package/dist/core/item/id.js +1 -112
- package/dist/core/item/index.js +1 -12
- package/dist/core/item/item-format-version.js +1 -78
- package/dist/core/item/item-format.js +5 -1473
- package/dist/core/item/item-record.js +1 -23
- package/dist/core/item/item-type-definition.js +1 -133
- package/dist/core/item/parent-reference-policy.js +1 -57
- package/dist/core/item/parse.js +2 -603
- package/dist/core/item/plan-lifecycle.js +1 -19
- package/dist/core/item/priority.js +1 -61
- package/dist/core/item/sprint-release-format.js +1 -38
- package/dist/core/item/status-filter.js +1 -115
- package/dist/core/item/status.js +1 -43
- package/dist/core/item/toon-decode.js +1 -29
- package/dist/core/item/type-registry.js +1 -715
- package/dist/core/item/type-synonyms.js +1 -38
- package/dist/core/lock/index.js +1 -9
- package/dist/core/lock/lock-gc.js +1 -240
- package/dist/core/lock/lock.js +3 -590
- package/dist/core/output/mutation-projection.js +1 -217
- package/dist/core/output/output-control.js +1 -25
- package/dist/core/output/output.js +11 -494
- package/dist/core/output/query-summary.js +1 -58
- package/dist/core/output/tabular.js +1 -77
- package/dist/core/packages/manifest.js +1 -244
- package/dist/core/packages/root.js +1 -84
- package/dist/core/profile/profile-describe.js +1 -83
- package/dist/core/profile/profile-lint.js +1 -415
- package/dist/core/profile/profile-plan.js +0 -0
- package/dist/core/profile/profile-presets.js +1 -370
- package/dist/core/reproducibility/context.js +1 -82
- package/dist/core/schema/fields-file.js +2 -288
- package/dist/core/schema/item-types-file.js +2 -296
- package/dist/core/schema/runtime-field-filters.js +1 -49
- package/dist/core/schema/runtime-field-values.js +1 -313
- package/dist/core/schema/runtime-schema.js +2 -826
- package/dist/core/schema/status-defs-file.js +2 -227
- package/dist/core/schema/status-token.js +1 -31
- package/dist/core/schema/type-inference.js +1 -95
- package/dist/core/schema/type-presets.js +1 -87
- package/dist/core/schema/type-workflows.js +1 -104
- package/dist/core/search/background-refresh.js +2 -286
- package/dist/core/search/bm25.js +1 -146
- package/dist/core/search/cache.js +2 -534
- package/dist/core/search/corpus.js +1 -311
- package/dist/core/search/embedding-batches.js +1 -163
- package/dist/core/search/eval.js +1 -225
- package/dist/core/search/http-client.js +1 -75
- package/dist/core/search/providers.js +1 -242
- package/dist/core/search/relevance.js +1 -244
- package/dist/core/search/semantic-defaults.js +1 -199
- package/dist/core/search/staleness.js +1 -30
- package/dist/core/search/vector-stores.js +2 -664
- package/dist/core/search/vectorization-metadata.js +1 -81
- package/dist/core/sentry/helpers.js +1 -272
- package/dist/core/sentry/instrument.js +1 -387
- package/dist/core/session/session-state.js +1 -260
- package/dist/core/shared/agent-session-context.js +1 -133
- package/dist/core/shared/author.js +1 -1172
- package/dist/core/shared/command-types.js +1 -4
- package/dist/core/shared/conflict-markers.js +1 -31
- package/dist/core/shared/constants.js +1 -405
- package/dist/core/shared/errors.js +1 -20
- package/dist/core/shared/html-entity-decode.js +1 -131
- package/dist/core/shared/index.js +1 -12
- package/dist/core/shared/item-metadata-contract.js +1 -89
- package/dist/core/shared/lazy-module.js +1 -16
- package/dist/core/shared/levenshtein.js +1 -61
- package/dist/core/shared/memo.js +1 -19
- package/dist/core/shared/numeric-parsers.js +1 -20
- package/dist/core/shared/option-alias-visibility.js +1 -67
- package/dist/core/shared/primitives.js +1 -127
- package/dist/core/shared/serial-queue.js +1 -79
- package/dist/core/shared/serialization.js +1 -210
- package/dist/core/shared/split-comma-list.js +1 -30
- package/dist/core/shared/text-normalization.js +1 -38
- package/dist/core/shared/time.js +1 -306
- package/dist/core/store/index.js +1 -12
- package/dist/core/store/item-format-migration.js +1 -125
- package/dist/core/store/item-metadata-cache.js +1 -1116
- package/dist/core/store/item-metadata-query-index.js +9 -394
- package/dist/core/store/item-metadata-read-work.js +1 -43
- package/dist/core/store/item-store.js +1 -693
- package/dist/core/store/paths.d.ts +1 -1
- package/dist/core/store/paths.js +1 -214
- package/dist/core/store/settings-read-cache.js +1 -85
- package/dist/core/store/settings-validator.js +1 -421
- package/dist/core/store/settings.js +3 -1309
- package/dist/core/store/tracker-preflight.js +1 -141
- package/dist/core/telemetry/consent.js +5 -108
- package/dist/core/telemetry/observability.js +1 -246
- package/dist/core/telemetry/runtime.js +4 -1988
- package/dist/core/test/background-runs.js +4 -945
- package/dist/core/test/item-test-run-tracking.js +1 -63
- package/dist/core/test/run-selectors.js +1 -120
- package/dist/core/validate/estimate-defaults.js +1 -89
- package/dist/core/validate/fix-planning.js +1 -189
- package/dist/core/validate/linked-artifact-reference.js +1 -36
- package/dist/core/validate/missing-by-type.js +1 -34
- package/dist/core/validate/missing-link-owners.js +1 -95
- package/dist/core/validate/stale-file-classification.js +1 -82
- package/dist/mcp/http-server.js +5 -448
- package/dist/mcp/legacy-adapter.js +1 -77
- package/dist/mcp/runtime-capabilities.js +1 -200
- package/dist/mcp/server.js +4 -1233
- package/dist/mcp/tool-definitions.js +1 -804
- package/dist/sdk/actionability.js +1 -13
- package/dist/sdk/agent/closed-domain-contracts.js +1 -161
- package/dist/sdk/agent/command-recovery.js +1 -59
- package/dist/sdk/agent/command-suggestions.js +1 -66
- package/dist/sdk/agent/create-option-policy.js +1 -31
- package/dist/sdk/agent/identity-config.js +1 -106
- package/dist/sdk/agent/item-addressing.js +1 -207
- package/dist/sdk/agent/refusal-closure-census.js +1 -151
- package/dist/sdk/agent/refusal-closure.js +1 -133
- package/dist/sdk/agent/refusal-corpus-contracts.js +1 -193
- package/dist/sdk/agent/refusal-reachability.js +1 -593
- package/dist/sdk/agent/subcommand-recovery.js +1 -83
- package/dist/sdk/agent/task-transcript-contracts.js +1 -197
- package/dist/sdk/agent/tracker-preflight-contracts.js +1 -126
- package/dist/sdk/agent-capability-contracts.js +1 -428
- package/dist/sdk/agent-session-context.js +1 -9
- package/dist/sdk/annotations.js +1 -448
- package/dist/sdk/author-attribution.js +1 -468
- package/dist/sdk/authoring.js +1 -20
- package/dist/sdk/cli-bootstrap.js +1 -838
- package/dist/sdk/cli-contracts/agent-output-contracts.d.ts +1 -1
- package/dist/sdk/cli-contracts/agent-output-contracts.js +4 -476
- package/dist/sdk/cli-contracts/bootstrap-command-scanner.js +1 -79
- package/dist/sdk/cli-contracts/command-aliases.d.ts +4 -0
- package/dist/sdk/cli-contracts/command-aliases.js +1 -107
- package/dist/sdk/cli-contracts/command-exit-contracts.js +1 -151
- package/dist/sdk/cli-contracts/commander-mutation-options.js +1 -1081
- package/dist/sdk/cli-contracts/commander-types.js +1 -159
- package/dist/sdk/cli-contracts/completeness.js +1 -393
- package/dist/sdk/cli-contracts/enum-contracts.js +1 -222
- package/dist/sdk/cli-contracts/flag-contracts.js +1 -1947
- package/dist/sdk/cli-contracts/flag-lexicon-contracts.js +1 -347
- package/dist/sdk/cli-contracts/grammar-contracts.js +1 -986
- package/dist/sdk/cli-contracts/registration-helpers.js +7 -990
- package/dist/sdk/cli-contracts/runtime-contracts.d.ts +5 -0
- package/dist/sdk/cli-contracts/runtime-contracts.js +1 -2090
- package/dist/sdk/cli-contracts/schemas/assurance-definition.js +1 -324
- package/dist/sdk/cli-contracts/string-lists.js +1 -16
- package/dist/sdk/cli-contracts/tool-option-contracts.js +1 -461
- package/dist/sdk/cli-contracts/tool-parameter-tables.js +1 -2167
- package/dist/sdk/cli-contracts/tool-schema.d.ts +3 -1
- package/dist/sdk/cli-contracts/tool-schema.js +1 -1664
- package/dist/sdk/cli-contracts/typed-action-inputs.js +1 -4
- package/dist/sdk/cli-contracts.d.ts +1 -1
- package/dist/sdk/cli-contracts.js +1 -17
- package/dist/sdk/cli-program.js +1 -49
- package/dist/sdk/command-line.d.ts +2 -5
- package/dist/sdk/command-line.js +1 -78
- package/dist/sdk/comments.js +1 -133
- package/dist/sdk/compile-cache.js +1 -73
- package/dist/sdk/completion.js +157 -837
- package/dist/sdk/compose.js +1 -1090
- package/dist/sdk/config.js +1 -1204
- package/dist/sdk/context/recency.js +1 -33
- package/dist/sdk/context/semantic-session-attribution.js +1 -82
- package/dist/sdk/context/usage-egress.js +1 -29
- package/dist/sdk/context/usage-ledger.js +3 -185
- package/dist/sdk/context-intent-contracts.js +1 -709
- package/dist/sdk/context-intent-runtime.js +1 -115
- package/dist/sdk/context-packing.js +1 -218
- package/dist/sdk/context-relevance.js +1 -503
- package/dist/sdk/context-signal-store.js +2 -426
- package/dist/sdk/context-usage.js +1 -367
- package/dist/sdk/contracts.js +1 -23
- package/dist/sdk/core-governance.js +1 -14
- package/dist/sdk/core.js +1 -14
- package/dist/sdk/corpus-shape.js +1 -401
- package/dist/sdk/define.js +1 -220
- package/dist/sdk/dependencies.js +1 -718
- package/dist/sdk/dependency-flag-validation.js +1 -156
- package/dist/sdk/dependency-provenance.js +1 -135
- package/dist/sdk/docs.js +1 -14
- package/dist/sdk/duplicates.js +1 -40
- package/dist/sdk/environment/agent-environment.js +1 -466
- package/dist/sdk/environment/host-environment-errors.js +1 -8
- package/dist/sdk/environment/project-runtime-compatibility.js +1 -481
- package/dist/sdk/environment/source-context.js +1 -28
- package/dist/sdk/environment/tracker-preflight.js +1 -9
- package/dist/sdk/error-code-catalog.js +1 -153
- package/dist/sdk/error-runtime.js +1 -58
- package/dist/sdk/errors.js +1 -58
- package/dist/sdk/eval.js +1 -190
- package/dist/sdk/extension/author-manifest.js +1 -92
- package/dist/sdk/extension/bundled-catalog.js +1 -332
- package/dist/sdk/extension/concurrency.js +1 -25
- package/dist/sdk/extension/copy-scope.d.ts +2 -0
- package/dist/sdk/extension/copy-scope.js +2 -0
- package/dist/sdk/extension/custom-field-collisions.js +1 -30
- package/dist/sdk/extension/describe.js +3 -189
- package/dist/sdk/extension/doctor.js +1 -606
- package/dist/sdk/extension/install-plan.d.ts +69 -0
- package/dist/sdk/extension/install-plan.js +2 -0
- package/dist/sdk/extension/install-receipts.d.ts +7 -0
- package/dist/sdk/extension/install-receipts.js +2 -0
- package/dist/sdk/extension/install-runtime.js +3 -313
- package/dist/sdk/extension/install-snapshot.js +1 -66
- package/dist/sdk/extension/install-sources.js +5 -1057
- package/dist/sdk/extension/managed-state.js +2 -252
- package/dist/sdk/extension/migrations.js +2 -174
- package/dist/sdk/extension/output-ownership.js +1 -25
- package/dist/sdk/extension/runtime-summary.js +1 -18
- package/dist/sdk/extension/scaffold.js +4 -2308
- package/dist/sdk/extension/shared.js +1 -147
- package/dist/sdk/extension/source-resolution.js +1 -65
- package/dist/sdk/extension/update-check.js +1 -67
- package/dist/sdk/extension-command-context.js +1 -44
- package/dist/sdk/extension-contracts.js +1 -73
- package/dist/sdk/extension.d.ts +7 -2
- package/dist/sdk/extension.js +1 -2196
- package/dist/sdk/files.js +1 -535
- package/dist/sdk/flag-invocation-contracts.d.ts +0 -5
- package/dist/sdk/flag-invocation-contracts.js +1 -344
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +1 -2224
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +1 -2236
- package/dist/sdk/generated-error-code-catalog.js +1 -16
- package/dist/sdk/governance/assurance-action-contracts.js +1 -4
- package/dist/sdk/governance/assurance-action.js +1 -411
- package/dist/sdk/governance/assurance-limits.js +1 -10
- package/dist/sdk/governance/assurance-mutation-error.js +1 -81
- package/dist/sdk/governance/assurance-presets.js +1 -135
- package/dist/sdk/governance/assurance-runtime.js +1 -238
- package/dist/sdk/governance/assurance.js +4 -1182
- package/dist/sdk/governance/boundary-fixtures.js +1 -200
- package/dist/sdk/governance/coverage-assurance.js +1 -60
- package/dist/sdk/governance/defect-recurrence-coverage.js +1 -188
- package/dist/sdk/governance/defect-recurrence-graph.js +1 -120
- package/dist/sdk/governance/defect-recurrence-signals.js +1 -24
- package/dist/sdk/governance/defect-recurrence.js +1 -623
- package/dist/sdk/governance/extension-host-version.js +1 -115
- package/dist/sdk/governance/gc.js +1 -368
- package/dist/sdk/governance/health.js +1 -2367
- package/dist/sdk/governance/hierarchy-validation.js +1 -108
- package/dist/sdk/governance/linked-file-report.js +1 -71
- package/dist/sdk/governance/merge-receipt-history.js +1 -206
- package/dist/sdk/governance/provenance-health.js +1 -282
- package/dist/sdk/governance/reindex.js +4 -761
- package/dist/sdk/governance/stale-work.js +1 -82
- package/dist/sdk/governance/status-role-diagnostics.js +1 -35
- package/dist/sdk/governance/storage-integrity.js +1 -280
- package/dist/sdk/governance/tracked-runtime-cache.js +1 -106
- package/dist/sdk/governance/upgrade.js +2 -327
- package/dist/sdk/governance/validate-history-drift.js +1 -81
- package/dist/sdk/governance/validate-item-reader.js +1 -17
- package/dist/sdk/governance/validate-normalization.js +1 -36
- package/dist/sdk/governance/validate.js +1 -2261
- package/dist/sdk/governance/workspace-position.js +1 -163
- package/dist/sdk/governance.d.ts +1 -0
- package/dist/sdk/governance.js +1 -31
- package/dist/sdk/graph/adapter.js +1 -181
- package/dist/sdk/graph/analytics.js +1 -474
- package/dist/sdk/graph/assembly.js +1 -377
- package/dist/sdk/graph/cache.js +1 -160
- package/dist/sdk/graph/centrality.js +1 -258
- package/dist/sdk/graph/conformance.js +1 -130
- package/dist/sdk/graph/deadline-scheduling.d.ts +90 -0
- package/dist/sdk/graph/deadline-scheduling.js +2 -0
- package/dist/sdk/graph/durable-cache.d.ts +1 -1
- package/dist/sdk/graph/durable-cache.js +1 -361
- package/dist/sdk/graph/governance-contracts.js +1 -34
- package/dist/sdk/graph/governance.d.ts +13 -1
- package/dist/sdk/graph/governance.js +1 -735
- package/dist/sdk/graph/hierarchy-integrity.js +1 -310
- package/dist/sdk/graph/index.d.ts +3 -2
- package/dist/sdk/graph/index.js +1 -26
- package/dist/sdk/graph/lifecycle-coverage.d.ts +32 -0
- package/dist/sdk/graph/lifecycle-coverage.js +2 -0
- package/dist/sdk/graph/mutation-advisory.js +1 -176
- package/dist/sdk/graph/remediation.js +1 -197
- package/dist/sdk/graph/run.d.ts +15 -1
- package/dist/sdk/graph/run.js +1 -768
- package/dist/sdk/graph/scheduling.js +1 -124
- package/dist/sdk/graph/traversal.js +1 -284
- package/dist/sdk/graph/workspace-relationship-kind-registry.js +1 -27
- package/dist/sdk/graph.js +1 -15
- package/dist/sdk/guide-topics.js +1 -707
- package/dist/sdk/guide.js +1 -279
- package/dist/sdk/history/maintenance.d.ts +71 -0
- package/dist/sdk/history/maintenance.js +2 -0
- package/dist/sdk/history/salvage.js +2 -247
- package/dist/sdk/history/subject.d.ts +19 -0
- package/dist/sdk/history/subject.js +2 -0
- package/dist/sdk/history-analytics.js +1 -282
- package/dist/sdk/history-compact.js +1 -527
- package/dist/sdk/history-mcp.js +1 -68
- package/dist/sdk/history-provenance.js +1 -182
- package/dist/sdk/history-read.js +1 -96
- package/dist/sdk/history-redact.d.ts +1 -14
- package/dist/sdk/history-redact.js +1 -643
- package/dist/sdk/history-repair.d.ts +5 -0
- package/dist/sdk/history-repair.js +1 -657
- package/dist/sdk/improvement-ledger-validation.js +1 -62
- package/dist/sdk/improvement-ledger.js +2 -257
- package/dist/sdk/index.d.ts +4 -0
- package/dist/sdk/index.js +1 -183
- package/dist/sdk/init-agent-guidance.js +2 -496
- package/dist/sdk/init.js +2 -820
- package/dist/sdk/invocation-author.js +1 -39
- package/dist/sdk/item-children.js +1 -90
- package/dist/sdk/item-metadata-index.js +1 -15
- package/dist/sdk/item-schedule.js +1 -27
- package/dist/sdk/item-transaction.js +1 -396
- package/dist/sdk/learnings.js +1 -29
- package/dist/sdk/lifecycle/append.js +2 -53
- package/dist/sdk/lifecycle/claim.d.ts +2 -0
- package/dist/sdk/lifecycle/claim.js +1 -242
- package/dist/sdk/lifecycle/close-many.js +1 -438
- package/dist/sdk/lifecycle/close.js +1 -468
- package/dist/sdk/lifecycle/copy.js +1 -212
- package/dist/sdk/lifecycle/create.js +1 -1949
- package/dist/sdk/lifecycle/delete.js +1 -45
- package/dist/sdk/lifecycle/event-validation-messages.js +1 -91
- package/dist/sdk/lifecycle/focus.js +1 -91
- package/dist/sdk/lifecycle/hierarchy-mutation-lock.js +1 -24
- package/dist/sdk/lifecycle/legacy-none-tokens.js +1 -149
- package/dist/sdk/lifecycle/lifecycle-transitions.js +1 -40
- package/dist/sdk/lifecycle/mcp-actions.d.ts +2 -0
- package/dist/sdk/lifecycle/mcp-actions.js +1 -51
- package/dist/sdk/lifecycle/metadata-normalizers.js +1 -46
- package/dist/sdk/lifecycle/mutation-command-options.js +1 -9
- package/dist/sdk/lifecycle/plan.js +1 -1606
- package/dist/sdk/lifecycle/recurrence-parsers.js +1 -147
- package/dist/sdk/lifecycle/reopen.js +1 -68
- package/dist/sdk/lifecycle/repeatable-metadata-parsers.js +1 -143
- package/dist/sdk/lifecycle/restore.d.ts +1 -0
- package/dist/sdk/lifecycle/restore.js +1 -330
- package/dist/sdk/lifecycle/shared-unset-fields.js +1 -295
- package/dist/sdk/lifecycle/update-many.js +1 -861
- package/dist/sdk/lifecycle/update.js +1 -2136
- package/dist/sdk/lifecycle-completion.js +1 -92
- package/dist/sdk/lifecycle-policy.js +1 -120
- package/dist/sdk/linked-artifacts.js +1 -530
- package/dist/sdk/mcp/apps.js +4 -133
- package/dist/sdk/mcp/authorization.js +1 -404
- package/dist/sdk/mcp/discovery.js +1 -494
- package/dist/sdk/mcp/interactions.js +1 -336
- package/dist/sdk/mcp/protocol.d.ts +7 -0
- package/dist/sdk/mcp/protocol.js +1 -184
- package/dist/sdk/mcp/skills.js +1 -389
- package/dist/sdk/mcp/subscriptions.js +1 -211
- package/dist/sdk/mcp/tasks.js +2 -430
- package/dist/sdk/mcp/transport.js +1 -260
- package/dist/sdk/merge/abandoned-receipts.js +1 -46
- package/dist/sdk/merge/driver.js +1 -212
- package/dist/sdk/merge/index.js +1 -16
- package/dist/sdk/merge/install.js +3 -561
- package/dist/sdk/merge/receipt-file-boundary.js +1 -46
- package/dist/sdk/merge/receipt-operation.js +1 -96
- package/dist/sdk/merge/receipt-schema.js +1 -397
- package/dist/sdk/merge/receipts.js +4 -525
- package/dist/sdk/merge/reconcile.js +1 -352
- package/dist/sdk/merge/three-way.js +3 -775
- package/dist/sdk/merge.js +1 -10
- package/dist/sdk/mutation-events.js +1 -292
- package/dist/sdk/mutation-guard.js +1 -216
- package/dist/sdk/notes.js +1 -139
- package/dist/sdk/output-contracts.js +1 -172
- package/dist/sdk/output-projection.js +1 -430
- package/dist/sdk/output-token-accounting.js +1 -140
- package/dist/sdk/output.js +1 -64
- package/dist/sdk/ownership-source.js +1 -21
- package/dist/sdk/package-import-adapters.js +1 -442
- package/dist/sdk/package-migrations.js +1 -20
- package/dist/sdk/package-runtime-options.js +1 -80
- package/dist/sdk/pagination.js +1 -162
- package/dist/sdk/profile.js +1 -454
- package/dist/sdk/provenance.js +1 -258
- package/dist/sdk/public-surface.json +1 -1
- package/dist/sdk/query/activity.js +1 -401
- package/dist/sdk/query/aggregate.d.ts +16 -0
- package/dist/sdk/query/aggregate.js +1 -390
- package/dist/sdk/query/calendar.js +1 -964
- package/dist/sdk/query/complete-list.js +1 -215
- package/dist/sdk/query/context.js +1 -1702
- package/dist/sdk/query/duplicate-candidates.js +1 -70
- package/dist/sdk/query/get.js +1 -450
- package/dist/sdk/query/history.js +1 -186
- package/dist/sdk/query/item-filter-options.js +1 -9
- package/dist/sdk/query/light-metadata.js +1 -18
- package/dist/sdk/query/list-filter-shared.js +1 -81
- package/dist/sdk/query/list.js +1 -1304
- package/dist/sdk/query/multi-value-filters.js +1 -78
- package/dist/sdk/query/next.js +1 -513
- package/dist/sdk/query/parsers.js +1 -44
- package/dist/sdk/query/projection-contracts.js +1 -184
- package/dist/sdk/query/search-contracts.js +1 -230
- package/dist/sdk/query/search-pagination.js +1 -35
- package/dist/sdk/query/search-rendering.js +1 -105
- package/dist/sdk/query/search.js +2 -1987
- package/dist/sdk/query.js +1 -35
- package/dist/sdk/read-output/continuation.js +1 -194
- package/dist/sdk/read-output-budget.js +1 -135
- package/dist/sdk/read-output-contracts.js +1 -1225
- package/dist/sdk/read-output-rows.js +1 -186
- package/dist/sdk/read-output-session.js +1 -208
- package/dist/sdk/relationship-analytics.js +1 -410
- package/dist/sdk/relationship-context.js +1 -284
- package/dist/sdk/relationship-history.js +4 -519
- package/dist/sdk/relationship-kinds/contract.js +1 -22
- package/dist/sdk/relationship-migration.js +1 -77
- package/dist/sdk/relationships.js +1 -579
- package/dist/sdk/reproducibility/process.js +1 -87
- package/dist/sdk/runtime-action-aliases.js +1 -59
- package/dist/sdk/runtime-extended-actions.js +1 -177
- package/dist/sdk/runtime-input.js +1 -614
- package/dist/sdk/runtime-primitives.js +1 -84
- package/dist/sdk/runtime-public-contracts.js +1 -4
- package/dist/sdk/runtime-stats-options.js +1 -38
- package/dist/sdk/runtime.js +1 -1928
- package/dist/sdk/scheduling-shortcuts.js +1 -63
- package/dist/sdk/schema-migration.js +3 -856
- package/dist/sdk/schema.js +1 -1376
- package/dist/sdk/search-refresh-worker.js +1 -18
- package/dist/sdk/similarity-scoring.js +1 -53
- package/dist/sdk/similarity.js +1 -326
- package/dist/sdk/start-task-status.js +1 -14
- package/dist/sdk/stats.js +1 -270
- package/dist/sdk/structured-mutations.js +1 -580
- package/dist/sdk/telemetry-flush.js +1 -13
- package/dist/sdk/telemetry.js +1 -405
- package/dist/sdk/templates.js +2 -292
- package/dist/sdk/test/batch.js +2 -509
- package/dist/sdk/test/entry.js +1 -70
- package/dist/sdk/test/execution.js +6 -2211
- package/dist/sdk/test/linked-command-detection.js +1 -546
- package/dist/sdk/test/measurements.js +1 -91
- package/dist/sdk/test/parsers.js +1 -545
- package/dist/sdk/test/runs.js +1 -193
- package/dist/sdk/test/trust.js +2 -206
- package/dist/sdk/testing.js +4 -1391
- package/dist/sdk/traceability/runtime-files-lookup.js +1 -32
- package/dist/sdk/traceability/source-traceability.js +1 -292
- package/dist/sdk/tracker-root-discovery.js +1 -9
- package/dist/sdk/workspace-contracts-cache.js +1 -28
- package/dist/sdk/workspace-contracts.js +1 -33
- package/dist/sdk/workspace-memory.js +2 -318
- package/dist/sdk/workspace-recipe.js +1 -71
- package/dist/sdk/workspace-snapshot.js +4 -613
- package/dist/sdk/workspace-transaction-gc.js +1 -149
- package/dist/sdk/workspace-transaction.js +2 -363
- package/dist/sdk/workspace.js +3 -127
- package/dist/types/index.js +1 -9
- package/dist/types.js +1 -245
- package/docs/AGENT_GUIDE.md +1 -1
- package/docs/CONTEXT_ALGORITHM_PORTFOLIO.md +167 -0
- package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +9 -3
- package/docs/EXTENSIONS.md +1 -0
- package/docs/HISTORY_MAINTENANCE.md +125 -0
- package/docs/MCP_2026_07_28_CONFORMANCE.md +20 -10
- package/docs/PACKAGE_EVIDENCE.md +92 -0
- package/docs/PLANNING_MEASUREMENTS.md +183 -0
- package/docs/README.md +3 -0
- package/docs/SCRIPTING.md +21 -0
- package/docs/TESTING.md +23 -0
- package/docs/generated/FLAG_LEXICON_BUDGETS.md +5 -5
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +2 -2
- package/marketplace.json +2 -2
- package/package.json +1 -1
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/scripts/finalize-build.mjs +45 -3
- package/dist/cli-bundle/chunks/chunk-AZDFJGH6.js +0 -9
- package/dist/cli-bundle/chunks/chunk-BAX2BYUW.js +0 -2111
- package/dist/cli-bundle/chunks/chunk-CKOL5U7J.js +0 -77
- package/dist/cli-bundle/chunks/chunk-OF76PSDQ.js +0 -5
- package/dist/cli-bundle/chunks/chunk-QUWFABDM.js +0 -232
- package/dist/cli-bundle/chunks/chunk-QWRRFYK3.js +0 -2
- package/dist/cli-bundle/chunks/chunk-RWWBOWJ5.js +0 -2
- package/dist/cli-bundle/chunks/chunk-WGAOYSYH.js +0 -40
- package/dist/cli-bundle/chunks/chunk-WI2AYPA7.js +0 -2
- package/dist/cli-bundle/chunks/register-list-query-5JL52D33.js +0 -11
- package/dist/cli-bundle/chunks/register-mutation-ZPG7KMVP.js +0 -20
- package/dist/cli-bundle/chunks/register-setup-H4SVAVX7.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-AL5ON6K6.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-CHC2IDXA.js +0 -6
- package/dist/cli-bundle/focused-chunks/chunk-CK6QFAPZ.js +0 -20
- package/dist/cli-bundle/focused-chunks/chunk-EZR3GHFE.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-H2WJWK7L.js +0 -56
- package/dist/cli-bundle/focused-chunks/chunk-NTPY5GSO.js +0 -3
- package/dist/cli-bundle/focused-chunks/chunk-P5O7MP5Y.js +0 -2
- package/dist/cli-bundle/focused-chunks/chunk-PDMW2E36.js +0 -177
- package/dist/cli-bundle/focused-chunks/chunk-QTVCLMSZ.js +0 -6
- package/dist/cli-bundle/focused-chunks/chunk-QTZRBUYA.js +0 -8
- package/dist/cli-bundle/focused-chunks/chunk-SGQST3PQ.js +0 -12
- package/dist/cli-bundle/focused-chunks/chunk-UKHASMMP.js +0 -10
- package/dist/cli-bundle/focused-chunks/chunk-ZPEKQHOC.js +0 -2
|
@@ -1,1703 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module sdk/query/context
|
|
3
|
-
*
|
|
4
|
-
* Implements the pm context command surface and its agent-facing runtime behavior.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="4f8e82a2-f4ea-5ecb-8ef5-3ca795cdeda5")}catch(e){}}();
|
|
8
|
-
import { SETTINGS_DEFAULTS, EXIT_CODE, PmCliError, compareTimestampStrings, nowIso, isTerminalStatus, normalizeStatusForRegistry, normalizeStatusInput, resolveRuntimeStatusRegistry, resolvePmRoot, readSettings, resolveAuthor, discoverExtensions, resolveItemTypeRegistry, } from "../runtime-primitives.js";
|
|
9
|
-
import { getActiveExtensionRegistrations } from "../../core/extensions/index.js";
|
|
10
|
-
import { collectDependencyBlockedIds, resolveItemBlockers, resolveItemExecutionRole, computeActionabilityReport, } from "../actionability.js";
|
|
11
|
-
import { CONTEXT_DEPTH_VALUES, CONTEXT_SECTION_VALUES, } from "../../types/index.js";
|
|
12
|
-
import { parseIntegerLimit } from "./parsers.js";
|
|
13
|
-
import { runCalendar, } from "./calendar.js";
|
|
14
|
-
import { runList } from "./list.js";
|
|
15
|
-
import { runActivity } from "./activity.js";
|
|
16
|
-
import { scoreContextCandidatesWithActiveExtensions, } from "../context-relevance.js";
|
|
17
|
-
export { buildItemContextRelevanceCandidates } from "../context-relevance.js";
|
|
18
|
-
import { createQueryFingerprint, encodeQueryCursor, resolveQueryCursorStart, selectCursorSemanticOptions, } from "../pagination.js";
|
|
19
|
-
import { CONTEXT_FLAG_CONTRACTS } from "../cli-contracts/flag-contracts.js";
|
|
20
|
-
import { packContextCandidates, } from "../context-packing.js";
|
|
21
|
-
import { attachContextUsageServingReceipt, collectContextUsageDeliveredItemIds, readContextUsageAffinity, recordContextUsageServing, } from "../context-usage.js";
|
|
22
|
-
import { readWorkspaceContextSignals, } from "../context-signal-store.js";
|
|
23
|
-
import { readWorkspaceMemory, selectWorkspaceMemory, } from "../workspace-memory.js";
|
|
24
|
-
import { detectAgentIdentity } from "../../core/shared/author.js";
|
|
25
|
-
import { readSessionState, semanticAttributionKey, } from "../../core/session/session-state.js";
|
|
26
|
-
import { semanticAttributionAffinity } from "../context/semantic-session-attribution.js";
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// Output format
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
/** Supported values accepted by the context output contract. */
|
|
31
|
-
export const CONTEXT_OUTPUT_VALUES = [
|
|
32
|
-
"markdown",
|
|
33
|
-
"toon",
|
|
34
|
-
"json",
|
|
35
|
-
"ndjson",
|
|
36
|
-
];
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
// Constants
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
const HIGH_LEVEL_TYPES = new Set(["Epic", "Feature"]);
|
|
41
|
-
const DEFAULT_CONTEXT_LIMIT = 10;
|
|
42
|
-
const CONTEXT_SCALE_THRESHOLD = 10_000;
|
|
43
|
-
const STANDARD_SECTIONS = [
|
|
44
|
-
"hierarchy",
|
|
45
|
-
"activity",
|
|
46
|
-
"progress",
|
|
47
|
-
"recently_created",
|
|
48
|
-
"unparented",
|
|
49
|
-
"workload",
|
|
50
|
-
];
|
|
51
|
-
const DEEP_SECTIONS = [
|
|
52
|
-
...STANDARD_SECTIONS,
|
|
53
|
-
"blockers",
|
|
54
|
-
"files",
|
|
55
|
-
"staleness",
|
|
56
|
-
"tests",
|
|
57
|
-
];
|
|
58
|
-
const LEADING_HYPHEN_DATE = /^(\d{4})-(\d{2})-(\d{2})/;
|
|
59
|
-
const LEADING_COMPACT_DATE = /^(\d{4})(\d{2})(\d{2})(?:[T ]?\d{2}|$)/;
|
|
60
|
-
const COMPACT_DATE = /^(\d{4})(\d{2})(\d{2})$/;
|
|
61
|
-
const COMPACT_DATETIME = /^(\d{4})(\d{2})(\d{2})(?:[T\s]?)(\d{2})(\d{2})(\d{2})?(Z|[+-]\d{2}:?\d{2})?$/i;
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
|
-
// Parsers
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
function parseOutputFormat(raw) {
|
|
66
|
-
if (!raw)
|
|
67
|
-
return undefined;
|
|
68
|
-
const normalized = raw.trim().toLowerCase();
|
|
69
|
-
if (!CONTEXT_OUTPUT_VALUES.includes(normalized)) {
|
|
70
|
-
throw new PmCliError("Context format must be one of markdown|toon|json|ndjson", EXIT_CODE.USAGE);
|
|
71
|
-
}
|
|
72
|
-
return normalized;
|
|
73
|
-
}
|
|
74
|
-
// Fields selectable by `pm context --fields` for focus-row projection. Every
|
|
75
|
-
// ContextFocusItem scalar plus `created_at` (carried by recently-created rows)
|
|
76
|
-
// is addressable; unknown names are rejected with a usage hint.
|
|
77
|
-
const FOCUS_PROJECTION_FIELDS = new Set([
|
|
78
|
-
"id",
|
|
79
|
-
"title",
|
|
80
|
-
"type",
|
|
81
|
-
"status",
|
|
82
|
-
"priority",
|
|
83
|
-
"order",
|
|
84
|
-
"deadline",
|
|
85
|
-
"assignee",
|
|
86
|
-
"tags",
|
|
87
|
-
"tags_inherited",
|
|
88
|
-
"updated_at",
|
|
89
|
-
"parent",
|
|
90
|
-
"children_total",
|
|
91
|
-
"children_closed",
|
|
92
|
-
"completion_pct",
|
|
93
|
-
"blocked",
|
|
94
|
-
"blocker_ids",
|
|
95
|
-
"created_at",
|
|
96
|
-
]);
|
|
97
|
-
/** Parses the comma-separated `--fields` value into a validated, de-duplicated focus-row projection list. Returns undefined when no projection was requested. */
|
|
98
|
-
export function parseContextFocusFields(raw) {
|
|
99
|
-
if (raw === undefined)
|
|
100
|
-
return undefined;
|
|
101
|
-
const fields = [];
|
|
102
|
-
for (const token of raw.split(",")) {
|
|
103
|
-
const normalized = token.trim().toLowerCase();
|
|
104
|
-
if (normalized.length === 0)
|
|
105
|
-
continue;
|
|
106
|
-
if (!FOCUS_PROJECTION_FIELDS.has(normalized)) {
|
|
107
|
-
throw new PmCliError(`Context --fields value not projectable: ${normalized} (valid: ${[...FOCUS_PROJECTION_FIELDS].join(", ")})`, EXIT_CODE.USAGE);
|
|
108
|
-
}
|
|
109
|
-
if (!fields.includes(normalized))
|
|
110
|
-
fields.push(normalized);
|
|
111
|
-
}
|
|
112
|
-
if (fields.length === 0) {
|
|
113
|
-
throw new PmCliError("Context --fields requires at least one field name", EXIT_CODE.USAGE);
|
|
114
|
-
}
|
|
115
|
-
return fields;
|
|
116
|
-
}
|
|
117
|
-
/** Projects focus rows onto the requested field subset, preserving field order so the rendered/serialized output mirrors the `--fields` argument. */
|
|
118
|
-
export function projectContextFocusRows(rows, fields) {
|
|
119
|
-
return rows.map((row) => {
|
|
120
|
-
const projected = {};
|
|
121
|
-
const source = row;
|
|
122
|
-
for (const field of fields) {
|
|
123
|
-
projected[field] = source[field] ?? null;
|
|
124
|
-
}
|
|
125
|
-
return projected;
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
/** Implements resolve context output format for the public runtime surface of this module. */
|
|
129
|
-
export function resolveContextOutputFormat(options, global) {
|
|
130
|
-
const commandFormat = parseOutputFormat(options.format);
|
|
131
|
-
if (global.json &&
|
|
132
|
-
commandFormat &&
|
|
133
|
-
commandFormat !== "json" &&
|
|
134
|
-
commandFormat !== "ndjson") {
|
|
135
|
-
throw new PmCliError("Cannot combine --json with --format markdown|toon", EXIT_CODE.USAGE);
|
|
136
|
-
}
|
|
137
|
-
if (global.json) {
|
|
138
|
-
return commandFormat === "ndjson" ? "ndjson" : "json";
|
|
139
|
-
}
|
|
140
|
-
return commandFormat ?? "toon";
|
|
141
|
-
}
|
|
142
|
-
// --depth full surfaces the entire backlog: when no explicit --limit is given,
|
|
143
|
-
// every section returns all rows instead of the default top-N sample.
|
|
144
|
-
function parseContextLimit(raw, depth) {
|
|
145
|
-
return (parseIntegerLimit(raw, "--limit") ??
|
|
146
|
-
(depth === "full" ? Number.MAX_SAFE_INTEGER : DEFAULT_CONTEXT_LIMIT));
|
|
147
|
-
}
|
|
148
|
-
/** Resolve a positive context-packing budget from CLI or SDK input. */
|
|
149
|
-
export function resolveContextTokenBudget(raw, fallback) {
|
|
150
|
-
if (raw === undefined)
|
|
151
|
-
return fallback;
|
|
152
|
-
const parsed = typeof raw === "number" ? raw : Number(raw);
|
|
153
|
-
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
154
|
-
throw new PmCliError("--token-budget must be a positive integer", EXIT_CODE.USAGE);
|
|
155
|
-
}
|
|
156
|
-
return parsed;
|
|
157
|
-
}
|
|
158
|
-
function resolveContextLimitAtScale(limit, itemCount) {
|
|
159
|
-
return itemCount >= CONTEXT_SCALE_THRESHOLD &&
|
|
160
|
-
limit === Number.MAX_SAFE_INTEGER
|
|
161
|
-
? DEFAULT_CONTEXT_LIMIT
|
|
162
|
-
: limit;
|
|
163
|
-
}
|
|
164
|
-
/** Implements parse context depth for the public runtime surface of this module. */
|
|
165
|
-
export function parseContextDepth(raw, settings) {
|
|
166
|
-
if (!raw)
|
|
167
|
-
return settings.default_depth;
|
|
168
|
-
const normalized = raw.trim().toLowerCase();
|
|
169
|
-
if (!CONTEXT_DEPTH_VALUES.includes(normalized)) {
|
|
170
|
-
throw new PmCliError(`Context --depth must be one of ${CONTEXT_DEPTH_VALUES.join("|")}`, EXIT_CODE.USAGE);
|
|
171
|
-
}
|
|
172
|
-
return normalized;
|
|
173
|
-
}
|
|
174
|
-
/** Implements parse context sections for the public runtime surface of this module. */
|
|
175
|
-
export function parseContextSections(raw, depth, settings) {
|
|
176
|
-
if (raw && raw.length > 0) {
|
|
177
|
-
const sections = [];
|
|
178
|
-
for (const value of raw) {
|
|
179
|
-
const normalized = value.trim().toLowerCase();
|
|
180
|
-
if (!CONTEXT_SECTION_VALUES.includes(normalized)) {
|
|
181
|
-
throw new PmCliError(`Context --section must be one of ${CONTEXT_SECTION_VALUES.join("|")}`, EXIT_CODE.USAGE);
|
|
182
|
-
}
|
|
183
|
-
if (!sections.includes(normalized)) {
|
|
184
|
-
sections.push(normalized);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return sections;
|
|
188
|
-
}
|
|
189
|
-
if (depth === "brief")
|
|
190
|
-
return [];
|
|
191
|
-
// --depth full is the comprehensive snapshot: every known section, regardless
|
|
192
|
-
// of the per-section settings toggles that brief/standard/deep respect.
|
|
193
|
-
if (depth === "full")
|
|
194
|
-
return [...CONTEXT_SECTION_VALUES];
|
|
195
|
-
const pool = depth === "deep" ? DEEP_SECTIONS : STANDARD_SECTIONS;
|
|
196
|
-
return pool.filter((section) => settings.sections[section]);
|
|
197
|
-
}
|
|
198
|
-
function parseActivityLimit(raw, settings) {
|
|
199
|
-
if (!raw)
|
|
200
|
-
return settings.activity_limit;
|
|
201
|
-
const parsed = parseIntegerLimit(raw, "--activity-limit");
|
|
202
|
-
/* c8 ignore start -- parseIntegerLimit either throws or returns a number; fallback is defensive typing guard */
|
|
203
|
-
return parsed ?? settings.activity_limit;
|
|
204
|
-
/* c8 ignore stop */
|
|
205
|
-
}
|
|
206
|
-
function parseStaleThresholdDays(raw, settings) {
|
|
207
|
-
if (!raw)
|
|
208
|
-
return settings.stale_threshold_days;
|
|
209
|
-
const trimmed = raw.trim().toLowerCase();
|
|
210
|
-
const match = /^(\d+)d?$/.exec(trimmed);
|
|
211
|
-
if (!match) {
|
|
212
|
-
throw new PmCliError("--stale-threshold must be a number of days (e.g. 7 or 7d)", EXIT_CODE.USAGE);
|
|
213
|
-
}
|
|
214
|
-
const days = parseInt(match[1], 10);
|
|
215
|
-
if (days <= 0) {
|
|
216
|
-
throw new PmCliError("--stale-threshold must be positive", EXIT_CODE.USAGE);
|
|
217
|
-
}
|
|
218
|
-
return days;
|
|
219
|
-
}
|
|
220
|
-
// ---------------------------------------------------------------------------
|
|
221
|
-
// Status helpers
|
|
222
|
-
// ---------------------------------------------------------------------------
|
|
223
|
-
function statusRank(status, statusRegistry) {
|
|
224
|
-
const normalizedStatus = normalizeStatusForRegistry(status, statusRegistry);
|
|
225
|
-
const blockedStatus = normalizeStatusInput("blocked", statusRegistry);
|
|
226
|
-
const draftStatus = normalizeStatusInput("draft", statusRegistry);
|
|
227
|
-
if (normalizedStatus === statusRegistry.in_progress_status)
|
|
228
|
-
return 0;
|
|
229
|
-
if (statusRegistry.active_statuses.has(normalizedStatus))
|
|
230
|
-
return 1;
|
|
231
|
-
if (blockedStatus && normalizedStatus === blockedStatus)
|
|
232
|
-
return 2;
|
|
233
|
-
if (draftStatus && normalizedStatus === draftStatus)
|
|
234
|
-
return 3;
|
|
235
|
-
if (statusRegistry.blocked_statuses.has(normalizedStatus))
|
|
236
|
-
return 5;
|
|
237
|
-
if (statusRegistry.terminal_statuses.has(normalizedStatus))
|
|
238
|
-
return 7;
|
|
239
|
-
return 6;
|
|
240
|
-
}
|
|
241
|
-
function isClosedStatus(status, statusRegistry) {
|
|
242
|
-
const closeStatus = normalizeStatusInput("closed", statusRegistry);
|
|
243
|
-
return normalizeStatusForRegistry(status, statusRegistry) === closeStatus;
|
|
244
|
-
}
|
|
245
|
-
function isInProgressStatus(status, statusRegistry) {
|
|
246
|
-
return (normalizeStatusForRegistry(status, statusRegistry) ===
|
|
247
|
-
statusRegistry.in_progress_status);
|
|
248
|
-
}
|
|
249
|
-
function isOpenStatus(status, statusRegistry) {
|
|
250
|
-
const normalizedStatus = normalizeStatusForRegistry(status, statusRegistry);
|
|
251
|
-
return (normalizedStatus !== statusRegistry.in_progress_status &&
|
|
252
|
-
statusRegistry.active_statuses.has(normalizedStatus));
|
|
253
|
-
}
|
|
254
|
-
function isBlockedStatus(status, statusRegistry) {
|
|
255
|
-
return statusRegistry.blocked_statuses.has(normalizeStatusForRegistry(status, statusRegistry));
|
|
256
|
-
}
|
|
257
|
-
// Projection flags belong to list/calendar/activity output shaping and must not
|
|
258
|
-
// leak into runContext's downstream calls, which need full ItemMetadata rows.
|
|
259
|
-
// --parent is a context-level subtree scope computed here transitively, so it
|
|
260
|
-
// must not reach runList (whose --parent matches direct children only).
|
|
261
|
-
const LIST_PROJECTION_FLAGS = [
|
|
262
|
-
"compact",
|
|
263
|
-
"brief",
|
|
264
|
-
"fields",
|
|
265
|
-
"includeBody",
|
|
266
|
-
"include_body",
|
|
267
|
-
"parent",
|
|
268
|
-
];
|
|
269
|
-
function stripListProjectionFlags(options) {
|
|
270
|
-
const copy = {
|
|
271
|
-
...options,
|
|
272
|
-
};
|
|
273
|
-
for (const key of LIST_PROJECTION_FLAGS) {
|
|
274
|
-
delete copy[key];
|
|
275
|
-
}
|
|
276
|
-
return copy;
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Resolves the transitive subtree (the anchor item plus every descendant via
|
|
280
|
-
* parent links) so `--parent` can scope a snapshot to one epic/milestone. Reuses
|
|
281
|
-
* the same {@link buildChildrenByParent}/{@link collectDescendants} helpers the
|
|
282
|
-
* hierarchy section relies on, so subtree membership stays consistent across the
|
|
283
|
-
* snapshot. Ids are normalized to lowercase for case-insensitive membership; the
|
|
284
|
-
* `found` flag is false when the anchor id is absent from the corpus. Exported so
|
|
285
|
-
* sibling read commands (e.g. `pm next`) can apply the same subtree scoping.
|
|
286
|
-
*/
|
|
287
|
-
export function collectSubtreeIds(corpus, parentId) {
|
|
288
|
-
const target = parentId.trim().toLowerCase();
|
|
289
|
-
const anchor = corpus.find((item) => item.id.trim().toLowerCase() === target);
|
|
290
|
-
if (!anchor) {
|
|
291
|
-
return { ids: new Set(), found: false };
|
|
292
|
-
}
|
|
293
|
-
const childrenByParent = buildChildrenByParent(corpus);
|
|
294
|
-
const descendants = collectDescendants(anchor.id, childrenByParent);
|
|
295
|
-
// Store normalized ids so membership checks are case-insensitive end-to-end.
|
|
296
|
-
const ids = new Set([anchor.id, ...descendants.map((item) => item.id)].map((id) => id.trim().toLowerCase()));
|
|
297
|
-
return { ids, found: true };
|
|
298
|
-
}
|
|
299
|
-
function parseContextParent(raw) {
|
|
300
|
-
if (raw === undefined)
|
|
301
|
-
return undefined;
|
|
302
|
-
const trimmed = raw.trim();
|
|
303
|
-
if (trimmed.length === 0) {
|
|
304
|
-
throw new PmCliError("Context --parent requires an item id", EXIT_CODE.USAGE);
|
|
305
|
-
}
|
|
306
|
-
return trimmed;
|
|
307
|
-
}
|
|
308
|
-
// ---------------------------------------------------------------------------
|
|
309
|
-
// Sorting / mapping helpers (unchanged from original)
|
|
310
|
-
// ---------------------------------------------------------------------------
|
|
311
|
-
function compareOptionalOrder(left, right) {
|
|
312
|
-
const leftValue = left ?? null;
|
|
313
|
-
const rightValue = right ?? null;
|
|
314
|
-
if (leftValue === null && rightValue === null)
|
|
315
|
-
return 0;
|
|
316
|
-
if (leftValue === null)
|
|
317
|
-
return 1;
|
|
318
|
-
if (rightValue === null)
|
|
319
|
-
return -1;
|
|
320
|
-
return leftValue - rightValue;
|
|
321
|
-
}
|
|
322
|
-
function compareOptionalDeadline(left, right) {
|
|
323
|
-
const leftValue = left ?? null;
|
|
324
|
-
const rightValue = right ?? null;
|
|
325
|
-
if (leftValue === null && rightValue === null)
|
|
326
|
-
return 0;
|
|
327
|
-
if (leftValue === null)
|
|
328
|
-
return 1;
|
|
329
|
-
if (rightValue === null)
|
|
330
|
-
return -1;
|
|
331
|
-
return compareTimestampStrings(leftValue, rightValue);
|
|
332
|
-
}
|
|
333
|
-
function daysInUtcMonth(year, monthIndex) {
|
|
334
|
-
return new Date(Date.UTC(year, monthIndex + 1, 0)).getUTCDate();
|
|
335
|
-
}
|
|
336
|
-
function hasNoInvalidDatePrefix(value) {
|
|
337
|
-
const match = LEADING_HYPHEN_DATE.exec(value) ?? LEADING_COMPACT_DATE.exec(value);
|
|
338
|
-
if (!match) {
|
|
339
|
-
return true;
|
|
340
|
-
}
|
|
341
|
-
const year = Number.parseInt(match[1], 10);
|
|
342
|
-
const month = Number.parseInt(match[2], 10);
|
|
343
|
-
const day = Number.parseInt(match[3], 10);
|
|
344
|
-
return (month >= 1 &&
|
|
345
|
-
month <= 12 &&
|
|
346
|
-
day >= 1 &&
|
|
347
|
-
day <= daysInUtcMonth(year, month - 1));
|
|
348
|
-
}
|
|
349
|
-
function normalizeTimestampCandidate(value) {
|
|
350
|
-
const compactDate = COMPACT_DATE.exec(value);
|
|
351
|
-
if (compactDate) {
|
|
352
|
-
return `${compactDate[1]}-${compactDate[2]}-${compactDate[3]}`;
|
|
353
|
-
}
|
|
354
|
-
const compactDateTime = COMPACT_DATETIME.exec(value);
|
|
355
|
-
if (compactDateTime) {
|
|
356
|
-
const [, year, month, day, hour, minute, secondRaw, offsetRaw] = compactDateTime;
|
|
357
|
-
const second = secondRaw ? `:${secondRaw}` : "";
|
|
358
|
-
const offset = offsetRaw && offsetRaw.length === 5 && offsetRaw !== "Z"
|
|
359
|
-
? `${offsetRaw.slice(0, 3)}:${offsetRaw.slice(3)}`
|
|
360
|
-
: (offsetRaw ?? "");
|
|
361
|
-
return `${year}-${month}-${day}T${hour}:${minute}${second}${offset}`;
|
|
362
|
-
}
|
|
363
|
-
return value;
|
|
364
|
-
}
|
|
365
|
-
function parseContextTimestampMs(value) {
|
|
366
|
-
if (typeof value !== "string") {
|
|
367
|
-
return Number.NaN;
|
|
368
|
-
}
|
|
369
|
-
const trimmed = value.trim();
|
|
370
|
-
if (trimmed.length === 0 || !hasNoInvalidDatePrefix(trimmed)) {
|
|
371
|
-
return Number.NaN;
|
|
372
|
-
}
|
|
373
|
-
const parsed = Date.parse(normalizeTimestampCandidate(trimmed));
|
|
374
|
-
return Number.isFinite(parsed) ? parsed : Number.NaN;
|
|
375
|
-
}
|
|
376
|
-
function sortableTimestamp(value) {
|
|
377
|
-
const parsed = parseContextTimestampMs(value);
|
|
378
|
-
return Number.isFinite(parsed) ? new Date(parsed).toISOString() : "";
|
|
379
|
-
}
|
|
380
|
-
function dateTokenForTimestamp(value) {
|
|
381
|
-
const parsed = parseContextTimestampMs(value);
|
|
382
|
-
return Number.isFinite(parsed)
|
|
383
|
-
? new Date(parsed).toISOString().slice(0, 10)
|
|
384
|
-
: "unknown";
|
|
385
|
-
}
|
|
386
|
-
function normalizedParentId(value) {
|
|
387
|
-
if (typeof value !== "string") {
|
|
388
|
-
return null;
|
|
389
|
-
}
|
|
390
|
-
const trimmed = value.trim();
|
|
391
|
-
return trimmed.length > 0 ? trimmed : null;
|
|
392
|
-
}
|
|
393
|
-
/**
|
|
394
|
-
* Ranks two items by work criticality: in-progress before open before blocked
|
|
395
|
-
* before draft (via {@link statusRank}), then ascending priority, explicit order,
|
|
396
|
-
* earliest deadline, most-recently-updated, and finally id for a total order.
|
|
397
|
-
* Exported as the canonical focus ordering so sibling read commands (e.g.
|
|
398
|
-
* `pm next`) recommend work in the exact same sequence as `pm context`.
|
|
399
|
-
*/
|
|
400
|
-
export function compareCriticalItems(left, right, statusRegistry) {
|
|
401
|
-
const byStatus = statusRank(left.status, statusRegistry) -
|
|
402
|
-
statusRank(right.status, statusRegistry);
|
|
403
|
-
if (byStatus !== 0)
|
|
404
|
-
return byStatus;
|
|
405
|
-
const byPriority = left.priority - right.priority;
|
|
406
|
-
if (byPriority !== 0)
|
|
407
|
-
return byPriority;
|
|
408
|
-
const byOrder = compareOptionalOrder(left.order, right.order);
|
|
409
|
-
if (byOrder !== 0)
|
|
410
|
-
return byOrder;
|
|
411
|
-
const byDeadline = compareOptionalDeadline(left.deadline, right.deadline);
|
|
412
|
-
if (byDeadline !== 0)
|
|
413
|
-
return byDeadline;
|
|
414
|
-
const byUpdated = compareTimestampStrings(sortableTimestamp(right.updated_at), sortableTimestamp(left.updated_at));
|
|
415
|
-
const byId = left.id.localeCompare(right.id);
|
|
416
|
-
return byUpdated !== 0 ? byUpdated : byId;
|
|
417
|
-
}
|
|
418
|
-
function estimateJsonTokens(value) {
|
|
419
|
-
return Math.max(1, Math.ceil(Buffer.byteLength(JSON.stringify(value), "utf8") / 4));
|
|
420
|
-
}
|
|
421
|
-
/** Packs a relevance report with monotone identity, summary, and full item projections. */
|
|
422
|
-
export function packRankedContextItems(report, tokenBudget, requiredIds = new Set(), profile = "context") {
|
|
423
|
-
return packContextCandidates(report.ranked.map((entry) => ({
|
|
424
|
-
id: entry.id,
|
|
425
|
-
item: entry.item,
|
|
426
|
-
rank: entry.rank,
|
|
427
|
-
score: entry.score,
|
|
428
|
-
required: requiredIds.has(entry.id),
|
|
429
|
-
cluster: entry.item.parent?.trim() || entry.item.type,
|
|
430
|
-
token_costs: {
|
|
431
|
-
identity: estimateJsonTokens({ id: entry.id, title: entry.item.title }),
|
|
432
|
-
summary: estimateJsonTokens({
|
|
433
|
-
id: entry.id,
|
|
434
|
-
title: entry.item.title,
|
|
435
|
-
type: entry.item.type,
|
|
436
|
-
status: entry.item.status,
|
|
437
|
-
priority: entry.item.priority,
|
|
438
|
-
parent: entry.item.parent,
|
|
439
|
-
}),
|
|
440
|
-
full: estimateJsonTokens(entry.item),
|
|
441
|
-
},
|
|
442
|
-
})), { tokenBudget, profile, latencyBudgetMs: 25 });
|
|
443
|
-
}
|
|
444
|
-
/** Projects the SDK packer report onto the stable low-token command envelope. */
|
|
445
|
-
export function toContextPackingSummary(report) {
|
|
446
|
-
return {
|
|
447
|
-
...report,
|
|
448
|
-
included: report.included.map((entry) => ({
|
|
449
|
-
id: entry.id,
|
|
450
|
-
projection: entry.projection,
|
|
451
|
-
tokens: entry.tokens,
|
|
452
|
-
})),
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
/** Project a full scorer report onto the low-token command explanation shape. */
|
|
456
|
-
export function toContextRankingSummary(report, featureStore, servedItemIds) {
|
|
457
|
-
const ranked = servedItemIds === undefined
|
|
458
|
-
? report.ranked
|
|
459
|
-
: report.ranked.filter((entry) => servedItemIds.has(entry.id));
|
|
460
|
-
return {
|
|
461
|
-
model: report.model,
|
|
462
|
-
available_signals: report.available_signals,
|
|
463
|
-
items: ranked.map((entry) => ({
|
|
464
|
-
id: entry.id,
|
|
465
|
-
rank: entry.rank,
|
|
466
|
-
baseline_rank: entry.baseline_rank,
|
|
467
|
-
score: entry.score,
|
|
468
|
-
contributions: entry.contributions,
|
|
469
|
-
...(entry.signal_provenance === undefined
|
|
470
|
-
? {}
|
|
471
|
-
: { recency: entry.signal_provenance.recency }),
|
|
472
|
-
})),
|
|
473
|
-
candidate_count: report.ranked.length,
|
|
474
|
-
omitted_count: report.ranked.length - ranked.length,
|
|
475
|
-
...(featureStore
|
|
476
|
-
? {
|
|
477
|
-
feature_store: {
|
|
478
|
-
source: featureStore.snapshot.source,
|
|
479
|
-
cache_status: featureStore.cache_status,
|
|
480
|
-
source_cursor: featureStore.snapshot.source_cursor,
|
|
481
|
-
generated_at: featureStore.snapshot.generated_at,
|
|
482
|
-
},
|
|
483
|
-
}
|
|
484
|
-
: {}),
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
function completionPct(closed, total) {
|
|
488
|
-
return total > 0 ? Math.round((closed / total) * 100) : 0;
|
|
489
|
-
}
|
|
490
|
-
/** Indexes items by their parent id, yielding a parent→direct-children map used by the hierarchy/progress rollups and by focus-row child completion. Items without a parent are skipped. Exported so sibling read commands reuse the same index. */
|
|
491
|
-
export function buildChildrenByParent(allItems) {
|
|
492
|
-
const childrenByParent = new Map();
|
|
493
|
-
for (const item of allItems) {
|
|
494
|
-
const parent = normalizedParentId(item.parent);
|
|
495
|
-
if (!parent)
|
|
496
|
-
continue;
|
|
497
|
-
const children = childrenByParent.get(parent) ?? [];
|
|
498
|
-
children.push(item);
|
|
499
|
-
childrenByParent.set(parent, children);
|
|
500
|
-
}
|
|
501
|
-
return childrenByParent;
|
|
502
|
-
}
|
|
503
|
-
/**
|
|
504
|
-
* Projects an item onto the compact {@link ContextFocusItem} focus row. When both
|
|
505
|
-
* a status registry and a parent→children index are supplied, it also folds in the
|
|
506
|
-
* descendant rollup (`children_total`/`children_closed`/`completion_pct`). Exported
|
|
507
|
-
* so sibling read commands (e.g. `pm next`) emit identically-shaped focus rows.
|
|
508
|
-
*/
|
|
509
|
-
export function toContextFocusItem(item, statusRegistry, childrenByParent) {
|
|
510
|
-
const focus = {
|
|
511
|
-
id: item.id,
|
|
512
|
-
title: item.title,
|
|
513
|
-
type: item.type,
|
|
514
|
-
status: item.status,
|
|
515
|
-
priority: item.priority,
|
|
516
|
-
order: item.order ?? null,
|
|
517
|
-
deadline: item.deadline ?? null,
|
|
518
|
-
assignee: item.assignee ?? null,
|
|
519
|
-
tags: Array.isArray(item.tags) ? [...item.tags] : [],
|
|
520
|
-
updated_at: item.updated_at,
|
|
521
|
-
parent: normalizedParentId(item.parent),
|
|
522
|
-
};
|
|
523
|
-
if (statusRegistry && childrenByParent) {
|
|
524
|
-
const descendants = collectDescendants(item.id, childrenByParent);
|
|
525
|
-
const total = descendants.length;
|
|
526
|
-
if (total > 0) {
|
|
527
|
-
const closed = descendants.filter((desc) => isClosedStatus(desc.status, statusRegistry)).length;
|
|
528
|
-
focus.children_total = total;
|
|
529
|
-
focus.children_closed = closed;
|
|
530
|
-
focus.completion_pct = completionPct(closed, total);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
return focus;
|
|
534
|
-
}
|
|
535
|
-
function summarizeAgenda(events) {
|
|
536
|
-
let deadlines = 0;
|
|
537
|
-
let reminders = 0;
|
|
538
|
-
let scheduled = 0;
|
|
539
|
-
const itemIds = new Set();
|
|
540
|
-
for (const event of events) {
|
|
541
|
-
itemIds.add(event.item_id);
|
|
542
|
-
if (event.kind === "deadline") {
|
|
543
|
-
deadlines += 1;
|
|
544
|
-
continue;
|
|
545
|
-
}
|
|
546
|
-
if (event.kind === "reminder") {
|
|
547
|
-
reminders += 1;
|
|
548
|
-
continue;
|
|
549
|
-
}
|
|
550
|
-
scheduled += 1;
|
|
551
|
-
}
|
|
552
|
-
return {
|
|
553
|
-
events: events.length,
|
|
554
|
-
items: itemIds.size,
|
|
555
|
-
deadlines,
|
|
556
|
-
reminders,
|
|
557
|
-
scheduled,
|
|
558
|
-
};
|
|
559
|
-
}
|
|
560
|
-
/** Combines warning groups into a stable, duplicate-free lexical ordering. */
|
|
561
|
-
export function mergeSortedWarnings(...warningGroups) {
|
|
562
|
-
return [...new Set(warningGroups.flatMap((group) => group ?? []))].sort((left, right) => left.localeCompare(right));
|
|
563
|
-
}
|
|
564
|
-
function filterTerminalCalendarEvents(events, statusRegistry) {
|
|
565
|
-
return events.filter((event) => !statusRegistry.terminal_statuses.has(normalizeStatusForRegistry(event.item_status, statusRegistry)));
|
|
566
|
-
}
|
|
567
|
-
// ---------------------------------------------------------------------------
|
|
568
|
-
// Section builders
|
|
569
|
-
// ---------------------------------------------------------------------------
|
|
570
|
-
function buildHierarchy(allItems, activeItems, statusRegistry, limit) {
|
|
571
|
-
const itemMap = new Map();
|
|
572
|
-
for (const item of allItems) {
|
|
573
|
-
itemMap.set(item.id, item);
|
|
574
|
-
}
|
|
575
|
-
const childrenByParent = buildChildrenByParent(allItems);
|
|
576
|
-
const activeHighLevelIds = new Set(activeItems
|
|
577
|
-
.filter((item) => HIGH_LEVEL_TYPES.has(item.type))
|
|
578
|
-
.map((item) => item.id));
|
|
579
|
-
const nodes = [];
|
|
580
|
-
for (const parentId of activeHighLevelIds) {
|
|
581
|
-
const parent = itemMap.get(parentId);
|
|
582
|
-
if (!parent)
|
|
583
|
-
continue;
|
|
584
|
-
const allDescendants = collectDescendants(parentId, childrenByParent);
|
|
585
|
-
const childItems = childrenByParent.get(parentId) ?? [];
|
|
586
|
-
let closedCount = 0;
|
|
587
|
-
let openCount = 0;
|
|
588
|
-
let inProgressCount = 0;
|
|
589
|
-
let blockedCount = 0;
|
|
590
|
-
/* c8 ignore start -- chained status-classification branch accounting is noisy under v8 for mixed descendant sets */
|
|
591
|
-
for (const desc of allDescendants) {
|
|
592
|
-
if (isClosedStatus(desc.status, statusRegistry))
|
|
593
|
-
closedCount++;
|
|
594
|
-
else if (isInProgressStatus(desc.status, statusRegistry))
|
|
595
|
-
inProgressCount++;
|
|
596
|
-
else if (isBlockedStatus(desc.status, statusRegistry))
|
|
597
|
-
blockedCount++;
|
|
598
|
-
else if (isOpenStatus(desc.status, statusRegistry))
|
|
599
|
-
openCount++;
|
|
600
|
-
}
|
|
601
|
-
/* c8 ignore stop */
|
|
602
|
-
const children = childItems
|
|
603
|
-
.sort((a, b) => compareCriticalItems(a, b, statusRegistry))
|
|
604
|
-
.slice(0, limit)
|
|
605
|
-
.map((child) => {
|
|
606
|
-
const grandchildren = collectDescendants(child.id, childrenByParent);
|
|
607
|
-
const gcClosed = grandchildren.filter((gc) => isClosedStatus(gc.status, statusRegistry)).length;
|
|
608
|
-
return {
|
|
609
|
-
id: child.id,
|
|
610
|
-
title: child.title,
|
|
611
|
-
type: child.type,
|
|
612
|
-
status: child.status,
|
|
613
|
-
children_total: grandchildren.length,
|
|
614
|
-
children_closed: gcClosed,
|
|
615
|
-
};
|
|
616
|
-
});
|
|
617
|
-
nodes.push({
|
|
618
|
-
id: parent.id,
|
|
619
|
-
title: parent.title,
|
|
620
|
-
type: parent.type,
|
|
621
|
-
status: parent.status,
|
|
622
|
-
children_total: allDescendants.length,
|
|
623
|
-
children_closed: closedCount,
|
|
624
|
-
children_open: openCount,
|
|
625
|
-
children_in_progress: inProgressCount,
|
|
626
|
-
children_blocked: blockedCount,
|
|
627
|
-
children,
|
|
628
|
-
});
|
|
629
|
-
}
|
|
630
|
-
return nodes
|
|
631
|
-
.sort((a, b) => {
|
|
632
|
-
const aParent = itemMap.get(a.id);
|
|
633
|
-
const bParent = itemMap.get(b.id);
|
|
634
|
-
return compareCriticalItems(aParent, bParent, statusRegistry);
|
|
635
|
-
})
|
|
636
|
-
.slice(0, limit);
|
|
637
|
-
}
|
|
638
|
-
function collectDescendants(parentId, childrenByParent) {
|
|
639
|
-
const result = [];
|
|
640
|
-
const stack = [parentId];
|
|
641
|
-
const visited = new Set();
|
|
642
|
-
while (stack.length > 0) {
|
|
643
|
-
const current = stack.pop();
|
|
644
|
-
if (visited.has(current))
|
|
645
|
-
continue;
|
|
646
|
-
visited.add(current);
|
|
647
|
-
const children = childrenByParent.get(current) ?? [];
|
|
648
|
-
for (const child of children) {
|
|
649
|
-
result.push(child);
|
|
650
|
-
stack.push(child.id);
|
|
651
|
-
}
|
|
652
|
-
}
|
|
653
|
-
return result;
|
|
654
|
-
}
|
|
655
|
-
/** Public contract for test only, shared by SDK and presentation-layer consumers. */
|
|
656
|
-
export const _testOnly = {
|
|
657
|
-
buildActivity,
|
|
658
|
-
buildBlockers,
|
|
659
|
-
buildChildrenByParent,
|
|
660
|
-
buildHierarchy,
|
|
661
|
-
buildHotFiles,
|
|
662
|
-
buildProgress,
|
|
663
|
-
buildRecentlyCreated,
|
|
664
|
-
buildStaleness,
|
|
665
|
-
buildTestHealth,
|
|
666
|
-
buildUnparented,
|
|
667
|
-
buildWorkload,
|
|
668
|
-
collectDescendants,
|
|
669
|
-
collectSubtreeIds,
|
|
670
|
-
compareCriticalItems,
|
|
671
|
-
compareOptionalDeadline,
|
|
672
|
-
compareOptionalOrder,
|
|
673
|
-
completionPct,
|
|
674
|
-
dateTokenForTimestamp,
|
|
675
|
-
filterTerminalCalendarEvents,
|
|
676
|
-
formatAgendaLine,
|
|
677
|
-
isBlockedStatus,
|
|
678
|
-
isClosedStatus,
|
|
679
|
-
isInProgressStatus,
|
|
680
|
-
isOpenStatus,
|
|
681
|
-
mergeSortedWarnings,
|
|
682
|
-
normalizedParentId,
|
|
683
|
-
parseActivityLimit,
|
|
684
|
-
parseContextLimit,
|
|
685
|
-
resolveContextLimitAtScale,
|
|
686
|
-
parseContextParent,
|
|
687
|
-
parseContextTimestampMs,
|
|
688
|
-
parseStaleThresholdDays,
|
|
689
|
-
sortableTimestamp,
|
|
690
|
-
statusRank,
|
|
691
|
-
stripListProjectionFlags,
|
|
692
|
-
summarizeAgenda,
|
|
693
|
-
toContextFocusItem,
|
|
694
|
-
};
|
|
695
|
-
async function buildActivity(activityLimit, global) {
|
|
696
|
-
const result = await runActivity({ compact: true, limit: String(activityLimit) }, global);
|
|
697
|
-
return result.compact_activity ?? [];
|
|
698
|
-
}
|
|
699
|
-
function buildProgress(allItems, activeItems, blockedIds, statusRegistry, limit) {
|
|
700
|
-
const childrenByParent = new Map();
|
|
701
|
-
for (const item of allItems) {
|
|
702
|
-
if (!item.parent)
|
|
703
|
-
continue;
|
|
704
|
-
const children = childrenByParent.get(item.parent) ?? [];
|
|
705
|
-
children.push(item);
|
|
706
|
-
childrenByParent.set(item.parent, children);
|
|
707
|
-
}
|
|
708
|
-
const activeHighLevel = activeItems.filter((item) => HIGH_LEVEL_TYPES.has(item.type));
|
|
709
|
-
const entries = [];
|
|
710
|
-
for (const parent of activeHighLevel) {
|
|
711
|
-
const descendants = collectDescendants(parent.id, childrenByParent);
|
|
712
|
-
const total = descendants.length;
|
|
713
|
-
if (total === 0)
|
|
714
|
-
continue;
|
|
715
|
-
let closed = 0;
|
|
716
|
-
let open = 0;
|
|
717
|
-
let inProgress = 0;
|
|
718
|
-
let blocked = 0;
|
|
719
|
-
/* c8 ignore start -- chained status-classification branch accounting is noisy under v8 for mixed descendant sets */
|
|
720
|
-
for (const desc of descendants) {
|
|
721
|
-
// Blocked is edge-aware (GH-578): dependency-blocked descendants count as
|
|
722
|
-
// blocked regardless of lifecycle status, matching pm next and list-blocked.
|
|
723
|
-
if (isClosedStatus(desc.status, statusRegistry))
|
|
724
|
-
closed++;
|
|
725
|
-
else if (blockedIds.has(desc.id.trim().toLowerCase()))
|
|
726
|
-
blocked++;
|
|
727
|
-
else if (isInProgressStatus(desc.status, statusRegistry))
|
|
728
|
-
inProgress++;
|
|
729
|
-
else if (isOpenStatus(desc.status, statusRegistry))
|
|
730
|
-
open++;
|
|
731
|
-
}
|
|
732
|
-
/* c8 ignore stop */
|
|
733
|
-
entries.push({
|
|
734
|
-
id: parent.id,
|
|
735
|
-
title: parent.title,
|
|
736
|
-
type: parent.type,
|
|
737
|
-
total,
|
|
738
|
-
closed,
|
|
739
|
-
open,
|
|
740
|
-
in_progress: inProgress,
|
|
741
|
-
blocked,
|
|
742
|
-
completion_pct: completionPct(closed, total),
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
return entries
|
|
746
|
-
.sort((a, b) => a.completion_pct - b.completion_pct)
|
|
747
|
-
.slice(0, limit);
|
|
748
|
-
}
|
|
749
|
-
function buildBlockers(blockedItems, fullCorpus, statusRegistry, limit) {
|
|
750
|
-
const itemsById = new Map(fullCorpus.map((item) => [item.id.trim().toLowerCase(), item]));
|
|
751
|
-
return blockedItems.slice(0, limit).map((item) => {
|
|
752
|
-
// Prefer a currently open blocker edge. The legacy scalar may still name a
|
|
753
|
-
// resolved predecessor, so it is only a fallback when no open edge exists.
|
|
754
|
-
const blockers = resolveItemBlockers(item, itemsById, statusRegistry);
|
|
755
|
-
const blockedBy = blockers.find((blocker) => !blocker.resolved)?.id ??
|
|
756
|
-
blockers[0]?.id ??
|
|
757
|
-
null;
|
|
758
|
-
const blockerItem = blockedBy
|
|
759
|
-
? itemsById.get(blockedBy.trim().toLowerCase())
|
|
760
|
-
: undefined;
|
|
761
|
-
return {
|
|
762
|
-
id: item.id,
|
|
763
|
-
title: item.title,
|
|
764
|
-
blocked_by: blockedBy,
|
|
765
|
-
blocked_by_title: blockerItem?.title ?? null,
|
|
766
|
-
blocked_by_status: blockerItem?.status ?? null,
|
|
767
|
-
blocked_reason: item.blocked_reason ?? null,
|
|
768
|
-
unblock_note: item.unblock_note ?? null,
|
|
769
|
-
};
|
|
770
|
-
});
|
|
771
|
-
}
|
|
772
|
-
function buildHotFiles(activeItems, limit) {
|
|
773
|
-
const fileMap = new Map();
|
|
774
|
-
for (const item of activeItems) {
|
|
775
|
-
for (const file of item.files ?? []) {
|
|
776
|
-
const existing = fileMap.get(file.path) ?? new Set();
|
|
777
|
-
existing.add(item.id);
|
|
778
|
-
fileMap.set(file.path, existing);
|
|
779
|
-
}
|
|
780
|
-
}
|
|
781
|
-
return [...fileMap.entries()]
|
|
782
|
-
.map(([filePath, itemIds]) => ({
|
|
783
|
-
path: filePath,
|
|
784
|
-
references: itemIds.size,
|
|
785
|
-
items: [...itemIds].sort(),
|
|
786
|
-
}))
|
|
787
|
-
.sort((a, b) => b.references - a.references)
|
|
788
|
-
.slice(0, limit);
|
|
789
|
-
}
|
|
790
|
-
function buildWorkload(activeItems, statusRegistry, limit) {
|
|
791
|
-
const groups = new Map();
|
|
792
|
-
for (const item of activeItems) {
|
|
793
|
-
const key = item.assignee ?? null;
|
|
794
|
-
const existing = groups.get(key) ?? [];
|
|
795
|
-
existing.push(item);
|
|
796
|
-
groups.set(key, existing);
|
|
797
|
-
}
|
|
798
|
-
return [...groups.entries()]
|
|
799
|
-
.map(([assignee, items]) => ({
|
|
800
|
-
assignee,
|
|
801
|
-
active: items.length,
|
|
802
|
-
in_progress: items.filter((item) => isInProgressStatus(item.status, statusRegistry)).length,
|
|
803
|
-
items: items.map((item) => item.id).sort(),
|
|
804
|
-
}))
|
|
805
|
-
.sort((a, b) => b.active - a.active)
|
|
806
|
-
.slice(0, limit);
|
|
807
|
-
}
|
|
808
|
-
function buildStaleness(allNonTerminal, staleThresholdDays, now, limit) {
|
|
809
|
-
const cutoffMs = new Date(now).getTime() - staleThresholdDays * 24 * 60 * 60 * 1000;
|
|
810
|
-
return allNonTerminal
|
|
811
|
-
.filter((item) => new Date(item.updated_at).getTime() < cutoffMs)
|
|
812
|
-
.map((item) => ({
|
|
813
|
-
id: item.id,
|
|
814
|
-
title: item.title,
|
|
815
|
-
status: item.status,
|
|
816
|
-
updated_at: item.updated_at,
|
|
817
|
-
stale_days: Math.floor((new Date(now).getTime() - new Date(item.updated_at).getTime()) /
|
|
818
|
-
(24 * 60 * 60 * 1000)),
|
|
819
|
-
}))
|
|
820
|
-
.sort((a, b) => b.stale_days - a.stale_days)
|
|
821
|
-
.slice(0, limit);
|
|
822
|
-
}
|
|
823
|
-
function buildRecentlyCreated(allNonTerminal, statusRegistry, childrenByParent, limit) {
|
|
824
|
-
return allNonTerminal
|
|
825
|
-
.map((item) => ({ item, sortKey: sortableTimestamp(item.created_at) }))
|
|
826
|
-
.sort((left, right) => {
|
|
827
|
-
const byCreated = compareTimestampStrings(right.sortKey, left.sortKey);
|
|
828
|
-
return byCreated !== 0
|
|
829
|
-
? byCreated
|
|
830
|
-
: left.item.id.localeCompare(right.item.id);
|
|
831
|
-
})
|
|
832
|
-
.slice(0, limit)
|
|
833
|
-
.map(({ item }) => ({
|
|
834
|
-
...toContextFocusItem(item, statusRegistry, childrenByParent),
|
|
835
|
-
created_at: item.created_at,
|
|
836
|
-
}));
|
|
837
|
-
}
|
|
838
|
-
function buildUnparented(allNonTerminal, statusRegistry, childrenByParent, limit) {
|
|
839
|
-
return allNonTerminal
|
|
840
|
-
.filter((item) => !normalizedParentId(item.parent) && !HIGH_LEVEL_TYPES.has(item.type))
|
|
841
|
-
.sort((left, right) => compareCriticalItems(left, right, statusRegistry))
|
|
842
|
-
.slice(0, limit)
|
|
843
|
-
.map((item) => toContextFocusItem(item, statusRegistry, childrenByParent));
|
|
844
|
-
}
|
|
845
|
-
function buildTestHealth(activeItems) {
|
|
846
|
-
let itemsWithTests = 0;
|
|
847
|
-
let itemsWithRecentRuns = 0;
|
|
848
|
-
let passed = 0;
|
|
849
|
-
let failed = 0;
|
|
850
|
-
let skipped = 0;
|
|
851
|
-
const itemsFailing = [];
|
|
852
|
-
for (const item of activeItems) {
|
|
853
|
-
if ((item.tests ?? []).length > 0) {
|
|
854
|
-
itemsWithTests++;
|
|
855
|
-
}
|
|
856
|
-
const runs = item.test_runs ?? [];
|
|
857
|
-
if (runs.length > 0) {
|
|
858
|
-
itemsWithRecentRuns++;
|
|
859
|
-
let itemHasFailure = false;
|
|
860
|
-
for (const run of runs) {
|
|
861
|
-
passed += run.passed ?? 0;
|
|
862
|
-
failed += run.failed ?? 0;
|
|
863
|
-
skipped += run.skipped ?? 0;
|
|
864
|
-
if ((run.failed ?? 0) > 0) {
|
|
865
|
-
itemHasFailure = true;
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
if (itemHasFailure) {
|
|
869
|
-
itemsFailing.push(item.id);
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
return {
|
|
874
|
-
items_with_tests: itemsWithTests,
|
|
875
|
-
items_with_recent_runs: itemsWithRecentRuns,
|
|
876
|
-
recent_runs: { passed, failed, skipped },
|
|
877
|
-
items_failing: itemsFailing.sort(),
|
|
878
|
-
};
|
|
879
|
-
}
|
|
880
|
-
// ---------------------------------------------------------------------------
|
|
881
|
-
// Markdown formatting
|
|
882
|
-
// ---------------------------------------------------------------------------
|
|
883
|
-
function formatClock(timestamp) {
|
|
884
|
-
return `${new Date(timestamp).toISOString().slice(11, 16)}Z`;
|
|
885
|
-
}
|
|
886
|
-
function formatFocusLine(item) {
|
|
887
|
-
const orderToken = item.order === null ? "-" : String(item.order);
|
|
888
|
-
const deadlineToken = item.deadline ?? "-";
|
|
889
|
-
const parentToken = item.parent ? ` parent:${item.parent}` : "";
|
|
890
|
-
const progressToken = item.children_total !== undefined &&
|
|
891
|
-
item.children_closed !== undefined &&
|
|
892
|
-
item.completion_pct !== undefined
|
|
893
|
-
? ` children:${item.children_closed}/${item.children_total} done:${item.completion_pct}%`
|
|
894
|
-
: "";
|
|
895
|
-
const blockedToken = item.blocked === true
|
|
896
|
-
? ` blocked:${item.blocker_ids?.join(",") || "lifecycle"}`
|
|
897
|
-
: "";
|
|
898
|
-
return `${item.id} p${item.priority} ${item.status} ${item.type} order:${orderToken} deadline:${deadlineToken}${parentToken}${progressToken}${blockedToken} ${item.title}`;
|
|
899
|
-
}
|
|
900
|
-
// Renders a focus row projected to a `--fields` subset as space-separated
|
|
901
|
-
// field:value tokens, mirroring the projection field order. Array values join
|
|
902
|
-
// with commas; missing/null values render as `-`.
|
|
903
|
-
function formatProjectedFocusLine(item, fields) {
|
|
904
|
-
const source = item;
|
|
905
|
-
return fields
|
|
906
|
-
.map((field) => {
|
|
907
|
-
const value = source[field];
|
|
908
|
-
if (Array.isArray(value))
|
|
909
|
-
return `${field}:${value.join(",")}`;
|
|
910
|
-
return `${field}:${value ?? "-"}`;
|
|
911
|
-
})
|
|
912
|
-
.join(" ");
|
|
913
|
-
}
|
|
914
|
-
function formatAgendaLine(event) {
|
|
915
|
-
if ("reference_only" in event) {
|
|
916
|
-
const suffix = event.kind === "reminder"
|
|
917
|
-
? ` — ${event.reminder_text ?? "reminder"}`
|
|
918
|
-
: event.kind === "event"
|
|
919
|
-
? ` — ${event.event_title ?? "event"}${event.event_recurring ? " (recurring)" : ""}`
|
|
920
|
-
: "";
|
|
921
|
-
return `${formatClock(event.at)} [${event.kind}] ${event.item_id} (see focus item)${suffix}`;
|
|
922
|
-
}
|
|
923
|
-
const base = `${formatClock(event.at)} [${event.kind}] ${event.item_id} p${event.item_priority} ${event.item_status} ${event.item_title}`;
|
|
924
|
-
if (event.kind === "reminder") {
|
|
925
|
-
return `${base} — ${event.reminder_text}`;
|
|
926
|
-
}
|
|
927
|
-
if (event.kind === "event") {
|
|
928
|
-
const recurringSuffix = event.event_recurring ? " (recurring)" : "";
|
|
929
|
-
const title = event.event_title ?? event.item_title;
|
|
930
|
-
return `${base} — ${title}${recurringSuffix}`;
|
|
931
|
-
}
|
|
932
|
-
return base;
|
|
933
|
-
}
|
|
934
|
-
function pushContextFocusSection(lines, title, rows, emptyText, renderFocus) {
|
|
935
|
-
lines.push(`## ${title}`);
|
|
936
|
-
if (rows.length === 0) {
|
|
937
|
-
lines.push(emptyText);
|
|
938
|
-
}
|
|
939
|
-
else {
|
|
940
|
-
for (const item of rows) {
|
|
941
|
-
lines.push(`- ${renderFocus(item)}`);
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
lines.push("");
|
|
945
|
-
}
|
|
946
|
-
function pushBlockedFallbackSection(lines, result, renderFocus) {
|
|
947
|
-
if (result.blocked_fallback.length === 0) {
|
|
948
|
-
return;
|
|
949
|
-
}
|
|
950
|
-
lines.push("## Blocked fallback");
|
|
951
|
-
for (const item of result.blocked_fallback) {
|
|
952
|
-
lines.push(`- ${renderFocus(item)}`);
|
|
953
|
-
}
|
|
954
|
-
lines.push("");
|
|
955
|
-
}
|
|
956
|
-
function pushAgendaSection(lines, result) {
|
|
957
|
-
lines.push("## Agenda");
|
|
958
|
-
lines.push(`- events: ${result.agenda.summary.events} (deadlines: ${result.agenda.summary.deadlines}, reminders: ${result.agenda.summary.reminders}, scheduled: ${result.agenda.summary.scheduled})`);
|
|
959
|
-
if (result.agenda.events.length === 0) {
|
|
960
|
-
lines.push("No agenda events matched the selected filters.");
|
|
961
|
-
}
|
|
962
|
-
else {
|
|
963
|
-
for (const event of result.agenda.events) {
|
|
964
|
-
lines.push(`- ${formatAgendaLine(event)}`);
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
lines.push("");
|
|
968
|
-
}
|
|
969
|
-
function pushHierarchySection(lines, result) {
|
|
970
|
-
if (!result.hierarchy || result.hierarchy.length === 0) {
|
|
971
|
-
return;
|
|
972
|
-
}
|
|
973
|
-
lines.push("## Hierarchy");
|
|
974
|
-
for (const node of result.hierarchy) {
|
|
975
|
-
const pct = node.children_total > 0
|
|
976
|
-
? Math.round((node.children_closed / node.children_total) * 100)
|
|
977
|
-
: 0;
|
|
978
|
-
lines.push(`- ${node.id} ${node.type} ${node.status} "${node.title}" [${node.children_closed}/${node.children_total} done ${pct}%]`);
|
|
979
|
-
for (const child of node.children) {
|
|
980
|
-
const cpct = child.children_total > 0
|
|
981
|
-
? Math.round((child.children_closed / child.children_total) * 100)
|
|
982
|
-
: 0;
|
|
983
|
-
lines.push(` - ${child.id} ${child.type} ${child.status} "${child.title}" [${child.children_closed}/${child.children_total} done ${cpct}%]`);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
lines.push("");
|
|
987
|
-
}
|
|
988
|
-
function pushProgressSection(lines, result) {
|
|
989
|
-
if (!result.progress || result.progress.length === 0) {
|
|
990
|
-
return;
|
|
991
|
-
}
|
|
992
|
-
lines.push("## Progress");
|
|
993
|
-
for (const entry of result.progress) {
|
|
994
|
-
lines.push(`- ${entry.id} "${entry.title}" ${entry.completion_pct}% (${entry.closed}/${entry.total} closed, ${entry.in_progress} wip, ${entry.open} open, ${entry.blocked} blocked)`);
|
|
995
|
-
}
|
|
996
|
-
lines.push("");
|
|
997
|
-
}
|
|
998
|
-
function pushRecentlyCreatedSection(lines, result, focusFields, renderFocus) {
|
|
999
|
-
if (!result.recently_created || result.recently_created.length === 0) {
|
|
1000
|
-
return;
|
|
1001
|
-
}
|
|
1002
|
-
lines.push("## Recently created");
|
|
1003
|
-
for (const item of result.recently_created) {
|
|
1004
|
-
lines.push(focusFields
|
|
1005
|
-
? `- ${renderFocus(item)}`
|
|
1006
|
-
: `- ${dateTokenForTimestamp(item.created_at)} ${formatFocusLine(item)}`);
|
|
1007
|
-
}
|
|
1008
|
-
lines.push("");
|
|
1009
|
-
}
|
|
1010
|
-
function pushUnparentedSection(lines, result, renderFocus) {
|
|
1011
|
-
if (!result.unparented || result.unparented.length === 0) {
|
|
1012
|
-
return;
|
|
1013
|
-
}
|
|
1014
|
-
lines.push("## Unparented");
|
|
1015
|
-
for (const item of result.unparented) {
|
|
1016
|
-
lines.push(`- ${renderFocus(item)}`);
|
|
1017
|
-
}
|
|
1018
|
-
lines.push("");
|
|
1019
|
-
}
|
|
1020
|
-
function pushActivitySection(lines, result) {
|
|
1021
|
-
if (!result.activity || result.activity.length === 0) {
|
|
1022
|
-
return;
|
|
1023
|
-
}
|
|
1024
|
-
lines.push("## Recent activity");
|
|
1025
|
-
for (const entry of result.activity) {
|
|
1026
|
-
const msg = entry.msg ? ` ${entry.msg}` : "";
|
|
1027
|
-
lines.push(`- ${entry.ts.slice(0, 16)}Z ${entry.id} ${entry.op} by:${entry.author}${msg}`);
|
|
1028
|
-
}
|
|
1029
|
-
lines.push("");
|
|
1030
|
-
}
|
|
1031
|
-
function pushBlockersSection(lines, result) {
|
|
1032
|
-
if (!result.blockers || result.blockers.length === 0) {
|
|
1033
|
-
return;
|
|
1034
|
-
}
|
|
1035
|
-
lines.push("## Blockers");
|
|
1036
|
-
for (const entry of result.blockers) {
|
|
1037
|
-
const by = entry.blocked_by
|
|
1038
|
-
? `blocked_by:${entry.blocked_by}(${entry.blocked_by_status ?? "?"})`
|
|
1039
|
-
: "blocked_by:-";
|
|
1040
|
-
const reason = entry.blocked_reason
|
|
1041
|
-
? ` reason:"${entry.blocked_reason}"`
|
|
1042
|
-
: "";
|
|
1043
|
-
const note = entry.unblock_note ? ` unblock:"${entry.unblock_note}"` : "";
|
|
1044
|
-
lines.push(`- ${entry.id} "${entry.title}" ${by}${reason}${note}`);
|
|
1045
|
-
}
|
|
1046
|
-
lines.push("");
|
|
1047
|
-
}
|
|
1048
|
-
function pushFilesSection(lines, result) {
|
|
1049
|
-
if (!result.files || result.files.length === 0) {
|
|
1050
|
-
return;
|
|
1051
|
-
}
|
|
1052
|
-
lines.push("## Hot files");
|
|
1053
|
-
for (const file of result.files) {
|
|
1054
|
-
lines.push(`- ${file.path} refs:${file.references} items:[${file.items.join(",")}]`);
|
|
1055
|
-
}
|
|
1056
|
-
lines.push("");
|
|
1057
|
-
}
|
|
1058
|
-
function pushWorkloadSection(lines, result) {
|
|
1059
|
-
if (!result.workload || result.workload.length === 0) {
|
|
1060
|
-
return;
|
|
1061
|
-
}
|
|
1062
|
-
lines.push("## Workload");
|
|
1063
|
-
for (const entry of result.workload) {
|
|
1064
|
-
const who = entry.assignee ?? "(unassigned)";
|
|
1065
|
-
lines.push(`- ${who} active:${entry.active} wip:${entry.in_progress} items:[${entry.items.join(",")}]`);
|
|
1066
|
-
}
|
|
1067
|
-
lines.push("");
|
|
1068
|
-
}
|
|
1069
|
-
function pushStalenessSection(lines, result) {
|
|
1070
|
-
if (!result.staleness || result.staleness.length === 0) {
|
|
1071
|
-
return;
|
|
1072
|
-
}
|
|
1073
|
-
lines.push("## Stale items");
|
|
1074
|
-
for (const entry of result.staleness) {
|
|
1075
|
-
lines.push(`- ${entry.id} ${entry.status} stale:${entry.stale_days}d last:${entry.updated_at.slice(0, 10)} "${entry.title}"`);
|
|
1076
|
-
}
|
|
1077
|
-
lines.push("");
|
|
1078
|
-
}
|
|
1079
|
-
function pushTestHealthSection(lines, result) {
|
|
1080
|
-
if (!result.tests) {
|
|
1081
|
-
return;
|
|
1082
|
-
}
|
|
1083
|
-
lines.push("## Test health");
|
|
1084
|
-
lines.push(`- items_with_tests: ${result.tests.items_with_tests}`);
|
|
1085
|
-
lines.push(`- items_with_recent_runs: ${result.tests.items_with_recent_runs}`);
|
|
1086
|
-
lines.push(`- passed: ${result.tests.recent_runs.passed}, failed: ${result.tests.recent_runs.failed}, skipped: ${result.tests.recent_runs.skipped}`);
|
|
1087
|
-
if (result.tests.items_failing.length > 0) {
|
|
1088
|
-
lines.push(`- items_failing: [${result.tests.items_failing.join(",")}]`);
|
|
1089
|
-
}
|
|
1090
|
-
lines.push("");
|
|
1091
|
-
}
|
|
1092
|
-
function pushEmptyContextSuggestions(lines, result) {
|
|
1093
|
-
const isEmpty = result.summary.active_items === 0 &&
|
|
1094
|
-
result.summary.blocked === 0 &&
|
|
1095
|
-
result.agenda.summary.events === 0;
|
|
1096
|
-
if (!isEmpty) {
|
|
1097
|
-
return;
|
|
1098
|
-
}
|
|
1099
|
-
lines.push("## Suggestions");
|
|
1100
|
-
lines.push("No active work items or upcoming events. Consider:");
|
|
1101
|
-
lines.push('- `pm create --type Task --title "..."` to add a new work item');
|
|
1102
|
-
lines.push("- `pm list --status closed --limit 5` to review recent completions");
|
|
1103
|
-
lines.push("- `pm search <keywords>` to find related past work");
|
|
1104
|
-
lines.push("- `pm aggregate` for a full project status overview");
|
|
1105
|
-
}
|
|
1106
|
-
/** Implements render context markdown for the public runtime surface of this module. */
|
|
1107
|
-
export function renderContextMarkdown(result) {
|
|
1108
|
-
const lines = [];
|
|
1109
|
-
const focusFields = result.focus_fields;
|
|
1110
|
-
const renderFocus = (item) => focusFields
|
|
1111
|
-
? formatProjectedFocusLine(item, focusFields)
|
|
1112
|
-
: formatFocusLine(item);
|
|
1113
|
-
lines.push("# pm context");
|
|
1114
|
-
lines.push("");
|
|
1115
|
-
lines.push(`- now: ${result.now}`);
|
|
1116
|
-
lines.push(`- depth: ${result.depth}`);
|
|
1117
|
-
if (result.filters.parent) {
|
|
1118
|
-
lines.push(`- scope: subtree of ${result.filters.parent}`);
|
|
1119
|
-
}
|
|
1120
|
-
lines.push(`- active_items: ${result.summary.active_items} (in_progress: ${result.summary.in_progress}, open: ${result.summary.open})`);
|
|
1121
|
-
if (result.summary.total_items !== undefined) {
|
|
1122
|
-
lines.push(`- total_items: ${result.summary.total_items} (closed: ${result.summary.closed ?? 0}, canceled: ${result.summary.canceled ?? 0})`);
|
|
1123
|
-
}
|
|
1124
|
-
lines.push(`- agenda_events: ${result.summary.agenda_events}`);
|
|
1125
|
-
lines.push(`- blocked_fallback_used: ${result.summary.blocked_fallback_used}`);
|
|
1126
|
-
if (result.extension_health) {
|
|
1127
|
-
const degradedNames = result.extension_health.packages
|
|
1128
|
-
.filter((entry) => entry.status === "degraded")
|
|
1129
|
-
.map((entry) => entry.name);
|
|
1130
|
-
lines.push(`- extensions: ${result.extension_health.ok} ok, ${result.extension_health.degraded} degraded${degradedNames.length > 0 ? ` (${degradedNames.join(", ")})` : ""}`);
|
|
1131
|
-
}
|
|
1132
|
-
const renderedSections = result.sections_included.filter((section) => section !== "blockers" || (result.blockers?.length ?? 0) > 0);
|
|
1133
|
-
if (renderedSections.length > 0) {
|
|
1134
|
-
lines.push(`- sections: ${renderedSections.join(", ")}`);
|
|
1135
|
-
}
|
|
1136
|
-
lines.push("");
|
|
1137
|
-
pushContextFocusSection(lines, "High-level focus", result.high_level, "No high-level active items.", renderFocus);
|
|
1138
|
-
pushContextFocusSection(lines, "Low-level focus", result.low_level, "No low-level active items.", renderFocus);
|
|
1139
|
-
pushBlockedFallbackSection(lines, result, renderFocus);
|
|
1140
|
-
pushAgendaSection(lines, result);
|
|
1141
|
-
if (result.workspace_memory) {
|
|
1142
|
-
lines.push("## Workspace memory");
|
|
1143
|
-
for (const rollup of result.workspace_memory.rollups) {
|
|
1144
|
-
lines.push(`- ${rollup.kind}:${rollup.key} — ${rollup.label} (${rollup.item_count} completed, latest ${rollup.last_closed_at})`);
|
|
1145
|
-
}
|
|
1146
|
-
lines.push("");
|
|
1147
|
-
}
|
|
1148
|
-
pushHierarchySection(lines, result);
|
|
1149
|
-
pushProgressSection(lines, result);
|
|
1150
|
-
pushRecentlyCreatedSection(lines, result, focusFields, renderFocus);
|
|
1151
|
-
pushUnparentedSection(lines, result, renderFocus);
|
|
1152
|
-
pushActivitySection(lines, result);
|
|
1153
|
-
pushBlockersSection(lines, result);
|
|
1154
|
-
pushFilesSection(lines, result);
|
|
1155
|
-
pushWorkloadSection(lines, result);
|
|
1156
|
-
pushStalenessSection(lines, result);
|
|
1157
|
-
pushTestHealthSection(lines, result);
|
|
1158
|
-
pushEmptyContextSuggestions(lines, result);
|
|
1159
|
-
return lines.join("\n");
|
|
1160
|
-
}
|
|
1161
|
-
async function resolveContextRuntime(options, global) {
|
|
1162
|
-
const pmRoot = resolvePmRoot(process.cwd(), global.path);
|
|
1163
|
-
const settings = await readSettings(pmRoot);
|
|
1164
|
-
/* c8 ignore start -- settings persistence currently always materializes context defaults */
|
|
1165
|
-
const contextSettings = settings.context ?? SETTINGS_DEFAULTS.context;
|
|
1166
|
-
/* c8 ignore stop */
|
|
1167
|
-
const statusRegistry = resolveRuntimeStatusRegistry(settings.schema);
|
|
1168
|
-
const depth = parseContextDepth(options.depth, contextSettings);
|
|
1169
|
-
const limit = parseContextLimit(options.limit ?? options.maxItems, depth);
|
|
1170
|
-
return {
|
|
1171
|
-
settings,
|
|
1172
|
-
contextSettings,
|
|
1173
|
-
statusRegistry,
|
|
1174
|
-
depth,
|
|
1175
|
-
limit,
|
|
1176
|
-
sectionsIncluded: parseContextSections(options.section, depth, contextSettings),
|
|
1177
|
-
activityLimit: parseActivityLimit(options.activityLimit, contextSettings),
|
|
1178
|
-
staleThresholdDays: parseStaleThresholdDays(options.staleThreshold, contextSettings),
|
|
1179
|
-
parentScope: parseContextParent(options.parent),
|
|
1180
|
-
focusFields: parseContextFocusFields(options.fields),
|
|
1181
|
-
baseListOptions: stripListProjectionFlags(options),
|
|
1182
|
-
};
|
|
1183
|
-
}
|
|
1184
|
-
function contextNeedsAllItems(sectionsIncluded) {
|
|
1185
|
-
return sectionsIncluded.some((section) => [
|
|
1186
|
-
"hierarchy",
|
|
1187
|
-
"progress",
|
|
1188
|
-
"blockers",
|
|
1189
|
-
"staleness",
|
|
1190
|
-
"recently_created",
|
|
1191
|
-
"unparented",
|
|
1192
|
-
].includes(section));
|
|
1193
|
-
}
|
|
1194
|
-
function buildUnpaginatedContextListOptions(baseListOptions, extra) {
|
|
1195
|
-
return {
|
|
1196
|
-
...baseListOptions,
|
|
1197
|
-
...extra,
|
|
1198
|
-
noTruncate: true,
|
|
1199
|
-
limit: undefined,
|
|
1200
|
-
offset: undefined,
|
|
1201
|
-
after: undefined,
|
|
1202
|
-
};
|
|
1203
|
-
}
|
|
1204
|
-
async function loadContextCorpus(global, runtime) {
|
|
1205
|
-
// One full-corpus scan serves every consumer: runList always reads all item
|
|
1206
|
-
// files before filtering, so deriving the non-terminal subset in memory is
|
|
1207
|
-
// free and keeps terminal blocker targets available for the shared
|
|
1208
|
-
// edge-aware blocked classification (GH-578).
|
|
1209
|
-
const listed = await runList(undefined, buildUnpaginatedContextListOptions(runtime.baseListOptions, {
|
|
1210
|
-
excludeTerminal: false,
|
|
1211
|
-
}), global);
|
|
1212
|
-
const fullCorpus = listed.items;
|
|
1213
|
-
let listedItemMetadata = fullCorpus.filter((item) => !isTerminalStatus(item.status, runtime.statusRegistry));
|
|
1214
|
-
let allItems = fullCorpus;
|
|
1215
|
-
const subtreeIds = resolveContextSubtreeIds(runtime.parentScope, fullCorpus);
|
|
1216
|
-
if (subtreeIds) {
|
|
1217
|
-
listedItemMetadata = listedItemMetadata.filter((item) => subtreeIds.has(item.id.trim().toLowerCase()));
|
|
1218
|
-
allItems = allItems.filter((item) => subtreeIds.has(item.id.trim().toLowerCase()));
|
|
1219
|
-
}
|
|
1220
|
-
return { listed, listedItemMetadata, allItems, fullCorpus, subtreeIds };
|
|
1221
|
-
}
|
|
1222
|
-
function resolveContextSubtreeIds(parentScope, fullCorpus) {
|
|
1223
|
-
if (parentScope === undefined) {
|
|
1224
|
-
return undefined;
|
|
1225
|
-
}
|
|
1226
|
-
const subtree = collectSubtreeIds(fullCorpus, parentScope);
|
|
1227
|
-
if (!subtree.found) {
|
|
1228
|
-
throw new PmCliError(`Context --parent item not found: ${parentScope}`, EXIT_CODE.NOT_FOUND);
|
|
1229
|
-
}
|
|
1230
|
-
return subtree.ids;
|
|
1231
|
-
}
|
|
1232
|
-
/** Classify strategic and human-directed context through the shared execution rules. */
|
|
1233
|
-
function collectHighLevelContextIds(activeItems, fullCorpus, statusRegistry, typeRegistry) {
|
|
1234
|
-
const containers = new Set(computeActionabilityReport(activeItems, fullCorpus, statusRegistry, typeRegistry)
|
|
1235
|
-
.containers.map((entry) => entry.item.id));
|
|
1236
|
-
return new Set(activeItems.filter((item) => HIGH_LEVEL_TYPES.has(item.type) || containers.has(item.id) ||
|
|
1237
|
-
resolveItemExecutionRole(item.type, typeRegistry) !== "agent").map((item) => item.id));
|
|
1238
|
-
}
|
|
1239
|
-
async function resolveContextFocusGroups(listedItemMetadata, allItems, fullCorpus, blockedIds, statusRegistry, sectionsIncluded, limit, now, author, cursor, cursorFingerprint, useBoundedPage, pmRoot, tokenBudget, claimFocus, typeRegistry) {
|
|
1240
|
-
const structural = [...listedItemMetadata].sort((left, right) => compareCriticalItems(left, right, statusRegistry));
|
|
1241
|
-
const usage = await readContextUsageAffinity({
|
|
1242
|
-
pmRoot,
|
|
1243
|
-
author,
|
|
1244
|
-
enabled: process.env.PM_CONTEXT_USAGE_DISABLED !== "1",
|
|
1245
|
-
});
|
|
1246
|
-
const featureStore = await readWorkspaceContextSignals(structural, {
|
|
1247
|
-
pmRoot,
|
|
1248
|
-
storeKey: "context",
|
|
1249
|
-
statusRegistry,
|
|
1250
|
-
now,
|
|
1251
|
-
author,
|
|
1252
|
-
usageAffinity: usage.affinity,
|
|
1253
|
-
claimFocus,
|
|
1254
|
-
});
|
|
1255
|
-
const ranking = await scoreContextCandidatesWithActiveExtensions("context", featureStore.candidates);
|
|
1256
|
-
const ranked = ranking.ranked.map((entry) => entry.item);
|
|
1257
|
-
const activeStatuses = statusRegistry.active_statuses;
|
|
1258
|
-
const rankedActiveItems = ranked.filter((item) => activeStatuses.has(normalizeStatusForRegistry(item.status, statusRegistry)));
|
|
1259
|
-
const pageStart = useBoundedPage
|
|
1260
|
-
? resolveQueryCursorStart(rankedActiveItems, cursor, cursorFingerprint, (item) => item.id)
|
|
1261
|
-
: 0;
|
|
1262
|
-
const focusPage = useBoundedPage
|
|
1263
|
-
? rankedActiveItems.slice(pageStart, pageStart + limit)
|
|
1264
|
-
: rankedActiveItems;
|
|
1265
|
-
const hasMore = useBoundedPage &&
|
|
1266
|
-
focusPage.length > 0 &&
|
|
1267
|
-
pageStart + focusPage.length < rankedActiveItems.length;
|
|
1268
|
-
const nextCursor = hasMore && focusPage.length > 0
|
|
1269
|
-
? encodeQueryCursor(cursorFingerprint, focusPage[focusPage.length - 1].id, pageStart + focusPage.length - 1)
|
|
1270
|
-
: undefined;
|
|
1271
|
-
const rankedBlockedItems = ranked.filter((item) => blockedIds.has(item.id.trim().toLowerCase()));
|
|
1272
|
-
const packingIds = new Set([
|
|
1273
|
-
...focusPage.map((item) => item.id),
|
|
1274
|
-
...rankedBlockedItems.slice(0, limit).map((item) => item.id),
|
|
1275
|
-
]);
|
|
1276
|
-
const packing = packRankedContextItems({
|
|
1277
|
-
...ranking,
|
|
1278
|
-
ranked: ranking.ranked.filter((entry) => packingIds.has(entry.id)),
|
|
1279
|
-
}, tokenBudget, new Set(Object.entries(claimFocus ?? {})
|
|
1280
|
-
.filter(([, affinity]) => affinity >= 1)
|
|
1281
|
-
.map(([id]) => id)));
|
|
1282
|
-
const packedItems = packing.included.map((entry) => entry.item);
|
|
1283
|
-
const activeItems = packedItems.filter((item) => activeStatuses.has(normalizeStatusForRegistry(item.status, statusRegistry)));
|
|
1284
|
-
const blockedItems = packedItems.filter((item) => blockedIds.has(item.id.trim().toLowerCase()));
|
|
1285
|
-
const childrenByParent = buildChildrenByParent(allItems);
|
|
1286
|
-
const focusChildrenByParent = contextNeedsAllItems(sectionsIncluded)
|
|
1287
|
-
? childrenByParent
|
|
1288
|
-
: undefined;
|
|
1289
|
-
const fullCorpusById = new Map(fullCorpus.map((item) => [item.id.trim().toLowerCase(), item]));
|
|
1290
|
-
const projectFocusItem = (item) => {
|
|
1291
|
-
const focus = toContextFocusItem(item, statusRegistry, focusChildrenByParent);
|
|
1292
|
-
if (!blockedIds.has(item.id.trim().toLowerCase())) {
|
|
1293
|
-
return focus;
|
|
1294
|
-
}
|
|
1295
|
-
focus.blocked = true;
|
|
1296
|
-
focus.blocker_ids = resolveItemBlockers(item, fullCorpusById, statusRegistry)
|
|
1297
|
-
.filter((blocker) => !blocker.resolved)
|
|
1298
|
-
.map((blocker) => blocker.id);
|
|
1299
|
-
return focus;
|
|
1300
|
-
};
|
|
1301
|
-
const highLevelIds = collectHighLevelContextIds(activeItems, fullCorpus, statusRegistry, typeRegistry);
|
|
1302
|
-
const highLevel = activeItems
|
|
1303
|
-
.filter((item) => highLevelIds.has(item.id))
|
|
1304
|
-
.slice(0, useBoundedPage ? focusPage.length : limit)
|
|
1305
|
-
.map(projectFocusItem);
|
|
1306
|
-
const lowLevel = activeItems
|
|
1307
|
-
.filter((item) => !highLevelIds.has(item.id))
|
|
1308
|
-
.slice(0, useBoundedPage ? focusPage.length : limit)
|
|
1309
|
-
.map(projectFocusItem);
|
|
1310
|
-
const blockedFallbackUsed = rankedActiveItems.length === 0;
|
|
1311
|
-
return {
|
|
1312
|
-
activeItems,
|
|
1313
|
-
blockedItems,
|
|
1314
|
-
highLevel,
|
|
1315
|
-
lowLevel,
|
|
1316
|
-
blockedFallback: blockedFallbackUsed
|
|
1317
|
-
? blockedItems.slice(0, limit).map(projectFocusItem)
|
|
1318
|
-
: [],
|
|
1319
|
-
blockedFallbackUsed,
|
|
1320
|
-
ranking,
|
|
1321
|
-
packing,
|
|
1322
|
-
featureStore,
|
|
1323
|
-
pageExtras: {
|
|
1324
|
-
...(useBoundedPage ? { applied_limit: limit } : {}),
|
|
1325
|
-
...(hasMore ? { has_more: true, truncated: true } : {}),
|
|
1326
|
-
...(nextCursor ? { next_cursor: nextCursor } : {}),
|
|
1327
|
-
},
|
|
1328
|
-
};
|
|
1329
|
-
}
|
|
1330
|
-
function buildContextCursorFingerprint(options) {
|
|
1331
|
-
return createQueryFingerprint("context", selectCursorSemanticOptions(options, CONTEXT_FLAG_CONTRACTS));
|
|
1332
|
-
}
|
|
1333
|
-
function shouldPageContextFocus(options, corpusItemCount) {
|
|
1334
|
-
return (options.limit !== undefined ||
|
|
1335
|
-
options.maxItems !== undefined ||
|
|
1336
|
-
options.after !== undefined ||
|
|
1337
|
-
corpusItemCount >= CONTEXT_SCALE_THRESHOLD);
|
|
1338
|
-
}
|
|
1339
|
-
async function buildContextAgenda(global, runtime, subtreeIds, listedFocusIds) {
|
|
1340
|
-
const calendarOptions = {
|
|
1341
|
-
...runtime.baseListOptions,
|
|
1342
|
-
view: "agenda",
|
|
1343
|
-
include: "all",
|
|
1344
|
-
limit: runtime.parentScope === undefined ? String(runtime.limit) : undefined,
|
|
1345
|
-
};
|
|
1346
|
-
const agenda = await runCalendar(calendarOptions, global);
|
|
1347
|
-
const scopedAgenda = subtreeIds === undefined
|
|
1348
|
-
? agenda.events
|
|
1349
|
-
: agenda.events.filter((event) => subtreeIds.has(event.item_id.trim().toLowerCase()));
|
|
1350
|
-
const fullAgendaEvents = filterTerminalCalendarEvents(scopedAgenda, runtime.statusRegistry).slice(0, runtime.limit);
|
|
1351
|
-
const agendaEvents = fullAgendaEvents.map((event) => {
|
|
1352
|
-
if (!listedFocusIds.has(event.item_id)) {
|
|
1353
|
-
return event;
|
|
1354
|
-
}
|
|
1355
|
-
return {
|
|
1356
|
-
at: event.at,
|
|
1357
|
-
date: event.date,
|
|
1358
|
-
kind: event.kind,
|
|
1359
|
-
item_id: event.item_id,
|
|
1360
|
-
reference_only: true,
|
|
1361
|
-
...(event.kind === "reminder"
|
|
1362
|
-
? { reminder_text: event.reminder_text }
|
|
1363
|
-
: {}),
|
|
1364
|
-
...(event.kind === "event"
|
|
1365
|
-
? {
|
|
1366
|
-
event_title: event.event_title,
|
|
1367
|
-
event_recurring: event.event_recurring,
|
|
1368
|
-
}
|
|
1369
|
-
: {}),
|
|
1370
|
-
};
|
|
1371
|
-
});
|
|
1372
|
-
return {
|
|
1373
|
-
agenda,
|
|
1374
|
-
agendaEvents,
|
|
1375
|
-
agendaSummary: summarizeAgenda(fullAgendaEvents),
|
|
1376
|
-
};
|
|
1377
|
-
}
|
|
1378
|
-
function countContextStatus(items, status, statusRegistry) {
|
|
1379
|
-
const targetStatus = String(status);
|
|
1380
|
-
return items.filter((item) => normalizeStatusForRegistry(item.status, statusRegistry) === targetStatus).length;
|
|
1381
|
-
}
|
|
1382
|
-
async function buildOptionalContextSections(params) {
|
|
1383
|
-
const { runtime, allItems, fullCorpus, blockedIds, focusGroups, now, global, } = params;
|
|
1384
|
-
const childrenByParent = buildChildrenByParent(allItems);
|
|
1385
|
-
const allNonTerminal = allItems.filter((item) => !isTerminalStatus(item.status, runtime.statusRegistry));
|
|
1386
|
-
const has = (section) => runtime.sectionsIncluded.includes(section);
|
|
1387
|
-
return {
|
|
1388
|
-
hierarchy: has("hierarchy")
|
|
1389
|
-
? buildHierarchy(allItems, focusGroups.activeItems, runtime.statusRegistry, runtime.limit)
|
|
1390
|
-
: undefined,
|
|
1391
|
-
activity: has("activity")
|
|
1392
|
-
? await buildActivity(runtime.activityLimit, global)
|
|
1393
|
-
: undefined,
|
|
1394
|
-
progress: has("progress")
|
|
1395
|
-
? buildProgress(allItems, focusGroups.activeItems, blockedIds, runtime.statusRegistry, runtime.limit)
|
|
1396
|
-
: undefined,
|
|
1397
|
-
blockersSection: has("blockers")
|
|
1398
|
-
? buildBlockers(focusGroups.blockedItems, fullCorpus, runtime.statusRegistry, runtime.limit)
|
|
1399
|
-
: undefined,
|
|
1400
|
-
recentlyCreated: has("recently_created")
|
|
1401
|
-
? buildRecentlyCreated(allNonTerminal, runtime.statusRegistry, childrenByParent, runtime.limit)
|
|
1402
|
-
: undefined,
|
|
1403
|
-
unparented: has("unparented")
|
|
1404
|
-
? buildUnparented(allNonTerminal, runtime.statusRegistry, childrenByParent, runtime.limit)
|
|
1405
|
-
: undefined,
|
|
1406
|
-
filesSection: has("files")
|
|
1407
|
-
? buildHotFiles(focusGroups.activeItems, runtime.limit)
|
|
1408
|
-
: undefined,
|
|
1409
|
-
workload: has("workload")
|
|
1410
|
-
? buildWorkload(focusGroups.activeItems, runtime.statusRegistry, runtime.limit)
|
|
1411
|
-
: undefined,
|
|
1412
|
-
staleness: has("staleness")
|
|
1413
|
-
? buildStaleness(allNonTerminal, runtime.staleThresholdDays, now, runtime.limit)
|
|
1414
|
-
: undefined,
|
|
1415
|
-
tests: has("tests") ? buildTestHealth(focusGroups.activeItems) : undefined,
|
|
1416
|
-
};
|
|
1417
|
-
}
|
|
1418
|
-
function buildContextSummaryExtras(allItems, statusRegistry, blockedIds) {
|
|
1419
|
-
const canceledStatus = normalizeStatusInput("canceled", statusRegistry);
|
|
1420
|
-
const activeItems = allItems.filter((item) => statusRegistry.active_statuses.has(normalizeStatusForRegistry(item.status, statusRegistry)));
|
|
1421
|
-
const inProgress = countContextStatus(activeItems, statusRegistry.in_progress_status, statusRegistry);
|
|
1422
|
-
return {
|
|
1423
|
-
scope: "matching_items",
|
|
1424
|
-
active_items: activeItems.length,
|
|
1425
|
-
open: activeItems.length - inProgress,
|
|
1426
|
-
in_progress: inProgress,
|
|
1427
|
-
blocked: allItems.filter((item) => blockedIds.has(item.id.trim().toLowerCase())).length,
|
|
1428
|
-
total_items: allItems.length,
|
|
1429
|
-
closed: allItems.filter((item) => isClosedStatus(item.status, statusRegistry)).length,
|
|
1430
|
-
canceled: allItems.filter((item) => normalizeStatusForRegistry(item.status, statusRegistry) ===
|
|
1431
|
-
canceledStatus).length,
|
|
1432
|
-
};
|
|
1433
|
-
}
|
|
1434
|
-
function attachOptionalContextSections(result, sections, warningDetails) {
|
|
1435
|
-
if (sections.hierarchy)
|
|
1436
|
-
result.hierarchy = sections.hierarchy;
|
|
1437
|
-
if (sections.activity)
|
|
1438
|
-
result.activity = sections.activity;
|
|
1439
|
-
if (sections.progress)
|
|
1440
|
-
result.progress = sections.progress;
|
|
1441
|
-
if (sections.blockersSection)
|
|
1442
|
-
result.blockers = sections.blockersSection;
|
|
1443
|
-
if (sections.recentlyCreated)
|
|
1444
|
-
result.recently_created = sections.recentlyCreated;
|
|
1445
|
-
if (sections.unparented)
|
|
1446
|
-
result.unparented = sections.unparented;
|
|
1447
|
-
if (sections.filesSection)
|
|
1448
|
-
result.files = sections.filesSection;
|
|
1449
|
-
if (sections.workload)
|
|
1450
|
-
result.workload = sections.workload;
|
|
1451
|
-
if (sections.staleness)
|
|
1452
|
-
result.staleness = sections.staleness;
|
|
1453
|
-
if (sections.tests)
|
|
1454
|
-
result.tests = sections.tests;
|
|
1455
|
-
if (warningDetails !== undefined && warningDetails.length > 0) {
|
|
1456
|
-
result.warning_details = warningDetails;
|
|
1457
|
-
}
|
|
1458
|
-
}
|
|
1459
|
-
function applyContextFocusProjection(result, focusFields) {
|
|
1460
|
-
if (!focusFields) {
|
|
1461
|
-
return;
|
|
1462
|
-
}
|
|
1463
|
-
result.focus_fields = focusFields;
|
|
1464
|
-
result.high_level = projectContextFocusRows(result.high_level, focusFields);
|
|
1465
|
-
result.low_level = projectContextFocusRows(result.low_level, focusFields);
|
|
1466
|
-
result.blocked_fallback = projectContextFocusRows(result.blocked_fallback, focusFields);
|
|
1467
|
-
if (result.recently_created) {
|
|
1468
|
-
result.recently_created = projectContextFocusRows(result.recently_created, focusFields);
|
|
1469
|
-
}
|
|
1470
|
-
if (result.unparented) {
|
|
1471
|
-
result.unparented = projectContextFocusRows(result.unparented, focusFields);
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
/**
|
|
1475
|
-
* Remove redundant context tags while preserving their provenance. Identical
|
|
1476
|
-
* child tag sets are folded only when the parent row is present in the same
|
|
1477
|
-
* bounded result, so consumers never need an unavailable follow-up read.
|
|
1478
|
-
* `--no-tags` removes both tag values and inheritance markers.
|
|
1479
|
-
*/
|
|
1480
|
-
export function applyContextTagProjection(result, noTags) {
|
|
1481
|
-
const rows = [
|
|
1482
|
-
result.high_level,
|
|
1483
|
-
result.low_level,
|
|
1484
|
-
result.blocked_fallback,
|
|
1485
|
-
result.recently_created ?? [],
|
|
1486
|
-
result.unparented ?? [],
|
|
1487
|
-
].flat();
|
|
1488
|
-
if (noTags) {
|
|
1489
|
-
for (const row of rows) {
|
|
1490
|
-
delete row.tags;
|
|
1491
|
-
delete row.tags_inherited;
|
|
1492
|
-
}
|
|
1493
|
-
return;
|
|
1494
|
-
}
|
|
1495
|
-
const rowsById = new Map(rows.map((row) => [row.id, row]));
|
|
1496
|
-
const originalTagSignatures = new Map(rows.map((row) => [
|
|
1497
|
-
row.id,
|
|
1498
|
-
row.tags ? [...row.tags].sort().join("\0") : undefined,
|
|
1499
|
-
]));
|
|
1500
|
-
for (const row of rows) {
|
|
1501
|
-
if (!row.parent || !row.tags) {
|
|
1502
|
-
continue;
|
|
1503
|
-
}
|
|
1504
|
-
const parent = rowsById.get(row.parent);
|
|
1505
|
-
if (parent &&
|
|
1506
|
-
originalTagSignatures.get(row.id) === originalTagSignatures.get(parent.id)) {
|
|
1507
|
-
delete row.tags;
|
|
1508
|
-
row.tags_inherited = parent.id;
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
function maybeAttachEmptyContextSuggestions(result, activeItems, blockedItems) {
|
|
1513
|
-
if (activeItems.length > 0 ||
|
|
1514
|
-
blockedItems.length > 0 ||
|
|
1515
|
-
result.agenda.events.length > 0) {
|
|
1516
|
-
return;
|
|
1517
|
-
}
|
|
1518
|
-
result.suggestions = [
|
|
1519
|
-
'pm create --type Task --title "..." to add a new work item',
|
|
1520
|
-
"pm list --status closed --limit 5 to review recent completions",
|
|
1521
|
-
"pm search <keywords> to find related past work",
|
|
1522
|
-
"pm aggregate for a full project status overview",
|
|
1523
|
-
];
|
|
1524
|
-
}
|
|
1525
|
-
async function attachContextUsageFeedback(result, focusGroups, pmRoot, author) {
|
|
1526
|
-
try {
|
|
1527
|
-
const included = new Set(focusGroups.packing.included.map((entry) => entry.id));
|
|
1528
|
-
const rows = focusGroups.ranking.ranked.map((entry) => ({
|
|
1529
|
-
id: entry.id,
|
|
1530
|
-
rank: entry.rank,
|
|
1531
|
-
included: included.has(entry.id),
|
|
1532
|
-
}));
|
|
1533
|
-
const rankedIds = new Set(rows.map((row) => row.id));
|
|
1534
|
-
for (const id of collectContextUsageDeliveredItemIds(result, "context")) {
|
|
1535
|
-
if (!rankedIds.has(id)) {
|
|
1536
|
-
rows.push({ id, rank: rows.length + 1, included: false });
|
|
1537
|
-
rankedIds.add(id);
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
const receipt = await recordContextUsageServing({
|
|
1541
|
-
pmRoot,
|
|
1542
|
-
author,
|
|
1543
|
-
surface: "context",
|
|
1544
|
-
profile: focusGroups.packing.profile,
|
|
1545
|
-
rows,
|
|
1546
|
-
enabled: process.env.PM_CONTEXT_USAGE_DISABLED !== "1",
|
|
1547
|
-
});
|
|
1548
|
-
attachContextUsageServingReceipt(result, receipt);
|
|
1549
|
-
}
|
|
1550
|
-
catch {
|
|
1551
|
-
result.warnings = mergeSortedWarnings(result.warnings, [
|
|
1552
|
-
"context_usage_feedback_write_failed",
|
|
1553
|
-
]);
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
/** Resolve the optional static extension-health projection without module imports. */
|
|
1557
|
-
export async function resolveContextExtensionHealthProjection(omitted, pmRoot, settings) {
|
|
1558
|
-
if (omitted)
|
|
1559
|
-
return {};
|
|
1560
|
-
const discovery = await discoverExtensions({
|
|
1561
|
-
pmRoot,
|
|
1562
|
-
settings,
|
|
1563
|
-
cwd: process.cwd(),
|
|
1564
|
-
noExtensions: false,
|
|
1565
|
-
});
|
|
1566
|
-
const packages = discovery.discovered
|
|
1567
|
-
.map((diagnostic) => ({
|
|
1568
|
-
name: diagnostic.name ?? diagnostic.directory,
|
|
1569
|
-
status: diagnostic.status === "ok" ? "ok" : "degraded",
|
|
1570
|
-
}))
|
|
1571
|
-
.sort((left, right) => left.name.localeCompare(right.name));
|
|
1572
|
-
const degraded = packages.filter((entry) => entry.status === "degraded").length;
|
|
1573
|
-
if (packages.length === 0)
|
|
1574
|
-
return {};
|
|
1575
|
-
return {
|
|
1576
|
-
extension_health: {
|
|
1577
|
-
total: packages.length,
|
|
1578
|
-
ok: packages.length - degraded,
|
|
1579
|
-
degraded,
|
|
1580
|
-
packages: packages.slice(0, 20),
|
|
1581
|
-
...(packages.length > 20 ? { truncated: true } : {}),
|
|
1582
|
-
},
|
|
1583
|
-
};
|
|
1584
|
-
}
|
|
1585
|
-
async function resolveContextSemanticAffinity(pmRoot, author) {
|
|
1586
|
-
const identity = detectAgentIdentity();
|
|
1587
|
-
const semanticState = await readSessionState(pmRoot);
|
|
1588
|
-
const attributionKey = identity.instance || semanticAttributionKey(author);
|
|
1589
|
-
return semanticAttributionAffinity(semanticState.semantic_attribution?.[attributionKey]);
|
|
1590
|
-
}
|
|
1591
|
-
/** Implements run context for the public runtime surface of this module. */
|
|
1592
|
-
export async function runContext(options, global) {
|
|
1593
|
-
const pmRoot = resolvePmRoot(process.cwd(), global.path);
|
|
1594
|
-
const resolvedRuntime = await resolveContextRuntime(options, global);
|
|
1595
|
-
const corpus = await loadContextCorpus(global, resolvedRuntime);
|
|
1596
|
-
const scaledLimit = resolveContextLimitAtScale(resolvedRuntime.limit, corpus.fullCorpus.length);
|
|
1597
|
-
const runtime = {
|
|
1598
|
-
...resolvedRuntime,
|
|
1599
|
-
limit: scaledLimit,
|
|
1600
|
-
tokenBudget: resolveContextTokenBudget(options.tokenBudget, Math.max(256, scaledLimit * 160)),
|
|
1601
|
-
};
|
|
1602
|
-
const author = resolveAuthor(undefined, runtime.settings.author_default);
|
|
1603
|
-
const claimFocus = await resolveContextSemanticAffinity(pmRoot, author);
|
|
1604
|
-
const extensionHealthPromise = resolveContextExtensionHealthProjection(options.noExtensionHealth === true, pmRoot, runtime.settings);
|
|
1605
|
-
// Single shared edge-aware blocked classification (GH-578): resolved against
|
|
1606
|
-
// the full corpus so terminal blocker targets count as satisfied at every depth.
|
|
1607
|
-
const blockedIds = collectDependencyBlockedIds(corpus.fullCorpus, runtime.statusRegistry);
|
|
1608
|
-
const focusGroups = await resolveContextFocusGroups(corpus.listedItemMetadata, corpus.allItems, corpus.fullCorpus, blockedIds, runtime.statusRegistry, runtime.sectionsIncluded, runtime.limit, nowIso(), author, options.after, buildContextCursorFingerprint(options), shouldPageContextFocus(options, corpus.fullCorpus.length), pmRoot, runtime.tokenBudget, claimFocus, resolveItemTypeRegistry(runtime.settings, getActiveExtensionRegistrations()));
|
|
1609
|
-
const agendaContext = await buildContextAgenda(global, runtime, corpus.subtreeIds, new Set([
|
|
1610
|
-
...focusGroups.highLevel,
|
|
1611
|
-
...focusGroups.lowLevel,
|
|
1612
|
-
...focusGroups.blockedFallback,
|
|
1613
|
-
].map((item) => item.id)));
|
|
1614
|
-
const workspaceMemory = await readWorkspaceMemory(corpus.fullCorpus, {
|
|
1615
|
-
pmRoot,
|
|
1616
|
-
statusRegistry: runtime.statusRegistry,
|
|
1617
|
-
now: agendaContext.agenda.now,
|
|
1618
|
-
});
|
|
1619
|
-
const warnings = mergeSortedWarnings(corpus.listed.warnings, agendaContext.agenda.warnings, focusGroups.ranking.warnings, [...focusGroups.featureStore.warnings], workspaceMemory.warnings);
|
|
1620
|
-
const inProgressCount = countContextStatus(focusGroups.activeItems, runtime.statusRegistry.in_progress_status, runtime.statusRegistry);
|
|
1621
|
-
const openCount = focusGroups.activeItems.length - inProgressCount;
|
|
1622
|
-
const sections = await buildOptionalContextSections({
|
|
1623
|
-
runtime,
|
|
1624
|
-
allItems: corpus.allItems,
|
|
1625
|
-
fullCorpus: corpus.fullCorpus,
|
|
1626
|
-
blockedIds,
|
|
1627
|
-
focusGroups,
|
|
1628
|
-
now: agendaContext.agenda.now,
|
|
1629
|
-
global,
|
|
1630
|
-
});
|
|
1631
|
-
const summaryExtras = buildContextSummaryExtras(corpus.allItems, runtime.statusRegistry, blockedIds);
|
|
1632
|
-
const result = {
|
|
1633
|
-
output_default: "toon",
|
|
1634
|
-
now: agendaContext.agenda.now,
|
|
1635
|
-
depth: runtime.depth,
|
|
1636
|
-
sections_included: runtime.sectionsIncluded,
|
|
1637
|
-
window: {
|
|
1638
|
-
anchor: agendaContext.agenda.anchor,
|
|
1639
|
-
start: agendaContext.agenda.range.start,
|
|
1640
|
-
end: agendaContext.agenda.range.end,
|
|
1641
|
-
past: agendaContext.agenda.range.past,
|
|
1642
|
-
from: agendaContext.agenda.range.from,
|
|
1643
|
-
to: agendaContext.agenda.range.to,
|
|
1644
|
-
},
|
|
1645
|
-
filters: {
|
|
1646
|
-
type: options.type ?? null,
|
|
1647
|
-
tag: options.tag ?? null,
|
|
1648
|
-
priority: options.priority ?? null,
|
|
1649
|
-
assignee: options.assignee ?? null,
|
|
1650
|
-
assignee_filter: options.assigneeFilter ?? null,
|
|
1651
|
-
sprint: options.sprint ?? null,
|
|
1652
|
-
release: options.release ?? null,
|
|
1653
|
-
limit: options.limit ?? options.maxItems ?? null,
|
|
1654
|
-
parent: runtime.parentScope ?? null,
|
|
1655
|
-
token_budget: runtime.tokenBudget,
|
|
1656
|
-
/* c8 ignore next -- listed/calendar runtime filters are always materialized by their command handlers */
|
|
1657
|
-
runtime_filters: (corpus.listed.filters.runtime_filters ??
|
|
1658
|
-
agendaContext.agenda.filters.runtime_filters ??
|
|
1659
|
-
{}),
|
|
1660
|
-
},
|
|
1661
|
-
summary: {
|
|
1662
|
-
returned_focus: {
|
|
1663
|
-
active_items: focusGroups.activeItems.length,
|
|
1664
|
-
in_progress: inProgressCount,
|
|
1665
|
-
open: openCount,
|
|
1666
|
-
blocked: focusGroups.blockedItems.length,
|
|
1667
|
-
},
|
|
1668
|
-
blocked_fallback_used: focusGroups.blockedFallbackUsed,
|
|
1669
|
-
high_level: focusGroups.highLevel.length,
|
|
1670
|
-
low_level: focusGroups.lowLevel.length,
|
|
1671
|
-
agenda_events: agendaContext.agendaSummary.events,
|
|
1672
|
-
...summaryExtras,
|
|
1673
|
-
},
|
|
1674
|
-
high_level: focusGroups.highLevel,
|
|
1675
|
-
low_level: focusGroups.lowLevel,
|
|
1676
|
-
blocked_fallback: focusGroups.blockedFallback,
|
|
1677
|
-
agenda: {
|
|
1678
|
-
summary: agendaContext.agendaSummary,
|
|
1679
|
-
events: agendaContext.agendaEvents,
|
|
1680
|
-
},
|
|
1681
|
-
...focusGroups.pageExtras,
|
|
1682
|
-
};
|
|
1683
|
-
result.workspace_memory = selectWorkspaceMemory(workspaceMemory, Math.min(800, Math.floor(runtime.tokenBudget * 0.2)));
|
|
1684
|
-
Object.assign(result, await extensionHealthPromise);
|
|
1685
|
-
attachOptionalContextSections(result, sections, focusGroups.featureStore.warning_details);
|
|
1686
|
-
applyContextTagProjection(result, options.noTags === true);
|
|
1687
|
-
if (options.explainRanking === true) {
|
|
1688
|
-
result.ranking = toContextRankingSummary(focusGroups.ranking, focusGroups.featureStore, new Set([
|
|
1689
|
-
...focusGroups.highLevel,
|
|
1690
|
-
...focusGroups.lowLevel,
|
|
1691
|
-
...focusGroups.blockedFallback,
|
|
1692
|
-
].map((entry) => entry.id)));
|
|
1693
|
-
result.packing = toContextPackingSummary(focusGroups.packing);
|
|
1694
|
-
}
|
|
1695
|
-
applyContextFocusProjection(result, runtime.focusFields);
|
|
1696
|
-
if (warnings.length > 0)
|
|
1697
|
-
result.warnings = warnings;
|
|
1698
|
-
maybeAttachEmptyContextSuggestions(result, focusGroups.activeItems, focusGroups.blockedItems);
|
|
1699
|
-
await attachContextUsageFeedback(result, focusGroups, pmRoot, author);
|
|
1700
|
-
return result;
|
|
1701
|
-
}
|
|
1
|
+
!(function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=new e.Error().stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ec0faae4-1fdb-5c7c-bcf1-eb338d73958a")}catch(e2){}})();import{SETTINGS_DEFAULTS,EXIT_CODE,PmCliError,compareTimestampStrings,nowIso,isTerminalStatus,normalizeStatusForRegistry,normalizeStatusInput,resolveRuntimeStatusRegistry,resolvePmRoot,readSettings,resolveAuthor,discoverExtensions,resolveItemTypeRegistry}from"../runtime-primitives.js";import{getActiveExtensionRegistrations}from"../../core/extensions/index.js";import{collectDependencyBlockedIds,resolveItemBlockers,resolveItemExecutionRole,computeActionabilityReport}from"../actionability.js";import{CONTEXT_DEPTH_VALUES,CONTEXT_SECTION_VALUES}from"../../types/index.js";import{parseIntegerLimit}from"./parsers.js";import{runCalendar}from"./calendar.js";import{runList}from"./list.js";import{runActivity}from"./activity.js";import{scoreContextCandidatesWithActiveExtensions}from"../context-relevance.js";import{buildItemContextRelevanceCandidates}from"../context-relevance.js";import{createQueryFingerprint,encodeQueryCursor,resolveQueryCursorStart,selectCursorSemanticOptions}from"../pagination.js";import{CONTEXT_FLAG_CONTRACTS}from"../cli-contracts/flag-contracts.js";import{packContextCandidates}from"../context-packing.js";import{attachContextUsageServingReceipt,collectContextUsageDeliveredItemIds,readContextUsageAffinity,recordContextUsageServing}from"../context-usage.js";import{readWorkspaceContextSignals}from"../context-signal-store.js";import{readWorkspaceMemory,selectWorkspaceMemory}from"../workspace-memory.js";import{detectAgentIdentity}from"../../core/shared/author.js";import{readSessionState,semanticAttributionKey}from"../../core/session/session-state.js";import{semanticAttributionAffinity}from"../context/semantic-session-attribution.js";const CONTEXT_OUTPUT_VALUES=["markdown","toon","json","ndjson"];const HIGH_LEVEL_TYPES=new Set(["Epic","Feature"]);const DEFAULT_CONTEXT_LIMIT=10;const CONTEXT_SCALE_THRESHOLD=1e4;const STANDARD_SECTIONS=["hierarchy","activity","progress","recently_created","unparented","workload"];const DEEP_SECTIONS=[...STANDARD_SECTIONS,"blockers","files","staleness","tests"];const LEADING_HYPHEN_DATE=/^(\d{4})-(\d{2})-(\d{2})/;const LEADING_COMPACT_DATE=/^(\d{4})(\d{2})(\d{2})(?:[T ]?\d{2}|$)/;const COMPACT_DATE=/^(\d{4})(\d{2})(\d{2})$/;const COMPACT_DATETIME=/^(\d{4})(\d{2})(\d{2})(?:[T\s]?)(\d{2})(\d{2})(\d{2})?(Z|[+-]\d{2}:?\d{2})?$/i;function parseOutputFormat(raw){if(!raw)return void 0;const normalized=raw.trim().toLowerCase();if(!CONTEXT_OUTPUT_VALUES.includes(normalized)){throw new PmCliError("Context format must be one of markdown|toon|json|ndjson",EXIT_CODE.USAGE)}return normalized}const FOCUS_PROJECTION_FIELDS=new Set(["id","title","type","status","priority","order","deadline","assignee","tags","tags_inherited","updated_at","parent","children_total","children_closed","completion_pct","blocked","blocker_ids","created_at"]);function parseContextFocusFields(raw){if(raw===void 0)return void 0;const fields=[];for(const token of raw.split(",")){const normalized=token.trim().toLowerCase();if(normalized.length===0)continue;if(!FOCUS_PROJECTION_FIELDS.has(normalized)){throw new PmCliError(`Context --fields value not projectable: ${normalized} (valid: ${[...FOCUS_PROJECTION_FIELDS].join(", ")})`,EXIT_CODE.USAGE)}if(!fields.includes(normalized))fields.push(normalized)}if(fields.length===0){throw new PmCliError("Context --fields requires at least one field name",EXIT_CODE.USAGE)}return fields}function projectContextFocusRows(rows,fields){return rows.map(row=>{const projected={};const source=row;for(const field of fields){projected[field]=source[field]??null}return projected})}function resolveContextOutputFormat(options,global2){const commandFormat=parseOutputFormat(options.format);if(global2.json&&commandFormat&&commandFormat!=="json"&&commandFormat!=="ndjson"){throw new PmCliError("Cannot combine --json with --format markdown|toon",EXIT_CODE.USAGE)}if(global2.json){return commandFormat==="ndjson"?"ndjson":"json"}return commandFormat??"toon"}function parseContextLimit(raw,depth){return parseIntegerLimit(raw,"--limit")??(depth==="full"?Number.MAX_SAFE_INTEGER:DEFAULT_CONTEXT_LIMIT)}function resolveContextTokenBudget(raw,fallback){if(raw===void 0)return fallback;const parsed=typeof raw==="number"?raw:Number(raw);if(!Number.isInteger(parsed)||parsed<1){throw new PmCliError("--token-budget must be a positive integer",EXIT_CODE.USAGE)}return parsed}function resolveContextLimitAtScale(limit,itemCount){return itemCount>=CONTEXT_SCALE_THRESHOLD&&limit===Number.MAX_SAFE_INTEGER?DEFAULT_CONTEXT_LIMIT:limit}function parseContextDepth(raw,settings){if(!raw)return settings.default_depth;const normalized=raw.trim().toLowerCase();if(!CONTEXT_DEPTH_VALUES.includes(normalized)){throw new PmCliError(`Context --depth must be one of ${CONTEXT_DEPTH_VALUES.join("|")}`,EXIT_CODE.USAGE)}return normalized}function parseContextSections(raw,depth,settings){if(raw&&raw.length>0){const sections=[];for(const value of raw){const normalized=value.trim().toLowerCase();if(!CONTEXT_SECTION_VALUES.includes(normalized)){throw new PmCliError(`Context --section must be one of ${CONTEXT_SECTION_VALUES.join("|")}`,EXIT_CODE.USAGE)}if(!sections.includes(normalized)){sections.push(normalized)}}return sections}if(depth==="brief")return[];if(depth==="full")return[...CONTEXT_SECTION_VALUES];const pool=depth==="deep"?DEEP_SECTIONS:STANDARD_SECTIONS;return pool.filter(section=>settings.sections[section])}function parseActivityLimit(raw,settings){if(!raw)return settings.activity_limit;const parsed=parseIntegerLimit(raw,"--activity-limit");return parsed??settings.activity_limit}function parseStaleThresholdDays(raw,settings){if(!raw)return settings.stale_threshold_days;const trimmed=raw.trim().toLowerCase();const match=/^(\d+)d?$/.exec(trimmed);if(!match){throw new PmCliError("--stale-threshold must be a number of days (e.g. 7 or 7d)",EXIT_CODE.USAGE)}const days=parseInt(match[1],10);if(days<=0){throw new PmCliError("--stale-threshold must be positive",EXIT_CODE.USAGE)}return days}function statusRank(status,statusRegistry){const normalizedStatus=normalizeStatusForRegistry(status,statusRegistry);const blockedStatus=normalizeStatusInput("blocked",statusRegistry);const draftStatus=normalizeStatusInput("draft",statusRegistry);if(normalizedStatus===statusRegistry.in_progress_status)return 0;if(statusRegistry.active_statuses.has(normalizedStatus))return 1;if(blockedStatus&&normalizedStatus===blockedStatus)return 2;if(draftStatus&&normalizedStatus===draftStatus)return 3;if(statusRegistry.blocked_statuses.has(normalizedStatus))return 5;if(statusRegistry.terminal_statuses.has(normalizedStatus))return 7;return 6}function isClosedStatus(status,statusRegistry){const closeStatus=normalizeStatusInput("closed",statusRegistry);return normalizeStatusForRegistry(status,statusRegistry)===closeStatus}function isInProgressStatus(status,statusRegistry){return normalizeStatusForRegistry(status,statusRegistry)===statusRegistry.in_progress_status}function isOpenStatus(status,statusRegistry){const normalizedStatus=normalizeStatusForRegistry(status,statusRegistry);return normalizedStatus!==statusRegistry.in_progress_status&&statusRegistry.active_statuses.has(normalizedStatus)}function isBlockedStatus(status,statusRegistry){return statusRegistry.blocked_statuses.has(normalizeStatusForRegistry(status,statusRegistry))}const LIST_PROJECTION_FLAGS=["compact","brief","fields","includeBody","include_body","parent"];function stripListProjectionFlags(options){const copy={...options};for(const key of LIST_PROJECTION_FLAGS){delete copy[key]}return copy}function collectSubtreeIds(corpus,parentId){const target=parentId.trim().toLowerCase();const anchor=corpus.find(item=>item.id.trim().toLowerCase()===target);if(!anchor){return{ids:new Set,found:false}}const childrenByParent=buildChildrenByParent(corpus);const descendants=collectDescendants(anchor.id,childrenByParent);const ids=new Set([anchor.id,...descendants.map(item=>item.id)].map(id=>id.trim().toLowerCase()));return{ids,found:true}}function parseContextParent(raw){if(raw===void 0)return void 0;const trimmed=raw.trim();if(trimmed.length===0){throw new PmCliError("Context --parent requires an item id",EXIT_CODE.USAGE)}return trimmed}function compareOptionalOrder(left,right){const leftValue=left??null;const rightValue=right??null;if(leftValue===null&&rightValue===null)return 0;if(leftValue===null)return 1;if(rightValue===null)return-1;return leftValue-rightValue}function compareOptionalDeadline(left,right){const leftValue=left??null;const rightValue=right??null;if(leftValue===null&&rightValue===null)return 0;if(leftValue===null)return 1;if(rightValue===null)return-1;return compareTimestampStrings(leftValue,rightValue)}function daysInUtcMonth(year,monthIndex){return new Date(Date.UTC(year,monthIndex+1,0)).getUTCDate()}function hasNoInvalidDatePrefix(value){const match=LEADING_HYPHEN_DATE.exec(value)??LEADING_COMPACT_DATE.exec(value);if(!match){return true}const year=Number.parseInt(match[1],10);const month=Number.parseInt(match[2],10);const day=Number.parseInt(match[3],10);return month>=1&&month<=12&&day>=1&&day<=daysInUtcMonth(year,month-1)}function normalizeTimestampCandidate(value){const compactDate=COMPACT_DATE.exec(value);if(compactDate){return`${compactDate[1]}-${compactDate[2]}-${compactDate[3]}`}const compactDateTime=COMPACT_DATETIME.exec(value);if(compactDateTime){const[,year,month,day,hour,minute,secondRaw,offsetRaw]=compactDateTime;const second=secondRaw?`:${secondRaw}`:"";const offset=offsetRaw&&offsetRaw.length===5&&offsetRaw!=="Z"?`${offsetRaw.slice(0,3)}:${offsetRaw.slice(3)}`:offsetRaw??"";return`${year}-${month}-${day}T${hour}:${minute}${second}${offset}`}return value}function parseContextTimestampMs(value){if(typeof value!=="string"){return Number.NaN}const trimmed=value.trim();if(trimmed.length===0||!hasNoInvalidDatePrefix(trimmed)){return Number.NaN}const parsed=Date.parse(normalizeTimestampCandidate(trimmed));return Number.isFinite(parsed)?parsed:Number.NaN}function sortableTimestamp(value){const parsed=parseContextTimestampMs(value);return Number.isFinite(parsed)?new Date(parsed).toISOString():""}function dateTokenForTimestamp(value){const parsed=parseContextTimestampMs(value);return Number.isFinite(parsed)?new Date(parsed).toISOString().slice(0,10):"unknown"}function normalizedParentId(value){if(typeof value!=="string"){return null}const trimmed=value.trim();return trimmed.length>0?trimmed:null}function compareCriticalItems(left,right,statusRegistry){const byStatus=statusRank(left.status,statusRegistry)-statusRank(right.status,statusRegistry);if(byStatus!==0)return byStatus;const byPriority=left.priority-right.priority;if(byPriority!==0)return byPriority;const byOrder=compareOptionalOrder(left.order,right.order);if(byOrder!==0)return byOrder;const byDeadline=compareOptionalDeadline(left.deadline,right.deadline);if(byDeadline!==0)return byDeadline;const byUpdated=compareTimestampStrings(sortableTimestamp(right.updated_at),sortableTimestamp(left.updated_at));const byId=left.id.localeCompare(right.id);return byUpdated!==0?byUpdated:byId}function estimateJsonTokens(value){return Math.max(1,Math.ceil(Buffer.byteLength(JSON.stringify(value),"utf8")/4))}function packRankedContextItems(report,tokenBudget,requiredIds=new Set,profile="context"){return packContextCandidates(report.ranked.map(entry=>({id:entry.id,item:entry.item,rank:entry.rank,score:entry.score,required:requiredIds.has(entry.id),cluster:entry.item.parent?.trim()||entry.item.type,token_costs:{identity:estimateJsonTokens({id:entry.id,title:entry.item.title}),summary:estimateJsonTokens({id:entry.id,title:entry.item.title,type:entry.item.type,status:entry.item.status,priority:entry.item.priority,parent:entry.item.parent}),full:estimateJsonTokens(entry.item)}})),{tokenBudget,profile,latencyBudgetMs:25})}function toContextPackingSummary(report){return{...report,included:report.included.map(entry=>({id:entry.id,projection:entry.projection,tokens:entry.tokens}))}}function toContextRankingSummary(report,featureStore,servedItemIds){const ranked=servedItemIds===void 0?report.ranked:report.ranked.filter(entry=>servedItemIds.has(entry.id));return{model:report.model,available_signals:report.available_signals,items:ranked.map(entry=>({id:entry.id,rank:entry.rank,baseline_rank:entry.baseline_rank,score:entry.score,contributions:entry.contributions,...entry.signal_provenance===void 0?{}:{recency:entry.signal_provenance.recency}})),candidate_count:report.ranked.length,omitted_count:report.ranked.length-ranked.length,...featureStore?{feature_store:{source:featureStore.snapshot.source,cache_status:featureStore.cache_status,source_cursor:featureStore.snapshot.source_cursor,generated_at:featureStore.snapshot.generated_at}}:{}}}function completionPct(closed,total){return total>0?Math.round(closed/total*100):0}function buildChildrenByParent(allItems){const childrenByParent=new Map;for(const item of allItems){const parent=normalizedParentId(item.parent);if(!parent)continue;const children=childrenByParent.get(parent)??[];children.push(item);childrenByParent.set(parent,children)}return childrenByParent}function toContextFocusItem(item,statusRegistry,childrenByParent){const focus={id:item.id,title:item.title,type:item.type,status:item.status,priority:item.priority,order:item.order??null,deadline:item.deadline??null,assignee:item.assignee??null,tags:Array.isArray(item.tags)?[...item.tags]:[],updated_at:item.updated_at,parent:normalizedParentId(item.parent)};if(statusRegistry&&childrenByParent){const descendants=collectDescendants(item.id,childrenByParent);const total=descendants.length;if(total>0){const closed=descendants.filter(desc=>isClosedStatus(desc.status,statusRegistry)).length;focus.children_total=total;focus.children_closed=closed;focus.completion_pct=completionPct(closed,total)}}return focus}function summarizeAgenda(events){let deadlines=0;let reminders=0;let scheduled=0;const itemIds=new Set;for(const event of events){itemIds.add(event.item_id);if(event.kind==="deadline"){deadlines+=1;continue}if(event.kind==="reminder"){reminders+=1;continue}scheduled+=1}return{events:events.length,items:itemIds.size,deadlines,reminders,scheduled}}function mergeSortedWarnings(...warningGroups){return[...new Set(warningGroups.flatMap(group=>group??[]))].sort((left,right)=>left.localeCompare(right))}function filterTerminalCalendarEvents(events,statusRegistry){return events.filter(event=>!statusRegistry.terminal_statuses.has(normalizeStatusForRegistry(event.item_status,statusRegistry)))}function buildHierarchy(allItems,activeItems,statusRegistry,limit){const itemMap=new Map;for(const item of allItems){itemMap.set(item.id,item)}const childrenByParent=buildChildrenByParent(allItems);const activeHighLevelIds=new Set(activeItems.filter(item=>HIGH_LEVEL_TYPES.has(item.type)).map(item=>item.id));const nodes=[];for(const parentId of activeHighLevelIds){const parent=itemMap.get(parentId);if(!parent)continue;const allDescendants=collectDescendants(parentId,childrenByParent);const childItems=childrenByParent.get(parentId)??[];let closedCount=0;let openCount=0;let inProgressCount=0;let blockedCount=0;for(const desc of allDescendants){if(isClosedStatus(desc.status,statusRegistry))closedCount++;else if(isInProgressStatus(desc.status,statusRegistry))inProgressCount++;else if(isBlockedStatus(desc.status,statusRegistry))blockedCount++;else if(isOpenStatus(desc.status,statusRegistry))openCount++}const children=childItems.sort((a,b)=>compareCriticalItems(a,b,statusRegistry)).slice(0,limit).map(child=>{const grandchildren=collectDescendants(child.id,childrenByParent);const gcClosed=grandchildren.filter(gc=>isClosedStatus(gc.status,statusRegistry)).length;return{id:child.id,title:child.title,type:child.type,status:child.status,children_total:grandchildren.length,children_closed:gcClosed}});nodes.push({id:parent.id,title:parent.title,type:parent.type,status:parent.status,children_total:allDescendants.length,children_closed:closedCount,children_open:openCount,children_in_progress:inProgressCount,children_blocked:blockedCount,children})}return nodes.sort((a,b)=>{const aParent=itemMap.get(a.id);const bParent=itemMap.get(b.id);return compareCriticalItems(aParent,bParent,statusRegistry)}).slice(0,limit)}function collectDescendants(parentId,childrenByParent){const result=[];const stack=[parentId];const visited=new Set;while(stack.length>0){const current=stack.pop();if(visited.has(current))continue;visited.add(current);const children=childrenByParent.get(current)??[];for(const child of children){result.push(child);stack.push(child.id)}}return result}const _testOnly={buildActivity,buildBlockers,buildChildrenByParent,buildHierarchy,buildHotFiles,buildProgress,buildRecentlyCreated,buildStaleness,buildTestHealth,buildUnparented,buildWorkload,collectDescendants,collectSubtreeIds,compareCriticalItems,compareOptionalDeadline,compareOptionalOrder,completionPct,dateTokenForTimestamp,filterTerminalCalendarEvents,formatAgendaLine,isBlockedStatus,isClosedStatus,isInProgressStatus,isOpenStatus,mergeSortedWarnings,normalizedParentId,parseActivityLimit,parseContextLimit,resolveContextLimitAtScale,parseContextParent,parseContextTimestampMs,parseStaleThresholdDays,sortableTimestamp,statusRank,stripListProjectionFlags,summarizeAgenda,toContextFocusItem};async function buildActivity(activityLimit,global2){const result=await runActivity({compact:true,limit:String(activityLimit)},global2);return result.compact_activity??[]}function buildProgress(allItems,activeItems,blockedIds,statusRegistry,limit){const childrenByParent=new Map;for(const item of allItems){if(!item.parent)continue;const children=childrenByParent.get(item.parent)??[];children.push(item);childrenByParent.set(item.parent,children)}const activeHighLevel=activeItems.filter(item=>HIGH_LEVEL_TYPES.has(item.type));const entries=[];for(const parent of activeHighLevel){const descendants=collectDescendants(parent.id,childrenByParent);const total=descendants.length;if(total===0)continue;let closed=0;let open=0;let inProgress=0;let blocked=0;for(const desc of descendants){if(isClosedStatus(desc.status,statusRegistry))closed++;else if(blockedIds.has(desc.id.trim().toLowerCase()))blocked++;else if(isInProgressStatus(desc.status,statusRegistry))inProgress++;else if(isOpenStatus(desc.status,statusRegistry))open++}entries.push({id:parent.id,title:parent.title,type:parent.type,total,closed,open,in_progress:inProgress,blocked,completion_pct:completionPct(closed,total)})}return entries.sort((a,b)=>a.completion_pct-b.completion_pct).slice(0,limit)}function buildBlockers(blockedItems,fullCorpus,statusRegistry,limit){const itemsById=new Map(fullCorpus.map(item=>[item.id.trim().toLowerCase(),item]));return blockedItems.slice(0,limit).map(item=>{const blockers=resolveItemBlockers(item,itemsById,statusRegistry);const blockedBy=blockers.find(blocker=>!blocker.resolved)?.id??blockers[0]?.id??null;const blockerItem=blockedBy?itemsById.get(blockedBy.trim().toLowerCase()):void 0;return{id:item.id,title:item.title,blocked_by:blockedBy,blocked_by_title:blockerItem?.title??null,blocked_by_status:blockerItem?.status??null,blocked_reason:item.blocked_reason??null,unblock_note:item.unblock_note??null}})}function buildHotFiles(activeItems,limit){const fileMap=new Map;for(const item of activeItems){for(const file of item.files??[]){const existing=fileMap.get(file.path)??new Set;existing.add(item.id);fileMap.set(file.path,existing)}}return[...fileMap.entries()].map(([filePath,itemIds])=>({path:filePath,references:itemIds.size,items:[...itemIds].sort()})).sort((a,b)=>b.references-a.references).slice(0,limit)}function buildWorkload(activeItems,statusRegistry,limit){const groups=new Map;for(const item of activeItems){const key=item.assignee??null;const existing=groups.get(key)??[];existing.push(item);groups.set(key,existing)}return[...groups.entries()].map(([assignee,items])=>({assignee,active:items.length,in_progress:items.filter(item=>isInProgressStatus(item.status,statusRegistry)).length,items:items.map(item=>item.id).sort()})).sort((a,b)=>b.active-a.active).slice(0,limit)}function buildStaleness(allNonTerminal,staleThresholdDays,now,limit){const cutoffMs=new Date(now).getTime()-staleThresholdDays*24*60*60*1e3;return allNonTerminal.filter(item=>new Date(item.updated_at).getTime()<cutoffMs).map(item=>({id:item.id,title:item.title,status:item.status,updated_at:item.updated_at,stale_days:Math.floor((new Date(now).getTime()-new Date(item.updated_at).getTime())/(24*60*60*1e3))})).sort((a,b)=>b.stale_days-a.stale_days).slice(0,limit)}function buildRecentlyCreated(allNonTerminal,statusRegistry,childrenByParent,limit){return allNonTerminal.map(item=>({item,sortKey:sortableTimestamp(item.created_at)})).sort((left,right)=>{const byCreated=compareTimestampStrings(right.sortKey,left.sortKey);return byCreated!==0?byCreated:left.item.id.localeCompare(right.item.id)}).slice(0,limit).map(({item})=>({...toContextFocusItem(item,statusRegistry,childrenByParent),created_at:item.created_at}))}function buildUnparented(allNonTerminal,statusRegistry,childrenByParent,limit){return allNonTerminal.filter(item=>!normalizedParentId(item.parent)&&!HIGH_LEVEL_TYPES.has(item.type)).sort((left,right)=>compareCriticalItems(left,right,statusRegistry)).slice(0,limit).map(item=>toContextFocusItem(item,statusRegistry,childrenByParent))}function buildTestHealth(activeItems){let itemsWithTests=0;let itemsWithRecentRuns=0;let passed=0;let failed=0;let skipped=0;const itemsFailing=[];for(const item of activeItems){if((item.tests??[]).length>0){itemsWithTests++}const runs=item.test_runs??[];if(runs.length>0){itemsWithRecentRuns++;let itemHasFailure=false;for(const run of runs){passed+=run.passed??0;failed+=run.failed??0;skipped+=run.skipped??0;if((run.failed??0)>0){itemHasFailure=true}}if(itemHasFailure){itemsFailing.push(item.id)}}}return{items_with_tests:itemsWithTests,items_with_recent_runs:itemsWithRecentRuns,recent_runs:{passed,failed,skipped},items_failing:itemsFailing.sort()}}function formatClock(timestamp){return`${new Date(timestamp).toISOString().slice(11,16)}Z`}function formatFocusLine(item){const orderToken=item.order===null?"-":String(item.order);const deadlineToken=item.deadline??"-";const parentToken=item.parent?` parent:${item.parent}`:"";const progressToken=item.children_total!==void 0&&item.children_closed!==void 0&&item.completion_pct!==void 0?` children:${item.children_closed}/${item.children_total} done:${item.completion_pct}%`:"";const blockedToken=item.blocked===true?` blocked:${item.blocker_ids?.join(",")||"lifecycle"}`:"";return`${item.id} p${item.priority} ${item.status} ${item.type} order:${orderToken} deadline:${deadlineToken}${parentToken}${progressToken}${blockedToken} ${item.title}`}function formatProjectedFocusLine(item,fields){const source=item;return fields.map(field=>{const value=source[field];if(Array.isArray(value))return`${field}:${value.join(",")}`;return`${field}:${value??"-"}`}).join(" ")}function formatAgendaLine(event){if("reference_only"in event){const suffix=event.kind==="reminder"?` \u2014 ${event.reminder_text??"reminder"}`:event.kind==="event"?` \u2014 ${event.event_title??"event"}${event.event_recurring?" (recurring)":""}`:"";return`${formatClock(event.at)} [${event.kind}] ${event.item_id} (see focus item)${suffix}`}const base=`${formatClock(event.at)} [${event.kind}] ${event.item_id} p${event.item_priority} ${event.item_status} ${event.item_title}`;if(event.kind==="reminder"){return`${base} \u2014 ${event.reminder_text}`}if(event.kind==="event"){const recurringSuffix=event.event_recurring?" (recurring)":"";const title=event.event_title??event.item_title;return`${base} \u2014 ${title}${recurringSuffix}`}return base}function pushContextFocusSection(lines,title,rows,emptyText,renderFocus){lines.push(`## ${title}`);if(rows.length===0){lines.push(emptyText)}else{for(const item of rows){lines.push(`- ${renderFocus(item)}`)}}lines.push("")}function pushBlockedFallbackSection(lines,result,renderFocus){if(result.blocked_fallback.length===0){return}lines.push("## Blocked fallback");for(const item of result.blocked_fallback){lines.push(`- ${renderFocus(item)}`)}lines.push("")}function pushAgendaSection(lines,result){lines.push("## Agenda");lines.push(`- events: ${result.agenda.summary.events} (deadlines: ${result.agenda.summary.deadlines}, reminders: ${result.agenda.summary.reminders}, scheduled: ${result.agenda.summary.scheduled})`);if(result.agenda.events.length===0){lines.push("No agenda events matched the selected filters.")}else{for(const event of result.agenda.events){lines.push(`- ${formatAgendaLine(event)}`)}}lines.push("")}function pushHierarchySection(lines,result){if(!result.hierarchy||result.hierarchy.length===0){return}lines.push("## Hierarchy");for(const node of result.hierarchy){const pct=node.children_total>0?Math.round(node.children_closed/node.children_total*100):0;lines.push(`- ${node.id} ${node.type} ${node.status} "${node.title}" [${node.children_closed}/${node.children_total} done ${pct}%]`);for(const child of node.children){const cpct=child.children_total>0?Math.round(child.children_closed/child.children_total*100):0;lines.push(` - ${child.id} ${child.type} ${child.status} "${child.title}" [${child.children_closed}/${child.children_total} done ${cpct}%]`)}}lines.push("")}function pushProgressSection(lines,result){if(!result.progress||result.progress.length===0){return}lines.push("## Progress");for(const entry of result.progress){lines.push(`- ${entry.id} "${entry.title}" ${entry.completion_pct}% (${entry.closed}/${entry.total} closed, ${entry.in_progress} wip, ${entry.open} open, ${entry.blocked} blocked)`)}lines.push("")}function pushRecentlyCreatedSection(lines,result,focusFields,renderFocus){if(!result.recently_created||result.recently_created.length===0){return}lines.push("## Recently created");for(const item of result.recently_created){lines.push(focusFields?`- ${renderFocus(item)}`:`- ${dateTokenForTimestamp(item.created_at)} ${formatFocusLine(item)}`)}lines.push("")}function pushUnparentedSection(lines,result,renderFocus){if(!result.unparented||result.unparented.length===0){return}lines.push("## Unparented");for(const item of result.unparented){lines.push(`- ${renderFocus(item)}`)}lines.push("")}function pushActivitySection(lines,result){if(!result.activity||result.activity.length===0){return}lines.push("## Recent activity");for(const entry of result.activity){const msg=entry.msg?` ${entry.msg}`:"";lines.push(`- ${entry.ts.slice(0,16)}Z ${entry.id} ${entry.op} by:${entry.author}${msg}`)}lines.push("")}function pushBlockersSection(lines,result){if(!result.blockers||result.blockers.length===0){return}lines.push("## Blockers");for(const entry of result.blockers){const by=entry.blocked_by?`blocked_by:${entry.blocked_by}(${entry.blocked_by_status??"?"})`:"blocked_by:-";const reason=entry.blocked_reason?` reason:"${entry.blocked_reason}"`:"";const note=entry.unblock_note?` unblock:"${entry.unblock_note}"`:"";lines.push(`- ${entry.id} "${entry.title}" ${by}${reason}${note}`)}lines.push("")}function pushFilesSection(lines,result){if(!result.files||result.files.length===0){return}lines.push("## Hot files");for(const file of result.files){lines.push(`- ${file.path} refs:${file.references} items:[${file.items.join(",")}]`)}lines.push("")}function pushWorkloadSection(lines,result){if(!result.workload||result.workload.length===0){return}lines.push("## Workload");for(const entry of result.workload){const who=entry.assignee??"(unassigned)";lines.push(`- ${who} active:${entry.active} wip:${entry.in_progress} items:[${entry.items.join(",")}]`)}lines.push("")}function pushStalenessSection(lines,result){if(!result.staleness||result.staleness.length===0){return}lines.push("## Stale items");for(const entry of result.staleness){lines.push(`- ${entry.id} ${entry.status} stale:${entry.stale_days}d last:${entry.updated_at.slice(0,10)} "${entry.title}"`)}lines.push("")}function pushTestHealthSection(lines,result){if(!result.tests){return}lines.push("## Test health");lines.push(`- items_with_tests: ${result.tests.items_with_tests}`);lines.push(`- items_with_recent_runs: ${result.tests.items_with_recent_runs}`);lines.push(`- passed: ${result.tests.recent_runs.passed}, failed: ${result.tests.recent_runs.failed}, skipped: ${result.tests.recent_runs.skipped}`);if(result.tests.items_failing.length>0){lines.push(`- items_failing: [${result.tests.items_failing.join(",")}]`)}lines.push("")}function pushEmptyContextSuggestions(lines,result){const isEmpty=result.summary.active_items===0&&result.summary.blocked===0&&result.agenda.summary.events===0;if(!isEmpty){return}lines.push("## Suggestions");lines.push("No active work items or upcoming events. Consider:");lines.push('- `pm create --type Task --title "..."` to add a new work item');lines.push("- `pm list --status closed --limit 5` to review recent completions");lines.push("- `pm search <keywords>` to find related past work");lines.push("- `pm aggregate` for a full project status overview")}function renderContextMarkdown(result){const lines=[];const focusFields=result.focus_fields;const renderFocus=item=>focusFields?formatProjectedFocusLine(item,focusFields):formatFocusLine(item);lines.push("# pm context");lines.push("");lines.push(`- now: ${result.now}`);lines.push(`- depth: ${result.depth}`);if(result.filters.parent){lines.push(`- scope: subtree of ${result.filters.parent}`)}lines.push(`- active_items: ${result.summary.active_items} (in_progress: ${result.summary.in_progress}, open: ${result.summary.open})`);if(result.summary.total_items!==void 0){lines.push(`- total_items: ${result.summary.total_items} (closed: ${result.summary.closed??0}, canceled: ${result.summary.canceled??0})`)}lines.push(`- agenda_events: ${result.summary.agenda_events}`);lines.push(`- blocked_fallback_used: ${result.summary.blocked_fallback_used}`);if(result.extension_health){const degradedNames=result.extension_health.packages.filter(entry=>entry.status==="degraded").map(entry=>entry.name);lines.push(`- extensions: ${result.extension_health.ok} ok, ${result.extension_health.degraded} degraded${degradedNames.length>0?` (${degradedNames.join(", ")})`:""}`)}const renderedSections=result.sections_included.filter(section=>section!=="blockers"||(result.blockers?.length??0)>0);if(renderedSections.length>0){lines.push(`- sections: ${renderedSections.join(", ")}`)}lines.push("");pushContextFocusSection(lines,"High-level focus",result.high_level,"No high-level active items.",renderFocus);pushContextFocusSection(lines,"Low-level focus",result.low_level,"No low-level active items.",renderFocus);pushBlockedFallbackSection(lines,result,renderFocus);pushAgendaSection(lines,result);if(result.workspace_memory){lines.push("## Workspace memory");for(const rollup of result.workspace_memory.rollups){lines.push(`- ${rollup.kind}:${rollup.key} \u2014 ${rollup.label} (${rollup.item_count} completed, latest ${rollup.last_closed_at})`)}lines.push("")}pushHierarchySection(lines,result);pushProgressSection(lines,result);pushRecentlyCreatedSection(lines,result,focusFields,renderFocus);pushUnparentedSection(lines,result,renderFocus);pushActivitySection(lines,result);pushBlockersSection(lines,result);pushFilesSection(lines,result);pushWorkloadSection(lines,result);pushStalenessSection(lines,result);pushTestHealthSection(lines,result);pushEmptyContextSuggestions(lines,result);return lines.join("\n")}async function resolveContextRuntime(options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);const settings=await readSettings(pmRoot);const contextSettings=settings.context??SETTINGS_DEFAULTS.context;const statusRegistry=resolveRuntimeStatusRegistry(settings.schema);const depth=parseContextDepth(options.depth,contextSettings);const limit=parseContextLimit(options.limit??options.maxItems,depth);return{settings,contextSettings,statusRegistry,depth,limit,sectionsIncluded:parseContextSections(options.section,depth,contextSettings),activityLimit:parseActivityLimit(options.activityLimit,contextSettings),staleThresholdDays:parseStaleThresholdDays(options.staleThreshold,contextSettings),parentScope:parseContextParent(options.parent),focusFields:parseContextFocusFields(options.fields),baseListOptions:stripListProjectionFlags(options)}}function contextNeedsAllItems(sectionsIncluded){return sectionsIncluded.some(section=>["hierarchy","progress","blockers","staleness","recently_created","unparented"].includes(section))}function buildUnpaginatedContextListOptions(baseListOptions,extra){return{...baseListOptions,...extra,noTruncate:true,limit:void 0,offset:void 0,after:void 0}}async function loadContextCorpus(global2,runtime){const listed=await runList(void 0,buildUnpaginatedContextListOptions(runtime.baseListOptions,{excludeTerminal:false}),global2);const fullCorpus=listed.items;let listedItemMetadata=fullCorpus.filter(item=>!isTerminalStatus(item.status,runtime.statusRegistry));let allItems=fullCorpus;const subtreeIds=resolveContextSubtreeIds(runtime.parentScope,fullCorpus);if(subtreeIds){listedItemMetadata=listedItemMetadata.filter(item=>subtreeIds.has(item.id.trim().toLowerCase()));allItems=allItems.filter(item=>subtreeIds.has(item.id.trim().toLowerCase()))}return{listed,listedItemMetadata,allItems,fullCorpus,subtreeIds}}function resolveContextSubtreeIds(parentScope,fullCorpus){if(parentScope===void 0){return void 0}const subtree=collectSubtreeIds(fullCorpus,parentScope);if(!subtree.found){throw new PmCliError(`Context --parent item not found: ${parentScope}`,EXIT_CODE.NOT_FOUND)}return subtree.ids}function collectHighLevelContextIds(activeItems,fullCorpus,statusRegistry,typeRegistry){const containers=new Set(computeActionabilityReport(activeItems,fullCorpus,statusRegistry,typeRegistry).containers.map(entry=>entry.item.id));return new Set(activeItems.filter(item=>HIGH_LEVEL_TYPES.has(item.type)||containers.has(item.id)||resolveItemExecutionRole(item.type,typeRegistry)!=="agent").map(item=>item.id))}async function resolveContextFocusGroups(listedItemMetadata,allItems,fullCorpus,blockedIds,statusRegistry,sectionsIncluded,limit,now,author,cursor,cursorFingerprint,useBoundedPage,pmRoot,tokenBudget,claimFocus,typeRegistry){const structural=[...listedItemMetadata].sort((left,right)=>compareCriticalItems(left,right,statusRegistry));const usage=await readContextUsageAffinity({pmRoot,author,enabled:process.env.PM_CONTEXT_USAGE_DISABLED!=="1"});const featureStore=await readWorkspaceContextSignals(structural,{pmRoot,storeKey:"context",statusRegistry,now,author,usageAffinity:usage.affinity,claimFocus});const ranking=await scoreContextCandidatesWithActiveExtensions("context",featureStore.candidates);const ranked=ranking.ranked.map(entry=>entry.item);const activeStatuses=statusRegistry.active_statuses;const rankedActiveItems=ranked.filter(item=>activeStatuses.has(normalizeStatusForRegistry(item.status,statusRegistry)));const pageStart=useBoundedPage?resolveQueryCursorStart(rankedActiveItems,cursor,cursorFingerprint,item=>item.id):0;const focusPage=useBoundedPage?rankedActiveItems.slice(pageStart,pageStart+limit):rankedActiveItems;const hasMore=useBoundedPage&&focusPage.length>0&&pageStart+focusPage.length<rankedActiveItems.length;const nextCursor=hasMore&&focusPage.length>0?encodeQueryCursor(cursorFingerprint,focusPage[focusPage.length-1].id,pageStart+focusPage.length-1):void 0;const rankedBlockedItems=ranked.filter(item=>blockedIds.has(item.id.trim().toLowerCase()));const packingIds=new Set([...focusPage.map(item=>item.id),...rankedBlockedItems.slice(0,limit).map(item=>item.id)]);const packing=packRankedContextItems({...ranking,ranked:ranking.ranked.filter(entry=>packingIds.has(entry.id))},tokenBudget,new Set(Object.entries(claimFocus??{}).filter(([,affinity])=>affinity>=1).map(([id])=>id)));const packedItems=packing.included.map(entry=>entry.item);const activeItems=packedItems.filter(item=>activeStatuses.has(normalizeStatusForRegistry(item.status,statusRegistry)));const blockedItems=packedItems.filter(item=>blockedIds.has(item.id.trim().toLowerCase()));const childrenByParent=buildChildrenByParent(allItems);const focusChildrenByParent=contextNeedsAllItems(sectionsIncluded)?childrenByParent:void 0;const fullCorpusById=new Map(fullCorpus.map(item=>[item.id.trim().toLowerCase(),item]));const projectFocusItem=item=>{const focus=toContextFocusItem(item,statusRegistry,focusChildrenByParent);if(!blockedIds.has(item.id.trim().toLowerCase())){return focus}focus.blocked=true;focus.blocker_ids=resolveItemBlockers(item,fullCorpusById,statusRegistry).filter(blocker=>!blocker.resolved).map(blocker=>blocker.id);return focus};const highLevelIds=collectHighLevelContextIds(activeItems,fullCorpus,statusRegistry,typeRegistry);const highLevel=activeItems.filter(item=>highLevelIds.has(item.id)).slice(0,useBoundedPage?focusPage.length:limit).map(projectFocusItem);const lowLevel=activeItems.filter(item=>!highLevelIds.has(item.id)).slice(0,useBoundedPage?focusPage.length:limit).map(projectFocusItem);const blockedFallbackUsed=rankedActiveItems.length===0;return{activeItems,blockedItems,highLevel,lowLevel,blockedFallback:blockedFallbackUsed?blockedItems.slice(0,limit).map(projectFocusItem):[],blockedFallbackUsed,ranking,packing,featureStore,pageExtras:{...useBoundedPage?{applied_limit:limit}:{},...hasMore?{has_more:true,truncated:true}:{},...nextCursor?{next_cursor:nextCursor}:{}}}}function buildContextCursorFingerprint(options){return createQueryFingerprint("context",selectCursorSemanticOptions(options,CONTEXT_FLAG_CONTRACTS))}function shouldPageContextFocus(options,corpusItemCount){return options.limit!==void 0||options.maxItems!==void 0||options.after!==void 0||corpusItemCount>=CONTEXT_SCALE_THRESHOLD}async function buildContextAgenda(global2,runtime,subtreeIds,listedFocusIds){const calendarOptions={...runtime.baseListOptions,view:"agenda",include:"all",limit:runtime.parentScope===void 0?String(runtime.limit):void 0};const agenda=await runCalendar(calendarOptions,global2);const scopedAgenda=subtreeIds===void 0?agenda.events:agenda.events.filter(event=>subtreeIds.has(event.item_id.trim().toLowerCase()));const fullAgendaEvents=filterTerminalCalendarEvents(scopedAgenda,runtime.statusRegistry).slice(0,runtime.limit);const agendaEvents=fullAgendaEvents.map(event=>{if(!listedFocusIds.has(event.item_id)){return event}return{at:event.at,date:event.date,kind:event.kind,item_id:event.item_id,reference_only:true,...event.kind==="reminder"?{reminder_text:event.reminder_text}:{},...event.kind==="event"?{event_title:event.event_title,event_recurring:event.event_recurring}:{}}});return{agenda,agendaEvents,agendaSummary:summarizeAgenda(fullAgendaEvents)}}function countContextStatus(items,status,statusRegistry){const targetStatus=String(status);return items.filter(item=>normalizeStatusForRegistry(item.status,statusRegistry)===targetStatus).length}async function buildOptionalContextSections(params){const{runtime,allItems,fullCorpus,blockedIds,focusGroups,now,global:global2}=params;const childrenByParent=buildChildrenByParent(allItems);const allNonTerminal=allItems.filter(item=>!isTerminalStatus(item.status,runtime.statusRegistry));const has=section=>runtime.sectionsIncluded.includes(section);return{hierarchy:has("hierarchy")?buildHierarchy(allItems,focusGroups.activeItems,runtime.statusRegistry,runtime.limit):void 0,activity:has("activity")?await buildActivity(runtime.activityLimit,global2):void 0,progress:has("progress")?buildProgress(allItems,focusGroups.activeItems,blockedIds,runtime.statusRegistry,runtime.limit):void 0,blockersSection:has("blockers")?buildBlockers(focusGroups.blockedItems,fullCorpus,runtime.statusRegistry,runtime.limit):void 0,recentlyCreated:has("recently_created")?buildRecentlyCreated(allNonTerminal,runtime.statusRegistry,childrenByParent,runtime.limit):void 0,unparented:has("unparented")?buildUnparented(allNonTerminal,runtime.statusRegistry,childrenByParent,runtime.limit):void 0,filesSection:has("files")?buildHotFiles(focusGroups.activeItems,runtime.limit):void 0,workload:has("workload")?buildWorkload(focusGroups.activeItems,runtime.statusRegistry,runtime.limit):void 0,staleness:has("staleness")?buildStaleness(allNonTerminal,runtime.staleThresholdDays,now,runtime.limit):void 0,tests:has("tests")?buildTestHealth(focusGroups.activeItems):void 0}}function buildContextSummaryExtras(allItems,statusRegistry,blockedIds){const canceledStatus=normalizeStatusInput("canceled",statusRegistry);const activeItems=allItems.filter(item=>statusRegistry.active_statuses.has(normalizeStatusForRegistry(item.status,statusRegistry)));const inProgress=countContextStatus(activeItems,statusRegistry.in_progress_status,statusRegistry);return{scope:"matching_items",active_items:activeItems.length,open:activeItems.length-inProgress,in_progress:inProgress,blocked:allItems.filter(item=>blockedIds.has(item.id.trim().toLowerCase())).length,total_items:allItems.length,closed:allItems.filter(item=>isClosedStatus(item.status,statusRegistry)).length,canceled:allItems.filter(item=>normalizeStatusForRegistry(item.status,statusRegistry)===canceledStatus).length}}function attachOptionalContextSections(result,sections,warningDetails){if(sections.hierarchy)result.hierarchy=sections.hierarchy;if(sections.activity)result.activity=sections.activity;if(sections.progress)result.progress=sections.progress;if(sections.blockersSection)result.blockers=sections.blockersSection;if(sections.recentlyCreated)result.recently_created=sections.recentlyCreated;if(sections.unparented)result.unparented=sections.unparented;if(sections.filesSection)result.files=sections.filesSection;if(sections.workload)result.workload=sections.workload;if(sections.staleness)result.staleness=sections.staleness;if(sections.tests)result.tests=sections.tests;if(warningDetails!==void 0&&warningDetails.length>0){result.warning_details=warningDetails}}function applyContextFocusProjection(result,focusFields){if(!focusFields){return}result.focus_fields=focusFields;result.high_level=projectContextFocusRows(result.high_level,focusFields);result.low_level=projectContextFocusRows(result.low_level,focusFields);result.blocked_fallback=projectContextFocusRows(result.blocked_fallback,focusFields);if(result.recently_created){result.recently_created=projectContextFocusRows(result.recently_created,focusFields)}if(result.unparented){result.unparented=projectContextFocusRows(result.unparented,focusFields)}}function applyContextTagProjection(result,noTags){const rows=[result.high_level,result.low_level,result.blocked_fallback,result.recently_created??[],result.unparented??[]].flat();if(noTags){for(const row of rows){delete row.tags;delete row.tags_inherited}return}const rowsById=new Map(rows.map(row=>[row.id,row]));const originalTagSignatures=new Map(rows.map(row=>[row.id,row.tags?[...row.tags].sort().join("\0"):void 0]));for(const row of rows){if(!row.parent||!row.tags){continue}const parent=rowsById.get(row.parent);if(parent&&originalTagSignatures.get(row.id)===originalTagSignatures.get(parent.id)){delete row.tags;row.tags_inherited=parent.id}}}function maybeAttachEmptyContextSuggestions(result,activeItems,blockedItems){if(activeItems.length>0||blockedItems.length>0||result.agenda.events.length>0){return}result.suggestions=['pm create --type Task --title "..." to add a new work item',"pm list --status closed --limit 5 to review recent completions","pm search <keywords> to find related past work","pm aggregate for a full project status overview"]}async function attachContextUsageFeedback(result,focusGroups,pmRoot,author){try{const included=new Set(focusGroups.packing.included.map(entry=>entry.id));const rows=focusGroups.ranking.ranked.map(entry=>({id:entry.id,rank:entry.rank,included:included.has(entry.id)}));const rankedIds=new Set(rows.map(row=>row.id));for(const id of collectContextUsageDeliveredItemIds(result,"context")){if(!rankedIds.has(id)){rows.push({id,rank:rows.length+1,included:false});rankedIds.add(id)}}const receipt=await recordContextUsageServing({pmRoot,author,surface:"context",profile:focusGroups.packing.profile,rows,enabled:process.env.PM_CONTEXT_USAGE_DISABLED!=="1"});attachContextUsageServingReceipt(result,receipt)}catch{result.warnings=mergeSortedWarnings(result.warnings,["context_usage_feedback_write_failed"])}}async function resolveContextExtensionHealthProjection(omitted,pmRoot,settings){if(omitted)return{};const discovery=await discoverExtensions({pmRoot,settings,cwd:process.cwd(),noExtensions:false});const packages=discovery.discovered.map(diagnostic=>({name:diagnostic.name??diagnostic.directory,status:diagnostic.status==="ok"?"ok":"degraded"})).sort((left,right)=>left.name.localeCompare(right.name));const degraded=packages.filter(entry=>entry.status==="degraded").length;if(packages.length===0)return{};return{extension_health:{total:packages.length,ok:packages.length-degraded,degraded,packages:packages.slice(0,20),...packages.length>20?{truncated:true}:{}}}}async function resolveContextSemanticAffinity(pmRoot,author){const identity=detectAgentIdentity();const semanticState=await readSessionState(pmRoot);const attributionKey=identity.instance||semanticAttributionKey(author);return semanticAttributionAffinity(semanticState.semantic_attribution?.[attributionKey])}async function runContext(options,global2){const pmRoot=resolvePmRoot(process.cwd(),global2.path);const resolvedRuntime=await resolveContextRuntime(options,global2);const corpus=await loadContextCorpus(global2,resolvedRuntime);const scaledLimit=resolveContextLimitAtScale(resolvedRuntime.limit,corpus.fullCorpus.length);const runtime={...resolvedRuntime,limit:scaledLimit,tokenBudget:resolveContextTokenBudget(options.tokenBudget,Math.max(256,scaledLimit*160))};const author=resolveAuthor(void 0,runtime.settings.author_default);const claimFocus=await resolveContextSemanticAffinity(pmRoot,author);const extensionHealthPromise=resolveContextExtensionHealthProjection(options.noExtensionHealth===true,pmRoot,runtime.settings);const blockedIds=collectDependencyBlockedIds(corpus.fullCorpus,runtime.statusRegistry);const focusGroups=await resolveContextFocusGroups(corpus.listedItemMetadata,corpus.allItems,corpus.fullCorpus,blockedIds,runtime.statusRegistry,runtime.sectionsIncluded,runtime.limit,nowIso(),author,options.after,buildContextCursorFingerprint(options),shouldPageContextFocus(options,corpus.fullCorpus.length),pmRoot,runtime.tokenBudget,claimFocus,resolveItemTypeRegistry(runtime.settings,getActiveExtensionRegistrations()));const agendaContext=await buildContextAgenda(global2,runtime,corpus.subtreeIds,new Set([...focusGroups.highLevel,...focusGroups.lowLevel,...focusGroups.blockedFallback].map(item=>item.id)));const workspaceMemory=await readWorkspaceMemory(corpus.fullCorpus,{pmRoot,statusRegistry:runtime.statusRegistry,now:agendaContext.agenda.now});const warnings=mergeSortedWarnings(corpus.listed.warnings,agendaContext.agenda.warnings,focusGroups.ranking.warnings,[...focusGroups.featureStore.warnings],workspaceMemory.warnings);const inProgressCount=countContextStatus(focusGroups.activeItems,runtime.statusRegistry.in_progress_status,runtime.statusRegistry);const openCount=focusGroups.activeItems.length-inProgressCount;const sections=await buildOptionalContextSections({runtime,allItems:corpus.allItems,fullCorpus:corpus.fullCorpus,blockedIds,focusGroups,now:agendaContext.agenda.now,global:global2});const summaryExtras=buildContextSummaryExtras(corpus.allItems,runtime.statusRegistry,blockedIds);const result={output_default:"toon",now:agendaContext.agenda.now,depth:runtime.depth,sections_included:runtime.sectionsIncluded,window:{anchor:agendaContext.agenda.anchor,start:agendaContext.agenda.range.start,end:agendaContext.agenda.range.end,past:agendaContext.agenda.range.past,from:agendaContext.agenda.range.from,to:agendaContext.agenda.range.to},filters:{type:options.type??null,tag:options.tag??null,priority:options.priority??null,assignee:options.assignee??null,assignee_filter:options.assigneeFilter??null,sprint:options.sprint??null,release:options.release??null,limit:options.limit??options.maxItems??null,parent:runtime.parentScope??null,token_budget:runtime.tokenBudget,runtime_filters:corpus.listed.filters.runtime_filters??agendaContext.agenda.filters.runtime_filters??{}},summary:{returned_focus:{active_items:focusGroups.activeItems.length,in_progress:inProgressCount,open:openCount,blocked:focusGroups.blockedItems.length},blocked_fallback_used:focusGroups.blockedFallbackUsed,high_level:focusGroups.highLevel.length,low_level:focusGroups.lowLevel.length,agenda_events:agendaContext.agendaSummary.events,...summaryExtras},high_level:focusGroups.highLevel,low_level:focusGroups.lowLevel,blocked_fallback:focusGroups.blockedFallback,agenda:{summary:agendaContext.agendaSummary,events:agendaContext.agendaEvents},...focusGroups.pageExtras};result.workspace_memory=selectWorkspaceMemory(workspaceMemory,Math.min(800,Math.floor(runtime.tokenBudget*.2)));Object.assign(result,await extensionHealthPromise);attachOptionalContextSections(result,sections,focusGroups.featureStore.warning_details);applyContextTagProjection(result,options.noTags===true);if(options.explainRanking===true){result.ranking=toContextRankingSummary(focusGroups.ranking,focusGroups.featureStore,new Set([...focusGroups.highLevel,...focusGroups.lowLevel,...focusGroups.blockedFallback].map(entry=>entry.id)));result.packing=toContextPackingSummary(focusGroups.packing)}applyContextFocusProjection(result,runtime.focusFields);if(warnings.length>0)result.warnings=warnings;maybeAttachEmptyContextSuggestions(result,focusGroups.activeItems,focusGroups.blockedItems);await attachContextUsageFeedback(result,focusGroups,pmRoot,author);return result}export{CONTEXT_OUTPUT_VALUES,_testOnly,applyContextTagProjection,buildChildrenByParent,buildItemContextRelevanceCandidates,collectSubtreeIds,compareCriticalItems,mergeSortedWarnings,packRankedContextItems,parseContextDepth,parseContextFocusFields,parseContextSections,projectContextFocusRows,renderContextMarkdown,resolveContextExtensionHealthProjection,resolveContextOutputFormat,resolveContextTokenBudget,runContext,toContextFocusItem,toContextPackingSummary,toContextRankingSummary};
|
|
1702
2
|
//# sourceMappingURL=context.js.map
|
|
1703
|
-
//# debugId=4f8e82a2-f4ea-5ecb-8ef5-3ca795cdeda5
|