create-cmp-cli 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-cmp-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Create production mobile apps (Android + iOS, one Kotlin codebase) with AI — the delivery harness for Compose Multiplatform, the current generation of cross-platform (Google-backed KMP, iOS stable since May 2025). A deterministic, non-interactive generator that scaffolds a green-building app in minutes, then holds AI-driven changes to a machine-enforced verify lane with a committed evidence receipt. Every app carries a device-free UI preview loop (real screens rendered headlessly on save; changed-screen attribution and compile-error surfacing for coding agents, a live gallery for humans) plus agent-first docs (CLAUDE.md + AGENTS.md). Installs the `create-cmp` command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@create-cmp/harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "The create-cmp verify lane — the machine-owned harness code every stamped app carries byte-identical: evidence receipts, spec coverage, approvals, conformance reporting, golden trees, a11y, and the preview/inspector libs. Dependency-free ESM, vendored into each generated project so the lane runs offline with no install step, and content-hashed so a receipt can name the exact lane that issued it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/verify.mjs",
|
|
@@ -53,17 +53,58 @@ import path from "node:path";
|
|
|
53
53
|
*/
|
|
54
54
|
export const HARNESS_DIRS = ["qa", "qa/lib"];
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* The lane's OWN tests, when a project carries them (payment-blueprint's
|
|
58
|
+
* qa/test/**, 2026-09-03). Every `.mjs` under it, recursively, is in the
|
|
59
|
+
* region: the tests that prove the gates are locked WITH the gates, or the
|
|
60
|
+
* lane can no longer tell you that the suite vouching for its verdicts is the
|
|
61
|
+
* one it was locked with — a narrower claim in the same words, which is the
|
|
62
|
+
* failure this mechanism exists to prevent. A Compose app has no qa/test and
|
|
63
|
+
* its region (and lock) is unchanged.
|
|
64
|
+
*/
|
|
65
|
+
export const HARNESS_TEST_DIR = "qa/test";
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* DECLARATIONS the lane READS to decide what it attests — locked for the same
|
|
69
|
+
* reason verify.mjs is. payment-blueprint's planted proof (2026-09-03): remove
|
|
70
|
+
* one entry from qa/verified-surface.json and 203 files — the whole backend —
|
|
71
|
+
* stop being attested; the next lane run mints a fresh receipt over the smaller
|
|
72
|
+
* surface, harnessIntegrity PASS, nothing in the chain says the coverage moved.
|
|
73
|
+
* An edited checker and an edited definition of what the checker looks at are
|
|
74
|
+
* the same attack. State a lane WRITES (approvals.json, comments.json, the
|
|
75
|
+
* journal, evidence/) stays out — that is the EXCLUDED_PREFIXES distinction.
|
|
76
|
+
* Neither file exists in a Compose app by default; its region is unchanged.
|
|
77
|
+
*/
|
|
78
|
+
export const HARNESS_DECLARATIONS = ["qa/verified-surface.json", "qa/harness-manifest.json"];
|
|
79
|
+
|
|
56
80
|
/**
|
|
57
81
|
* Is this project-relative path part of the machine-owned harness region?
|
|
58
82
|
* @param {string} relPath project-relative path, "/"-separated
|
|
59
83
|
* @returns {boolean}
|
|
60
84
|
*/
|
|
61
85
|
export function isHarnessFile(relPath) {
|
|
62
|
-
if (typeof relPath !== "string"
|
|
86
|
+
if (typeof relPath !== "string") return false;
|
|
87
|
+
if (HARNESS_DECLARATIONS.includes(relPath)) return true;
|
|
88
|
+
if (!relPath.endsWith(".mjs")) return false;
|
|
89
|
+
if (relPath.startsWith(`${HARNESS_TEST_DIR}/`)) return true;
|
|
63
90
|
const dir = relPath.includes("/") ? relPath.slice(0, relPath.lastIndexOf("/")) : "";
|
|
64
91
|
return HARNESS_DIRS.includes(dir);
|
|
65
92
|
}
|
|
66
93
|
|
|
94
|
+
function walkMjs(dirAbs, relPrefix, out) {
|
|
95
|
+
let entries;
|
|
96
|
+
try {
|
|
97
|
+
entries = fs.readdirSync(dirAbs, { withFileTypes: true });
|
|
98
|
+
} catch {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
for (const ent of entries) {
|
|
102
|
+
const rel = `${relPrefix}/${ent.name}`;
|
|
103
|
+
if (ent.isDirectory()) walkMjs(path.join(dirAbs, ent.name), rel, out);
|
|
104
|
+
else if (ent.isFile() && isHarnessFile(rel)) out.push(rel);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
67
108
|
/**
|
|
68
109
|
* Every machine-owned file present under `root`, as project-relative posix
|
|
69
110
|
* paths, sorted — so the list (and any hash over it) is deterministic.
|
|
@@ -85,7 +126,14 @@ export function listHarnessFiles(root) {
|
|
|
85
126
|
if (isHarnessFile(rel)) found.push(rel);
|
|
86
127
|
}
|
|
87
128
|
}
|
|
88
|
-
|
|
129
|
+
walkMjs(path.join(root, HARNESS_TEST_DIR), HARNESS_TEST_DIR, found);
|
|
130
|
+
// A declaration directly under qa/ is already seen by the scan above (it is
|
|
131
|
+
// a harness file by name); the explicit loop covers one that lives deeper.
|
|
132
|
+
// Deduplicated so no path is hashed twice.
|
|
133
|
+
for (const rel of HARNESS_DECLARATIONS) {
|
|
134
|
+
if (fs.existsSync(path.join(root, ...rel.split("/")))) found.push(rel);
|
|
135
|
+
}
|
|
136
|
+
return [...new Set(found)].sort();
|
|
89
137
|
}
|
|
90
138
|
|
|
91
139
|
/** sha256 of one file's bytes, hex. */
|
|
@@ -53,17 +53,58 @@ import path from "node:path";
|
|
|
53
53
|
*/
|
|
54
54
|
export const HARNESS_DIRS = ["qa", "qa/lib"];
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* The lane's OWN tests, when a project carries them (payment-blueprint's
|
|
58
|
+
* qa/test/**, 2026-09-03). Every `.mjs` under it, recursively, is in the
|
|
59
|
+
* region: the tests that prove the gates are locked WITH the gates, or the
|
|
60
|
+
* lane can no longer tell you that the suite vouching for its verdicts is the
|
|
61
|
+
* one it was locked with — a narrower claim in the same words, which is the
|
|
62
|
+
* failure this mechanism exists to prevent. A Compose app has no qa/test and
|
|
63
|
+
* its region (and lock) is unchanged.
|
|
64
|
+
*/
|
|
65
|
+
export const HARNESS_TEST_DIR = "qa/test";
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* DECLARATIONS the lane READS to decide what it attests — locked for the same
|
|
69
|
+
* reason verify.mjs is. payment-blueprint's planted proof (2026-09-03): remove
|
|
70
|
+
* one entry from qa/verified-surface.json and 203 files — the whole backend —
|
|
71
|
+
* stop being attested; the next lane run mints a fresh receipt over the smaller
|
|
72
|
+
* surface, harnessIntegrity PASS, nothing in the chain says the coverage moved.
|
|
73
|
+
* An edited checker and an edited definition of what the checker looks at are
|
|
74
|
+
* the same attack. State a lane WRITES (approvals.json, comments.json, the
|
|
75
|
+
* journal, evidence/) stays out — that is the EXCLUDED_PREFIXES distinction.
|
|
76
|
+
* Neither file exists in a Compose app by default; its region is unchanged.
|
|
77
|
+
*/
|
|
78
|
+
export const HARNESS_DECLARATIONS = ["qa/verified-surface.json", "qa/harness-manifest.json"];
|
|
79
|
+
|
|
56
80
|
/**
|
|
57
81
|
* Is this project-relative path part of the machine-owned harness region?
|
|
58
82
|
* @param {string} relPath project-relative path, "/"-separated
|
|
59
83
|
* @returns {boolean}
|
|
60
84
|
*/
|
|
61
85
|
export function isHarnessFile(relPath) {
|
|
62
|
-
if (typeof relPath !== "string"
|
|
86
|
+
if (typeof relPath !== "string") return false;
|
|
87
|
+
if (HARNESS_DECLARATIONS.includes(relPath)) return true;
|
|
88
|
+
if (!relPath.endsWith(".mjs")) return false;
|
|
89
|
+
if (relPath.startsWith(`${HARNESS_TEST_DIR}/`)) return true;
|
|
63
90
|
const dir = relPath.includes("/") ? relPath.slice(0, relPath.lastIndexOf("/")) : "";
|
|
64
91
|
return HARNESS_DIRS.includes(dir);
|
|
65
92
|
}
|
|
66
93
|
|
|
94
|
+
function walkMjs(dirAbs, relPrefix, out) {
|
|
95
|
+
let entries;
|
|
96
|
+
try {
|
|
97
|
+
entries = fs.readdirSync(dirAbs, { withFileTypes: true });
|
|
98
|
+
} catch {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
for (const ent of entries) {
|
|
102
|
+
const rel = `${relPrefix}/${ent.name}`;
|
|
103
|
+
if (ent.isDirectory()) walkMjs(path.join(dirAbs, ent.name), rel, out);
|
|
104
|
+
else if (ent.isFile() && isHarnessFile(rel)) out.push(rel);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
67
108
|
/**
|
|
68
109
|
* Every machine-owned file present under `root`, as project-relative posix
|
|
69
110
|
* paths, sorted — so the list (and any hash over it) is deterministic.
|
|
@@ -85,7 +126,14 @@ export function listHarnessFiles(root) {
|
|
|
85
126
|
if (isHarnessFile(rel)) found.push(rel);
|
|
86
127
|
}
|
|
87
128
|
}
|
|
88
|
-
|
|
129
|
+
walkMjs(path.join(root, HARNESS_TEST_DIR), HARNESS_TEST_DIR, found);
|
|
130
|
+
// A declaration directly under qa/ is already seen by the scan above (it is
|
|
131
|
+
// a harness file by name); the explicit loop covers one that lives deeper.
|
|
132
|
+
// Deduplicated so no path is hashed twice.
|
|
133
|
+
for (const rel of HARNESS_DECLARATIONS) {
|
|
134
|
+
if (fs.existsSync(path.join(root, ...rel.split("/")))) found.push(rel);
|
|
135
|
+
}
|
|
136
|
+
return [...new Set(found)].sort();
|
|
89
137
|
}
|
|
90
138
|
|
|
91
139
|
/** sha256 of one file's bytes, hex. */
|