coding-agent-skills 0.2.15 → 0.2.17
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/CHANGELOG.md +29 -0
- package/README.md +34 -1
- package/ROADMAP.md +3 -0
- package/bin/coding-agent-skills +333 -2
- package/docs/adapters/README.md +6 -0
- package/docs/adapters/project-installation.md +6 -0
- package/docs/release/README.md +12 -9
- package/docs/release/npm-package.md +21 -1
- package/docs/testing/README.md +8 -0
- package/docs/usage/README.md +53 -7
- package/package.json +1 -1
- package/runs/skill-runs.md +17 -0
- package/scripts/lib/adapter-repo-map.mjs +194 -12
- package/scripts/test-pack.mjs +133 -1
- package/scripts/validate-pack.mjs +2 -2
- package/work-ledger.md +21 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes follow [Semantic Versioning](docs/versioning/README.md).
|
|
4
4
|
|
|
5
|
+
## [0.2.17] - 2026-07-04
|
|
6
|
+
|
|
7
|
+
### Changed
|
|
8
|
+
|
|
9
|
+
- `repo-map` now supports generic safe discovery when no `.coding-agent` project declaration exists.
|
|
10
|
+
- JSON output now reports `adapterPresent: false`, `mode: generic-safe-discovery`, reduced confidence, `changedState: false`, no target-project commands, and no secret-file reads for no-adapter repo-map runs.
|
|
11
|
+
- Invalid or unsafe project adapters still fail closed instead of falling back silently.
|
|
12
|
+
- Usage, adapter, and release documentation now clarify that adapters are optional hints, not mandatory requirements.
|
|
13
|
+
|
|
14
|
+
## [0.2.16] - 2026-07-03
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- Optional `--json` output for every public `coding-agent-skills` CLI command.
|
|
19
|
+
- OpenClaw-compatible structured result fields for command identity, skill id, package
|
|
20
|
+
version, status, findings, warnings, skipped checks, refused behavior, safety summary,
|
|
21
|
+
`recommendedNextAction`, and exit-code meaning.
|
|
22
|
+
- Release tests that validate the JSON contract across the public CLI surface and confirm
|
|
23
|
+
redaction of local home paths and secret-like values.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- The public wrapper now preserves default human-readable output while offering sanitized
|
|
28
|
+
machine-readable results for orchestrator callers.
|
|
29
|
+
- Usage, release, testing, roadmap, ledger, and run-log docs now describe the exit-code
|
|
30
|
+
contract and OpenClaw integration boundary.
|
|
31
|
+
- Stale v0.2.15 ledger and run-log entries now reflect that the deployment-preflight
|
|
32
|
+
release was published, smoke-tested, and released.
|
|
33
|
+
|
|
5
34
|
## [0.2.15] - 2026-07-03
|
|
6
35
|
|
|
7
36
|
### Added
|
package/README.md
CHANGED
|
@@ -52,12 +52,16 @@ Every skill emits the evidence-pack contract. A command being attempted is never
|
|
|
52
52
|
- Review static migration and schema evidence with `coding-agent-skills migration-review <project-root>`.
|
|
53
53
|
- Prepare local Git handoff evidence with `coding-agent-skills github-handoff <project-root>`.
|
|
54
54
|
- Map static deployment readiness evidence with `coding-agent-skills deployment-preflight <project-root>`.
|
|
55
|
+
- Add `--json` to any public CLI command when an OpenClaw-style orchestrator
|
|
56
|
+
needs a sanitized machine-readable result with `success`, `status`,
|
|
57
|
+
`recommendedNextAction`, safety flags, and exit-code meaning.
|
|
55
58
|
- Validate project adapters against [the formal adapter schema](schemas/project-adapter.schema.json).
|
|
56
59
|
- Review [external adapter discovery](docs/adapters/discovery.md).
|
|
57
60
|
- Run `node scripts/validate-adapters.mjs <adapter-root>` for a disposable external root.
|
|
58
61
|
- Review [project-owned installation and pinning](docs/adapters/project-installation.md).
|
|
59
62
|
- Run `node scripts/validate-project-adapters.mjs <project-root>` for a declared project root.
|
|
60
|
-
- Render
|
|
63
|
+
- Render repo-map orientation with optional adapter hints and generic safe
|
|
64
|
+
discovery when no adapter is present:
|
|
61
65
|
`node scripts/render-adapter-repo-map.mjs <project-root>`.
|
|
62
66
|
- Render a static route-trace report with
|
|
63
67
|
`node scripts/render-route-trace.mjs <project-root>`.
|
|
@@ -92,6 +96,35 @@ Every skill emits the evidence-pack contract. A command being attempted is never
|
|
|
92
96
|
Governance lives in [CONTRIBUTING.md](CONTRIBUTING.md), [ROADMAP.md](ROADMAP.md), and the [release policy](docs/release/README.md).
|
|
93
97
|
The [harness guide](docs/testing/README.md) explains trigger, command, mutation, privacy, adapter, and completion checks.
|
|
94
98
|
|
|
99
|
+
## Orchestrator Output
|
|
100
|
+
|
|
101
|
+
The default CLI output remains human-readable. OpenClaw-style callers can request a
|
|
102
|
+
structured result with `--json`:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
coding-agent-skills repo-map /path/to/project --json
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
JSON output is read-only and sanitized. It includes command identity, package version,
|
|
109
|
+
skill id, status, findings, warnings, skipped checks, refused behavior, safety flags,
|
|
110
|
+
and `recommendedNextAction`. Exit codes follow the public contract:
|
|
111
|
+
|
|
112
|
+
- `0`: handled execution path, including complete, partial, blocked, or controlled audit results
|
|
113
|
+
- `2`: usage error
|
|
114
|
+
- `3`: safety refusal
|
|
115
|
+
- `4`: missing required input or file
|
|
116
|
+
- `5`: unexpected internal or runtime failure
|
|
117
|
+
|
|
118
|
+
OpenClaw should remain the owner of memory, routing, permissions, scheduling, user
|
|
119
|
+
interaction, and workflow state. `coding-agent-skills` is a safe callable evidence
|
|
120
|
+
producer, not an orchestrator.
|
|
121
|
+
|
|
122
|
+
Adapters are optional hints, not a prerequisite for safe orientation. `repo-map`
|
|
123
|
+
falls back to `generic-safe-discovery` when no `.coding-agent` declaration exists,
|
|
124
|
+
marks `adapterPresent: false`, reduces confidence, and still refuses target project
|
|
125
|
+
builds, tests, runtime checks, deploys, migrations, package installs, and secret-file
|
|
126
|
+
reads.
|
|
127
|
+
|
|
95
128
|
## Autonomous Maintainer Loop
|
|
96
129
|
|
|
97
130
|
The local maintainer loop reads Git tags, `ROADMAP.md`, `CHANGELOG.md`, and
|
package/ROADMAP.md
CHANGED
|
@@ -42,6 +42,8 @@ execution constraints remain unchanged.
|
|
|
42
42
|
evidence before separately approved GitHub work.
|
|
43
43
|
- `v0.2.15`: audit-only `deployment-preflight` skill and CLI renderer for static
|
|
44
44
|
deployment readiness evidence before separately approved deployment work.
|
|
45
|
+
- `v0.2.16`: OpenClaw-compatible optional `--json` output and documented exit-code
|
|
46
|
+
semantics for every public CLI command.
|
|
45
47
|
|
|
46
48
|
The next milestone is recorded in [work-ledger.md](work-ledger.md). The
|
|
47
49
|
[maintainer loop](RUNBOOK.md) may select and evidence that milestone, but it must stop
|
|
@@ -89,6 +91,7 @@ Next safe milestone options:
|
|
|
89
91
|
| `migration-review-skill` | General with platform adapters | Audit-only | Implemented in `v0.2.13` |
|
|
90
92
|
| `github-handoff-skill` | General | Audit-only | Implemented in `v0.2.14` |
|
|
91
93
|
| `deployment-preflight-skill` | General | Audit-only | Implemented in `v0.2.15` |
|
|
94
|
+
| `orchestrator-json-output-contract` | General tooling | Read-only CLI contract | Implemented in `v0.2.16` |
|
|
92
95
|
| `cloudflare-preflight-skill` | Platform-specific | Audit-only | Builder-mode approved; later in wave |
|
|
93
96
|
| `cloudflare-deploy-skill` | Platform-specific | Action-capable | Blocked on approval model |
|
|
94
97
|
| `supabase-rls-audit-skill` | Platform-specific | Audit-only | Builder-mode approved; later in wave |
|
package/bin/coding-agent-skills
CHANGED
|
@@ -1,9 +1,125 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
6
|
|
|
7
|
+
import { externalAdapterCliResult } from "../scripts/lib/adapter-discovery.mjs";
|
|
8
|
+
import { adapterRepoMapCliResult } from "../scripts/lib/adapter-repo-map.mjs";
|
|
9
|
+
import { apiContractAuditCliResult } from "../scripts/lib/api-contract-audit.mjs";
|
|
10
|
+
import { deploymentPreflightCliResult } from "../scripts/lib/deployment-preflight.mjs";
|
|
11
|
+
import { envAuditCliResult } from "../scripts/lib/env-audit.mjs";
|
|
12
|
+
import { githubHandoffCliResult } from "../scripts/lib/github-handoff.mjs";
|
|
13
|
+
import { migrationReviewCliResult } from "../scripts/lib/migration-review.mjs";
|
|
14
|
+
import { redactSensitiveText } from "../scripts/lib/pack-rules.mjs";
|
|
15
|
+
import { projectAdapterCliResult } from "../scripts/lib/project-adapter-installation.mjs";
|
|
16
|
+
import { routeTraceCliResult } from "../scripts/lib/route-trace.mjs";
|
|
17
|
+
import { secretAuditCliResult } from "../scripts/lib/secret-audit.mjs";
|
|
18
|
+
|
|
6
19
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
20
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8"));
|
|
21
|
+
|
|
22
|
+
const commandMetadata = {
|
|
23
|
+
"validate-pack": {
|
|
24
|
+
skillId: "pack-validation",
|
|
25
|
+
mode: "validation",
|
|
26
|
+
next: {
|
|
27
|
+
label: "Select a read-only audit command",
|
|
28
|
+
reason: "The package is structurally valid; choose the narrowest command for the target task.",
|
|
29
|
+
requiresApproval: false,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
"validate-project": {
|
|
33
|
+
skillId: "project-adapter-validation",
|
|
34
|
+
mode: "validation",
|
|
35
|
+
next: {
|
|
36
|
+
label: "Run an adapter-aware read-only report",
|
|
37
|
+
reason: "The project adapter declaration is valid; use a scoped report command next.",
|
|
38
|
+
requiresApproval: false,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
"repo-map": {
|
|
42
|
+
skillId: "repo-map",
|
|
43
|
+
mode: "audit-only",
|
|
44
|
+
next: {
|
|
45
|
+
label: "Review reported repo boundaries",
|
|
46
|
+
reason: "Use the reported docs, safe paths, ignored paths, adapter status, confidence, and evidence requirements before choosing another action.",
|
|
47
|
+
requiresApproval: false,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
"route-trace": {
|
|
51
|
+
skillId: "route-trace",
|
|
52
|
+
mode: "audit-only",
|
|
53
|
+
next: {
|
|
54
|
+
label: "Review static route findings",
|
|
55
|
+
reason: "Inspect verified and inferred route surfaces before approving code or runtime work.",
|
|
56
|
+
requiresApproval: false,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
"env-audit": {
|
|
60
|
+
skillId: "env-audit",
|
|
61
|
+
mode: "audit-only",
|
|
62
|
+
next: {
|
|
63
|
+
label: "Review variable-name inventory",
|
|
64
|
+
reason: "Use value-free environment names to plan documentation or configuration review without reading secrets.",
|
|
65
|
+
requiresApproval: false,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
"secret-audit": {
|
|
69
|
+
skillId: "secret-audit",
|
|
70
|
+
mode: "audit-only",
|
|
71
|
+
next: {
|
|
72
|
+
label: "Review redacted secret-risk findings",
|
|
73
|
+
reason: "Any remediation, credential rotation, or repository mutation needs separate approval.",
|
|
74
|
+
requiresApproval: true,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
"api-contract-audit": {
|
|
78
|
+
skillId: "api-contract-audit",
|
|
79
|
+
mode: "audit-only",
|
|
80
|
+
next: {
|
|
81
|
+
label: "Review static API contract surfaces",
|
|
82
|
+
reason: "Use contract, endpoint, client, and schema evidence before editing API code.",
|
|
83
|
+
requiresApproval: false,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
"migration-review": {
|
|
87
|
+
skillId: "migration-review",
|
|
88
|
+
mode: "audit-only",
|
|
89
|
+
next: {
|
|
90
|
+
label: "Review migration evidence",
|
|
91
|
+
reason: "Database access, migration edits, generation, or execution require separate approval.",
|
|
92
|
+
requiresApproval: true,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
"github-handoff": {
|
|
96
|
+
skillId: "github-handoff",
|
|
97
|
+
mode: "audit-only",
|
|
98
|
+
next: {
|
|
99
|
+
label: "Request explicit GitHub handoff approval",
|
|
100
|
+
reason: "Commit, push, tag, pull request, or API mutation work is outside this read-only report.",
|
|
101
|
+
requiresApproval: true,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
"deployment-preflight": {
|
|
105
|
+
skillId: "deployment-preflight",
|
|
106
|
+
mode: "audit-only",
|
|
107
|
+
next: {
|
|
108
|
+
label: "Request explicit deployment approval",
|
|
109
|
+
reason: "Deployment, provider API, provider CLI, build, runtime, or migration work is outside this read-only report.",
|
|
110
|
+
requiresApproval: true,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
"validate-adapters": {
|
|
114
|
+
skillId: "external-adapter-validation",
|
|
115
|
+
mode: "validation",
|
|
116
|
+
next: {
|
|
117
|
+
label: "Use accepted adapters only as narrowing metadata",
|
|
118
|
+
reason: "Adapters may narrow safe context but must not grant new powers or weaken shared restrictions.",
|
|
119
|
+
requiresApproval: false,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
};
|
|
7
123
|
|
|
8
124
|
const commands = {
|
|
9
125
|
"validate-pack": {
|
|
@@ -73,6 +189,29 @@ const commands = {
|
|
|
73
189
|
},
|
|
74
190
|
};
|
|
75
191
|
|
|
192
|
+
const jsonHandlers = {
|
|
193
|
+
"validate-project": ([projectRoot]) => projectAdapterCliResult(projectRoot, { coreRoot: repoRoot }),
|
|
194
|
+
"repo-map": ([projectRoot]) => adapterRepoMapCliResult(projectRoot, { coreRoot: repoRoot }),
|
|
195
|
+
"route-trace": ([projectRoot]) => routeTraceCliResult(projectRoot, { coreRoot: repoRoot }),
|
|
196
|
+
"env-audit": ([projectRoot]) => envAuditCliResult(projectRoot, { coreRoot: repoRoot }),
|
|
197
|
+
"secret-audit": ([projectRoot]) => secretAuditCliResult(projectRoot, { coreRoot: repoRoot }),
|
|
198
|
+
"api-contract-audit": ([projectRoot]) => apiContractAuditCliResult(projectRoot, { coreRoot: repoRoot }),
|
|
199
|
+
"migration-review": ([projectRoot]) => migrationReviewCliResult(projectRoot, { coreRoot: repoRoot }),
|
|
200
|
+
"github-handoff": ([projectRoot]) => githubHandoffCliResult(projectRoot, { coreRoot: repoRoot }),
|
|
201
|
+
"deployment-preflight": ([projectRoot]) => deploymentPreflightCliResult(projectRoot, { coreRoot: repoRoot }),
|
|
202
|
+
"validate-adapters": ([adapterRoot]) => externalAdapterCliResult(adapterRoot, { coreRoot: repoRoot }),
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
function stripJsonFlag(args) {
|
|
206
|
+
let json = false;
|
|
207
|
+
const rest = [];
|
|
208
|
+
for (const arg of args) {
|
|
209
|
+
if (arg === "--json") json = true;
|
|
210
|
+
else rest.push(arg);
|
|
211
|
+
}
|
|
212
|
+
return { json, args: rest };
|
|
213
|
+
}
|
|
214
|
+
|
|
76
215
|
function usage(exitCode = 0) {
|
|
77
216
|
const lines = [
|
|
78
217
|
"usage: coding-agent-skills <command> [args]",
|
|
@@ -97,7 +236,192 @@ function usage(exitCode = 0) {
|
|
|
97
236
|
process.exitCode = exitCode;
|
|
98
237
|
}
|
|
99
238
|
|
|
100
|
-
|
|
239
|
+
function sanitizedLines(text) {
|
|
240
|
+
return redactSensitiveText(text)
|
|
241
|
+
.split(/\r?\n/)
|
|
242
|
+
.map((line) => line.trimEnd())
|
|
243
|
+
.filter((line) => line.length > 0);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function sectionLines(lines, heading) {
|
|
247
|
+
const result = [];
|
|
248
|
+
let active = false;
|
|
249
|
+
for (const line of lines) {
|
|
250
|
+
if (line === `## ${heading}`) {
|
|
251
|
+
active = true;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (active && line.startsWith("## ")) break;
|
|
255
|
+
if (active && line.trim()) result.push(line.replace(/^- /, ""));
|
|
256
|
+
}
|
|
257
|
+
return result.filter((line) => line !== "none" && line !== "none found");
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function detectedStatus(lines, exitCode) {
|
|
261
|
+
for (const line of lines) {
|
|
262
|
+
const match = /^Status:\s*([a-z-]+)/i.exec(line);
|
|
263
|
+
if (match) return match[1].toLowerCase();
|
|
264
|
+
}
|
|
265
|
+
if (exitCode === 0) return "complete";
|
|
266
|
+
if (exitCode === 2) return "failed";
|
|
267
|
+
return "failed";
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function exitCodeMeaning(exitCode) {
|
|
271
|
+
if (exitCode === 0) return "handled";
|
|
272
|
+
if (exitCode === 2) return "usage-error";
|
|
273
|
+
if (exitCode === 3) return "safety-refusal";
|
|
274
|
+
if (exitCode === 4) return "missing-required-input";
|
|
275
|
+
return "unexpected-internal-or-runtime-failure";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function adapterSummary(lines) {
|
|
279
|
+
const adapter = {};
|
|
280
|
+
const adapterLines = [
|
|
281
|
+
...sectionLines(lines, "Adapter Scope"),
|
|
282
|
+
...sectionLines(lines, "Adapter Bounds"),
|
|
283
|
+
];
|
|
284
|
+
for (const line of adapterLines) {
|
|
285
|
+
const match = /^([^:]+):\s*(.*)$/.exec(line);
|
|
286
|
+
if (!match) continue;
|
|
287
|
+
const key = match[1]
|
|
288
|
+
.trim()
|
|
289
|
+
.replace(/[^a-zA-Z0-9]+([a-zA-Z0-9])/g, (_, char) => char.toUpperCase())
|
|
290
|
+
.replace(/^[A-Z]/, (char) => char.toLowerCase());
|
|
291
|
+
adapter[key] = match[2].trim();
|
|
292
|
+
}
|
|
293
|
+
return adapter;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function summaryLines(lines) {
|
|
297
|
+
return lines
|
|
298
|
+
.filter((line) => line.trim().length > 0)
|
|
299
|
+
.filter((line) => !line.startsWith("#"))
|
|
300
|
+
.filter((line) => !line.startsWith("## "))
|
|
301
|
+
.filter((line) => !line.startsWith("- "))
|
|
302
|
+
.slice(0, 12);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function findingLines(lines) {
|
|
306
|
+
const ignoredSections = new Set([
|
|
307
|
+
"Git State",
|
|
308
|
+
"Adapter Scope",
|
|
309
|
+
"Adapter Bounds",
|
|
310
|
+
"Scope Paths",
|
|
311
|
+
"Ignored Paths",
|
|
312
|
+
"Summary",
|
|
313
|
+
"Skipped",
|
|
314
|
+
"Not Verified",
|
|
315
|
+
"Warnings",
|
|
316
|
+
"Refused Behavior",
|
|
317
|
+
]);
|
|
318
|
+
const findings = [];
|
|
319
|
+
let current = null;
|
|
320
|
+
for (const line of lines) {
|
|
321
|
+
if (line.startsWith("## ")) {
|
|
322
|
+
current = line.replace(/^## /, "");
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (!current || ignoredSections.has(current)) continue;
|
|
326
|
+
if (line.startsWith("- ") && !line.includes("none found")) findings.push(line.slice(2));
|
|
327
|
+
}
|
|
328
|
+
return findings.slice(0, 100);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function normalizeOutcome(outcome) {
|
|
332
|
+
if (Array.isArray(outcome.lines)) {
|
|
333
|
+
return {
|
|
334
|
+
exitCode: outcome.exitCode ?? 5,
|
|
335
|
+
lines: outcome.lines
|
|
336
|
+
.map((line) => redactSensitiveText(line))
|
|
337
|
+
.filter((line) => line.trim().length > 0),
|
|
338
|
+
report: outcome.report ?? outcome.result ?? null,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
exitCode: outcome.status ?? 5,
|
|
344
|
+
lines: [
|
|
345
|
+
...sanitizedLines(outcome.stdout ?? ""),
|
|
346
|
+
...sanitizedLines(outcome.stderr ?? ""),
|
|
347
|
+
],
|
|
348
|
+
report: null,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function buildJsonResult(commandName, args, outcome) {
|
|
353
|
+
const normalized = normalizeOutcome(outcome);
|
|
354
|
+
const lines = normalized.lines;
|
|
355
|
+
const metadata = commandMetadata[commandName] ?? {
|
|
356
|
+
skillId: commandName,
|
|
357
|
+
mode: "unknown",
|
|
358
|
+
next: null,
|
|
359
|
+
};
|
|
360
|
+
const status = normalized.report?.status ?? detectedStatus(lines, normalized.exitCode);
|
|
361
|
+
const summary = summaryLines(lines);
|
|
362
|
+
const warnings = [
|
|
363
|
+
...(Array.isArray(normalized.report?.warnings)
|
|
364
|
+
? normalized.report.warnings.map((line) => redactSensitiveText(line))
|
|
365
|
+
: []),
|
|
366
|
+
...sectionLines(lines, "Warnings"),
|
|
367
|
+
...lines.filter((line) => /\bwarning\b/i.test(line) && !line.startsWith("## ")),
|
|
368
|
+
];
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
success: normalized.exitCode === 0,
|
|
372
|
+
status,
|
|
373
|
+
tool: "coding-agent-skills",
|
|
374
|
+
command: commandName,
|
|
375
|
+
skillId: metadata.skillId,
|
|
376
|
+
packageVersion: packageJson.version,
|
|
377
|
+
mode: metadata.mode,
|
|
378
|
+
changedState: false,
|
|
379
|
+
summary: summary.length > 0
|
|
380
|
+
? summary
|
|
381
|
+
: [
|
|
382
|
+
normalized.exitCode === 0
|
|
383
|
+
? `${commandName} completed successfully`
|
|
384
|
+
: `${commandName} did not complete successfully`,
|
|
385
|
+
],
|
|
386
|
+
findings: Array.isArray(normalized.report?.findings)
|
|
387
|
+
? normalized.report.findings.map((finding) => redactSensitiveText(JSON.stringify(finding)))
|
|
388
|
+
: findingLines(lines),
|
|
389
|
+
warnings: [...new Set(warnings)],
|
|
390
|
+
risks: Array.isArray(normalized.report?.riskIndicators)
|
|
391
|
+
? normalized.report.riskIndicators.map((risk) => redactSensitiveText(JSON.stringify(risk)))
|
|
392
|
+
: sectionLines(lines, "Risk Indicators"),
|
|
393
|
+
skipped: Array.isArray(normalized.report?.skipped)
|
|
394
|
+
? normalized.report.skipped.map((item) => redactSensitiveText(JSON.stringify(item)))
|
|
395
|
+
: sectionLines(lines, "Skipped"),
|
|
396
|
+
notVerified: Array.isArray(normalized.report?.notVerified)
|
|
397
|
+
? normalized.report.notVerified.map((item) => redactSensitiveText(String(item)))
|
|
398
|
+
: sectionLines(lines, "Not Verified"),
|
|
399
|
+
refusedBehavior: Array.isArray(normalized.report?.refusedBehavior)
|
|
400
|
+
? normalized.report.refusedBehavior.map((item) => redactSensitiveText(String(item)))
|
|
401
|
+
: sectionLines(lines, "Refused Behavior"),
|
|
402
|
+
adapter: adapterSummary(lines),
|
|
403
|
+
recommendedNextAction: metadata.next,
|
|
404
|
+
safety: {
|
|
405
|
+
readOnly: true,
|
|
406
|
+
secretsRead: false,
|
|
407
|
+
targetCommandsRun: false,
|
|
408
|
+
mutationsPerformed: false,
|
|
409
|
+
},
|
|
410
|
+
exitCode: normalized.exitCode,
|
|
411
|
+
exitCodeMeaning: exitCodeMeaning(normalized.exitCode),
|
|
412
|
+
invocation: {
|
|
413
|
+
args: args.map((arg) => redactSensitiveText(arg)),
|
|
414
|
+
outputFormat: "json",
|
|
415
|
+
},
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function printJson(value) {
|
|
420
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const { json, args: rawArgs } = stripJsonFlag(process.argv.slice(2));
|
|
424
|
+
const [commandName, ...args] = rawArgs;
|
|
101
425
|
if (!commandName || commandName === "help" || commandName === "--help" || commandName === "-h") {
|
|
102
426
|
usage(commandName ? 0 : 2);
|
|
103
427
|
} else if (!Object.hasOwn(commands, commandName)) {
|
|
@@ -108,6 +432,10 @@ if (!commandName || commandName === "help" || commandName === "--help" || comman
|
|
|
108
432
|
if ((command.requiredArgs ?? 0) !== args.length) {
|
|
109
433
|
process.stderr.write(`usage: ${command.usage}\n`);
|
|
110
434
|
process.exitCode = 2;
|
|
435
|
+
} else if (json && jsonHandlers[commandName]) {
|
|
436
|
+
const outcome = jsonHandlers[commandName](args);
|
|
437
|
+
printJson(buildJsonResult(commandName, args, outcome));
|
|
438
|
+
process.exitCode = outcome.exitCode ?? 5;
|
|
111
439
|
} else {
|
|
112
440
|
const result = spawnSync(
|
|
113
441
|
process.execPath,
|
|
@@ -115,10 +443,13 @@ if (!commandName || commandName === "help" || commandName === "--help" || comman
|
|
|
115
443
|
{
|
|
116
444
|
cwd: repoRoot,
|
|
117
445
|
encoding: "utf8",
|
|
118
|
-
stdio: "inherit",
|
|
446
|
+
stdio: json ? "pipe" : "inherit",
|
|
119
447
|
shell: false,
|
|
120
448
|
},
|
|
121
449
|
);
|
|
450
|
+
if (json) {
|
|
451
|
+
printJson(buildJsonResult(commandName, args, result));
|
|
452
|
+
}
|
|
122
453
|
process.exitCode = result.status ?? 1;
|
|
123
454
|
}
|
|
124
455
|
}
|
package/docs/adapters/README.md
CHANGED
|
@@ -35,6 +35,12 @@ ignored paths, required evidence, package-manager hints, repository bounds, and
|
|
|
35
35
|
Git branch state. It does not read target project file contents, run project tests or
|
|
36
36
|
builds, install packages, perform runtime checks, deploy, migrate, or read `.env` files.
|
|
37
37
|
|
|
38
|
+
Adapters are optional hints, not mandatory gates. If no `.coding-agent` declaration is
|
|
39
|
+
present, `repo-map` falls back to `generic-safe-discovery`, reports `adapterPresent: false`,
|
|
40
|
+
uses reduced confidence, applies built-in ignored paths, and still refuses target project
|
|
41
|
+
builds, tests, runtime checks, deployments, migrations, package installs, and secret-file
|
|
42
|
+
reads. Invalid or weakening adapters still fail closed.
|
|
43
|
+
|
|
38
44
|
This is agent context for safer repository understanding. It is not target-application
|
|
39
45
|
product behavior.
|
|
40
46
|
|
|
@@ -109,6 +109,12 @@ The renderer is metadata-only. It does not read target project file contents, ru
|
|
|
109
109
|
project tests, run builds, install packages, perform runtime checks, deploy, migrate, read
|
|
110
110
|
`.env` files, or modify project state.
|
|
111
111
|
|
|
112
|
+
Project adapters are optional hints. If no project declaration exists, `repo-map` still
|
|
113
|
+
runs a bounded `generic-safe-discovery` report with `adapterPresent: false`, reduced
|
|
114
|
+
confidence, built-in ignored paths, and explicit no-secret/no-build/no-runtime safety
|
|
115
|
+
warnings. A declared adapter that is invalid, unsafe, or incompatible remains a hard
|
|
116
|
+
failure.
|
|
117
|
+
|
|
112
118
|
A project-owned adapter can also enable read-only `route-trace` context:
|
|
113
119
|
|
|
114
120
|
```bash
|
package/docs/release/README.md
CHANGED
|
@@ -19,20 +19,23 @@
|
|
|
19
19
|
13. Inspect tarball contents for local-only files, credentials, `.env` files, dependency
|
|
20
20
|
folders, generated output, and unrelated repositories.
|
|
21
21
|
14. Install the tarball into a temporary npm prefix and smoke-test the installed CLI.
|
|
22
|
-
15. Smoke-test
|
|
22
|
+
15. Smoke-test human-readable and `--json` output for public commands touched by the
|
|
23
|
+
release. JSON output must include safety flags, `recommendedNextAction`, and
|
|
24
|
+
`exitCodeMeaning` without secrets or `.env` contents.
|
|
25
|
+
16. Smoke-test any new CLI command such as `coding-agent-skills route-trace`,
|
|
23
26
|
`coding-agent-skills env-audit`, `coding-agent-skills secret-audit`,
|
|
24
27
|
`coding-agent-skills api-contract-audit`, `coding-agent-skills migration-review`,
|
|
25
28
|
`coding-agent-skills github-handoff`, or `coding-agent-skills deployment-preflight`
|
|
26
29
|
against synthetic fixtures only unless a real project read-only smoke is explicitly
|
|
27
30
|
approved.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
17. Review changelog, ledger, run evidence, and versioning impact.
|
|
32
|
+
18. Commit with approved identity.
|
|
33
|
+
19. Push `main` using credential-free remotes.
|
|
34
|
+
20. Confirm a clean synchronized worktree.
|
|
35
|
+
21. Create and push the annotated version tag.
|
|
36
|
+
22. Publish with `npm publish --access public --registry=https://registry.npmjs.org/`.
|
|
37
|
+
23. Install the published package into a temporary prefix and smoke-test the installed CLI.
|
|
38
|
+
24. Create the GitHub Release for the pushed tag.
|
|
36
39
|
|
|
37
40
|
Deployments, migrations, runtime mutation, platform actions, and target-project builds or
|
|
38
41
|
tests remain outside this release process unless separately approved.
|
|
@@ -7,7 +7,7 @@ safety model.
|
|
|
7
7
|
## Current Package Shape
|
|
8
8
|
|
|
9
9
|
- Package name: `coding-agent-skills`.
|
|
10
|
-
- Package version: `0.2.
|
|
10
|
+
- Package version: `0.2.17`.
|
|
11
11
|
- CLI bin: `coding-agent-skills` mapped to `bin/coding-agent-skills`.
|
|
12
12
|
- Module type: `module`.
|
|
13
13
|
- Dependencies: none.
|
|
@@ -37,6 +37,18 @@ coding-agent-skills deployment-preflight /path/to/project
|
|
|
37
37
|
coding-agent-skills validate-adapters /path/to/adapter-root
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
+
Each public command also supports optional machine-readable output:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
coding-agent-skills repo-map /path/to/project --json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The JSON contract is intended for OpenClaw-style orchestrators that already own memory,
|
|
47
|
+
routing, approvals, scheduling, and workflow state. It includes `success`, `status`,
|
|
48
|
+
`command`, `skillId`, `packageVersion`, sanitized findings and warnings, safety flags,
|
|
49
|
+
`recommendedNextAction`, and `exitCodeMeaning`. The default human-readable output is
|
|
50
|
+
unchanged.
|
|
51
|
+
|
|
40
52
|
The package can also be executed without a repo-local install:
|
|
41
53
|
|
|
42
54
|
```bash
|
|
@@ -112,3 +124,11 @@ already permits a bounded local validation action. The installed `repo-map`,
|
|
|
112
124
|
|
|
113
125
|
Project adapters narrow context for safer repository understanding; they do not weaken
|
|
114
126
|
shared restrictions or authorize additional command families.
|
|
127
|
+
|
|
128
|
+
## Exit Codes
|
|
129
|
+
|
|
130
|
+
- `0`: handled execution path, including complete, partial, blocked, or controlled audit result
|
|
131
|
+
- `2`: usage error
|
|
132
|
+
- `3`: safety refusal
|
|
133
|
+
- `4`: missing required input or file
|
|
134
|
+
- `5`: unexpected internal or runtime failure
|
package/docs/testing/README.md
CHANGED
|
@@ -126,6 +126,14 @@ compatibility, schema drift, restriction weakening, evidence removal, failure su
|
|
|
126
126
|
completion override, mode escalation, `.env` avoidance, traversal, symlinks, and mutation
|
|
127
127
|
snapshots. Chain summaries use ordinal revision labels rather than directory names.
|
|
128
128
|
|
|
129
|
+
## OpenClaw-Compatible CLI JSON
|
|
130
|
+
|
|
131
|
+
Public CLI tests exercise `--json` for every exposed command. The contract must remain
|
|
132
|
+
valid JSON, preserve default human-readable output, include safety flags and
|
|
133
|
+
`recommendedNextAction`, use handled exit semantics for successful/partial audit results,
|
|
134
|
+
redact local home paths, and avoid token, private-key, authorization-header, or `.env`
|
|
135
|
+
contents.
|
|
136
|
+
|
|
129
137
|
## Evidence Bundles
|
|
130
138
|
|
|
131
139
|
Disposable evidence bundles cover valid replay, hash mismatch, missing entries,
|
package/docs/usage/README.md
CHANGED
|
@@ -41,16 +41,18 @@ Select the least-privileged skill that matches the request:
|
|
|
41
41
|
|
|
42
42
|
Every skill emits an evidence pack. Read `status`, skipped checks, failures, confidence, and changed state before relying on a completion claim.
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
with:
|
|
44
|
+
Render read-only `repo-map` context with:
|
|
46
45
|
|
|
47
46
|
```bash
|
|
48
47
|
node scripts/render-adapter-repo-map.mjs <project-root>
|
|
49
48
|
```
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
precedence, safe read paths, ignored paths, and required
|
|
53
|
-
|
|
50
|
+
When a project owns a compatible adapter, this validates the adapter first and reports
|
|
51
|
+
adapter-declared documentation precedence, safe read paths, ignored paths, and required
|
|
52
|
+
evidence. When no `.coding-agent` declaration exists, adapters remain optional: `repo-map`
|
|
53
|
+
uses `generic-safe-discovery`, reports `adapterPresent: false`, reduces confidence, and
|
|
54
|
+
still refuses builds, tests, runtime checks, deployments, migrations, package installs, and
|
|
55
|
+
secret-file reads.
|
|
54
56
|
|
|
55
57
|
See [examples](../../examples/README.md) for safe concrete inputs and outputs.
|
|
56
58
|
|
|
@@ -85,8 +87,8 @@ npx coding-agent-skills validate-pack
|
|
|
85
87
|
```
|
|
86
88
|
|
|
87
89
|
These commands wrap the same validated scripts shipped in the repository. `repo-map`
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
uses adapter metadata when present and valid; otherwise it falls back to generic safe
|
|
91
|
+
discovery with reduced confidence and clear adapter-absence warnings.
|
|
90
92
|
`route-trace` validates a project adapter when present, uses adapter-declared safe paths
|
|
91
93
|
when enabled, and statically reports verified route files, inferred route declarations,
|
|
92
94
|
skipped items, and not-verified runtime-dependent route classes.
|
|
@@ -117,6 +119,50 @@ The installed CLI does not run target project builds or tests, perform runtime c
|
|
|
117
119
|
deploy, migrate, mutate services or processes, or read `.env` files. Project adapters
|
|
118
120
|
narrow context; they do not grant additional power or weaken shared restrictions.
|
|
119
121
|
|
|
122
|
+
## Machine-Readable Output
|
|
123
|
+
|
|
124
|
+
Every public CLI command accepts optional `--json` for OpenClaw-style tool callers:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
coding-agent-skills repo-map /path/to/project --json
|
|
128
|
+
coding-agent-skills validate-pack --json
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The default human-readable output is unchanged. JSON output is sanitized and includes:
|
|
132
|
+
|
|
133
|
+
- `success`
|
|
134
|
+
- `status`
|
|
135
|
+
- `tool`
|
|
136
|
+
- `command`
|
|
137
|
+
- `skillId`
|
|
138
|
+
- `packageVersion`
|
|
139
|
+
- `mode`
|
|
140
|
+
- `changedState`
|
|
141
|
+
- `summary`
|
|
142
|
+
- `findings`
|
|
143
|
+
- `warnings`
|
|
144
|
+
- `risks`
|
|
145
|
+
- `skipped`
|
|
146
|
+
- `notVerified`
|
|
147
|
+
- `refusedBehavior`
|
|
148
|
+
- `adapter`
|
|
149
|
+
- `recommendedNextAction`
|
|
150
|
+
- `safety`
|
|
151
|
+
- `exitCode`
|
|
152
|
+
- `exitCodeMeaning`
|
|
153
|
+
|
|
154
|
+
Exit-code semantics:
|
|
155
|
+
|
|
156
|
+
- `0`: handled execution path, including complete, partial, blocked, or controlled audit result
|
|
157
|
+
- `2`: usage error
|
|
158
|
+
- `3`: safety refusal
|
|
159
|
+
- `4`: missing required input or file
|
|
160
|
+
- `5`: unexpected internal or runtime failure
|
|
161
|
+
|
|
162
|
+
OpenClaw or another orchestrator should own memory, routing, permissions, scheduling,
|
|
163
|
+
chat/user interaction, and workflow state. This package remains a read-only external tool
|
|
164
|
+
and evidence producer.
|
|
165
|
+
|
|
120
166
|
## Local Command Surface
|
|
121
167
|
|
|
122
168
|
From the shared skill repository root, the same wrapper can be used directly:
|
package/package.json
CHANGED
package/runs/skill-runs.md
CHANGED
|
@@ -277,6 +277,23 @@ This file records bounded maintainer-loop runs. Entries must not contain secrets
|
|
|
277
277
|
API calls, no provider CLI execution, no package installs, no builds, no tests, no
|
|
278
278
|
runtime checks, no service mutation, no deployments, no migrations, and no target-project
|
|
279
279
|
mutation.
|
|
280
|
+
- Validation commands: full source validation, package dry-run, tarball smoke, registry
|
|
281
|
+
install smoke, npm exec, and GitHub Release verification.
|
|
282
|
+
- Result: passed; `v0.2.15` was committed, pushed, tagged, published to npm, smoke-tested
|
|
283
|
+
from the registry, and released on GitHub.
|
|
284
|
+
- Commit/tag/push status: complete.
|
|
285
|
+
|
|
286
|
+
## implementation-v0.2.16-openclaw-json-output
|
|
287
|
+
|
|
288
|
+
- Run ID: `implementation-v0.2.16-openclaw-json-output`
|
|
289
|
+
- Repository: `/home/oneclickwebsitedesignfactory/coding-agent-skills`
|
|
290
|
+
- Command used: `bounded approval for OpenClaw-compatible JSON output and exit-code contract`
|
|
291
|
+
- Files changed: CLI wrapper, release tests, usage/release/testing docs, changelog,
|
|
292
|
+
roadmap, work ledger, run log, and package metadata.
|
|
293
|
+
- Safety boundary: read-only machine-readable output only; no `.env` reads, no OpenClaw
|
|
294
|
+
modification, no OpenClaw plugin code, no cron or memory integration, no real project
|
|
295
|
+
repository mutation, no target-project builds/tests/runtime checks, no deployments, no
|
|
296
|
+
migrations, and no dependency changes.
|
|
280
297
|
- Validation commands: pending final release validation matrix.
|
|
281
298
|
- Result: pass pending final publication evidence.
|
|
282
299
|
- Commit/tag/push status: pending approved release workflow.
|
|
@@ -30,6 +30,87 @@ const REFUSED_BEHAVIOR = [
|
|
|
30
30
|
"no project writes",
|
|
31
31
|
];
|
|
32
32
|
|
|
33
|
+
const GENERIC_IGNORED_PATHS = [
|
|
34
|
+
".git",
|
|
35
|
+
".env",
|
|
36
|
+
".env.*",
|
|
37
|
+
".next",
|
|
38
|
+
".nuxt",
|
|
39
|
+
".output",
|
|
40
|
+
".turbo",
|
|
41
|
+
".cache",
|
|
42
|
+
"node_modules",
|
|
43
|
+
"dist",
|
|
44
|
+
"build",
|
|
45
|
+
"coverage",
|
|
46
|
+
"validation-output",
|
|
47
|
+
"supabase/.temp",
|
|
48
|
+
"secrets",
|
|
49
|
+
"tokens",
|
|
50
|
+
"credentials",
|
|
51
|
+
"private",
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const GENERIC_DOCUMENTATION_PRECEDENCE = [
|
|
55
|
+
"README.md",
|
|
56
|
+
"docs/README.md",
|
|
57
|
+
"docs/kb/README.md",
|
|
58
|
+
"AGENTS.md",
|
|
59
|
+
"CLAUDE.md",
|
|
60
|
+
"RUNBOOK.md",
|
|
61
|
+
"CONTRIBUTING.md",
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const GENERIC_SAFE_READ_PATHS = [
|
|
65
|
+
"README.md",
|
|
66
|
+
"docs",
|
|
67
|
+
"app",
|
|
68
|
+
"apps",
|
|
69
|
+
"src",
|
|
70
|
+
"lib",
|
|
71
|
+
"pages",
|
|
72
|
+
"components",
|
|
73
|
+
"server",
|
|
74
|
+
"api",
|
|
75
|
+
"routes",
|
|
76
|
+
"services",
|
|
77
|
+
"packages",
|
|
78
|
+
"schemas",
|
|
79
|
+
"scripts",
|
|
80
|
+
"tests",
|
|
81
|
+
"package.json",
|
|
82
|
+
"tsconfig.json",
|
|
83
|
+
"vite.config.ts",
|
|
84
|
+
"vite.config.js",
|
|
85
|
+
"next.config.ts",
|
|
86
|
+
"next.config.js",
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const GENERIC_ROOT_MARKERS = [
|
|
90
|
+
"README.md",
|
|
91
|
+
"package.json",
|
|
92
|
+
"pnpm-lock.yaml",
|
|
93
|
+
"package-lock.json",
|
|
94
|
+
"yarn.lock",
|
|
95
|
+
"bun.lockb",
|
|
96
|
+
"tsconfig.json",
|
|
97
|
+
"vite.config.ts",
|
|
98
|
+
"vite.config.js",
|
|
99
|
+
"next.config.ts",
|
|
100
|
+
"next.config.js",
|
|
101
|
+
"pyproject.toml",
|
|
102
|
+
"Cargo.toml",
|
|
103
|
+
"go.mod",
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const PACKAGE_MANAGER_MARKERS = [
|
|
107
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
108
|
+
["package-lock.json", "npm"],
|
|
109
|
+
["yarn.lock", "yarn"],
|
|
110
|
+
["bun.lockb", "bun"],
|
|
111
|
+
["package.json", "npm-compatible"],
|
|
112
|
+
];
|
|
113
|
+
|
|
33
114
|
function inside(root, candidate) {
|
|
34
115
|
const relative = path.relative(root, candidate);
|
|
35
116
|
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
@@ -90,6 +171,28 @@ function describeApprovedPath(projectRoot, relativePath) {
|
|
|
90
171
|
};
|
|
91
172
|
}
|
|
92
173
|
|
|
174
|
+
function existingPathRecords(projectRoot, candidates) {
|
|
175
|
+
return candidates
|
|
176
|
+
.map((relativePath) => describeApprovedPath(projectRoot, relativePath))
|
|
177
|
+
.filter((record) => record.status === "present");
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function genericRootMarkers(projectRoot) {
|
|
181
|
+
return GENERIC_ROOT_MARKERS
|
|
182
|
+
.map((relativePath) => describeApprovedPath(projectRoot, relativePath))
|
|
183
|
+
.filter((record) => record.status === "present")
|
|
184
|
+
.map((record) => ({
|
|
185
|
+
kind: record.type,
|
|
186
|
+
path: record.path,
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function genericPackageManagers(projectRoot) {
|
|
191
|
+
return PACKAGE_MANAGER_MARKERS
|
|
192
|
+
.filter(([relativePath]) => describeApprovedPath(projectRoot, relativePath).status === "present")
|
|
193
|
+
.map(([, manager]) => manager);
|
|
194
|
+
}
|
|
195
|
+
|
|
93
196
|
function gitSummary(projectRoot) {
|
|
94
197
|
const summary = {
|
|
95
198
|
root: null,
|
|
@@ -174,30 +277,92 @@ function loadRepoMapAdapters(loaded) {
|
|
|
174
277
|
|
|
175
278
|
export function buildAdapterRepoMapReport(projectRootInput, options = {}) {
|
|
176
279
|
const coreRoot = path.resolve(options.coreRoot ?? DEFAULT_CORE_ROOT);
|
|
177
|
-
const
|
|
178
|
-
if (!
|
|
280
|
+
const loaded = readProjectAdapterDeclaration(projectRootInput);
|
|
281
|
+
if (!loaded.ok) {
|
|
282
|
+
if (loaded.codes.length === 1 && loaded.codes[0] === "missing-project-declaration") {
|
|
283
|
+
const projectRoot = fs.realpathSync(path.resolve(projectRootInput));
|
|
284
|
+
const git = gitSummary(projectRoot);
|
|
285
|
+
return {
|
|
286
|
+
ok: true,
|
|
287
|
+
status: "complete",
|
|
288
|
+
coreVersion: PILOT_VERSION,
|
|
289
|
+
projectRoot,
|
|
290
|
+
declarationPath: "not present",
|
|
291
|
+
adapterRoot: "not present",
|
|
292
|
+
projectId: "not declared",
|
|
293
|
+
adapterIds: [],
|
|
294
|
+
manifestPaths: [],
|
|
295
|
+
enabledSkills: ["repo-map"],
|
|
296
|
+
adapter: {
|
|
297
|
+
present: false,
|
|
298
|
+
enabled: false,
|
|
299
|
+
mode: "generic-safe-discovery",
|
|
300
|
+
codes: ["missing-project-declaration"],
|
|
301
|
+
},
|
|
302
|
+
adapterPresent: false,
|
|
303
|
+
mode: "generic-safe-discovery",
|
|
304
|
+
confidence: "reduced",
|
|
305
|
+
rootMarkers: genericRootMarkers(projectRoot),
|
|
306
|
+
maximumDepth: 2,
|
|
307
|
+
scope: "generic-project-root",
|
|
308
|
+
requireApprovalOutsideScope: true,
|
|
309
|
+
documentationPrecedence: existingPathRecords(projectRoot, GENERIC_DOCUMENTATION_PRECEDENCE),
|
|
310
|
+
safeReadPaths: existingPathRecords(projectRoot, GENERIC_SAFE_READ_PATHS),
|
|
311
|
+
ignoredPaths: GENERIC_IGNORED_PATHS,
|
|
312
|
+
requiredEvidence: [
|
|
313
|
+
"repository root markers",
|
|
314
|
+
"bounded generic file/directory inventory",
|
|
315
|
+
"adapter absence evidence",
|
|
316
|
+
],
|
|
317
|
+
packageManagers: unique(genericPackageManagers(projectRoot)),
|
|
318
|
+
git,
|
|
319
|
+
warnings: unique([
|
|
320
|
+
...git.warnings,
|
|
321
|
+
"adapterPresent: false",
|
|
322
|
+
"mode: generic-safe-discovery",
|
|
323
|
+
"reduced confidence; adapter-provided project intent is unavailable",
|
|
324
|
+
"no project adapter declaration found; repo-map used generic bounded discovery",
|
|
325
|
+
"no target project build, test, runtime, deployment, migration, package, or secret-reading command was performed",
|
|
326
|
+
"no secrets read",
|
|
327
|
+
]),
|
|
328
|
+
refusedBehavior: REFUSED_BEHAVIOR,
|
|
329
|
+
validation: {
|
|
330
|
+
ok: false,
|
|
331
|
+
status: "failed",
|
|
332
|
+
acceptedAdapters: 0,
|
|
333
|
+
acceptedSkills: [],
|
|
334
|
+
codes: ["missing-project-declaration"],
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
179
338
|
return {
|
|
180
339
|
ok: false,
|
|
181
340
|
status: "failed",
|
|
182
|
-
codes:
|
|
183
|
-
validation
|
|
341
|
+
codes: loaded.codes,
|
|
342
|
+
validation: {
|
|
343
|
+
ok: false,
|
|
344
|
+
status: "failed",
|
|
345
|
+
acceptedAdapters: 0,
|
|
346
|
+
acceptedSkills: [],
|
|
347
|
+
codes: loaded.codes,
|
|
348
|
+
},
|
|
184
349
|
};
|
|
185
350
|
}
|
|
186
|
-
|
|
351
|
+
|
|
352
|
+
const validation = validateProjectAdapters(loaded.projectRoot, { coreRoot });
|
|
353
|
+
if (!validation.ok) {
|
|
187
354
|
return {
|
|
188
355
|
ok: false,
|
|
189
356
|
status: "failed",
|
|
190
|
-
codes:
|
|
357
|
+
codes: validation.codes,
|
|
191
358
|
validation,
|
|
192
359
|
};
|
|
193
360
|
}
|
|
194
|
-
|
|
195
|
-
const loaded = readProjectAdapterDeclaration(projectRootInput);
|
|
196
|
-
if (!loaded.ok) {
|
|
361
|
+
if (!validation.acceptedSkills.includes("repo-map")) {
|
|
197
362
|
return {
|
|
198
363
|
ok: false,
|
|
199
364
|
status: "failed",
|
|
200
|
-
codes:
|
|
365
|
+
codes: ["repo-map-not-enabled"],
|
|
201
366
|
validation,
|
|
202
367
|
};
|
|
203
368
|
}
|
|
@@ -246,6 +411,15 @@ export function buildAdapterRepoMapReport(projectRootInput, options = {}) {
|
|
|
246
411
|
adapterIds: adapters.map((adapter) => adapter.manifest.adapterId).sort(),
|
|
247
412
|
manifestPaths: adapters.map((adapter) => adapter.manifestPath).sort(),
|
|
248
413
|
enabledSkills: ["repo-map"],
|
|
414
|
+
adapter: {
|
|
415
|
+
present: true,
|
|
416
|
+
enabled: true,
|
|
417
|
+
mode: "adapter-limited",
|
|
418
|
+
codes: [],
|
|
419
|
+
},
|
|
420
|
+
adapterPresent: true,
|
|
421
|
+
mode: "adapter-limited",
|
|
422
|
+
confidence: "adapter-declared",
|
|
249
423
|
rootMarkers,
|
|
250
424
|
maximumDepth,
|
|
251
425
|
scope: "declared-project-root",
|
|
@@ -294,10 +468,16 @@ export function renderAdapterRepoMapReport(report) {
|
|
|
294
468
|
`Project root: ${redactSensitiveText(report.projectRoot)}`,
|
|
295
469
|
`Declaration: ${report.declarationPath}`,
|
|
296
470
|
`Adapter root: ${report.adapterRoot}`,
|
|
297
|
-
`Adapter IDs: ${report.adapterIds.join(", ")}`,
|
|
298
|
-
`Adapter manifests: ${report.manifestPaths.join(", ")}`,
|
|
471
|
+
`Adapter IDs: ${report.adapterIds.length ? report.adapterIds.join(", ") : "none"}`,
|
|
472
|
+
`Adapter manifests: ${report.manifestPaths.length ? report.manifestPaths.join(", ") : "none"}`,
|
|
299
473
|
`Enabled skills: ${report.enabledSkills.join(", ")}`,
|
|
300
474
|
"",
|
|
475
|
+
"## Adapter Scope",
|
|
476
|
+
`- Adapter present: ${report.adapterPresent ? "yes" : "no"}`,
|
|
477
|
+
`- Repo-map enabled: ${report.adapter?.enabled ? "yes" : "generic fallback"}`,
|
|
478
|
+
`- Mode: ${report.mode}`,
|
|
479
|
+
`- Confidence: ${report.confidence}`,
|
|
480
|
+
"",
|
|
301
481
|
"## Git State",
|
|
302
482
|
`- Git root: ${redactSensitiveText(report.git.root ?? "not detected")}`,
|
|
303
483
|
`- Branch state: ${redactSensitiveText(report.git.branchState ?? "not detected")}`,
|
|
@@ -334,6 +514,8 @@ export function renderAdapterRepoMapReport(report) {
|
|
|
334
514
|
...formatList("Refused Behavior", report.refusedBehavior),
|
|
335
515
|
"",
|
|
336
516
|
"No target project build, test, runtime, deployment, migration, package installation, or secret-file read was performed.",
|
|
517
|
+
"No project commands beyond safe repository metadata inspection were performed.",
|
|
518
|
+
"No secrets were read.",
|
|
337
519
|
];
|
|
338
520
|
|
|
339
521
|
return lines.join("\n");
|
package/scripts/test-pack.mjs
CHANGED
|
@@ -221,6 +221,39 @@ function snapshotAbsoluteDirectory(directory) {
|
|
|
221
221
|
return digest.digest("hex");
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
function assertOpenClawJsonContract(value, command, packageVersion = "0.2.17") {
|
|
225
|
+
assert.equal(value.tool, "coding-agent-skills");
|
|
226
|
+
assert.equal(value.command, command);
|
|
227
|
+
assert.equal(value.packageVersion, packageVersion);
|
|
228
|
+
assert.equal(value.success, true);
|
|
229
|
+
assert.match(value.status, /^(complete|partial|blocked|failed|empty)$/);
|
|
230
|
+
assert.equal(value.changedState, false);
|
|
231
|
+
assert.equal(value.safety?.readOnly, true);
|
|
232
|
+
assert.equal(value.safety?.secretsRead, false);
|
|
233
|
+
assert.equal(value.safety?.targetCommandsRun, false);
|
|
234
|
+
assert.equal(value.safety?.mutationsPerformed, false);
|
|
235
|
+
assert.equal(value.exitCode, 0);
|
|
236
|
+
assert.equal(value.exitCodeMeaning, "handled");
|
|
237
|
+
assert.ok(value.recommendedNextAction);
|
|
238
|
+
assert.equal(typeof value.recommendedNextAction.label, "string");
|
|
239
|
+
assert.equal(typeof value.recommendedNextAction.reason, "string");
|
|
240
|
+
assert.equal(typeof value.recommendedNextAction.requiresApproval, "boolean");
|
|
241
|
+
for (const key of [
|
|
242
|
+
"summary",
|
|
243
|
+
"findings",
|
|
244
|
+
"warnings",
|
|
245
|
+
"risks",
|
|
246
|
+
"skipped",
|
|
247
|
+
"notVerified",
|
|
248
|
+
"refusedBehavior",
|
|
249
|
+
]) {
|
|
250
|
+
assert.ok(Array.isArray(value[key]), `${command}.${key} must be an array`);
|
|
251
|
+
}
|
|
252
|
+
const encoded = JSON.stringify(value);
|
|
253
|
+
assert.doesNotMatch(encoded, /github_pat_|ghp_|Authorization:\s*Bearer|BEGIN .*PRIVATE KEY/i);
|
|
254
|
+
assert.doesNotMatch(encoded, /\/home\/oneclickwebsitedesignfactory\//);
|
|
255
|
+
}
|
|
256
|
+
|
|
224
257
|
const manifestSchema = readJson("schemas/skill-manifest.schema.json");
|
|
225
258
|
const policySchema = readJson("schemas/command-policy.schema.json");
|
|
226
259
|
const adapterSchema = readJson("schemas/project-adapter.schema.json");
|
|
@@ -345,6 +378,10 @@ test("local CLI maps approved commands to existing safe scripts", () => {
|
|
|
345
378
|
["repo-map", path.join(fixtureRoot, "project-adapter-installation", "valid-exact-pin")],
|
|
346
379
|
/# Adapter-Aware Repo Map/,
|
|
347
380
|
],
|
|
381
|
+
[
|
|
382
|
+
["repo-map", path.join(fixtureRoot, "sample-repo")],
|
|
383
|
+
/generic-safe-discovery/,
|
|
384
|
+
],
|
|
348
385
|
[
|
|
349
386
|
["route-trace", path.join(fixtureRoot, "route-trace", "static-project")],
|
|
350
387
|
/# Route Trace Report/,
|
|
@@ -394,10 +431,69 @@ test("local CLI maps approved commands to existing safe scripts", () => {
|
|
|
394
431
|
assert.match(unknown.stderr, /unknown command: deploy/);
|
|
395
432
|
});
|
|
396
433
|
|
|
434
|
+
test("local CLI emits OpenClaw-compatible JSON for public commands", () => {
|
|
435
|
+
const cliPath = path.join(root, "bin", "coding-agent-skills");
|
|
436
|
+
const fixtureRoot = path.join(root, "tests", "fixtures");
|
|
437
|
+
const githubHandoffFixture = createGitFixture(
|
|
438
|
+
path.join("tests", "fixtures", "github-handoff", "static-project"),
|
|
439
|
+
);
|
|
440
|
+
fs.appendFileSync(path.join(githubHandoffFixture, "README.md"), "\nLocal handoff change.\n");
|
|
441
|
+
|
|
442
|
+
const commands = [
|
|
443
|
+
["validate-pack"],
|
|
444
|
+
["validate-adapters", path.join(fixtureRoot, "external-adapters", "valid-basic")],
|
|
445
|
+
[
|
|
446
|
+
"validate-project",
|
|
447
|
+
path.join(fixtureRoot, "project-adapter-installation", "valid-exact-pin"),
|
|
448
|
+
],
|
|
449
|
+
["repo-map", path.join(fixtureRoot, "project-adapter-installation", "valid-exact-pin")],
|
|
450
|
+
["repo-map", path.join(fixtureRoot, "sample-repo")],
|
|
451
|
+
["route-trace", path.join(fixtureRoot, "route-trace", "static-project")],
|
|
452
|
+
["env-audit", path.join(fixtureRoot, "env-audit", "static-project")],
|
|
453
|
+
["secret-audit", path.join(fixtureRoot, "secret-audit", "static-project")],
|
|
454
|
+
["api-contract-audit", path.join(fixtureRoot, "api-contract-audit", "static-project")],
|
|
455
|
+
["migration-review", path.join(fixtureRoot, "migration-review", "static-project")],
|
|
456
|
+
["github-handoff", githubHandoffFixture],
|
|
457
|
+
["deployment-preflight", path.join(fixtureRoot, "deployment-preflight", "static-project")],
|
|
458
|
+
];
|
|
459
|
+
|
|
460
|
+
for (const args of commands) {
|
|
461
|
+
const result = spawnSync(cliPath, [...args, "--json"], {
|
|
462
|
+
cwd: root,
|
|
463
|
+
encoding: "utf8",
|
|
464
|
+
stdio: "pipe",
|
|
465
|
+
});
|
|
466
|
+
assert.equal(result.status, 0, `${args.join(" ")}\n${result.stderr}`);
|
|
467
|
+
assert.equal(result.stderr, "");
|
|
468
|
+
const parsed = JSON.parse(result.stdout);
|
|
469
|
+
assertOpenClawJsonContract(parsed, args[0]);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const partial = spawnSync(
|
|
473
|
+
cliPath,
|
|
474
|
+
[
|
|
475
|
+
"deployment-preflight",
|
|
476
|
+
path.join(fixtureRoot, "project-adapter-installation", "valid-exact-pin"),
|
|
477
|
+
"--json",
|
|
478
|
+
],
|
|
479
|
+
{
|
|
480
|
+
cwd: root,
|
|
481
|
+
encoding: "utf8",
|
|
482
|
+
stdio: "pipe",
|
|
483
|
+
},
|
|
484
|
+
);
|
|
485
|
+
assert.equal(partial.status, 0);
|
|
486
|
+
const parsedPartial = JSON.parse(partial.stdout);
|
|
487
|
+
assertOpenClawJsonContract(parsedPartial, "deployment-preflight");
|
|
488
|
+
assert.equal(parsedPartial.status, "partial");
|
|
489
|
+
assert.ok(parsedPartial.skipped.length > 0);
|
|
490
|
+
assert.ok(parsedPartial.refusedBehavior.includes("no deployments"));
|
|
491
|
+
});
|
|
492
|
+
|
|
397
493
|
test("npm package metadata is public-ready and dependency-free", () => {
|
|
398
494
|
const packageJson = readJson("package.json");
|
|
399
495
|
assert.equal(packageJson.name, "coding-agent-skills");
|
|
400
|
-
assert.equal(packageJson.version, "0.2.
|
|
496
|
+
assert.equal(packageJson.version, "0.2.17");
|
|
401
497
|
assert.equal(
|
|
402
498
|
packageJson.description,
|
|
403
499
|
"Evidence-first, read-only coding-agent skills and project adapter tooling.",
|
|
@@ -1678,6 +1774,42 @@ test("adapter-aware repo-map consumes validated project adapter metadata", () =>
|
|
|
1678
1774
|
assert.match(rendered, /No target project build, test, runtime, deployment/);
|
|
1679
1775
|
});
|
|
1680
1776
|
|
|
1777
|
+
test("repo-map supports generic safe discovery without a project adapter", () => {
|
|
1778
|
+
const report = buildAdapterRepoMapReport(
|
|
1779
|
+
path.join(root, "tests", "fixtures", "sample-repo"),
|
|
1780
|
+
{ coreRoot: root },
|
|
1781
|
+
);
|
|
1782
|
+
|
|
1783
|
+
assert.equal(report.ok, true, report.codes?.join(","));
|
|
1784
|
+
assert.equal(report.adapter.present, false);
|
|
1785
|
+
assert.equal(report.adapter.enabled, false);
|
|
1786
|
+
assert.equal(report.mode, "generic-safe-discovery");
|
|
1787
|
+
assert.equal(report.confidence, "reduced");
|
|
1788
|
+
assert.equal(report.adapterPresent, false);
|
|
1789
|
+
assert.ok(report.validation.codes.includes("missing-project-declaration"));
|
|
1790
|
+
assert.ok(report.warnings.includes("adapterPresent: false"));
|
|
1791
|
+
assert.ok(report.warnings.includes("mode: generic-safe-discovery"));
|
|
1792
|
+
assert.ok(report.warnings.includes("no secrets read"));
|
|
1793
|
+
assert.ok(report.ignoredPaths.includes(".env"));
|
|
1794
|
+
assert.ok(report.ignoredPaths.includes(".env.*"));
|
|
1795
|
+
assert.ok(
|
|
1796
|
+
report.documentationPrecedence.some((record) => record.path === "README.md"),
|
|
1797
|
+
);
|
|
1798
|
+
|
|
1799
|
+
const rendered = renderAdapterRepoMapReport(report);
|
|
1800
|
+
assert.match(rendered, /Adapter present: no/);
|
|
1801
|
+
assert.match(rendered, /Mode: generic-safe-discovery/);
|
|
1802
|
+
assert.match(rendered, /Confidence: reduced/);
|
|
1803
|
+
assert.match(rendered, /No secrets were read/);
|
|
1804
|
+
|
|
1805
|
+
const cli = adapterRepoMapCliResult(path.join(root, "tests", "fixtures", "sample-repo"), {
|
|
1806
|
+
coreRoot: root,
|
|
1807
|
+
});
|
|
1808
|
+
assert.equal(cli.exitCode, 0);
|
|
1809
|
+
assert.equal(cli.stream, "stdout");
|
|
1810
|
+
assert.match(cli.lines.join("\n"), /generic-safe-discovery/);
|
|
1811
|
+
});
|
|
1812
|
+
|
|
1681
1813
|
test("adapter-aware repo-map fails closed without repo-map compatibility", () => {
|
|
1682
1814
|
const fixtureRoot = path.join(
|
|
1683
1815
|
root,
|
|
@@ -687,8 +687,8 @@ if (packageJson) {
|
|
|
687
687
|
if (packageJson.name !== "coding-agent-skills") {
|
|
688
688
|
failures.push("package.json has unexpected package name");
|
|
689
689
|
}
|
|
690
|
-
if (packageJson.version !== "0.2.
|
|
691
|
-
failures.push("package.json version must be 0.2.
|
|
690
|
+
if (packageJson.version !== "0.2.17") {
|
|
691
|
+
failures.push("package.json version must be 0.2.17 for public package validation");
|
|
692
692
|
}
|
|
693
693
|
if (packageJson.type !== "module") failures.push("package.json must preserve ESM mode");
|
|
694
694
|
if (packageJson.private !== false) {
|
package/work-ledger.md
CHANGED
|
@@ -10,8 +10,10 @@
|
|
|
10
10
|
- First external project-owned adapter adoption completed for `/home/oneclickwebsitedesignfactory/tax-lien-platform` at candidate commit `c548b1a6cbb3455a70b89d0e301e22435bfccac9`.
|
|
11
11
|
- The adopted adapter is `repo-map` only, docs/metadata-only, and contains no commands, runtime checks, build/test/package behavior, platform/deployment behavior, or secret-aware behavior.
|
|
12
12
|
- The shared repository does not contain real adapter manifests; real project adapters remain owned by their project repositories.
|
|
13
|
-
- Public npm package release `v0.2.
|
|
13
|
+
- Public npm package release `v0.2.16` exposes the dependency-free
|
|
14
14
|
`coding-agent-skills` CLI under MIT license.
|
|
15
|
+
- `v0.2.16` adds optional OpenClaw-compatible `--json` output and documented
|
|
16
|
+
exit-code semantics for every public CLI command.
|
|
15
17
|
- `route-trace` is implemented as an audit-only static route tracing skill.
|
|
16
18
|
- `env-audit` is implemented as an audit-only value-free environment variable name mapping
|
|
17
19
|
skill.
|
|
@@ -28,14 +30,14 @@
|
|
|
28
30
|
|
|
29
31
|
## Last Completed Version
|
|
30
32
|
|
|
31
|
-
`v0.2.
|
|
33
|
+
`v0.2.16`
|
|
32
34
|
|
|
33
35
|
## Current Recommended Milestone
|
|
34
36
|
|
|
35
|
-
The
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
`cloudflare-preflight-skill`.
|
|
37
|
+
The OpenClaw-compatible JSON output contract is the current release milestone. After
|
|
38
|
+
`v0.2.16` is published and verified, continue the remaining read-only skill wave one
|
|
39
|
+
release at a time unless a real safety, validation, publication, or authentication
|
|
40
|
+
boundary appears. The next approved wave item remains `cloudflare-preflight-skill`.
|
|
39
41
|
|
|
40
42
|
## Allowed Next Actions
|
|
41
43
|
|
|
@@ -122,9 +124,20 @@ No autonomous maintainer-loop run has been recorded yet.
|
|
|
122
124
|
- Implemented milestone: `deployment-preflight` audit-only static deployment readiness
|
|
123
125
|
evidence skill and CLI command.
|
|
124
126
|
- Required permission: `builder-mode-skill-implementation`
|
|
125
|
-
- Validation result:
|
|
127
|
+
- Validation result: passed; `v0.2.15` commit, tag, npm publication, registry smoke,
|
|
128
|
+
npm exec, and GitHub Release completed
|
|
129
|
+
- Next recommended milestone: implement the OpenClaw-compatible JSON output and exit-code
|
|
130
|
+
contract before continuing to `cloudflare-preflight-skill`.
|
|
131
|
+
|
|
132
|
+
### 2026-07-03T15:00:00Z
|
|
133
|
+
|
|
134
|
+
- Latest tag observed: `v0.2.15`
|
|
135
|
+
- Implemented milestone: OpenClaw-compatible optional `--json` output and documented
|
|
136
|
+
exit-code semantics for every public CLI command.
|
|
137
|
+
- Required permission: `orchestrator-output-contract`
|
|
138
|
+
- Validation result: pass pending final release validation matrix
|
|
126
139
|
- Next recommended milestone: continue builder-mode wave with `cloudflare-preflight-skill`
|
|
127
|
-
after `v0.2.
|
|
140
|
+
after `v0.2.16` publication completes.
|
|
128
141
|
|
|
129
142
|
### 2026-07-03T12:00:00Z
|
|
130
143
|
|