@pcircle/footprint 1.3.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +215 -137
- package/SKILL.md +77 -33
- package/bin/footprint.js +16 -0
- package/dist/src/adapters/claude.d.ts +2 -0
- package/dist/src/adapters/claude.d.ts.map +1 -0
- package/dist/src/adapters/claude.js +7 -0
- package/dist/src/adapters/claude.js.map +1 -0
- package/dist/src/adapters/codex.d.ts +2 -0
- package/dist/src/adapters/codex.d.ts.map +1 -0
- package/dist/src/adapters/codex.js +7 -0
- package/dist/src/adapters/codex.js.map +1 -0
- package/dist/src/adapters/gemini.d.ts +2 -0
- package/dist/src/adapters/gemini.d.ts.map +1 -0
- package/dist/src/adapters/gemini.js +7 -0
- package/dist/src/adapters/gemini.js.map +1 -0
- package/dist/src/adapters/index.d.ts +5 -0
- package/dist/src/adapters/index.d.ts.map +1 -0
- package/dist/src/adapters/index.js +12 -0
- package/dist/src/adapters/index.js.map +1 -0
- package/dist/src/adapters/structured-prefix.d.ts +10 -0
- package/dist/src/adapters/structured-prefix.d.ts.map +1 -0
- package/dist/src/adapters/structured-prefix.js +59 -0
- package/dist/src/adapters/structured-prefix.js.map +1 -0
- package/dist/src/adapters/types.d.ts +32 -0
- package/dist/src/adapters/types.d.ts.map +1 -0
- package/dist/src/adapters/types.js +2 -0
- package/dist/src/adapters/types.js.map +1 -0
- package/dist/src/analyzers/content-analyzer.d.ts.map +1 -1
- package/dist/src/analyzers/content-analyzer.js +20 -4
- package/dist/src/analyzers/content-analyzer.js.map +1 -1
- package/dist/src/cli/context-flow.d.ts +92 -0
- package/dist/src/cli/context-flow.d.ts.map +1 -0
- package/dist/src/cli/context-flow.js +724 -0
- package/dist/src/cli/context-flow.js.map +1 -0
- package/dist/src/cli/history-display.d.ts +27 -0
- package/dist/src/cli/history-display.d.ts.map +1 -0
- package/dist/src/cli/history-display.js +167 -0
- package/dist/src/cli/history-display.js.map +1 -0
- package/dist/src/cli/index.js +924 -0
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/launch-spec.d.ts +31 -0
- package/dist/src/cli/launch-spec.d.ts.map +1 -0
- package/dist/src/cli/launch-spec.js +182 -0
- package/dist/src/cli/launch-spec.js.map +1 -0
- package/dist/src/cli/live-demo.d.ts +34 -0
- package/dist/src/cli/live-demo.d.ts.map +1 -0
- package/dist/src/cli/live-demo.js +254 -0
- package/dist/src/cli/live-demo.js.map +1 -0
- package/dist/src/cli/pty-transcript.d.ts +34 -0
- package/dist/src/cli/pty-transcript.d.ts.map +1 -0
- package/dist/src/cli/pty-transcript.js +174 -0
- package/dist/src/cli/pty-transcript.js.map +1 -0
- package/dist/src/cli/session-display.d.ts +74 -0
- package/dist/src/cli/session-display.d.ts.map +1 -0
- package/dist/src/cli/session-display.js +922 -0
- package/dist/src/cli/session-display.js.map +1 -0
- package/dist/src/cli/session-execution.d.ts +55 -0
- package/dist/src/cli/session-execution.d.ts.map +1 -0
- package/dist/src/cli/session-execution.js +817 -0
- package/dist/src/cli/session-execution.js.map +1 -0
- package/dist/src/cli/session-runtime.d.ts +5 -0
- package/dist/src/cli/session-runtime.d.ts.map +1 -0
- package/dist/src/cli/session-runtime.js +11 -0
- package/dist/src/cli/session-runtime.js.map +1 -0
- package/dist/src/cli/setup.d.ts.map +1 -1
- package/dist/src/cli/setup.js +36 -12
- package/dist/src/cli/setup.js.map +1 -1
- package/dist/src/cli/utils/env.d.ts +7 -2
- package/dist/src/cli/utils/env.d.ts.map +1 -1
- package/dist/src/cli/utils/env.js +37 -6
- package/dist/src/cli/utils/env.js.map +1 -1
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +187 -33
- package/dist/src/index.js.map +1 -1
- package/dist/src/ingestion/deterministic.d.ts +3 -0
- package/dist/src/ingestion/deterministic.d.ts.map +1 -0
- package/dist/src/ingestion/deterministic.js +862 -0
- package/dist/src/ingestion/deterministic.js.map +1 -0
- package/dist/src/ingestion/index.d.ts +5 -0
- package/dist/src/ingestion/index.d.ts.map +1 -0
- package/dist/src/ingestion/index.js +27 -0
- package/dist/src/ingestion/index.js.map +1 -0
- package/dist/src/ingestion/semantic.d.ts +6 -0
- package/dist/src/ingestion/semantic.d.ts.map +1 -0
- package/dist/src/ingestion/semantic.js +627 -0
- package/dist/src/ingestion/semantic.js.map +1 -0
- package/dist/src/ingestion/types.d.ts +10 -0
- package/dist/src/ingestion/types.d.ts.map +1 -0
- package/dist/src/ingestion/types.js +2 -0
- package/dist/src/ingestion/types.js.map +1 -0
- package/dist/src/lib/context-memory.d.ts +140 -0
- package/dist/src/lib/context-memory.d.ts.map +1 -0
- package/dist/src/lib/context-memory.js +974 -0
- package/dist/src/lib/context-memory.js.map +1 -0
- package/dist/src/lib/crypto/decrypt.d.ts.map +1 -1
- package/dist/src/lib/crypto/decrypt.js +12 -8
- package/dist/src/lib/crypto/decrypt.js.map +1 -1
- package/dist/src/lib/crypto/encrypt.d.ts.map +1 -1
- package/dist/src/lib/crypto/encrypt.js +6 -3
- package/dist/src/lib/crypto/encrypt.js.map +1 -1
- package/dist/src/lib/crypto/key-derivation.d.ts +1 -1
- package/dist/src/lib/crypto/key-derivation.d.ts.map +1 -1
- package/dist/src/lib/crypto/key-derivation.js +11 -11
- package/dist/src/lib/crypto/key-derivation.js.map +1 -1
- package/dist/src/lib/history-handoff.d.ts +43 -0
- package/dist/src/lib/history-handoff.d.ts.map +1 -0
- package/dist/src/lib/history-handoff.js +179 -0
- package/dist/src/lib/history-handoff.js.map +1 -0
- package/dist/src/lib/observability.d.ts +3 -0
- package/dist/src/lib/observability.d.ts.map +1 -0
- package/dist/src/lib/observability.js +63 -0
- package/dist/src/lib/observability.js.map +1 -0
- package/dist/src/lib/session-artifacts.d.ts +51 -0
- package/dist/src/lib/session-artifacts.d.ts.map +1 -0
- package/dist/src/lib/session-artifacts.js +132 -0
- package/dist/src/lib/session-artifacts.js.map +1 -0
- package/dist/src/lib/session-filters.d.ts +11 -0
- package/dist/src/lib/session-filters.d.ts.map +1 -0
- package/dist/src/lib/session-filters.js +16 -0
- package/dist/src/lib/session-filters.js.map +1 -0
- package/dist/src/lib/session-history.d.ts +50 -0
- package/dist/src/lib/session-history.d.ts.map +1 -0
- package/dist/src/lib/session-history.js +73 -0
- package/dist/src/lib/session-history.js.map +1 -0
- package/dist/src/lib/session-trends.d.ts +129 -0
- package/dist/src/lib/session-trends.d.ts.map +1 -0
- package/dist/src/lib/session-trends.js +361 -0
- package/dist/src/lib/session-trends.js.map +1 -0
- package/dist/src/lib/storage/database.d.ts +257 -3
- package/dist/src/lib/storage/database.d.ts.map +1 -1
- package/dist/src/lib/storage/database.js +1836 -161
- package/dist/src/lib/storage/database.js.map +1 -1
- package/dist/src/lib/storage/export-sessions.d.ts +33 -0
- package/dist/src/lib/storage/export-sessions.d.ts.map +1 -0
- package/dist/src/lib/storage/export-sessions.js +525 -0
- package/dist/src/lib/storage/export-sessions.js.map +1 -0
- package/dist/src/lib/storage/export.d.ts +1 -2
- package/dist/src/lib/storage/export.d.ts.map +1 -1
- package/dist/src/lib/storage/export.js +46 -33
- package/dist/src/lib/storage/export.js.map +1 -1
- package/dist/src/lib/storage/index.d.ts +7 -6
- package/dist/src/lib/storage/index.d.ts.map +1 -1
- package/dist/src/lib/storage/index.js +6 -5
- package/dist/src/lib/storage/index.js.map +1 -1
- package/dist/src/lib/storage/salt-storage.d.ts +1 -1
- package/dist/src/lib/storage/salt-storage.d.ts.map +1 -1
- package/dist/src/lib/storage/salt-storage.js +26 -18
- package/dist/src/lib/storage/salt-storage.js.map +1 -1
- package/dist/src/lib/storage/schema.d.ts +7 -2
- package/dist/src/lib/storage/schema.d.ts.map +1 -1
- package/dist/src/lib/storage/schema.js +357 -40
- package/dist/src/lib/storage/schema.js.map +1 -1
- package/dist/src/lib/storage/types.d.ts +122 -0
- package/dist/src/lib/storage/types.d.ts.map +1 -1
- package/dist/src/lib/tool-wrapper.d.ts.map +1 -1
- package/dist/src/lib/tool-wrapper.js +2 -2
- package/dist/src/lib/tool-wrapper.js.map +1 -1
- package/dist/src/prompts/skill-prompt.d.ts +6 -0
- package/dist/src/prompts/skill-prompt.d.ts.map +1 -0
- package/dist/src/prompts/skill-prompt.js +138 -0
- package/dist/src/prompts/skill-prompt.js.map +1 -0
- package/dist/src/tools/capture-footprint.d.ts +2 -2
- package/dist/src/tools/capture-footprint.d.ts.map +1 -1
- package/dist/src/tools/capture-footprint.js +52 -11
- package/dist/src/tools/capture-footprint.js.map +1 -1
- package/dist/src/tools/confirm-context-link.d.ts +62 -0
- package/dist/src/tools/confirm-context-link.d.ts.map +1 -0
- package/dist/src/tools/confirm-context-link.js +36 -0
- package/dist/src/tools/confirm-context-link.js.map +1 -0
- package/dist/src/tools/context-schemas.d.ts +694 -0
- package/dist/src/tools/context-schemas.d.ts.map +1 -0
- package/dist/src/tools/context-schemas.js +171 -0
- package/dist/src/tools/context-schemas.js.map +1 -0
- package/dist/src/tools/delete-footprints.d.ts +18 -1
- package/dist/src/tools/delete-footprints.d.ts.map +1 -1
- package/dist/src/tools/delete-footprints.js +53 -5
- package/dist/src/tools/delete-footprints.js.map +1 -1
- package/dist/src/tools/export-footprints.d.ts +11 -3
- package/dist/src/tools/export-footprints.d.ts.map +1 -1
- package/dist/src/tools/export-footprints.js +48 -9
- package/dist/src/tools/export-footprints.js.map +1 -1
- package/dist/src/tools/export-sessions.d.ts +111 -0
- package/dist/src/tools/export-sessions.d.ts.map +1 -0
- package/dist/src/tools/export-sessions.js +136 -0
- package/dist/src/tools/export-sessions.js.map +1 -0
- package/dist/src/tools/get-context.d.ts +208 -0
- package/dist/src/tools/get-context.d.ts.map +1 -0
- package/dist/src/tools/get-context.js +27 -0
- package/dist/src/tools/get-context.js.map +1 -0
- package/dist/src/tools/get-footprint.d.ts +1 -7
- package/dist/src/tools/get-footprint.d.ts.map +1 -1
- package/dist/src/tools/get-footprint.js +7 -3
- package/dist/src/tools/get-footprint.js.map +1 -1
- package/dist/src/tools/get-history-handoff.d.ts +109 -0
- package/dist/src/tools/get-history-handoff.d.ts.map +1 -0
- package/dist/src/tools/get-history-handoff.js +85 -0
- package/dist/src/tools/get-history-handoff.js.map +1 -0
- package/dist/src/tools/get-history-trends.d.ts +155 -0
- package/dist/src/tools/get-history-trends.d.ts.map +1 -0
- package/dist/src/tools/get-history-trends.js +123 -0
- package/dist/src/tools/get-history-trends.js.map +1 -0
- package/dist/src/tools/get-session-artifacts.d.ts +151 -0
- package/dist/src/tools/get-session-artifacts.d.ts.map +1 -0
- package/dist/src/tools/get-session-artifacts.js +184 -0
- package/dist/src/tools/get-session-artifacts.js.map +1 -0
- package/dist/src/tools/get-session-decisions.d.ts +69 -0
- package/dist/src/tools/get-session-decisions.d.ts.map +1 -0
- package/dist/src/tools/get-session-decisions.js +99 -0
- package/dist/src/tools/get-session-decisions.js.map +1 -0
- package/dist/src/tools/get-session-messages.d.ts +55 -0
- package/dist/src/tools/get-session-messages.d.ts.map +1 -0
- package/dist/src/tools/get-session-messages.js +89 -0
- package/dist/src/tools/get-session-messages.js.map +1 -0
- package/dist/src/tools/get-session-narrative.d.ts +72 -0
- package/dist/src/tools/get-session-narrative.d.ts.map +1 -0
- package/dist/src/tools/get-session-narrative.js +106 -0
- package/dist/src/tools/get-session-narrative.js.map +1 -0
- package/dist/src/tools/get-session-timeline.d.ts +55 -0
- package/dist/src/tools/get-session-timeline.d.ts.map +1 -0
- package/dist/src/tools/get-session-timeline.js +93 -0
- package/dist/src/tools/get-session-timeline.js.map +1 -0
- package/dist/src/tools/get-session-trends.d.ts +108 -0
- package/dist/src/tools/get-session-trends.d.ts.map +1 -0
- package/dist/src/tools/get-session-trends.js +130 -0
- package/dist/src/tools/get-session-trends.js.map +1 -0
- package/dist/src/tools/get-session.d.ts +251 -0
- package/dist/src/tools/get-session.d.ts.map +1 -0
- package/dist/src/tools/get-session.js +290 -0
- package/dist/src/tools/get-session.js.map +1 -0
- package/dist/src/tools/index.d.ts +23 -3
- package/dist/src/tools/index.d.ts.map +1 -1
- package/dist/src/tools/index.js +23 -3
- package/dist/src/tools/index.js.map +1 -1
- package/dist/src/tools/list-contexts.d.ts +50 -0
- package/dist/src/tools/list-contexts.d.ts.map +1 -0
- package/dist/src/tools/list-contexts.js +28 -0
- package/dist/src/tools/list-contexts.js.map +1 -0
- package/dist/src/tools/list-footprints.d.ts +1 -15
- package/dist/src/tools/list-footprints.d.ts.map +1 -1
- package/dist/src/tools/list-footprints.js +17 -6
- package/dist/src/tools/list-footprints.js.map +1 -1
- package/dist/src/tools/list-sessions.d.ts +86 -0
- package/dist/src/tools/list-sessions.d.ts.map +1 -0
- package/dist/src/tools/list-sessions.js +97 -0
- package/dist/src/tools/list-sessions.js.map +1 -0
- package/dist/src/tools/manage-tags.d.ts +47 -0
- package/dist/src/tools/manage-tags.d.ts.map +1 -0
- package/dist/src/tools/manage-tags.js +109 -0
- package/dist/src/tools/manage-tags.js.map +1 -0
- package/dist/src/tools/merge-contexts.d.ts +58 -0
- package/dist/src/tools/merge-contexts.d.ts.map +1 -0
- package/dist/src/tools/merge-contexts.js +27 -0
- package/dist/src/tools/merge-contexts.js.map +1 -0
- package/dist/src/tools/move-session-context.d.ts +62 -0
- package/dist/src/tools/move-session-context.d.ts.map +1 -0
- package/dist/src/tools/move-session-context.js +33 -0
- package/dist/src/tools/move-session-context.js.map +1 -0
- package/dist/src/tools/reingest-session.d.ts +31 -0
- package/dist/src/tools/reingest-session.d.ts.map +1 -0
- package/dist/src/tools/reingest-session.js +43 -0
- package/dist/src/tools/reingest-session.js.map +1 -0
- package/dist/src/tools/reject-context-link.d.ts +58 -0
- package/dist/src/tools/reject-context-link.d.ts.map +1 -0
- package/dist/src/tools/reject-context-link.js +26 -0
- package/dist/src/tools/reject-context-link.js.map +1 -0
- package/dist/src/tools/resolve-context.d.ts +287 -0
- package/dist/src/tools/resolve-context.d.ts.map +1 -0
- package/dist/src/tools/resolve-context.js +35 -0
- package/dist/src/tools/resolve-context.js.map +1 -0
- package/dist/src/tools/search-footprints.d.ts +2 -16
- package/dist/src/tools/search-footprints.d.ts.map +1 -1
- package/dist/src/tools/search-footprints.js +23 -7
- package/dist/src/tools/search-footprints.js.map +1 -1
- package/dist/src/tools/search-history.d.ts +86 -0
- package/dist/src/tools/search-history.d.ts.map +1 -0
- package/dist/src/tools/search-history.js +103 -0
- package/dist/src/tools/search-history.js.map +1 -0
- package/dist/src/tools/session-ui-metadata.d.ts +15 -0
- package/dist/src/tools/session-ui-metadata.d.ts.map +1 -0
- package/dist/src/tools/session-ui-metadata.js +15 -0
- package/dist/src/tools/session-ui-metadata.js.map +1 -0
- package/dist/src/tools/set-active-context.d.ts +58 -0
- package/dist/src/tools/set-active-context.d.ts.map +1 -0
- package/dist/src/tools/set-active-context.js +26 -0
- package/dist/src/tools/set-active-context.js.map +1 -0
- package/dist/src/tools/split-context.d.ts +62 -0
- package/dist/src/tools/split-context.d.ts.map +1 -0
- package/dist/src/tools/split-context.js +36 -0
- package/dist/src/tools/split-context.js.map +1 -0
- package/dist/src/tools/suggest-capture.d.ts +1 -1
- package/dist/src/tools/suggest-capture.d.ts.map +1 -1
- package/dist/src/tools/suggest-capture.js +6 -2
- package/dist/src/tools/suggest-capture.js.map +1 -1
- package/dist/src/tools/verify-footprint.d.ts +7 -54
- package/dist/src/tools/verify-footprint.d.ts.map +1 -1
- package/dist/src/tools/verify-footprint.js +11 -8
- package/dist/src/tools/verify-footprint.js.map +1 -1
- package/dist/src/types.d.ts +6 -4
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/ui/register.d.ts +6 -1
- package/dist/src/ui/register.d.ts.map +1 -1
- package/dist/src/ui/register.js +60 -16
- package/dist/src/ui/register.js.map +1 -1
- package/dist/ui/dashboard.html +259 -875
- package/dist/ui/detail.html +124 -252
- package/dist/ui/export.html +133 -303
- package/dist/ui/session-dashboard-live.html +264 -0
- package/dist/ui/session-dashboard.html +329 -0
- package/dist/ui/session-detail-live.html +336 -0
- package/dist/ui/session-detail.html +355 -0
- package/package.json +61 -16
- package/dist/src/lib/errors/base-error.d.ts +0 -15
- package/dist/src/lib/errors/base-error.d.ts.map +0 -1
- package/dist/src/lib/errors/base-error.js +0 -34
- package/dist/src/lib/errors/base-error.js.map +0 -1
- package/dist/src/lib/errors/crypto-error.d.ts +0 -29
- package/dist/src/lib/errors/crypto-error.d.ts.map +0 -1
- package/dist/src/lib/errors/crypto-error.js +0 -43
- package/dist/src/lib/errors/crypto-error.js.map +0 -1
- package/dist/src/lib/errors/index.d.ts +0 -26
- package/dist/src/lib/errors/index.d.ts.map +0 -1
- package/dist/src/lib/errors/index.js +0 -26
- package/dist/src/lib/errors/index.js.map +0 -1
- package/dist/src/lib/errors/storage-error.d.ts +0 -25
- package/dist/src/lib/errors/storage-error.d.ts.map +0 -1
- package/dist/src/lib/errors/storage-error.js +0 -38
- package/dist/src/lib/errors/storage-error.js.map +0 -1
- package/dist/src/lib/errors/validation-error.d.ts +0 -21
- package/dist/src/lib/errors/validation-error.d.ts.map +0 -1
- package/dist/src/lib/errors/validation-error.js +0 -29
- package/dist/src/lib/errors/validation-error.js.map +0 -1
- package/dist/src/test-helpers.d.ts +0 -33
- package/dist/src/test-helpers.d.ts.map +0 -1
- package/dist/src/test-helpers.js +0 -108
- package/dist/src/test-helpers.js.map +0 -1
- package/dist/src/tools/get-tag-stats.d.ts +0 -30
- package/dist/src/tools/get-tag-stats.d.ts.map +0 -1
- package/dist/src/tools/get-tag-stats.js +0 -33
- package/dist/src/tools/get-tag-stats.js.map +0 -1
- package/dist/src/tools/remove-tag.d.ts +0 -22
- package/dist/src/tools/remove-tag.d.ts.map +0 -1
- package/dist/src/tools/remove-tag.js +0 -30
- package/dist/src/tools/remove-tag.js.map +0 -1
- package/dist/src/tools/rename-tag.d.ts +0 -24
- package/dist/src/tools/rename-tag.d.ts.map +0 -1
- package/dist/src/tools/rename-tag.js +0 -34
- package/dist/src/tools/rename-tag.js.map +0 -1
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
/* global process */
|
|
2
|
+
import { createInterface } from "node:readline/promises";
|
|
3
|
+
import { confirmContextLink, getContextReport, listContexts, mergeContexts, moveSessionContext, rejectContextLink, resolveContext, setActiveContext, splitContext, } from "../lib/context-memory.js";
|
|
4
|
+
import { truncateSummary } from "../lib/session-history.js";
|
|
5
|
+
import { EvidenceDatabase, } from "../lib/storage/index.js";
|
|
6
|
+
import { appendTimelineEvent, ensureParentDir, resolveDbPath, } from "./session-execution.js";
|
|
7
|
+
import { printContextBriefing, printJson, printSection, } from "./session-display.js";
|
|
8
|
+
function canPromptInteractively(forceInteractive) {
|
|
9
|
+
return Boolean(forceInteractive || (process.stdin.isTTY && process.stderr.isTTY));
|
|
10
|
+
}
|
|
11
|
+
function shouldAskFollowUpQuestion() {
|
|
12
|
+
return Boolean(process.stdin.isTTY && process.stderr.isTTY);
|
|
13
|
+
}
|
|
14
|
+
async function promptChoice(options) {
|
|
15
|
+
process.stderr.write(`\n${options.title}\n`);
|
|
16
|
+
options.choices.forEach((choice, index) => {
|
|
17
|
+
process.stderr.write(` ${index + 1}. ${choice.label}\n`);
|
|
18
|
+
});
|
|
19
|
+
while (true) {
|
|
20
|
+
const answer = (await options.reader.question("Select an option: ")).trim();
|
|
21
|
+
const numeric = Number.parseInt(answer, 10);
|
|
22
|
+
if (Number.isInteger(numeric) &&
|
|
23
|
+
numeric >= 1 &&
|
|
24
|
+
numeric <= options.choices.length) {
|
|
25
|
+
return options.choices[numeric - 1].key;
|
|
26
|
+
}
|
|
27
|
+
const direct = options.choices.find((choice) => choice.key.toLowerCase() === answer.toLowerCase());
|
|
28
|
+
if (direct) {
|
|
29
|
+
return direct.key;
|
|
30
|
+
}
|
|
31
|
+
process.stderr.write("Invalid choice. Enter the number shown above.\n");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function promptOptionalText(reader, prompt) {
|
|
35
|
+
const answer = (await reader.question(`${prompt}: `)).trim();
|
|
36
|
+
return answer || null;
|
|
37
|
+
}
|
|
38
|
+
export function listContextsCli(options) {
|
|
39
|
+
const dbPath = resolveDbPath();
|
|
40
|
+
ensureParentDir(dbPath);
|
|
41
|
+
const db = new EvidenceDatabase(dbPath);
|
|
42
|
+
try {
|
|
43
|
+
const result = listContexts(db);
|
|
44
|
+
if (options?.json) {
|
|
45
|
+
printJson(result);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
console.log(`Contexts: ${result.contexts.length}`);
|
|
49
|
+
for (const context of result.contexts) {
|
|
50
|
+
console.log(`${context.id} | ${context.label} | sessions ${context.sessionCount} | latest ${context.latestSessionLabel}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
db.close();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export function showContextCli(id, options) {
|
|
58
|
+
const dbPath = resolveDbPath();
|
|
59
|
+
ensureParentDir(dbPath);
|
|
60
|
+
const db = new EvidenceDatabase(dbPath);
|
|
61
|
+
try {
|
|
62
|
+
const report = getContextReport(db, id);
|
|
63
|
+
if (options?.json) {
|
|
64
|
+
printJson(report);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
printContextBriefing(report);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
db.close();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
export function appendContextTimelineEvent(db, sessionId, eventSeq, input) {
|
|
74
|
+
return appendTimelineEvent(db, {
|
|
75
|
+
sessionId,
|
|
76
|
+
eventSeq,
|
|
77
|
+
eventType: input.eventType,
|
|
78
|
+
source: "wrapper",
|
|
79
|
+
summary: input.summary,
|
|
80
|
+
payload: input.payload,
|
|
81
|
+
startedAt: new Date().toISOString(),
|
|
82
|
+
endedAt: new Date().toISOString(),
|
|
83
|
+
status: input.status,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
async function applyInteractiveContextSelection(db, resolution, options) {
|
|
87
|
+
if (resolution.mode === "linked" &&
|
|
88
|
+
resolution.currentContext &&
|
|
89
|
+
resolution.briefing) {
|
|
90
|
+
return {
|
|
91
|
+
action: "used-linked",
|
|
92
|
+
contextId: resolution.currentContext.id,
|
|
93
|
+
report: resolution.briefing,
|
|
94
|
+
note: "Session already linked to a confirmed context.",
|
|
95
|
+
relatedSessionIds: [],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (resolution.mode === "preferred" &&
|
|
99
|
+
resolution.currentContext &&
|
|
100
|
+
resolution.briefing) {
|
|
101
|
+
return {
|
|
102
|
+
action: "used-preferred",
|
|
103
|
+
contextId: resolution.currentContext.id,
|
|
104
|
+
report: resolution.briefing,
|
|
105
|
+
note: "Using the preferred context for this workspace.",
|
|
106
|
+
relatedSessionIds: [],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (!options.allowInteractive) {
|
|
110
|
+
return {
|
|
111
|
+
action: resolution.candidates.length === 0 ? "none" : "skipped",
|
|
112
|
+
contextId: null,
|
|
113
|
+
report: resolution.briefing,
|
|
114
|
+
note: resolution.candidates.length === 0
|
|
115
|
+
? "No strong context candidate was found."
|
|
116
|
+
: "Context confirmation is required; rerun with interactive mode to confirm or correct the link.",
|
|
117
|
+
relatedSessionIds: [],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (!options.promptReader) {
|
|
121
|
+
throw new Error("Interactive context selection requires a prompt reader");
|
|
122
|
+
}
|
|
123
|
+
const choices = resolution.candidates.map((candidate, index) => ({
|
|
124
|
+
key: `candidate:${index}`,
|
|
125
|
+
label: `${candidate.label} [${candidate.kind}] | ${candidate.confidence} | ${candidate.reasons.join(", ") || "no reasons"}`,
|
|
126
|
+
}));
|
|
127
|
+
if (options.sessionId) {
|
|
128
|
+
choices.push({
|
|
129
|
+
key: "create:new",
|
|
130
|
+
label: "Create a new canonical context for this session",
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
else if (resolution.candidates.some((candidate) => candidate.kind === "existing-context")) {
|
|
134
|
+
choices.push({
|
|
135
|
+
key: "use:preferred",
|
|
136
|
+
label: "Set one of the candidate contexts as the preferred active context for this workspace",
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
choices.push({
|
|
140
|
+
key: "skip",
|
|
141
|
+
label: "Skip for now and leave the session/context unchanged",
|
|
142
|
+
});
|
|
143
|
+
const choice = await promptChoice({
|
|
144
|
+
reader: options.promptReader,
|
|
145
|
+
title: "Footprint found possible contexts. Confirm how this work should be grouped.",
|
|
146
|
+
choices,
|
|
147
|
+
});
|
|
148
|
+
if (choice === "skip") {
|
|
149
|
+
return {
|
|
150
|
+
action: "skipped",
|
|
151
|
+
contextId: null,
|
|
152
|
+
report: resolution.briefing,
|
|
153
|
+
note: "Left the session unlinked for now.",
|
|
154
|
+
relatedSessionIds: [],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
if (choice === "use:preferred") {
|
|
158
|
+
const existingCandidates = resolution.candidates.filter((candidate) => candidate.kind === "existing-context" && candidate.contextId);
|
|
159
|
+
if (existingCandidates.length === 0) {
|
|
160
|
+
return {
|
|
161
|
+
action: "none",
|
|
162
|
+
contextId: null,
|
|
163
|
+
report: resolution.briefing,
|
|
164
|
+
note: "No existing context candidate is available to set as preferred.",
|
|
165
|
+
relatedSessionIds: [],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const preferredChoice = await promptChoice({
|
|
169
|
+
reader: options.promptReader,
|
|
170
|
+
title: "Choose the preferred context for this workspace",
|
|
171
|
+
choices: existingCandidates.map((candidate, index) => ({
|
|
172
|
+
key: `preferred:${index}`,
|
|
173
|
+
label: `${candidate.label} | ${candidate.reasons.join(", ") || "no reasons"}`,
|
|
174
|
+
})),
|
|
175
|
+
});
|
|
176
|
+
const preferredIndex = Number.parseInt(preferredChoice.split(":")[1] ?? "-1", 10);
|
|
177
|
+
const selected = existingCandidates[preferredIndex];
|
|
178
|
+
if (!selected?.contextId) {
|
|
179
|
+
throw new Error("Invalid preferred context choice");
|
|
180
|
+
}
|
|
181
|
+
const result = setActiveContext(db, selected.contextId, options.cwd);
|
|
182
|
+
return {
|
|
183
|
+
action: "preferred",
|
|
184
|
+
contextId: result.contextId,
|
|
185
|
+
report: result.contextId ? getContextReport(db, result.contextId) : null,
|
|
186
|
+
note: "Updated the preferred context for this workspace.",
|
|
187
|
+
relatedSessionIds: [],
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (choice === "create:new") {
|
|
191
|
+
if (!options.sessionId) {
|
|
192
|
+
throw new Error("A sessionId is required to create a new context here");
|
|
193
|
+
}
|
|
194
|
+
const label = shouldAskFollowUpQuestion()
|
|
195
|
+
? ((await promptOptionalText(options.promptReader, "New context label (leave blank to use the session title)")) ??
|
|
196
|
+
options.defaultNewContextLabel ??
|
|
197
|
+
undefined)
|
|
198
|
+
: (options.defaultNewContextLabel ?? undefined);
|
|
199
|
+
const result = confirmContextLink(db, {
|
|
200
|
+
sessionIds: [options.sessionId],
|
|
201
|
+
label,
|
|
202
|
+
linkSource: options.linkSource ?? "confirmed",
|
|
203
|
+
});
|
|
204
|
+
return {
|
|
205
|
+
action: "confirmed",
|
|
206
|
+
contextId: result.contextId,
|
|
207
|
+
report: result.contextId ? getContextReport(db, result.contextId) : null,
|
|
208
|
+
note: "Created a new canonical context for this session.",
|
|
209
|
+
relatedSessionIds: [],
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
const candidateIndex = Number.parseInt(choice.split(":")[1] ?? "-1", 10);
|
|
213
|
+
const candidate = resolution.candidates[candidateIndex];
|
|
214
|
+
if (!candidate) {
|
|
215
|
+
throw new Error("Invalid context candidate choice");
|
|
216
|
+
}
|
|
217
|
+
if (candidate.kind === "existing-context" && candidate.contextId) {
|
|
218
|
+
if (options.sessionId) {
|
|
219
|
+
const result = confirmContextLink(db, {
|
|
220
|
+
sessionIds: [options.sessionId],
|
|
221
|
+
contextId: candidate.contextId,
|
|
222
|
+
linkSource: options.linkSource ?? "confirmed",
|
|
223
|
+
});
|
|
224
|
+
return {
|
|
225
|
+
action: "confirmed",
|
|
226
|
+
contextId: result.contextId,
|
|
227
|
+
report: result.contextId
|
|
228
|
+
? getContextReport(db, result.contextId)
|
|
229
|
+
: null,
|
|
230
|
+
note: "Confirmed the suggested existing context.",
|
|
231
|
+
relatedSessionIds: [],
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
const result = setActiveContext(db, candidate.contextId, options.cwd);
|
|
235
|
+
return {
|
|
236
|
+
action: "preferred",
|
|
237
|
+
contextId: result.contextId,
|
|
238
|
+
report: result.contextId ? getContextReport(db, result.contextId) : null,
|
|
239
|
+
note: "Set the selected context as preferred for this workspace.",
|
|
240
|
+
relatedSessionIds: [],
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
if (candidate.kind === "new-context") {
|
|
244
|
+
if (!options.sessionId) {
|
|
245
|
+
return {
|
|
246
|
+
action: "none",
|
|
247
|
+
contextId: null,
|
|
248
|
+
report: null,
|
|
249
|
+
note: "A session must exist before Footprint can confirm a new context from related sessions.",
|
|
250
|
+
relatedSessionIds: [],
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const label = shouldAskFollowUpQuestion()
|
|
254
|
+
? ((await promptOptionalText(options.promptReader, "New context label (leave blank to use the suggested session title)")) ??
|
|
255
|
+
options.defaultNewContextLabel ??
|
|
256
|
+
undefined)
|
|
257
|
+
: (options.defaultNewContextLabel ?? undefined);
|
|
258
|
+
const sessionIds = candidate.sessionIds.includes(options.sessionId)
|
|
259
|
+
? candidate.sessionIds
|
|
260
|
+
: [options.sessionId, ...candidate.sessionIds];
|
|
261
|
+
const result = confirmContextLink(db, {
|
|
262
|
+
sessionIds,
|
|
263
|
+
label,
|
|
264
|
+
linkSource: options.linkSource ?? "confirmed",
|
|
265
|
+
});
|
|
266
|
+
return {
|
|
267
|
+
action: "confirmed",
|
|
268
|
+
contextId: result.contextId,
|
|
269
|
+
report: result.contextId ? getContextReport(db, result.contextId) : null,
|
|
270
|
+
note: "Created a new canonical context from the suggested related sessions.",
|
|
271
|
+
relatedSessionIds: sessionIds,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
action: "none",
|
|
276
|
+
contextId: null,
|
|
277
|
+
report: resolution.briefing,
|
|
278
|
+
note: "No context change was applied.",
|
|
279
|
+
relatedSessionIds: [],
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
async function prepareContextFlow(db, options) {
|
|
283
|
+
const writeLine = options.printTo === "stderr"
|
|
284
|
+
? (line) => process.stderr.write(`${line}\n`)
|
|
285
|
+
: (line) => console.log(line);
|
|
286
|
+
let titleHint = options.title?.trim() || undefined;
|
|
287
|
+
const allowInteractive = canPromptInteractively(options.interactive);
|
|
288
|
+
const promptReader = allowInteractive
|
|
289
|
+
? createInterface({
|
|
290
|
+
input: process.stdin,
|
|
291
|
+
output: process.stderr,
|
|
292
|
+
terminal: Boolean(process.stdin.isTTY && process.stderr.isTTY),
|
|
293
|
+
})
|
|
294
|
+
: null;
|
|
295
|
+
try {
|
|
296
|
+
if (options.sessionId && titleHint) {
|
|
297
|
+
db.updateSessionTitle(options.sessionId, truncateSummary(titleHint));
|
|
298
|
+
}
|
|
299
|
+
let resolution = resolveContext(db, {
|
|
300
|
+
sessionId: options.sessionId,
|
|
301
|
+
cwd: options.cwd,
|
|
302
|
+
title: titleHint,
|
|
303
|
+
host: options.host,
|
|
304
|
+
});
|
|
305
|
+
if (options.sessionId &&
|
|
306
|
+
allowInteractive &&
|
|
307
|
+
resolution.mode === "none" &&
|
|
308
|
+
!titleHint &&
|
|
309
|
+
promptReader) {
|
|
310
|
+
const hintedTitle = await promptOptionalText(promptReader, "Short task summary for context matching (leave blank to skip)");
|
|
311
|
+
if (hintedTitle) {
|
|
312
|
+
titleHint = hintedTitle;
|
|
313
|
+
db.updateSessionTitle(options.sessionId, truncateSummary(hintedTitle));
|
|
314
|
+
resolution = resolveContext(db, {
|
|
315
|
+
sessionId: options.sessionId,
|
|
316
|
+
cwd: options.cwd,
|
|
317
|
+
title: hintedTitle,
|
|
318
|
+
host: options.host,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (options.verbose !== false) {
|
|
323
|
+
writeLine(`Context mode: ${resolution.mode}`);
|
|
324
|
+
writeLine(`Confirmation required: ${resolution.confirmationRequired ? "yes" : "no"}`);
|
|
325
|
+
writeLine(`Recommended action: ${resolution.recommendedAction}`);
|
|
326
|
+
if (resolution.currentContext) {
|
|
327
|
+
writeLine(`Current context: ${resolution.currentContext.id} | ${resolution.currentContext.label}`);
|
|
328
|
+
}
|
|
329
|
+
if (resolution.candidates.length > 0) {
|
|
330
|
+
printSection("Context Candidates", resolution.candidates.map((candidate) => `${candidate.label} | ${candidate.kind} | ${candidate.confidence} (${candidate.confidenceScore}) | ${candidate.reasons.join(", ") || "no reasons"}`), writeLine);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const applied = await applyInteractiveContextSelection(db, resolution, {
|
|
334
|
+
sessionId: options.sessionId,
|
|
335
|
+
cwd: options.cwd,
|
|
336
|
+
allowInteractive,
|
|
337
|
+
defaultNewContextLabel: titleHint ?? null,
|
|
338
|
+
linkSource: options.linkSource,
|
|
339
|
+
promptReader,
|
|
340
|
+
});
|
|
341
|
+
if (options.verbose !== false) {
|
|
342
|
+
writeLine(applied.note);
|
|
343
|
+
}
|
|
344
|
+
if (applied.report && options.verbose !== false) {
|
|
345
|
+
writeLine("");
|
|
346
|
+
writeLine("Context Briefing:");
|
|
347
|
+
printContextBriefing(applied.report, writeLine);
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
resolution,
|
|
351
|
+
action: applied.action,
|
|
352
|
+
contextId: applied.contextId,
|
|
353
|
+
report: applied.report,
|
|
354
|
+
note: applied.note,
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
finally {
|
|
358
|
+
promptReader?.close();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function buildPendingContextChoices(resolution) {
|
|
362
|
+
const choices = resolution.candidates.map((candidate, index) => ({
|
|
363
|
+
key: `candidate:${index}`,
|
|
364
|
+
label: `${candidate.label} [${candidate.kind}] | ${candidate.confidence} | ${candidate.reasons.join(", ") || "no reasons"}`,
|
|
365
|
+
kind: candidate.kind,
|
|
366
|
+
contextId: candidate.contextId,
|
|
367
|
+
report: candidate.kind === "existing-context" &&
|
|
368
|
+
candidate.contextId &&
|
|
369
|
+
resolution.briefing?.context.id === candidate.contextId
|
|
370
|
+
? resolution.briefing
|
|
371
|
+
: null,
|
|
372
|
+
sessionIds: candidate.sessionIds,
|
|
373
|
+
}));
|
|
374
|
+
if (resolution.mode === "preferred" &&
|
|
375
|
+
resolution.currentContext &&
|
|
376
|
+
!choices.some((choice) => choice.contextId === resolution.currentContext?.id)) {
|
|
377
|
+
choices.unshift({
|
|
378
|
+
key: "preferred:current",
|
|
379
|
+
label: `${resolution.currentContext.label} [existing-context] | high | preferred workspace context`,
|
|
380
|
+
kind: "existing-context",
|
|
381
|
+
contextId: resolution.currentContext.id,
|
|
382
|
+
report: resolution.briefing,
|
|
383
|
+
sessionIds: [],
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
return choices;
|
|
387
|
+
}
|
|
388
|
+
export async function preparePendingRunContextFlow(db, options) {
|
|
389
|
+
const writeLine = options.printTo === "stderr"
|
|
390
|
+
? (line) => process.stderr.write(`${line}\n`)
|
|
391
|
+
: (line) => console.log(line);
|
|
392
|
+
let titleHint = options.title?.trim() || undefined;
|
|
393
|
+
const allowInteractive = canPromptInteractively(options.interactive);
|
|
394
|
+
const promptReader = allowInteractive
|
|
395
|
+
? createInterface({
|
|
396
|
+
input: process.stdin,
|
|
397
|
+
output: process.stderr,
|
|
398
|
+
terminal: Boolean(process.stdin.isTTY && process.stderr.isTTY),
|
|
399
|
+
})
|
|
400
|
+
: null;
|
|
401
|
+
try {
|
|
402
|
+
let resolution = resolveContext(db, {
|
|
403
|
+
cwd: options.cwd,
|
|
404
|
+
title: titleHint,
|
|
405
|
+
host: options.host,
|
|
406
|
+
});
|
|
407
|
+
if (allowInteractive &&
|
|
408
|
+
resolution.mode === "none" &&
|
|
409
|
+
!titleHint &&
|
|
410
|
+
promptReader) {
|
|
411
|
+
const hintedTitle = await promptOptionalText(promptReader, "Short task summary for context matching (leave blank to skip)");
|
|
412
|
+
if (hintedTitle) {
|
|
413
|
+
titleHint = hintedTitle;
|
|
414
|
+
resolution = resolveContext(db, {
|
|
415
|
+
cwd: options.cwd,
|
|
416
|
+
title: hintedTitle,
|
|
417
|
+
host: options.host,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (options.verbose !== false) {
|
|
422
|
+
writeLine(`Context mode: ${resolution.mode}`);
|
|
423
|
+
writeLine(`Confirmation required: ${resolution.confirmationRequired ? "yes" : "no"}`);
|
|
424
|
+
writeLine(`Recommended action: ${resolution.recommendedAction}`);
|
|
425
|
+
if (resolution.currentContext) {
|
|
426
|
+
writeLine(`Current context: ${resolution.currentContext.id} | ${resolution.currentContext.label}`);
|
|
427
|
+
}
|
|
428
|
+
if (resolution.candidates.length > 0) {
|
|
429
|
+
printSection("Context Candidates", resolution.candidates.map((candidate) => `${candidate.label} | ${candidate.kind} | ${candidate.confidence} (${candidate.confidenceScore}) | ${candidate.reasons.join(", ") || "no reasons"}`), writeLine);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
const pendingChoices = buildPendingContextChoices(resolution);
|
|
433
|
+
if (!allowInteractive) {
|
|
434
|
+
return {
|
|
435
|
+
resolution,
|
|
436
|
+
action: resolution.mode === "preferred" && resolution.currentContext
|
|
437
|
+
? "used-preferred"
|
|
438
|
+
: pendingChoices.length === 0
|
|
439
|
+
? "none"
|
|
440
|
+
: "skipped",
|
|
441
|
+
contextId: resolution.mode === "preferred" && resolution.currentContext
|
|
442
|
+
? resolution.currentContext.id
|
|
443
|
+
: null,
|
|
444
|
+
report: resolution.mode === "preferred" && resolution.currentContext
|
|
445
|
+
? resolution.briefing
|
|
446
|
+
: null,
|
|
447
|
+
note: resolution.mode === "preferred" && resolution.currentContext
|
|
448
|
+
? "Using the preferred context for this workspace."
|
|
449
|
+
: pendingChoices.length === 0
|
|
450
|
+
? "No strong context candidate was found."
|
|
451
|
+
: "Context confirmation is required; rerun with interactive mode to confirm or correct the link.",
|
|
452
|
+
title: titleHint ?? null,
|
|
453
|
+
contextLabel: null,
|
|
454
|
+
relatedSessionIds: [],
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
if (!promptReader) {
|
|
458
|
+
throw new Error("Interactive context preparation requires a prompt reader");
|
|
459
|
+
}
|
|
460
|
+
const choice = await promptChoice({
|
|
461
|
+
reader: promptReader,
|
|
462
|
+
title: "Footprint found possible contexts. Confirm how this work should be grouped.",
|
|
463
|
+
choices: [
|
|
464
|
+
...pendingChoices.map((candidate) => ({
|
|
465
|
+
key: candidate.key,
|
|
466
|
+
label: candidate.label,
|
|
467
|
+
})),
|
|
468
|
+
{
|
|
469
|
+
key: "create:new",
|
|
470
|
+
label: "Create a new canonical context for this session",
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
key: "skip",
|
|
474
|
+
label: "Skip for now and leave the session unlinked",
|
|
475
|
+
},
|
|
476
|
+
],
|
|
477
|
+
});
|
|
478
|
+
let prepared;
|
|
479
|
+
if (choice === "skip") {
|
|
480
|
+
prepared = {
|
|
481
|
+
action: "skipped",
|
|
482
|
+
contextId: null,
|
|
483
|
+
report: resolution.briefing,
|
|
484
|
+
note: "Left the session unlinked for now.",
|
|
485
|
+
contextLabel: null,
|
|
486
|
+
relatedSessionIds: [],
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
else if (choice === "create:new") {
|
|
490
|
+
const label = shouldAskFollowUpQuestion()
|
|
491
|
+
? ((await promptOptionalText(promptReader, "New context label (leave blank to use the session title)")) ??
|
|
492
|
+
titleHint ??
|
|
493
|
+
null)
|
|
494
|
+
: (titleHint ?? null);
|
|
495
|
+
prepared = {
|
|
496
|
+
action: "create-new",
|
|
497
|
+
contextId: null,
|
|
498
|
+
report: null,
|
|
499
|
+
note: "Will create a new canonical context for this session.",
|
|
500
|
+
contextLabel: label,
|
|
501
|
+
relatedSessionIds: [],
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
const selected = pendingChoices.find((candidate) => candidate.key === choice);
|
|
506
|
+
if (!selected) {
|
|
507
|
+
throw new Error("Invalid pending context choice");
|
|
508
|
+
}
|
|
509
|
+
prepared =
|
|
510
|
+
selected.kind === "existing-context" && selected.contextId
|
|
511
|
+
? {
|
|
512
|
+
action: resolution.mode === "preferred"
|
|
513
|
+
? "used-preferred"
|
|
514
|
+
: "confirmed",
|
|
515
|
+
contextId: selected.contextId,
|
|
516
|
+
report: selected.report ?? getContextReport(db, selected.contextId),
|
|
517
|
+
note: resolution.mode === "preferred"
|
|
518
|
+
? "Using the preferred context for this workspace."
|
|
519
|
+
: "Confirmed the suggested existing context.",
|
|
520
|
+
contextLabel: null,
|
|
521
|
+
relatedSessionIds: [],
|
|
522
|
+
}
|
|
523
|
+
: {
|
|
524
|
+
action: "create-new",
|
|
525
|
+
contextId: null,
|
|
526
|
+
report: null,
|
|
527
|
+
note: "Will create a new canonical context from the suggested related sessions.",
|
|
528
|
+
contextLabel: shouldAskFollowUpQuestion()
|
|
529
|
+
? ((await promptOptionalText(promptReader, "New context label (leave blank to use the suggested session title)")) ??
|
|
530
|
+
titleHint ??
|
|
531
|
+
selected.label)
|
|
532
|
+
: (titleHint ?? selected.label),
|
|
533
|
+
relatedSessionIds: selected.sessionIds,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
if (options.verbose !== false) {
|
|
537
|
+
writeLine(prepared.note);
|
|
538
|
+
}
|
|
539
|
+
if (prepared.report && options.verbose !== false) {
|
|
540
|
+
writeLine("");
|
|
541
|
+
writeLine("Context Briefing:");
|
|
542
|
+
printContextBriefing(prepared.report, writeLine);
|
|
543
|
+
}
|
|
544
|
+
return {
|
|
545
|
+
resolution,
|
|
546
|
+
action: prepared.action,
|
|
547
|
+
contextId: prepared.contextId,
|
|
548
|
+
report: prepared.report,
|
|
549
|
+
note: prepared.note,
|
|
550
|
+
title: titleHint ?? null,
|
|
551
|
+
contextLabel: prepared.contextLabel,
|
|
552
|
+
relatedSessionIds: prepared.relatedSessionIds,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
finally {
|
|
556
|
+
promptReader?.close();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
export function resolveContextCli(options) {
|
|
560
|
+
const dbPath = resolveDbPath();
|
|
561
|
+
ensureParentDir(dbPath);
|
|
562
|
+
const db = new EvidenceDatabase(dbPath);
|
|
563
|
+
try {
|
|
564
|
+
const result = resolveContext(db, options);
|
|
565
|
+
if (options.json) {
|
|
566
|
+
printJson(result);
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
console.log(`Mode: ${result.mode}`);
|
|
570
|
+
console.log(`Confirmation required: ${result.confirmationRequired ? "yes" : "no"}`);
|
|
571
|
+
console.log(`Recommended action: ${result.recommendedAction}`);
|
|
572
|
+
if (result.currentContext) {
|
|
573
|
+
console.log(`Current context: ${result.currentContext.id} | ${result.currentContext.label}`);
|
|
574
|
+
}
|
|
575
|
+
if (result.briefing) {
|
|
576
|
+
printSection("Current Truth", [result.briefing.currentTruth.summary]);
|
|
577
|
+
}
|
|
578
|
+
if (result.candidates.length > 0) {
|
|
579
|
+
printSection("Candidates", result.candidates.map((candidate) => `${candidate.kind} | ${candidate.label} | ${candidate.confidence} (${candidate.confidenceScore}) | ${candidate.reasons.join(", ") || "no reasons"}`));
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
finally {
|
|
583
|
+
db.close();
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
export async function prepareContextCli(options) {
|
|
587
|
+
const dbPath = resolveDbPath();
|
|
588
|
+
ensureParentDir(dbPath);
|
|
589
|
+
const db = new EvidenceDatabase(dbPath);
|
|
590
|
+
try {
|
|
591
|
+
const prepared = await prepareContextFlow(db, {
|
|
592
|
+
sessionId: options.sessionId?.trim() || undefined,
|
|
593
|
+
cwd: options.cwd?.trim() || undefined,
|
|
594
|
+
title: options.title?.trim() || undefined,
|
|
595
|
+
host: options.host,
|
|
596
|
+
interactive: options.interactive,
|
|
597
|
+
printTo: "stdout",
|
|
598
|
+
linkSource: "confirmed",
|
|
599
|
+
verbose: !options.json,
|
|
600
|
+
});
|
|
601
|
+
if (options.json) {
|
|
602
|
+
printJson({
|
|
603
|
+
resolution: prepared.resolution,
|
|
604
|
+
action: prepared.action,
|
|
605
|
+
contextId: prepared.contextId,
|
|
606
|
+
note: prepared.note,
|
|
607
|
+
report: prepared.report,
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
finally {
|
|
612
|
+
db.close();
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
export function confirmContextLinkCli(options) {
|
|
616
|
+
const dbPath = resolveDbPath();
|
|
617
|
+
ensureParentDir(dbPath);
|
|
618
|
+
const db = new EvidenceDatabase(dbPath);
|
|
619
|
+
try {
|
|
620
|
+
const result = confirmContextLink(db, options);
|
|
621
|
+
if (options.json) {
|
|
622
|
+
printJson(result);
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
console.log(`Action: ${result.action}`);
|
|
626
|
+
console.log(`Context: ${result.context?.label ?? result.contextId ?? "unknown"}`);
|
|
627
|
+
console.log(`Sessions: ${result.affectedSessionIds.join(", ")}`);
|
|
628
|
+
}
|
|
629
|
+
finally {
|
|
630
|
+
db.close();
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
export function rejectContextLinkCli(options) {
|
|
634
|
+
const dbPath = resolveDbPath();
|
|
635
|
+
ensureParentDir(dbPath);
|
|
636
|
+
const db = new EvidenceDatabase(dbPath);
|
|
637
|
+
try {
|
|
638
|
+
const result = rejectContextLink(db, options.sessionId, options.contextId);
|
|
639
|
+
if (options.json) {
|
|
640
|
+
printJson(result);
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
console.log(`Action: ${result.action}`);
|
|
644
|
+
console.log(`Session: ${options.sessionId}`);
|
|
645
|
+
console.log(`Rejected context: ${options.contextId}`);
|
|
646
|
+
}
|
|
647
|
+
finally {
|
|
648
|
+
db.close();
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
export function moveSessionContextCli(options) {
|
|
652
|
+
const dbPath = resolveDbPath();
|
|
653
|
+
ensureParentDir(dbPath);
|
|
654
|
+
const db = new EvidenceDatabase(dbPath);
|
|
655
|
+
try {
|
|
656
|
+
const result = moveSessionContext(db, options);
|
|
657
|
+
if (options.json) {
|
|
658
|
+
printJson(result);
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
console.log(`Action: ${result.action}`);
|
|
662
|
+
console.log(`Session: ${options.sessionId}`);
|
|
663
|
+
console.log(`Context: ${result.context?.label ?? result.contextId ?? "unknown"}`);
|
|
664
|
+
}
|
|
665
|
+
finally {
|
|
666
|
+
db.close();
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
export function mergeContextsCli(options) {
|
|
670
|
+
const dbPath = resolveDbPath();
|
|
671
|
+
ensureParentDir(dbPath);
|
|
672
|
+
const db = new EvidenceDatabase(dbPath);
|
|
673
|
+
try {
|
|
674
|
+
const result = mergeContexts(db, options.sourceContextId, options.targetContextId);
|
|
675
|
+
if (options.json) {
|
|
676
|
+
printJson(result);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
console.log(`Action: ${result.action}`);
|
|
680
|
+
console.log(`Merged from: ${options.sourceContextId}`);
|
|
681
|
+
console.log(`Merged into: ${result.context?.label ?? result.contextId ?? "unknown"}`);
|
|
682
|
+
}
|
|
683
|
+
finally {
|
|
684
|
+
db.close();
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
export function splitContextCli(options) {
|
|
688
|
+
const dbPath = resolveDbPath();
|
|
689
|
+
ensureParentDir(dbPath);
|
|
690
|
+
const db = new EvidenceDatabase(dbPath);
|
|
691
|
+
try {
|
|
692
|
+
const result = splitContext(db, options);
|
|
693
|
+
if (options.json) {
|
|
694
|
+
printJson(result);
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
console.log(`Action: ${result.action}`);
|
|
698
|
+
console.log(`Source context: ${options.contextId}`);
|
|
699
|
+
console.log(`New context: ${result.context?.label ?? result.contextId ?? "unknown"}`);
|
|
700
|
+
console.log(`Sessions: ${result.affectedSessionIds.join(", ")}`);
|
|
701
|
+
}
|
|
702
|
+
finally {
|
|
703
|
+
db.close();
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
export function setActiveContextCli(options) {
|
|
707
|
+
const dbPath = resolveDbPath();
|
|
708
|
+
ensureParentDir(dbPath);
|
|
709
|
+
const db = new EvidenceDatabase(dbPath);
|
|
710
|
+
try {
|
|
711
|
+
const result = setActiveContext(db, options.contextId, options.cwd);
|
|
712
|
+
if (options.json) {
|
|
713
|
+
printJson(result);
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
console.log(`Action: ${result.action}`);
|
|
717
|
+
console.log(`Context: ${result.context?.label ?? result.contextId ?? "unknown"}`);
|
|
718
|
+
console.log(`Workspace: ${options.cwd ?? result.context?.workspaceKey ?? "default"}`);
|
|
719
|
+
}
|
|
720
|
+
finally {
|
|
721
|
+
db.close();
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
//# sourceMappingURL=context-flow.js.map
|