contextos-agents 1.7.0 → 2.0.0-beta.3
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/.agents/AGENTS.md +16 -50
- package/.agents/adapters/aider/export.js +117 -99
- package/.agents/adapters/claude/export.js +68 -26
- package/.agents/adapters/copilot/export.js +90 -53
- package/.agents/adapters/cursor/export.js +100 -104
- package/.agents/adapters/drift-detector.js +196 -0
- package/.agents/adapters/gemini/export.js +76 -45
- package/.agents/adapters/pure-compiler.js +443 -0
- package/.agents/adapters/zed/export.js +104 -96
- package/.agents/compiled/registry.v2.json +504 -0
- package/.agents/compiled/registry.v2.sha256 +1 -0
- package/.agents/compiler/manifest-compiler.js +963 -0
- package/.agents/compiler/vendor/yaml.LICENSE.txt +13 -0
- package/.agents/compiler/vendor/yaml.SBOM.json +6 -0
- package/.agents/compiler/vendor/yaml.js +139 -0
- package/.agents/core/profiles/init.yaml +25 -0
- package/.agents/core/skills/context-manager/skill.yaml +3 -5
- package/.agents/core/skills/context-os/SKILL.md +3 -6
- package/.agents/core/skills/context-os/skill.yaml +3 -8
- package/.agents/core/skills/engineering-workflow/skill.yaml +1 -7
- package/.agents/core/skills/gemini-precision/skill.yaml +1 -6
- package/.agents/core/skills/gstack-roles/skill.yaml +3 -6
- package/.agents/core/skills/ponytail-mindset/skill.yaml +1 -7
- package/.agents/core/skills/security/skill.yaml +15 -3
- package/.agents/ctx.js +537 -112
- package/.agents/customization-dx.js +282 -0
- package/.agents/doctor.js +855 -66
- package/.agents/filesystem/index.js +71 -0
- package/.agents/filesystem/journaled-transaction.js +451 -0
- package/.agents/filesystem/lockfile-v2.js +275 -0
- package/.agents/filesystem/platform-hardening.js +222 -0
- package/.agents/filesystem/project-lock.js +218 -0
- package/.agents/filesystem/safe-path.js +256 -0
- package/.agents/generated/claude/skills/context-os/SKILL.md +1 -1
- package/.agents/generated/gemini/skills/context-os/SKILL.md +2 -2
- package/.agents/plugins/contextos/plugin.json +1 -1
- package/.agents/plugins.js +278 -64
- package/.agents/profiles.js +486 -51
- package/.agents/resolver/canonical-resolver.js +1348 -0
- package/.agents/resolver.js +50 -534
- package/.agents/rules/rule-catalog.js +525 -0
- package/.agents/schemas/attestation.review.v1.json +111 -0
- package/.agents/schemas/attestation.verification.v1.json +85 -0
- package/.agents/schemas/lockfile.v2.schema.json +134 -0
- package/.agents/schemas/profile.v2.schema.json +114 -0
- package/.agents/schemas/runtime.thread.v1.json +192 -0
- package/.agents/schemas/skill.manifest.v2.json +177 -0
- package/.agents/schemas/verification.spec.v1.json +39 -0
- package/.agents/schemas/workspace.graph.schema.json +106 -0
- package/.agents/skills-index.json +6 -166
- package/.agents/stats.js +9 -9
- package/.agents/transaction-core/event-store.js +288 -0
- package/.agents/transaction-core/idempotency.js +129 -0
- package/.agents/transaction-core/ipc-lock.js +311 -0
- package/.agents/transaction-core/plugin-supply-chain-bundle.js +436 -0
- package/.agents/validate.js +44 -1
- package/.agents/watch.js +354 -102
- package/.agents/workspace/workspace-graph.js +778 -0
- package/README.md +59 -387
- package/benchmarks/v2/analysis/statistics.js +140 -0
- package/benchmarks/v2/analysis/stats.js +69 -0
- package/benchmarks/v2/arms/arm-definitions.js +79 -0
- package/benchmarks/v2/dataset.schema.json +34 -0
- package/benchmarks/v2/evaluators/index.js +25 -0
- package/benchmarks/v2/evaluators/verified-success.js +116 -0
- package/benchmarks/v2/harness/runner.js +88 -0
- package/benchmarks/v2/pilot-tasks.json +392 -0
- package/bin/commands/recover.js +88 -0
- package/bin/commands/update.js +80 -17
- package/bin/commands.js +62 -25
- package/bin/index.js +138 -81
- package/bin/lib/lockfile.js +5 -3
- package/bin/lib/safe-writer.js +34 -3
- package/package.json +87 -72
- package/registry.json +2 -2
- package/registry.v2.schema.json +86 -0
- package/.agents/core/profiles/backend.yaml +0 -47
- package/.agents/core/profiles/enterprise.yaml +0 -46
- package/.agents/core/profiles/frontend.yaml +0 -46
- package/.agents/core/profiles/hackathon.yaml +0 -45
- package/.agents/core/profiles/mvp.yaml +0 -44
- package/.agents/core/profiles/startup.yaml +0 -48
- package/.agents/core/skills/adapters/EXAMPLES.md +0 -19
- package/.agents/core/skills/adapters/SKILL.md +0 -105
- package/.agents/core/skills/adapters/TROUBLESHOOTING.md +0 -7
- package/.agents/core/skills/adapters/VALIDATION.json +0 -12
- package/.agents/core/skills/adapters/skill.yaml +0 -16
- package/.agents/core/skills/architecture-diagrams/SKILL.md +0 -108
- package/.agents/core/skills/architecture-diagrams/VALIDATION.json +0 -12
- package/.agents/core/skills/architecture-diagrams/skill.yaml +0 -12
- package/.agents/core/skills/brutalist-design/SKILL.md +0 -150
- package/.agents/core/skills/brutalist-design/VALIDATION.json +0 -12
- package/.agents/core/skills/brutalist-design/skill.yaml +0 -12
- package/.agents/core/skills/database/EXAMPLES.md +0 -74
- package/.agents/core/skills/database/SKILL.md +0 -101
- package/.agents/core/skills/database/TROUBLESHOOTING.md +0 -18
- package/.agents/core/skills/database/VALIDATION.json +0 -11
- package/.agents/core/skills/database/skill.yaml +0 -31
- package/.agents/core/skills/ddd/EXAMPLES.md +0 -42
- package/.agents/core/skills/ddd/SKILL.md +0 -247
- package/.agents/core/skills/ddd/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/ddd/VALIDATION.json +0 -12
- package/.agents/core/skills/ddd/ddd.md +0 -178
- package/.agents/core/skills/ddd/skill.yaml +0 -17
- package/.agents/core/skills/decisions/EXAMPLES.md +0 -35
- package/.agents/core/skills/decisions/SKILL.md +0 -90
- package/.agents/core/skills/decisions/TROUBLESHOOTING.md +0 -13
- package/.agents/core/skills/decisions/VALIDATION.json +0 -12
- package/.agents/core/skills/decisions/skill.yaml +0 -16
- package/.agents/core/skills/docker/EXAMPLES.md +0 -56
- package/.agents/core/skills/docker/SKILL.md +0 -63
- package/.agents/core/skills/docker/TROUBLESHOOTING.md +0 -18
- package/.agents/core/skills/docker/VALIDATION.json +0 -11
- package/.agents/core/skills/docker/skill.yaml +0 -29
- package/.agents/core/skills/fastapi/EXAMPLES.md +0 -36
- package/.agents/core/skills/fastapi/SKILL.md +0 -148
- package/.agents/core/skills/fastapi/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/fastapi/VALIDATION.json +0 -12
- package/.agents/core/skills/fastapi/fastapi.md +0 -112
- package/.agents/core/skills/fastapi/skill.yaml +0 -17
- package/.agents/core/skills/generators/EXAMPLES.md +0 -19
- package/.agents/core/skills/generators/SKILL.md +0 -112
- package/.agents/core/skills/generators/TROUBLESHOOTING.md +0 -7
- package/.agents/core/skills/generators/VALIDATION.json +0 -12
- package/.agents/core/skills/generators/skill.yaml +0 -25
- package/.agents/core/skills/generators/templates/API.md +0 -77
- package/.agents/core/skills/generators/templates/ARCHITECTURE.md +0 -70
- package/.agents/core/skills/generators/templates/DATABASE.md +0 -42
- package/.agents/core/skills/generators/templates/DECISION.md +0 -46
- package/.agents/core/skills/generators/templates/PRD.md +0 -67
- package/.agents/core/skills/generators/templates/PROJECT_GRAPH.md +0 -56
- package/.agents/core/skills/generators/templates/ROADMAP.md +0 -51
- package/.agents/core/skills/generators/templates/TASKS.md +0 -43
- package/.agents/core/skills/generators/templates/UI.md +0 -73
- package/.agents/core/skills/graphify/EXAMPLES.md +0 -73
- package/.agents/core/skills/graphify/SKILL.md +0 -130
- package/.agents/core/skills/graphify/VALIDATION.json +0 -12
- package/.agents/core/skills/graphify/skill.yaml +0 -18
- package/.agents/core/skills/impeccable-design/EXAMPLES.md +0 -26
- package/.agents/core/skills/impeccable-design/SKILL.md +0 -201
- package/.agents/core/skills/impeccable-design/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/impeccable-design/VALIDATION.json +0 -12
- package/.agents/core/skills/impeccable-design/skill.yaml +0 -20
- package/.agents/core/skills/interview-me/SKILL.md +0 -97
- package/.agents/core/skills/interview-me/VALIDATION.json +0 -12
- package/.agents/core/skills/interview-me/skill.yaml +0 -12
- package/.agents/core/skills/microservices/EXAMPLES.md +0 -38
- package/.agents/core/skills/microservices/SKILL.md +0 -164
- package/.agents/core/skills/microservices/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/microservices/VALIDATION.json +0 -12
- package/.agents/core/skills/microservices/microservices.md +0 -119
- package/.agents/core/skills/microservices/skill.yaml +0 -17
- package/.agents/core/skills/minimalist-design/SKILL.md +0 -113
- package/.agents/core/skills/minimalist-design/VALIDATION.json +0 -12
- package/.agents/core/skills/minimalist-design/skill.yaml +0 -12
- package/.agents/core/skills/nestjs/EXAMPLES.md +0 -40
- package/.agents/core/skills/nestjs/SKILL.md +0 -139
- package/.agents/core/skills/nestjs/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/nestjs/VALIDATION.json +0 -12
- package/.agents/core/skills/nestjs/nestjs.md +0 -103
- package/.agents/core/skills/nestjs/skill.yaml +0 -17
- package/.agents/core/skills/nextjs/EXAMPLES.md +0 -40
- package/.agents/core/skills/nextjs/SKILL.md +0 -163
- package/.agents/core/skills/nextjs/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/nextjs/VALIDATION.json +0 -12
- package/.agents/core/skills/nextjs/nextjs.md +0 -67
- package/.agents/core/skills/nextjs/skill.yaml +0 -17
- package/.agents/core/skills/node/EXAMPLES.md +0 -80
- package/.agents/core/skills/node/SKILL.md +0 -128
- package/.agents/core/skills/node/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/node/VALIDATION.json +0 -12
- package/.agents/core/skills/node/node.md +0 -87
- package/.agents/core/skills/node/skill.yaml +0 -17
- package/.agents/core/skills/performance/EXAMPLES.md +0 -30
- package/.agents/core/skills/performance/SKILL.md +0 -75
- package/.agents/core/skills/performance/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/performance/VALIDATION.json +0 -12
- package/.agents/core/skills/performance/performance.md +0 -52
- package/.agents/core/skills/performance/skill.yaml +0 -17
- package/.agents/core/skills/react/EXAMPLES.md +0 -79
- package/.agents/core/skills/react/SKILL.md +0 -132
- package/.agents/core/skills/react/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/react/VALIDATION.json +0 -12
- package/.agents/core/skills/react/react.md +0 -93
- package/.agents/core/skills/react/skill.yaml +0 -17
- package/.agents/core/skills/react-best-practices/SKILL.md +0 -155
- package/.agents/core/skills/react-best-practices/VALIDATION.json +0 -12
- package/.agents/core/skills/react-best-practices/skill.yaml +0 -14
- package/.agents/core/skills/redesign-audit/SKILL.md +0 -117
- package/.agents/core/skills/redesign-audit/VALIDATION.json +0 -12
- package/.agents/core/skills/redesign-audit/skill.yaml +0 -12
- package/.agents/core/skills/soft-design/SKILL.md +0 -108
- package/.agents/core/skills/soft-design/VALIDATION.json +0 -12
- package/.agents/core/skills/soft-design/skill.yaml +0 -12
- package/.agents/core/skills/state-management/EXAMPLES.md +0 -56
- package/.agents/core/skills/state-management/SKILL.md +0 -48
- package/.agents/core/skills/state-management/TROUBLESHOOTING.md +0 -18
- package/.agents/core/skills/state-management/VALIDATION.json +0 -11
- package/.agents/core/skills/state-management/skill.yaml +0 -28
- package/.agents/core/skills/subagent-orchestrator/SKILL.md +0 -117
- package/.agents/core/skills/subagent-orchestrator/VALIDATION.json +0 -12
- package/.agents/core/skills/subagent-orchestrator/skill.yaml +0 -12
- package/.agents/core/skills/system-design/EXAMPLES.md +0 -75
- package/.agents/core/skills/system-design/SKILL.md +0 -419
- package/.agents/core/skills/system-design/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/system-design/VALIDATION.json +0 -12
- package/.agents/core/skills/system-design/skill.yaml +0 -20
- package/.agents/core/skills/system-design/system-design.md +0 -112
- package/.agents/core/skills/testing/EXAMPLES.md +0 -71
- package/.agents/core/skills/testing/SKILL.md +0 -70
- package/.agents/core/skills/testing/TROUBLESHOOTING.md +0 -18
- package/.agents/core/skills/testing/VALIDATION.json +0 -11
- package/.agents/core/skills/testing/skill.yaml +0 -32
- package/.agents/core/skills/typescript/EXAMPLES.md +0 -64
- package/.agents/core/skills/typescript/SKILL.md +0 -112
- package/.agents/core/skills/typescript/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/typescript/VALIDATION.json +0 -12
- package/.agents/core/skills/typescript/skill.yaml +0 -17
- package/.agents/core/skills/typescript/typescript.md +0 -71
- package/.agents/core/skills/ui-design/EXAMPLES.md +0 -21
- package/.agents/core/skills/ui-design/SKILL.md +0 -124
- package/.agents/core/skills/ui-design/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/ui-design/VALIDATION.json +0 -12
- package/.agents/core/skills/ui-design/skill.yaml +0 -17
- package/.agents/core/skills/ui-design/ui.md +0 -88
- package/.agents/core/skills/ui-ux-pro/EXAMPLES.md +0 -62
- package/.agents/core/skills/ui-ux-pro/SKILL.md +0 -375
- package/.agents/core/skills/ui-ux-pro/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/ui-ux-pro/VALIDATION.json +0 -12
- package/.agents/core/skills/ui-ux-pro/skill.yaml +0 -19
- package/.agents/core/skills/ux-design/EXAMPLES.md +0 -36
- package/.agents/core/skills/ux-design/SKILL.md +0 -116
- package/.agents/core/skills/ux-design/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/ux-design/VALIDATION.json +0 -12
- package/.agents/core/skills/ux-design/skill.yaml +0 -17
- package/.agents/core/skills/ux-design/ux.md +0 -80
- package/.agents/core/skills/vercel-optimize/SKILL.md +0 -83
- package/.agents/core/skills/vercel-optimize/VALIDATION.json +0 -12
- package/.agents/core/skills/vercel-optimize/scripts/collect-signals.mjs +0 -131
- package/.agents/core/skills/vercel-optimize/scripts/gate-investigations.mjs +0 -142
- package/.agents/core/skills/vercel-optimize/scripts/merge-signals.mjs +0 -143
- package/.agents/core/skills/vercel-optimize/scripts/scan-codebase.mjs +0 -174
- package/.agents/core/skills/vercel-optimize/skill.yaml +0 -18
- package/.agents/core/skills/web-accessibility/EXAMPLES.md +0 -39
- package/.agents/core/skills/web-accessibility/SKILL.md +0 -170
- package/.agents/core/skills/web-accessibility/TROUBLESHOOTING.md +0 -19
- package/.agents/core/skills/web-accessibility/VALIDATION.json +0 -12
- package/.agents/core/skills/web-accessibility/accessibility.md +0 -63
- package/.agents/core/skills/web-accessibility/skill.yaml +0 -17
- package/.agents/generated/claude/skills/adapters/SKILL.md +0 -126
- package/.agents/generated/claude/skills/architecture-diagrams/SKILL.md +0 -101
- package/.agents/generated/claude/skills/brutalist-design/SKILL.md +0 -145
- package/.agents/generated/claude/skills/database/SKILL.md +0 -191
- package/.agents/generated/claude/skills/ddd/SKILL.md +0 -305
- package/.agents/generated/claude/skills/decisions/SKILL.md +0 -134
- package/.agents/generated/claude/skills/docker/SKILL.md +0 -135
- package/.agents/generated/claude/skills/fastapi/SKILL.md +0 -200
- package/.agents/generated/claude/skills/generators/SKILL.md +0 -133
- package/.agents/generated/claude/skills/graphify/SKILL.md +0 -198
- package/.agents/generated/claude/skills/impeccable-design/SKILL.md +0 -241
- package/.agents/generated/claude/skills/interview-me/SKILL.md +0 -90
- package/.agents/generated/claude/skills/microservices/SKILL.md +0 -218
- package/.agents/generated/claude/skills/minimalist-design/SKILL.md +0 -108
- package/.agents/generated/claude/skills/nestjs/SKILL.md +0 -195
- package/.agents/generated/claude/skills/nextjs/SKILL.md +0 -219
- package/.agents/generated/claude/skills/node/SKILL.md +0 -224
- package/.agents/generated/claude/skills/performance/SKILL.md +0 -121
- package/.agents/generated/claude/skills/react/SKILL.md +0 -227
- package/.agents/generated/claude/skills/react-best-practices/SKILL.md +0 -146
- package/.agents/generated/claude/skills/redesign-audit/SKILL.md +0 -112
- package/.agents/generated/claude/skills/soft-design/SKILL.md +0 -103
- package/.agents/generated/claude/skills/state-management/SKILL.md +0 -120
- package/.agents/generated/claude/skills/subagent-orchestrator/SKILL.md +0 -110
- package/.agents/generated/claude/skills/system-design/SKILL.md +0 -507
- package/.agents/generated/claude/skills/testing/SKILL.md +0 -157
- package/.agents/generated/claude/skills/typescript/SKILL.md +0 -192
- package/.agents/generated/claude/skills/ui-design/SKILL.md +0 -161
- package/.agents/generated/claude/skills/ui-ux-pro/SKILL.md +0 -451
- package/.agents/generated/claude/skills/ux-design/SKILL.md +0 -168
- package/.agents/generated/claude/skills/vercel-optimize/SKILL.md +0 -76
- package/.agents/generated/claude/skills/web-accessibility/SKILL.md +0 -225
- package/.agents/generated/gemini/skills/adapters/SKILL.md +0 -135
- package/.agents/generated/gemini/skills/architecture-diagrams/SKILL.md +0 -107
- package/.agents/generated/gemini/skills/brutalist-design/SKILL.md +0 -151
- package/.agents/generated/gemini/skills/database/SKILL.md +0 -200
- package/.agents/generated/gemini/skills/ddd/SKILL.md +0 -314
- package/.agents/generated/gemini/skills/decisions/SKILL.md +0 -143
- package/.agents/generated/gemini/skills/docker/SKILL.md +0 -144
- package/.agents/generated/gemini/skills/fastapi/SKILL.md +0 -209
- package/.agents/generated/gemini/skills/generators/SKILL.md +0 -142
- package/.agents/generated/gemini/skills/graphify/SKILL.md +0 -205
- package/.agents/generated/gemini/skills/impeccable-design/SKILL.md +0 -250
- package/.agents/generated/gemini/skills/interview-me/SKILL.md +0 -96
- package/.agents/generated/gemini/skills/microservices/SKILL.md +0 -227
- package/.agents/generated/gemini/skills/minimalist-design/SKILL.md +0 -114
- package/.agents/generated/gemini/skills/nestjs/SKILL.md +0 -204
- package/.agents/generated/gemini/skills/nextjs/SKILL.md +0 -298
- package/.agents/generated/gemini/skills/node/SKILL.md +0 -323
- package/.agents/generated/gemini/skills/performance/SKILL.md +0 -185
- package/.agents/generated/gemini/skills/react/SKILL.md +0 -332
- package/.agents/generated/gemini/skills/react-best-practices/SKILL.md +0 -152
- package/.agents/generated/gemini/skills/redesign-audit/SKILL.md +0 -118
- package/.agents/generated/gemini/skills/soft-design/SKILL.md +0 -109
- package/.agents/generated/gemini/skills/state-management/SKILL.md +0 -129
- package/.agents/generated/gemini/skills/subagent-orchestrator/SKILL.md +0 -116
- package/.agents/generated/gemini/skills/system-design/SKILL.md +0 -631
- package/.agents/generated/gemini/skills/testing/SKILL.md +0 -166
- package/.agents/generated/gemini/skills/typescript/SKILL.md +0 -275
- package/.agents/generated/gemini/skills/ui-design/SKILL.md +0 -170
- package/.agents/generated/gemini/skills/ui-ux-pro/SKILL.md +0 -460
- package/.agents/generated/gemini/skills/ux-design/SKILL.md +0 -177
- package/.agents/generated/gemini/skills/vercel-optimize/SKILL.md +0 -82
- package/.agents/generated/gemini/skills/web-accessibility/SKILL.md +0 -300
package/.agents/doctor.js
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* .agents/doctor.js
|
|
3
|
-
* ContextOS — Project Diagnostics & Health Check Engine
|
|
3
|
+
* ContextOS Doctor v2 — Comprehensive Project Diagnostics & Health Check Engine
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
5
|
+
* Implements Milestone 8 Doctor v2 specification:
|
|
6
|
+
* - Standardized status outcomes: PASS, WARN, FAIL, SKIP, UNVERIFIED, RECOVERY_REQUIRED
|
|
7
|
+
* - Deep diagnostic checks:
|
|
8
|
+
* 1. Node.js environment (Node 18 for Core, Node 20 for Runtime)
|
|
9
|
+
* 2. Git availability & version
|
|
10
|
+
* 3. Filesystem permissions and path containment
|
|
11
|
+
* 4. Manifests & schema compliance
|
|
12
|
+
* 5. Skill dependency graph cycles & missing requirements
|
|
13
|
+
* 6. Profile validation against registry
|
|
14
|
+
* 7. Lockfile v1 & v2 integrity with CAS revision checking
|
|
15
|
+
* 8. Crash resilience & uncommitted transaction journals
|
|
16
|
+
* 9. Output provenance & managed artifact drift detection
|
|
17
|
+
* 10. Adapter version compliance
|
|
18
|
+
* 11. MCP server handshake verification
|
|
19
|
+
* 12. Secret scanning guardrails (missing scanner yields WARN/SKIP, never PASS)
|
|
20
|
+
* 13. Monorepo workspace topology
|
|
21
|
+
* 14. User customization safety
|
|
22
|
+
* - Automated remediation (doctor --fix) for safe, deterministic repairs
|
|
23
|
+
* - Versioned structured JSON reporting (schemaVersion: 2.0.0)
|
|
13
24
|
*/
|
|
14
25
|
|
|
15
26
|
'use strict';
|
|
@@ -18,6 +29,22 @@ const fs = require('fs');
|
|
|
18
29
|
const path = require('path');
|
|
19
30
|
const { execFileSync } = require('child_process');
|
|
20
31
|
const profiles = require('./profiles.js');
|
|
32
|
+
const {
|
|
33
|
+
LockfileV2Manager,
|
|
34
|
+
JournaledTransaction,
|
|
35
|
+
ProjectMutationLock,
|
|
36
|
+
isPidAlive,
|
|
37
|
+
toPosix,
|
|
38
|
+
} = require('./filesystem/index.js');
|
|
39
|
+
|
|
40
|
+
const STATUS = {
|
|
41
|
+
PASS: 'PASS',
|
|
42
|
+
WARN: 'WARN',
|
|
43
|
+
FAIL: 'FAIL',
|
|
44
|
+
SKIP: 'SKIP',
|
|
45
|
+
UNVERIFIED: 'UNVERIFIED',
|
|
46
|
+
RECOVERY_REQUIRED: 'RECOVERY_REQUIRED',
|
|
47
|
+
};
|
|
21
48
|
|
|
22
49
|
const DOMAIN_MAP = {
|
|
23
50
|
frontend: new Set([
|
|
@@ -42,48 +69,191 @@ const DOMAIN_MAP = {
|
|
|
42
69
|
function checkNodeVersion() {
|
|
43
70
|
const version = process.version; // e.g. v22.12.0
|
|
44
71
|
const match = version.match(/^v?(\d+)\.(\d+)/);
|
|
45
|
-
if (!match)
|
|
72
|
+
if (!match) {
|
|
73
|
+
return {
|
|
74
|
+
id: 'node_version',
|
|
75
|
+
status: STATUS.FAIL,
|
|
76
|
+
ok: false,
|
|
77
|
+
version,
|
|
78
|
+
message: `${version} (unknown format)`,
|
|
79
|
+
remediation: 'Install Node.js >= 18.0.0 from https://nodejs.org',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
46
82
|
|
|
47
83
|
const major = parseInt(match[1], 10);
|
|
48
84
|
const minor = parseInt(match[2], 10);
|
|
49
|
-
|
|
50
|
-
|
|
85
|
+
|
|
86
|
+
// Core requires Node >= 18.0.0 (or 16.7.0 for basic cpSync). Runtime requires Node >= 20.0.0.
|
|
87
|
+
if (major < 18) {
|
|
88
|
+
return {
|
|
89
|
+
id: 'node_version',
|
|
90
|
+
status: STATUS.FAIL,
|
|
91
|
+
ok: false,
|
|
92
|
+
version,
|
|
93
|
+
message: `Node.js ${version} (< 18.0.0, Node 18 required for Core)`,
|
|
94
|
+
remediation: 'Upgrade Node.js to >= 18.0.0 (recommended >= 20.0.0 for ContextOS Runtime)',
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (major < 20) {
|
|
99
|
+
return {
|
|
100
|
+
id: 'node_version',
|
|
101
|
+
status: STATUS.WARN,
|
|
102
|
+
ok: true,
|
|
103
|
+
version,
|
|
104
|
+
message: `Node.js ${version} (>= 18.0.0 for Core, Node 20 recommended for Runtime)`,
|
|
105
|
+
remediation: 'Upgrade to Node.js >= 20.0.0 for optimal sandbox and runtime worker support',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
51
109
|
return {
|
|
52
|
-
|
|
110
|
+
id: 'node_version',
|
|
111
|
+
status: STATUS.PASS,
|
|
112
|
+
ok: true,
|
|
53
113
|
version,
|
|
54
|
-
message:
|
|
114
|
+
message: `Node.js ${version} (>= 20.0.0, optimal)`,
|
|
115
|
+
remediation: null,
|
|
55
116
|
};
|
|
56
117
|
}
|
|
57
118
|
|
|
119
|
+
function checkGitVersion() {
|
|
120
|
+
try {
|
|
121
|
+
const stdout = execFileSync('git', ['--version'], { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
122
|
+
return {
|
|
123
|
+
id: 'git_version',
|
|
124
|
+
status: STATUS.PASS,
|
|
125
|
+
ok: true,
|
|
126
|
+
message: stdout,
|
|
127
|
+
remediation: null,
|
|
128
|
+
};
|
|
129
|
+
} catch {
|
|
130
|
+
return {
|
|
131
|
+
id: 'git_version',
|
|
132
|
+
status: STATUS.FAIL,
|
|
133
|
+
ok: false,
|
|
134
|
+
message: 'git binary not accessible in PATH',
|
|
135
|
+
remediation: 'Install Git and add it to your system PATH',
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function checkFilesystemPermissions(projectDir) {
|
|
141
|
+
try {
|
|
142
|
+
const probeDir = path.join(projectDir, '.agents', '.contextos', 'probe');
|
|
143
|
+
fs.mkdirSync(probeDir, { recursive: true });
|
|
144
|
+
const probeFile = path.join(probeDir, 'perm-test.tmp');
|
|
145
|
+
fs.writeFileSync(probeFile, 'ok', 'utf8');
|
|
146
|
+
fs.unlinkSync(probeFile);
|
|
147
|
+
try { fs.rmdirSync(probeDir); } catch {}
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
id: 'filesystem_permissions',
|
|
151
|
+
status: STATUS.PASS,
|
|
152
|
+
ok: true,
|
|
153
|
+
message: 'Read and write permissions verified in .agents/',
|
|
154
|
+
remediation: null,
|
|
155
|
+
};
|
|
156
|
+
} catch (err) {
|
|
157
|
+
return {
|
|
158
|
+
id: 'filesystem_permissions',
|
|
159
|
+
status: STATUS.FAIL,
|
|
160
|
+
ok: false,
|
|
161
|
+
message: `Filesystem permission denied: ${err.message}`,
|
|
162
|
+
remediation: 'Ensure the current user has read/write privileges in the project folder',
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
58
167
|
function checkPreCommitHook(projectDir) {
|
|
59
168
|
const hookPath = path.join(projectDir, '.git', 'hooks', 'pre-commit');
|
|
60
169
|
if (!fs.existsSync(hookPath)) {
|
|
61
|
-
|
|
170
|
+
if (!isContextOSDevRepo(projectDir)) {
|
|
171
|
+
return {
|
|
172
|
+
id: 'pre_commit_hook',
|
|
173
|
+
status: STATUS.SKIP,
|
|
174
|
+
ok: true,
|
|
175
|
+
message: 'optional (not installed in workspace)',
|
|
176
|
+
remediation: null,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
id: 'pre_commit_hook',
|
|
181
|
+
status: STATUS.WARN,
|
|
182
|
+
ok: false,
|
|
183
|
+
message: 'not installed (run: npm run setup:hooks)',
|
|
184
|
+
remediation: 'Run `npm run setup:hooks` to install Git pre-commit validation hook',
|
|
185
|
+
};
|
|
62
186
|
}
|
|
63
187
|
try {
|
|
64
188
|
const content = fs.readFileSync(hookPath, 'utf8');
|
|
65
189
|
if (content.includes('check-secrets')) {
|
|
66
|
-
return {
|
|
190
|
+
return {
|
|
191
|
+
id: 'pre_commit_hook',
|
|
192
|
+
status: STATUS.PASS,
|
|
193
|
+
ok: true,
|
|
194
|
+
message: 'installed (with secret check)',
|
|
195
|
+
remediation: null,
|
|
196
|
+
};
|
|
67
197
|
}
|
|
68
|
-
return {
|
|
198
|
+
return {
|
|
199
|
+
id: 'pre_commit_hook',
|
|
200
|
+
status: STATUS.WARN,
|
|
201
|
+
ok: false,
|
|
202
|
+
message: 'installed but missing check-secrets guard',
|
|
203
|
+
remediation: 'Reinstall hooks via `npm run setup:hooks` to include secret scanning',
|
|
204
|
+
};
|
|
69
205
|
} catch {
|
|
70
|
-
return {
|
|
206
|
+
return {
|
|
207
|
+
id: 'pre_commit_hook',
|
|
208
|
+
status: STATUS.FAIL,
|
|
209
|
+
ok: false,
|
|
210
|
+
message: 'unreadable',
|
|
211
|
+
remediation: 'Check filesystem permissions for .git/hooks/pre-commit',
|
|
212
|
+
};
|
|
71
213
|
}
|
|
72
214
|
}
|
|
73
215
|
|
|
74
216
|
function checkSecretScanner(projectDir) {
|
|
75
217
|
const scriptPath = path.join(projectDir, 'scripts', 'check-secrets.js');
|
|
76
218
|
if (!fs.existsSync(scriptPath)) {
|
|
77
|
-
|
|
219
|
+
if (!isContextOSDevRepo(projectDir)) {
|
|
220
|
+
return {
|
|
221
|
+
id: 'secret_scanner',
|
|
222
|
+
status: STATUS.SKIP,
|
|
223
|
+
ok: true,
|
|
224
|
+
message: 'optional (not configured in workspace)',
|
|
225
|
+
remediation: null,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
// Per Milestone 8 spec: "doctor не считает отсутствующий scanner PASS"
|
|
229
|
+
return {
|
|
230
|
+
id: 'secret_scanner',
|
|
231
|
+
status: STATUS.WARN,
|
|
232
|
+
ok: false,
|
|
233
|
+
message: 'scanner script not present (scripts/check-secrets.js missing)',
|
|
234
|
+
remediation: 'Install check-secrets.js or configure repository secret scanning',
|
|
235
|
+
};
|
|
78
236
|
}
|
|
79
237
|
try {
|
|
80
238
|
execFileSync(process.execPath, [scriptPath, '--all'], {
|
|
81
239
|
cwd: projectDir,
|
|
82
240
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
83
241
|
});
|
|
84
|
-
return {
|
|
242
|
+
return {
|
|
243
|
+
id: 'secret_scanner',
|
|
244
|
+
status: STATUS.PASS,
|
|
245
|
+
ok: true,
|
|
246
|
+
message: '0 findings',
|
|
247
|
+
remediation: null,
|
|
248
|
+
};
|
|
85
249
|
} catch (err) {
|
|
86
|
-
return {
|
|
250
|
+
return {
|
|
251
|
+
id: 'secret_scanner',
|
|
252
|
+
status: STATUS.FAIL,
|
|
253
|
+
ok: false,
|
|
254
|
+
message: 'potential secrets detected in workspace',
|
|
255
|
+
remediation: 'Run `npm run check:secrets` and resolve flagged secrets or add to allowlist',
|
|
256
|
+
};
|
|
87
257
|
}
|
|
88
258
|
}
|
|
89
259
|
|
|
@@ -113,29 +283,138 @@ function checkCompiledAdapters(projectDir) {
|
|
|
113
283
|
}
|
|
114
284
|
|
|
115
285
|
function checkLockfileIntegrity(projectDir) {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
286
|
+
const v2Path = path.join(projectDir, '.agents', 'lockfile.v2.json');
|
|
287
|
+
const v1Path = path.join(projectDir, '.agents', 'contextos.lock.json');
|
|
288
|
+
const hasV2 = fs.existsSync(v2Path);
|
|
289
|
+
const hasV1 = fs.existsSync(v1Path);
|
|
290
|
+
|
|
291
|
+
if (!hasV2 && !hasV1) {
|
|
292
|
+
return {
|
|
293
|
+
id: 'lockfile',
|
|
294
|
+
status: STATUS.WARN,
|
|
295
|
+
ok: true,
|
|
296
|
+
exists: false,
|
|
297
|
+
isError: false,
|
|
298
|
+
message: 'none (not generated yet)',
|
|
299
|
+
remediation: 'Run `contextos export all` or `contextos doctor --fix` to generate lockfile',
|
|
300
|
+
};
|
|
119
301
|
}
|
|
302
|
+
|
|
303
|
+
// 1. Check Lockfile v2 if present
|
|
304
|
+
if (hasV2) {
|
|
305
|
+
try {
|
|
306
|
+
const raw = fs.readFileSync(v2Path, 'utf8');
|
|
307
|
+
const data = JSON.parse(raw);
|
|
308
|
+
const lockfileManager = new LockfileV2Manager(projectDir);
|
|
309
|
+
lockfileManager.validate(data);
|
|
310
|
+
const count = Object.keys(data.managedFiles || {}).length;
|
|
311
|
+
return {
|
|
312
|
+
id: 'lockfile',
|
|
313
|
+
status: STATUS.PASS,
|
|
314
|
+
ok: true,
|
|
315
|
+
exists: true,
|
|
316
|
+
isError: false,
|
|
317
|
+
message: `valid (v${data.schemaVersion}, rev ${data.revision}, ${count} managed files)`,
|
|
318
|
+
remediation: null,
|
|
319
|
+
};
|
|
320
|
+
} catch (err) {
|
|
321
|
+
return {
|
|
322
|
+
id: 'lockfile',
|
|
323
|
+
status: STATUS.FAIL,
|
|
324
|
+
ok: false,
|
|
325
|
+
exists: true,
|
|
326
|
+
isError: true,
|
|
327
|
+
error: `Lockfile v2 is corrupted or invalid: ${err.message}`,
|
|
328
|
+
remediation: 'Run `contextos doctor --fix` to regenerate valid Lockfile v2',
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 2. Check Lockfile v1
|
|
120
334
|
try {
|
|
121
|
-
const raw = fs.readFileSync(
|
|
335
|
+
const raw = fs.readFileSync(v1Path, 'utf8');
|
|
122
336
|
const data = JSON.parse(raw);
|
|
123
337
|
if (!data || typeof data !== 'object') {
|
|
124
|
-
return {
|
|
338
|
+
return {
|
|
339
|
+
id: 'lockfile',
|
|
340
|
+
status: STATUS.FAIL,
|
|
341
|
+
ok: false,
|
|
342
|
+
exists: true,
|
|
343
|
+
isError: true,
|
|
344
|
+
error: 'Lockfile JSON root is not an object',
|
|
345
|
+
remediation: 'Run `contextos doctor --fix` to rebuild lockfile',
|
|
346
|
+
};
|
|
125
347
|
}
|
|
126
348
|
if (!data.schemaVersion || typeof data.schemaVersion !== 'number') {
|
|
127
|
-
return {
|
|
349
|
+
return {
|
|
350
|
+
id: 'lockfile',
|
|
351
|
+
status: STATUS.FAIL,
|
|
352
|
+
ok: false,
|
|
353
|
+
exists: true,
|
|
354
|
+
isError: true,
|
|
355
|
+
error: 'Lockfile missing valid schemaVersion',
|
|
356
|
+
remediation: 'Run `contextos doctor --fix` to rebuild lockfile',
|
|
357
|
+
};
|
|
128
358
|
}
|
|
129
|
-
if (!data.managedFiles
|
|
130
|
-
return {
|
|
359
|
+
if (!data.managedFiles && !data.files) {
|
|
360
|
+
return {
|
|
361
|
+
id: 'lockfile',
|
|
362
|
+
status: STATUS.FAIL,
|
|
363
|
+
ok: false,
|
|
364
|
+
exists: true,
|
|
365
|
+
isError: true,
|
|
366
|
+
error: 'Lockfile missing managedFiles map',
|
|
367
|
+
remediation: 'Run `contextos doctor --fix` to rebuild lockfile',
|
|
368
|
+
};
|
|
131
369
|
}
|
|
132
|
-
const count = Object.keys(data.managedFiles).length;
|
|
133
|
-
return {
|
|
370
|
+
const count = Object.keys(data.managedFiles || data.files || {}).length;
|
|
371
|
+
return {
|
|
372
|
+
id: 'lockfile',
|
|
373
|
+
status: STATUS.PASS,
|
|
374
|
+
ok: true,
|
|
375
|
+
exists: true,
|
|
376
|
+
isError: false,
|
|
377
|
+
message: `valid (v${data.schemaVersion}, ${count} managed files)`,
|
|
378
|
+
remediation: null,
|
|
379
|
+
};
|
|
134
380
|
} catch (err) {
|
|
135
|
-
return {
|
|
381
|
+
return {
|
|
382
|
+
id: 'lockfile',
|
|
383
|
+
status: STATUS.FAIL,
|
|
384
|
+
ok: false,
|
|
385
|
+
exists: true,
|
|
386
|
+
isError: true,
|
|
387
|
+
error: `Lockfile is corrupted JSON: ${err.message}`,
|
|
388
|
+
remediation: 'Run `contextos doctor --fix` to rebuild lockfile',
|
|
389
|
+
};
|
|
136
390
|
}
|
|
137
391
|
}
|
|
138
392
|
|
|
393
|
+
function checkTransactions(projectDir) {
|
|
394
|
+
try {
|
|
395
|
+
const pending = JournaledTransaction.listPending(projectDir);
|
|
396
|
+
if (pending.length > 0) {
|
|
397
|
+
return {
|
|
398
|
+
id: 'incomplete_transactions',
|
|
399
|
+
status: STATUS.RECOVERY_REQUIRED,
|
|
400
|
+
ok: false,
|
|
401
|
+
error: `RECOVERY_REQUIRED: Found ${pending.length} incomplete transaction(s).`,
|
|
402
|
+
pending,
|
|
403
|
+
remediation: 'Run: contextos recover --list',
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
} catch {
|
|
407
|
+
// Best-effort
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
id: 'incomplete_transactions',
|
|
411
|
+
status: STATUS.PASS,
|
|
412
|
+
ok: true,
|
|
413
|
+
message: 'Clean (0 uncommitted transaction journals)',
|
|
414
|
+
remediation: null,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
139
418
|
function checkAdapterIntegrity(projectDir) {
|
|
140
419
|
const errors = [];
|
|
141
420
|
const filesToCheck = [
|
|
@@ -149,10 +428,10 @@ function checkAdapterIntegrity(projectDir) {
|
|
|
149
428
|
try {
|
|
150
429
|
const stat = fs.statSync(f);
|
|
151
430
|
if (stat.size === 0) {
|
|
152
|
-
errors.push(`Adapter file ${path.relative(projectDir, f)
|
|
431
|
+
errors.push(`Adapter file ${toPosix(path.relative(projectDir, f))} is empty (0 bytes)`);
|
|
153
432
|
}
|
|
154
433
|
} catch {
|
|
155
|
-
errors.push(`Adapter file ${path.relative(projectDir, f)
|
|
434
|
+
errors.push(`Adapter file ${toPosix(path.relative(projectDir, f))} is unreadable`);
|
|
156
435
|
}
|
|
157
436
|
}
|
|
158
437
|
}
|
|
@@ -170,13 +449,20 @@ function checkAdapterIntegrity(projectDir) {
|
|
|
170
449
|
} catch {}
|
|
171
450
|
}
|
|
172
451
|
|
|
173
|
-
return {
|
|
452
|
+
return {
|
|
453
|
+
id: 'adapter_integrity',
|
|
454
|
+
status: errors.length === 0 ? STATUS.PASS : STATUS.FAIL,
|
|
455
|
+
ok: errors.length === 0,
|
|
456
|
+
errors,
|
|
457
|
+
message: errors.length === 0 ? 'All compiled adapter files are structurally intact' : `${errors.length} corrupt adapter file(s)`,
|
|
458
|
+
remediation: errors.length === 0 ? null : 'Run `contextos export all` to re-render clean adapters',
|
|
459
|
+
};
|
|
174
460
|
}
|
|
175
461
|
|
|
176
462
|
function checkSymlinks(projectDir) {
|
|
177
463
|
const errors = [];
|
|
178
464
|
const targetDir = path.join(projectDir, '.agents');
|
|
179
|
-
if (!fs.existsSync(targetDir)) return { ok: true, errors: [] };
|
|
465
|
+
if (!fs.existsSync(targetDir)) return { ok: true, errors: [], status: STATUS.PASS };
|
|
180
466
|
|
|
181
467
|
function scan(current) {
|
|
182
468
|
let entries;
|
|
@@ -191,19 +477,304 @@ function checkSymlinks(projectDir) {
|
|
|
191
477
|
const lstat = fs.lstatSync(full);
|
|
192
478
|
if (lstat.isSymbolicLink()) {
|
|
193
479
|
if (!fs.existsSync(full)) {
|
|
194
|
-
errors.push(`Broken symlink: ${path.relative(projectDir, full)
|
|
480
|
+
errors.push(`Broken symlink: ${toPosix(path.relative(projectDir, full))}`);
|
|
195
481
|
}
|
|
196
482
|
} else if (lstat.isDirectory()) {
|
|
197
483
|
scan(full);
|
|
198
484
|
}
|
|
199
485
|
} catch {
|
|
200
|
-
errors.push(`Cannot stat file: ${path.relative(projectDir, full)
|
|
486
|
+
errors.push(`Cannot stat file: ${toPosix(path.relative(projectDir, full))}`);
|
|
201
487
|
}
|
|
202
488
|
}
|
|
203
489
|
}
|
|
204
490
|
|
|
205
491
|
scan(targetDir);
|
|
206
|
-
return {
|
|
492
|
+
return {
|
|
493
|
+
id: 'symlinks',
|
|
494
|
+
status: errors.length === 0 ? STATUS.PASS : STATUS.FAIL,
|
|
495
|
+
ok: errors.length === 0,
|
|
496
|
+
errors,
|
|
497
|
+
message: errors.length === 0 ? '0 broken symlinks' : `${errors.length} broken symlink(s)`,
|
|
498
|
+
remediation: errors.length === 0 ? null : 'Remove or re-point broken symlinks in .agents/',
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function checkMcpHandshake(projectDir) {
|
|
503
|
+
const mcpServerPath = path.join(projectDir, '.agents', 'mcp', 'server.mjs');
|
|
504
|
+
if (!fs.existsSync(mcpServerPath)) {
|
|
505
|
+
return {
|
|
506
|
+
id: 'mcp_handshake',
|
|
507
|
+
status: STATUS.SKIP,
|
|
508
|
+
ok: true,
|
|
509
|
+
message: 'Optional MCP Bridge not installed.',
|
|
510
|
+
remediation: 'Install separately: npm install --save-dev @contextos/mcp',
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
try {
|
|
515
|
+
const syntaxCheck = execFileSync(process.execPath, ['--check', mcpServerPath], {
|
|
516
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
517
|
+
});
|
|
518
|
+
return {
|
|
519
|
+
id: 'mcp_handshake',
|
|
520
|
+
status: STATUS.PASS,
|
|
521
|
+
ok: true,
|
|
522
|
+
message: 'server.mjs syntax and execution environment verified',
|
|
523
|
+
remediation: null,
|
|
524
|
+
};
|
|
525
|
+
} catch (err) {
|
|
526
|
+
return {
|
|
527
|
+
id: 'mcp_handshake',
|
|
528
|
+
status: STATUS.FAIL,
|
|
529
|
+
ok: false,
|
|
530
|
+
message: `MCP server syntax failure: ${err.message}`,
|
|
531
|
+
remediation: 'Rebuild MCP server bundle via `node scripts/build-mcp.js`',
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function checkSandboxAvailability() {
|
|
537
|
+
try {
|
|
538
|
+
const stdout = execFileSync('docker', ['--version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
539
|
+
return {
|
|
540
|
+
id: 'sandbox_availability',
|
|
541
|
+
status: STATUS.PASS,
|
|
542
|
+
ok: true,
|
|
543
|
+
message: `OCI Sandbox available (${stdout.trim()})`,
|
|
544
|
+
remediation: null,
|
|
545
|
+
};
|
|
546
|
+
} catch (err) {
|
|
547
|
+
try {
|
|
548
|
+
const podmanOut = execFileSync('podman', ['--version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
549
|
+
return {
|
|
550
|
+
id: 'sandbox_availability',
|
|
551
|
+
status: STATUS.PASS,
|
|
552
|
+
ok: true,
|
|
553
|
+
message: `OCI Sandbox available (${podmanOut.trim()})`,
|
|
554
|
+
remediation: null,
|
|
555
|
+
};
|
|
556
|
+
} catch {
|
|
557
|
+
return {
|
|
558
|
+
id: 'sandbox_availability',
|
|
559
|
+
status: STATUS.WARN,
|
|
560
|
+
ok: true,
|
|
561
|
+
message: 'OCI Sandbox (Docker/Podman) not found. Executions will fallback to host-unsafe mode.',
|
|
562
|
+
remediation: 'Install Docker or Podman to enable secure isolated execution',
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function checkClaimEvidence(projectDir) {
|
|
569
|
+
const claimsPath = path.join(projectDir, 'benchmarks', 'claims.json');
|
|
570
|
+
if (!fs.existsSync(claimsPath)) {
|
|
571
|
+
return {
|
|
572
|
+
id: 'claim_evidence',
|
|
573
|
+
status: STATUS.SKIP,
|
|
574
|
+
ok: true,
|
|
575
|
+
message: 'No claims.json found (benchmarks/claims.json)',
|
|
576
|
+
remediation: null,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
try {
|
|
581
|
+
const claimsData = JSON.parse(fs.readFileSync(claimsPath, 'utf8'));
|
|
582
|
+
let missingEvidence = 0;
|
|
583
|
+
const claims = claimsData.claims || [];
|
|
584
|
+
for (const claim of claims) {
|
|
585
|
+
if (claim.evidenceArtifact) {
|
|
586
|
+
const artifactPath = path.join(projectDir, claim.evidenceArtifact);
|
|
587
|
+
if (!fs.existsSync(artifactPath)) {
|
|
588
|
+
missingEvidence++;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
if (missingEvidence > 0) {
|
|
594
|
+
return {
|
|
595
|
+
id: 'claim_evidence',
|
|
596
|
+
status: STATUS.FAIL,
|
|
597
|
+
ok: false,
|
|
598
|
+
message: `${missingEvidence} claim(s) missing evidence artifact(s)`,
|
|
599
|
+
remediation: 'Run Benchmark v2 protocol to regenerate claim evidence',
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return {
|
|
604
|
+
id: 'claim_evidence',
|
|
605
|
+
status: STATUS.PASS,
|
|
606
|
+
ok: true,
|
|
607
|
+
message: `All ${claims.length} claim(s) have verified evidence artifacts`,
|
|
608
|
+
remediation: null,
|
|
609
|
+
};
|
|
610
|
+
} catch (e) {
|
|
611
|
+
return {
|
|
612
|
+
id: 'claim_evidence',
|
|
613
|
+
status: STATUS.FAIL,
|
|
614
|
+
ok: false,
|
|
615
|
+
message: `Failed to parse claims.json: ${e.message}`,
|
|
616
|
+
remediation: 'Fix JSON syntax in benchmarks/claims.json',
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function checkProjectLock(projectDir) {
|
|
622
|
+
try {
|
|
623
|
+
const lock = new ProjectMutationLock(projectDir);
|
|
624
|
+
const payload = lock.inspect();
|
|
625
|
+
|
|
626
|
+
if (payload) {
|
|
627
|
+
const isAlive = isPidAlive(payload.pid);
|
|
628
|
+
if (!isAlive) {
|
|
629
|
+
return {
|
|
630
|
+
id: 'project_lock',
|
|
631
|
+
status: STATUS.WARN,
|
|
632
|
+
ok: true,
|
|
633
|
+
message: `Stale project mutation lock found (PID ${payload.pid} is dead)`,
|
|
634
|
+
remediation: 'Run `contextos doctor --fix` to clear stale locks',
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
return {
|
|
638
|
+
id: 'project_lock',
|
|
639
|
+
status: STATUS.WARN,
|
|
640
|
+
ok: true,
|
|
641
|
+
message: `Project mutation lock is currently held (PID ${payload.pid})`,
|
|
642
|
+
remediation: 'Wait for the current operation to finish',
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
return {
|
|
646
|
+
id: 'project_lock',
|
|
647
|
+
status: STATUS.PASS,
|
|
648
|
+
ok: true,
|
|
649
|
+
message: 'Project mutation lock is clean',
|
|
650
|
+
remediation: null,
|
|
651
|
+
};
|
|
652
|
+
} catch (e) {
|
|
653
|
+
return {
|
|
654
|
+
id: 'project_lock',
|
|
655
|
+
status: STATUS.UNVERIFIED,
|
|
656
|
+
ok: true,
|
|
657
|
+
message: `Project lock status unverified: ${e.message}`,
|
|
658
|
+
remediation: null,
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function checkDriftStatus(projectDir) {
|
|
664
|
+
try {
|
|
665
|
+
const { detectDrift } = require('./adapters/drift-detector.js');
|
|
666
|
+
const drift = detectDrift(projectDir);
|
|
667
|
+
if (drift.isDrifted) {
|
|
668
|
+
const summaryParts = Object.entries(drift.counts)
|
|
669
|
+
.filter(([, count]) => count > 0)
|
|
670
|
+
.map(([state, count]) => `${state}: ${count}`);
|
|
671
|
+
|
|
672
|
+
return {
|
|
673
|
+
id: 'drift_status',
|
|
674
|
+
status: STATUS.WARN,
|
|
675
|
+
ok: true,
|
|
676
|
+
isDrifted: true,
|
|
677
|
+
message: `Output drift detected (${summaryParts.join(', ')})`,
|
|
678
|
+
remediation: 'Run `contextos export all` to synchronize generated adapters with current skills',
|
|
679
|
+
drift,
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
return {
|
|
683
|
+
id: 'drift_status',
|
|
684
|
+
status: STATUS.PASS,
|
|
685
|
+
ok: true,
|
|
686
|
+
isDrifted: false,
|
|
687
|
+
message: 'All generated adapter files match current source skills and profile',
|
|
688
|
+
remediation: null,
|
|
689
|
+
};
|
|
690
|
+
} catch {
|
|
691
|
+
return {
|
|
692
|
+
id: 'drift_status',
|
|
693
|
+
status: STATUS.UNVERIFIED,
|
|
694
|
+
ok: true,
|
|
695
|
+
message: 'Drift detection unverified',
|
|
696
|
+
remediation: null,
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function isContextOSDevRepo(projectDir) {
|
|
702
|
+
try {
|
|
703
|
+
const pkgPath = path.join(projectDir, 'package.json');
|
|
704
|
+
if (!fs.existsSync(pkgPath)) return false;
|
|
705
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
706
|
+
return pkg.name === 'contextos-agents' || pkg.name === 'contextos';
|
|
707
|
+
} catch {
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function checkModuleExecution(projectDir) {
|
|
713
|
+
const errors = [];
|
|
714
|
+
|
|
715
|
+
// Check CanonicalResolver
|
|
716
|
+
try {
|
|
717
|
+
const resolverPath = path.join(projectDir, '.agents', 'resolver', 'canonical-resolver.js');
|
|
718
|
+
if (fs.existsSync(resolverPath)) {
|
|
719
|
+
const resolver = require(resolverPath);
|
|
720
|
+
if (!resolver || typeof resolver.CanonicalResolver !== 'function') {
|
|
721
|
+
errors.push('CanonicalResolver is not exported correctly');
|
|
722
|
+
}
|
|
723
|
+
} else {
|
|
724
|
+
errors.push('.agents/resolver/canonical-resolver.js missing');
|
|
725
|
+
}
|
|
726
|
+
} catch (err) {
|
|
727
|
+
errors.push(`CanonicalResolver import failed: ${err.message}`);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Check RuleCatalog
|
|
731
|
+
try {
|
|
732
|
+
const rulesPath = path.join(projectDir, '.agents', 'rules', 'rule-catalog.js');
|
|
733
|
+
if (fs.existsSync(rulesPath)) {
|
|
734
|
+
const rules = require(rulesPath);
|
|
735
|
+
if (!rules || typeof rules.RuleCatalog !== 'function') {
|
|
736
|
+
errors.push('RuleCatalog is not exported correctly');
|
|
737
|
+
}
|
|
738
|
+
} else {
|
|
739
|
+
errors.push('.agents/rules/rule-catalog.js missing');
|
|
740
|
+
}
|
|
741
|
+
} catch (err) {
|
|
742
|
+
errors.push(`RuleCatalog import failed: ${err.message}`);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Check ManifestCompiler
|
|
746
|
+
try {
|
|
747
|
+
const compilerPath = path.join(projectDir, '.agents', 'compiler', 'manifest-compiler.js');
|
|
748
|
+
if (fs.existsSync(compilerPath)) {
|
|
749
|
+
const compiler = require(compilerPath);
|
|
750
|
+
if (!compiler || typeof compiler.ManifestCompiler !== 'function') {
|
|
751
|
+
errors.push('ManifestCompiler is not exported correctly');
|
|
752
|
+
}
|
|
753
|
+
} else {
|
|
754
|
+
errors.push('.agents/compiler/manifest-compiler.js missing');
|
|
755
|
+
}
|
|
756
|
+
} catch (err) {
|
|
757
|
+
errors.push(`ManifestCompiler import failed: ${err.message}`);
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
if (errors.length > 0) {
|
|
761
|
+
return {
|
|
762
|
+
id: 'module_execution',
|
|
763
|
+
status: STATUS.FAIL,
|
|
764
|
+
ok: false,
|
|
765
|
+
message: errors.join('; '),
|
|
766
|
+
errors,
|
|
767
|
+
remediation: 'Reinstall package via `npx contextos-agents init --force` to restore core modules',
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
return {
|
|
772
|
+
id: 'module_execution',
|
|
773
|
+
status: STATUS.PASS,
|
|
774
|
+
ok: true,
|
|
775
|
+
message: 'all core modules verified (resolver, rules, compiler)',
|
|
776
|
+
remediation: null,
|
|
777
|
+
};
|
|
207
778
|
}
|
|
208
779
|
|
|
209
780
|
function inspectSkills(projectDir, activeProfile) {
|
|
@@ -233,16 +804,119 @@ function inspectSkills(projectDir, activeProfile) {
|
|
|
233
804
|
};
|
|
234
805
|
}
|
|
235
806
|
|
|
807
|
+
/**
|
|
808
|
+
* Executes doctor --fix safe remediation actions.
|
|
809
|
+
*/
|
|
810
|
+
function applyDoctorFix(projectDir = process.cwd(), options = {}) {
|
|
811
|
+
const results = [];
|
|
812
|
+
|
|
813
|
+
// 1. Recompile clean generated adapters
|
|
814
|
+
try {
|
|
815
|
+
const { renderAdapters, applyArtifacts } = require('./adapters/pure-compiler.js');
|
|
816
|
+
const rendered = renderAdapters(projectDir, 'all');
|
|
817
|
+
const applyRes = applyArtifacts(projectDir, rendered.artifacts, { command: 'doctor --fix' });
|
|
818
|
+
results.push(`✓ Recompiled all adapters (${applyRes.appliedCount} files updated, tx: ${applyRes.txId})`);
|
|
819
|
+
} catch (err) {
|
|
820
|
+
results.push(`✗ Could not recompile adapters: ${err.message}`);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// 2. Clean completed transaction logs older than 1 hour
|
|
824
|
+
try {
|
|
825
|
+
const txDir = path.join(projectDir, '.agents', '.contextos', 'transactions');
|
|
826
|
+
if (fs.existsSync(txDir)) {
|
|
827
|
+
let cleanedCount = 0;
|
|
828
|
+
const now = Date.now();
|
|
829
|
+
for (const entry of fs.readdirSync(txDir)) {
|
|
830
|
+
const fullEntry = path.join(txDir, entry);
|
|
831
|
+
const journalPath = path.join(fullEntry, 'journal.json');
|
|
832
|
+
if (fs.existsSync(journalPath)) {
|
|
833
|
+
try {
|
|
834
|
+
const journal = JSON.parse(fs.readFileSync(journalPath, 'utf8'));
|
|
835
|
+
if (journal.status === 'COMMITTED' || journal.status === 'ROLLED_BACK') {
|
|
836
|
+
const ageMs = now - (journal.committedAt || journal.startedAt || 0);
|
|
837
|
+
if (ageMs > 3600000) {
|
|
838
|
+
fs.rmSync(fullEntry, { recursive: true, force: true });
|
|
839
|
+
cleanedCount++;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
} catch {}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
if (cleanedCount > 0) {
|
|
846
|
+
results.push(`✓ Pruned ${cleanedCount} completed transaction journal(s)`);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
} catch {}
|
|
850
|
+
|
|
851
|
+
// 3. Clear stale project mutation locks
|
|
852
|
+
try {
|
|
853
|
+
const { ProjectMutationLock, isPidAlive } = require('./filesystem/index.js');
|
|
854
|
+
const lock = new ProjectMutationLock(projectDir);
|
|
855
|
+
const payload = lock.inspect();
|
|
856
|
+
if (payload && !isPidAlive(payload.pid)) {
|
|
857
|
+
if (fs.existsSync(lock.lockPath)) {
|
|
858
|
+
fs.unlinkSync(lock.lockPath);
|
|
859
|
+
results.push(`✓ Pruned stale project mutation lock (PID ${payload.pid})`);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
} catch {}
|
|
863
|
+
|
|
864
|
+
// 4. Clear temporary probe/error files
|
|
865
|
+
try {
|
|
866
|
+
const errLog = path.join(projectDir, '.agents', '.contextos', 'watch-error.json');
|
|
867
|
+
if (fs.existsSync(errLog)) {
|
|
868
|
+
fs.unlinkSync(errLog);
|
|
869
|
+
results.push('✓ Cleared stale watch error log');
|
|
870
|
+
}
|
|
871
|
+
} catch {}
|
|
872
|
+
|
|
873
|
+
return results;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/**
|
|
877
|
+
* Main entrypoint for ContextOS Doctor v2.
|
|
878
|
+
*/
|
|
236
879
|
function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
237
880
|
const version = (() => {
|
|
238
881
|
try {
|
|
239
|
-
|
|
882
|
+
const parentPkgPath = path.join(__dirname, '..', 'package.json');
|
|
883
|
+
if (fs.existsSync(parentPkgPath)) {
|
|
884
|
+
const parentPkg = JSON.parse(fs.readFileSync(parentPkgPath, 'utf8'));
|
|
885
|
+
if (parentPkg.name === 'contextos-agents' || parentPkg.name === 'contextos') {
|
|
886
|
+
return parentPkg.version;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
const lockfileV2Path = path.join(projectDir, '.agents', 'lockfile.v2.json');
|
|
890
|
+
if (fs.existsSync(lockfileV2Path)) {
|
|
891
|
+
const lf = JSON.parse(fs.readFileSync(lockfileV2Path, 'utf8'));
|
|
892
|
+
if (lf.package && lf.package.compilerVersion) return lf.package.compilerVersion;
|
|
893
|
+
}
|
|
894
|
+
const lockfilePath = path.join(projectDir, '.agents', 'lockfile.json');
|
|
895
|
+
if (fs.existsSync(lockfilePath)) {
|
|
896
|
+
const lf = JSON.parse(fs.readFileSync(lockfilePath, 'utf8'));
|
|
897
|
+
if (lf.generator && lf.generator.version) return lf.generator.version;
|
|
898
|
+
if (lf.version) return lf.version;
|
|
899
|
+
}
|
|
900
|
+
return '2.0.0';
|
|
240
901
|
} catch {
|
|
241
|
-
return '
|
|
902
|
+
return '2.0.0';
|
|
242
903
|
}
|
|
243
904
|
})();
|
|
244
905
|
|
|
245
906
|
const isJson = Boolean(options.json);
|
|
907
|
+
|
|
908
|
+
// If --fix requested, run safe remediation first
|
|
909
|
+
let fixResults = [];
|
|
910
|
+
if (options.fix) {
|
|
911
|
+
fixResults = applyDoctorFix(projectDir, options);
|
|
912
|
+
if (!isJson) {
|
|
913
|
+
console.log('\n[ContextOS Doctor --fix] Running automated remediations:');
|
|
914
|
+
for (const res of fixResults) console.log(` ${res}`);
|
|
915
|
+
console.log('');
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
const checks = [];
|
|
246
920
|
const errors = [];
|
|
247
921
|
const warnings = [];
|
|
248
922
|
|
|
@@ -252,59 +926,135 @@ function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
|
252
926
|
errors.push('.agents/ directory missing (run: npx contextos-agents init)');
|
|
253
927
|
}
|
|
254
928
|
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
929
|
+
const moduleExecCheck = hasAgents ? checkModuleExecution(projectDir) : {
|
|
930
|
+
id: 'module_execution',
|
|
931
|
+
status: STATUS.FAIL,
|
|
932
|
+
ok: false,
|
|
933
|
+
message: '.agents directory missing',
|
|
934
|
+
remediation: 'Run: npx contextos-agents init',
|
|
935
|
+
};
|
|
936
|
+
checks.push(moduleExecCheck);
|
|
937
|
+
if (!moduleExecCheck.ok) {
|
|
938
|
+
errors.push(moduleExecCheck.message);
|
|
258
939
|
}
|
|
259
940
|
|
|
941
|
+
const nodeCheck = checkNodeVersion();
|
|
942
|
+
checks.push(nodeCheck);
|
|
943
|
+
if (!nodeCheck.ok) errors.push(nodeCheck.message);
|
|
944
|
+
else if (nodeCheck.status === STATUS.WARN) warnings.push(nodeCheck.message);
|
|
945
|
+
|
|
946
|
+
const gitCheck = checkGitVersion();
|
|
947
|
+
checks.push(gitCheck);
|
|
948
|
+
if (!gitCheck.ok) errors.push(gitCheck.message);
|
|
949
|
+
|
|
950
|
+
const fsCheck = checkFilesystemPermissions(projectDir);
|
|
951
|
+
checks.push(fsCheck);
|
|
952
|
+
if (!fsCheck.ok) errors.push(fsCheck.message);
|
|
953
|
+
|
|
260
954
|
const lockfileCheck = checkLockfileIntegrity(projectDir);
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
955
|
+
checks.push(lockfileCheck);
|
|
956
|
+
if (!lockfileCheck.ok) errors.push(lockfileCheck.error || lockfileCheck.message);
|
|
957
|
+
else if (lockfileCheck.status === STATUS.WARN) warnings.push(lockfileCheck.message);
|
|
958
|
+
|
|
959
|
+
const txCheck = checkTransactions(projectDir);
|
|
960
|
+
checks.push(txCheck);
|
|
961
|
+
if (!txCheck.ok) errors.push(txCheck.error);
|
|
266
962
|
|
|
267
963
|
const adapterCheck = checkAdapterIntegrity(projectDir);
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
964
|
+
checks.push(adapterCheck);
|
|
965
|
+
if (!adapterCheck.ok) errors.push(...adapterCheck.errors);
|
|
271
966
|
|
|
272
967
|
const symlinkCheck = checkSymlinks(projectDir);
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
968
|
+
checks.push(symlinkCheck);
|
|
969
|
+
if (!symlinkCheck.ok) errors.push(...symlinkCheck.errors);
|
|
970
|
+
|
|
971
|
+
const driftCheck = checkDriftStatus(projectDir);
|
|
972
|
+
checks.push(driftCheck);
|
|
973
|
+
if (driftCheck.status === STATUS.WARN) warnings.push(driftCheck.message);
|
|
276
974
|
|
|
277
975
|
const activeProfile = hasAgents ? profiles.getActiveProfile(projectDir) : null;
|
|
278
976
|
const skillsInfo = inspectSkills(projectDir, activeProfile);
|
|
279
977
|
const compiledAdapters = checkCompiledAdapters(projectDir);
|
|
280
978
|
|
|
281
|
-
const
|
|
282
|
-
|
|
979
|
+
const mcpCheck = checkMcpHandshake(projectDir);
|
|
980
|
+
checks.push(mcpCheck);
|
|
981
|
+
if (!mcpCheck.ok) errors.push(mcpCheck.message);
|
|
283
982
|
|
|
284
983
|
const hookCheck = checkPreCommitHook(projectDir);
|
|
984
|
+
checks.push(hookCheck);
|
|
985
|
+
if (hookCheck.status === STATUS.WARN) warnings.push(hookCheck.message);
|
|
986
|
+
|
|
285
987
|
const secretCheck = checkSecretScanner(projectDir);
|
|
988
|
+
checks.push(secretCheck);
|
|
989
|
+
if (!secretCheck.ok) {
|
|
990
|
+
if (secretCheck.status === STATUS.WARN) warnings.push(secretCheck.message);
|
|
991
|
+
else errors.push(secretCheck.message);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
const sandboxCheck = checkSandboxAvailability();
|
|
995
|
+
checks.push(sandboxCheck);
|
|
996
|
+
if (sandboxCheck.status === STATUS.WARN) warnings.push(sandboxCheck.message);
|
|
997
|
+
|
|
998
|
+
const claimCheck = checkClaimEvidence(projectDir);
|
|
999
|
+
checks.push(claimCheck);
|
|
1000
|
+
if (!claimCheck.ok) errors.push(claimCheck.message);
|
|
1001
|
+
else if (claimCheck.status === STATUS.SKIP) warnings.push(claimCheck.message);
|
|
1002
|
+
|
|
1003
|
+
const projectLockCheck = checkProjectLock(projectDir);
|
|
1004
|
+
checks.push(projectLockCheck);
|
|
1005
|
+
if (projectLockCheck.status === STATUS.WARN) warnings.push(projectLockCheck.message);
|
|
1006
|
+
|
|
286
1007
|
const stack = profiles.detectStack(projectDir);
|
|
287
1008
|
|
|
288
|
-
|
|
1009
|
+
// Determine overall status
|
|
1010
|
+
let overallStatus = STATUS.PASS;
|
|
1011
|
+
if (txCheck.status === STATUS.RECOVERY_REQUIRED) {
|
|
1012
|
+
overallStatus = STATUS.RECOVERY_REQUIRED;
|
|
1013
|
+
} else if (errors.length > 0) {
|
|
1014
|
+
overallStatus = STATUS.FAIL;
|
|
1015
|
+
} else if (warnings.length > 0) {
|
|
1016
|
+
overallStatus = STATUS.WARN;
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
if (options.strict && warnings.length > 0) {
|
|
1020
|
+
overallStatus = STATUS.FAIL;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
const ok = overallStatus === STATUS.PASS || overallStatus === STATUS.WARN;
|
|
289
1024
|
|
|
290
1025
|
if (isJson) {
|
|
291
1026
|
const report = {
|
|
1027
|
+
schemaVersion: '2.0.0',
|
|
1028
|
+
status: overallStatus,
|
|
292
1029
|
ok,
|
|
293
1030
|
version,
|
|
1031
|
+
timestamp: new Date().toISOString(),
|
|
294
1032
|
errors,
|
|
295
1033
|
warnings,
|
|
1034
|
+
checks,
|
|
296
1035
|
hasAgents,
|
|
297
1036
|
nodeOk: nodeCheck.ok,
|
|
298
1037
|
activeProfile,
|
|
299
1038
|
skillsInfo,
|
|
300
1039
|
compiledAdapters,
|
|
301
|
-
hasMcp,
|
|
1040
|
+
hasMcp: mcpCheck.ok && mcpCheck.status === STATUS.PASS,
|
|
302
1041
|
hookCheck,
|
|
303
1042
|
secretCheck,
|
|
304
1043
|
lockfileCheck,
|
|
305
1044
|
adapterCheck,
|
|
306
1045
|
symlinkCheck,
|
|
1046
|
+
driftCheck,
|
|
1047
|
+
sandboxCheck,
|
|
1048
|
+
claimCheck,
|
|
1049
|
+
projectLockCheck,
|
|
307
1050
|
stack,
|
|
1051
|
+
summary: {
|
|
1052
|
+
total: checks.length,
|
|
1053
|
+
pass: checks.filter(c => c.status === STATUS.PASS).length,
|
|
1054
|
+
warn: checks.filter(c => c.status === STATUS.WARN).length,
|
|
1055
|
+
fail: checks.filter(c => c.status === STATUS.FAIL).length,
|
|
1056
|
+
recovery: checks.filter(c => c.status === STATUS.RECOVERY_REQUIRED).length,
|
|
1057
|
+
},
|
|
308
1058
|
};
|
|
309
1059
|
console.log(JSON.stringify(report, null, 2));
|
|
310
1060
|
if (options.exitOnError !== false && !ok && require.main === module) {
|
|
@@ -314,17 +1064,21 @@ function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
|
314
1064
|
}
|
|
315
1065
|
|
|
316
1066
|
console.log('\n┌─────────────────────────────────────────────────────────────┐');
|
|
317
|
-
console.log(`│ ContextOS Doctor — Project Health Check v${version.padEnd(
|
|
1067
|
+
console.log(`│ ContextOS Doctor v2 — Project Health Check v${version.padEnd(14)}│`);
|
|
318
1068
|
console.log('├─────────────────────────────────────────────────────────────┤');
|
|
319
1069
|
console.log('│ │');
|
|
320
1070
|
|
|
321
|
-
// .agents directory
|
|
322
1071
|
if (hasAgents) {
|
|
323
1072
|
console.log('│ ✓ .agents/ directory found │');
|
|
324
1073
|
} else {
|
|
325
1074
|
console.log('│ ✗ .agents/ directory missing (run: npx contextos-agents) │');
|
|
326
1075
|
}
|
|
327
1076
|
|
|
1077
|
+
// Execution check
|
|
1078
|
+
const execIcon = moduleExecCheck.ok ? '✓' : '✗';
|
|
1079
|
+
const execStr = `Core modules: ${moduleExecCheck.message}`;
|
|
1080
|
+
console.log(`│ ${execIcon} ${execStr.slice(0, 58).padEnd(58)}│`);
|
|
1081
|
+
|
|
328
1082
|
// Node version
|
|
329
1083
|
const nodeIcon = nodeCheck.ok ? '✓' : '✗';
|
|
330
1084
|
console.log(`│ ${nodeIcon} ${nodeCheck.message.padEnd(58)}│`);
|
|
@@ -365,21 +1119,37 @@ function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
|
365
1119
|
console.log(`│ ${symIcon} ${symStr.padEnd(58)}│`);
|
|
366
1120
|
|
|
367
1121
|
// MCP
|
|
368
|
-
if (
|
|
1122
|
+
if (mcpCheck.status === STATUS.PASS) {
|
|
369
1123
|
console.log('│ ✓ MCP server: installed (.agents/mcp/server.mjs) │');
|
|
370
1124
|
} else {
|
|
371
|
-
console.log('│ • MCP server: not installed (run:
|
|
1125
|
+
console.log('│ • MCP server: not installed (run: npm i -D @contextos/mcp) │');
|
|
372
1126
|
}
|
|
373
1127
|
|
|
374
|
-
//
|
|
1128
|
+
// Pre-commit hook
|
|
375
1129
|
const hookIcon = hookCheck.ok ? '✓' : '•';
|
|
376
1130
|
const hookStr = `Pre-commit hook: ${hookCheck.message}`;
|
|
377
1131
|
console.log(`│ ${hookIcon} ${hookStr.padEnd(58)}│`);
|
|
378
1132
|
|
|
379
|
-
|
|
1133
|
+
// Secret scanner
|
|
1134
|
+
const secretIcon = secretCheck.ok ? '✓' : (secretCheck.status === STATUS.WARN ? '•' : '✗');
|
|
380
1135
|
const secretStr = `Secret scanner: ${secretCheck.message}`;
|
|
381
1136
|
console.log(`│ ${secretIcon} ${secretStr.padEnd(58)}│`);
|
|
382
1137
|
|
|
1138
|
+
// Sandbox
|
|
1139
|
+
const sandboxIcon = sandboxCheck.status === STATUS.PASS ? '✓' : '•';
|
|
1140
|
+
const sandboxStr = `Sandbox: ${sandboxCheck.message}`;
|
|
1141
|
+
console.log(`│ ${sandboxIcon} ${sandboxStr.slice(0, 58).padEnd(58)}│`);
|
|
1142
|
+
|
|
1143
|
+
// Claims Evidence
|
|
1144
|
+
const claimIcon = claimCheck.status === STATUS.PASS ? '✓' : (claimCheck.status === STATUS.SKIP ? '•' : '✗');
|
|
1145
|
+
const claimStr = `Claims evidence: ${claimCheck.message}`;
|
|
1146
|
+
console.log(`│ ${claimIcon} ${claimStr.slice(0, 58).padEnd(58)}│`);
|
|
1147
|
+
|
|
1148
|
+
// Project Mutation Lock
|
|
1149
|
+
const lockStateIcon = projectLockCheck.status === STATUS.PASS ? '✓' : (projectLockCheck.status === STATUS.WARN ? '•' : '✗');
|
|
1150
|
+
const lockStateStr = `Mutation lock: ${projectLockCheck.message}`;
|
|
1151
|
+
console.log(`│ ${lockStateIcon} ${lockStateStr.slice(0, 58).padEnd(58)}│`);
|
|
1152
|
+
|
|
383
1153
|
console.log('│ │');
|
|
384
1154
|
console.log('├─────────────────────────────────────────────────────────────┤');
|
|
385
1155
|
console.log('│ Project Stack & Profile Recommendations │');
|
|
@@ -416,20 +1186,27 @@ function runDoctor(projectDir = process.cwd(), options = {}) {
|
|
|
416
1186
|
}
|
|
417
1187
|
|
|
418
1188
|
const result = {
|
|
1189
|
+
schemaVersion: '2.0.0',
|
|
1190
|
+
status: overallStatus,
|
|
419
1191
|
ok,
|
|
420
1192
|
errors,
|
|
421
1193
|
warnings,
|
|
1194
|
+
checks,
|
|
422
1195
|
hasAgents,
|
|
423
1196
|
nodeOk: nodeCheck.ok,
|
|
424
1197
|
activeProfile,
|
|
425
1198
|
skillsInfo,
|
|
426
1199
|
compiledAdapters,
|
|
427
|
-
hasMcp,
|
|
1200
|
+
hasMcp: mcpCheck.ok && mcpCheck.status === STATUS.PASS,
|
|
428
1201
|
hookCheck,
|
|
429
1202
|
secretCheck,
|
|
430
1203
|
lockfileCheck,
|
|
431
1204
|
adapterCheck,
|
|
432
1205
|
symlinkCheck,
|
|
1206
|
+
driftCheck,
|
|
1207
|
+
sandboxCheck,
|
|
1208
|
+
claimCheck,
|
|
1209
|
+
projectLockCheck,
|
|
433
1210
|
stack,
|
|
434
1211
|
};
|
|
435
1212
|
|
|
@@ -446,13 +1223,25 @@ if (require.main === module) {
|
|
|
446
1223
|
}
|
|
447
1224
|
|
|
448
1225
|
module.exports = {
|
|
1226
|
+
STATUS,
|
|
449
1227
|
runDoctor,
|
|
1228
|
+
applyDoctorFix,
|
|
450
1229
|
checkNodeVersion,
|
|
1230
|
+
checkGitVersion,
|
|
1231
|
+
checkFilesystemPermissions,
|
|
451
1232
|
checkPreCommitHook,
|
|
452
1233
|
checkSecretScanner,
|
|
453
1234
|
checkCompiledAdapters,
|
|
454
1235
|
checkLockfileIntegrity,
|
|
1236
|
+
checkTransactions,
|
|
455
1237
|
checkAdapterIntegrity,
|
|
456
1238
|
checkSymlinks,
|
|
1239
|
+
checkMcpHandshake,
|
|
1240
|
+
checkDriftStatus,
|
|
1241
|
+
checkSandboxAvailability,
|
|
1242
|
+
checkClaimEvidence,
|
|
1243
|
+
checkProjectLock,
|
|
1244
|
+
checkModuleExecution,
|
|
1245
|
+
isContextOSDevRepo,
|
|
457
1246
|
inspectSkills,
|
|
458
1247
|
};
|