sneakoscope 0.7.37 → 0.7.38

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "0.7.37",
4
+ "version": "0.7.38",
5
5
  "description": "Sneakoscope Codex: database-safe Codex CLI/App harness with Team, Goal, AutoResearch, TriWiki, and Honest Mode.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
package/src/cli/main.mjs CHANGED
@@ -2043,9 +2043,11 @@ async function selftest() {
2043
2043
  if (!firstVersionBump.ok || firstVersionBump.version !== '0.1.1' || !firstVersionBump.changed) throw new Error('selftest failed: first version bump did not advance patch version');
2044
2044
  const bumpedPackage = await readJson(path.join(versionTmp, 'package.json'));
2045
2045
  const bumpedLock = await readJson(path.join(versionTmp, 'package-lock.json'));
2046
+ const bumpedChangelog = await safeReadText(path.join(versionTmp, 'CHANGELOG.md'));
2046
2047
  if (bumpedPackage.version !== '0.1.1' || bumpedLock.version !== '0.1.1' || bumpedLock.packages[''].version !== '0.1.1') throw new Error('selftest failed: package lock versions not synced');
2048
+ if (!bumpedChangelog.includes('## [0.1.1]') || !bumpedChangelog.includes('automatic SKS version guard')) throw new Error('selftest failed: version bump did not sync changelog section');
2047
2049
  const firstCached = await runProcess('git', ['diff', '--cached', '--name-only'], { cwd: versionTmp, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
2048
- if (!firstCached.stdout.includes('package.json') || !firstCached.stdout.includes('package-lock.json')) throw new Error('selftest failed: version files not staged');
2050
+ if (!firstCached.stdout.includes('package.json') || !firstCached.stdout.includes('package-lock.json') || !firstCached.stdout.includes('CHANGELOG.md')) throw new Error('selftest failed: version files not staged');
2049
2051
  await runProcess('git', ['commit', '--no-verify', '-m', 'first versioned commit'], { cwd: versionTmp, timeoutMs: 15000, maxOutputBytes: 64 * 1024 });
2050
2052
  await writeJsonAtomic(versionStatus.state_path, { schema_version: 1, last_version: '0.1.5', updated_at: nowIso(), pid: process.pid, changed: true });
2051
2053
  await writeTextAtomic(path.join(versionTmp, 'CHANGELOG.md'), 'collision selftest\n');
package/src/core/fsx.mjs CHANGED
@@ -5,7 +5,7 @@ import os from 'node:os';
5
5
  import crypto from 'node:crypto';
6
6
  import { spawn } from 'node:child_process';
7
7
 
8
- export const PACKAGE_VERSION = '0.7.37';
8
+ export const PACKAGE_VERSION = '0.7.38';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
 
@@ -108,15 +108,17 @@ export async function bumpProjectVersion(root, opts = {}) {
108
108
  pkg.version = formatSemver(target);
109
109
  await writeJsonAtomic(pkgPath, pkg);
110
110
  }
111
- const sourceVersion = await syncSourcePackageVersion(root, formatSemver(target));
112
- const synced = await syncPackageLockVersions(root, formatSemver(target));
113
- const staged = await stageVersionFiles(root, [pkgPath, ...synced.files, ...sourceVersion.files]);
111
+ const targetVersion = formatSemver(target);
112
+ const sourceVersion = await syncSourcePackageVersion(root, targetVersion);
113
+ const changelog = await syncChangelogVersionSection(root, targetVersion);
114
+ const synced = await syncPackageLockVersions(root, targetVersion);
115
+ const staged = await stageVersionFiles(root, [pkgPath, ...synced.files, ...sourceVersion.files, ...changelog.files]);
114
116
  if (!staged.ok) return { ok: false, reason: 'git_add_version_files_failed', stderr: staged.stderr };
115
117
  if (statePath) {
116
118
  await writeJsonAtomic(statePath, {
117
119
  schema_version: 1,
118
- last_version: formatSemver(target),
119
- previous_version: pkg.version === formatSemver(target) && !changed ? formatSemver(current) : formatSemver(current),
120
+ last_version: targetVersion,
121
+ previous_version: pkg.version === targetVersion && !changed ? formatSemver(current) : formatSemver(current),
120
122
  updated_at: nowIso(),
121
123
  pid: process.pid,
122
124
  bump: policy.bump || DEFAULT_BUMP,
@@ -125,19 +127,19 @@ export async function bumpProjectVersion(root, opts = {}) {
125
127
  }
126
128
  await writeJsonAtomic(path.join(root, '.sneakoscope', 'version', 'last.json'), {
127
129
  schema_version: 1,
128
- version: formatSemver(target),
130
+ version: targetVersion,
129
131
  previous_version: formatSemver(current),
130
132
  changed,
131
- synced_files: [...synced.relative_files, ...sourceVersion.relative_files],
133
+ synced_files: [...synced.relative_files, ...sourceVersion.relative_files, ...changelog.relative_files],
132
134
  staged_files: staged.relative_files,
133
135
  updated_at: nowIso()
134
136
  }).catch(() => {});
135
137
  return {
136
138
  ok: true,
137
139
  changed,
138
- version: formatSemver(target),
140
+ version: targetVersion,
139
141
  previous_version: formatSemver(current),
140
- synced_files: [...synced.relative_files, ...sourceVersion.relative_files],
142
+ synced_files: [...synced.relative_files, ...sourceVersion.relative_files, ...changelog.relative_files],
141
143
  staged_files: staged.relative_files,
142
144
  lock_scope: git.common_dir
143
145
  };
@@ -230,6 +232,26 @@ async function syncSourcePackageVersion(root, version) {
230
232
  return { files: [file], relative_files: [path.relative(root, file)] };
231
233
  }
232
234
 
235
+ async function syncChangelogVersionSection(root, version) {
236
+ const file = path.join(root, 'CHANGELOG.md');
237
+ let text = await readFileMaybe(file);
238
+ const date = nowIso().slice(0, 10);
239
+ const sectionRe = new RegExp(`^##\\s+\\[${escapeRegExp(version)}\\]\\s+-\\s+\\d{4}-\\d{2}-\\d{2}\\s*$`, 'm');
240
+ if (sectionRe.test(text)) return { files: [], relative_files: [] };
241
+
242
+ if (!text.trim()) text = '# Changelog\n\n## [Unreleased]\n\n';
243
+ if (!/^#\s+Changelog\s*$/m.test(text)) text = `# Changelog\n\n${text.replace(/^\s+/, '')}`;
244
+ if (!/^##\s+\[Unreleased\]\s*$/m.test(text)) {
245
+ text = text.replace(/^#\s+Changelog\s*$/m, (title) => `${title}\n\n## [Unreleased]`);
246
+ }
247
+
248
+ const managedSection = `\n## [${version}] - ${date}\n\n### Fixed\n\n- Keep release metadata aligned after the automatic SKS version guard advances the package version.\n`;
249
+ const next = text.replace(/^##\s+\[Unreleased\]\s*$/m, (heading) => `${heading}\n${managedSection}`);
250
+ if (next === text) return { files: [], relative_files: [] };
251
+ await writeTextAtomic(file, next);
252
+ return { files: [file], relative_files: [path.relative(root, file)] };
253
+ }
254
+
233
255
  async function stageVersionFiles(root, files) {
234
256
  const existing = [];
235
257
  for (const file of files) if (await exists(file)) existing.push(path.relative(root, file));
@@ -238,6 +260,10 @@ async function stageVersionFiles(root, files) {
238
260
  return { ok: result.code === 0, relative_files: existing, stderr: result.stderr };
239
261
  }
240
262
 
263
+ function escapeRegExp(value) {
264
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
265
+ }
266
+
241
267
  function parseSemver(value) {
242
268
  const match = String(value || '').trim().match(/^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);
243
269
  if (!match) return null;