@karmaniverous/stan-cli 0.11.2 → 0.11.3

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/cli/stan.js CHANGED
@@ -17153,6 +17153,25 @@ const readDocsMeta = async (cwd, stanPath) => {
17153
17153
  }
17154
17154
  return null;
17155
17155
  };
17156
+ const updateDocsMetaPrompt = async (cwd, stanPath, prompt) => {
17157
+ const p = metaPath(cwd, stanPath);
17158
+ let base = {};
17159
+ try {
17160
+ const raw = await readFile(p, 'utf8');
17161
+ const v = JSON.parse(raw);
17162
+ if (v && typeof v === 'object')
17163
+ base = v;
17164
+ }
17165
+ catch {
17166
+ base = {};
17167
+ }
17168
+ const next = {
17169
+ ...base,
17170
+ prompt: { ...(base.prompt ?? {}), ...prompt },
17171
+ };
17172
+ await ensureDir(path.dirname(p));
17173
+ await writeFile(p, JSON.stringify(next, null, 2), 'utf8');
17174
+ };
17156
17175
  const updateDocsMetaOverlay = async (cwd, stanPath, overlay) => {
17157
17176
  const p = metaPath(cwd, stanPath);
17158
17177
  let base = {};
@@ -28897,6 +28916,18 @@ const flushUiOnce = async (ui) => {
28897
28916
  }
28898
28917
  };
28899
28918
 
28919
+ /* src/stan/util/hash.ts
28920
+ * SHA-256 hashing helper for file paths.
28921
+ */
28922
+ /**
28923
+ * Compute the SHA-256 hash (hex) of a file's bytes.
28924
+ */
28925
+ const sha256File = async (abs) => {
28926
+ return createHash('sha256')
28927
+ .update(await readFile(abs))
28928
+ .digest('hex');
28929
+ };
28930
+
28900
28931
  /** Build FULL and DIFF base configs; DIFF intentionally excludes anchors. */
28901
28932
  const makeBaseConfigs = (config) => {
28902
28933
  const full = {
@@ -29183,18 +29214,6 @@ const buildArchiveProgress = (ui, cwd) => {
29183
29214
  };
29184
29215
  };
29185
29216
 
29186
- /* src/stan/util/hash.ts
29187
- * SHA-256 hashing helper for file paths.
29188
- */
29189
- /**
29190
- * Compute the SHA-256 hash (hex) of a file's bytes.
29191
- */
29192
- const sha256File = async (abs) => {
29193
- return createHash('sha256')
29194
- .update(await readFile(abs))
29195
- .digest('hex');
29196
- };
29197
-
29198
29217
  // src/runner/run/session/archive-stage/prompt-ephemeral.ts
29199
29218
  /** True when the resolved prompt source is not the local system file. */
29200
29219
  const isEphemeralPrompt = (systemAbs, promptAbs) => {
@@ -29214,8 +29233,8 @@ const isEphemeralPrompt = (systemAbs, promptAbs) => {
29214
29233
  */
29215
29234
  const decideIncludeOnChange = async (args) => {
29216
29235
  const { cwd, stanPath, promptAbs } = args;
29217
- // Default to include once when we cannot decide safely
29218
- let includeOnChange = true;
29236
+ // Default to suppress when we cannot decide safely (no baseline yet).
29237
+ let includeOnChange = false;
29219
29238
  let currentHash;
29220
29239
  try {
29221
29240
  if (promptAbs)
@@ -29233,14 +29252,14 @@ const decideIncludeOnChange = async (args) => {
29233
29252
  includeOnChange = baseline !== currentHash;
29234
29253
  }
29235
29254
  else if (!baseline) {
29236
- includeOnChange = true;
29255
+ includeOnChange = false;
29237
29256
  }
29238
29257
  else if (!currentHash) {
29239
29258
  includeOnChange = false;
29240
29259
  }
29241
29260
  }
29242
29261
  catch {
29243
- includeOnChange = true;
29262
+ includeOnChange = false;
29244
29263
  }
29245
29264
  return includeOnChange;
29246
29265
  };
@@ -29469,28 +29488,55 @@ const preparePromptOrThrow = async (args) => {
29469
29488
  };
29470
29489
 
29471
29490
  /**
29472
- + * Unified archive runner (ephemeral and non‑ephemeral).
29473
- *+ - Ephemeral:
29474
- *+ • includeOnChange=true: inject BEFORE diff so prompt appears exactly once in diff; then full.
29475
- *+ • includeOnChange=false: quiet diff first; inject ONLY for full.
29476
- *+ - Non‑ephemeral:
29477
- *+ • prepare once when needed; run diff then full; restore afterward.
29491
+ * Normalize an imports map so that all values are concrete string[].
29492
+ * Drops entries with undefined values and returns null when empty.
29493
+ */
29494
+ const normalizeImportsMap = (m) => {
29495
+ if (!m)
29496
+ return m;
29497
+ const out = {};
29498
+ for (const [k, v] of Object.entries(m)) {
29499
+ if (Array.isArray(v))
29500
+ out[k] = v;
29501
+ }
29502
+ return Object.keys(out).length ? out : null;
29503
+ };
29504
+ /**
29505
+ * Unified archive runner (ephemeral and non‑ephemeral).
29506
+ * - Ephemeral:
29507
+ * • includeOnChange=true: inject BEFORE diff so prompt appears exactly once in diff; then full.
29508
+ * • includeOnChange=false: quiet diff first; inject ONLY for full.
29509
+ * - Non‑ephemeral:
29510
+ * • prepare once when needed; run diff then full; restore afterward.
29478
29511
  */
29479
29512
  const runArchiveUnified = async (args) => {
29480
29513
  const { cwd, stanPath, behavior, promptAbs, promptDisplay, baseFull, baseDiff, archivePhase, stageImports, preparePrompt, prepareIfNeeded, shouldContinue, progress, ephemeral, includeOnChange, importsMap, } = args;
29481
29514
  const created = [];
29482
29515
  const includeOutputs = Boolean(behavior.combine);
29516
+ const posix = (p) => p.replace(/\\+/g, '/');
29483
29517
  // Optional imports staging (ephemeral path stages once for both passes)
29484
29518
  if (ephemeral && typeof stageImports === 'function') {
29485
29519
  if (typeof shouldContinue !== 'function' || shouldContinue())
29486
- await stageImports(cwd, stanPath, importsMap);
29520
+ await stageImports(cwd, stanPath, normalizeImportsMap(importsMap));
29487
29521
  else
29488
29522
  return created;
29489
29523
  }
29490
29524
  const runDiff = async () => {
29491
29525
  if (typeof shouldContinue === 'function' && !shouldContinue())
29492
29526
  return;
29493
- const d = await archivePhase({ cwd, config: baseDiff, includeOutputs }, { silent: true, which: 'diff', progress, shouldContinue });
29527
+ // Quiet DIFF when the prompt is unchanged:
29528
+ // Exclude stan.system.md to neutralize snapshot residue (whether the prompt
29529
+ // source is ephemeral or local) so DIFF doesn't reintroduce or delete it.
29530
+ const diffCfg = includeOnChange === false
29531
+ ? {
29532
+ ...baseDiff,
29533
+ excludes: [
29534
+ ...(baseDiff.excludes ?? []),
29535
+ posix(`${stanPath}/system/stan.system.md`),
29536
+ ],
29537
+ }
29538
+ : baseDiff;
29539
+ const d = await archivePhase({ cwd, config: diffCfg, includeOutputs }, { silent: true, which: 'diff', progress, shouldContinue });
29494
29540
  if (d.diffPath)
29495
29541
  created.push(d.diffPath);
29496
29542
  };
@@ -29553,7 +29599,7 @@ const runArchiveUnified = async (args) => {
29553
29599
  };
29554
29600
 
29555
29601
  const runArchiveStage = async (args) => {
29556
- const { cwd, config, behavior, ui, promptAbs, promptDisplay } = args;
29602
+ const { cwd, config, behavior, ui, promptAbs, promptDisplay, promptSource } = args;
29557
29603
  const shouldContinue = typeof args.shouldContinue === 'function'
29558
29604
  ? args.shouldContinue
29559
29605
  : () => true;
@@ -29566,6 +29612,22 @@ const runArchiveStage = async (args) => {
29566
29612
  const systemAbs = path.join(cwd, config.stanPath, 'system', 'stan.system.md');
29567
29613
  const { full: baseFull, diff: baseDiff } = makeBaseConfigs(config);
29568
29614
  const progress = buildArchiveProgress(ui, cwd);
29615
+ // Persist the prompt baseline best-effort (source + hash + path).
29616
+ const persistPromptBaseline = async () => {
29617
+ try {
29618
+ if (!promptAbs || !promptSource)
29619
+ return;
29620
+ const hash = await sha256File(promptAbs);
29621
+ await updateDocsMetaPrompt(cwd, config.stanPath, {
29622
+ source: promptSource,
29623
+ hash,
29624
+ path: promptAbs,
29625
+ });
29626
+ }
29627
+ catch {
29628
+ /* best-effort */
29629
+ }
29630
+ };
29569
29631
  const ephemeral = isEphemeralPrompt(systemAbs, promptAbs);
29570
29632
  if (ephemeral) {
29571
29633
  if (!shouldContinue())
@@ -29612,6 +29674,7 @@ const runArchiveStage = async (args) => {
29612
29674
  importsMap: config.imports,
29613
29675
  });
29614
29676
  created.push(...out);
29677
+ await persistPromptBaseline();
29615
29678
  return { created, cancelled: false };
29616
29679
  }
29617
29680
  catch (e) {
@@ -29655,6 +29718,7 @@ const runArchiveStage = async (args) => {
29655
29718
  ephemeral: false,
29656
29719
  });
29657
29720
  created.push(...out);
29721
+ await persistPromptBaseline();
29658
29722
  return { created, cancelled: false };
29659
29723
  }
29660
29724
  catch (e) {
@@ -29952,7 +30016,9 @@ const printPlanWithPrompt = (cwd, args) => {
29952
30016
  console.log('');
29953
30017
  };
29954
30018
 
29955
- // src/runner/run/session/run-session/steps/prompt.ts
30019
+ /* src/stan/run/session/run-session/steps/prompt.ts
30020
+ * Prompt resolution and plan printing (with prompt line).
30021
+ */
29956
30022
  /**
29957
30023
  * Resolve the prompt to use and (optionally) print the plan with the resolved prompt line.
29958
30024
  *
@@ -29966,10 +30032,12 @@ function resolvePromptAndMaybePrintPlan(args) {
29966
30032
  const { cwd, config, selection, mode, behavior, ui, planBody, printPlan, promptChoice, } = args;
29967
30033
  let resolvedPromptDisplay = '';
29968
30034
  let resolvedPromptAbs = null;
30035
+ let resolvedPromptSource = null;
29969
30036
  try {
29970
30037
  const rp = resolvePromptOrThrow(cwd, config.stanPath, promptChoice);
29971
30038
  resolvedPromptDisplay = rp.display;
29972
30039
  resolvedPromptAbs = rp.abs;
30040
+ resolvedPromptSource = rp.kind;
29973
30041
  try {
29974
30042
  if (process.env.STAN_DEBUG === '1') {
29975
30043
  const srcKind = rp.kind ||
@@ -29988,6 +30056,7 @@ function resolvePromptAndMaybePrintPlan(args) {
29988
30056
  console.log('');
29989
30057
  resolvedPromptDisplay = 'auto (unresolved)';
29990
30058
  resolvedPromptAbs = null;
30059
+ resolvedPromptSource = null;
29991
30060
  }
29992
30061
  if (printPlan && planBody) {
29993
30062
  try {
@@ -30005,7 +30074,11 @@ function resolvePromptAndMaybePrintPlan(args) {
30005
30074
  /* ignore plan print failure */
30006
30075
  }
30007
30076
  }
30008
- return { display: resolvedPromptDisplay, abs: resolvedPromptAbs };
30077
+ return {
30078
+ display: resolvedPromptDisplay,
30079
+ abs: resolvedPromptAbs,
30080
+ source: resolvedPromptSource,
30081
+ };
30009
30082
  }
30010
30083
 
30011
30084
  /**
@@ -30099,7 +30172,7 @@ const runSessionOnce = async (args) => {
30099
30172
  // Optional order file (tests)
30100
30173
  const orderFile = await ensureOrderFile(shouldWriteOrder, outAbs, Boolean(behavior.keep));
30101
30174
  // Resolve prompt & maybe print plan; then start UI and ensure a first frame
30102
- const { display: resolvedPromptDisplay, abs: resolvedPromptAbs } = resolvePromptAndMaybePrintPlan({
30175
+ const { display: resolvedPromptDisplay, abs: resolvedPromptAbs} = resolvePromptAndMaybePrintPlan({
30103
30176
  cwd,
30104
30177
  config,
30105
30178
  selection,