@djordje-stojanovic/sigmaskills 0.2.0

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 (53) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/LICENSE +21 -0
  3. package/README.md +342 -0
  4. package/bin/sigmaskills.js +7 -0
  5. package/manifest.json +28 -0
  6. package/package.json +42 -0
  7. package/registry/agent-hosts.json +2404 -0
  8. package/registry/schema.json +110 -0
  9. package/registry/skill-baselines.json +4 -0
  10. package/registry/source.json +6 -0
  11. package/sigmabrief/SKILL.md +56 -0
  12. package/sigmabrief/agents/openai.yaml +12 -0
  13. package/sigmabrief/references/brief-method.md +73 -0
  14. package/sigmabrief/references/prompt-contract.md +174 -0
  15. package/sigmaperformance/SKILL.md +118 -0
  16. package/sigmaperformance/agents/openai.yaml +12 -0
  17. package/sigmaperformance/references/audit-method.md +112 -0
  18. package/sigmaperformance/references/calibration.md +45 -0
  19. package/sigmaperformance/references/report-contract.md +103 -0
  20. package/sigmareview/SKILL.md +133 -0
  21. package/sigmareview/agents/openai.yaml +12 -0
  22. package/sigmareview/references/report-contract.md +217 -0
  23. package/sigmareview/references/review-method.md +233 -0
  24. package/sigmawrite/SKILL.md +45 -0
  25. package/sigmawrite/agents/openai.yaml +12 -0
  26. package/src/adoption.js +370 -0
  27. package/src/backup.js +398 -0
  28. package/src/catalog.js +211 -0
  29. package/src/cli.js +657 -0
  30. package/src/customization.js +344 -0
  31. package/src/destinations.js +491 -0
  32. package/src/interactive.js +959 -0
  33. package/src/links.js +157 -0
  34. package/src/plan.js +429 -0
  35. package/src/prepack.js +10 -0
  36. package/src/project-lock.js +169 -0
  37. package/src/purge.js +477 -0
  38. package/src/registry/automation-ci.js +411 -0
  39. package/src/registry/automation.js +554 -0
  40. package/src/registry/diff.js +149 -0
  41. package/src/registry/normalize.js +67 -0
  42. package/src/registry/parse.js +184 -0
  43. package/src/registry/sync.js +230 -0
  44. package/src/registry/validate.js +223 -0
  45. package/src/release-ci.js +12 -0
  46. package/src/release.js +837 -0
  47. package/src/restore.js +518 -0
  48. package/src/revision.js +82 -0
  49. package/src/state.js +480 -0
  50. package/src/status.js +469 -0
  51. package/src/transaction.js +636 -0
  52. package/src/uninstall.js +647 -0
  53. package/src/update.js +815 -0
@@ -0,0 +1,233 @@
1
+ # SigmaReview ten-pass method
2
+
3
+ Use this as an investigation protocol, not a generic checklist. Start from repository evidence, follow behavior across files and boundaries, and deepen the passes that carry the most risk. Complete every pass with either evidence-backed findings or a repository-specific `N/A`/clean result for the coverage ledger.
4
+
5
+ ## Contents
6
+
7
+ 1. Preflight: establish the review frame
8
+ 2. System map, intent, and coverage
9
+ 3. Functional correctness and feature completeness
10
+ 4. Architecture, modularity, and maintainability
11
+ 5. Data, state, concurrency, and distributed behavior
12
+ 6. Reliability, failure handling, and safety
13
+ 7. Security, privacy, secrets, and abuse resistance
14
+ 8. Performance, efficiency, and scale
15
+ 9. Tests, verification, and specification quality
16
+ 10. Dependencies, build, supply chain, CI/CD, and operations
17
+ 11. Domain overlay, SOTA gap, and cross-pass coherence
18
+
19
+ ## Preflight: establish the review frame
20
+
21
+ Before Pass 1:
22
+
23
+ 1. Resolve the canonical repository, default branch, reviewed commit SHA, license, primary languages, repository size, and whether the worktree is clean.
24
+ 2. Inventory all tracked paths by role: first-party source, tests, configuration, schemas/migrations, CI/CD, infrastructure/deployment, documentation, generated, vendored, lockfiles, fixtures, binaries, and assets.
25
+ 3. Read root and path-specific engineering instructions, README files, architecture decision records, domain glossaries, contribution rules, changelogs, release metadata, and package manifests.
26
+ 4. Identify declared product purpose, supported platforms, public interfaces, deployment modes, data sensitivity, compatibility promises, and the repository's own validation commands without running them.
27
+ 5. Record exclusions and why semantic review would be invalid or wasteful; exclusions remain visible in the final coverage ledger.
28
+
29
+ Do not accept the README as ground truth. Treat documentation, code, tests, schemas, configuration, and released behavior as evidence that may contradict each other.
30
+
31
+ ## Pass 1 — System map, intent, and coverage
32
+
33
+ Build the smallest accurate model that explains the repository:
34
+
35
+ - runtime entry points, packages, processes, services, libraries, jobs, CLIs, UI surfaces, hardware blocks, and generated outputs;
36
+ - public APIs, commands, events, file formats, protocols, and compatibility surfaces;
37
+ - state stores, schemas, caches, queues, external services, devices, and third-party integrations;
38
+ - trust boundaries, privilege changes, network boundaries, user-controlled inputs, and sensitive assets;
39
+ - build, test, release, deployment, migration, rollback, and recovery paths;
40
+ - primary user journeys and the code path responsible for each.
41
+
42
+ Compare declared structure with actual dependencies. Flag only concrete mismatches: unreachable packages, undocumented production entry points, dead deployment paths, two sources of truth, or architecture documents that would mislead a maintainer.
43
+
44
+ Pass output in reasoning: component map, boundary map, user-flow map, and file-accounting baseline.
45
+
46
+ ## Pass 2 — Functional correctness and feature completeness
47
+
48
+ Trace behavior from inputs to externally visible results. Focus on what the product actually promises.
49
+
50
+ Inspect:
51
+
52
+ - conditional logic, precedence, default branches, off-by-one boundaries, sign/unit/timezone/locale mistakes, overflow/underflow, rounding, precision, parsing, and serialization;
53
+ - null, empty, duplicate, malformed, stale, partial, out-of-order, replayed, and extreme inputs;
54
+ - state machines: legal transitions, terminal states, cancellation, retries, resumption, idempotency, and impossible states;
55
+ - API/client, producer/consumer, schema/model, UI/backend, CLI/library, firmware/hardware, and config/runtime contract agreement;
56
+ - error-to-result conversion, partial success, optimistic state, stale closures, cached state, pagination, filtering, ordering, and boundary semantics;
57
+ - incomplete branches, TODO/FIXME markers, feature flags, stubs, placeholder implementations, disabled checks, and code made unreachable by configuration;
58
+ - documented features and acceptance behavior against implementation and tests.
59
+
60
+ Use tests as intent evidence, then test the tests mentally against consumer expectations. A test that asserts current implementation behavior may merely preserve the bug.
61
+
62
+ ## Pass 3 — Architecture, modularity, and maintainability
63
+
64
+ Evaluate design by change cost and correctness, not aesthetic preference.
65
+
66
+ Inspect:
67
+
68
+ - dependency direction, cycles, package boundaries, ownership, locality, and whether responsibilities live with the data/invariant they protect;
69
+ - interfaces that expose implementation detail, shallow wrappers, god modules, shotgun surgery, duplicated policy, parallel hierarchies, and feature logic scattered across layers;
70
+ - abstractions with one hypothetical caller, configuration that behaves like code, overly generic extension points, and indirection that obscures the primary path;
71
+ - deep-module opportunities: a small stable interface hiding substantial complexity, with a clear test seam;
72
+ - inconsistent domain concepts, names, units, error types, lifecycle rules, and sources of truth;
73
+ - public compatibility, versioning, deprecation, migration, and extension strategy;
74
+ - code generation boundaries and whether generated and hand-written code can drift.
75
+
76
+ Apply three tests before reporting a redesign:
77
+
78
+ 1. **Deletion test:** what machinery disappears if the proposed boundary exists?
79
+ 2. **Change-amplification test:** show a realistic change that currently touches too many locations.
80
+ 3. **Seam test:** identify the contract and how it improves isolated verification or replacement.
81
+
82
+ Offer a migration sequence, not a greenfield fantasy. Preserve sound local conventions.
83
+
84
+ ## Pass 4 — Data, state, concurrency, and distributed behavior
85
+
86
+ Inspect every stateful boundary:
87
+
88
+ - schema constraints versus application assumptions; uniqueness, nullability, foreign keys, enum evolution, encoding, precision, and default drift;
89
+ - migrations for ordering, backfill, locking, compatibility windows, rollback, partial failure, restartability, and mixed-version deployments;
90
+ - transaction boundaries, atomicity, lost updates, check-then-act races, double processing, duplicate delivery, and isolation assumptions;
91
+ - locks, atomics, channels, async tasks, cancellation, shared mutable state, reentrancy, memory visibility, deadlocks, livelocks, starvation, and ordering;
92
+ - cache keys, invalidation, TTL, stampedes, stale reads, negative caching, and authorization-sensitive caching;
93
+ - queues and events: at-least/at-most/exactly-once claims, idempotency keys, poison messages, retry storms, ordering, backpressure, and dead-letter handling;
94
+ - clocks, monotonic versus wall time, timezone/DST, expiration, skew, and cross-system timestamp formats;
95
+ - replicas, partitions, leader changes, split brain, reconciliation, eventual consistency, and conflict resolution where applicable.
96
+
97
+ Trace invariants across persistence, retries, and process restarts. Report a race only with a concrete interleaving.
98
+
99
+ ## Pass 5 — Reliability, failure handling, and safety
100
+
101
+ Assume dependencies fail, resources exhaust, processes restart, and inputs arrive at the worst time.
102
+
103
+ Inspect:
104
+
105
+ - exception and error propagation, swallowed failures, catch-all handling, fallback correctness, and cleanup on every exit path;
106
+ - file, socket, transaction, process, thread, handle, GPU/device, and memory lifecycle;
107
+ - timeouts, retries, jitter, retry budgets, circuit breaking, bulkheads, backpressure, rate limiting, and cascading failure;
108
+ - startup, shutdown, cancellation, crash recovery, checkpointing, resume, rollback, safe defaults, and known-safe states;
109
+ - configuration absence, invalid values, partial rollout, version skew, region failure, and dependency degradation;
110
+ - health/readiness checks that prove meaningful service capability instead of process existence;
111
+ - observability of user-visible failures: structured logs, metrics, traces, audit trails, correlation identifiers, actionable messages, and sensitive-data hygiene;
112
+ - safety-critical paths: hazard contribution, fail-safe state, prerequisite checks, command ordering, integrity checks, single-event hazards, and recovery time constraints.
113
+
114
+ Distinguish resilient degradation from silent corruption. Prefer explicit failure over plausible-looking wrong output.
115
+
116
+ ## Pass 6 — Security, privacy, secrets, and abuse resistance
117
+
118
+ Perform defensive source review grounded in the repository's actual exposure.
119
+
120
+ Model assets, entry points, trust boundaries, attacker capabilities, and abuse paths. Inspect:
121
+
122
+ - authentication, session/token lifecycle, identity binding, recovery, impersonation, and account enumeration;
123
+ - authorization at every object, action, tenant, admin, background-job, and indirect-access boundary;
124
+ - injection into SQL/NoSQL, shells, templates, HTML, headers, logs, paths, URLs, deserializers, interpreters, prompts, and configuration;
125
+ - SSRF, request smuggling, traversal, unsafe upload/extraction, insecure redirects, CORS/CSRF, XSS, prototype pollution, and mass assignment where applicable;
126
+ - cryptographic purpose, algorithm/mode, key generation/storage/rotation, nonce/IV uniqueness, verification order, downgrade handling, and constant-time requirements;
127
+ - secrets in current files and relevant history, insecure examples/defaults, overbroad permissions, and secret exposure through logs, errors, artifacts, or client bundles;
128
+ - tenant isolation, data minimization, retention/deletion, consent, export, telemetry, backups, logs, and privacy boundary violations;
129
+ - denial-of-service through unbounded inputs, expensive parsing, algorithmic complexity, fan-out, resource allocation, regex, compression, or retry amplification;
130
+ - CI/CD permissions, untrusted pull-request execution, dependency confusion, artifact provenance, release signing, and build-secret exposure;
131
+ - AI systems: prompt injection boundaries, tool authorization, retrieval poisoning, data exfiltration, output validation, model/provider failure, and cost abuse.
132
+
133
+ For a vulnerability, state the preconditions and data/control flow. Never include a live secret or unnecessarily weaponized exploit. Recommend revocation/rotation whenever a real credential may have been committed; deleting it from the current tree is insufficient.
134
+
135
+ Use OWASP ASVS/API/LLM guidance, CWE, NIST SSDF, CERT, or language-specific secure-coding standards only when applicable. Repository evidence outranks checklist matching.
136
+
137
+ ## Pass 7 — Performance, efficiency, and scale
138
+
139
+ Derive likely bottlenecks from workload and code paths; do not perform benchmark theatre.
140
+
141
+ Inspect:
142
+
143
+ - asymptotic complexity, nested scans, repeated parsing/serialization, redundant work, hot-path allocation, copies, boxing, reflection, and synchronization;
144
+ - database query counts, N+1 patterns, missing/bad indexes, unbounded result sets, pagination, connection/transaction duration, and pool exhaustion;
145
+ - network round trips, fan-out, payload size, compression, batching, streaming, head-of-line blocking, and backpressure;
146
+ - memory retention, unbounded maps/queues/caches, leaks, fragmentation, large-object churn, and load-proportional buffering;
147
+ - CPU/GPU/device utilization, vectorization, parallelism overhead, kernel launch/data transfer, I/O scheduling, and hardware locality when relevant;
148
+ - frontend bundle/runtime cost, rendering waterfalls, unnecessary hydration, layout thrash, image/font delivery, and interaction latency;
149
+ - cold starts, startup work, build time, CI time, artifact size, and deployment scaling limits;
150
+ - caching correctness before caching opportunity; an invalid fast answer is still wrong.
151
+
152
+ Quantify the mechanism where repository data permits. Label estimates as estimates and show assumptions. Recommend measurement before optimization when impact cannot be established from source.
153
+
154
+ ## Pass 8 — Tests, verification, and specification quality
155
+
156
+ Evaluate whether the verification system would detect the failures found in Passes 2–7.
157
+
158
+ Inspect:
159
+
160
+ - test pyramid/shape relative to architecture; unit, integration, contract, end-to-end, property, fuzz, mutation, load, chaos, hardware simulation, and formal verification as applicable;
161
+ - assertions that prove outcomes versus mocks that prove calls; consumer contracts versus producer snapshots;
162
+ - negative, boundary, concurrency, failure, recovery, migration, compatibility, authorization, and abuse cases;
163
+ - determinism, isolation, time/randomness control, flaky retries, order dependence, shared fixtures, cleanup, and hermeticity;
164
+ - test data realism and whether fixtures conceal encoding, scale, permission, or schema problems;
165
+ - coverage configuration, excluded critical paths, generated code, dead tests, skipped tests, expected failures, and CI paths that do not run what maintainers think;
166
+ - type checking, linting, static analysis, sanitizers, model checking, assertions, and compiler warnings;
167
+ - traceability from requirements/invariants to tests and from defects to regression tests.
168
+
169
+ For every accepted P0–P2 finding, specify the smallest regression test that would fail before the fix and pass afterward. Do not demand 100% line coverage; demand evidence at consequential behavior boundaries.
170
+
171
+ ## Pass 9 — Dependencies, build, supply chain, CI/CD, and operations
172
+
173
+ Inspect the path from source to a running, supportable release:
174
+
175
+ - direct and transitive dependency purpose, duplication, abandonment, license constraints, advisories, update policy, pinning, lockfile consistency, and runtime compatibility;
176
+ - supported language/framework/platform versions, end-of-life components, deprecated APIs, and migration blockers;
177
+ - build determinism, generated artifacts, environment leakage, timestamps, network dependence, architecture/platform assumptions, and reproducibility;
178
+ - CI triggers, branch/tag filters, permissions, cache poisoning, artifact handoff, matrix gaps, skipped gates, race conditions, and protected-environment expectations;
179
+ - release versioning, changelog, migrations, signing, provenance/SBOM, rollback, canary/blue-green strategy, and backward compatibility;
180
+ - infrastructure defaults, network exposure, identity/permissions, encryption, backups, restore testing, regional assumptions, capacity, and cost traps;
181
+ - runtime configuration validation, secret injection, feature flags, observability, alertability, runbooks, SLOs, and on-call diagnosability;
182
+ - developer experience only where it affects correctness or delivery: bootstrap reproducibility, command drift, hidden prerequisites, and misleading documentation.
183
+
184
+ Verify version/update claims from official registries, maintainers, advisories, or platform documentation. Group routine compatible patch updates; give individual treatment to security, end-of-life, breaking, or architecturally meaningful upgrades.
185
+
186
+ ## Pass 10 — Domain overlay, SOTA gap, and cross-pass coherence
187
+
188
+ Select every applicable overlay; do not force irrelevant disciplines.
189
+
190
+ ### Web and frontend
191
+
192
+ Inspect accessibility, semantic structure, keyboard/focus behavior, responsive states, loading/empty/error/success states, form semantics, navigation, internationalization, color/contrast, reduced motion, browser support, hydration, and whether the visual system is coherent rather than generically decorative.
193
+
194
+ ### API, service, and library
195
+
196
+ Inspect contract clarity, versioning, idempotency, pagination, errors, compatibility, rate limits, discoverability, composability, unsafe defaults, resource ownership, cancellation, and behavior under partial failure.
197
+
198
+ ### CLI and developer tool
199
+
200
+ Inspect exit codes, stdout/stderr separation, non-interactive behavior, shell quoting, signals, config precedence, dry-run semantics, destructive confirmations, stable machine-readable output, cross-platform behavior, and recoverability.
201
+
202
+ ### Mobile and desktop
203
+
204
+ Inspect lifecycle transitions, offline behavior, persistence, permissions, background execution, updates, deep links, platform conventions, accessibility, resource pressure, and crash recovery.
205
+
206
+ ### Infrastructure and systems
207
+
208
+ Inspect privilege, isolation, kernel/OS assumptions, signals, process supervision, file-descriptor and memory limits, filesystem semantics, network partitions, immutable deployment, drift, and rollback.
209
+
210
+ ### AI/ML and data science
211
+
212
+ Inspect dataset lineage, leakage, evaluation validity, reproducibility, seed/control, metric gaming, baseline comparison, drift, prompt/model versioning, structured-output validation, token/context limits, cost/latency budgets, fallbacks, and human oversight.
213
+
214
+ ### Hardware, RTL, firmware, and drivers
215
+
216
+ Inspect specification traceability; reset and initialization; clock/reset-domain crossings; metastability; combinational loops and inferred latches; width, signedness, overflow, X/unknown propagation; state-machine completeness and illegal-state recovery; timing and resource constraints; interrupts, DMA, memory ordering, register maps, and hardware/software contract agreement; concurrency and reentrancy in firmware; power, thermal, watchdog, and safe-state behavior; synthesizability and portability; lint/CDC/RDC evidence; simulation assertions, functional/code coverage, constrained-random tests, formal properties, equivalence, and sign-off criteria. Separate simulation-correct from synthesis-correct and software-correct from silicon-correct.
217
+
218
+ ### Cryptographic or high-assurance code
219
+
220
+ Inspect protocol composition, misuse resistance, side channels, constant-time behavior, fault handling, key lifecycle, test vectors, formal claims, unsafe optimization, and dependence on undocumented behavior. Prefer established constructions over novel cryptography.
221
+
222
+ ### Safety-critical or regulated systems
223
+
224
+ Inspect requirement-to-code-to-test traceability, hazards, failure modes, independence, safe states, deterministic timing, diagnostic coverage, auditability, change control, and evidence required by the repository's actual regulatory domain. Do not claim certification from source inspection.
225
+
226
+ Finally perform a cross-pass coherence review:
227
+
228
+ 1. Merge duplicate symptoms under common root causes.
229
+ 2. Detect remediation conflicts and order prerequisite fixes.
230
+ 3. Compare architecture claims with deployment reality, tests with contracts, schemas with models, docs with code, and security controls with operational configuration.
231
+ 4. Identify the smallest set of changes that removes the greatest combined risk.
232
+ 5. Separate **defects**, **engineering upgrades**, and **optional opportunities**. Never inflate optional modernization into a bug.
233
+ 6. Re-run the evidence gate mentally against every retained finding and discard anything that does not survive.
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: sigmawrite
3
+ description: Write and explain in clear, STE-inspired Simplified Technical English — high quality, readable, still technical. Use when the user wants less jargon or gibberish, ASD-STE100 / simplified technical English, or outsider-clear explanations. Do not use to rewrite code identifiers, paths, or APIs, or to override another skill’s rigid output contract.
4
+ ---
5
+
6
+ # SigmaWrite
7
+
8
+ Write user-facing English in the spirit of ASD-STE100 Simplified Technical English: clear shared meaning, living voice, still precise. Not certified STE; no controlled dictionary.
9
+
10
+ While active (until turned off), use this voice for explanations, summaries, plans, and answers. Leave code, paths, APIs, identifiers, and other skills’ required formats alone.
11
+
12
+ ## North star
13
+
14
+ Write amazing high-quality technical English that never gets too long. A sharp person from a completely unrelated field — even without your jargon — should still grasp your meaning. Stay technical when the topic is. Be fun to read without baby-talk or buzzword cosplay.
15
+
16
+ Prefer clear who-does-what over foggy abstractions. Prefer one clean idea per sentence when something is hard. Prefer stable names for the same concept; don’t synonym-hop for style. Prefer simple words; when a real technical term is needed, keep it and make its meaning obvious once. Prefer light noun stacks over packed noun piles. Prefer simple time (now / then / next). Prefer enough detail to act or understand; cut empty filler. Prefer steps a human can follow without sounding like a robot manual.
17
+
18
+ ## Never
19
+
20
+ - Invent nonsense words or fake-technical coinages.
21
+ - Hide meaning in abstract gibberish.
22
+ - Talk down to the reader.
23
+ - Rename real code, paths, APIs, or identifiers to “sound simpler.”
24
+
25
+ ## Before / after
26
+
27
+ **Before:** We refactored the orchestration layer to idempotently hydrate the ephemeral projection surface across seven modules.
28
+
29
+ **After:** I changed how temporary data loads across seven files. The load can run twice without creating duplicate records. Here is what each file does and why.
30
+
31
+ ## Paste as system prompt
32
+
33
+ ```text
34
+ From now on, write in clear Simplified Technical English inspired by ASD-STE100.
35
+ Use high-quality sentences that never get too long. A sharp outsider from another
36
+ field should still understand you. Stay technical when needed; never dumb it down;
37
+ never invent words or hide meaning in jargon soup. Prefer who-does-what, stable
38
+ terms, simple time, and enough detail to act. Do not rename real code or paths.
39
+ ```
40
+
41
+ ## Personal instructions
42
+
43
+ <sigmaskills-custom>
44
+ </sigmaskills-custom>
45
+
@@ -0,0 +1,12 @@
1
+ interface:
2
+ display_name: SigmaWrite
3
+ short_description: Clear STE-inspired technical English for explanations
4
+ default_prompt: Use $sigmawrite so explanations and summaries stay clear,
5
+ technical, and free of jargon gibberish.
6
+ policy:
7
+ products:
8
+ - chatgpt
9
+ - codex
10
+ - api
11
+ - atlas
12
+ allow_implicit_invocation: true
@@ -0,0 +1,370 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parseSkillFrontmatter, findPackageRoot } from './catalog.js';
4
+ import { inspectCustomizationBlock } from './customization.js';
5
+ import { UNIVERSAL_PROJECT_DESTINATION } from './destinations.js';
6
+ import { inspectManagedPath, pathExists, recommendedLinkMethod } from './links.js';
7
+ import { computeSkillRevisionAndHashes } from './revision.js';
8
+
9
+ export const RECOGNITION_PRECEDENCE = ['sigma-state', 'exact-revision', 'recognized-link'];
10
+ export const MIGRATABLE_KINDS = ['legacy', 'changed', 'unverified', 'malformed-custom'];
11
+ export const BASELINES_FILENAME = 'skill-baselines.json';
12
+
13
+ function hashRealDirectory(dirPath) {
14
+ if (!pathExists(dirPath)) return null;
15
+ const stat = fs.lstatSync(dirPath);
16
+ if (stat.isSymbolicLink() || !stat.isDirectory()) return null;
17
+ return computeSkillRevisionAndHashes(dirPath);
18
+ }
19
+
20
+ /**
21
+ * Compare live file hashes with an upstream or baseline map.
22
+ *
23
+ * @param {Record<string, string>} liveFiles
24
+ * @param {Record<string, string>} upstreamFiles
25
+ * @returns {{ added: string[], replaced: string[], deleted: string[] }}
26
+ */
27
+ export function diffSkillFiles(liveFiles = {}, upstreamFiles = {}) {
28
+ const added = [];
29
+ const replaced = [];
30
+ const deleted = [];
31
+
32
+ for (const file of Object.keys(liveFiles).sort()) {
33
+ if (!(file in upstreamFiles)) added.push(file);
34
+ else if (liveFiles[file] !== upstreamFiles[file]) replaced.push(file);
35
+ }
36
+ for (const file of Object.keys(upstreamFiles).sort()) {
37
+ if (!(file in liveFiles)) deleted.push(file);
38
+ }
39
+
40
+ return { added, replaced, deleted };
41
+ }
42
+
43
+ /**
44
+ * Load explicit historical Skill Revision baselines bundled with the package.
45
+ *
46
+ * @param {string} [packageRoot]
47
+ * @returns {Record<string, { revision: string, files?: Record<string, string> }[]>}
48
+ */
49
+ export function loadSkillBaselines(packageRoot = findPackageRoot()) {
50
+ const baselinePath = path.join(packageRoot, 'registry', BASELINES_FILENAME);
51
+ if (!pathExists(baselinePath)) return {};
52
+ const parsed = JSON.parse(fs.readFileSync(baselinePath, 'utf8'));
53
+ if (!parsed || typeof parsed !== 'object' || !parsed.skills || typeof parsed.skills !== 'object') {
54
+ throw new Error(`invalid skill baselines at ${baselinePath}`);
55
+ }
56
+ return parsed.skills;
57
+ }
58
+
59
+ function inspectLiveSkill(destPath, skillId) {
60
+ const skillMdPath = path.join(destPath, 'SKILL.md');
61
+ if (!pathExists(skillMdPath)) {
62
+ return { sigmaLooking: false, customization: { status: 'absent' } };
63
+ }
64
+
65
+ let markdown;
66
+ try {
67
+ markdown = fs.readFileSync(skillMdPath, 'utf8');
68
+ } catch {
69
+ return { sigmaLooking: false, customization: { status: 'absent' } };
70
+ }
71
+
72
+ let name = null;
73
+ try {
74
+ name = parseSkillFrontmatter(markdown).name;
75
+ } catch {
76
+ name = null;
77
+ }
78
+
79
+ return {
80
+ sigmaLooking: Boolean(skillId && name === skillId),
81
+ customization: inspectCustomizationBlock(markdown, skillId || 'skill'),
82
+ };
83
+ }
84
+
85
+ function findBaseline(hashed, bundledBaselines = []) {
86
+ if (!hashed) return null;
87
+ return bundledBaselines.find((baseline) => baseline && baseline.revision === hashed.revision) || null;
88
+ }
89
+
90
+ function sigmaLookingMigration(params) {
91
+ const {
92
+ hashed,
93
+ live,
94
+ bundledFiles,
95
+ bundledBaselines,
96
+ owned,
97
+ extra = {},
98
+ } = params;
99
+ const vsUpstream = diffSkillFiles(hashed?.files || {}, bundledFiles);
100
+ const baseline = findBaseline(hashed, bundledBaselines);
101
+ const customization = live.customization;
102
+
103
+ if ((owned || live.sigmaLooking) && customization?.status === 'malformed') {
104
+ return classified({
105
+ kind: 'malformed-custom',
106
+ migratable: true,
107
+ confidence: owned ? 'high' : 'low',
108
+ revision: hashed?.revision,
109
+ files: hashed?.files,
110
+ diff: vsUpstream,
111
+ customization,
112
+ ...extra,
113
+ });
114
+ }
115
+
116
+ if (baseline) {
117
+ return classified({
118
+ kind: 'legacy',
119
+ migratable: true,
120
+ confidence: 'high',
121
+ revision: hashed.revision,
122
+ files: hashed.files,
123
+ baselineRevision: baseline.revision,
124
+ diff: vsUpstream,
125
+ customization,
126
+ ...extra,
127
+ });
128
+ }
129
+
130
+ if (owned) {
131
+ return classified({
132
+ kind: 'changed',
133
+ migratable: true,
134
+ confidence: 'high',
135
+ revision: hashed?.revision,
136
+ files: hashed?.files,
137
+ diff: vsUpstream,
138
+ customization,
139
+ ...extra,
140
+ });
141
+ }
142
+
143
+ if (live.sigmaLooking) {
144
+ return classified({
145
+ kind: 'unverified',
146
+ migratable: true,
147
+ confidence: 'low',
148
+ revision: hashed?.revision,
149
+ files: hashed?.files,
150
+ diff: vsUpstream,
151
+ customization,
152
+ ...extra,
153
+ });
154
+ }
155
+
156
+ return null;
157
+ }
158
+
159
+ function classified(fields) {
160
+ return {
161
+ adoptable: false,
162
+ migratable: false,
163
+ confidence: 'none',
164
+ method: 'copy',
165
+ ...fields,
166
+ };
167
+ }
168
+
169
+ /**
170
+ * Classify one on-disk skill path. Scope-neutral: callers supply ownership and
171
+ * the bundled Skill Revision. Global Installation can reuse this without a project root.
172
+ *
173
+ * @param {object} params
174
+ * @returns {object}
175
+ */
176
+ export function classifySkillPath(params) {
177
+ const {
178
+ destPath,
179
+ skillId = null,
180
+ bundledRevision,
181
+ bundledFiles = {},
182
+ bundledBaselines = [],
183
+ expectedCanonicalPath,
184
+ sigmaOwned = false,
185
+ sigmaRevision = null,
186
+ baseHashes = null,
187
+ } = params;
188
+
189
+ if (!pathExists(destPath)) {
190
+ return classified({ kind: 'missing', method: null });
191
+ }
192
+
193
+ const inspected = inspectManagedPath(destPath, expectedCanonicalPath);
194
+ if (inspected.broken) {
195
+ return classified({ kind: 'broken-link', method: inspected.method });
196
+ }
197
+
198
+ const isLink = fs.lstatSync(destPath).isSymbolicLink();
199
+ const live = isLink ? inspectLiveSkill(inspected.target || destPath, skillId) : inspectLiveSkill(destPath, skillId);
200
+
201
+ if (sigmaOwned && isLink) {
202
+ return classified({
203
+ kind: 'sigma-state',
204
+ adoptable: true,
205
+ confidence: 'high',
206
+ method: inspected.method || recommendedLinkMethod(),
207
+ revision: sigmaRevision || null,
208
+ resolvedTarget: inspected.target || destPath,
209
+ customization: live.customization,
210
+ });
211
+ }
212
+
213
+ if (sigmaOwned && !isLink) {
214
+ const hashed = hashRealDirectory(destPath);
215
+ if (hashed && bundledRevision && hashed.revision === bundledRevision) {
216
+ return classified({
217
+ kind: 'sigma-state',
218
+ adoptable: true,
219
+ confidence: 'high',
220
+ method: 'copy',
221
+ revision: hashed.revision,
222
+ files: hashed.files,
223
+ resolvedTarget: destPath,
224
+ customization: live.customization,
225
+ });
226
+ }
227
+
228
+ const migrated = sigmaLookingMigration({
229
+ hashed,
230
+ live,
231
+ bundledFiles,
232
+ bundledBaselines,
233
+ owned: true,
234
+ extra: { revision: hashed?.revision || sigmaRevision },
235
+ });
236
+ return migrated || classified({
237
+ kind: 'changed',
238
+ migratable: true,
239
+ confidence: 'high',
240
+ revision: hashed?.revision || sigmaRevision,
241
+ files: hashed?.files,
242
+ diff: diffSkillFiles(hashed?.files || {}, bundledFiles),
243
+ customization: live.customization,
244
+ });
245
+ }
246
+
247
+ if (!isLink) {
248
+ let hashed = null;
249
+ try {
250
+ hashed = hashRealDirectory(destPath);
251
+ } catch {
252
+ return classified({ kind: 'foreign', customization: live.customization });
253
+ }
254
+
255
+ if (hashed && bundledRevision && hashed.revision === bundledRevision) {
256
+ return classified({
257
+ kind: 'exact-revision',
258
+ adoptable: true,
259
+ confidence: 'high',
260
+ revision: hashed.revision,
261
+ files: hashed.files,
262
+ customization: live.customization,
263
+ });
264
+ }
265
+
266
+ const migrated = sigmaLookingMigration({
267
+ hashed,
268
+ live,
269
+ bundledFiles,
270
+ bundledBaselines,
271
+ owned: false,
272
+ });
273
+ if (migrated) return migrated;
274
+ return classified({
275
+ kind: 'foreign',
276
+ customization: live.customization,
277
+ diff: diffSkillFiles(hashed?.files || {}, bundledFiles),
278
+ });
279
+ }
280
+
281
+ let realPath;
282
+ try {
283
+ realPath = fs.realpathSync(destPath);
284
+ } catch {
285
+ return classified({ kind: 'broken-link', method: recommendedLinkMethod() });
286
+ }
287
+
288
+ let hashedTarget = null;
289
+ try {
290
+ hashedTarget = hashRealDirectory(realPath);
291
+ } catch {
292
+ hashedTarget = null;
293
+ }
294
+ const targetExact = Boolean(hashedTarget && bundledRevision && hashedTarget.revision === bundledRevision);
295
+ if (targetExact) {
296
+ return classified({
297
+ kind: 'recognized-link',
298
+ adoptable: true,
299
+ confidence: 'high',
300
+ method: recommendedLinkMethod(),
301
+ revision: hashedTarget.revision,
302
+ resolvedTarget: realPath,
303
+ dependsOn: expectedCanonicalPath || realPath,
304
+ customization: live.customization,
305
+ });
306
+ }
307
+
308
+ const targetLive = inspectLiveSkill(realPath, skillId);
309
+ const migrated = sigmaLookingMigration({
310
+ hashed: hashedTarget,
311
+ live: targetLive,
312
+ bundledFiles,
313
+ bundledBaselines,
314
+ owned: false,
315
+ extra: {
316
+ method: recommendedLinkMethod(),
317
+ resolvedTarget: realPath,
318
+ },
319
+ });
320
+ if (migrated) return migrated;
321
+
322
+ return classified({
323
+ kind: inspected.wrongTarget ? 'wrong-target' : 'foreign',
324
+ method: recommendedLinkMethod(),
325
+ resolvedTarget: realPath,
326
+ customization: targetLive.customization,
327
+ });
328
+ }
329
+
330
+ function fateFor(candidate) {
331
+ const kind = candidate.classification?.kind;
332
+ if (!kind || kind === 'missing') return 'create';
333
+ if (candidate.resolution) return candidate.resolution;
334
+ if (candidate.classification?.adoptable) return 'keep';
335
+ if (candidate.classification?.migratable) return 'needs-resolution';
336
+ return 'keep';
337
+ }
338
+
339
+ /**
340
+ * Choose one canonical copy among recognized destinations and name the fate of every other copy.
341
+ *
342
+ * @param {object[]} candidates
343
+ * @param {object} [options]
344
+ * @returns {{ canonical: object, others: object[] }}
345
+ */
346
+ export function chooseCanonical(candidates, options = {}) {
347
+ const universalRoot = options.universalRelativeRoot || UNIVERSAL_PROJECT_DESTINATION;
348
+ if (!Array.isArray(candidates) || candidates.length === 0) {
349
+ throw new Error('chooseCanonical requires at least one destination candidate');
350
+ }
351
+
352
+ const preferred = candidates.find((candidate) => candidate.relativeRoot === universalRoot)
353
+ || candidates[0];
354
+
355
+ const canonical = {
356
+ ...preferred,
357
+ fate: fateFor(preferred),
358
+ role: 'canonical',
359
+ };
360
+
361
+ const others = candidates
362
+ .filter((candidate) => candidate.relativeDestination !== canonical.relativeDestination)
363
+ .map((candidate) => ({
364
+ ...candidate,
365
+ fate: fateFor(candidate),
366
+ role: candidate.relativeRoot === universalRoot ? 'canonical' : 'host',
367
+ }));
368
+
369
+ return { canonical, others };
370
+ }