snipara-companion 3.2.8 → 3.2.9

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.
Files changed (2) hide show
  1. package/dist/index.js +135 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -17121,6 +17121,12 @@ function scoreBootstrapBriefEntry(entry, source2, now) {
17121
17121
  }
17122
17122
  if (type === "decision") {
17123
17123
  score += 25;
17124
+ const authorityStatus = bootstrapAuthorityStatus(entry);
17125
+ if (authorityStatus === "canonical") {
17126
+ score += 55;
17127
+ } else if (authorityStatus === "approved" || authorityStatus === "authoritative") {
17128
+ score += 20;
17129
+ }
17124
17130
  } else if (type === "context") {
17125
17131
  score += 20;
17126
17132
  } else if (type === "learning") {
@@ -17166,6 +17172,89 @@ function scoreBootstrapBriefEntry(entry, source2, now) {
17166
17172
  function estimateBootstrapEntryTokens(entry) {
17167
17173
  return Math.max(8, Math.ceil(compactSessionEntryLine(entry).length / 4));
17168
17174
  }
17175
+ function bootstrapEntryType(entry) {
17176
+ return typeof entry.type === "string" ? entry.type.toLowerCase() : "";
17177
+ }
17178
+ function isBootstrapDecisionEntry(entry) {
17179
+ return bootstrapEntryType(entry) === "decision";
17180
+ }
17181
+ function bootstrapAuthorityStatus(entry) {
17182
+ const authority = isRecord9(entry.authority) ? entry.authority : void 0;
17183
+ return (stringValue7(entry.authority_status) ?? stringValue7(authority?.authorityStatus) ?? stringValue7(authority?.level) ?? "").toLowerCase();
17184
+ }
17185
+ function bootstrapSimilarityTokens(entry) {
17186
+ const stopWords = /* @__PURE__ */ new Set([
17187
+ "checkpoint",
17188
+ "workflow",
17189
+ "final",
17190
+ "commit",
17191
+ "phase",
17192
+ "summary",
17193
+ "context",
17194
+ "team",
17195
+ "sync",
17196
+ "handoff",
17197
+ "released",
17198
+ "release"
17199
+ ]);
17200
+ const text = readSessionEntryPreview(entry).toLowerCase().replace(/snipara-companion@\d+\.\d+\.\d+/g, "snipara-companion").replace(/[^a-z0-9]+/g, " ");
17201
+ return new Set(text.split(/\s+/).filter((token) => token.length >= 4 && !stopWords.has(token)));
17202
+ }
17203
+ function buildBootstrapTopicTokens(ranked) {
17204
+ const tokens = /* @__PURE__ */ new Set();
17205
+ for (const candidate of ranked) {
17206
+ if (tokens.size >= 48) {
17207
+ break;
17208
+ }
17209
+ if (candidate.score <= 0 || !isSessionCarryoverEntry(candidate.entry)) {
17210
+ continue;
17211
+ }
17212
+ for (const token of bootstrapSimilarityTokens(candidate.entry)) {
17213
+ tokens.add(token);
17214
+ }
17215
+ }
17216
+ return tokens;
17217
+ }
17218
+ function isDecisionRelevantToBootstrap(entry, topicTokens, hasCarryoverCandidate) {
17219
+ if (!isBootstrapDecisionEntry(entry)) {
17220
+ return false;
17221
+ }
17222
+ if (!hasCarryoverCandidate) {
17223
+ return true;
17224
+ }
17225
+ const text = readBootstrapEntryText(entry).toLowerCase();
17226
+ const hasExplicitTopic = text.includes("control plane") || text.includes("control-plane") || text.includes("lite") || text.includes("session-bootstrap") || text.includes("bootstrap brief");
17227
+ if (hasExplicitTopic) {
17228
+ return true;
17229
+ }
17230
+ const category = typeof entry.category === "string" ? entry.category.toLowerCase() : "";
17231
+ if (category.includes("workflow-phase") || category.includes("final-commit") || category.includes("journal:") || category.includes("team_sync")) {
17232
+ return false;
17233
+ }
17234
+ let overlap = 0;
17235
+ for (const token of bootstrapSimilarityTokens(entry)) {
17236
+ if (topicTokens.has(token)) {
17237
+ overlap += 1;
17238
+ }
17239
+ }
17240
+ return overlap >= 3;
17241
+ }
17242
+ function areBootstrapEntriesSimilar(a, b) {
17243
+ const aTokens = bootstrapSimilarityTokens(a);
17244
+ const bTokens = bootstrapSimilarityTokens(b);
17245
+ if (aTokens.size === 0 || bTokens.size === 0) {
17246
+ return false;
17247
+ }
17248
+ let overlap = 0;
17249
+ for (const token of aTokens) {
17250
+ if (bTokens.has(token)) {
17251
+ overlap += 1;
17252
+ }
17253
+ }
17254
+ const smaller = Math.min(aTokens.size, bTokens.size);
17255
+ const larger = Math.max(aTokens.size, bTokens.size);
17256
+ return overlap / smaller >= 0.6 || overlap >= 8 && overlap / larger >= 0.5;
17257
+ }
17169
17258
  function buildSessionBootstrapBrief(result, options) {
17170
17259
  const normalized = normalizeSessionMemoriesResult(result);
17171
17260
  const now = options.now ?? /* @__PURE__ */ new Date();
@@ -17184,8 +17273,17 @@ function buildSessionBootstrapBrief(result, options) {
17184
17273
  const hasFreshCandidate = ranked.some(
17185
17274
  (candidate) => candidate.score > 0 && !isLikelyStaleBootstrapEntry(candidate.entry, now) && !isLikelyTestMemory(candidate.entry)
17186
17275
  );
17276
+ const topicTokens = buildBootstrapTopicTokens(ranked);
17277
+ const hasCarryoverCandidate = ranked.some(
17278
+ (candidate) => candidate.score > 0 && isSessionCarryoverEntry(candidate.entry) && !isLikelyStaleBootstrapEntry(candidate.entry, now) && !isLikelyTestMemory(candidate.entry)
17279
+ );
17280
+ const hasFreshDecisionCandidate = maxEntries >= 4 && ranked.some(
17281
+ (candidate) => candidate.score > 0 && isDecisionRelevantToBootstrap(candidate.entry, topicTokens, hasCarryoverCandidate) && !isLikelyStaleBootstrapEntry(candidate.entry, now) && !isLikelyTestMemory(candidate.entry)
17282
+ );
17187
17283
  const entries = [];
17188
17284
  let estimatedTokens = 0;
17285
+ let selectedCarryoverCount = 0;
17286
+ let selectedDecisionCount = 0;
17189
17287
  for (const candidate of ranked) {
17190
17288
  if (hasFreshCandidate && (isLikelyStaleBootstrapEntry(candidate.entry, now) || isLikelyTestMemory(candidate.entry))) {
17191
17289
  continue;
@@ -17196,13 +17294,30 @@ function buildSessionBootstrapBrief(result, options) {
17196
17294
  if (seen.has(key)) {
17197
17295
  continue;
17198
17296
  }
17199
- seen.add(key);
17297
+ if (entries.some((entry) => areBootstrapEntriesSimilar(entry, candidate.entry))) {
17298
+ continue;
17299
+ }
17300
+ const isCarryover = isSessionCarryoverEntry(candidate.entry);
17301
+ const isDecision = isBootstrapDecisionEntry(candidate.entry);
17302
+ if (isDecision && !isDecisionRelevantToBootstrap(candidate.entry, topicTokens, hasCarryoverCandidate)) {
17303
+ continue;
17304
+ }
17305
+ if (hasFreshDecisionCandidate && selectedDecisionCount === 0 && isCarryover && selectedCarryoverCount >= maxEntries - 1) {
17306
+ continue;
17307
+ }
17200
17308
  const entryTokens = estimateBootstrapEntryTokens(candidate.entry);
17201
17309
  if (entries.length > 0 && estimatedTokens + entryTokens > budgetTokens) {
17202
17310
  continue;
17203
17311
  }
17312
+ seen.add(key);
17204
17313
  entries.push(candidate.entry);
17205
17314
  estimatedTokens += entryTokens;
17315
+ if (isCarryover) {
17316
+ selectedCarryoverCount += 1;
17317
+ }
17318
+ if (isDecision) {
17319
+ selectedDecisionCount += 1;
17320
+ }
17206
17321
  if (entries.length >= maxEntries) {
17207
17322
  break;
17208
17323
  }
@@ -23891,7 +24006,25 @@ async function loadDocumentCommand(options) {
23891
24006
  printLoadDocumentResult(options.path, result);
23892
24007
  }
23893
24008
  async function sessionBootstrapCommand(options) {
23894
- ensureConfigured();
24009
+ if (!isConfigured()) {
24010
+ if (options.json) {
24011
+ printJson2({
24012
+ critical: { memories: [], count: 0, tokens: 0 },
24013
+ daily: { memories: [], count: 0, tokens: 0 },
24014
+ total_tokens: 0,
24015
+ session_context: {
24016
+ included: Boolean(options.includeSessionContext),
24017
+ max_tokens: options.includeSessionContext ? DEFAULT_SESSION_CONTEXT_TOKENS : 0
24018
+ },
24019
+ session_bootstrap_quality: buildSessionBootstrapQuality({
24020
+ critical: { memories: [], count: 0, tokens: 0 },
24021
+ daily: { memories: [], count: 0, tokens: 0 },
24022
+ total_tokens: 0
24023
+ })
24024
+ });
24025
+ }
24026
+ return;
24027
+ }
23895
24028
  const resolvedContextTokens = options.maxContextTokens !== void 0 ? options.maxContextTokens : options.includeSessionContext ? DEFAULT_SESSION_CONTEXT_TOKENS : 0;
23896
24029
  const client = createClient(15e3);
23897
24030
  const result = await client.getSessionMemories(options.maxCriticalTokens, resolvedContextTokens);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snipara-companion",
3
- "version": "3.2.8",
3
+ "version": "3.2.9",
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": {