@ccoalm/ccl-skills 0.14.0 → 0.15.1

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 (59) hide show
  1. package/dist/assets/marketplace/plugins/ccl-skills/packages/opencode-plugin/ccl-skills.ts +80 -4
  2. package/dist/assets/marketplace/plugins/ccl-skills/packages/opencode-plugin/commands/ccl-install-skills.md +16 -4
  3. package/dist/assets/marketplace/plugins/ccl-skills/scripts/owner-dispatch/owner-dispatch.sh +13 -2
  4. package/dist/assets/marketplace/plugins/ccl-skills/scripts/owner-dispatch/test.sh +53 -0
  5. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/SKILL.md +19 -24
  6. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/client-routing.md +32 -32
  7. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/manual-invocation-and-prompts.md +16 -14
  8. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/staged-review-contract.md +24 -26
  9. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/AGENTS.md +11 -0
  10. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/claude_review.sh +60 -209
  11. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/init_policy_matrix.py +114 -367
  12. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/parse_probe_result.py +52 -672
  13. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/review_gate.py +17 -3
  14. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/runtime-surface-verification-design.md +4 -2
  15. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_claude_review_probe.sh +77 -444
  16. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_init_policy_matrix.sh +33 -98
  17. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_parse_probe_result.sh +57 -173
  18. package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_gate.sh +65 -0
  19. package/dist/assets/marketplace/plugins/ccl-skills/skills/defect-diagnosis/SKILL.md +1 -1
  20. package/dist/assets/marketplace/plugins/ccl-skills/skills/grill-me/SKILL.md +1 -1
  21. package/dist/assets/marketplace/plugins/ccl-skills/skills/miniapp-product-dev/SKILL.md +1 -1
  22. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/SKILL.md +5 -5
  23. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/references/alerting-and-on-call.md +8 -0
  24. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/SKILL.md +1 -1
  25. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-service-connectivity/SKILL.md +14 -14
  26. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-service-connectivity/references/dual-sidecar-and-traffic-config-center.md +1 -1
  27. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-service-connectivity/references/grpc-authority-workaround.md +40 -83
  28. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-service-connectivity/references/mesh-architecture.md +2 -2
  29. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-service-connectivity/references/retry-timeout-circuit-breaker.md +44 -37
  30. package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-service-connectivity/references/service-discovery-recipe.md +1 -1
  31. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/SKILL.md +2 -2
  32. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/delivery-lifecycle.md +1 -1
  33. package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/rd-standards-doc-family-checklist.md +2 -2
  34. package/dist/assets/marketplace/plugins/ccl-skills/skills/requirement-baseline/SKILL.md +1 -1
  35. package/dist/assets/marketplace/plugins/ccl-skills/skills/requirement-doc-writer/SKILL.md +1 -1
  36. package/dist/assets/marketplace/plugins/ccl-skills/skills/requirement-scope/SKILL.md +9 -6
  37. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/eval-routing.md +6 -0
  38. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/external-practice-controls.md +4 -4
  39. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +52 -0
  40. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/eval-golden-trace.rb +31 -7
  41. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/eval-routing-bank.rb +62 -3
  42. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/impact-chain-gate.rb +188 -13
  43. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/review_ledger_binding.py +36 -13
  44. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/skill-behavior-eval.py +103 -21
  45. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_impact_chain_refscripts.sh +261 -14
  46. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_check_ccl_regressions.sh +2 -0
  47. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_eval_routing_bank_resolution.sh +253 -0
  48. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_eval_runtime.py +428 -0
  49. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_impact_chain_gate_verdict_differential.sh +49 -25
  50. package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_review_ledger_binding.sh +63 -5
  51. package/dist/assets/release.json +76 -56
  52. package/dist/claude-adapter.js +14 -7
  53. package/dist/codex-host.d.ts +1 -3
  54. package/dist/codex-host.js +6 -9
  55. package/dist/host-probe.d.ts +11 -0
  56. package/dist/host-probe.js +27 -0
  57. package/dist/opencode-adapter.js +24 -19
  58. package/dist/unified.js +11 -9
  59. package/package.json +1 -1
@@ -0,0 +1,27 @@
1
+ import { spawnSync } from "node:child_process";
2
+ // Version discovery must not hold up other hosts or wait for interactive input.
3
+ const HOST_PROBE_TIMEOUT_MS = 10000;
4
+ export function probeHostVersion(command, env = process.env) {
5
+ const result = spawnSync(command, ["--version"], {
6
+ encoding: "utf8",
7
+ env,
8
+ stdio: ["ignore", "pipe", "pipe"],
9
+ timeout: HOST_PROBE_TIMEOUT_MS,
10
+ // spawnSync still waits if a child catches SIGTERM and does not exit.
11
+ killSignal: "SIGKILL",
12
+ });
13
+ if (result.error || result.status !== 0) {
14
+ const errorCode = result.error?.code;
15
+ const kind = errorCode === "ETIMEDOUT" ? "timeout" : errorCode === "ENOENT" ? "missing" : "probe-failed";
16
+ return {
17
+ ok: false,
18
+ kind,
19
+ message: kind === "timeout"
20
+ ? `${command} --version timed out after ${HOST_PROBE_TIMEOUT_MS}ms`
21
+ : kind === "missing"
22
+ ? `${command} CLI is unavailable (ENOENT)`
23
+ : `${command} --version failed (${errorCode || (result.signal ? `signal ${result.signal}` : result.error ? "spawn error" : `exit ${result.status}`)})`,
24
+ };
25
+ }
26
+ return { ok: true, output: `${result.stdout}\n${result.stderr}` };
27
+ }
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
6
6
  import { atomicJson, sha256 } from "./fs-safe.js";
7
7
  import { readRelease } from "./manifest.js";
8
8
  import { compare } from "./version.js";
9
+ import { probeHostVersion } from "./host-probe.js";
9
10
  const PACKAGE = "@ccoalm/ccl-skills";
10
11
  function paths(context) {
11
12
  const rawHome = context.home ?? (context.env ? context.env.HOME : process.env.HOME);
@@ -21,7 +22,7 @@ function assertExclusiveRoot(base, root, createBase = false) {
21
22
  throw new Error("managed root is not canonical");
22
23
  if (createBase)
23
24
  mkdirSync(base, { recursive: true });
24
- for (const component of [base, root]) {
25
+ for (const component of [base, root, join(root, "snapshots")]) {
25
26
  if (!existsSync(component))
26
27
  continue;
27
28
  const stat = lstatSync(component);
@@ -124,10 +125,6 @@ function readManifest(path, root) {
124
125
  }).sort((a, b) => a.destination.localeCompare(b.destination));
125
126
  return { schema: 1, npmPackage: PACKAGE, installedAt: raw.installedAt, version: raw.version, sourceCommit: raw.sourceCommit, sourceKind: raw.sourceKind, snapshot, snapshotHash: raw.snapshotHash, entries };
126
127
  }
127
- function hostAvailable(context) {
128
- const result = spawnSync("opencode", ["--version"], { encoding: "utf8", env: context.env || process.env });
129
- return !result.error && result.status === 0;
130
- }
131
128
  function sharedPath(base, rel) {
132
129
  const path = resolve(base, safeRel(rel, "OpenCode destination"));
133
130
  if (!path.startsWith(`${resolve(base)}${sep}`))
@@ -190,17 +187,19 @@ function actualFiles(directory) {
190
187
  function preflight(base, next, old) {
191
188
  assertSafeSharedParents(base, next);
192
189
  const previous = new Map((old?.entries || []).map((entry) => [entry.destination, entry]));
190
+ const drift = [];
193
191
  if (old) {
192
+ assertSafeSharedParents(base, old.entries);
194
193
  for (const entry of old.entries) {
195
194
  const path = sharedPath(base, entry.destination);
196
195
  if (!existsSync(path) || (regular(path).mode & 0o777) !== entry.mode || sha256(path) !== entry.sha256)
197
- throw new Error(`owned OpenCode file drifted: ${entry.destination}`);
196
+ drift.push(entry.destination);
198
197
  }
199
198
  const skillNames = new Set(old.entries.filter((entry) => entry.destination.startsWith("skills/")).map((entry) => entry.destination.split("/")[1]));
200
199
  for (const skill of skillNames) {
201
200
  const expected = old.entries.filter((entry) => entry.destination.startsWith(`skills/${skill}/`)).map((entry) => entry.destination.slice(`skills/${skill}/`.length)).sort();
202
201
  if (JSON.stringify(actualFiles(join(base, "skills", skill))) !== JSON.stringify(expected))
203
- throw new Error(`owned OpenCode skill directory drifted: ${skill}`);
202
+ drift.push(`skills/${skill}`);
204
203
  }
205
204
  }
206
205
  const nextSkills = new Set(next.filter((entry) => entry.destination.startsWith("skills/")).map((entry) => entry.destination.split("/")[1]));
@@ -211,6 +210,7 @@ function preflight(base, next, old) {
211
210
  for (const entry of next)
212
211
  if (!previous.has(entry.destination) && existsSync(sharedPath(base, entry.destination)))
213
212
  throw new Error(`OpenCode path collision: ${entry.destination}`);
213
+ return drift;
214
214
  }
215
215
  function prepareSnapshot(p, candidate) {
216
216
  const snapshotRel = join("snapshots", candidate.snapshotHash, "source"), snapshot = join(p.root, snapshotRel);
@@ -305,8 +305,9 @@ function writeShared(p, snapshot, entries, old, isInterrupted, copyShared = (fro
305
305
  function doctor(context) {
306
306
  const p = paths(context);
307
307
  assertExclusiveRoot(p.base, p.root);
308
- if (!hostAvailable(context))
309
- return { code: 4, status: "host-missing", message: "OpenCode CLI is not installed" };
308
+ const host = probeHostVersion("opencode", context.env || process.env);
309
+ if (!host.ok)
310
+ return { code: 4, status: `host-${host.kind}`, message: host.message };
310
311
  const legacy = existsSync(join(p.base, "ccl-skills/install-manifest.json")), manifest = readManifest(p.manifest, p.root);
311
312
  if (legacy && manifest)
312
313
  return { code: 3, status: "double-install", message: "legacy checkout and npm OpenCode installs are both present; keep exactly one" };
@@ -315,20 +316,22 @@ function doctor(context) {
315
316
  return { code: 3, status: legacy ? "legacy-install" : "unowned-registration", message: "OpenCode ccl-skills files exist without an npm ownership receipt" };
316
317
  return { code: 3, status: "absent", message: "ccl-skills is not installed for OpenCode" };
317
318
  }
318
- const drift = [];
319
- for (const entry of manifest.entries) {
320
- const path = sharedPath(p.base, entry.destination);
321
- if (!existsSync(path) || sha256(path) !== entry.sha256)
322
- drift.push(entry.destination);
319
+ let drift = [], reason;
320
+ try {
321
+ drift = preflight(p.base, manifest.entries, manifest);
322
+ }
323
+ catch (error) {
324
+ reason = String(error);
323
325
  }
324
- if (drift.length)
325
- return { code: 3, status: "shared-drift", message: "OpenCode shared files changed after npm install; they will never be auto-deleted", details: { drift } };
326
+ if (drift.length || reason)
327
+ return { code: 3, status: "shared-drift", message: "OpenCode shared files changed after npm install; they will never be auto-deleted", details: { drift, ...(reason ? { reason } : {}) } };
326
328
  return { code: 0, status: "healthy", message: `OpenCode uses @ccoalm/ccl-skills ${manifest.version}` };
327
329
  }
328
330
  function installOrUpdate(commandName, options, context) {
329
331
  const p = paths(context);
330
- if (!hostAvailable(context))
331
- return { code: 4, status: "host-missing", message: "OpenCode CLI is not installed" };
332
+ const host = probeHostVersion("opencode", context.env || process.env);
333
+ if (!host.ok)
334
+ return { code: 4, status: `host-${host.kind}`, message: host.message };
332
335
  let candidate;
333
336
  try {
334
337
  candidate = source(context);
@@ -350,7 +353,9 @@ function installOrUpdate(commandName, options, context) {
350
353
  return { code: 3, status: "downgrade-refused", message: "newer OpenCode assets are installed" };
351
354
  }
352
355
  try {
353
- preflight(p.base, candidate.entries, old);
356
+ const drift = preflight(p.base, candidate.entries, old);
357
+ if (drift.length)
358
+ throw new Error(`owned OpenCode paths drifted: ${drift.join(", ")}`);
354
359
  }
355
360
  catch (error) {
356
361
  return { code: 3, status: "collision", message: String(error) };
package/dist/unified.js CHANGED
@@ -1,14 +1,9 @@
1
- import { spawnSync } from "node:child_process";
1
+ import { probeHostVersion } from "./host-probe.js";
2
2
  import { existsSync } from "node:fs";
3
3
  import { join, resolve } from "node:path";
4
4
  import { runClaude } from "./claude-adapter.js";
5
5
  import { run as runCodex } from "./operations.js";
6
6
  import { runOpenCode } from "./opencode-adapter.js";
7
- function available(host) {
8
- const command = host === "opencode" ? "opencode" : host;
9
- const result = spawnSync(command, ["--version"], { encoding: "utf8" });
10
- return !result.error && result.status === 0;
11
- }
12
7
  function owned(host) {
13
8
  const home = process.env.HOME;
14
9
  if (!home)
@@ -57,8 +52,15 @@ export function runUnified(command, options = {}, codexContext = {}) {
57
52
  if (options.host)
58
53
  return dispatch(options.host, command, options, codexContext);
59
54
  const includeOwned = command === "doctor" || command === "uninstall";
60
- const hosts = ["claude", "codex", "opencode"].filter((host) => command === "update" ? available(host) && owned(host) : available(host) || (includeOwned && owned(host)));
55
+ const candidates = ["claude", "codex", "opencode"].filter((host) => command !== "update" || owned(host));
56
+ const probes = candidates.map((host) => ({ host, probe: probeHostVersion(host) }));
57
+ const hosts = probes.filter(({ host, probe }) => probe.ok || probe.kind !== "missing" || (includeOwned && owned(host))).map(({ host }) => host);
61
58
  if (!hosts.length)
62
- return { code: 4, status: "host-missing", message: "none of Claude Code, Codex, or OpenCode is installed" };
63
- return runHostSequence(hosts, (host) => dispatch(host, command, { ...options, host }, codexContext));
59
+ return { code: 4, status: "host-missing", message: "no Claude Code, Codex, or OpenCode CLI is available" };
60
+ return runHostSequence(hosts, (host) => {
61
+ const probe = probes.find((entry) => entry.host === host).probe;
62
+ if (!probe.ok && probe.kind !== "missing")
63
+ return { code: 4, status: `host-${probe.kind}`, message: probe.message };
64
+ return dispatch(host, command, { ...options, host }, codexContext);
65
+ });
64
66
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ccoalm/ccl-skills",
3
- "version": "0.14.0",
3
+ "version": "0.15.1",
4
4
  "description": "Reusable workflows that help coding agents plan, build, test, review, and release software — for Claude Code, Codex, and OpenCode",
5
5
  "keywords": ["skills", "agent-skills", "claude", "claude-code", "codex", "opencode", "agent", "ai", "ai-agents", "cli", "anthropic", "developer-tools"],
6
6
  "type": "module",