@ccoalm/ccl-skills 0.15.1 → 0.15.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 -1
- package/dist/assets/marketplace/plugins/ccl-skills/agent-context/session-start.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/app-cross-platform-dev/SKILL.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/SKILL.md +6 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/development-completion.md +26 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/references/staged-review-contract.md +37 -13
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/AGENTS.md +5 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/codex_review.sh +77 -5
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/kimi_packet_mcp.py +98 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/parse_cli_review.py +48 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/review_gate.py +230 -16
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_cli_review_wrappers.sh +165 -11
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_kimi_packet_mcp.py +143 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/code-review/scripts/test_review_client_compat.py +572 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/defect-diagnosis/SKILL.md +3 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/go-microservice-dev/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/llm-inference-integration/SKILL.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/miniapp-product-dev/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/multi-agent-delegation/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/nodejs-service-dev/SKILL.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-observability/SKILL.md +3 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-release-engineering/SKILL.md +3 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/platform-service-connectivity/SKILL.md +2 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/SKILL.md +7 -7
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/design-review-gate-mechanics.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/pre-final-continuation-gate.md +20 -11
- package/dist/assets/marketplace/plugins/ccl-skills/skills/product-rd-workflow/references/refactoring-discipline.md +7 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/python-service-dev/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/SKILL.md +2 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/dual-track-review-gate.md +17 -17
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/harness-patterns-and-eval.md +4 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/resume-paused-delivery.md +3 -3
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/references/source-register.md +11 -0
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_ai_coding_implementation_gates.sh +83 -48
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_body_compliance_grading.sh +80 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_controlled_escalation_pins.sh +3 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/test_validate_extraction_review_state.sh +190 -2
- package/dist/assets/marketplace/plugins/ccl-skills/skills/skill-extraction-workflow/scripts/validate_extraction_review_state.py +106 -4
- package/dist/assets/marketplace/plugins/ccl-skills/skills/terminal-cli-dev/SKILL.md +1 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/testing-strategy/SKILL.md +3 -1
- package/dist/assets/marketplace/plugins/ccl-skills/skills/web-react-dev/SKILL.md +1 -1
- package/dist/assets/release.json +54 -49
- package/dist/codex-host.d.ts +1 -1
- package/dist/codex-host.js +39 -15
- package/dist/host-probe.d.ts +16 -0
- package/dist/host-probe.js +29 -5
- package/dist/operations.js +34 -8
- package/dist/unified.d.ts +1 -1
- package/dist/unified.js +11 -4
- package/package.json +1 -1
package/dist/codex-host.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
2
|
+
import { existsSync, mkdtempSync, rmSync, statSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
3
4
|
import { isAbsolute, join, resolve } from "node:path";
|
|
4
5
|
import { LEGACY_REF, MARKET, REF } from "./paths.js";
|
|
5
|
-
import { canonicalAlias } from "./fs-safe.js";
|
|
6
|
-
import {
|
|
7
|
-
import { probeHostVersion } from "./host-probe.js";
|
|
6
|
+
import { assertSafeRoot, canonicalAlias } from "./fs-safe.js";
|
|
7
|
+
import { probeHostCommand } from "./host-probe.js";
|
|
8
8
|
function call(args, codexHome) {
|
|
9
9
|
const env = {
|
|
10
10
|
...process.env,
|
|
@@ -17,17 +17,41 @@ function call(args, codexHome) {
|
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
export function checkHost(codexHome) {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
const targetHome = codexHome || process.env.CODEX_HOME || join(process.env.HOME || "", ".codex");
|
|
21
|
+
try {
|
|
22
|
+
assertSafeRoot(targetHome, join(targetHome, "ccl-skills-npm"));
|
|
23
|
+
try {
|
|
24
|
+
if (!statSync(targetHome).isDirectory())
|
|
25
|
+
throw new Error(`Codex home is not a directory: ${targetHome}`);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error.code !== "ENOENT")
|
|
29
|
+
throw error;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
return { ok: false, kind: "safety-refusal", message: String(error) };
|
|
34
|
+
}
|
|
35
|
+
// Codex refuses a nonexistent CODEX_HOME. Probe the binary against a private
|
|
36
|
+
// empty home without creating the user's target just to discover capability.
|
|
37
|
+
const temporaryHome = existsSync(targetHome) ? null : mkdtempSync(join(tmpdir(), "ccl-codex-probe-"));
|
|
38
|
+
try {
|
|
39
|
+
const env = { ...process.env, CODEX_HOME: temporaryHome || targetHome };
|
|
40
|
+
const marketplace = probeHostCommand("codex", ["plugin", "marketplace", "list"], env);
|
|
41
|
+
if (!marketplace.ok)
|
|
42
|
+
return marketplace;
|
|
43
|
+
const plugins = probeHostCommand("codex", ["plugin", "list"], env);
|
|
44
|
+
if (!plugins.ok)
|
|
45
|
+
return plugins;
|
|
46
|
+
const parsed = parseMarketplace(marketplace.stdout);
|
|
47
|
+
if (!parsed.ok || !parsePluginList(plugins.stdout, parsed.source).ok)
|
|
48
|
+
return { ok: false, kind: "state-unknown", message: "Codex public state could not be verified" };
|
|
49
|
+
return { ok: true };
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
if (temporaryHome)
|
|
53
|
+
rmSync(temporaryHome, { recursive: true, force: true });
|
|
54
|
+
}
|
|
31
55
|
}
|
|
32
56
|
export function parsePluginList(text, marketplaceSource) {
|
|
33
57
|
const lines = text.split(/\r?\n/).filter((line) => line.trim());
|
package/dist/host-probe.d.ts
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
|
+
export declare function isHostCommandMissing(command: string, env?: NodeJS.ProcessEnv): boolean;
|
|
1
2
|
export declare function probeHostVersion(command: string, env?: NodeJS.ProcessEnv): {
|
|
2
3
|
ok: false;
|
|
3
4
|
kind: string;
|
|
4
5
|
message: string;
|
|
5
6
|
output?: undefined;
|
|
7
|
+
stdout?: undefined;
|
|
6
8
|
} | {
|
|
7
9
|
ok: true;
|
|
8
10
|
output: string;
|
|
11
|
+
stdout: string;
|
|
12
|
+
kind?: undefined;
|
|
13
|
+
message?: undefined;
|
|
14
|
+
};
|
|
15
|
+
export declare function probeHostCommand(command: string, args: string[], env?: NodeJS.ProcessEnv): {
|
|
16
|
+
ok: false;
|
|
17
|
+
kind: string;
|
|
18
|
+
message: string;
|
|
19
|
+
output?: undefined;
|
|
20
|
+
stdout?: undefined;
|
|
21
|
+
} | {
|
|
22
|
+
ok: true;
|
|
23
|
+
output: string;
|
|
24
|
+
stdout: string;
|
|
9
25
|
kind?: undefined;
|
|
10
26
|
message?: undefined;
|
|
11
27
|
};
|
package/dist/host-probe.js
CHANGED
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
|
|
2
|
+
import { accessSync } from "node:fs";
|
|
3
|
+
import { delimiter, resolve } from "node:path";
|
|
4
|
+
// Host discovery must not hold up other hosts or wait for interactive input.
|
|
3
5
|
const HOST_PROBE_TIMEOUT_MS = 10000;
|
|
6
|
+
// Presence only: do not execute a candidate or inspect its configuration.
|
|
7
|
+
export function isHostCommandMissing(command, env = process.env) {
|
|
8
|
+
const candidates = command.includes("/")
|
|
9
|
+
? [command]
|
|
10
|
+
: (env.PATH ?? "/usr/bin:/bin").split(delimiter).map((dir) => resolve(dir, command));
|
|
11
|
+
let uncertain = false;
|
|
12
|
+
for (const candidate of candidates) {
|
|
13
|
+
try {
|
|
14
|
+
accessSync(candidate);
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
const code = error.code;
|
|
19
|
+
if (code !== "ENOENT" && code !== "ENOTDIR")
|
|
20
|
+
uncertain = true;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return !uncertain;
|
|
24
|
+
}
|
|
4
25
|
export function probeHostVersion(command, env = process.env) {
|
|
5
|
-
|
|
26
|
+
return probeHostCommand(command, ["--version"], env);
|
|
27
|
+
}
|
|
28
|
+
export function probeHostCommand(command, args, env = process.env) {
|
|
29
|
+
const result = spawnSync(command, args, {
|
|
6
30
|
encoding: "utf8",
|
|
7
31
|
env,
|
|
8
32
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -17,11 +41,11 @@ export function probeHostVersion(command, env = process.env) {
|
|
|
17
41
|
ok: false,
|
|
18
42
|
kind,
|
|
19
43
|
message: kind === "timeout"
|
|
20
|
-
? `${command}
|
|
44
|
+
? `${command} ${args.join(" ")} timed out after ${HOST_PROBE_TIMEOUT_MS}ms`
|
|
21
45
|
: kind === "missing"
|
|
22
46
|
? `${command} CLI is unavailable (ENOENT)`
|
|
23
|
-
: `${command}
|
|
47
|
+
: `${command} ${args.join(" ")} failed (${errorCode || (result.signal ? `signal ${result.signal}` : result.error ? "spawn error" : `exit ${result.status}`)})`,
|
|
24
48
|
};
|
|
25
49
|
}
|
|
26
|
-
return { ok: true, output: `${result.stdout}\n${result.stderr}
|
|
50
|
+
return { ok: true, output: `${result.stdout}\n${result.stderr}`, stdout: result.stdout };
|
|
27
51
|
}
|
package/dist/operations.js
CHANGED
|
@@ -200,13 +200,24 @@ function cleanupCandidate(path, files, context) {
|
|
|
200
200
|
removeEmptyTree(path, files);
|
|
201
201
|
return [];
|
|
202
202
|
}
|
|
203
|
-
function resumeJournal(context) {
|
|
203
|
+
function resumeJournal(context, host) {
|
|
204
204
|
const p = paths(context), j = journal(p.journal, p.root);
|
|
205
|
+
const hostFailure = host.ok ? undefined : { kind: host.kind, message: host.message };
|
|
205
206
|
if (!j)
|
|
206
207
|
return {
|
|
207
208
|
code: 5,
|
|
208
209
|
status: "partial-journal",
|
|
209
210
|
message: "operation journal is malformed",
|
|
211
|
+
...(hostFailure ? { details: { hostFailure } } : {}),
|
|
212
|
+
};
|
|
213
|
+
if (!host.ok)
|
|
214
|
+
return {
|
|
215
|
+
code: 5,
|
|
216
|
+
status: "partial-journal",
|
|
217
|
+
message: host.kind === "missing"
|
|
218
|
+
? "operation journal retained; restore or reinstall the Codex CLI, then rerun this command"
|
|
219
|
+
: "operation journal retained; restore readable Codex public plugin state, then rerun this command",
|
|
220
|
+
details: { hostFailure },
|
|
210
221
|
};
|
|
211
222
|
if (j.step === "committed" || j.step === "journal-unlink-failed") {
|
|
212
223
|
if (j.operation !== "install" && j.operation !== "update")
|
|
@@ -274,6 +285,12 @@ function resumeJournal(context) {
|
|
|
274
285
|
}
|
|
275
286
|
}
|
|
276
287
|
if (j.trashPath && j.snapshot) {
|
|
288
|
+
if (j.operation === "uninstall" && !exactState(p.codexHome, null, false))
|
|
289
|
+
return {
|
|
290
|
+
code: 5,
|
|
291
|
+
status: "partial",
|
|
292
|
+
message: "uninstall resume requires absent public state",
|
|
293
|
+
};
|
|
277
294
|
const trash = contained(p.root, j.trashPath), deleted = j.deletedFiles || [], errors = remainingErrors(trash, j.snapshot, deleted);
|
|
278
295
|
if (errors.length)
|
|
279
296
|
return {
|
|
@@ -423,13 +440,16 @@ function doctor(context) {
|
|
|
423
440
|
return { code: 3, status: "safety-refusal", message: String(error) };
|
|
424
441
|
}
|
|
425
442
|
const host = checkHost(p.codexHome);
|
|
426
|
-
if (!host.ok)
|
|
427
|
-
return { code:
|
|
443
|
+
if (!host.ok && host.kind === "safety-refusal")
|
|
444
|
+
return { code: 3, status: host.kind, message: host.message };
|
|
445
|
+
// Diagnose pending journals first; unreadable public state cannot authorize cleanup.
|
|
428
446
|
if (existsSync(p.journal)) {
|
|
429
|
-
const repair = resumeJournal(context);
|
|
447
|
+
const repair = resumeJournal(context, host);
|
|
430
448
|
if (repair)
|
|
431
449
|
return repair;
|
|
432
450
|
}
|
|
451
|
+
if (!host.ok)
|
|
452
|
+
return { code: host.kind === "state-unknown" ? 3 : 4, status: `host-${host.kind}`, message: host.message };
|
|
433
453
|
const unknownTrash = existsSync(p.root)
|
|
434
454
|
? readdirSync(p.root).filter((name) => name.startsWith(".trash-"))
|
|
435
455
|
: [];
|
|
@@ -478,13 +498,16 @@ function installOrUpdate(command, options, context) {
|
|
|
478
498
|
return { code: 3, status: "safety-refusal", message: String(error) };
|
|
479
499
|
}
|
|
480
500
|
const host = checkHost(p.codexHome);
|
|
481
|
-
if (!host.ok)
|
|
482
|
-
return { code:
|
|
501
|
+
if (!host.ok && host.kind === "safety-refusal")
|
|
502
|
+
return { code: 3, status: host.kind, message: host.message };
|
|
503
|
+
// Diagnose pending journals first; unreadable public state cannot authorize cleanup.
|
|
483
504
|
if (existsSync(p.journal)) {
|
|
484
|
-
const repair = resumeJournal(context);
|
|
505
|
+
const repair = resumeJournal(context, host);
|
|
485
506
|
if (repair)
|
|
486
507
|
return repair;
|
|
487
508
|
}
|
|
509
|
+
if (!host.ok)
|
|
510
|
+
return { code: host.kind === "state-unknown" ? 3 : 4, status: `host-${host.kind}`, message: host.message };
|
|
488
511
|
const problem = conflict(command, context);
|
|
489
512
|
if (problem)
|
|
490
513
|
return problem;
|
|
@@ -787,7 +810,10 @@ function uninstall(options, context) {
|
|
|
787
810
|
return { code: 3, status: "safety-refusal", message: String(error) };
|
|
788
811
|
}
|
|
789
812
|
if (existsSync(p.journal)) {
|
|
790
|
-
const
|
|
813
|
+
const host = checkHost(p.codexHome);
|
|
814
|
+
if (!host.ok && host.kind === "safety-refusal")
|
|
815
|
+
return { code: 3, status: host.kind, message: host.message };
|
|
816
|
+
const repair = resumeJournal(context, host);
|
|
791
817
|
if (repair)
|
|
792
818
|
return repair;
|
|
793
819
|
}
|
package/dist/unified.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type PathContext } from "./paths.js";
|
|
2
2
|
import type { Host, Options, Result } from "./types.js";
|
|
3
3
|
export declare function runHostSequence(hosts: Host[], invoke: (host: Host) => Result): Result;
|
|
4
4
|
export declare function runUnified(command: string, options?: Options, codexContext?: PathContext): Result;
|
package/dist/unified.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { probeHostVersion } from "./host-probe.js";
|
|
1
|
+
import { isHostCommandMissing, probeHostVersion } from "./host-probe.js";
|
|
2
|
+
import { checkHost } from "./codex-host.js";
|
|
2
3
|
import { existsSync } from "node:fs";
|
|
3
4
|
import { join, resolve } from "node:path";
|
|
4
5
|
import { runClaude } from "./claude-adapter.js";
|
|
5
6
|
import { run as runCodex } from "./operations.js";
|
|
6
7
|
import { runOpenCode } from "./opencode-adapter.js";
|
|
8
|
+
import { paths } from "./paths.js";
|
|
7
9
|
function owned(host) {
|
|
8
10
|
const home = process.env.HOME;
|
|
9
11
|
if (!home)
|
|
@@ -53,14 +55,19 @@ export function runUnified(command, options = {}, codexContext = {}) {
|
|
|
53
55
|
return dispatch(options.host, command, options, codexContext);
|
|
54
56
|
const includeOwned = command === "doctor" || command === "uninstall";
|
|
55
57
|
const candidates = ["claude", "codex", "opencode"].filter((host) => command !== "update" || owned(host));
|
|
56
|
-
const probes = candidates.map((host) =>
|
|
58
|
+
const probes = candidates.map((host) => {
|
|
59
|
+
const probe = host !== "codex" ? probeHostVersion(host)
|
|
60
|
+
: isHostCommandMissing(host) ? { ok: false, kind: "missing", message: "codex CLI was not found on PATH" }
|
|
61
|
+
: checkHost(paths(codexContext).codexHome);
|
|
62
|
+
return { host, probe };
|
|
63
|
+
});
|
|
57
64
|
const hosts = probes.filter(({ host, probe }) => probe.ok || probe.kind !== "missing" || (includeOwned && owned(host))).map(({ host }) => host);
|
|
58
65
|
if (!hosts.length)
|
|
59
66
|
return { code: 4, status: "host-missing", message: "no Claude Code, Codex, or OpenCode CLI is available" };
|
|
60
67
|
return runHostSequence(hosts, (host) => {
|
|
61
68
|
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 };
|
|
69
|
+
if (!probe.ok && probe.kind !== "missing" && !(host === "codex" && probe.kind !== "safety-refusal" && existsSync(paths(codexContext).journal)))
|
|
70
|
+
return { code: probe.kind === "state-unknown" || probe.kind === "safety-refusal" ? 3 : 4, status: probe.kind === "safety-refusal" ? probe.kind : `host-${probe.kind}`, message: probe.message };
|
|
64
71
|
return dispatch(host, command, { ...options, host }, codexContext);
|
|
65
72
|
});
|
|
66
73
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ccoalm/ccl-skills",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.2",
|
|
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",
|