multimodel-dev-os 3.1.0 → 3.5.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.
Files changed (102) hide show
  1. package/.ai/policies/registry-policy.yaml +29 -1
  2. package/.ai/registries/trusted-keys.yaml +12 -0
  3. package/.ai/schema/registry-manifest.schema.json +31 -2
  4. package/.ai/schema/registry-policy.schema.json +37 -1
  5. package/.ai/schema/trusted-keys.schema.json +69 -0
  6. package/AGENTS.md +22 -26
  7. package/MEMORY.md +34 -11
  8. package/README.md +2 -1
  9. package/RUNBOOK.md +28 -36
  10. package/TASKS.md +15 -5
  11. package/bin/multimodel-dev-os.js +1366 -548
  12. package/docs/.vitepress/config.js +3 -1
  13. package/docs/architecture.md +3 -1
  14. package/docs/index.md +5 -5
  15. package/docs/npm-publishing.md +5 -5
  16. package/docs/package-safety.md +17 -0
  17. package/docs/public/llms-full.txt +5 -1
  18. package/docs/public/llms.txt +6 -1
  19. package/docs/public/sitemap.xml +15 -0
  20. package/docs/registry-policy.md +29 -1
  21. package/docs/registry-security.md +73 -6
  22. package/docs/registry-signing.md +70 -0
  23. package/docs/registry-sync.md +5 -2
  24. package/docs/registry-trust-store.md +66 -0
  25. package/docs/release-policy.md +6 -5
  26. package/docs/security-threat-model.md +96 -0
  27. package/docs/testing.md +25 -2
  28. package/docs/trusted-registries.md +1 -1
  29. package/docs/v3-roadmap.md +17 -6
  30. package/docs/v3.5.0-readiness.md +46 -0
  31. package/package.json +5 -2
  32. package/scripts/build-cli.js +45 -3
  33. package/scripts/check-build-fresh.js +52 -0
  34. package/scripts/install.ps1 +1 -1
  35. package/scripts/install.sh +1 -1
  36. package/scripts/verify.js +327 -14
  37. package/scripts/verify.sh +10 -0
  38. package/src/catalog/loader.js +117 -0
  39. package/src/cli/args.js +118 -0
  40. package/src/cli/help.js +60 -0
  41. package/src/cli/main.js +6263 -0
  42. package/src/core/globals.js +52 -0
  43. package/src/core/hashes.js +15 -0
  44. package/src/core/policy.js +44 -0
  45. package/src/core/security.js +61 -0
  46. package/src/core/yaml.js +136 -0
  47. package/src/plugin/manifest.js +95 -0
  48. package/src/registry/provenance.js +114 -0
  49. package/src/registry/signing.js +392 -0
  50. package/src/registry/sources.js +40 -0
  51. package/src/registry/trust-store.js +41 -0
  52. package/src/registry/validation.js +45 -0
  53. package/src/registry/verdict.js +51 -0
  54. package/tests/README.md +37 -0
  55. package/tests/fixtures/README.md +22 -0
  56. package/tests/fixtures/custom-template-example/README.md +10 -0
  57. package/tests/fixtures/proposals/approved-append-line.md +28 -0
  58. package/tests/fixtures/proposals/approved-create-file.md +29 -0
  59. package/tests/fixtures/proposals/approved-replace-text.md +30 -0
  60. package/tests/fixtures/proposals/existing-create-file-no-overwrite.md +29 -0
  61. package/tests/fixtures/proposals/no-operations.md +18 -0
  62. package/tests/fixtures/proposals/path-traversal.md +29 -0
  63. package/tests/fixtures/proposals/pending-proposal.md +29 -0
  64. package/tests/fixtures/proposals/protected-path.md +29 -0
  65. package/tests/fixtures/proposals/replace-multiple-without-allow.md +30 -0
  66. package/tests/fixtures/registry-overrides/README.md +20 -0
  67. package/tests/fixtures/signed-registries/README.md +4 -0
  68. package/tests/fixtures/signed-registries/revoked-key/catalog.yaml +8 -0
  69. package/tests/fixtures/signed-registries/revoked-key/expected-verdict.json +7 -0
  70. package/tests/fixtures/signed-registries/revoked-key/registry-manifest.yaml +14 -0
  71. package/tests/fixtures/signed-registries/tampered-manifest/catalog.yaml +8 -0
  72. package/tests/fixtures/signed-registries/tampered-manifest/expected-verdict.json +7 -0
  73. package/tests/fixtures/signed-registries/tampered-manifest/registry-manifest.yaml +14 -0
  74. package/tests/fixtures/signed-registries/trusted-keys.yaml +23 -0
  75. package/tests/fixtures/signed-registries/unsigned-remote-required/catalog.yaml +8 -0
  76. package/tests/fixtures/signed-registries/unsigned-remote-required/expected-verdict.json +7 -0
  77. package/tests/fixtures/signed-registries/unsigned-remote-required/registry-manifest.yaml +9 -0
  78. package/tests/fixtures/signed-registries/unsupported-algorithm/catalog.yaml +8 -0
  79. package/tests/fixtures/signed-registries/unsupported-algorithm/expected-verdict.json +7 -0
  80. package/tests/fixtures/signed-registries/unsupported-algorithm/registry-manifest.yaml +14 -0
  81. package/tests/fixtures/signed-registries/valid-signed-registry/catalog.yaml +8 -0
  82. package/tests/fixtures/signed-registries/valid-signed-registry/expected-verdict.json +7 -0
  83. package/tests/fixtures/signed-registries/valid-signed-registry/registry-manifest.yaml +14 -0
  84. package/tests/fixtures/signed-registries/wrong-key/catalog.yaml +8 -0
  85. package/tests/fixtures/signed-registries/wrong-key/expected-verdict.json +7 -0
  86. package/tests/fixtures/signed-registries/wrong-key/registry-manifest.yaml +14 -0
  87. package/tests/smoke/README.md +37 -0
  88. package/tests/smoke/cli-smoke.md +49 -0
  89. package/tests/unit/build-output.test.js +40 -0
  90. package/tests/unit/catalog-loader.test.js +44 -0
  91. package/tests/unit/path-safety.test.js +62 -0
  92. package/tests/unit/plugin-manifest.test.js +94 -0
  93. package/tests/unit/prepublish-guard.test.js +35 -0
  94. package/tests/unit/registry-e2e-signature-fixtures.test.js +288 -0
  95. package/tests/unit/registry-policy.test.js +52 -0
  96. package/tests/unit/registry-provenance.test.js +185 -0
  97. package/tests/unit/registry-public-signing.test.js +109 -0
  98. package/tests/unit/registry-signature-policy.test.js +100 -0
  99. package/tests/unit/registry-signing.test.js +193 -0
  100. package/tests/unit/registry-trust-store.test.js +133 -0
  101. package/tests/unit/registry-url-validation.test.js +64 -0
  102. package/tests/unit/yaml.test.js +92 -0
@@ -0,0 +1,29 @@
1
+ ---
2
+ id: proposal-20260611-000000
3
+ created_at: 2026-06-11T00:00:00Z
4
+ title: Approved Create File Proposal
5
+ problem: Missing custom templates in tests.
6
+ evidence: Directory is empty.
7
+ risk_level: low
8
+ affected_files:
9
+ - tests/fixtures/custom-template-example/new-file.md
10
+ suggested_change: Create a new markdown file.
11
+ verify_command: npm run verify
12
+ rollback_plan: rm tests/fixtures/custom-template-example/new-file.md
13
+ approval_status: approved
14
+ ---
15
+
16
+ # Approved Create File Proposal
17
+
18
+ ```json
19
+ {
20
+ "operations": [
21
+ {
22
+ "type": "create_file",
23
+ "path": "tests/fixtures/custom-template-example/new-file.md",
24
+ "content": "new file content\n",
25
+ "overwrite": true
26
+ }
27
+ ]
28
+ }
29
+ ```
@@ -0,0 +1,30 @@
1
+ ---
2
+ id: proposal-20260611-000002
3
+ created_at: 2026-06-11T00:00:02Z
4
+ title: Approved Replace Text Proposal
5
+ problem: Test replace text behavior.
6
+ evidence: N/A
7
+ risk_level: low
8
+ affected_files:
9
+ - tests/fixtures/custom-template-example/replace-target.md
10
+ suggested_change: Replace placeholder in the target.
11
+ verify_command: npm run verify
12
+ rollback_plan: git checkout -- tests/fixtures/custom-template-example/replace-target.md
13
+ approval_status: approved
14
+ ---
15
+
16
+ # Approved Replace Text Proposal
17
+
18
+ ```json
19
+ {
20
+ "operations": [
21
+ {
22
+ "type": "replace_text",
23
+ "path": "tests/fixtures/custom-template-example/replace-target.md",
24
+ "find": "placeholder",
25
+ "replace": "replaced content",
26
+ "allow_multiple": false
27
+ }
28
+ ]
29
+ }
30
+ ```
@@ -0,0 +1,29 @@
1
+ ---
2
+ id: proposal-20260611-000004
3
+ created_at: 2026-06-11T00:00:04Z
4
+ title: Existing Create File No Overwrite Proposal
5
+ problem: Test existing file without overwrite flag.
6
+ evidence: N/A
7
+ risk_level: low
8
+ affected_files:
9
+ - tests/fixtures/custom-template-example/existing.md
10
+ suggested_change: Create file without overwrite flag.
11
+ verify_command: npm run verify
12
+ rollback_plan: N/A
13
+ approval_status: approved
14
+ ---
15
+
16
+ # Existing Create File No Overwrite Proposal
17
+
18
+ ```json
19
+ {
20
+ "operations": [
21
+ {
22
+ "type": "create_file",
23
+ "path": "tests/fixtures/custom-template-example/existing.md",
24
+ "content": "new content\n",
25
+ "overwrite": false
26
+ }
27
+ ]
28
+ }
29
+ ```
@@ -0,0 +1,18 @@
1
+ ---
2
+ id: proposal-20260611-333333
3
+ created_at: 2026-06-11T33:33:33Z
4
+ title: No Operations Proposal
5
+ problem: Vague proposal without machine readable block.
6
+ evidence: N/A
7
+ risk_level: low
8
+ affected_files:
9
+ - README.md
10
+ suggested_change: Edit README.md manually.
11
+ verify_command: npm run verify
12
+ rollback_plan: git checkout -- README.md
13
+ approval_status: approved
14
+ ---
15
+
16
+ # No Operations Proposal
17
+
18
+ Please edit the README.md to describe the codebase in more detail.
@@ -0,0 +1,29 @@
1
+ ---
2
+ id: proposal-20260611-000003
3
+ created_at: 2026-06-11T00:00:03Z
4
+ title: Path Traversal Proposal
5
+ problem: Test directory traversal boundaries check.
6
+ evidence: N/A
7
+ risk_level: low
8
+ affected_files:
9
+ - ../outside.md
10
+ suggested_change: Modify file outside root.
11
+ verify_command: npm run verify
12
+ rollback_plan: N/A
13
+ approval_status: approved
14
+ ---
15
+
16
+ # Path Traversal Proposal
17
+
18
+ ```json
19
+ {
20
+ "operations": [
21
+ {
22
+ "type": "create_file",
23
+ "path": "../outside-file.md",
24
+ "content": "outside content\n",
25
+ "overwrite": true
26
+ }
27
+ ]
28
+ }
29
+ ```
@@ -0,0 +1,29 @@
1
+ ---
2
+ id: proposal-20260611-111111
3
+ created_at: 2026-06-11T11:11:11Z
4
+ title: Pending Proposal
5
+ problem: Test pending proposal.
6
+ evidence: N/A
7
+ risk_level: low
8
+ affected_files:
9
+ - tests/fixtures/custom-template-example/pending.md
10
+ suggested_change: Create pending file.
11
+ verify_command: npm run verify
12
+ rollback_plan: rm tests/fixtures/custom-template-example/pending.md
13
+ approval_status: pending
14
+ ---
15
+
16
+ # Pending Proposal
17
+
18
+ ```json
19
+ {
20
+ "operations": [
21
+ {
22
+ "type": "create_file",
23
+ "path": "tests/fixtures/custom-template-example/pending.md",
24
+ "content": "pending\n",
25
+ "overwrite": true
26
+ }
27
+ ]
28
+ }
29
+ ```
@@ -0,0 +1,29 @@
1
+ ---
2
+ id: proposal-20260611-222222
3
+ created_at: 2026-06-11T22:22:22Z
4
+ title: Protected Path Proposal
5
+ problem: Try to modify protected path.
6
+ evidence: N/A
7
+ risk_level: low
8
+ affected_files:
9
+ - .env
10
+ suggested_change: Modify .env file.
11
+ verify_command: npm run verify
12
+ rollback_plan: git checkout -- .env
13
+ approval_status: approved
14
+ ---
15
+
16
+ # Protected Path Proposal
17
+
18
+ ```json
19
+ {
20
+ "operations": [
21
+ {
22
+ "type": "create_file",
23
+ "path": ".env",
24
+ "content": "SECRET_KEY=stolen\n",
25
+ "overwrite": true
26
+ }
27
+ ]
28
+ }
29
+ ```
@@ -0,0 +1,30 @@
1
+ ---
2
+ id: proposal-20260611-000005
3
+ created_at: 2026-06-11T00:00:05Z
4
+ title: Replace Multiple Without Allow Proposal
5
+ problem: Test replace text matching multiple times without allow_multiple.
6
+ evidence: N/A
7
+ risk_level: low
8
+ affected_files:
9
+ - tests/fixtures/custom-template-example/multiple.md
10
+ suggested_change: Replace multiple occurrences without allow_multiple flag.
11
+ verify_command: npm run verify
12
+ rollback_plan: N/A
13
+ approval_status: approved
14
+ ---
15
+
16
+ # Replace Multiple Without Allow Proposal
17
+
18
+ ```json
19
+ {
20
+ "operations": [
21
+ {
22
+ "type": "replace_text",
23
+ "path": "tests/fixtures/custom-template-example/multiple.md",
24
+ "find": "target",
25
+ "replace": "replaced",
26
+ "allow_multiple": false
27
+ }
28
+ ]
29
+ }
30
+ ```
@@ -0,0 +1,20 @@
1
+ # Registry Overrides Test Fixture Guide
2
+
3
+ This fixture folder demonstrates and validates overriding the default model/adapter/template registries using custom YAML files and the `--registry` flag.
4
+
5
+ ## Files Structure
6
+
7
+ For testing registry overrides, you can specify custom templates/adapters files:
8
+
9
+ * `custom-templates.yaml`: Defines mock template profiles.
10
+ * `custom-adapters.yaml`: Defines mock adapters setup.
11
+
12
+ ## Usage in Testing
13
+
14
+ ```bash
15
+ # List templates from the custom templates fixture file
16
+ node bin/multimodel-dev-os.js templates --registry tests/fixtures/registry-overrides/custom-templates.yaml
17
+
18
+ # Run validations on a custom template entry
19
+ node bin/multimodel-dev-os.js validate-template my-mock-template --registry tests/fixtures/registry-overrides/custom-templates.yaml
20
+ ```
@@ -0,0 +1,4 @@
1
+ # Mock Registry Signing Fixtures (Test-Only)
2
+
3
+ WARNING: The keys, manifests, and signatures in this directory are generated automatically for MultiModel Dev OS unit and E2E tests.
4
+ DO NOT use these private/public keys in production. All keys are mock keys.
@@ -0,0 +1,8 @@
1
+
2
+ # Catalog fixture
3
+ catalog:
4
+ plugins:
5
+ - slug: test-plugin
6
+ version: 1.0.0
7
+ name: Test Plugin
8
+ description: A mock plugin for testing
@@ -0,0 +1,7 @@
1
+ {
2
+ "final_status": "untrusted",
3
+ "signature_status": "failed",
4
+ "errors": [
5
+ "Key ID 'test-key-revoked' is revoked (must be active)."
6
+ ]
7
+ }
@@ -0,0 +1,14 @@
1
+ registry_name: "test-fixture-registry"
2
+ publisher: "Mock Valid Publisher"
3
+ version: "1.0.0"
4
+ generated_at: "2026-06-20T12:00:00Z"
5
+ minimum_mmdo_version: "3.2.0"
6
+ safety_policy_version: "1.0.0"
7
+ catalog_hash: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
8
+ files_hashes:
9
+ catalog.yaml: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
10
+ signature:
11
+ algorithm: "ed25519"
12
+ key_id: "test-key-revoked"
13
+ signature: "LyuSIQ72kyOuNVqie1P/vZVi2PqFsCS//Q6/mAstTG1zDQvGPd5HwH8t1FPEOtP7Sbb31udJ7NJY3cdCWpACCg=="
14
+ signed_fields: ["registry_name", "publisher", "version", "generated_at", "catalog_hash", "minimum_mmdo_version", "safety_policy_version"]
@@ -0,0 +1,8 @@
1
+
2
+ # Catalog fixture
3
+ catalog:
4
+ plugins:
5
+ - slug: test-plugin
6
+ version: 1.0.0
7
+ name: Test Plugin
8
+ description: A mock plugin for testing
@@ -0,0 +1,7 @@
1
+ {
2
+ "final_status": "untrusted",
3
+ "signature_status": "failed",
4
+ "errors": [
5
+ "Invalid Ed25519 signature for key_id 'test-key-valid'."
6
+ ]
7
+ }
@@ -0,0 +1,14 @@
1
+ registry_name: "test-fixture-registry"
2
+ publisher: "Mock Valid Publisher"
3
+ version: "1.0.0"
4
+ generated_at: "2026-06-20T12:00:00Z"
5
+ minimum_mmdo_version: "3.2.0"
6
+ safety_policy_version: "1.0.0"
7
+ catalog_hash: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
8
+ files_hashes:
9
+ catalog.yaml: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
10
+ signature:
11
+ algorithm: "ed25519"
12
+ key_id: "test-key-valid"
13
+ signature: "MCowBQYDK2VwAyEA9vWwyE5+fY0dvEzl9S1UcvtoMkOAIDhDCzZAkP+CVNo="
14
+ signed_fields: ["registry_name", "publisher", "version", "generated_at", "catalog_hash", "minimum_mmdo_version", "safety_policy_version"]
@@ -0,0 +1,23 @@
1
+
2
+ # MultiModel Dev OS Test-Only Trusted Keys Fixture
3
+ # WARNING: These keys are for unit testing and E2E verification validation only.
4
+ # DO NOT USE IN PRODUCTION.
5
+ trusted_publishers:
6
+ - key_id: test-key-valid
7
+ name: "Mock Valid Publisher"
8
+ algorithm: ed25519
9
+ public_key: "MCowBQYDK2VwAyEAU1uu8t1/5aXZRvNDNkKpBRJ2SgkKJtlX6pMmuF90tx8="
10
+ scopes: ["registry"]
11
+ status: "active"
12
+ - key_id: test-key-revoked
13
+ name: "Mock Revoked Publisher"
14
+ algorithm: ed25519
15
+ public_key: "MCowBQYDK2VwAyEAvcxhJeWu5VSDxdAva1GwtzgWspYFJ9U1TRJ1a7bUyRA="
16
+ scopes: ["registry"]
17
+ status: "revoked"
18
+ - key_id: test-key-disabled
19
+ name: "Mock Disabled Publisher"
20
+ algorithm: ed25519
21
+ public_key: "MCowBQYDK2VwAyEA9uWCD3w/zkUoQ9RTpQUZ52sib3ctl3fre1PZZowVmvg="
22
+ scopes: ["registry"]
23
+ status: "disabled"
@@ -0,0 +1,8 @@
1
+
2
+ # Catalog fixture
3
+ catalog:
4
+ plugins:
5
+ - slug: test-plugin
6
+ version: 1.0.0
7
+ name: Test Plugin
8
+ description: A mock plugin for testing
@@ -0,0 +1,7 @@
1
+ {
2
+ "final_status": "untrusted",
3
+ "signature_status": "failed",
4
+ "errors": [
5
+ "Unsigned remote registries are not allowed by policy."
6
+ ]
7
+ }
@@ -0,0 +1,9 @@
1
+ registry_name: "test-fixture-registry"
2
+ publisher: "Mock Valid Publisher"
3
+ version: "1.0.0"
4
+ generated_at: "2026-06-20T12:00:00Z"
5
+ minimum_mmdo_version: "3.2.0"
6
+ safety_policy_version: "1.0.0"
7
+ catalog_hash: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
8
+ files_hashes:
9
+ catalog.yaml: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
@@ -0,0 +1,8 @@
1
+
2
+ # Catalog fixture
3
+ catalog:
4
+ plugins:
5
+ - slug: test-plugin
6
+ version: 1.0.0
7
+ name: Test Plugin
8
+ description: A mock plugin for testing
@@ -0,0 +1,7 @@
1
+ {
2
+ "final_status": "untrusted",
3
+ "signature_status": "failed",
4
+ "errors": [
5
+ "Signature algorithm 'rsa-sha256' is not allowed by policy (allowed: ed25519, hmac-sha256)."
6
+ ]
7
+ }
@@ -0,0 +1,14 @@
1
+ registry_name: "test-fixture-registry"
2
+ publisher: "Mock Valid Publisher"
3
+ version: "1.0.0"
4
+ generated_at: "2026-06-20T12:00:00Z"
5
+ minimum_mmdo_version: "3.2.0"
6
+ safety_policy_version: "1.0.0"
7
+ catalog_hash: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
8
+ files_hashes:
9
+ catalog.yaml: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
10
+ signature:
11
+ algorithm: "rsa-sha256"
12
+ key_id: "test-key-valid"
13
+ signature: "mock-sig"
14
+ signed_fields: ["registry_name", "publisher", "version", "generated_at", "catalog_hash", "minimum_mmdo_version", "safety_policy_version"]
@@ -0,0 +1,8 @@
1
+
2
+ # Catalog fixture
3
+ catalog:
4
+ plugins:
5
+ - slug: test-plugin
6
+ version: 1.0.0
7
+ name: Test Plugin
8
+ description: A mock plugin for testing
@@ -0,0 +1,7 @@
1
+ {
2
+ "final_status": "trusted",
3
+ "signature_status": "verified",
4
+ "trusted_publisher_status": "trusted",
5
+ "errors": [],
6
+ "warnings": []
7
+ }
@@ -0,0 +1,14 @@
1
+ registry_name: "test-fixture-registry"
2
+ publisher: "Mock Valid Publisher"
3
+ version: "1.0.0"
4
+ generated_at: "2026-06-20T12:00:00Z"
5
+ minimum_mmdo_version: "3.2.0"
6
+ safety_policy_version: "1.0.0"
7
+ catalog_hash: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
8
+ files_hashes:
9
+ catalog.yaml: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
10
+ signature:
11
+ algorithm: "ed25519"
12
+ key_id: "test-key-valid"
13
+ signature: "CjhqbNnmTh9wzBTBRlnkaCjZDTvxj8rImdtwOQIBOu0I+kCd8ec3x/jcNvJesy/Fqd2yl70vToqKAiVxD0GVAQ=="
14
+ signed_fields: ["registry_name", "publisher", "version", "generated_at", "catalog_hash", "minimum_mmdo_version", "safety_policy_version"]
@@ -0,0 +1,8 @@
1
+
2
+ # Catalog fixture
3
+ catalog:
4
+ plugins:
5
+ - slug: test-plugin
6
+ version: 1.0.0
7
+ name: Test Plugin
8
+ description: A mock plugin for testing
@@ -0,0 +1,7 @@
1
+ {
2
+ "final_status": "untrusted",
3
+ "signature_status": "failed",
4
+ "errors": [
5
+ "Key ID 'test-key-wrong' not found in trust store."
6
+ ]
7
+ }
@@ -0,0 +1,14 @@
1
+ registry_name: "test-fixture-registry"
2
+ publisher: "Mock Valid Publisher"
3
+ version: "1.0.0"
4
+ generated_at: "2026-06-20T12:00:00Z"
5
+ minimum_mmdo_version: "3.2.0"
6
+ safety_policy_version: "1.0.0"
7
+ catalog_hash: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
8
+ files_hashes:
9
+ catalog.yaml: "sha256:289e7ff82e8037b27471990d4106f76e7f8e44e26aeb049787c185a30249420e"
10
+ signature:
11
+ algorithm: "ed25519"
12
+ key_id: "test-key-wrong"
13
+ signature: "XChca7Nje+hM8QPfHHL8iwVjW8mL9wOeNhZBOo0hloV5CHhaFH9qm0k5e2xvFLMLOc3j+8CMCqJozPdWgiqxBw=="
14
+ signed_fields: ["registry_name", "publisher", "version", "generated_at", "catalog_hash", "minimum_mmdo_version", "safety_policy_version"]
@@ -0,0 +1,37 @@
1
+ # Smoke Tests Playbook
2
+
3
+ Follow these quick manual verification commands to assert the functional integrity of `multimodel-dev-os` before pushing tag releases.
4
+
5
+ ---
6
+
7
+ ## 1. Quick Verification Pipeline
8
+
9
+ Run the following actions sequentially inside a target sandbox:
10
+
11
+ ```bash
12
+ # 1. Inspect version parameters and command menu
13
+ node bin/multimodel-dev-os.js --help
14
+
15
+ # 2. View all built-in configurations
16
+ node bin/multimodel-dev-os.js templates
17
+
18
+ # 3. Inspect a specific template's specifications
19
+ node bin/multimodel-dev-os.js show-template nextjs-saas
20
+
21
+ # 4. Dry-run scaffold inside the current repository
22
+ node bin/multimodel-dev-os.js init --template general-app --dry-run
23
+
24
+ # 5. Execute strict validation lints
25
+ node bin/multimodel-dev-os.js validate --target .
26
+
27
+ # 6. Execute diagnostic doctor checkups
28
+ node bin/multimodel-dev-os.js doctor --target .
29
+ ```
30
+
31
+ ---
32
+
33
+ ## 2. Dry-Run Verification
34
+
35
+ Assert that no physical files are created when running the `--dry-run` parameter:
36
+ - Verify stdout logs indicate `[DRY-RUN] Would create...`
37
+ - Confirm `git status` reports zero untracked files or modifications.
@@ -0,0 +1,49 @@
1
+ # CLI Smoke Test Specification
2
+
3
+ This document lists the essential CLI commands to execute when verifying the MultiModel Dev OS installation stability.
4
+
5
+ ## Sanity Commands
6
+
7
+ ```bash
8
+ # Verify help output
9
+ node bin/multimodel-dev-os.js --help
10
+
11
+ # List templates
12
+ node bin/multimodel-dev-os.js templates
13
+ node bin/multimodel-dev-os.js templates --json
14
+
15
+ # List models and adapters
16
+ node bin/multimodel-dev-os.js models --json
17
+ node bin/multimodel-dev-os.js adapters --json
18
+
19
+ # List active skills
20
+ node bin/multimodel-dev-os.js skills
21
+ ```
22
+
23
+ ## Validation & Doctor Commands
24
+
25
+ ```bash
26
+ # Validate built-in resources
27
+ node bin/multimodel-dev-os.js validate-template nextjs-saas
28
+ node bin/multimodel-dev-os.js validate-adapter cursor
29
+ node bin/multimodel-dev-os.js validate-skill custom-skill.example
30
+
31
+ # Validate all registry entries
32
+ node bin/multimodel-dev-os.js validate --all-registries
33
+
34
+ # Release advisory checkup
35
+ node bin/multimodel-dev-os.js doctor --release
36
+
37
+ # Token budget checks
38
+ node bin/multimodel-dev-os.js doctor --tokens --threshold 100KB
39
+ ```
40
+
41
+ ## Initialization Dry-Runs
42
+
43
+ ```bash
44
+ # Dry-run general app init
45
+ node bin/multimodel-dev-os.js init --dry-run --template general-app
46
+
47
+ # Dry-run with adapter injections
48
+ node bin/multimodel-dev-os.js init --dry-run --template nextjs-saas -a cursor -a claude
49
+ ```
@@ -0,0 +1,40 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { existsSync, readFileSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { execSync } from 'child_process';
5
+
6
+ describe('Build Output Verification', () => {
7
+ const buildPath = join(process.cwd(), 'bin', 'multimodel-dev-os.js');
8
+
9
+ it('should compile the output file successfully', () => {
10
+ expect(existsSync(buildPath)).toBe(true);
11
+ });
12
+
13
+ it('should contain exactly one shebang at the top', () => {
14
+ const content = readFileSync(buildPath, 'utf8');
15
+ const totalShebangs = (content.match(/#!/g) || []).length;
16
+
17
+ expect(content.startsWith('#!/usr/bin/env node')).toBe(true);
18
+ expect(totalShebangs).toBe(1);
19
+ });
20
+
21
+ it('should contain the generation warning header', () => {
22
+ const content = readFileSync(buildPath, 'utf8');
23
+ expect(content).toContain('// Generated from src/. Do not edit directly.');
24
+ });
25
+
26
+ it('should not contain dangerous registry URL interpolation', () => {
27
+ const content = readFileSync(buildPath, 'utf8');
28
+
29
+ const hasUnsafeSync = content.includes("mod.get('${targetUrl}'") || (content.includes('execSync(`node -e "') && content.includes('${targetUrl}'));
30
+ expect(hasUnsafeSync).toBe(false);
31
+
32
+ expect(content).toContain('execFileSync(process.execPath');
33
+ });
34
+
35
+ it('should be completely fresh and match the source modules', () => {
36
+ expect(() => {
37
+ execSync('node scripts/check-build-fresh.js', { stdio: 'ignore' });
38
+ }).not.toThrow();
39
+ });
40
+ });
@@ -0,0 +1,44 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from 'vitest';
2
+ import { loadCatalogFromSource, loadCatalog } from '../../src/catalog/loader.js';
3
+ import { existsSync, mkdirSync, writeFileSync, rmSync } from 'fs';
4
+ import { join } from 'path';
5
+
6
+ describe('Catalog Loader', () => {
7
+ const tempDir = join(process.cwd(), 'temp-catalog-test');
8
+ const localCatalogDir = join(tempDir, '.ai', 'plugins');
9
+ const localCatalogFile = join(localCatalogDir, 'catalog.yaml');
10
+
11
+ beforeAll(() => {
12
+ mkdirSync(localCatalogDir, { recursive: true });
13
+ });
14
+
15
+ afterAll(() => {
16
+ if (existsSync(tempDir)) {
17
+ rmSync(tempDir, { recursive: true, force: true });
18
+ }
19
+ });
20
+
21
+ it('should load bundled catalog from fallback or global if no target is specified', () => {
22
+ const catalog = loadCatalog();
23
+ expect(catalog).toBeDefined();
24
+ expect(Array.isArray(catalog.plugins)).toBe(true);
25
+ });
26
+
27
+ it('should load local catalog when source is "local"', () => {
28
+ const localYaml = `
29
+ catalog:
30
+ plugins:
31
+ - name: local-plugin
32
+ slug: local-slug
33
+ version: 1.0.0
34
+ description: local test
35
+ author: test
36
+ `;
37
+ writeFileSync(localCatalogFile, localYaml, 'utf8');
38
+
39
+ const result = loadCatalogFromSource('local', { target: tempDir });
40
+ expect(result.plugins).toHaveLength(1);
41
+ expect(result.plugins[0].name).toBe('local-plugin');
42
+ expect(result.plugins[0]._source).toBe('local');
43
+ });
44
+ });