sneakoscope 0.7.74 → 0.7.78
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 +6 -1
- package/package.json +1 -1
- package/src/cli/install-helpers.mjs +178 -3
- package/src/cli/main.mjs +166 -27
- package/src/cli/maintenance-commands.mjs +40 -8
- package/src/core/fsx.mjs +1 -1
- package/src/core/hooks-runtime.mjs +24 -8
- package/src/core/init.mjs +12 -17
- package/src/core/pipeline.mjs +6 -1
- package/src/core/routes.mjs +3 -3
- package/src/core/team-live.mjs +81 -12
- package/src/core/tmux-ui.mjs +303 -61
- package/src/core/version-manager.mjs +61 -31
|
@@ -1,22 +1,38 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import fsp from 'node:fs/promises';
|
|
3
|
-
import {
|
|
3
|
+
import { exists, nowIso, readJson, runProcess, writeJsonAtomic, writeTextAtomic } from './fsx.mjs';
|
|
4
4
|
|
|
5
5
|
const VERSION_HOOK_MARKER = 'Sneakoscope Codex Version Guard';
|
|
6
6
|
const VERSION_STATE_FILE = 'sks-version-state.json';
|
|
7
7
|
const DEFAULT_BUMP = 'patch';
|
|
8
8
|
|
|
9
9
|
export async function installVersionGitHook(root, commandPrefix = 'sks') {
|
|
10
|
+
void root;
|
|
11
|
+
void commandPrefix;
|
|
12
|
+
return {
|
|
13
|
+
ok: false,
|
|
14
|
+
installed: false,
|
|
15
|
+
reason: 'pre_commit_hooks_unsupported',
|
|
16
|
+
message: 'SKS no longer installs Git pre-commit hooks. Use `sks versioning bump` and release checks explicitly.'
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function disableVersionGitHook(root) {
|
|
21
|
+
await setVersionPolicyEnabled(root, false);
|
|
10
22
|
const git = await gitPaths(root);
|
|
11
|
-
if (!git.ok) return { ok: true,
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
if (!git.ok) return { ok: true, disabled: true, hook_removed: false, reason: git.reason || 'not_git' };
|
|
24
|
+
const current = await readFileMaybe(git.hook_path);
|
|
25
|
+
if (!current.includes(`BEGIN ${VERSION_HOOK_MARKER}`)) {
|
|
26
|
+
return { ok: true, disabled: true, hook_removed: false, hook_path: git.hook_path, reason: 'managed_hook_not_installed' };
|
|
27
|
+
}
|
|
28
|
+
const next = removeShellBlock(current, VERSION_HOOK_MARKER);
|
|
29
|
+
if (next.trim() === '#!/bin/sh' || next.trim() === '#!/usr/bin/env sh' || !next.trim()) {
|
|
30
|
+
await fsp.rm(git.hook_path, { force: true });
|
|
31
|
+
return { ok: true, disabled: true, hook_removed: true, hook_path: git.hook_path };
|
|
32
|
+
}
|
|
33
|
+
await writeTextAtomic(git.hook_path, next);
|
|
34
|
+
await fsp.chmod(git.hook_path, 0o755).catch(() => {});
|
|
35
|
+
return { ok: true, disabled: true, hook_removed: true, hook_path: git.hook_path };
|
|
20
36
|
}
|
|
21
37
|
|
|
22
38
|
export async function versioningStatus(root) {
|
|
@@ -40,7 +56,7 @@ export async function versioningStatus(root) {
|
|
|
40
56
|
state_path: path.join(git.common_dir, VERSION_STATE_FILE),
|
|
41
57
|
last_version: state.last_version || null,
|
|
42
58
|
runtime_drift: runtimeDrift,
|
|
43
|
-
reason: version ? null : 'package_json_version_missing'
|
|
59
|
+
reason: !policy.enabled ? 'disabled_by_policy' : (version ? null : 'package_json_version_missing')
|
|
44
60
|
};
|
|
45
61
|
}
|
|
46
62
|
|
|
@@ -190,11 +206,38 @@ export async function verifyProjectVersion(root, opts = {}) {
|
|
|
190
206
|
async function versionPolicy(root) {
|
|
191
207
|
const policy = await readJson(path.join(root, '.sneakoscope', 'policy.json'), {});
|
|
192
208
|
return {
|
|
193
|
-
enabled: policy.versioning?.enabled
|
|
209
|
+
enabled: policy.versioning?.enabled === true,
|
|
194
210
|
bump: policy.versioning?.bump || DEFAULT_BUMP
|
|
195
211
|
};
|
|
196
212
|
}
|
|
197
213
|
|
|
214
|
+
async function setVersionPolicyEnabled(root, enabled) {
|
|
215
|
+
const policyPath = path.join(root, '.sneakoscope', 'policy.json');
|
|
216
|
+
const policy = await readJson(policyPath, {});
|
|
217
|
+
await writeJsonAtomic(policyPath, {
|
|
218
|
+
...policy,
|
|
219
|
+
git: {
|
|
220
|
+
...(policy.git || {}),
|
|
221
|
+
versioning: {
|
|
222
|
+
...(policy.git?.versioning || {}),
|
|
223
|
+
enabled: Boolean(enabled),
|
|
224
|
+
bump: policy.git?.versioning?.bump || policy.versioning?.bump || DEFAULT_BUMP,
|
|
225
|
+
lock: 'git-common-dir/sks-version.lock',
|
|
226
|
+
state: 'git-common-dir/sks-version-state.json'
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
versioning: {
|
|
230
|
+
...(policy.versioning || {}),
|
|
231
|
+
enabled: Boolean(enabled),
|
|
232
|
+
bump: policy.versioning?.bump || DEFAULT_BUMP,
|
|
233
|
+
trigger: 'manual',
|
|
234
|
+
lock_scope: 'git-common-dir',
|
|
235
|
+
managed_files: policy.versioning?.managed_files || ['package.json', 'package-lock.json', 'npm-shrinkwrap.json'],
|
|
236
|
+
collision_policy: policy.versioning?.collision_policy || 'explicit_bump_only'
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
198
241
|
async function gitPaths(root) {
|
|
199
242
|
const top = await git(root, ['rev-parse', '--show-toplevel']);
|
|
200
243
|
if (top.code !== 0) return { ok: false, reason: 'not_git' };
|
|
@@ -287,7 +330,7 @@ async function syncChangelogVersionSection(root, version) {
|
|
|
287
330
|
text = text.replace(/^#\s+Changelog\s*$/m, (title) => `${title}\n\n## [Unreleased]`);
|
|
288
331
|
}
|
|
289
332
|
|
|
290
|
-
const managedSection = `\n## [${version}] - ${date}\n\n### Fixed\n\n- Keep release metadata aligned after
|
|
333
|
+
const managedSection = `\n## [${version}] - ${date}\n\n### Fixed\n\n- Keep release metadata aligned after an explicit SKS version bump advances the package version.\n`;
|
|
291
334
|
const next = text.replace(/^##\s+\[Unreleased\]\s*$/m, (heading) => `${heading}\n${managedSection}`);
|
|
292
335
|
if (next === text) return { files: [], relative_files: [] };
|
|
293
336
|
await writeTextAtomic(file, next);
|
|
@@ -341,26 +384,13 @@ function bumpSemver(v, bump = DEFAULT_BUMP) {
|
|
|
341
384
|
return { major: v.major, minor: v.minor, patch: v.patch + 1 };
|
|
342
385
|
}
|
|
343
386
|
|
|
344
|
-
function
|
|
345
|
-
return `# SKS keeps package versions unique across worker commits.\n${commandPrefix} versioning pre-commit\nstatus=$?\nif [ $status -ne 0 ]; then\n echo \"SKS versioning blocked commit. Run: sks versioning status\" >&2\n exit $status\nfi`;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function mergeShellBlock(current, marker, block) {
|
|
387
|
+
function removeShellBlock(current, marker) {
|
|
349
388
|
const begin = `# BEGIN ${marker}`;
|
|
350
389
|
const end = `# END ${marker}`;
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const endIdx = withShebang.indexOf(end);
|
|
356
|
-
if (beginIdx >= 0 && endIdx >= beginIdx) {
|
|
357
|
-
return `${withShebang.slice(0, beginIdx)}${managed}${withShebang.slice(endIdx + end.length).replace(/^\n/, '')}`;
|
|
358
|
-
}
|
|
359
|
-
const lines = withShebang.split('\n');
|
|
360
|
-
if (lines[0]?.startsWith('#!')) {
|
|
361
|
-
return `${lines[0]}\n${managed}${lines.slice(1).join('\n').replace(/^\n/, '')}`.replace(/\s*$/, '\n');
|
|
362
|
-
}
|
|
363
|
-
return `${managed}${withShebang.replace(/^\n/, '').replace(/\s*$/, '\n')}`;
|
|
390
|
+
const beginIdx = current.indexOf(begin);
|
|
391
|
+
const endIdx = current.indexOf(end);
|
|
392
|
+
if (beginIdx < 0 || endIdx < beginIdx) return current;
|
|
393
|
+
return `${current.slice(0, beginIdx)}${current.slice(endIdx + end.length).replace(/^\n/, '')}`.replace(/\s*$/, '\n');
|
|
364
394
|
}
|
|
365
395
|
|
|
366
396
|
async function readFileMaybe(file) {
|