contextos-agents 1.7.0 → 2.0.0-beta.2
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 +1 -1
- 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 +80 -101
- 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 +574 -111
- package/.agents/customization-dx.js +282 -0
- package/.agents/doctor.js +719 -64
- 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 +259 -60
- package/.agents/profiles.js +486 -51
- package/.agents/resolver.js +50 -534
- 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/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 +85 -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/profiles.js
CHANGED
|
@@ -12,21 +12,28 @@
|
|
|
12
12
|
|
|
13
13
|
const fs = require('fs');
|
|
14
14
|
const path = require('path');
|
|
15
|
+
const { ProjectMutationLock, JournaledTransaction } = require('./filesystem/index.js');
|
|
15
16
|
|
|
16
17
|
const AGENTS_DIR = path.join(__dirname);
|
|
17
18
|
const PROFILES_DIR = path.join(AGENTS_DIR, 'core', 'profiles');
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
21
|
* @typedef {Object} ProfileConfig
|
|
22
|
+
* @property {number} [schemaVersion] - Profile schema version
|
|
21
23
|
* @property {string} id - Profile unique slug
|
|
22
24
|
* @property {string} name - Human-readable profile title
|
|
23
25
|
* @property {string} description - Summary of the profile's purpose
|
|
24
|
-
* @property {
|
|
25
|
-
* @property {
|
|
26
|
-
* @property {
|
|
27
|
-
* @property {
|
|
28
|
-
* @property {
|
|
29
|
-
* @property {
|
|
26
|
+
* @property {Object} skills - Profile skill groups (required, preferred, excluded)
|
|
27
|
+
* @property {Object} policy - Quality gate policies
|
|
28
|
+
* @property {Object} generation - Document templates to generate
|
|
29
|
+
* @property {Object} technologyDefaults - Default framework/tool selections
|
|
30
|
+
* @property {string[]} prefer_skills - Legacy priority skills
|
|
31
|
+
* @property {string[]} exclude_skills - Legacy filtered out skills
|
|
32
|
+
* @property {string[]} require_skills - Legacy mandatory skills
|
|
33
|
+
* @property {string[]} generate_docs - Legacy documentation templates
|
|
34
|
+
* @property {string[]} skip_docs - Legacy documentation to skip
|
|
35
|
+
* @property {Object} enforce - Legacy invariant rules
|
|
36
|
+
* @property {Object} defaults - Legacy default parameters
|
|
30
37
|
*/
|
|
31
38
|
|
|
32
39
|
/**
|
|
@@ -37,17 +44,39 @@ const PROFILES_DIR = path.join(AGENTS_DIR, 'core', 'profiles');
|
|
|
37
44
|
*/
|
|
38
45
|
function parseYamlProfile(text) {
|
|
39
46
|
const result = {
|
|
47
|
+
schemaVersion: 1,
|
|
40
48
|
id: '',
|
|
41
49
|
name: '',
|
|
42
50
|
description: '',
|
|
51
|
+
skills: {
|
|
52
|
+
required: [],
|
|
53
|
+
preferred: [],
|
|
54
|
+
excluded: [],
|
|
55
|
+
},
|
|
56
|
+
policy: {
|
|
57
|
+
verification: 'recommended',
|
|
58
|
+
review: 'recommended',
|
|
59
|
+
security: 'recommended',
|
|
60
|
+
adr: 'off',
|
|
61
|
+
testing: 'recommended',
|
|
62
|
+
accessibility: 'off',
|
|
63
|
+
},
|
|
64
|
+
generation: {
|
|
65
|
+
documents: [],
|
|
66
|
+
},
|
|
67
|
+
technologyDefaults: {},
|
|
43
68
|
prefer_skills: [],
|
|
44
69
|
exclude_skills: [],
|
|
70
|
+
require_skills: [],
|
|
45
71
|
generate_docs: [],
|
|
46
72
|
skip_docs: [],
|
|
47
73
|
enforce: {},
|
|
48
74
|
defaults: {},
|
|
49
75
|
};
|
|
50
76
|
|
|
77
|
+
const schemaMatch = text.match(/^schemaVersion:\s*(\d+)/m);
|
|
78
|
+
if (schemaMatch) result.schemaVersion = parseInt(schemaMatch[1], 10);
|
|
79
|
+
|
|
51
80
|
const idMatch = text.match(/^id:\s*(.+)$/m);
|
|
52
81
|
if (idMatch) result.id = idMatch[1].trim().replace(/^['"]|['"]$/g, '');
|
|
53
82
|
|
|
@@ -57,12 +86,12 @@ function parseYamlProfile(text) {
|
|
|
57
86
|
const descMatch = text.match(/^description:\s*(.+)$/m);
|
|
58
87
|
if (descMatch) result.description = descMatch[1].trim().replace(/^['"]|['"]$/g, '');
|
|
59
88
|
|
|
60
|
-
function parseList(field) {
|
|
61
|
-
const blockRegex = new RegExp(
|
|
62
|
-
const match =
|
|
89
|
+
function parseList(field, sourceText = text) {
|
|
90
|
+
const blockRegex = new RegExp(`^[ \\t]*${field}:\\s*\\n((?:[ \\t]+-[^\\n]*\\n?)+)`, 'm');
|
|
91
|
+
const match = sourceText.match(blockRegex);
|
|
63
92
|
if (!match) {
|
|
64
|
-
const inlineRegex = new RegExp(
|
|
65
|
-
const inlineMatch =
|
|
93
|
+
const inlineRegex = new RegExp(`^[ \t]*${field}:\\s*\\[([^\\]]*)]`, 'm');
|
|
94
|
+
const inlineMatch = sourceText.match(inlineRegex);
|
|
66
95
|
if (!inlineMatch || !inlineMatch[1].trim()) return [];
|
|
67
96
|
return inlineMatch[1].split(',').map(s => s.trim().replace(/^['"]|['"]$/g, '')).filter(Boolean);
|
|
68
97
|
}
|
|
@@ -72,9 +101,9 @@ function parseYamlProfile(text) {
|
|
|
72
101
|
.filter(Boolean);
|
|
73
102
|
}
|
|
74
103
|
|
|
75
|
-
function parseMap(field) {
|
|
76
|
-
const blockRegex = new RegExp(
|
|
77
|
-
const match =
|
|
104
|
+
function parseMap(field, sourceText = text) {
|
|
105
|
+
const blockRegex = new RegExp(`^[ \t]*${field}:\\s*\\n((?:[ \t]+[a-zA-Z0-9_-]+:[^\\n]*\\n?)+)`, 'm');
|
|
106
|
+
const match = sourceText.match(blockRegex);
|
|
78
107
|
if (!match) return {};
|
|
79
108
|
const map = {};
|
|
80
109
|
const lines = match[1].split('\n');
|
|
@@ -94,31 +123,201 @@ function parseYamlProfile(text) {
|
|
|
94
123
|
return map;
|
|
95
124
|
}
|
|
96
125
|
|
|
126
|
+
function getSectionBlock(sectionKey) {
|
|
127
|
+
const blockRegex = new RegExp(`^${sectionKey}:\\s*\\n((?:[ \t]+[^\\n]*\\n?)+)`, 'm');
|
|
128
|
+
const match = text.match(blockRegex);
|
|
129
|
+
return match ? match[1] : '';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Parse ProfileV2 sections
|
|
133
|
+
const skillsBlock = getSectionBlock('skills');
|
|
134
|
+
if (skillsBlock) {
|
|
135
|
+
result.schemaVersion = 2;
|
|
136
|
+
result.skills.required = parseList('required', skillsBlock);
|
|
137
|
+
result.skills.preferred = parseList('preferred', skillsBlock);
|
|
138
|
+
result.skills.excluded = parseList('excluded', skillsBlock);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const policyBlock = getSectionBlock('policy');
|
|
142
|
+
if (policyBlock) {
|
|
143
|
+
result.schemaVersion = 2;
|
|
144
|
+
result.policy = { ...result.policy, ...parseMap('policy', `policy:\n${policyBlock}`) };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const genBlock = getSectionBlock('generation');
|
|
148
|
+
if (genBlock) {
|
|
149
|
+
result.schemaVersion = 2;
|
|
150
|
+
result.generation.documents = parseList('documents', genBlock);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const techDefaultsBlock = getSectionBlock('technologyDefaults');
|
|
154
|
+
if (techDefaultsBlock) {
|
|
155
|
+
result.schemaVersion = 2;
|
|
156
|
+
result.technologyDefaults = parseMap('technologyDefaults', `technologyDefaults:\n${techDefaultsBlock}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Legacy fields
|
|
97
160
|
result.prefer_skills = parseList('prefer_skills');
|
|
98
161
|
result.exclude_skills = parseList('exclude_skills');
|
|
162
|
+
result.require_skills = parseList('require_skills');
|
|
99
163
|
result.generate_docs = parseList('generate_docs');
|
|
100
164
|
result.skip_docs = parseList('skip_docs');
|
|
101
165
|
result.enforce = parseMap('enforce');
|
|
102
166
|
result.defaults = parseMap('defaults');
|
|
103
167
|
|
|
168
|
+
// Normalization and bidirectional sync
|
|
169
|
+
if (result.skills.preferred.length === 0 && result.prefer_skills.length > 0) {
|
|
170
|
+
result.skills.preferred = [...result.prefer_skills];
|
|
171
|
+
} else if (result.prefer_skills.length === 0 && result.skills.preferred.length > 0) {
|
|
172
|
+
result.prefer_skills = [...result.skills.preferred];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (result.skills.excluded.length === 0 && result.exclude_skills.length > 0) {
|
|
176
|
+
result.skills.excluded = [...result.exclude_skills];
|
|
177
|
+
} else if (result.exclude_skills.length === 0 && result.skills.excluded.length > 0) {
|
|
178
|
+
result.exclude_skills = [...result.skills.excluded];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (result.skills.required.length === 0 && result.require_skills.length > 0) {
|
|
182
|
+
result.skills.required = [...result.require_skills];
|
|
183
|
+
} else if (result.require_skills.length === 0 && result.skills.required.length > 0) {
|
|
184
|
+
result.require_skills = [...result.skills.required];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (Object.keys(result.technologyDefaults).length === 0 && Object.keys(result.defaults).length > 0) {
|
|
188
|
+
result.technologyDefaults = { ...result.defaults };
|
|
189
|
+
} else if (Object.keys(result.defaults).length === 0 && Object.keys(result.technologyDefaults).length > 0) {
|
|
190
|
+
result.defaults = { ...result.technologyDefaults };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (result.generation.documents.length === 0 && result.generate_docs.length > 0) {
|
|
194
|
+
result.generation.documents = [...result.generate_docs];
|
|
195
|
+
} else if (result.generate_docs.length === 0 && result.generation.documents.length > 0) {
|
|
196
|
+
result.generate_docs = [...result.generation.documents];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Policy <-> Enforce sync
|
|
200
|
+
if (policyBlock) {
|
|
201
|
+
result.enforce.testing = result.policy.testing === 'required' || result.policy.verification === 'required';
|
|
202
|
+
result.enforce.adr = result.policy.adr === 'required';
|
|
203
|
+
result.enforce.code_review = result.policy.review === 'required';
|
|
204
|
+
result.enforce.security_audit = result.policy.security === 'required';
|
|
205
|
+
result.enforce.accessibility_audit = result.policy.accessibility === 'required';
|
|
206
|
+
} else if (Object.keys(result.enforce).length > 0) {
|
|
207
|
+
if (result.enforce.testing !== undefined) {
|
|
208
|
+
result.policy.testing = result.enforce.testing ? 'required' : 'off';
|
|
209
|
+
result.policy.verification = result.enforce.testing ? 'required' : 'off';
|
|
210
|
+
}
|
|
211
|
+
if (result.enforce.adr !== undefined) {
|
|
212
|
+
result.policy.adr = result.enforce.adr ? 'required' : 'off';
|
|
213
|
+
}
|
|
214
|
+
if (result.enforce.code_review !== undefined) {
|
|
215
|
+
result.policy.review = result.enforce.code_review ? 'required' : 'off';
|
|
216
|
+
}
|
|
217
|
+
if (result.enforce.security_audit !== undefined) {
|
|
218
|
+
result.policy.security = result.enforce.security_audit ? 'required' : 'off';
|
|
219
|
+
}
|
|
220
|
+
if (result.enforce.accessibility_audit !== undefined) {
|
|
221
|
+
result.policy.accessibility = result.enforce.accessibility_audit ? 'required' : 'off';
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
104
225
|
return result;
|
|
105
226
|
}
|
|
106
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Validates a profile configuration for contradictions and unknown skills.
|
|
230
|
+
*
|
|
231
|
+
* @param {ProfileConfig} profile - Profile to validate
|
|
232
|
+
* @param {Object} [registry] - Compiled skill registry
|
|
233
|
+
* @returns {{ valid: boolean }} Validation result
|
|
234
|
+
*/
|
|
235
|
+
function validateProfile(profile, registry = null) {
|
|
236
|
+
if (!profile || typeof profile !== 'object') {
|
|
237
|
+
const err = new Error('Invalid profile: expected an object');
|
|
238
|
+
err.code = 'CTX_PROFILE_INVALID';
|
|
239
|
+
throw err;
|
|
240
|
+
}
|
|
241
|
+
if (!profile.id || typeof profile.id !== 'string') {
|
|
242
|
+
const err = new Error('Invalid profile: missing profile id');
|
|
243
|
+
err.code = 'CTX_PROFILE_INVALID';
|
|
244
|
+
throw err;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const required = profile.skills?.required || profile.require_skills || [];
|
|
248
|
+
const preferred = profile.skills?.preferred || profile.prefer_skills || [];
|
|
249
|
+
const excluded = profile.skills?.excluded || profile.exclude_skills || [];
|
|
250
|
+
|
|
251
|
+
// Check contradictions between required and excluded
|
|
252
|
+
const excSet = new Set(excluded);
|
|
253
|
+
for (const s of required) {
|
|
254
|
+
if (excSet.has(s)) {
|
|
255
|
+
const err = new Error(`Profile contradiction in '${profile.id}': skill '${s}' cannot be both required and excluded.`);
|
|
256
|
+
err.code = 'CTX_PROFILE_CONTRADICTION';
|
|
257
|
+
err.skill = s;
|
|
258
|
+
throw err;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Check contradictions between preferred and excluded
|
|
263
|
+
for (const s of preferred) {
|
|
264
|
+
if (excSet.has(s)) {
|
|
265
|
+
const err = new Error(`Profile contradiction in '${profile.id}': skill '${s}' cannot be both preferred and excluded.`);
|
|
266
|
+
err.code = 'CTX_PROFILE_CONTRADICTION';
|
|
267
|
+
err.skill = s;
|
|
268
|
+
throw err;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Validate skill IDs against registry if provided
|
|
273
|
+
if (registry && registry.skills) {
|
|
274
|
+
const validSkillIds = new Set(Object.keys(registry.skills));
|
|
275
|
+
const allSkills = [...required, ...preferred, ...excluded];
|
|
276
|
+
for (const s of allSkills) {
|
|
277
|
+
if (!validSkillIds.has(s)) {
|
|
278
|
+
const err = new Error(`Unknown skill ID '${s}' in profile '${profile.id}'. Real registered skill IDs only.`);
|
|
279
|
+
err.code = 'CTX_PROFILE_UNKNOWN_SKILL';
|
|
280
|
+
err.skill = s;
|
|
281
|
+
throw err;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Validate policy values
|
|
287
|
+
const validPolicyValues = new Set(['required', 'recommended', 'off']);
|
|
288
|
+
const policy = profile.policy || {};
|
|
289
|
+
for (const [key, val] of Object.entries(policy)) {
|
|
290
|
+
if (typeof val === 'string' && !validPolicyValues.has(val)) {
|
|
291
|
+
const err = new Error(`Invalid policy value '${val}' for '${key}' in profile '${profile.id}'. Expected: required, recommended, off.`);
|
|
292
|
+
err.code = 'CTX_PROFILE_INVALID_POLICY';
|
|
293
|
+
throw err;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return { valid: true };
|
|
298
|
+
}
|
|
299
|
+
|
|
107
300
|
/**
|
|
108
301
|
* Lists all available project profiles defined in core/profiles.
|
|
109
302
|
*
|
|
110
303
|
* @returns {ProfileConfig[]} Array of profile configurations
|
|
111
304
|
*/
|
|
112
305
|
function listProfiles() {
|
|
113
|
-
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
306
|
+
const profiles = [];
|
|
307
|
+
const searchDirs = [PROFILES_DIR, path.join(AGENTS_DIR, 'catalog', 'presets')];
|
|
308
|
+
|
|
309
|
+
for (const dir of searchDirs) {
|
|
310
|
+
if (!fs.existsSync(dir)) continue;
|
|
311
|
+
const files = fs.readdirSync(dir).filter(f => f.endsWith('.yaml') || f.endsWith('.yml'));
|
|
312
|
+
for (const file of files) {
|
|
313
|
+
const content = fs.readFileSync(path.join(dir, file), 'utf8');
|
|
314
|
+
const parsed = parseYamlProfile(content);
|
|
315
|
+
if (!parsed.id) parsed.id = path.basename(file, path.extname(file));
|
|
316
|
+
if (!parsed.name) parsed.name = parsed.id;
|
|
317
|
+
profiles.push(parsed);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return profiles;
|
|
122
321
|
}
|
|
123
322
|
|
|
124
323
|
/**
|
|
@@ -138,48 +337,289 @@ function getProfileLockPath(projectDir = process.cwd()) {
|
|
|
138
337
|
return path.join(projectDir, '.agents', 'profile.json');
|
|
139
338
|
}
|
|
140
339
|
|
|
141
|
-
|
|
340
|
+
/**
|
|
341
|
+
* Gets the active profile, taking package overrides into account when a file or scope is passed.
|
|
342
|
+
*
|
|
343
|
+
* @param {string} [projectDir=process.cwd()] - Project directory
|
|
344
|
+
* @param {string} [targetFileOrScope] - Optional file path or package scope
|
|
345
|
+
* @returns {Object|null} Active profile config or null
|
|
346
|
+
*/
|
|
347
|
+
function getActiveProfile(projectDir = process.cwd(), targetFileOrScope = null) {
|
|
142
348
|
const lockPath = getProfileLockPath(projectDir);
|
|
143
349
|
if (!fs.existsSync(lockPath)) return null;
|
|
350
|
+
let lockData = null;
|
|
144
351
|
try {
|
|
145
352
|
const raw = fs.readFileSync(lockPath, 'utf8');
|
|
146
|
-
|
|
353
|
+
lockData = JSON.parse(raw);
|
|
147
354
|
} catch {
|
|
148
355
|
return null;
|
|
149
356
|
}
|
|
357
|
+
if (!lockData) return null;
|
|
358
|
+
|
|
359
|
+
// Check for monorepo package override
|
|
360
|
+
if (targetFileOrScope && lockData.overrides && typeof lockData.overrides === 'object') {
|
|
361
|
+
const normalizedScope = targetFileOrScope.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
362
|
+
|
|
363
|
+
// 1. Direct key match in overrides
|
|
364
|
+
let matchedProfileName = lockData.overrides[normalizedScope];
|
|
365
|
+
let matchedScope = normalizedScope;
|
|
366
|
+
|
|
367
|
+
// 2. Prefix match in overrides
|
|
368
|
+
if (!matchedProfileName) {
|
|
369
|
+
for (const [scopeKey, profName] of Object.entries(lockData.overrides)) {
|
|
370
|
+
const cleanScope = scopeKey.replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/$/, '');
|
|
371
|
+
if (normalizedScope === cleanScope || normalizedScope.startsWith(cleanScope + '/')) {
|
|
372
|
+
matchedProfileName = profName;
|
|
373
|
+
matchedScope = cleanScope;
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// 3. Nearest package match via WorkspaceGraphBuilder
|
|
380
|
+
if (!matchedProfileName) {
|
|
381
|
+
try {
|
|
382
|
+
const { WorkspaceGraphBuilder } = require('./workspace/workspace-graph.js');
|
|
383
|
+
const builder = new WorkspaceGraphBuilder();
|
|
384
|
+
const graph = builder.build(projectDir);
|
|
385
|
+
const nearestPkg = builder.findNearestPackage(normalizedScope, graph);
|
|
386
|
+
if (nearestPkg) {
|
|
387
|
+
if (lockData.overrides[nearestPkg.name]) {
|
|
388
|
+
matchedProfileName = lockData.overrides[nearestPkg.name];
|
|
389
|
+
matchedScope = nearestPkg.name;
|
|
390
|
+
} else if (lockData.overrides[nearestPkg.root]) {
|
|
391
|
+
matchedProfileName = lockData.overrides[nearestPkg.root];
|
|
392
|
+
matchedScope = nearestPkg.root;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
} catch {
|
|
396
|
+
// ignore workspace graph failure
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (matchedProfileName) {
|
|
401
|
+
const overrideProf = getProfile(matchedProfileName);
|
|
402
|
+
if (overrideProf) {
|
|
403
|
+
return {
|
|
404
|
+
...overrideProf,
|
|
405
|
+
profile: overrideProf.id,
|
|
406
|
+
isOverride: true,
|
|
407
|
+
scope: matchedScope,
|
|
408
|
+
rootProfile: lockData.profile,
|
|
409
|
+
overrides: lockData.overrides,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return lockData;
|
|
150
416
|
}
|
|
151
417
|
|
|
152
|
-
|
|
418
|
+
/**
|
|
419
|
+
* Applies a profile to the project, supporting root apply or package-scoped overrides.
|
|
420
|
+
*
|
|
421
|
+
* @param {string} profileName - Profile slug or name
|
|
422
|
+
* @param {string} [projectDir=process.cwd()] - Project directory
|
|
423
|
+
* @param {Object} [options={}] - Options (scope, noExport)
|
|
424
|
+
* @returns {Object} Updated profile lock data
|
|
425
|
+
*/
|
|
426
|
+
function applyProfile(profileName, projectDir = process.cwd(), options = {}) {
|
|
153
427
|
const profile = getProfile(profileName);
|
|
154
428
|
if (!profile) {
|
|
155
429
|
throw new Error(`Profile '${profileName}' not found. Available profiles: ${listProfiles().map(p => p.id).join(', ')}`);
|
|
156
430
|
}
|
|
157
431
|
|
|
432
|
+
validateProfile(profile);
|
|
433
|
+
|
|
158
434
|
const lockPath = getProfileLockPath(projectDir);
|
|
159
435
|
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
|
|
160
436
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
437
|
+
let existing = {};
|
|
438
|
+
if (fs.existsSync(lockPath)) {
|
|
439
|
+
try {
|
|
440
|
+
existing = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
|
|
441
|
+
} catch {
|
|
442
|
+
existing = {};
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
let lockData;
|
|
447
|
+
if (options.scope) {
|
|
448
|
+
const scope = options.scope.replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/$/, '');
|
|
449
|
+
const overrides = existing.overrides || {};
|
|
450
|
+
overrides[scope] = profile.id;
|
|
451
|
+
|
|
452
|
+
lockData = {
|
|
453
|
+
...existing,
|
|
454
|
+
overrides,
|
|
455
|
+
updatedAt: new Date().toISOString(),
|
|
456
|
+
};
|
|
457
|
+
if (!lockData.profile) {
|
|
458
|
+
lockData.profile = profile.id;
|
|
459
|
+
lockData.name = profile.name;
|
|
460
|
+
lockData.description = profile.description;
|
|
461
|
+
lockData.skills = profile.skills;
|
|
462
|
+
lockData.policy = profile.policy;
|
|
463
|
+
lockData.generation = profile.generation;
|
|
464
|
+
lockData.technologyDefaults = profile.technologyDefaults;
|
|
465
|
+
lockData.exclude_skills = profile.exclude_skills || [];
|
|
466
|
+
lockData.prefer_skills = profile.prefer_skills || [];
|
|
467
|
+
lockData.require_skills = profile.require_skills || [];
|
|
468
|
+
lockData.enforce = profile.enforce || {};
|
|
469
|
+
lockData.defaults = profile.defaults || {};
|
|
470
|
+
}
|
|
471
|
+
} else {
|
|
472
|
+
lockData = {
|
|
473
|
+
schemaVersion: 2,
|
|
474
|
+
profile: profile.id,
|
|
475
|
+
name: profile.name,
|
|
476
|
+
description: profile.description,
|
|
477
|
+
skills: profile.skills || {
|
|
478
|
+
required: profile.require_skills || [],
|
|
479
|
+
preferred: profile.prefer_skills || [],
|
|
480
|
+
excluded: profile.exclude_skills || [],
|
|
481
|
+
},
|
|
482
|
+
policy: profile.policy || {},
|
|
483
|
+
generation: profile.generation || {
|
|
484
|
+
documents: profile.generate_docs || [],
|
|
485
|
+
},
|
|
486
|
+
technologyDefaults: profile.technologyDefaults || profile.defaults || {},
|
|
487
|
+
exclude_skills: profile.exclude_skills || [],
|
|
488
|
+
prefer_skills: profile.prefer_skills || [],
|
|
489
|
+
require_skills: profile.require_skills || [],
|
|
490
|
+
enforce: profile.enforce || {},
|
|
491
|
+
defaults: profile.defaults || {},
|
|
492
|
+
overrides: existing.overrides || {},
|
|
493
|
+
appliedAt: new Date().toISOString(),
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const lock = new ProjectMutationLock(projectDir);
|
|
498
|
+
const lockToken = lock.acquire({ command: `profile apply ${profile.id}` });
|
|
499
|
+
|
|
500
|
+
try {
|
|
501
|
+
const tx = new JournaledTransaction(projectDir);
|
|
502
|
+
tx.stageWrite(path.relative(projectDir, lockPath).replace(/\\/g, '/'), JSON.stringify(lockData, null, 2) + '\n');
|
|
503
|
+
tx.commit();
|
|
504
|
+
} finally {
|
|
505
|
+
lock.release(lockToken);
|
|
506
|
+
}
|
|
171
507
|
|
|
172
|
-
fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2) + '\n');
|
|
173
508
|
return lockData;
|
|
174
509
|
}
|
|
175
510
|
|
|
176
|
-
|
|
511
|
+
/**
|
|
512
|
+
* Removes active profile lock or a specific package override.
|
|
513
|
+
*
|
|
514
|
+
* @param {string} [projectDir=process.cwd()] - Project directory
|
|
515
|
+
* @param {Object} [options={}] - Options (scope)
|
|
516
|
+
* @returns {boolean} True if removed
|
|
517
|
+
*/
|
|
518
|
+
function removeActiveProfile(projectDir = process.cwd(), options = {}) {
|
|
177
519
|
const lockPath = getProfileLockPath(projectDir);
|
|
178
|
-
if (fs.existsSync(lockPath))
|
|
179
|
-
|
|
180
|
-
|
|
520
|
+
if (!fs.existsSync(lockPath)) return false;
|
|
521
|
+
|
|
522
|
+
if (options && options.scope) {
|
|
523
|
+
try {
|
|
524
|
+
const lockData = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
|
|
525
|
+
const cleanScope = options.scope.replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/$/, '');
|
|
526
|
+
if (lockData.overrides && lockData.overrides[cleanScope]) {
|
|
527
|
+
delete lockData.overrides[cleanScope];
|
|
528
|
+
|
|
529
|
+
const lock = new ProjectMutationLock(projectDir);
|
|
530
|
+
const lockToken = lock.acquire({ command: 'profile remove override' });
|
|
531
|
+
try {
|
|
532
|
+
const tx = new JournaledTransaction(projectDir);
|
|
533
|
+
tx.stageWrite(path.relative(projectDir, lockPath).replace(/\\/g, '/'), JSON.stringify(lockData, null, 2) + '\n');
|
|
534
|
+
tx.commit();
|
|
535
|
+
} finally {
|
|
536
|
+
lock.release(lockToken);
|
|
537
|
+
}
|
|
538
|
+
return true;
|
|
539
|
+
}
|
|
540
|
+
} catch {
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
return false;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
const lock = new ProjectMutationLock(projectDir);
|
|
547
|
+
const lockToken = lock.acquire({ command: 'profile remove' });
|
|
548
|
+
try {
|
|
549
|
+
const tx = new JournaledTransaction(projectDir);
|
|
550
|
+
tx.stageDelete(path.relative(projectDir, lockPath).replace(/\\/g, '/'));
|
|
551
|
+
tx.commit();
|
|
552
|
+
} finally {
|
|
553
|
+
lock.release(lockToken);
|
|
554
|
+
}
|
|
555
|
+
return true;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Returns structured explanation of a profile.
|
|
560
|
+
*
|
|
561
|
+
* @param {string} profileName - Profile slug or name
|
|
562
|
+
* @param {string} [projectDir=process.cwd()] - Project directory
|
|
563
|
+
* @returns {Object} Explanation structure
|
|
564
|
+
*/
|
|
565
|
+
function explainProfile(profileName, projectDir = process.cwd()) {
|
|
566
|
+
const profile = getProfile(profileName);
|
|
567
|
+
if (!profile) {
|
|
568
|
+
throw new Error(`Profile '${profileName}' not found. Available profiles: ${listProfiles().map(p => p.id).join(', ')}`);
|
|
181
569
|
}
|
|
182
|
-
|
|
570
|
+
|
|
571
|
+
return {
|
|
572
|
+
schemaVersion: profile.schemaVersion || 2,
|
|
573
|
+
id: profile.id,
|
|
574
|
+
name: profile.name,
|
|
575
|
+
description: profile.description,
|
|
576
|
+
skills: {
|
|
577
|
+
required: profile.skills?.required || profile.require_skills || [],
|
|
578
|
+
preferred: profile.skills?.preferred || profile.prefer_skills || [],
|
|
579
|
+
excluded: profile.skills?.excluded || profile.exclude_skills || [],
|
|
580
|
+
},
|
|
581
|
+
policy: profile.policy || {},
|
|
582
|
+
technologyDefaults: profile.technologyDefaults || profile.defaults || {},
|
|
583
|
+
generation: {
|
|
584
|
+
documents: profile.generation?.documents || profile.generate_docs || [],
|
|
585
|
+
},
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Formats structured profile explanation into human-readable text.
|
|
591
|
+
*
|
|
592
|
+
* @param {Object} explanation - Profile explanation object
|
|
593
|
+
* @returns {string} Formatted text
|
|
594
|
+
*/
|
|
595
|
+
function formatProfileExplanation(explanation) {
|
|
596
|
+
const lines = [];
|
|
597
|
+
lines.push(`Profile: ${explanation.name} (${explanation.id})`);
|
|
598
|
+
lines.push(`Description: ${explanation.description}`);
|
|
599
|
+
lines.push('');
|
|
600
|
+
lines.push('Skills:');
|
|
601
|
+
lines.push(` Required: ${explanation.skills.required.length > 0 ? explanation.skills.required.join(', ') : 'none'}`);
|
|
602
|
+
lines.push(` Preferred: ${explanation.skills.preferred.length > 0 ? explanation.skills.preferred.join(', ') : 'none'}`);
|
|
603
|
+
lines.push(` Excluded: ${explanation.skills.excluded.length > 0 ? explanation.skills.excluded.join(', ') : 'none'}`);
|
|
604
|
+
lines.push('');
|
|
605
|
+
lines.push('Policy:');
|
|
606
|
+
for (const [key, val] of Object.entries(explanation.policy)) {
|
|
607
|
+
lines.push(` ${(key.charAt(0).toUpperCase() + key.slice(1)).padEnd(14)}: ${val}`);
|
|
608
|
+
}
|
|
609
|
+
lines.push('');
|
|
610
|
+
lines.push('Technology Defaults:');
|
|
611
|
+
const techKeys = Object.keys(explanation.technologyDefaults);
|
|
612
|
+
if (techKeys.length === 0) {
|
|
613
|
+
lines.push(' none');
|
|
614
|
+
} else {
|
|
615
|
+
for (const [key, val] of Object.entries(explanation.technologyDefaults)) {
|
|
616
|
+
lines.push(` ${key.padEnd(14)}: ${val}`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
lines.push('');
|
|
620
|
+
lines.push('Generation Documents:');
|
|
621
|
+
lines.push(` ${explanation.generation.documents.join(', ') || 'none'}`);
|
|
622
|
+
return lines.join('\n');
|
|
183
623
|
}
|
|
184
624
|
|
|
185
625
|
/**
|
|
@@ -263,18 +703,10 @@ function detectStack(projectDir = process.cwd()) {
|
|
|
263
703
|
}
|
|
264
704
|
|
|
265
705
|
// Infer recommended profile
|
|
266
|
-
let recommendedProfile = '
|
|
706
|
+
let recommendedProfile = 'init';
|
|
267
707
|
const hasFrontend = detected.some(d => ['React', 'Next.js', 'Tailwind CSS'].includes(d));
|
|
268
708
|
const hasBackend = detected.some(d => ['Node.js Backend', 'NestJS', 'Python'].includes(d));
|
|
269
709
|
|
|
270
|
-
if (hasFrontend && !hasBackend) {
|
|
271
|
-
recommendedProfile = 'frontend';
|
|
272
|
-
} else if (!hasFrontend && hasBackend) {
|
|
273
|
-
recommendedProfile = 'backend';
|
|
274
|
-
} else if (hasFrontend && hasBackend) {
|
|
275
|
-
recommendedProfile = 'startup';
|
|
276
|
-
}
|
|
277
|
-
|
|
278
710
|
return {
|
|
279
711
|
detected,
|
|
280
712
|
recommendedProfile,
|
|
@@ -290,4 +722,7 @@ module.exports = {
|
|
|
290
722
|
removeActiveProfile,
|
|
291
723
|
detectStack,
|
|
292
724
|
parseYamlProfile,
|
|
725
|
+
validateProfile,
|
|
726
|
+
explainProfile,
|
|
727
|
+
formatProfileExplanation,
|
|
293
728
|
};
|