sneakoscope 4.8.0 → 4.8.2
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 +3 -3
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/sks.js +1 -1
- package/dist/core/codex-app/sks-menubar.js +94 -10
- package/dist/core/codex-native/core-skill-manifest.js +16 -5
- package/dist/core/fsx.js +1 -1
- package/dist/core/managed-assets/managed-assets-manifest.js +1 -1
- package/dist/core/release/release-gate-hermetic-env.js +4 -1
- package/dist/core/update/update-migration-state.js +1 -1
- package/dist/core/version.js +1 -1
- package/dist/scripts/release-metadata-1-19-check.js +3 -0
- package/dist/scripts/runtime-ts-rust-boundary-check.js +1 -1
- package/dist/scripts/seo-geo-skill-rich-content-check.js +16 -0
- package/dist/scripts/sks-menubar-install-check.js +54 -7
- package/dist/scripts/update-first-command-migration-check.js +1 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -49,7 +49,7 @@ sks seo-geo-optimizer apply latest --mode seo --apply --json
|
|
|
49
49
|
sks seo-geo-optimizer audit --mode geo --target package --offline --json
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
> 📋 **Current release: `v4.8.
|
|
52
|
+
> 📋 **Current release: `v4.8.2`** — full release history lives in [CHANGELOG.md](CHANGELOG.md). This README documents how Sneakoscope works today, not its version-by-version changes. Release readiness is tracked in [docs/release-readiness.md](docs/release-readiness.md).
|
|
53
53
|
|
|
54
54
|
## 🍥 Parallelism, UX, And Integrations
|
|
55
55
|
|
|
@@ -363,7 +363,7 @@ sks codex-app set-openrouter-key --api-key-stdin
|
|
|
363
363
|
sks codex-app glm-profile install
|
|
364
364
|
```
|
|
365
365
|
|
|
366
|
-
This writes the OpenRouter key to the SKS user secret store, writes redacted metadata only, and installs Codex Desktop-compatible `openrouter` provider plus selectable `sks-glm-52-*` reasoning profiles. The npm package does not patch the Codex Desktop macOS menu binary; a top-level menu between Window and Help requires upstream Codex Desktop UI support. On macOS, `sks doctor --fix` installs or refreshes a right-side SKS menu bar companion
|
|
366
|
+
This writes the OpenRouter key to the SKS user secret store, writes redacted metadata only, and installs Codex Desktop-compatible `openrouter` provider plus selectable `sks-glm-52-*` reasoning profiles. The npm package does not patch the Codex Desktop macOS menu binary; a top-level menu between Window and Help requires upstream Codex Desktop UI support. On macOS, `sks doctor --fix` installs or refreshes a right-side `SKS` menu bar companion with codex-lb, ChatGPT OAuth, OpenRouter/GLM, Fast Check, SKS Version Check, Update SKS Now, settings, and Codex restart actions.
|
|
367
367
|
|
|
368
368
|
### Switching auth mode: codex-lb ↔ ChatGPT OAuth
|
|
369
369
|
|
|
@@ -763,7 +763,7 @@ npm run release:check
|
|
|
763
763
|
npm run publish:dry
|
|
764
764
|
```
|
|
765
765
|
|
|
766
|
-
`release:check` runs the change-aware affected release gate for ordinary local checks. Publish readiness uses `release:check:full`, which runs the full release DAG and writes a source digest stamp under `.sneakoscope/reports/` so publish commands can verify the same source/dist state. The DAG preserves the 1.18 baseline gates and adds Codex 0.136 compatibility, inherited Codex 0.135/0.134 runner truth, patch swarm runtime truth, transaction journaling, serial conflict rebase, strict strategy-to-patch proof, rollback command proof, Native CLI Session Swarm 5/10/20-process proof, Real Worker Backend Router proof, Codex child overlap proof, model-authored patch-envelope separation, Zellij layout/pane/screen/socket-dir proof, no-subagent-scaling proof, Fast mode default/worker/Codex/MAD propagation proof, Appshots attachment provenance, MCP runtime overlap evidence, task graph expansion, schema-bound follow-up work, actual Agent/Team/Research/QA route blackboxes, scheduler proof hardening, Source Intelligence propagation, Goal mode propagation checks, slot telemetry, update notice, MAD-DB, and Naruto SSOT gates. Broader live gates remain explicit scripts such as `release:real-check`; real Codex patch smoke, real Codex parallel worker proof, and real Zellij proof are optional unless their `SKS_REQUIRE_REAL_*` or `SKS_REQUIRE_ZELLIJ=1` environment variables are set. Generate the human-readable registry with `sks features inventory --write-docs`. Plain `npm publish` uses the `latest` dist-tag. `npm run publish:dry` runs `release:check:full`, verifies the fresh stamp, and then performs provenance/registry and npm dry-run checks. `npm run publish:npm` and `npm run release:publish` run
|
|
766
|
+
`release:check` runs the change-aware affected release gate for ordinary local checks. Publish readiness uses `release:check:full`, which runs the full release DAG and writes a source digest stamp under `.sneakoscope/reports/` so publish commands can verify the same source/dist state. The DAG preserves the 1.18 baseline gates and adds Codex 0.136 compatibility, inherited Codex 0.135/0.134 runner truth, patch swarm runtime truth, transaction journaling, serial conflict rebase, strict strategy-to-patch proof, rollback command proof, Native CLI Session Swarm 5/10/20-process proof, Real Worker Backend Router proof, Codex child overlap proof, model-authored patch-envelope separation, Zellij layout/pane/screen/socket-dir proof, no-subagent-scaling proof, Fast mode default/worker/Codex/MAD propagation proof, Appshots attachment provenance, MCP runtime overlap evidence, task graph expansion, schema-bound follow-up work, actual Agent/Team/Research/QA route blackboxes, scheduler proof hardening, Source Intelligence propagation, Goal mode propagation checks, slot telemetry, update notice, MAD-DB, and Naruto SSOT gates. Broader live gates remain explicit scripts such as `release:real-check`; real Codex patch smoke, real Codex parallel worker proof, and real Zellij proof are optional unless their `SKS_REQUIRE_REAL_*` or `SKS_REQUIRE_ZELLIJ=1` environment variables are set. Generate the human-readable registry with `sks features inventory --write-docs`. Plain `npm publish` uses the `latest` dist-tag. `npm run publish:dry` runs `release:check:full`, verifies the fresh stamp, and then performs provenance/registry and npm dry-run checks. `npm run publish:npm` and `npm run release:publish` run `npm run publish:prep-ignore-scripts` first, then `npm publish --ignore-scripts`, so the real publish path stays strict even when lifecycle scripts are skipped. Operators who intentionally run `npm publish --ignore-scripts` directly must run `npm run publish:prep-ignore-scripts` immediately before it. npm's `prepublishOnly` uses `prepublish-release-check-or-fast` to accept that current stamp before the real publish; if the stamp is missing or stale, it runs `release:check:full` once before continuing.
|
|
767
767
|
|
|
768
768
|
Version bumps are manual. Run `sks versioning bump` only when preparing release metadata; SKS will not create `.git/hooks/pre-commit` or auto-bump during ordinary commits.
|
|
769
769
|
|
|
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
|
|
|
4
4
|
fn main() {
|
|
5
5
|
let mut args = std::env::args().skip(1);
|
|
6
6
|
match args.next().as_deref() {
|
|
7
|
-
Some("--version") => println!("sks-rs 4.8.
|
|
7
|
+
Some("--version") => println!("sks-rs 4.8.2"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
package/dist/bin/sks.js
CHANGED
|
@@ -3,6 +3,8 @@ import os from 'node:os';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { ensureDir, exists, runProcess, which, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
|
|
5
5
|
const LABEL = 'com.sneakoscope.sks-menubar';
|
|
6
|
+
const CONTROL_CENTER_DOMAIN = 'com.apple.controlcenter';
|
|
7
|
+
const CONTROL_CENTER_PREFERRED_POSITION = 360;
|
|
6
8
|
const MENU_ITEMS = [
|
|
7
9
|
'Use codex-lb',
|
|
8
10
|
'Use ChatGPT OAuth',
|
|
@@ -105,7 +107,21 @@ export async function installSksMenuBar(opts = {}) {
|
|
|
105
107
|
actions.push(`compiled ${executablePath}`);
|
|
106
108
|
await writeTextAtomic(launchAgentPath, launchAgentSource(executablePath, installDir));
|
|
107
109
|
actions.push(`wrote ${launchAgentPath}`);
|
|
108
|
-
const
|
|
110
|
+
const launchWanted = opts.launch !== false && env.SKS_SKIP_SKS_MENUBAR_LAUNCH !== '1';
|
|
111
|
+
const launchAllowedForHome = path.resolve(home) === realUserHome();
|
|
112
|
+
const installUnderTempDir = isMenuBarInstallPathUnderTempDir(executablePath, env);
|
|
113
|
+
if (launchWanted && !launchAllowedForHome)
|
|
114
|
+
warnings.push('launch_skipped_non_user_home');
|
|
115
|
+
if (launchWanted && installUnderTempDir)
|
|
116
|
+
warnings.push('launch_skipped_temp_install');
|
|
117
|
+
const launchRequested = launchWanted && launchAllowedForHome && !installUnderTempDir;
|
|
118
|
+
if (launchRequested) {
|
|
119
|
+
const preferredPosition = await seedMenuBarPreferredPosition(env);
|
|
120
|
+
if (preferredPosition.ok)
|
|
121
|
+
actions.push('seeded SKS menu bar preferred position');
|
|
122
|
+
else
|
|
123
|
+
warnings.push(preferredPosition.warning);
|
|
124
|
+
}
|
|
109
125
|
const launch = launchRequested && launchctl
|
|
110
126
|
? await launchWithLaunchctl({ launchctl, open, appPath, executablePath, launchAgentPath })
|
|
111
127
|
: {
|
|
@@ -169,6 +185,71 @@ export async function installSksMenuBar(opts = {}) {
|
|
|
169
185
|
async function fallbackTool(candidate) {
|
|
170
186
|
return await exists(candidate).then((ok) => ok ? candidate : null).catch(() => null);
|
|
171
187
|
}
|
|
188
|
+
async function seedMenuBarPreferredPosition(env) {
|
|
189
|
+
const defaults = env.SKS_MENUBAR_DEFAULTS || await which('defaults').catch(() => null) || await fallbackTool('/usr/bin/defaults');
|
|
190
|
+
if (!defaults)
|
|
191
|
+
return { ok: false, warning: 'defaults_missing_for_menubar_position_seed' };
|
|
192
|
+
// macOS stores status-item ordering hints in Control Center prefs keyed by
|
|
193
|
+
// autosaveName. Seeding this keeps SKS right of the notch-overflow zone.
|
|
194
|
+
const writes = [
|
|
195
|
+
['write', CONTROL_CENTER_DOMAIN, `NSStatusItem Preferred Position ${LABEL}`, '-int', String(CONTROL_CENTER_PREFERRED_POSITION)],
|
|
196
|
+
['write', CONTROL_CENTER_DOMAIN, `NSStatusItem Visible ${LABEL}`, '-bool', 'true'],
|
|
197
|
+
['write', CONTROL_CENTER_DOMAIN, `NSStatusItem VisibleCC ${LABEL}`, '-bool', 'true']
|
|
198
|
+
];
|
|
199
|
+
for (const args of writes) {
|
|
200
|
+
const result = await runProcess(defaults, args, {
|
|
201
|
+
timeoutMs: 10_000,
|
|
202
|
+
maxOutputBytes: 16 * 1024
|
|
203
|
+
}).catch((err) => ({ code: 1, stdout: '', stderr: err?.message || String(err) }));
|
|
204
|
+
if (result.code !== 0)
|
|
205
|
+
return { ok: false, warning: 'menubar_position_seed_failed' };
|
|
206
|
+
}
|
|
207
|
+
return { ok: true };
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Refuse to auto-launch a menu bar app whose executable lives under a temp dir.
|
|
211
|
+
* Release gates run in hermetic envs rooted at os.tmpdir()/SKS_TMP_DIR; without
|
|
212
|
+
* this guard a gate could spawn a real GUI status item that leaks into the
|
|
213
|
+
* user's live menu bar (duplicate `com.sneakoscope.sks-menubar` process). This
|
|
214
|
+
* is defense-in-depth behind SKS_SKIP_SKS_MENUBAR_LAUNCH and the home check.
|
|
215
|
+
*/
|
|
216
|
+
export function isMenuBarInstallPathUnderTempDir(target, env = process.env) {
|
|
217
|
+
const resolved = path.resolve(target);
|
|
218
|
+
const roots = new Set();
|
|
219
|
+
const addRoot = (value) => {
|
|
220
|
+
if (!value)
|
|
221
|
+
return;
|
|
222
|
+
const abs = path.resolve(value);
|
|
223
|
+
roots.add(abs);
|
|
224
|
+
// macOS resolves TMPDIR / os.tmpdir() (/var/folders/...) through a /private symlink.
|
|
225
|
+
if (abs.startsWith('/var/'))
|
|
226
|
+
roots.add(path.resolve('/private', abs.slice(1)));
|
|
227
|
+
else if (abs.startsWith('/private/var/'))
|
|
228
|
+
roots.add(abs.replace('/private', ''));
|
|
229
|
+
};
|
|
230
|
+
addRoot(os.tmpdir());
|
|
231
|
+
addRoot(env.TMPDIR);
|
|
232
|
+
addRoot(env.SKS_TMP_DIR);
|
|
233
|
+
for (const root of roots) {
|
|
234
|
+
if (resolved === root)
|
|
235
|
+
return true;
|
|
236
|
+
const prefix = root.endsWith(path.sep) ? root : root + path.sep;
|
|
237
|
+
if (resolved.startsWith(prefix))
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
function realUserHome() {
|
|
243
|
+
try {
|
|
244
|
+
const userHome = os.userInfo().homedir;
|
|
245
|
+
if (userHome)
|
|
246
|
+
return path.resolve(userHome);
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
// Fall back below for platforms where userInfo is unavailable.
|
|
250
|
+
}
|
|
251
|
+
return path.resolve(os.homedir());
|
|
252
|
+
}
|
|
172
253
|
function resolveSksEntry(explicit) {
|
|
173
254
|
if (explicit)
|
|
174
255
|
return path.resolve(explicit);
|
|
@@ -222,7 +303,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
|
|
|
222
303
|
|
|
223
304
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
|
224
305
|
NSApp.setActivationPolicy(.accessory)
|
|
225
|
-
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.
|
|
306
|
+
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
|
|
307
|
+
statusItem.autosaveName = "com.sneakoscope.sks-menubar"
|
|
308
|
+
statusItem.isVisible = true
|
|
226
309
|
if let button = statusItem.button {
|
|
227
310
|
configureStatusButton(button)
|
|
228
311
|
}
|
|
@@ -244,14 +327,15 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
|
|
|
244
327
|
}
|
|
245
328
|
|
|
246
329
|
func configureStatusButton(_ button: NSStatusBarButton) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
330
|
+
button.image = nil
|
|
331
|
+
button.title = "SKS"
|
|
332
|
+
let titleFont = NSFont.systemFont(ofSize: NSFont.systemFontSize, weight: .semibold)
|
|
333
|
+
let titleAttributes: [NSAttributedString.Key: Any] = [
|
|
334
|
+
.font: titleFont,
|
|
335
|
+
.foregroundColor: NSColor.labelColor
|
|
336
|
+
]
|
|
337
|
+
button.font = titleFont
|
|
338
|
+
button.attributedTitle = NSAttributedString(string: "SKS", attributes: titleAttributes)
|
|
255
339
|
button.toolTip = "SKS - Sneakoscope Codex settings"
|
|
256
340
|
button.setAccessibilityLabel("SKS")
|
|
257
341
|
button.setAccessibilityHelp("Open SKS menu")
|
|
@@ -90,9 +90,12 @@ const CORE_SKILL_DEFINITIONS = [
|
|
|
90
90
|
canonical_name: 'search-visibility-core',
|
|
91
91
|
display_name: 'search-visibility-core',
|
|
92
92
|
route: '$SEO-GEO-OPTIMIZER',
|
|
93
|
-
purpose: 'provide the shared search-visibility kernel for SEO and GEO audit, plan, explicit apply, verify, rollback, and Completion Proof without ranking, traffic, or citation guarantees.',
|
|
94
|
-
when: 'Use when $SEO-GEO-OPTIMIZER or sks seo-geo-optimizer needs typed mode-specific evidence, gates, artifacts, or safe mutation planning.',
|
|
95
|
-
|
|
93
|
+
purpose: 'provide the shared search-visibility kernel for SEO and GEO audit, plan, explicit apply, verify, rollback, and Completion Proof without ranking, traffic, indexing, rich-result, answer inclusion, or AI citation guarantees.',
|
|
94
|
+
when: 'Use when $SEO-GEO-OPTIMIZER or sks seo-geo-optimizer needs typed mode-specific evidence, gates, artifacts, or safe mutation planning for websites, docs, packages, README/npm/GitHub surfaces, or storefront SEO architecture.',
|
|
95
|
+
workflow: 'Read the touched search-visibility flow before planning: SEO constants/source-of-truth, metadata builders, route topology, sitemap/robots, llms.txt, structured-data builders, target route files, internal-link sources, and SEO tests. Inventory canonical URLs, alternates/hreflang, crawlable localized sitemap rows, JSON-LD, crawler hints, claim evidence, and verification commands before compiling mutation-plan.json.',
|
|
96
|
+
safety: 'Treat SEO/GEO as an architecture compiler, not scattered page copy. Prefer existing project helpers and constants; never duplicate public route strings across files. JSON-LD must describe visible or source-of-truth facts only. Do not invent prices, reviews, ratings, availability, shipping terms, rankings, traffic, rich-result eligibility, indexing, or AI-answer outcomes.',
|
|
97
|
+
cli: 'sks seo-geo-optimizer doctor|audit|plan|apply|verify|status|rollback|fixture --mode seo|geo',
|
|
98
|
+
evidence: 'search-visibility/intake.json, adapter-detection.json, site-inventory.json, route-graph.json, robots-policy.json, structured-data-ledger.json, sitemap coverage, llms.txt plan/evidence, mutation-plan.json, rollback-manifest.json, verification-report.json, seo-gate.json or geo-gate.json, and completion-proof.json.',
|
|
96
99
|
fallback: 'Keep unsupported frameworks plan-only, record unverified production/browser/Search Console/AI citation outcomes, and never invent guarantee evidence.'
|
|
97
100
|
},
|
|
98
101
|
{
|
|
@@ -101,8 +104,11 @@ const CORE_SKILL_DEFINITIONS = [
|
|
|
101
104
|
display_name: 'seo-geo-optimizer',
|
|
102
105
|
route: '$SEO-GEO-OPTIMIZER',
|
|
103
106
|
purpose: 'run the unified SEO/GEO optimizer route for Search Engine Optimization and Generative Engine Optimization, not geolocation or GeoIP, with no ranking, traffic, indexing, rich-result, answer inclusion, or AI citation guarantee.',
|
|
104
|
-
when: 'Use the CLI entrypoint: sks seo-geo-optimizer doctor|audit|plan|apply|verify|status|rollback|fixture --mode seo|geo for SEO and GEO visibility work.',
|
|
105
|
-
|
|
107
|
+
when: 'Use the single CLI entrypoint: sks seo-geo-optimizer doctor|audit|plan|apply|verify|status|rollback|fixture --mode seo|geo for SEO and GEO visibility work. Legacy seo-geo wording, SEO, GEO, search visibility, AI visibility, sitemap, canonical, JSON-LD, llms.txt, metadata, keyword/intent, or crawler-policy requests should converge here.',
|
|
108
|
+
workflow: 'Follow the architecture-first playbook: identify target intent and market, locate the central SEO source of truth, update/reuse canonical helpers before page code, build metadata through project helpers, emit independently crawlable localized sitemap rows plus alternates when applicable, add factual JSON-LD only from visible/source data, add internal crawl links, update AI/GEO documentation surfaces such as llms.txt when strategic, add focused tests/guards, then verify. For CELIMAX-style storefronts, prefer STOREFRONT_PATH, buildSeoMetadata, buildSeoPath, buildSeoUrl, app/sitemap.ts, proxy.ts, public/llms.txt, and market-specific tests when those files exist.',
|
|
109
|
+
safety: 'Separate implemented, verified, and unverified claims. Competitor/retailer intent pages must be factual, helpful, and non-deceptive; block keyword stuffing, doorway pages, hidden AI-only text, fake ratings/reviews/prices/availability/shipping, competitor defamation, and unsupported "official retailer", "cheapest", "best", "exclusive", first-page, traffic, Search Console, or AI-citation claims.',
|
|
110
|
+
cli: 'sks seo-geo-optimizer doctor|audit|plan|apply|verify|status|rollback|fixture --mode seo|geo',
|
|
111
|
+
evidence: 'SEO intent map, canonical URL map, metadata summary, JSON-LD summary, sitemap coverage summary, internal link plan, unsupported claims ledger, Search Console/analytics follow-up plan, site inventory, route graph, seo-findings.json or geo-findings.json, claim-evidence-ledger.json, ai-crawler-policy.json, llms-txt-plan.json, verification report, route gate, and Completion Proof.',
|
|
106
112
|
fallback: 'Do not auto-allow training crawlers or fabricate AI answer visibility; mark missing live outcomes unverified and keep recovery on the unified optimizer route.'
|
|
107
113
|
}
|
|
108
114
|
];
|
|
@@ -139,9 +145,14 @@ export function renderCoreSkillTemplate(name) {
|
|
|
139
145
|
`Command: ${skill.route}`,
|
|
140
146
|
`Purpose: ${skill.purpose}`,
|
|
141
147
|
`Use when: ${skill.when}`,
|
|
148
|
+
`Workflow: ${skill.workflow || 'Run the selected route lifecycle, read source evidence before mutation planning, keep changes scoped, verify with the cheapest sufficient check, and record blockers honestly.'}`,
|
|
149
|
+
`CLI entrypoint: ${skill.cli || skill.route}`,
|
|
142
150
|
`Lean policy: ${lean.policy_id}/${lean.policy_hash}`,
|
|
151
|
+
`Safety: ${skill.safety || 'Preserve user-authored content, keep route state bounded, avoid unsupported guarantees, and stop on hard blockers instead of fabricating fallback behavior.'}`,
|
|
152
|
+
`Evidence/artifacts: ${skill.evidence}`,
|
|
143
153
|
`Proof paths: ${skill.evidence}`,
|
|
144
154
|
'Safety rules: preserve user-authored skills, keep route state bounded, and stop on hard blockers instead of fabricating fallback behavior.',
|
|
155
|
+
`Failure/recovery: ${skill.fallback}`,
|
|
145
156
|
`Failure recovery: ${skill.fallback}`,
|
|
146
157
|
''
|
|
147
158
|
].join('\n');
|
package/dist/core/fsx.js
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
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
export const PACKAGE_VERSION = '4.8.
|
|
8
|
+
export const PACKAGE_VERSION = '4.8.2';
|
|
9
9
|
export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
|
|
10
10
|
export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
|
|
11
11
|
export function nowIso() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { REQUIRED_CODEX_MODEL } from '../codex-model-guard.js';
|
|
2
2
|
export const MANAGED_ASSET_SCHEMA_VERSION = 1;
|
|
3
|
-
export const MANAGED_ASSET_VERSION = '4.8.
|
|
3
|
+
export const MANAGED_ASSET_VERSION = '4.8.1';
|
|
4
4
|
export const MANAGED_ASSET_MARKER = 'SKS-MANAGED-ASSET';
|
|
5
5
|
export const MANAGED_AGENT_ROLES = Object.freeze([
|
|
6
6
|
role('sks-explorer', 'analysis-scout.toml', 'analysis_scout', 'SKS analysis scout for bounded read/write slices retained for stale Codex agent-role config repair.', 'workspace-write', ['analysis-scout', 'analysis_scout']),
|
|
@@ -25,7 +25,10 @@ export function createReleaseGateHermeticEnv(input) {
|
|
|
25
25
|
CODEX_HOME: input.gate.isolation.codex_home === 'temp' ? codexHome : process.env.CODEX_HOME,
|
|
26
26
|
XDG_CACHE_HOME: cacheHome,
|
|
27
27
|
SKS_DISABLE_REAL_MODEL_CALLS: input.gate.preset.includes('real-check') ? process.env.SKS_DISABLE_REAL_MODEL_CALLS || '0' : '1',
|
|
28
|
-
SKS_DISABLE_GLOBAL_CONFIG_MUTATION: '1'
|
|
28
|
+
SKS_DISABLE_GLOBAL_CONFIG_MUTATION: '1',
|
|
29
|
+
// Gates must never spawn a real GUI menu bar status item into the user's
|
|
30
|
+
// live session. Belt to the temp-path launch guard in installSksMenuBar.
|
|
31
|
+
SKS_SKIP_SKS_MENUBAR_LAUNCH: '1'
|
|
29
32
|
}
|
|
30
33
|
};
|
|
31
34
|
}
|
|
@@ -237,7 +237,7 @@ export async function ensureCurrentMigrationBeforeCommand(input) {
|
|
|
237
237
|
SKS_UPDATE_MIGRATION_GATE_DISABLED: '1',
|
|
238
238
|
SKS_DISABLE_UPDATE_CHECK: '1'
|
|
239
239
|
},
|
|
240
|
-
timeoutMs:
|
|
240
|
+
timeoutMs: 30_000,
|
|
241
241
|
maxOutputBytes: 32 * 1024
|
|
242
242
|
});
|
|
243
243
|
if (!doctor.ok) {
|
package/dist/core/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const PACKAGE_VERSION = '4.8.
|
|
1
|
+
export const PACKAGE_VERSION = '4.8.2';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
|
@@ -329,6 +329,9 @@ for (const script of requiredRealScripts) {
|
|
|
329
329
|
assertGate(pkg.bin?.sks === 'dist/bin/sks.js', 'package runtime must use dist/bin/sks.js');
|
|
330
330
|
assertGate(pkg.bin?.sneakoscope === 'dist/bin/sks.js', 'sneakoscope runtime must use dist/bin/sks.js');
|
|
331
331
|
assertGate(!pkg.files?.includes('src'), 'package files must not include src runtime shadows');
|
|
332
|
+
assertGate(pkg.scripts?.['publish:prep-ignore-scripts'] === 'npm run prepublishOnly', 'publish:prep-ignore-scripts must run the prepublishOnly release gate before lifecycle-disabled publish', { script: pkg.scripts?.['publish:prep-ignore-scripts'] || null });
|
|
333
|
+
assertGate(String(pkg.scripts?.['publish:ignore-scripts'] || '').includes('npm run publish:prep-ignore-scripts'), 'publish:ignore-scripts must run publish:prep-ignore-scripts before npm publish --ignore-scripts', { script: pkg.scripts?.['publish:ignore-scripts'] || null });
|
|
334
|
+
assertGate(String(pkg.scripts?.['publish:ignore-scripts'] || '').includes('--ignore-scripts'), 'publish:ignore-scripts must keep npm lifecycle scripts disabled for the final publish', { script: pkg.scripts?.['publish:ignore-scripts'] || null });
|
|
332
335
|
for (const file of requiredDocs) {
|
|
333
336
|
const absolute = path.join(root, file);
|
|
334
337
|
assertGate(fs.existsSync(absolute), `missing release doc: ${file}`);
|
|
@@ -13,7 +13,7 @@ import { root, assertGate, emitGate, importDist, readText, readJson } from './sk
|
|
|
13
13
|
assertGate(!/cargo|rustc/.test(readText('src/scripts/build-dist.ts')), 'build-dist must not invoke cargo/rustc');
|
|
14
14
|
assertGate(!/cargo|rustc/.test(readText('src/scripts/clean-dist.ts')), 'clean-dist must not invoke cargo/rustc');
|
|
15
15
|
const pkg = readJson('package.json');
|
|
16
|
-
for (const s of ['prepack', 'prepublishOnly', 'publish:dry', 'publish:ignore-scripts', 'publish:npm']) {
|
|
16
|
+
for (const s of ['prepack', 'prepublishOnly', 'publish:dry', 'publish:prep-ignore-scripts', 'publish:ignore-scripts', 'publish:npm']) {
|
|
17
17
|
assertGate(!String(pkg.scripts?.[s] || '').includes('cargo'), `publish_script_compiles_rust:${s}`);
|
|
18
18
|
}
|
|
19
19
|
// 2) The published binary is JS; no prebuilt native artifact is shipped or required.
|
|
@@ -7,6 +7,7 @@ import { assertGate, emitGate, importDist } from './sks-1-18-gate-lib.js';
|
|
|
7
7
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'sks-seo-geo-skills-'));
|
|
8
8
|
const init = await importDist('core/init.js');
|
|
9
9
|
await init.installSkills(root);
|
|
10
|
+
assertGate(!fs.existsSync(path.join(root, '.agents', 'skills', 'seo-geo', 'SKILL.md')), 'generated skills must not recreate legacy seo-geo alias');
|
|
10
11
|
for (const name of ['search-visibility-core', 'seo-geo-optimizer']) {
|
|
11
12
|
const file = path.join(root, '.agents', 'skills', name, 'SKILL.md');
|
|
12
13
|
assertGate(fs.existsSync(file), `generated skill missing: ${name}`);
|
|
@@ -18,5 +19,20 @@ for (const name of ['search-visibility-core', 'seo-geo-optimizer']) {
|
|
|
18
19
|
assertGate(/CLI entrypoint:/i.test(text), `skill missing CLI entrypoint: ${name}`, text);
|
|
19
20
|
assertGate(/ranking|citation|traffic|guarantee|보장/i.test(text), `skill must name forbidden guarantee boundary: ${name}`, text);
|
|
20
21
|
}
|
|
22
|
+
const optimizer = fs.readFileSync(path.join(root, '.agents', 'skills', 'seo-geo-optimizer', 'SKILL.md'), 'utf8');
|
|
23
|
+
for (const token of [
|
|
24
|
+
'SEO intent map',
|
|
25
|
+
'canonical URL map',
|
|
26
|
+
'JSON-LD summary',
|
|
27
|
+
'sitemap coverage summary',
|
|
28
|
+
'internal link plan',
|
|
29
|
+
'unsupported claims ledger',
|
|
30
|
+
'llms.txt',
|
|
31
|
+
'Search Console',
|
|
32
|
+
'STOREFRONT_PATH',
|
|
33
|
+
'buildSeoMetadata'
|
|
34
|
+
]) {
|
|
35
|
+
assertGate(optimizer.includes(token), `seo-geo-optimizer skill missing CELIMAX architecture token: ${token}`, optimizer);
|
|
36
|
+
}
|
|
21
37
|
emitGate('seo-geo:skill-rich-content', { skills_checked: 2 });
|
|
22
38
|
//# sourceMappingURL=seo-geo-skill-rich-content-check.js.map
|
|
@@ -3,21 +3,37 @@ import os from 'node:os';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { installSksMenuBar } from '../core/codex-app/sks-menubar.js';
|
|
5
5
|
const temp = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-menubar-check-'));
|
|
6
|
+
const envHomeTemp = await fs.mkdtemp(path.join(os.tmpdir(), 'sks-menubar-env-home-check-'));
|
|
7
|
+
const launchGuardEnv = { ...process.env, SKS_SKIP_SKS_MENUBAR_LAUNCH: '0' };
|
|
6
8
|
const result = await installSksMenuBar({
|
|
7
9
|
apply: true,
|
|
8
|
-
launch:
|
|
10
|
+
launch: true,
|
|
9
11
|
home: temp,
|
|
10
12
|
root: temp,
|
|
11
|
-
sksEntry: path.join(process.cwd(), 'dist', 'bin', 'sks.js')
|
|
13
|
+
sksEntry: path.join(process.cwd(), 'dist', 'bin', 'sks.js'),
|
|
14
|
+
env: launchGuardEnv
|
|
15
|
+
});
|
|
16
|
+
const envHomeResult = await installSksMenuBar({
|
|
17
|
+
apply: true,
|
|
18
|
+
launch: true,
|
|
19
|
+
root: envHomeTemp,
|
|
20
|
+
sksEntry: path.join(process.cwd(), 'dist', 'bin', 'sks.js'),
|
|
21
|
+
env: { ...launchGuardEnv, HOME: envHomeTemp }
|
|
12
22
|
});
|
|
13
23
|
const executableExists = result.executable_path ? await exists(result.executable_path) : false;
|
|
14
24
|
const launchAgentExists = result.launch_agent_path ? await exists(result.launch_agent_path) : false;
|
|
15
25
|
const actionScriptExists = result.action_script_path ? await exists(result.action_script_path) : false;
|
|
16
26
|
const generatedSourcePath = result.app_path ? path.join(path.dirname(result.app_path), 'SKSMenuBar.swift') : null;
|
|
17
27
|
const generatedSource = generatedSourcePath ? await fs.readFile(generatedSourcePath, 'utf8').catch(() => '') : '';
|
|
18
|
-
const
|
|
19
|
-
&& generatedSource.includes('
|
|
20
|
-
&& generatedSource.includes('
|
|
28
|
+
const hasVisibleLabelSource = generatedSource.includes('NSStatusItem.variableLength')
|
|
29
|
+
&& generatedSource.includes('button.title = "SKS"')
|
|
30
|
+
&& generatedSource.includes('button.attributedTitle = NSAttributedString(string: "SKS"')
|
|
31
|
+
&& generatedSource.includes('NSColor.labelColor')
|
|
32
|
+
&& generatedSource.includes('button.image = nil');
|
|
33
|
+
// The status item must persist its user-arranged position across restarts so a
|
|
34
|
+
// once-dragged item does not jump back behind the notch on the next doctor --fix.
|
|
35
|
+
const hasAutosaveNameSource = generatedSource.includes('statusItem.autosaveName = "com.sneakoscope.sks-menubar"');
|
|
36
|
+
const hasExplicitVisibleSource = generatedSource.includes('statusItem.isVisible = true');
|
|
21
37
|
const expectedMenuItems = [
|
|
22
38
|
'Use codex-lb',
|
|
23
39
|
'Use ChatGPT OAuth',
|
|
@@ -28,25 +44,56 @@ const expectedMenuItems = [
|
|
|
28
44
|
];
|
|
29
45
|
const missingExpectedItems = expectedMenuItems.filter((item) => !result.menu_items.includes(item));
|
|
30
46
|
const hasExpectedItems = missingExpectedItems.length === 0;
|
|
47
|
+
const launchSkippedForTempHome = result.launch?.requested === false
|
|
48
|
+
&& result.launch?.method === 'skipped'
|
|
49
|
+
&& result.warnings.includes('launch_skipped_non_user_home');
|
|
50
|
+
const launchSkippedForEnvHome = envHomeResult.launch?.requested === false
|
|
51
|
+
&& envHomeResult.launch?.method === 'skipped'
|
|
52
|
+
&& envHomeResult.warnings.includes('launch_skipped_non_user_home');
|
|
53
|
+
// Temp-path guard: a temp-rooted install must never request a launch, even if
|
|
54
|
+
// some caller forgets SKS_SKIP_SKS_MENUBAR_LAUNCH. Both fixtures live under
|
|
55
|
+
// os.tmpdir(), so both results must carry the temp-install skip warning.
|
|
56
|
+
const launchSkippedForTempInstall = result.launch?.requested === false
|
|
57
|
+
&& result.warnings.includes('launch_skipped_temp_install')
|
|
58
|
+
&& envHomeResult.launch?.requested === false
|
|
59
|
+
&& envHomeResult.warnings.includes('launch_skipped_temp_install');
|
|
60
|
+
const preferredPositionSkippedForTempInstall = !result.actions.includes('seeded SKS menu bar preferred position')
|
|
61
|
+
&& !envHomeResult.actions.includes('seeded SKS menu bar preferred position');
|
|
31
62
|
const ok = process.platform === 'darwin'
|
|
32
63
|
? result.ok === true
|
|
33
64
|
&& result.status === 'installed_launch_skipped'
|
|
65
|
+
&& envHomeResult.ok === true
|
|
66
|
+
&& envHomeResult.status === 'installed_launch_skipped'
|
|
34
67
|
&& executableExists
|
|
35
68
|
&& launchAgentExists
|
|
36
69
|
&& actionScriptExists
|
|
37
70
|
&& hasExpectedItems
|
|
38
|
-
&&
|
|
71
|
+
&& hasVisibleLabelSource
|
|
72
|
+
&& hasAutosaveNameSource
|
|
73
|
+
&& hasExplicitVisibleSource
|
|
74
|
+
&& launchSkippedForTempHome
|
|
75
|
+
&& launchSkippedForEnvHome
|
|
76
|
+
&& launchSkippedForTempInstall
|
|
77
|
+
&& preferredPositionSkippedForTempInstall
|
|
39
78
|
: result.ok === true && result.status === 'unsupported_platform';
|
|
40
79
|
const report = {
|
|
41
80
|
schema: 'sks.sks-menubar-install-check.v1',
|
|
42
81
|
ok,
|
|
43
82
|
temp,
|
|
83
|
+
env_home_temp: envHomeTemp,
|
|
44
84
|
result,
|
|
85
|
+
env_home_result: envHomeResult,
|
|
45
86
|
executable_exists: executableExists,
|
|
46
87
|
launch_agent_exists: launchAgentExists,
|
|
47
88
|
action_script_exists: actionScriptExists,
|
|
48
89
|
generated_source_path: generatedSourcePath,
|
|
49
|
-
|
|
90
|
+
has_visible_label_source: hasVisibleLabelSource,
|
|
91
|
+
has_autosave_name_source: hasAutosaveNameSource,
|
|
92
|
+
has_explicit_visible_source: hasExplicitVisibleSource,
|
|
93
|
+
launch_skipped_for_temp_home: launchSkippedForTempHome,
|
|
94
|
+
launch_skipped_for_env_home: launchSkippedForEnvHome,
|
|
95
|
+
launch_skipped_for_temp_install: launchSkippedForTempInstall,
|
|
96
|
+
preferred_position_skipped_for_temp_install: preferredPositionSkippedForTempInstall,
|
|
50
97
|
expected_menu_items: expectedMenuItems,
|
|
51
98
|
missing_expected_items: missingExpectedItems,
|
|
52
99
|
has_expected_items: hasExpectedItems,
|
|
@@ -7,6 +7,7 @@ assertGate(router.includes('ensureCurrentMigrationBeforeCommand'), 'router must
|
|
|
7
7
|
assertGate(helper.includes('INSTALLATION_EPOCH_SCHEMA') && helper.includes('installationEpochPath'), 'migration helper must keep a persistent installation epoch');
|
|
8
8
|
assertGate(helper.includes('projectUpdateMigrationReceiptPath'), 'migration helper must keep a project receipt');
|
|
9
9
|
assertGate(helper.includes("'--profile', 'migration'") && helper.includes("'--machine-only'") && helper.includes("'--report-file'"), 'first normal command must repair through package-local migration Doctor machine report before continuing');
|
|
10
|
+
assertGate(helper.includes('timeoutMs: 30_000'), 'first normal command migration Doctor timeout must allow slow but successful macOS repair profiles');
|
|
10
11
|
assertGate(helper.includes('isProjectReceiptCurrentForEpoch'), 'project receipts must be compared against the current installation epoch');
|
|
11
12
|
assertGate(helper.includes('runUpdateRetentionCleanup') && helper.includes('retention_cleanup'), 'project update migration receipt must run mission retention cleanup and record its receipt');
|
|
12
13
|
assertGate(helper.includes('clearPendingUpdateMigration') && helper.includes('one project must not consume global migration state'), 'legacy clear helper must preserve the persistent epoch contract');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "4.8.
|
|
4
|
+
"version": "4.8.2",
|
|
5
5
|
"description": "Sneakoscope Codex: fast proof-first Codex trust layer with image-based Voxel TriWiki.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
|
@@ -381,7 +381,8 @@
|
|
|
381
381
|
"release:real-check": "node ./dist/scripts/release-real-check.js",
|
|
382
382
|
"release:publish": "npm run publish:ignore-scripts",
|
|
383
383
|
"publish:dry": "npm run release:metadata && npm run release:version-truth && npm run publish:packlist-performance && npm run release:check:full && node ./dist/scripts/check-publish-tag.js && node ./dist/scripts/release-check-stamp.js verify && npm run release:provenance -- --publish && npm run release:dist-freshness && node ./dist/scripts/release-registry-check.js --require-unpublished && npm --cache /tmp/sks-npm-cache publish --dry-run --ignore-scripts --registry https://registry.npmjs.org/ --access public",
|
|
384
|
-
"publish:ignore-scripts": "npm run prepublishOnly
|
|
384
|
+
"publish:prep-ignore-scripts": "npm run prepublishOnly",
|
|
385
|
+
"publish:ignore-scripts": "npm run publish:prep-ignore-scripts && npm --cache /tmp/sks-npm-cache publish --ignore-scripts --registry https://registry.npmjs.org/ --access public",
|
|
385
386
|
"publish:npm": "npm run publish:ignore-scripts",
|
|
386
387
|
"publish:fast": "node -e \"console.error('publish:fast is quarantined; use publish:dry and publish:npm with the release gates.'); process.exit(1)\"",
|
|
387
388
|
"prepack": "npm run build",
|