@remnic/core 9.69.12 → 9.69.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/access-admin-ops-surface.d.ts +11 -11
- package/dist/access-admin-ops-surface.js +4 -4
- package/dist/access-authorization-probe.d.ts +11 -11
- package/dist/access-authorization-probe.js +5 -5
- package/dist/access-boundary.d.ts +11 -11
- package/dist/access-boundary.js +4 -4
- package/dist/access-cli.js +28 -28
- package/dist/access-coding-context-resolution.d.ts +1 -1
- package/dist/access-extraction-force-flush.d.ts +11 -11
- package/dist/access-extraction-force-flush.js +4 -4
- package/dist/access-health-types.d.ts +1 -1
- package/dist/access-http-lcm-compaction.d.ts +11 -11
- package/dist/access-http-lifecycle-flush.d.ts +11 -11
- package/dist/access-http-offline-stream.d.ts +11 -11
- package/dist/access-http-query.d.ts +2 -2
- package/dist/access-http-query.js +4 -4
- package/dist/access-http.d.ts +12 -12
- package/dist/access-http.js +15 -15
- package/dist/access-identity-continuity-surface.d.ts +9 -9
- package/dist/access-identity-continuity-surface.js +4 -4
- package/dist/access-lcm-surface.d.ts +11 -11
- package/dist/access-lcm-surface.js +4 -4
- package/dist/access-mcp.d.ts +11 -11
- package/dist/access-mcp.js +11 -11
- package/dist/access-memory-search-fanout.d.ts +2 -2
- package/dist/{access-namespace-preflight-Cy0yPsaE.d.ts → access-namespace-preflight-Rzuo1nGR.d.ts} +1 -1
- package/dist/access-namespace-preflight.d.ts +3 -3
- package/dist/access-namespace-preflight.js +4 -4
- package/dist/access-observe-write-surface.d.ts +11 -11
- package/dist/access-observe-write-surface.js +4 -4
- package/dist/access-offline-manifest.d.ts +9 -9
- package/dist/access-operations-batch.js +5 -5
- package/dist/access-operations.d.ts +12 -12
- package/dist/access-operations.js +10 -10
- package/dist/access-recall-concurrency.d.ts +11 -11
- package/dist/access-recall-concurrency.js +4 -4
- package/dist/access-recall-response.d.ts +11 -11
- package/dist/access-recall-response.js +4 -4
- package/dist/access-recall-surface.d.ts +11 -11
- package/dist/access-recall-surface.js +4 -4
- package/dist/{access-service-Baz5xW5X.d.ts → access-service-Bb2Fykzc.d.ts} +7 -7
- package/dist/access-service-helpers.d.ts +11 -11
- package/dist/access-service.d.ts +11 -11
- package/dist/access-service.js +4 -4
- package/dist/access-surface-catalog.d.ts +11 -11
- package/dist/access-surface-catalog.js +6 -6
- package/dist/access-wearables-meetings-surface.d.ts +3 -3
- package/dist/action-confidence.d.ts +1 -1
- package/dist/active-memory-bridge.d.ts +1 -1
- package/dist/active-recall.d.ts +1 -1
- package/dist/active-recall.js +4 -4
- package/dist/ambient-provenance.d.ts +1 -1
- package/dist/artifact-search.d.ts +1 -1
- package/dist/behavior-learner.d.ts +1 -1
- package/dist/behavior-signals.d.ts +1 -1
- package/dist/bootstrap.d.ts +9 -9
- package/dist/briefing-window.d.ts +2 -2
- package/dist/briefing.d.ts +2 -2
- package/dist/buffer-surprise-report.d.ts +1 -1
- package/dist/buffer-turn-helpers.d.ts +1 -1
- package/dist/buffer.d.ts +2 -2
- package/dist/buffer.js +4 -4
- package/dist/bulk-import/index.d.ts +3 -3
- package/dist/calibration.d.ts +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/{catalog-9X8WpZNO.d.ts → catalog-DjTLVOYp.d.ts} +1 -1
- package/dist/causal-behavior.d.ts +1 -1
- package/dist/causal-consolidation.d.ts +1 -1
- package/dist/causal-trajectory-graph.d.ts +1 -1
- package/dist/{chunk-TEZ2HTC7.js → chunk-33JKYLPX.js} +2 -2
- package/dist/{chunk-MNH2GM4O.js → chunk-3BS2OGDT.js} +6 -19
- package/dist/{chunk-MNH2GM4O.js.map → chunk-3BS2OGDT.js.map} +1 -1
- package/dist/{chunk-5PTS43CH.js → chunk-4LG4CPOC.js} +2 -2
- package/dist/{chunk-Z33VUMKY.js → chunk-5H2G5X4R.js} +2 -2
- package/dist/{chunk-XFPHV65N.js → chunk-5JQV4T5Q.js} +2 -2
- package/dist/{chunk-OASGWNYL.js → chunk-63I7LMLG.js} +2 -2
- package/dist/{chunk-J3NGQ5SC.js → chunk-7GSSHAXC.js} +2 -2
- package/dist/{chunk-X4FIZAVQ.js → chunk-7JV4JST3.js} +854 -42
- package/dist/chunk-7JV4JST3.js.map +1 -0
- package/dist/{chunk-VHAKBUBD.js → chunk-B64IVEOH.js} +2 -2
- package/dist/{chunk-R7FXPG4T.js → chunk-C25N5HWX.js} +2 -2
- package/dist/{chunk-DH3V7RCQ.js → chunk-F2ERDKY2.js} +7 -7
- package/dist/{chunk-LJNLLNEM.js → chunk-HODFGQGT.js} +2 -2
- package/dist/{chunk-B75VCEX5.js → chunk-K2IJH4D3.js} +2 -2
- package/dist/{chunk-227WJVPY.js → chunk-KLSPKMIO.js} +2 -2
- package/dist/{chunk-5P2KNFJZ.js → chunk-MSDIETFC.js} +3 -3
- package/dist/{chunk-HFRGCNFY.js → chunk-OAKATRAN.js} +2 -2
- package/dist/chunk-OGUKSLYZ.js +560 -0
- package/dist/chunk-OGUKSLYZ.js.map +1 -0
- package/dist/{chunk-M6L7HI7R.js → chunk-P6FHFTZ7.js} +16 -1
- package/dist/chunk-P6FHFTZ7.js.map +1 -0
- package/dist/{chunk-LWBSEYW3.js → chunk-PAWIY5OQ.js} +5 -5
- package/dist/{chunk-3ITTF7YH.js → chunk-QVI63XSO.js} +90 -383
- package/dist/chunk-QVI63XSO.js.map +1 -0
- package/dist/{chunk-6TOWUN2R.js → chunk-RE3H6Y2X.js} +2 -2
- package/dist/{chunk-V3HUBF7T.js → chunk-S35IIP5P.js} +2 -2
- package/dist/{chunk-3YKOFCKP.js → chunk-SXHBZXHK.js} +2 -2
- package/dist/{chunk-7NCUHD5U.js → chunk-VAMZYOOV.js} +2 -2
- package/dist/{chunk-PGCZLHRB.js → chunk-W272SWN7.js} +3 -3
- package/dist/{chunk-5HO3QBAD.js → chunk-W6GEMUZM.js} +2 -2
- package/dist/{chunk-UCNQV43K.js → chunk-XQYTSZW3.js} +2 -2
- package/dist/{chunk-GR4C3FHT.js → chunk-ZKZCJRRO.js} +2 -2
- package/dist/{cli-CMtkGBdz.d.ts → cli-DXXy60A4.d.ts} +5 -5
- package/dist/cli.d.ts +13 -13
- package/dist/cli.js +21 -21
- package/dist/coding/export-okf-codegraph.d.ts +1 -1
- package/dist/coding/pre-action-gate.d.ts +1 -1
- package/dist/compounding/engine.d.ts +2 -2
- package/dist/compounding/preference-consolidator.d.ts +1 -1
- package/dist/compression-optimizer.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +4 -4
- package/dist/connectors/codex-materialize-runner.d.ts +1 -1
- package/dist/connectors/codex-materialize.d.ts +1 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/consolidation-provenance-check.d.ts +2 -2
- package/dist/consolidation-undo.d.ts +2 -2
- package/dist/contradiction/index.d.ts +3 -3
- package/dist/converge-config.d.ts +1 -1
- package/dist/convergence-refresh.d.ts +2 -2
- package/dist/conversation-index/backend.d.ts +1 -1
- package/dist/conversation-index/chunker.d.ts +1 -1
- package/dist/conversation-index/faiss-adapter.d.ts +1 -1
- package/dist/conversation-index/indexer.d.ts +1 -1
- package/dist/conversation-index/search.d.ts +1 -1
- package/dist/corpus-watermark.d.ts +1 -1
- package/dist/cross-lingual-recall.d.ts +1 -1
- package/dist/day-summary.d.ts +1 -1
- package/dist/deep-recall-http-glue.d.ts +11 -11
- package/dist/deep-recall-mcp-tools.d.ts +11 -11
- package/dist/deep-recall-policy-llm.d.ts +1 -1
- package/dist/deep-recall-seeds.d.ts +1 -1
- package/dist/delinearize.d.ts +1 -1
- package/dist/dependency-propagation-config.d.ts +1 -1
- package/dist/{dependency-propagation-delivery-BZGUecZf.d.ts → dependency-propagation-delivery-37JdQv_3.d.ts} +1 -1
- package/dist/direct-answer-wiring.d.ts +1 -1
- package/dist/direct-answer.d.ts +1 -1
- package/dist/embedding-fallback.d.ts +1 -1
- package/dist/enrichment/index.d.ts +1 -1
- package/dist/entity-origin-fields.d.ts +1 -1
- package/dist/entity-retrieval.d.ts +2 -2
- package/dist/entity-schema.d.ts +1 -1
- package/dist/episodic-context.d.ts +1 -1
- package/dist/eval-trajectory-contracts.d.ts +1 -1
- package/dist/eval-trajectory-store.d.ts +1 -1
- package/dist/eval-trajectory.d.ts +1 -1
- package/dist/explicit-capture.d.ts +9 -9
- package/dist/external-wiki-access.d.ts +11 -11
- package/dist/external-wiki-access.js +5 -5
- package/dist/external-wiki-collection-registration.d.ts +1 -1
- package/dist/external-wiki-collection.d.ts +1 -1
- package/dist/external-wiki-mcp-tools.d.ts +11 -11
- package/dist/extraction-error-classification.d.ts +1 -1
- package/dist/extraction-faithfulness.d.ts +1 -1
- package/dist/extraction-judge-telemetry.d.ts +1 -1
- package/dist/extraction-judge-training.d.ts +1 -1
- package/dist/extraction-judge.d.ts +1 -1
- package/dist/extraction-liveness.d.ts +1 -1
- package/dist/extraction-normalization.d.ts +1 -1
- package/dist/extraction-prompt.d.ts +1 -1
- package/dist/extraction-source-grounding-rules.d.ts +1 -1
- package/dist/extraction-source-grounding.d.ts +1 -1
- package/dist/extraction.d.ts +1 -1
- package/dist/extraction.js +5 -5
- package/dist/fallback-llm.d.ts +1 -1
- package/dist/graph-dashboard-diff.d.ts +1 -1
- package/dist/graph-dashboard-key.d.ts +1 -1
- package/dist/graph-dashboard-parser.d.ts +1 -1
- package/dist/graph-edge-reinforcement.d.ts +1 -1
- package/dist/graph-path-reconstruction.d.ts +1 -1
- package/dist/graph-path-scoring.d.ts +1 -1
- package/dist/graph-snapshot.d.ts +1 -1
- package/dist/graph.d.ts +1 -1
- package/dist/importance.d.ts +1 -1
- package/dist/importers/index.d.ts +1 -1
- package/dist/in-flight-reads.d.ts +1 -1
- package/dist/{index-B4iTvT-w.d.ts → index-DquhYqsc.d.ts} +2 -2
- package/dist/index.d.ts +200 -62
- package/dist/index.js +59 -37
- package/dist/intent.d.ts +1 -1
- package/dist/lcm/engine.d.ts +1 -1
- package/dist/lcm/index.d.ts +1 -1
- package/dist/lcm/tools.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -1
- package/dist/live-connectors-runner.d.ts +1 -1
- package/dist/local-llm.d.ts +1 -1
- package/dist/local-model-endpoint.d.ts +1 -1
- package/dist/location/index.d.ts +3 -3
- package/dist/location/index.js +5 -5
- package/dist/maintenance/memory-governance.d.ts +1 -1
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
- package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
- package/dist/{maintenance-BBpNqEeJ.d.ts → maintenance-CizZOycr.d.ts} +3 -3
- package/dist/mcp-memory-inspector-app.d.ts +11 -11
- package/dist/memory-action-policy.d.ts +1 -1
- package/dist/memory-cache.d.ts +1 -1
- package/dist/memory-controller.d.ts +1 -1
- package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
- package/dist/memory-projection-store.d.ts +1 -1
- package/dist/memory-provenance.d.ts +1 -1
- package/dist/memory-snapshot.d.ts +1 -1
- package/dist/memory-subject-cli.d.ts +11 -11
- package/dist/memory-subject.d.ts +11 -11
- package/dist/memory-worth-outcomes.d.ts +2 -2
- package/dist/models-json.d.ts +1 -1
- package/dist/namespaces/migrate.d.ts +3 -3
- package/dist/namespaces/principal.d.ts +1 -1
- package/dist/namespaces/search.d.ts +1 -1
- package/dist/namespaces/storage.d.ts +3 -3
- package/dist/native-knowledge.d.ts +1 -1
- package/dist/offline-sync-impression-drain.d.ts +1 -1
- package/dist/operator-doctor-corpus.d.ts +1 -1
- package/dist/operator-doctor-replica.d.ts +1 -1
- package/dist/operator-toolkit.d.ts +3 -3
- package/dist/operator-toolkit.js +5 -5
- package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
- package/dist/orchestration/maintenance.d.ts +4 -4
- package/dist/orchestration/maintenance.js +6 -6
- package/dist/{orchestrator-DglY1H0w.d.ts → orchestrator-DqLcuNMP.d.ts} +8 -8
- package/dist/orchestrator.d.ts +9 -9
- package/dist/orchestrator.js +28 -28
- package/dist/patterns-cli.d.ts +1 -1
- package/dist/{pipeline-C7qRsy6g.d.ts → pipeline-DbJpmcRu.d.ts} +1 -1
- package/dist/policy-runtime.d.ts +1 -1
- package/dist/proactive-contention.d.ts +1 -1
- package/dist/provenance.d.ts +1 -1
- package/dist/{public-http-CU-Cwwdb.d.ts → public-http-NQ3UUp6W.d.ts} +1 -1
- package/dist/{qmd-YRLTa3AE.d.ts → qmd-DA_7fbSz.d.ts} +1 -1
- package/dist/qmd-preflight.d.ts +2 -2
- package/dist/qmd-recall-cache.d.ts +1 -1
- package/dist/qmd.d.ts +2 -2
- package/dist/recall-concurrency-config.d.ts +1 -1
- package/dist/recall-disclosure-escalation.d.ts +1 -1
- package/dist/recall-explain-renderer.d.ts +1 -1
- package/dist/recall-memory-map.d.ts +1 -1
- package/dist/recall-navigate-access.d.ts +1 -1
- package/dist/recall-navigate-disclosure.d.ts +1 -1
- package/dist/recall-navigate.d.ts +1 -1
- package/dist/recall-planner-llm.d.ts +1 -1
- package/dist/recall-state.d.ts +1 -1
- package/dist/recall-tag-filter.d.ts +1 -1
- package/dist/recall-timings.d.ts +1 -1
- package/dist/recall-xray-cli.d.ts +1 -1
- package/dist/recall-xray-renderer.d.ts +1 -1
- package/dist/recall-xray.d.ts +1 -1
- package/dist/reconcile/cursor.d.ts +1 -1
- package/dist/reconcile/manifest.d.ts +1 -1
- package/dist/reconcile/plan.d.ts +1 -1
- package/dist/replay/normalizers/chatgpt.d.ts +1 -1
- package/dist/replay/normalizers/claude.d.ts +1 -1
- package/dist/replay/normalizers/openclaw.d.ts +1 -1
- package/dist/replay/normalizers/shared.d.ts +1 -1
- package/dist/replay/runner.d.ts +1 -1
- package/dist/replay/types.d.ts +1 -1
- package/dist/replica-divergence.d.ts +1 -1
- package/dist/replica-peers-config.d.ts +1 -1
- package/dist/resolve-auth-token.d.ts +1 -1
- package/dist/resume-bundles.js +5 -5
- package/dist/retrieval-agents.d.ts +2 -2
- package/dist/retrieval-tiers.d.ts +1 -1
- package/dist/routing/engine.d.ts +1 -1
- package/dist/routing/store.d.ts +1 -1
- package/dist/salvage-envelope.d.ts +1 -1
- package/dist/{scope-profiles-BVvqVzJX.d.ts → scope-profiles-BpiWKZzx.d.ts} +1 -1
- package/dist/search/embed-helper.d.ts +1 -1
- package/dist/search/factory.d.ts +1 -1
- package/dist/search/index.d.ts +1 -1
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/port.d.ts +1 -1
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/{semantic-consolidation-BzCI6Kuu.d.ts → semantic-consolidation-NJpaZpkY.d.ts} +1 -1
- package/dist/semantic-consolidation.d.ts +2 -2
- package/dist/semantic-rule-verifier.d.ts +1 -1
- package/dist/{service-DQ3Gscy5.d.ts → service-DdlZjX1D.d.ts} +2 -2
- package/dist/session-observer-bands.d.ts +1 -1
- package/dist/session-observer-state.d.ts +1 -1
- package/dist/shared-context/manager.d.ts +1 -1
- package/dist/signal.d.ts +1 -1
- package/dist/source-agent-qualifier.d.ts +1 -1
- package/dist/source-agent-qualifier.js +4 -4
- package/dist/standup/build.js +2 -2
- package/dist/{storage-DmTbQdD0.d.ts → storage-C7BWrpU9.d.ts} +1 -1
- package/dist/storage.d.ts +2 -2
- package/dist/subject-config.d.ts +1 -1
- package/dist/summarizer.d.ts +1 -1
- package/dist/summary-snapshot.d.ts +1 -1
- package/dist/support-passport/index.d.ts +14 -14
- package/dist/support-passport/index.js +7 -7
- package/dist/temporal-supersession.d.ts +2 -2
- package/dist/temporal-timeline-recall.d.ts +1 -1
- package/dist/temporal-validity.d.ts +1 -1
- package/dist/threading.d.ts +1 -1
- package/dist/tier-migration.d.ts +2 -2
- package/dist/tier-routing.d.ts +1 -1
- package/dist/topics.d.ts +1 -1
- package/dist/transcript.d.ts +1 -1
- package/dist/transfer/export-okf.d.ts +1 -1
- package/dist/trust-score-stage.d.ts +1 -1
- package/dist/trust-score.d.ts +1 -1
- package/dist/{types-CLRTESSD.d.ts → types-Cy1l6fzp.d.ts} +48 -1
- package/dist/types.d.ts +1 -1
- package/dist/utility-runtime.d.ts +1 -1
- package/dist/who-knows-cli.d.ts +11 -11
- package/dist/who-knows.d.ts +11 -11
- package/dist/write-envelope.d.ts +1 -1
- package/package.json +2 -2
- package/src/activity/config.test.ts +94 -1
- package/src/activity/config.ts +256 -3
- package/src/activity/digest.ts +21 -0
- package/src/activity/index.ts +2 -0
- package/src/activity/runner.ts +2 -17
- package/src/activity/timeline/publish-cli.test.ts +169 -0
- package/src/activity/timeline/publish-cli.ts +144 -0
- package/src/activity/timeline/query.ts +51 -15
- package/src/activity/types.ts +45 -0
- package/src/activity/vault-insert.ts +14 -33
- package/src/activity/vault-path.ts +100 -22
- package/src/activity/vault-publish.test.ts +51 -0
- package/src/activity/vault-publish.ts +157 -17
- package/src/activity/vault-publisher.test.ts +881 -0
- package/src/activity/vault-publisher.ts +597 -0
- package/src/activity/vault-region.test.ts +5 -0
- package/src/activity/vault-region.ts +7 -2
- package/src/cli/timeline-publish-commands.ts +34 -0
- package/src/cli.ts +2 -2
- package/src/config.test.ts +24 -1
- package/dist/chunk-3ITTF7YH.js.map +0 -1
- package/dist/chunk-KJUWWN3T.js +0 -237
- package/dist/chunk-KJUWWN3T.js.map +0 -1
- package/dist/chunk-M6L7HI7R.js.map +0 -1
- package/dist/chunk-X4FIZAVQ.js.map +0 -1
- /package/dist/{chunk-TEZ2HTC7.js.map → chunk-33JKYLPX.js.map} +0 -0
- /package/dist/{chunk-5PTS43CH.js.map → chunk-4LG4CPOC.js.map} +0 -0
- /package/dist/{chunk-Z33VUMKY.js.map → chunk-5H2G5X4R.js.map} +0 -0
- /package/dist/{chunk-XFPHV65N.js.map → chunk-5JQV4T5Q.js.map} +0 -0
- /package/dist/{chunk-OASGWNYL.js.map → chunk-63I7LMLG.js.map} +0 -0
- /package/dist/{chunk-J3NGQ5SC.js.map → chunk-7GSSHAXC.js.map} +0 -0
- /package/dist/{chunk-VHAKBUBD.js.map → chunk-B64IVEOH.js.map} +0 -0
- /package/dist/{chunk-R7FXPG4T.js.map → chunk-C25N5HWX.js.map} +0 -0
- /package/dist/{chunk-DH3V7RCQ.js.map → chunk-F2ERDKY2.js.map} +0 -0
- /package/dist/{chunk-LJNLLNEM.js.map → chunk-HODFGQGT.js.map} +0 -0
- /package/dist/{chunk-B75VCEX5.js.map → chunk-K2IJH4D3.js.map} +0 -0
- /package/dist/{chunk-227WJVPY.js.map → chunk-KLSPKMIO.js.map} +0 -0
- /package/dist/{chunk-5P2KNFJZ.js.map → chunk-MSDIETFC.js.map} +0 -0
- /package/dist/{chunk-HFRGCNFY.js.map → chunk-OAKATRAN.js.map} +0 -0
- /package/dist/{chunk-LWBSEYW3.js.map → chunk-PAWIY5OQ.js.map} +0 -0
- /package/dist/{chunk-6TOWUN2R.js.map → chunk-RE3H6Y2X.js.map} +0 -0
- /package/dist/{chunk-V3HUBF7T.js.map → chunk-S35IIP5P.js.map} +0 -0
- /package/dist/{chunk-3YKOFCKP.js.map → chunk-SXHBZXHK.js.map} +0 -0
- /package/dist/{chunk-7NCUHD5U.js.map → chunk-VAMZYOOV.js.map} +0 -0
- /package/dist/{chunk-PGCZLHRB.js.map → chunk-W272SWN7.js.map} +0 -0
- /package/dist/{chunk-5HO3QBAD.js.map → chunk-W6GEMUZM.js.map} +0 -0
- /package/dist/{chunk-UCNQV43K.js.map → chunk-XQYTSZW3.js.map} +0 -0
- /package/dist/{chunk-GR4C3FHT.js.map → chunk-ZKZCJRRO.js.map} +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared `timeline publish` implementation (issue #1985).
|
|
3
|
+
*
|
|
4
|
+
* Both CLI trees dispatch this one function: the host command group in
|
|
5
|
+
* `cli/timeline-publish-commands.ts` and the standalone binary's shared
|
|
6
|
+
* runner in `timeline/query.ts`. A documented command that exists on only
|
|
7
|
+
* one tree is a defect — the shipped `@remnic/cli` binary must publish too.
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
10
|
+
|
|
11
|
+
import type { PluginConfig } from "../../types.js";
|
|
12
|
+
import { activityDateInTimezone, isValidActivityDate } from "../digest.js";
|
|
13
|
+
import { journalPath } from "../journal.js";
|
|
14
|
+
import { publishVaultNote, type VaultSectionPublish } from "../vault-publisher.js";
|
|
15
|
+
|
|
16
|
+
export interface TimelinePublishIo {
|
|
17
|
+
stdout: { write(chunk: string): unknown };
|
|
18
|
+
stderr: { write(chunk: string): unknown };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface TimelinePublishOptions {
|
|
22
|
+
date?: unknown;
|
|
23
|
+
week?: unknown;
|
|
24
|
+
what?: unknown;
|
|
25
|
+
dryRun?: unknown;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const AVAILABLE_KINDS = ["timeline"] as const;
|
|
29
|
+
const KNOWN_KINDS = ["timeline", "standup", "weekly", "locations"] as const;
|
|
30
|
+
const UNLANDED_REASONS: Record<string, string> = {
|
|
31
|
+
standup: "the standup renderer lands with timeline phase 3",
|
|
32
|
+
weekly: "the weekly review renderer lands with timeline phase 4",
|
|
33
|
+
locations: "the location day renderer is not wired to publishing yet",
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export function runTimelinePublishCli(
|
|
37
|
+
config: PluginConfig,
|
|
38
|
+
options: TimelinePublishOptions,
|
|
39
|
+
io: TimelinePublishIo,
|
|
40
|
+
): number {
|
|
41
|
+
const vault = config.activity?.timeline?.vault;
|
|
42
|
+
|
|
43
|
+
if (options.week !== undefined) {
|
|
44
|
+
io.stderr.write("--week is not available yet: the weekly review renderer lands with timeline phase 4.\n");
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
if (!vault || vault.enabled !== true) {
|
|
48
|
+
io.stderr.write(
|
|
49
|
+
"Vault publishing is disabled: set activity.timeline.vault.enabled=true and configure vaultPath first.\n",
|
|
50
|
+
);
|
|
51
|
+
return 1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Timeline artifacts are grouped on the configured activity timezone's
|
|
55
|
+
// calendar day, so "today" must be derived there — the host-local day
|
|
56
|
+
// drifts by one near either timezone's midnight.
|
|
57
|
+
const date =
|
|
58
|
+
typeof options.date === "string" && options.date.length > 0
|
|
59
|
+
? options.date
|
|
60
|
+
: activityDateInTimezone(new Date(), config.activity?.timezone ?? "UTC");
|
|
61
|
+
if (!isValidActivityDate(date)) {
|
|
62
|
+
io.stderr.write(`Invalid --date "${date}"; expected YYYY-MM-DD.\n`);
|
|
63
|
+
return 1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const requested = parseKinds(typeof options.what === "string" && options.what.length > 0 ? options.what : "timeline");
|
|
67
|
+
if (typeof requested !== "string") {
|
|
68
|
+
io.stderr.write(requested.error);
|
|
69
|
+
return 1;
|
|
70
|
+
}
|
|
71
|
+
const kinds = requested.split(",");
|
|
72
|
+
const sections: VaultSectionPublish[] = [];
|
|
73
|
+
let notePathTemplate = vault.dailyNotePath;
|
|
74
|
+
|
|
75
|
+
for (const kind of kinds) {
|
|
76
|
+
if ((AVAILABLE_KINDS as readonly string[]).indexOf(kind) === -1) {
|
|
77
|
+
io.stderr.write(`--what ${kind} is not available yet: ${UNLANDED_REASONS[kind] ?? "unknown kind"}.\n`);
|
|
78
|
+
return 1;
|
|
79
|
+
}
|
|
80
|
+
const target = vault.publish[kind as "timeline"];
|
|
81
|
+
if (target.enabled !== true) {
|
|
82
|
+
io.stderr.write(`--what ${kind} is disabled: enable activity.timeline.vault.publish.${kind}.enabled.\n`);
|
|
83
|
+
return 1;
|
|
84
|
+
}
|
|
85
|
+
// Honor the target's configured note file; config parsing already
|
|
86
|
+
// rejects target="weekly" with an empty weeklyNotePath.
|
|
87
|
+
notePathTemplate = target.target === "weekly" ? vault.weeklyNotePath : vault.dailyNotePath;
|
|
88
|
+
const source = journalPath(config.memoryDir, date);
|
|
89
|
+
if (!existsSync(source)) {
|
|
90
|
+
io.stderr.write(
|
|
91
|
+
`No timeline artifact for ${date}: run the journal recap generation first (${kind} target reads the persisted day recap).\n`,
|
|
92
|
+
);
|
|
93
|
+
return 1;
|
|
94
|
+
}
|
|
95
|
+
sections.push({ name: target.section, content: readFileSync(source, "utf8").trimEnd() });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const status = publishVaultNote({
|
|
99
|
+
vaultPath: vault.vaultPath,
|
|
100
|
+
notePathTemplate,
|
|
101
|
+
date,
|
|
102
|
+
sections,
|
|
103
|
+
strategy: vault.sectionStrategy,
|
|
104
|
+
insertUnderHeading: vault.insertUnderHeading.length > 0 ? vault.insertUnderHeading : undefined,
|
|
105
|
+
createMissingNotes: vault.createMissingNotes,
|
|
106
|
+
noteTemplate: vault.noteTemplate.length > 0 ? vault.noteTemplate : undefined,
|
|
107
|
+
propertiesMode: vault.properties.mode,
|
|
108
|
+
propertiesPrefix: vault.properties.prefix,
|
|
109
|
+
dryRun: options.dryRun === true,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
for (const row of status.results) {
|
|
113
|
+
const label = row.outcome === "updated" && options.dryRun === true ? "would update" : row.outcome;
|
|
114
|
+
io.stdout.write(`${label} ${row.path}${row.reason ? ` (${row.reason})` : ""}\n`);
|
|
115
|
+
}
|
|
116
|
+
io.stdout.write(
|
|
117
|
+
`updated=${status.counts.updated} unchanged=${status.counts.unchanged} skipped=${status.counts.skipped} error=${status.counts.error}\n`,
|
|
118
|
+
);
|
|
119
|
+
return status.hasError ? 1 : 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* `--what` is normalized to a comma-joined kind list. An empty normalized
|
|
124
|
+
* list (`--what ','`) is rejected here rather than reaching the publisher,
|
|
125
|
+
* which throws a RangeError on a zero-length section list.
|
|
126
|
+
*/
|
|
127
|
+
function parseKinds(raw: string): string | { error: string } {
|
|
128
|
+
const kinds = raw
|
|
129
|
+
.split(",")
|
|
130
|
+
.map((part) => part.trim())
|
|
131
|
+
.filter((part) => part.length > 0);
|
|
132
|
+
for (const kind of kinds) {
|
|
133
|
+
if ((KNOWN_KINDS as readonly string[]).indexOf(kind) === -1) {
|
|
134
|
+
return { error: `Unknown --what kind "${kind}"; valid kinds are ${KNOWN_KINDS.join(", ")}.\n` };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (new Set(kinds).size !== kinds.length) {
|
|
138
|
+
return { error: "--what must not repeat a kind.\n" };
|
|
139
|
+
}
|
|
140
|
+
if (kinds.length === 0) {
|
|
141
|
+
return { error: `--what must name at least one artifact; valid kinds are ${KNOWN_KINDS.join(", ")}.\n` };
|
|
142
|
+
}
|
|
143
|
+
return kinds.join(",");
|
|
144
|
+
}
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* MCP/HTTP and recall injection are later PRs.
|
|
10
10
|
*/
|
|
11
11
|
import { parseFlexibleIsoTimestamp } from "../../utils/iso-timestamp.js";
|
|
12
|
+
import type { PluginConfig } from "../../types.js";
|
|
12
13
|
import type { ActivityTimelineQaConfig } from "../types.js";
|
|
14
|
+
import { runTimelinePublishCli } from "./publish-cli.js";
|
|
13
15
|
import type { TimelineCard } from "./types.js";
|
|
14
16
|
|
|
15
17
|
const RANGE_FORMATS = ["cards", "compact"] as const;
|
|
@@ -70,6 +72,8 @@ export interface TimelineCliDeps {
|
|
|
70
72
|
cards: readonly TimelineQueryCard[] | null;
|
|
71
73
|
qa: ActivityTimelineQaConfig;
|
|
72
74
|
timelineEnabled?: boolean;
|
|
75
|
+
/** Required for `publish`; range/search never read it. */
|
|
76
|
+
config?: PluginConfig;
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
const USAGE = `Usage: timeline <command> [options]
|
|
@@ -77,17 +81,25 @@ const USAGE = `Usage: timeline <command> [options]
|
|
|
77
81
|
Commands:
|
|
78
82
|
range --from <ISO> --to <ISO> [--format cards|compact] [--categories id,id] [--include-distractions]
|
|
79
83
|
search --query <text> [--from <ISO>] [--to <ISO>] [--limit N]
|
|
84
|
+
publish [--date YYYY-MM-DD] [--what timeline] [--dry-run]
|
|
80
85
|
`;
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"--
|
|
88
|
-
"--
|
|
87
|
+
// Per-subcommand flag allow-lists (issue #1985 review): a shared union table
|
|
88
|
+
// let `timeline publish --from …` parse and then silently discard `--from`,
|
|
89
|
+
// publishing today's note on a query-only flag — and `range --date` likewise.
|
|
90
|
+
// Every subcommand validates its own syntax before dispatch.
|
|
91
|
+
const PUBLISH_FLAGS = {
|
|
92
|
+
value: { "--date": true, "--week": true, "--what": true } as Record<string, true>,
|
|
93
|
+
boolean: { "--dry-run": true } as Record<string, true>,
|
|
94
|
+
};
|
|
95
|
+
const RANGE_FLAGS = {
|
|
96
|
+
value: { "--from": true, "--to": true, "--format": true, "--categories": true } as Record<string, true>,
|
|
97
|
+
boolean: { "--include-distractions": true } as Record<string, true>,
|
|
98
|
+
};
|
|
99
|
+
const SEARCH_FLAGS = {
|
|
100
|
+
value: { "--query": true, "--from": true, "--to": true, "--limit": true } as Record<string, true>,
|
|
101
|
+
boolean: {} as Record<string, true>,
|
|
89
102
|
};
|
|
90
|
-
const BOOLEAN_FLAGS: Record<string, true> = { "--include-distractions": true };
|
|
91
103
|
|
|
92
104
|
function parseInstant(value: string, label: string): number {
|
|
93
105
|
const ms = parseFlexibleIsoTimestamp(value);
|
|
@@ -214,16 +226,19 @@ export function queryTimelineSearch(
|
|
|
214
226
|
return { ok: true, results: ranked };
|
|
215
227
|
}
|
|
216
228
|
|
|
217
|
-
function parseFlags(
|
|
229
|
+
function parseFlags(
|
|
230
|
+
args: string[],
|
|
231
|
+
spec: { value: Record<string, true>; boolean: Record<string, true> },
|
|
232
|
+
): { flags: Map<string, string | true>; positional: string[] } {
|
|
218
233
|
const flags = new Map<string, string | true>();
|
|
219
234
|
const positional: string[] = [];
|
|
220
|
-
for (let i = 0; i < args.length; i
|
|
235
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
221
236
|
const arg = args[i]!;
|
|
222
237
|
if (!arg.startsWith("--")) {
|
|
223
238
|
positional.push(arg);
|
|
224
239
|
continue;
|
|
225
240
|
}
|
|
226
|
-
if (
|
|
241
|
+
if (spec.value[arg] === true) {
|
|
227
242
|
const value = args[i + 1];
|
|
228
243
|
if (value === undefined || value.startsWith("--")) {
|
|
229
244
|
throw new TypeError(`flag ${arg} requires a value`);
|
|
@@ -232,11 +247,11 @@ function parseFlags(args: string[]): { flags: Map<string, string | true>; positi
|
|
|
232
247
|
i += 1;
|
|
233
248
|
continue;
|
|
234
249
|
}
|
|
235
|
-
if (
|
|
250
|
+
if (spec.boolean[arg] === true) {
|
|
236
251
|
flags.set(arg, true);
|
|
237
252
|
continue;
|
|
238
253
|
}
|
|
239
|
-
const known = [...Object.keys(
|
|
254
|
+
const known = [...Object.keys(spec.value), ...Object.keys(spec.boolean)].sort().join(", ");
|
|
240
255
|
throw new TypeError(`unknown flag ${arg} (valid: ${known})`);
|
|
241
256
|
}
|
|
242
257
|
return { flags, positional };
|
|
@@ -259,11 +274,32 @@ export async function runTimelineCliCommand(
|
|
|
259
274
|
io.stdout.write(USAGE);
|
|
260
275
|
return command === undefined ? 1 : 0;
|
|
261
276
|
}
|
|
277
|
+
// `publish` writes into the user's vault and is not a qa/query surface,
|
|
278
|
+
// so it dispatches before the qa gate. Flags are validated against the
|
|
279
|
+
// publish-only set: `--from`/`--format` are query syntax and must be
|
|
280
|
+
// rejected here, not silently dropped while today's note is published.
|
|
281
|
+
if (command === "publish") {
|
|
282
|
+
if (deps.config === undefined) {
|
|
283
|
+
io.stderr.write("timeline publish requires the Remnic config — run `remnic doctor`\n");
|
|
284
|
+
return 1;
|
|
285
|
+
}
|
|
286
|
+
const publishFlags = parseFlags(rest, PUBLISH_FLAGS).flags;
|
|
287
|
+
return runTimelinePublishCli(
|
|
288
|
+
deps.config,
|
|
289
|
+
{
|
|
290
|
+
date: flagString(publishFlags, "--date"),
|
|
291
|
+
week: flagString(publishFlags, "--week"),
|
|
292
|
+
what: flagString(publishFlags, "--what"),
|
|
293
|
+
dryRun: publishFlags.get("--dry-run") === true,
|
|
294
|
+
},
|
|
295
|
+
io,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
262
298
|
if (deps.timelineEnabled === false || !deps.qa.enabled) {
|
|
263
299
|
io.stderr.write("timeline qa disabled — set activity.timeline.qa.enabled=true\n");
|
|
264
300
|
return 1;
|
|
265
301
|
}
|
|
266
|
-
const { flags } = parseFlags(rest);
|
|
302
|
+
const { flags } = parseFlags(rest, command === "range" ? RANGE_FLAGS : SEARCH_FLAGS);
|
|
267
303
|
if (command === "range") {
|
|
268
304
|
const from = flagString(flags, "--from");
|
|
269
305
|
const to = flagString(flags, "--to");
|
|
@@ -305,7 +341,7 @@ export async function runTimelineCliCommand(
|
|
|
305
341
|
io.stdout.write(`${JSON.stringify(result)}\n`);
|
|
306
342
|
return result.ok ? 0 : 1;
|
|
307
343
|
}
|
|
308
|
-
throw new TypeError(`unknown command ${command} (valid: range, search)`);
|
|
344
|
+
throw new TypeError(`unknown command ${command} (valid: range, search, publish)`);
|
|
309
345
|
} catch (err) {
|
|
310
346
|
io.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
311
347
|
return 1;
|
package/src/activity/types.ts
CHANGED
|
@@ -105,6 +105,50 @@ export interface ActivityTimelineJournalConfig {
|
|
|
105
105
|
heading?: string;
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/** Section ownership strategy for vault publishing (issue #1985). */
|
|
109
|
+
export type VaultSectionStrategy = "markers" | "heading";
|
|
110
|
+
|
|
111
|
+
/** Where section stats are written (issue #1985). `off` writes none. */
|
|
112
|
+
export type VaultPropertiesMode = "off" | "frontmatter" | "dataview-inline";
|
|
113
|
+
|
|
114
|
+
export interface ActivityTimelineVaultTargetConfig {
|
|
115
|
+
enabled: boolean;
|
|
116
|
+
target: "daily" | "weekly";
|
|
117
|
+
section: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Opt-in markdown-vault publisher settings (issue #1985). Default off:
|
|
122
|
+
* `enabled: false` means no vault reads or writes ever occur.
|
|
123
|
+
*/
|
|
124
|
+
export interface ActivityTimelineVaultConfig {
|
|
125
|
+
enabled: boolean;
|
|
126
|
+
/** Vault root; absolute or `~` path. Required and non-empty when enabled. */
|
|
127
|
+
vaultPath: string;
|
|
128
|
+
/** Vault-relative daily-note path template; date tokens. */
|
|
129
|
+
dailyNotePath: string;
|
|
130
|
+
/** Empty means weekly-note publishing is disabled. */
|
|
131
|
+
weeklyNotePath: string;
|
|
132
|
+
createMissingNotes: boolean;
|
|
133
|
+
/** Vault-relative template file, used only when creating a missing note. */
|
|
134
|
+
noteTemplate: string;
|
|
135
|
+
sectionStrategy: VaultSectionStrategy;
|
|
136
|
+
publish: {
|
|
137
|
+
timeline: ActivityTimelineVaultTargetConfig;
|
|
138
|
+
/** Standup target lights up when phase 3 lands. */
|
|
139
|
+
standup: ActivityTimelineVaultTargetConfig;
|
|
140
|
+
/** Weekly target lights up when phase 4 lands. */
|
|
141
|
+
weekly: ActivityTimelineVaultTargetConfig;
|
|
142
|
+
/** Locations target lights up with the location day renderer. */
|
|
143
|
+
locations: ActivityTimelineVaultTargetConfig;
|
|
144
|
+
};
|
|
145
|
+
/** Markers strategy: heading under which missing marker pairs are inserted; empty = never insert. */
|
|
146
|
+
insertUnderHeading: string;
|
|
147
|
+
wikilinks: { places: boolean; placesFolder: string };
|
|
148
|
+
properties: { mode: VaultPropertiesMode; prefix: string };
|
|
149
|
+
autoPublish: boolean;
|
|
150
|
+
}
|
|
151
|
+
|
|
108
152
|
/** Opt-in timeline Q&A range/search settings (issue #1983 PR1). Default off. */
|
|
109
153
|
export interface ActivityTimelineQaConfig {
|
|
110
154
|
enabled: boolean;
|
|
@@ -117,6 +161,7 @@ export interface ActivityTimelineConfig {
|
|
|
117
161
|
enabled: boolean;
|
|
118
162
|
journal: ActivityTimelineJournalConfig;
|
|
119
163
|
qa: ActivityTimelineQaConfig;
|
|
164
|
+
vault: ActivityTimelineVaultConfig;
|
|
120
165
|
}
|
|
121
166
|
|
|
122
167
|
/** Opt-in activity synchronization + trust-gated extraction settings. */
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
* Managed-region marker insertion (issue #1985).
|
|
3
3
|
*
|
|
4
4
|
* Inserts a `<!-- remnic:<name>:start/end -->` pair under a unique ATX
|
|
5
|
-
* heading when the region is absent.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* heading when the region is absent. Code blocks — fenced or indented —
|
|
6
|
+
* are invisible to
|
|
7
|
+
* the heading scan, so a live region is never inserted into a `## …`
|
|
8
|
+
* example. This helper never replaces (see `vault-publish.ts`), never
|
|
9
|
+
* creates files, and never invents a heading. Pure string-in/string-out.
|
|
8
10
|
*/
|
|
11
|
+
import { fileLines, parseAtxHeading } from "./vault-publish.js";
|
|
9
12
|
|
|
10
13
|
export type InsertVaultRegionResult =
|
|
11
14
|
| { ok: true; text: string; inserted: true }
|
|
@@ -26,13 +29,17 @@ export function insertMarkersUnderHeading(
|
|
|
26
29
|
|
|
27
30
|
const startMarker = `<!-- remnic:${opts.name}:start -->`;
|
|
28
31
|
const endMarker = `<!-- remnic:${opts.name}:end -->`;
|
|
29
|
-
if (fileText.includes(startMarker) && fileText.includes(endMarker)) {
|
|
30
|
-
return { ok: true, text: fileText, inserted: false };
|
|
31
|
-
}
|
|
32
|
-
|
|
33
32
|
const rows = fileLines(fileText);
|
|
33
|
+
// A fenced example pair is sample text, not a live region (issue #1985):
|
|
34
|
+
// only markers outside a code block count as "already present".
|
|
35
|
+
const present =
|
|
36
|
+
rows.some((row) => !row.fenced && row.line.includes(startMarker)) &&
|
|
37
|
+
rows.some((row) => !row.fenced && row.line.includes(endMarker));
|
|
38
|
+
if (present) return { ok: true, text: fileText, inserted: false };
|
|
39
|
+
|
|
34
40
|
const hits: Array<{ row: (typeof rows)[number]; level: number }> = [];
|
|
35
41
|
for (const row of rows) {
|
|
42
|
+
if (row.fenced) continue;
|
|
36
43
|
const heading = parseAtxHeading(row.line);
|
|
37
44
|
if (heading && heading.text === opts.heading) {
|
|
38
45
|
hits.push({ row, level: heading.level });
|
|
@@ -58,29 +65,3 @@ export function insertMarkersUnderHeading(
|
|
|
58
65
|
return { ok: true, text, inserted: true };
|
|
59
66
|
}
|
|
60
67
|
|
|
61
|
-
type FileLine = { line: string; start: number; next: number; number: number };
|
|
62
|
-
|
|
63
|
-
function fileLines(fileText: string): FileLine[] {
|
|
64
|
-
const rows: FileLine[] = [];
|
|
65
|
-
let start = 0;
|
|
66
|
-
let number = 1;
|
|
67
|
-
while (start < fileText.length) {
|
|
68
|
-
let i = start;
|
|
69
|
-
while (i < fileText.length && fileText[i] !== "\n" && fileText[i] !== "\r") i++;
|
|
70
|
-
let next = i;
|
|
71
|
-
if (fileText[i] === "\r" && fileText[i + 1] === "\n") next = i + 2;
|
|
72
|
-
else if (fileText[i] === "\n" || fileText[i] === "\r") next = i + 1;
|
|
73
|
-
else next = fileText.length;
|
|
74
|
-
rows.push({ line: fileText.slice(start, i), start, next, number });
|
|
75
|
-
number += 1;
|
|
76
|
-
start = next;
|
|
77
|
-
}
|
|
78
|
-
return rows;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function parseAtxHeading(line: string): { level: number; text: string } | null {
|
|
82
|
-
let level = 0;
|
|
83
|
-
while (level < line.length && level < 6 && line[level] === "#") level++;
|
|
84
|
-
if (level === 0 || line[level] !== " ") return null;
|
|
85
|
-
return { level, text: line.slice(level + 1) };
|
|
86
|
-
}
|
|
@@ -2,34 +2,56 @@
|
|
|
2
2
|
* Vault note path templates (issue #1985).
|
|
3
3
|
*
|
|
4
4
|
* Expands date tokens on a YYYY-MM-DD day. Returns a vault-relative path.
|
|
5
|
-
* Rejects `..` segments and
|
|
5
|
+
* Rejects `..` segments, absolute templates, and unknown `{...}` tokens —
|
|
6
|
+
* naming the token and the full valid set (never a silent pass-through).
|
|
6
7
|
*/
|
|
7
8
|
import path from "node:path";
|
|
8
9
|
|
|
9
10
|
import { assertValidTimezone, isValidActivityDate } from "./digest.js";
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
/** Every token a vault template accepts (English names in v1). */
|
|
13
|
+
export const VAULT_TEMPLATE_TOKENS = [
|
|
14
|
+
"{yyyy}",
|
|
15
|
+
"{yy}",
|
|
16
|
+
"{M}",
|
|
17
|
+
"{MM}",
|
|
18
|
+
"{d}",
|
|
19
|
+
"{dd}",
|
|
20
|
+
"{ww}",
|
|
21
|
+
"{MMM}",
|
|
22
|
+
"{MMMM}",
|
|
23
|
+
"{ddd}",
|
|
24
|
+
"{dddd}",
|
|
25
|
+
] as const;
|
|
12
26
|
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
export type VaultTemplateToken = (typeof VAULT_TEMPLATE_TOKENS)[number];
|
|
28
|
+
|
|
29
|
+
const TOKEN_LOOKUP: Record<string, true> = Object.fromEntries(VAULT_TEMPLATE_TOKENS.map((t) => [t, true]));
|
|
30
|
+
|
|
31
|
+
const ANY_BRACED_RE = /\{[^{}]*\}/g;
|
|
32
|
+
|
|
33
|
+
const MONTH_NAMES = [
|
|
34
|
+
"January",
|
|
35
|
+
"February",
|
|
36
|
+
"March",
|
|
37
|
+
"April",
|
|
38
|
+
"May",
|
|
39
|
+
"June",
|
|
40
|
+
"July",
|
|
41
|
+
"August",
|
|
42
|
+
"September",
|
|
43
|
+
"October",
|
|
44
|
+
"November",
|
|
45
|
+
"December",
|
|
46
|
+
] as const;
|
|
47
|
+
|
|
48
|
+
const WEEKDAY_NAMES = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"] as const;
|
|
30
49
|
|
|
50
|
+
function templateValues(date: string): Record<string, string> {
|
|
31
51
|
const [yyyy, mm, dd] = date.split("-");
|
|
32
|
-
const
|
|
52
|
+
const monthIndex = Number(mm) - 1;
|
|
53
|
+
const weekday = new Date(Date.UTC(Number(yyyy), monthIndex, Number(dd))).getUTCDay();
|
|
54
|
+
return {
|
|
33
55
|
"{yyyy}": yyyy,
|
|
34
56
|
"{yy}": yyyy.slice(2),
|
|
35
57
|
"{MM}": mm,
|
|
@@ -37,11 +59,67 @@ export function resolveVaultNotePath(
|
|
|
37
59
|
"{M}": String(Number(mm)),
|
|
38
60
|
"{d}": String(Number(dd)),
|
|
39
61
|
"{ww}": isoWeekNumber(date),
|
|
62
|
+
"{MMM}": MONTH_NAMES[monthIndex]!.slice(0, 3),
|
|
63
|
+
"{MMMM}": MONTH_NAMES[monthIndex]!,
|
|
64
|
+
"{ddd}": WEEKDAY_NAMES[weekday]!.slice(0, 3),
|
|
65
|
+
"{dddd}": WEEKDAY_NAMES[weekday]!,
|
|
40
66
|
};
|
|
41
|
-
|
|
42
|
-
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Expand vault date tokens in any template text (a note path or a note
|
|
71
|
+
* template body). Unknown `{...}` tokens are rejected naming the token and
|
|
72
|
+
* the full valid set; an unreplaced token in a path would silently publish
|
|
73
|
+
* to the wrong file.
|
|
74
|
+
*/
|
|
75
|
+
export function expandVaultTemplateTokens(text: string, date: string): string {
|
|
76
|
+
if (!isValidActivityDate(date)) {
|
|
77
|
+
throw new RangeError(`Invalid vault date "${date}"; expected YYYY-MM-DD.`);
|
|
78
|
+
}
|
|
79
|
+
if (typeof text !== "string" || text.length === 0) {
|
|
80
|
+
throw new RangeError("Vault template must be a non-empty string.");
|
|
81
|
+
}
|
|
82
|
+
const values = templateValues(date);
|
|
83
|
+
return text.replace(ANY_BRACED_RE, (token) => {
|
|
84
|
+
if (TOKEN_LOOKUP[token] === true) return values[token]!;
|
|
85
|
+
throw new RangeError(
|
|
86
|
+
`Unknown vault template token ${token}; valid tokens are ${VAULT_TEMPLATE_TOKENS.join(" ")}.`,
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Validate a vault note path template without resolving it to a date:
|
|
93
|
+
* token allow-list plus the relative/no-`..` shape checks shared with
|
|
94
|
+
* `resolveVaultNotePath`.
|
|
95
|
+
*/
|
|
96
|
+
export function validateVaultNoteTemplate(template: string): void {
|
|
97
|
+
if (typeof template !== "string" || template.length === 0) {
|
|
98
|
+
throw new RangeError("Vault note path template must be a non-empty relative path.");
|
|
99
|
+
}
|
|
100
|
+
if (path.posix.isAbsolute(template) || path.win32.isAbsolute(template)) {
|
|
101
|
+
throw new RangeError("Vault note path template must be relative; absolute templates are rejected.");
|
|
102
|
+
}
|
|
103
|
+
if (template.split(/[\\/]/).some((segment) => segment === "..")) {
|
|
43
104
|
throw new RangeError("Vault note path template must not contain `..` segments.");
|
|
44
105
|
}
|
|
106
|
+
expandVaultTemplateTokens(template, "2026-01-04");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function resolveVaultNotePath(
|
|
110
|
+
template: string,
|
|
111
|
+
date: string,
|
|
112
|
+
options?: { timezone?: string },
|
|
113
|
+
): string {
|
|
114
|
+
if (!isValidActivityDate(date)) {
|
|
115
|
+
throw new RangeError(`Invalid vault date "${date}"; expected YYYY-MM-DD.`);
|
|
116
|
+
}
|
|
117
|
+
if (options?.timezone !== undefined) {
|
|
118
|
+
assertValidTimezone(options.timezone);
|
|
119
|
+
}
|
|
120
|
+
validateVaultNoteTemplate(template);
|
|
121
|
+
|
|
122
|
+
const expanded = expandVaultTemplateTokens(template, date);
|
|
45
123
|
if (path.posix.isAbsolute(expanded) || path.win32.isAbsolute(expanded)) {
|
|
46
124
|
throw new RangeError("Vault note path template must be relative; absolute templates are rejected.");
|
|
47
125
|
}
|
|
@@ -266,3 +266,54 @@ test("applyManagedRegion heading refuses duplicate headings and lists line numbe
|
|
|
266
266
|
});
|
|
267
267
|
assert.deepEqual(result, { ok: false, reason: "duplicate_heading", lines: [3, 6], text: fileText });
|
|
268
268
|
});
|
|
269
|
+
|
|
270
|
+
test("applyManagedRegion heading demotes owned headings below the owning heading, clamped at six", () => {
|
|
271
|
+
const first = applyManagedRegion("## Timeline\nold\n\n## Notes\nkeep\n", {
|
|
272
|
+
strategy: "heading",
|
|
273
|
+
name: "Timeline",
|
|
274
|
+
content: "# Journal\n\n## Cards\n\n- a",
|
|
275
|
+
});
|
|
276
|
+
assert.equal(first.ok, true);
|
|
277
|
+
if (!first.ok) return;
|
|
278
|
+
assert.equal(first.text, "## Timeline\n### Journal\n\n#### Cards\n\n- a\n## Notes\nkeep\n");
|
|
279
|
+
|
|
280
|
+
const second = applyManagedRegion(first.text, {
|
|
281
|
+
strategy: "heading",
|
|
282
|
+
name: "Timeline",
|
|
283
|
+
content: "# Journal\n\n## Cards\n\n- a",
|
|
284
|
+
});
|
|
285
|
+
assert.equal(second.ok, true);
|
|
286
|
+
if (!second.ok) return;
|
|
287
|
+
assert.equal(second.text, first.text);
|
|
288
|
+
|
|
289
|
+
// A recap heading already at six stays six — still strictly deeper than
|
|
290
|
+
// a level-2 owning heading, so it cannot terminate the region either.
|
|
291
|
+
const clamped = applyManagedRegion("## Timeline\nold\n", {
|
|
292
|
+
strategy: "heading",
|
|
293
|
+
name: "Timeline",
|
|
294
|
+
content: "###### deep",
|
|
295
|
+
});
|
|
296
|
+
assert.equal(clamped.ok, true);
|
|
297
|
+
if (!clamped.ok) return;
|
|
298
|
+
assert.equal(clamped.text, "## Timeline\n###### deep\n");
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
test("applyManagedRegion heading under a level-6 owner flattens owned headings to bold text", () => {
|
|
302
|
+
const first = applyManagedRegion("###### Timeline\nold\n", {
|
|
303
|
+
strategy: "heading",
|
|
304
|
+
name: "Timeline",
|
|
305
|
+
content: "# Journal\nbody",
|
|
306
|
+
});
|
|
307
|
+
assert.equal(first.ok, true);
|
|
308
|
+
if (!first.ok) return;
|
|
309
|
+
assert.equal(first.text, "###### Timeline\n**Journal**\nbody\n");
|
|
310
|
+
|
|
311
|
+
const second = applyManagedRegion(first.text, {
|
|
312
|
+
strategy: "heading",
|
|
313
|
+
name: "Timeline",
|
|
314
|
+
content: "# Journal\nbody",
|
|
315
|
+
});
|
|
316
|
+
assert.equal(second.ok, true);
|
|
317
|
+
if (!second.ok) return;
|
|
318
|
+
assert.equal(second.text, first.text);
|
|
319
|
+
});
|