multimodel-dev-os 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/docs/.vitepress/config.js +1 -1
- package/docs/index.md +5 -5
- package/docs/npm-publishing.md +5 -5
- package/docs/package-safety.md +16 -0
- package/docs/public/llms-full.txt +1 -1
- package/docs/public/llms.txt +1 -1
- package/docs/public/sitemap.xml +5 -0
- package/docs/release-policy.md +6 -5
- package/docs/testing.md +12 -2
- package/docs/v3-roadmap.md +8 -2
- package/package.json +5 -2
- package/scripts/build-cli.js +45 -3
- package/scripts/check-build-fresh.js +52 -0
- package/scripts/install.ps1 +1 -1
- package/scripts/install.sh +1 -1
- package/scripts/verify.js +128 -12
- package/scripts/verify.sh +10 -0
- package/src/catalog/loader.js +117 -0
- package/src/cli/args.js +118 -0
- package/src/cli/help.js +60 -0
- package/src/cli/main.js +5718 -0
- package/src/core/globals.js +52 -0
- package/src/core/hashes.js +15 -0
- package/src/core/policy.js +36 -0
- package/src/core/security.js +61 -0
- package/src/core/yaml.js +136 -0
- package/src/plugin/manifest.js +95 -0
- package/src/registry/sources.js +40 -0
- package/src/registry/validation.js +45 -0
- package/tests/README.md +37 -0
- package/tests/fixtures/README.md +22 -0
- package/tests/fixtures/custom-template-example/README.md +10 -0
- package/tests/fixtures/proposals/approved-append-line.md +28 -0
- package/tests/fixtures/proposals/approved-create-file.md +29 -0
- package/tests/fixtures/proposals/approved-replace-text.md +30 -0
- package/tests/fixtures/proposals/existing-create-file-no-overwrite.md +29 -0
- package/tests/fixtures/proposals/no-operations.md +18 -0
- package/tests/fixtures/proposals/path-traversal.md +29 -0
- package/tests/fixtures/proposals/pending-proposal.md +29 -0
- package/tests/fixtures/proposals/protected-path.md +29 -0
- package/tests/fixtures/proposals/replace-multiple-without-allow.md +30 -0
- package/tests/fixtures/registry-overrides/README.md +20 -0
- package/tests/smoke/README.md +37 -0
- package/tests/smoke/cli-smoke.md +49 -0
- package/tests/unit/build-output.test.js +40 -0
- package/tests/unit/catalog-loader.test.js +44 -0
- package/tests/unit/path-safety.test.js +62 -0
- package/tests/unit/plugin-manifest.test.js +94 -0
- package/tests/unit/prepublish-guard.test.js +35 -0
- package/tests/unit/registry-policy.test.js +46 -0
- package/tests/unit/registry-url-validation.test.js +64 -0
- package/tests/unit/yaml.test.js +92 -0
package/README.md
CHANGED
|
@@ -161,6 +161,7 @@ npx multimodel-dev-os@latest handoff build
|
|
|
161
161
|
| **v3.0.1** | Registry UX & Policy Safety Patch | ✅ Released |
|
|
162
162
|
| **v3.0.2** | Registry Sync Security Hotfix | ✅ Released |
|
|
163
163
|
| **v3.1.0** | Modular Source Layout + Formal Unit Tests | ✅ Released |
|
|
164
|
+
| **v3.2.0** | Stable Modular Build + Package Governance | ✅ Released |
|
|
164
165
|
|
|
165
166
|
|
|
166
167
|
**[Full Roadmap →](https://rizvee.github.io/multimodel-dev-os/v3-roadmap)**
|
|
@@ -32,7 +32,7 @@ export default {
|
|
|
32
32
|
'license': 'https://opensource.org/licenses/MIT',
|
|
33
33
|
'url': 'https://github.com/rizvee/multimodel-dev-os',
|
|
34
34
|
'downloadUrl': 'https://www.npmjs.com/package/multimodel-dev-os',
|
|
35
|
-
'softwareVersion': '3.
|
|
35
|
+
'softwareVersion': '3.2.0',
|
|
36
36
|
'description': 'Portable, vendor-neutral AI Developer OS for multi-agent coding workflows.'
|
|
37
37
|
})
|
|
38
38
|
]
|
package/docs/index.md
CHANGED
|
@@ -214,12 +214,12 @@ You use **Cursor** for autocomplete, **Claude Code** for terminal ops, **Gemini*
|
|
|
214
214
|
|
|
215
215
|
<div class="highlight-box">
|
|
216
216
|
|
|
217
|
-
### 🆕 What's New in
|
|
217
|
+
### 🆕 What's New in v3.2
|
|
218
218
|
|
|
219
|
-
-
|
|
220
|
-
-
|
|
221
|
-
-
|
|
222
|
-
-
|
|
219
|
+
- 🏗️ **Modular Source Layout** — Pure ES modules under `src/` cleanly isolate core, CLI routing, catalog engines, and security systems.
|
|
220
|
+
- 🧪 **Formal Unit Testing** — Integrated a Vitest-powered test suite executing 45 unit tests covering path safety, sandbox boundaries, and policy validation.
|
|
221
|
+
- ⚙️ **Build Freshness Guard** — Automated build auditing checks to ensure the generated CLI binary is always synchronized with the source modules.
|
|
222
|
+
- 🛡️ **Hardened Registry Sync** — Strict HTTPS-only URL syntax parsing and argument-isolated sub-process executors prevent shell injection vulnerabilities.
|
|
223
223
|
|
|
224
224
|
</div>
|
|
225
225
|
|
package/docs/npm-publishing.md
CHANGED
|
@@ -12,13 +12,13 @@ Before publishing, always test the built package locally by compiling a compress
|
|
|
12
12
|
```bash
|
|
13
13
|
npm pack
|
|
14
14
|
```
|
|
15
|
-
This creates a file named like `multimodel-dev-os-3.0.
|
|
15
|
+
This creates a file named like `multimodel-dev-os-3.2.0.tgz` in your directory root.
|
|
16
16
|
|
|
17
17
|
2. **Verify bundle contents:**
|
|
18
18
|
Create an empty temporary workspace, extract the tarball, and confirm that only required scaffold folders are included (no `.github/`, test configurations, or local system files):
|
|
19
19
|
```bash
|
|
20
20
|
mkdir -p /tmp/package-test && cd /tmp/package-test
|
|
21
|
-
tar -xzf /path/to/multimodel-dev-os-3.0.
|
|
21
|
+
tar -xzf /path/to/multimodel-dev-os-3.2.0.tgz
|
|
22
22
|
ls -la package/
|
|
23
23
|
```
|
|
24
24
|
|
|
@@ -78,17 +78,17 @@ Execute these validation actions strictly in sequence before triggering a releas
|
|
|
78
78
|
## 4. Prepublish Safety Guard
|
|
79
79
|
|
|
80
80
|
> [!IMPORTANT]
|
|
81
|
-
> **v3.0
|
|
81
|
+
> **v3.2.0 is the active stable release.** NPM publishing is live.
|
|
82
82
|
|
|
83
83
|
### Source vs. Registry Strategy
|
|
84
|
-
* **GitHub main branch (Source)**: Contains the current stable `v3.0
|
|
84
|
+
* **GitHub main branch (Source)**: Contains the current stable `v3.2.0` codebase.
|
|
85
85
|
* **npm latest (Registry)**: Pulled and installed globally or via npx.
|
|
86
86
|
|
|
87
87
|
### Prepublish Safety Guard
|
|
88
88
|
To prevent accidental `npm publish` executions on developer environments, a local validation script has been added to package hooks. If you run `npm publish`, it is blocked by default.
|
|
89
89
|
|
|
90
90
|
To bypass this check during approved release windows:
|
|
91
|
-
1. Ensure the version in `package.json` is a valid stable major version >= 2 (e.g., v3.0
|
|
91
|
+
1. Ensure the version in `package.json` is a valid stable major version >= 2 (e.g., v3.2.0).
|
|
92
92
|
2. Run publication with the override env variable:
|
|
93
93
|
```powershell
|
|
94
94
|
# PowerShell
|
package/docs/package-safety.md
CHANGED
|
@@ -35,3 +35,19 @@ A security hotfix has been applied in `v3.0.2` to secure the registry synchroniz
|
|
|
35
35
|
* **Registry URL Sanitization:** Enforces strict validation of remote registry URLs using Node's `URL` parser. URLs must use HTTPS by default. Control characters, credentials, spaces, quotes, and shell metacharacters are strictly rejected.
|
|
36
36
|
* **Upgrade Guidance:** Users running `v3.0.0` or `v3.0.1` must upgrade to `v3.0.2` immediately.
|
|
37
37
|
* **Safety Boundaries Preserved:** Remote registries remain disabled by default, sync operations are cache-only (never installing or running plugins), and conflict checks on sensitive files (`.env`, `.npmrc`, package configuration files) are strictly enforced.
|
|
38
|
+
|
|
39
|
+
## Package Governance Policies
|
|
40
|
+
|
|
41
|
+
1. **Zero Runtime Dependencies:**
|
|
42
|
+
* The runtime package is strictly zero-dependency to ensure minimal installation footprint and maximum security.
|
|
43
|
+
* All compilation, testing, and dev tools (e.g., `esbuild`, `vitest`, `vitepress`) are restricted to `devDependencies` only.
|
|
44
|
+
|
|
45
|
+
2. **Open-Source Transparency:**
|
|
46
|
+
* The complete modular source files (`src/`) and testing suites (`tests/`) are intentionally included in the published NPM package, allowing for visual auditing, validation, and debugging.
|
|
47
|
+
|
|
48
|
+
3. **Manual NPM Publishing Only:**
|
|
49
|
+
* Automated publishing via CI is disabled. NPM publish is performed manually by maintainers using verification guards.
|
|
50
|
+
|
|
51
|
+
4. **Milestone-Based Releases:**
|
|
52
|
+
* Patch-level releases are kept internal by default for stabilization sprints (such as `v3.2.0-prep`).
|
|
53
|
+
* Public updates are batched into stable, fully-audited milestone releases (e.g., `v3.2.0`). Critical security hotfixes are the only exception.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# MultiModel Dev OS — Comprehensive AI Assistant Discoverability Guide (v3.
|
|
1
|
+
# MultiModel Dev OS — Comprehensive AI Assistant Discoverability Guide (v3.2.0 Stable Release)
|
|
2
2
|
|
|
3
3
|
MultiModel Dev OS is a repository-level porting specification designed to align context and instructions across diverse developer tools and AI models.
|
|
4
4
|
|
package/docs/public/llms.txt
CHANGED
package/docs/public/sitemap.xml
CHANGED
package/docs/release-policy.md
CHANGED
|
@@ -34,9 +34,10 @@ No package shall be merged or released without:
|
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
|
37
|
-
## 4. Release
|
|
37
|
+
## 4. Release Strategy & Staging Controls
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
To ensure developer stability and avoid package version fatigue, MultiModel Dev OS enforces the following distribution strategy:
|
|
40
|
+
- **Internal Stabilization Sprints**: Patch-level work (e.g. bug fixes, refactoring, test stability, documentation formatting) is treated as internal by default and committed directly to `main` without bumping versions or publishing to npm.
|
|
41
|
+
- **Batched Milestone Releases**: Public npm and GitHub releases are batched into stable milestone releases (e.g., `v3.2.0`, `v3.3.0`, `v4.0.0`).
|
|
42
|
+
- **Security Hotfix Exceptions**: Critical security hotfixes (e.g., remote execution, command injection remediations) bypass the batching policy and are published immediately as public patch releases.
|
|
43
|
+
- **Staging Verification**: To test new configurations prior to publishing, package the bundle locally (`npm pack`) and run CLI checkups in clean test directories (`C:\mmdo-smoke`).
|
package/docs/testing.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# MultiModel Dev OS — Testing Guide (v3.
|
|
1
|
+
# MultiModel Dev OS — Testing Guide (v3.2.0+)
|
|
2
2
|
|
|
3
3
|
This document outlines the testing strategy, tools, and execution processes for MultiModel Dev OS.
|
|
4
4
|
|
|
@@ -114,10 +114,20 @@ To ensure the npm package functions flawlessly after installation, we run a loca
|
|
|
114
114
|
```
|
|
115
115
|
3. **Install the generated tarball locally**:
|
|
116
116
|
```bash
|
|
117
|
-
npm install F:\multimodel-dev-os\multimodel-dev-os-3.
|
|
117
|
+
npm install F:\multimodel-dev-os\multimodel-dev-os-3.2.0.tgz --no-audit --no-fund
|
|
118
118
|
```
|
|
119
119
|
4. **Validate npx invocation**:
|
|
120
120
|
```bash
|
|
121
121
|
npx multimodel-dev-os --help
|
|
122
122
|
npx multimodel-dev-os doctor
|
|
123
123
|
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 6. Maintainer Guidelines
|
|
128
|
+
|
|
129
|
+
For contributors and maintainers modifying the codebase:
|
|
130
|
+
1. **Always edit source modules** located under `src/`. Do NOT make manual edits to `bin/multimodel-dev-os.js` directly, as it will be overwritten during compilation.
|
|
131
|
+
2. **Execute the build script** via `npm run build` after completing modifications to compile the single-file binary.
|
|
132
|
+
3. **Execute Vitest unit tests** (`npm test`) to ensure all core modules pass verification gates in isolation.
|
|
133
|
+
4. **Execute release verification** (`npm run verify`) to run the strict verification pipeline (250+ assertions check compiled binary, folder layouts, sitemaps, etc.).
|
package/docs/v3-roadmap.md
CHANGED
|
@@ -7,12 +7,18 @@ This document outlines the development path, completed milestones, and future pl
|
|
|
7
7
|
## 1. Current Status
|
|
8
8
|
|
|
9
9
|
> [!IMPORTANT]
|
|
10
|
-
> **v3.
|
|
10
|
+
> **v3.2.0 is the active stable release** on the public npm registry. All features below marked ✅ are shipped and production-ready.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
## 2. Completed Milestones
|
|
15
15
|
|
|
16
|
+
### v3.2.0 — Stable Modular Build + Package Governance ✅
|
|
17
|
+
- **Build Freshness Auditing**: Integrated `check-build-fresh.js` to ensure the generated single-file CLI binary matches standard ES modules under `src/` dynamically.
|
|
18
|
+
- **Hardened Package Governance**: Configured the NPM manifest (`package.json`) to include the modular source folder (`src/`) and unit test suites (`tests/unit/`) for developer auditability, while verifying the strict exclusion of sensitive and temporary files.
|
|
19
|
+
- **Cross-Platform CI Pipeline**: Configured a complete multi-platform CI verification matrix on GitHub Actions covering Windows, Linux, and macOS across Node.js versions `20.x` and `22.x`.
|
|
20
|
+
- **Harden Build & Verification Gates**: Applied post-build validations asserting shebang count uniqueness, warning headers, and URL shell-injection safety, while expanding integration audits to 269 assertions.
|
|
21
|
+
|
|
16
22
|
### v3.1.0 — Modular Source Layout + Formal Unit Tests ✅
|
|
17
23
|
- **Modular Source Layout**: Refactored the monolithic CLI structure into isolated, clean modules under `src/` (core, registry, catalog, plugin, cli).
|
|
18
24
|
- **Programmatic Compiler**: Programmed `scripts/build-cli.js` using `esbuild` to compile modules into a single zero-dependency executable (`bin/multimodel-dev-os.js`) with shebang preservation.
|
|
@@ -74,7 +80,7 @@ All releases follow this strict publishing checklist:
|
|
|
74
80
|
|
|
75
81
|
---
|
|
76
82
|
|
|
77
|
-
## 4. Upcoming: v3.
|
|
83
|
+
## 4. Upcoming: v3.3.0 stable candidate — Asymmetric Key Signatures
|
|
78
84
|
|
|
79
85
|
* **Asymmetric Key Signatures**: Cryptographic signature validation for remote registries using public/private key pairs.
|
|
80
86
|
* **Decentralized Trust Anchors**: Trust anchors configuration allowing teams to pin public keys of verified catalog authors.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "multimodel-dev-os",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"bin": {
|
|
5
5
|
"multimodel-dev-os": "bin/multimodel-dev-os.js"
|
|
6
6
|
},
|
|
@@ -35,12 +35,15 @@
|
|
|
35
35
|
"!docs/.vitepress/cache/",
|
|
36
36
|
"examples/",
|
|
37
37
|
"bin/",
|
|
38
|
+
"src/",
|
|
39
|
+
"tests/",
|
|
38
40
|
"assets/"
|
|
39
41
|
],
|
|
40
42
|
"scripts": {
|
|
41
43
|
"build": "node scripts/build-cli.js",
|
|
44
|
+
"check:build": "node scripts/check-build-fresh.js",
|
|
42
45
|
"test": "vitest run",
|
|
43
|
-
"verify": "npm run build && npm test && node scripts/verify.js",
|
|
46
|
+
"verify": "npm run check:build && npm test && node scripts/verify.js",
|
|
44
47
|
"verify:bash": "bash scripts/verify.sh",
|
|
45
48
|
"test:cli": "node bin/multimodel-dev-os.js verify",
|
|
46
49
|
"pack:template": "bash scripts/pack-template.sh",
|
package/scripts/build-cli.js
CHANGED
|
@@ -1,16 +1,58 @@
|
|
|
1
1
|
import esbuild from 'esbuild';
|
|
2
|
+
import { existsSync, chmodSync, readFileSync } from 'fs';
|
|
3
|
+
|
|
4
|
+
const entryPoint = 'src/cli/main.js';
|
|
5
|
+
const outfile = 'bin/multimodel-dev-os.js';
|
|
6
|
+
|
|
7
|
+
if (!existsSync(entryPoint)) {
|
|
8
|
+
console.error(`[ERROR] Entrypoint file not found: ${entryPoint}`);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
2
11
|
|
|
3
12
|
esbuild.build({
|
|
4
|
-
entryPoints: [
|
|
13
|
+
entryPoints: [entryPoint],
|
|
5
14
|
bundle: true,
|
|
6
15
|
platform: 'node',
|
|
7
16
|
format: 'esm',
|
|
8
|
-
outfile:
|
|
17
|
+
outfile: outfile,
|
|
9
18
|
banner: {
|
|
10
19
|
js: `#!/usr/bin/env node\n// Generated from src/. Do not edit directly.\n`
|
|
11
20
|
}
|
|
12
21
|
}).then(() => {
|
|
13
|
-
|
|
22
|
+
// Post-build validation & hardening
|
|
23
|
+
try {
|
|
24
|
+
const content = readFileSync(outfile, 'utf8');
|
|
25
|
+
|
|
26
|
+
// 1. Check shebang counts
|
|
27
|
+
const shebangMatches = content.match(/^#!/g) || [];
|
|
28
|
+
const totalShebangs = (content.match(/#!/g) || []).length;
|
|
29
|
+
|
|
30
|
+
// We expect exactly one shebang at the very top
|
|
31
|
+
if (totalShebangs !== 1 || !content.startsWith('#!/usr/bin/env node')) {
|
|
32
|
+
console.error('[ERROR] Compiled output has invalid shebang configuration.');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 2. Check warning header
|
|
37
|
+
if (!content.includes('// Generated from src/. Do not edit directly.')) {
|
|
38
|
+
console.error('[ERROR] Compiled output is missing the generation warning header.');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 3. Check for unsafe URL interpolation patterns
|
|
43
|
+
if (content.includes("mod.get('${targetUrl}'") || (content.includes('execSync(`node -e "') && content.includes('${targetUrl}'))) {
|
|
44
|
+
console.error('[ERROR] Compiled output contains unsafe registry URL interpolation!');
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 4. Set execution permissions (0755)
|
|
49
|
+
chmodSync(outfile, 0o755);
|
|
50
|
+
|
|
51
|
+
console.log('Build succeeded.');
|
|
52
|
+
} catch (e) {
|
|
53
|
+
console.error('Post-build verification failed:', e.message);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
14
56
|
}).catch((err) => {
|
|
15
57
|
console.error('Build failed:', err);
|
|
16
58
|
process.exit(1);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import esbuild from 'esbuild';
|
|
2
|
+
import { readFileSync, unlinkSync, existsSync } from 'fs';
|
|
3
|
+
|
|
4
|
+
const entryPoint = 'src/cli/main.js';
|
|
5
|
+
const currentFile = 'bin/multimodel-dev-os.js';
|
|
6
|
+
const tempFile = 'bin/multimodel-dev-os.tmp.js';
|
|
7
|
+
|
|
8
|
+
if (!existsSync(entryPoint)) {
|
|
9
|
+
console.error(`[ERROR] Entrypoint file not found: ${entryPoint}`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!existsSync(currentFile)) {
|
|
14
|
+
console.error(`[ERROR] Current build file not found: ${currentFile}`);
|
|
15
|
+
console.error('Generated CLI is stale. Run npm run build and commit bin/multimodel-dev-os.js.');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
await esbuild.build({
|
|
21
|
+
entryPoints: [entryPoint],
|
|
22
|
+
bundle: true,
|
|
23
|
+
platform: 'node',
|
|
24
|
+
format: 'esm',
|
|
25
|
+
outfile: tempFile,
|
|
26
|
+
banner: {
|
|
27
|
+
js: `#!/usr/bin/env node\n// Generated from src/. Do not edit directly.\n`
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const currentContent = readFileSync(currentFile, 'utf8');
|
|
32
|
+
const tempContent = readFileSync(tempFile, 'utf8');
|
|
33
|
+
|
|
34
|
+
// Clean up temp file immediately
|
|
35
|
+
unlinkSync(tempFile);
|
|
36
|
+
|
|
37
|
+
const normalize = str => str.replace(/\r\n/g, '\n');
|
|
38
|
+
|
|
39
|
+
if (normalize(currentContent) !== normalize(tempContent)) {
|
|
40
|
+
console.error('[ERROR] Generated CLI is stale. Run npm run build and commit bin/multimodel-dev-os.js.');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
console.log('Generated CLI is fresh.');
|
|
45
|
+
process.exit(0);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
console.error('Build freshness check failed:', err);
|
|
48
|
+
if (existsSync(tempFile)) {
|
|
49
|
+
unlinkSync(tempFile);
|
|
50
|
+
}
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
package/scripts/install.ps1
CHANGED
package/scripts/install.sh
CHANGED
package/scripts/verify.js
CHANGED
|
@@ -189,6 +189,21 @@ checkFile('scripts/pack-template.sh');
|
|
|
189
189
|
checkFile('scripts/prepublish-guard.js');
|
|
190
190
|
checkFile('bin/multimodel-dev-os.js');
|
|
191
191
|
|
|
192
|
+
// --- Modular Source Files ---
|
|
193
|
+
console.log('\nModular Source Files:');
|
|
194
|
+
checkFile('src/cli/main.js');
|
|
195
|
+
checkFile('src/cli/args.js');
|
|
196
|
+
checkFile('src/cli/help.js');
|
|
197
|
+
checkFile('src/core/yaml.js');
|
|
198
|
+
checkFile('src/core/hashes.js');
|
|
199
|
+
checkFile('src/core/policy.js');
|
|
200
|
+
checkFile('src/core/security.js');
|
|
201
|
+
checkFile('src/core/globals.js');
|
|
202
|
+
checkFile('src/registry/validation.js');
|
|
203
|
+
checkFile('src/registry/sources.js');
|
|
204
|
+
checkFile('src/catalog/loader.js');
|
|
205
|
+
checkFile('src/plugin/manifest.js');
|
|
206
|
+
|
|
192
207
|
// --- GitHub Integration ---
|
|
193
208
|
console.log('\nGitHub Workflows:');
|
|
194
209
|
checkFile('.github/workflows/verify.yml');
|
|
@@ -549,7 +564,7 @@ try {
|
|
|
549
564
|
}
|
|
550
565
|
}
|
|
551
566
|
|
|
552
|
-
// Test 2: Allows version 3.
|
|
567
|
+
// Test 2: Allows version 3.2.0 with MMDO_ALLOW_PUBLISH=true
|
|
553
568
|
try {
|
|
554
569
|
const output = execSync('node scripts/prepublish-guard.js', {
|
|
555
570
|
cwd: projectRoot,
|
|
@@ -557,7 +572,7 @@ try {
|
|
|
557
572
|
encoding: 'utf8'
|
|
558
573
|
});
|
|
559
574
|
if (output.includes('Prepublish guard passed')) {
|
|
560
|
-
console.log(` ${GREEN}✓${NC} prepublish guard allows version 3.
|
|
575
|
+
console.log(` ${GREEN}✓${NC} prepublish guard allows version 3.2.0 when MMDO_ALLOW_PUBLISH=true`);
|
|
561
576
|
pass++;
|
|
562
577
|
} else {
|
|
563
578
|
console.error(` ${RED}✗${NC} prepublish guard passed but stdout missing success indicator`);
|
|
@@ -565,7 +580,7 @@ try {
|
|
|
565
580
|
}
|
|
566
581
|
} catch (err) {
|
|
567
582
|
const errText = err.stderr ? err.stderr.toString() : '';
|
|
568
|
-
console.error(` ${RED}✗${NC} prepublish guard blocked version 3.
|
|
583
|
+
console.error(` ${RED}✗${NC} prepublish guard blocked version 3.2.0: ${errText || err.message}`);
|
|
569
584
|
fail++;
|
|
570
585
|
}
|
|
571
586
|
|
|
@@ -579,12 +594,12 @@ try {
|
|
|
579
594
|
pass++;
|
|
580
595
|
}
|
|
581
596
|
|
|
582
|
-
// Test 4: Package.json version is exactly 3.
|
|
583
|
-
if (expectedVersion === '3.
|
|
584
|
-
console.log(` ${GREEN}✓${NC} package.json version is exactly 3.
|
|
597
|
+
// Test 4: Package.json version is exactly 3.2.0
|
|
598
|
+
if (expectedVersion === '3.2.0') {
|
|
599
|
+
console.log(` ${GREEN}✓${NC} package.json version is exactly 3.2.0`);
|
|
585
600
|
pass++;
|
|
586
601
|
} else {
|
|
587
|
-
console.error(` ${RED}✗${NC} package.json version is not 3.
|
|
602
|
+
console.error(` ${RED}✗${NC} package.json version is not 3.2.0 (found ${expectedVersion})`);
|
|
588
603
|
fail++;
|
|
589
604
|
}
|
|
590
605
|
} catch (e) {
|
|
@@ -592,6 +607,52 @@ try {
|
|
|
592
607
|
fail++;
|
|
593
608
|
}
|
|
594
609
|
|
|
610
|
+
// --- Post-build Generated CLI Checks ---
|
|
611
|
+
console.log('\nPost-build Generated CLI Checks:');
|
|
612
|
+
try {
|
|
613
|
+
// 0. Check build freshness
|
|
614
|
+
try {
|
|
615
|
+
execSync('node scripts/check-build-fresh.js', { cwd: projectRoot, stdio: 'ignore' });
|
|
616
|
+
console.log(` ${GREEN}✓${NC} generated bin matches current source layout`);
|
|
617
|
+
pass++;
|
|
618
|
+
} catch (err) {
|
|
619
|
+
console.error(` ${RED}✗${NC} generated bin is stale! Run 'npm run build' and commit bin/multimodel-dev-os.js`);
|
|
620
|
+
fail++;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const buildPath = join(projectRoot, 'bin', 'multimodel-dev-os.js');
|
|
624
|
+
const binContent = readFileSync(buildPath, 'utf8');
|
|
625
|
+
|
|
626
|
+
const totalShebangs = (binContent.match(/#!/g) || []).length;
|
|
627
|
+
if (binContent.startsWith('#!/usr/bin/env node') && totalShebangs === 1) {
|
|
628
|
+
console.log(` ${GREEN}✓${NC} generated bin has exactly one shebang at the top`);
|
|
629
|
+
pass++;
|
|
630
|
+
} else {
|
|
631
|
+
console.error(` ${RED}✗${NC} generated bin has invalid shebang layout (count: ${totalShebangs})`);
|
|
632
|
+
fail++;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (binContent.includes('// Generated from src/. Do not edit directly.')) {
|
|
636
|
+
console.log(` ${GREEN}✓${NC} generated bin has warning header`);
|
|
637
|
+
pass++;
|
|
638
|
+
} else {
|
|
639
|
+
console.error(` ${RED}✗${NC} generated bin is missing the warning header`);
|
|
640
|
+
fail++;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const hasUnsafeSync = binContent.includes("mod.get('${targetUrl}'") || (binContent.includes('execSync(`node -e "') && binContent.includes('${targetUrl}'));
|
|
644
|
+
if (!hasUnsafeSync && binContent.includes('execFileSync(process.execPath')) {
|
|
645
|
+
console.log(` ${GREEN}✓${NC} generated bin is free of unsafe URL interpolation and uses execFileSync`);
|
|
646
|
+
pass++;
|
|
647
|
+
} else {
|
|
648
|
+
console.error(` ${RED}✗${NC} generated bin fails safety scan (unsafe interpolation found)`);
|
|
649
|
+
fail++;
|
|
650
|
+
}
|
|
651
|
+
} catch (e) {
|
|
652
|
+
console.error(` ${RED}✗${NC} post-build generated CLI checks failed: ${e.message}`);
|
|
653
|
+
fail++;
|
|
654
|
+
}
|
|
655
|
+
|
|
595
656
|
// --- v2.8.0 / v2.8.1 Dashboard & Plugin Tests ---
|
|
596
657
|
console.log('\nRunning TUI Dashboard & Plugin Pre-Flight Tests...');
|
|
597
658
|
|
|
@@ -921,22 +982,77 @@ try {
|
|
|
921
982
|
fail++;
|
|
922
983
|
}
|
|
923
984
|
|
|
924
|
-
// Verify npm pack dry-run shows current version dynamically
|
|
985
|
+
// Verify npm pack dry-run shows current version dynamically and has clean hygiene
|
|
925
986
|
try {
|
|
926
|
-
const packOutput = execSync('npm pack --dry-run', { cwd: projectRoot, encoding: 'utf8'
|
|
927
|
-
|
|
987
|
+
const packOutput = execSync('npm pack --dry-run 2>&1', { cwd: projectRoot, encoding: 'utf8' });
|
|
988
|
+
const combinedOutput = packOutput;
|
|
989
|
+
|
|
990
|
+
const hasVersion = combinedOutput.includes(`multimodel-dev-os@${expectedVersion}`) || combinedOutput.includes(`multimodel-dev-os-${expectedVersion}.tgz`) || combinedOutput.includes(`version: ${expectedVersion}`);
|
|
991
|
+
if (hasVersion) {
|
|
928
992
|
console.log(` ${GREEN}✓${NC} npm pack --dry-run reports version ${expectedVersion}`);
|
|
929
993
|
pass++;
|
|
930
994
|
} else {
|
|
931
|
-
console.error(` ${RED}✗${NC} npm pack --dry-run did not report ${expectedVersion} in
|
|
995
|
+
console.error(` ${RED}✗${NC} npm pack --dry-run did not report ${expectedVersion} in output`);
|
|
996
|
+
fail++;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// Hygiene checks
|
|
1000
|
+
const lines = combinedOutput.split('\n');
|
|
1001
|
+
const files = lines
|
|
1002
|
+
.filter(l => l.includes('npm notice') && !l.includes('Tarball Details') && !l.includes('Tarball Filename') && !l.includes('package size:') && !l.includes('unpacked size:') && !l.includes('shasum:') && !l.includes('integrity:') && !l.includes('total files:'))
|
|
1003
|
+
.map(l => {
|
|
1004
|
+
const match = l.match(/npm notice\s+\d+(\.\d+)?[a-zA-Z]+\s+(.+)$/);
|
|
1005
|
+
return match ? match[2].trim() : '';
|
|
1006
|
+
})
|
|
1007
|
+
.filter(f => f !== '');
|
|
1008
|
+
|
|
1009
|
+
const hasSrc = files.some(f => f.startsWith('src/'));
|
|
1010
|
+
const hasTests = files.some(f => f.startsWith('tests/'));
|
|
1011
|
+
|
|
1012
|
+
if (hasSrc && hasTests) {
|
|
1013
|
+
console.log(` ${GREEN}✓${NC} npm pack includes 'src/' and 'tests/' directories`);
|
|
1014
|
+
pass++;
|
|
1015
|
+
} else {
|
|
1016
|
+
console.error(` ${RED}✗${NC} npm pack is missing 'src/' or 'tests/' directory`);
|
|
1017
|
+
fail++;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
const hasBlacklisted = files.some(f => f.includes('.npmrc') || f.includes('.env') || f.includes('node_modules') || f.endsWith('.tgz') || f.includes('coverage/'));
|
|
1021
|
+
if (!hasBlacklisted) {
|
|
1022
|
+
console.log(` ${GREEN}✓${NC} npm pack excludes sensitive and temporary files (.npmrc, .env, node_modules, .tgz, coverage)`);
|
|
1023
|
+
pass++;
|
|
1024
|
+
} else {
|
|
1025
|
+
console.error(` ${RED}✗${NC} npm pack contains blacklisted files!`);
|
|
932
1026
|
fail++;
|
|
933
1027
|
}
|
|
934
1028
|
} catch (e) {
|
|
935
1029
|
const stdErrOut = e.stderr ? e.stderr.toString() : '';
|
|
936
1030
|
const stdOutOut = e.stdout ? e.stdout.toString() : '';
|
|
937
|
-
|
|
1031
|
+
const combined = stdErrOut + '\n' + stdOutOut;
|
|
1032
|
+
|
|
1033
|
+
const hasVersion = combined.includes(`multimodel-dev-os@${expectedVersion}`) || combined.includes(`multimodel-dev-os-${expectedVersion}.tgz`) || combined.includes(`version: ${expectedVersion}`);
|
|
1034
|
+
if (hasVersion) {
|
|
938
1035
|
console.log(` ${GREEN}✓${NC} npm pack --dry-run reports version ${expectedVersion}`);
|
|
939
1036
|
pass++;
|
|
1037
|
+
|
|
1038
|
+
const hasSrc = combined.includes('src/') || combined.includes('src\\');
|
|
1039
|
+
const hasTests = combined.includes('tests/') || combined.includes('tests\\');
|
|
1040
|
+
if (hasSrc && hasTests) {
|
|
1041
|
+
console.log(` ${GREEN}✓${NC} npm pack includes 'src/' and 'tests/' directories`);
|
|
1042
|
+
pass++;
|
|
1043
|
+
} else {
|
|
1044
|
+
console.error(` ${RED}✗${NC} npm pack is missing 'src/' or 'tests/' directory`);
|
|
1045
|
+
fail++;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
const hasBlacklisted = combined.includes('.npmrc') || combined.includes('.env') || combined.includes('node_modules') || combined.includes('.tgz') || combined.includes('coverage/');
|
|
1049
|
+
if (!hasBlacklisted) {
|
|
1050
|
+
console.log(` ${GREEN}✓${NC} npm pack excludes sensitive and temporary files`);
|
|
1051
|
+
pass++;
|
|
1052
|
+
} else {
|
|
1053
|
+
console.error(` ${RED}✗${NC} npm pack contains blacklisted files!`);
|
|
1054
|
+
fail++;
|
|
1055
|
+
}
|
|
940
1056
|
} else {
|
|
941
1057
|
console.error(` ${RED}✗${NC} npm pack --dry-run failed or did not report ${expectedVersion}: ${e.message}`);
|
|
942
1058
|
fail++;
|
package/scripts/verify.sh
CHANGED
|
@@ -192,6 +192,8 @@ check_file "docs/cli-roadmap.md"
|
|
|
192
192
|
check_file "docs/faq.md"
|
|
193
193
|
check_file "docs/testing.md"
|
|
194
194
|
check_file "docs/npm-publishing.md"
|
|
195
|
+
check_file "docs/release-policy.md"
|
|
196
|
+
check_file "docs/package-safety.md"
|
|
195
197
|
|
|
196
198
|
# --- CLI & Packaging Pre-Flight Tests ---
|
|
197
199
|
echo ""
|
|
@@ -235,6 +237,14 @@ else
|
|
|
235
237
|
PASS=$((PASS + 1))
|
|
236
238
|
fi
|
|
237
239
|
|
|
240
|
+
if ! npm run check:build >/dev/null; then
|
|
241
|
+
echo -e " ${RED}✗${NC} Generated CLI is stale. Run npm run build."
|
|
242
|
+
FAIL=$((FAIL + 1))
|
|
243
|
+
else
|
|
244
|
+
echo -e " ${GREEN}✓${NC} Generated CLI is fresh"
|
|
245
|
+
PASS=$((PASS + 1))
|
|
246
|
+
fi
|
|
247
|
+
|
|
238
248
|
if ! node bin/multimodel-dev-os.js init --dry-run --force >/dev/null; then
|
|
239
249
|
echo -e " ${RED}✗${NC} node bin/multimodel-dev-os.js init --dry-run failed"
|
|
240
250
|
FAIL=$((FAIL + 1))
|