snipara-companion 3.2.13 → 3.2.14

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/README.md CHANGED
@@ -22,6 +22,27 @@ npx -y snipara-companion impact src/auth/session.ts --source local
22
22
  npx -y snipara-companion source init .
23
23
  ```
24
24
 
25
+ ## Companion Continuity Contract V1
26
+
27
+ Editor integrations and post-activation workflows can ask Companion for one
28
+ machine-readable "continue this workspace" payload:
29
+
30
+ ```bash
31
+ npx -y snipara-companion@latest continue-workspace --include-session-context --json
32
+ ```
33
+
34
+ The payload version is `snipara.companion.continuity.v1`. It is designed for
35
+ native editor commands, status bars, panels, and agent handoffs that need to
36
+ resume real work without rescanning or reimplementing Snipara semantics. It
37
+ includes project binding, session bootstrap entries and quality warnings,
38
+ workflow phase state, Team Sync handoff summary, passive source snapshot status,
39
+ session snapshot summary, stable local artifact paths, and recommended next
40
+ actions.
41
+
42
+ Use this after `create-snipara` activation. `create-snipara` remains the
43
+ canonical engine for first workspace setup; Companion owns the repeatable local
44
+ continuity loop after that.
45
+
25
46
  Example output excerpt:
26
47
 
27
48
  ```text
package/dist/index.js CHANGED
@@ -16928,6 +16928,7 @@ var MIN_WORKFLOW_SURFACE_TOKENS = 200;
16928
16928
  var STALE_BOOTSTRAP_MEMORY_DAYS = 90;
16929
16929
  var TASK_COMMIT_TIMEOUT_MS = 3e4;
16930
16930
  var FINAL_COMMIT_TIMEOUT_MS = 9e4;
16931
+ var COMPANION_CONTINUITY_CONTRACT_VERSION = "snipara.companion.continuity.v1";
16931
16932
  var FINAL_COMMIT_RETRY_TIMEOUT_MS = 45e3;
16932
16933
  var FINAL_COMMIT_SUMMARY_MAX_CHARS = 1200;
16933
16934
  var FINAL_COMMIT_RETRY_SUMMARY_MAX_CHARS = 600;
@@ -24482,6 +24483,191 @@ async function sessionBootstrapCommand(options) {
24482
24483
  printSessionBootstrapQuality(buildPrintedBootstrapQuality(printedBootstrap));
24483
24484
  }
24484
24485
  }
24486
+ function emptySessionMemoriesResult() {
24487
+ return {
24488
+ critical: { memories: [], count: 0, tokens: 0 },
24489
+ daily: { memories: [], count: 0, tokens: 0 },
24490
+ total_tokens: 0
24491
+ };
24492
+ }
24493
+ function readLocalJsonFile(relativePath2) {
24494
+ const absolutePath = path20.join(process.cwd(), relativePath2);
24495
+ if (!fs21.existsSync(absolutePath)) {
24496
+ return null;
24497
+ }
24498
+ try {
24499
+ const parsed = JSON.parse(fs21.readFileSync(absolutePath, "utf-8"));
24500
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
24501
+ } catch {
24502
+ return null;
24503
+ }
24504
+ }
24505
+ function summarizeSourceSnapshot(snapshot) {
24506
+ if (!snapshot) {
24507
+ return {
24508
+ status: "missing",
24509
+ snapshotPath: path20.join(".snipara", "source", "latest.json"),
24510
+ nextAction: "Run snipara-companion source sync --json after meaningful local code or docs movement."
24511
+ };
24512
+ }
24513
+ const summary = snapshot.summary && typeof snapshot.summary === "object" && !Array.isArray(snapshot.summary) ? snapshot.summary : {};
24514
+ return {
24515
+ status: "present",
24516
+ snapshotPath: path20.join(".snipara", "source", "latest.json"),
24517
+ generatedAt: snapshot.generatedAt,
24518
+ revision: snapshot.revision,
24519
+ totalFiles: summary.totalFiles,
24520
+ totalBytes: summary.totalBytes,
24521
+ skipped: summary.skipped
24522
+ };
24523
+ }
24524
+ function summarizeWorkflowStateForContinuity(workflow2) {
24525
+ if (!workflow2) {
24526
+ return {
24527
+ status: "missing",
24528
+ statePath: WORKFLOW_STATE_RELATIVE_PATH,
24529
+ nextAction: "Run snipara-companion workflow start for multi-phase work."
24530
+ };
24531
+ }
24532
+ return {
24533
+ status: workflow2.status,
24534
+ workflowId: workflow2.workflowId,
24535
+ goal: workflow2.goal,
24536
+ currentPhaseId: workflow2.currentPhaseId,
24537
+ statePath: WORKFLOW_STATE_RELATIVE_PATH,
24538
+ phases: workflow2.phases.map((phase) => ({
24539
+ id: phase.id,
24540
+ title: phase.title,
24541
+ status: phase.status
24542
+ }))
24543
+ };
24544
+ }
24545
+ function summarizeTeamSyncForContinuity() {
24546
+ const state = loadTeamSyncState();
24547
+ const summary = buildTeamSyncSummary(state);
24548
+ const latestHandoff = state.handoffs.at(-1);
24549
+ return {
24550
+ statePath: path20.relative(process.cwd(), getTeamSyncStatePath()),
24551
+ activeWorkCount: summary.activeWorkCount,
24552
+ staleWorkCount: summary.staleWorkCount,
24553
+ completedWorkCount: summary.completedWorkCount,
24554
+ archivedWorkCount: summary.archivedWorkCount,
24555
+ handoffCount: summary.handoffCount,
24556
+ latestHandoff: latestHandoff ? {
24557
+ summary: latestHandoff.summary,
24558
+ next: latestHandoff.next,
24559
+ attention: latestHandoff.attention,
24560
+ createdAt: latestHandoff.createdAt
24561
+ } : null
24562
+ };
24563
+ }
24564
+ function buildCompanionContinuityPayload(args) {
24565
+ const config = args.configured ? loadConfig() : null;
24566
+ const workflow2 = readWorkflowState2();
24567
+ const sessionSnapshot = readSessionSnapshot();
24568
+ const sourceSnapshot = readLocalJsonFile(path20.join(".snipara", "source", "latest.json"));
24569
+ return {
24570
+ version: COMPANION_CONTINUITY_CONTRACT_VERSION,
24571
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
24572
+ configured: args.configured,
24573
+ project: config ? {
24574
+ projectId: config.projectId,
24575
+ apiUrl: config.apiUrl,
24576
+ sessionId: config.sessionId
24577
+ } : null,
24578
+ artifacts: {
24579
+ workflowStatePath: WORKFLOW_STATE_RELATIVE_PATH,
24580
+ teamSyncStatePath: path20.relative(process.cwd(), getTeamSyncStatePath()),
24581
+ sessionSnapshotPath: path20.join(".snipara", "activity", "session.json"),
24582
+ sourceSnapshotPath: path20.join(".snipara", "source", "latest.json")
24583
+ },
24584
+ bootstrap: args.bootstrap,
24585
+ bootstrapQuality: args.bootstrapQuality,
24586
+ localWarmSnapshot: {
24587
+ storedEntries: args.warmSnapshotStoredEntries
24588
+ },
24589
+ sessionContext: {
24590
+ included: args.includeSessionContext,
24591
+ maxTokens: args.maxContextTokens
24592
+ },
24593
+ workflow: summarizeWorkflowStateForContinuity(workflow2),
24594
+ teamSync: summarizeTeamSyncForContinuity(),
24595
+ source: summarizeSourceSnapshot(sourceSnapshot),
24596
+ sessionSnapshot: sessionSnapshot ? {
24597
+ latestActivityAt: sessionSnapshot.summary.latestActivityAt,
24598
+ latestTitle: sessionSnapshot.summary.latestActivityTitle,
24599
+ latestKind: sessionSnapshot.summary.latestActivityKind,
24600
+ risk: sessionSnapshot.summary.risk,
24601
+ riskReasons: sessionSnapshot.summary.riskReasons,
24602
+ touchedFiles: sessionSnapshot.summary.touchedFiles,
24603
+ nextAction: sessionSnapshot.summary.recommendedNextAction
24604
+ } : null,
24605
+ nextActions: [
24606
+ {
24607
+ id: "open_bootstrap_brief",
24608
+ label: "Open bootstrap brief",
24609
+ command: "snipara-companion session-bootstrap --include-session-context --max-context-tokens 1000",
24610
+ when: "bootstrap has entries or session context is included"
24611
+ },
24612
+ {
24613
+ id: "refresh_source_snapshot",
24614
+ label: "Refresh source snapshot",
24615
+ command: "snipara-companion source sync --json",
24616
+ when: "source.status is missing or local code/docs changed"
24617
+ },
24618
+ {
24619
+ id: "run_impact_gate",
24620
+ label: "Run code impact before risky edits",
24621
+ command: 'snipara-companion code impact --changed-files <files> --diff-summary "next edit"',
24622
+ when: "the next edit is multi-file, risky, or user-visible"
24623
+ },
24624
+ {
24625
+ id: "commit_task_context",
24626
+ label: "Commit durable task context",
24627
+ command: 'snipara-companion task-commit --summary "<done>" --files <files>',
24628
+ when: "a durable phase or task is complete"
24629
+ }
24630
+ ]
24631
+ };
24632
+ }
24633
+ async function continueWorkspaceCommand(options) {
24634
+ const configured = isConfigured();
24635
+ const resolvedContextTokens = options.maxContextTokens !== void 0 ? options.maxContextTokens : options.includeSessionContext ? DEFAULT_SESSION_CONTEXT_TOKENS : 0;
24636
+ let bootstrap = emptySessionMemoriesResult();
24637
+ let warmSnapshotStoredEntries = 0;
24638
+ if (configured) {
24639
+ const client = createClient(15e3);
24640
+ bootstrap = await client.getSessionMemories(options.maxCriticalTokens, resolvedContextTokens);
24641
+ const config = loadConfig();
24642
+ const warmSnapshot = createLocalQueryCache({
24643
+ cwd: process.cwd(),
24644
+ projectId: config.projectId,
24645
+ sessionId: config.sessionId
24646
+ }).storeWarmSnapshot(bootstrap);
24647
+ warmSnapshotStoredEntries = warmSnapshot.storedEntries;
24648
+ }
24649
+ const bootstrapQuality = buildSessionBootstrapQuality(bootstrap, {
24650
+ expectedMaxTokens: (options.maxCriticalTokens ?? DEFAULT_FULL_WORKFLOW_CRITICAL_TOKENS) + resolvedContextTokens
24651
+ });
24652
+ const payload = buildCompanionContinuityPayload({
24653
+ configured,
24654
+ bootstrap,
24655
+ bootstrapQuality,
24656
+ includeSessionContext: resolvedContextTokens > 0,
24657
+ maxContextTokens: resolvedContextTokens,
24658
+ warmSnapshotStoredEntries
24659
+ });
24660
+ if (options.json) {
24661
+ printJson2(payload);
24662
+ return;
24663
+ }
24664
+ console.log(import_chalk6.default.bold("Snipara Companion Continuity"));
24665
+ console.log(`Version: ${COMPANION_CONTINUITY_CONTRACT_VERSION}`);
24666
+ console.log(`Configured: ${configured ? "yes" : "no"}`);
24667
+ console.log(`Workflow: ${payload.workflow.status ?? "unknown"}`);
24668
+ console.log(`Source: ${payload.source.status ?? "unknown"}`);
24669
+ console.log("Next: snipara-companion continue-workspace --json for editor integrations");
24670
+ }
24485
24671
  async function recallCommand(options) {
24486
24672
  ensureConfigured();
24487
24673
  const client = createClient(15e3);
@@ -34867,6 +35053,19 @@ program.command("session-bootstrap").description(
34867
35053
  json: options.json
34868
35054
  });
34869
35055
  });
35056
+ program.command("continue-workspace").description("Print the stable Companion Continuity Contract for editor integrations").option("--max-critical-tokens <number>", "Durable memory token budget").option(
35057
+ "--include-session-context",
35058
+ "Include short-lived session carryover in addition to durable memory"
35059
+ ).option("--max-context-tokens <number>", "Short-lived session context token budget").option("--json", "Print raw JSON").action(async (options) => {
35060
+ const maxCriticalTokens = options.maxCriticalTokens !== void 0 ? parseInt(options.maxCriticalTokens, 10) : void 0;
35061
+ const maxContextTokens = options.maxContextTokens !== void 0 ? parseInt(options.maxContextTokens, 10) : void 0;
35062
+ await continueWorkspaceCommand({
35063
+ maxCriticalTokens,
35064
+ maxContextTokens,
35065
+ includeSessionContext: Boolean(options.includeSessionContext || options.maxContextTokens),
35066
+ json: options.json
35067
+ });
35068
+ });
34870
35069
  program.command("task-commit").description("Persist durable outcomes after meaningful task work, not every git commit").requiredOption("-s, --summary <summary>", "Task summary").option("-c, --category <category>", "Category").option("-o, --outcome <outcome>", "Outcome", "completed").option("-f, --files <files...>", "Files touched").option("--json", "Print raw JSON").action(async (options) => {
34871
35070
  await taskCommitCommand({
34872
35071
  summary: options.summary,
@@ -21,6 +21,35 @@ snipara-companion code impact --changed-files src/app.ts --diff-summary "next ed
21
21
  snipara-companion task-commit --summary "completed durable change" --files src/app.ts
22
22
  ```
23
23
 
24
+ ## Companion Continuity Contract V1
25
+
26
+ For editor integrations and "continue this workspace" flows, use the stable JSON
27
+ contract instead of rebuilding Companion internals:
28
+
29
+ ```bash
30
+ snipara-companion continue-workspace --include-session-context --json
31
+ ```
32
+
33
+ The response version is `snipara.companion.continuity.v1`. It includes:
34
+
35
+ - `project`: configured project id, API URL, and session id when available.
36
+ - `bootstrap` and `bootstrapQuality`: pushed session memories plus token,
37
+ freshness, and warning metadata.
38
+ - `workflow`: active workflow id, goal, current phase, phase statuses, and local
39
+ state path.
40
+ - `teamSync`: active/stale/completed counts and latest handoff summary.
41
+ - `source`: passive `.snipara/source/latest.json` status; no source sync is
42
+ performed by this command.
43
+ - `sessionSnapshot`: latest local activity summary, risks, touched files, and
44
+ recommended next action.
45
+ - `artifacts` and `nextActions`: stable local paths and commands an editor can
46
+ surface to the user.
47
+
48
+ This contract starts after `create-snipara` has activated the workspace. Editor
49
+ extensions should orchestrate these commands and render the payload, not fork
50
+ local source scanning, First Work Brief, memory candidate, or Hosted MCP config
51
+ logic.
52
+
24
53
  In this repository, the source currently lives in `packages/cli`, and the installed executable is `snipara-companion`.
25
54
 
26
55
  This package complements `snipara-mcp`. It does not replace it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snipara-companion",
3
- "version": "3.2.13",
3
+ "version": "3.2.14",
4
4
  "description": "Local-first CLI that asks your repo what breaks before an AI coding agent edits it.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {