contextos-agents 1.6.1 → 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 +6 -1
- package/.agents/adapters/aider/export.js +117 -97
- package/.agents/adapters/claude/export.js +68 -26
- package/.agents/adapters/copilot/export.js +90 -51
- package/.agents/adapters/cursor/export.js +83 -68
- 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 +109 -62
- 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/references/context-rules.md +59 -0
- package/.agents/core/skills/context-manager/skill.yaml +10 -5
- package/.agents/core/skills/context-os/SKILL.md +3 -6
- package/.agents/core/skills/context-os/skill.yaml +14 -8
- package/.agents/core/skills/engineering-workflow/SKILL.md +1 -1
- package/.agents/core/skills/engineering-workflow/skill.yaml +7 -7
- package/.agents/core/skills/gemini-precision/SKILL.md +4 -0
- package/.agents/core/skills/gemini-precision/skill.yaml +5 -6
- package/.agents/core/skills/gstack-roles/SKILL.md +3 -1
- package/.agents/core/skills/gstack-roles/skill.yaml +9 -6
- package/.agents/core/skills/ponytail-mindset/skill.yaml +7 -7
- package/.agents/core/skills/security/skill.yaml +21 -2
- package/.agents/ctx.js +587 -111
- package/.agents/customization-dx.js +282 -0
- package/.agents/doctor.js +877 -33
- 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/claude/skills/engineering-workflow/SKILL.md +1 -1
- package/.agents/generated/claude/skills/gemini-precision/SKILL.md +4 -0
- package/.agents/generated/claude/skills/gstack-roles/SKILL.md +3 -1
- package/.agents/generated/gemini/skills/context-os/SKILL.md +2 -2
- package/.agents/generated/gemini/skills/engineering-workflow/SKILL.md +1 -1
- package/.agents/generated/gemini/skills/gemini-precision/SKILL.md +4 -0
- package/.agents/generated/gemini/skills/gstack-roles/SKILL.md +3 -1
- package/.agents/plugins/contextos/hooks.json +25 -0
- package/.agents/plugins/contextos/plugin.json +19 -0
- package/.agents/plugins.js +432 -73
- package/.agents/profiles.js +507 -46
- package/.agents/resolver.js +50 -414
- 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/stats.js +22 -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 +143 -14
- 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/uninstall.js +207 -0
- package/bin/commands/update.js +325 -0
- package/bin/commands.js +342 -0
- package/bin/index.js +326 -149
- package/bin/lib/detector.js +106 -0
- package/bin/lib/lockfile.js +253 -0
- package/bin/lib/safe-writer.js +290 -0
- package/package.json +85 -73
- package/registry.json +15 -7
- package/registry.schema.json +3 -1
- 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 -10
- 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 -8
- 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 -8
- 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 -25
- 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 -10
- 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 -10
- 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 -23
- 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 -10
- 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 -10
- 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 -13
- 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 -14
- 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 -8
- 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 -10
- 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 -8
- 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 -10
- 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 -10
- 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 -10
- 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 -10
- 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 -10
- 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 -10
- 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 -8
- 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 -8
- 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 -22
- package/.agents/core/skills/subagent-orchestrator/SKILL.md +0 -100
- package/.agents/core/skills/subagent-orchestrator/VALIDATION.json +0 -12
- package/.agents/core/skills/subagent-orchestrator/skill.yaml +0 -8
- 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 -13
- 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 -26
- 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 -10
- 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 -10
- 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 -13
- 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 -10
- 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/skill.yaml +0 -10
- 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 -10
- 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 -93
- 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 -99
- 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/mcp/runtime.py +0 -470
- package/.agents/mcp/server.mjs +0 -189271
- package/benchmarks/gemini-issues.js +0 -533
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,29 +101,223 @@ function parseYamlProfile(text) {
|
|
|
72
101
|
.filter(Boolean);
|
|
73
102
|
}
|
|
74
103
|
|
|
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);
|
|
107
|
+
if (!match) return {};
|
|
108
|
+
const map = {};
|
|
109
|
+
const lines = match[1].split('\n');
|
|
110
|
+
for (const line of lines) {
|
|
111
|
+
const lineClean = line.replace(/#.*$/, '').trim();
|
|
112
|
+
if (!lineClean) continue;
|
|
113
|
+
const kv = lineClean.match(/^([a-zA-Z0-9_-]+):\s*(.*)$/);
|
|
114
|
+
if (kv) {
|
|
115
|
+
const key = kv[1].trim();
|
|
116
|
+
let val = kv[2].trim().replace(/^['"]|['"]$/g, '');
|
|
117
|
+
if (val === 'true') val = true;
|
|
118
|
+
else if (val === 'false') val = false;
|
|
119
|
+
else if (/^\d+$/.test(val)) val = parseInt(val, 10);
|
|
120
|
+
map[key] = val;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return map;
|
|
124
|
+
}
|
|
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
|
|
75
160
|
result.prefer_skills = parseList('prefer_skills');
|
|
76
161
|
result.exclude_skills = parseList('exclude_skills');
|
|
162
|
+
result.require_skills = parseList('require_skills');
|
|
77
163
|
result.generate_docs = parseList('generate_docs');
|
|
78
164
|
result.skip_docs = parseList('skip_docs');
|
|
165
|
+
result.enforce = parseMap('enforce');
|
|
166
|
+
result.defaults = parseMap('defaults');
|
|
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
|
+
}
|
|
79
224
|
|
|
80
225
|
return result;
|
|
81
226
|
}
|
|
82
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
|
+
|
|
83
300
|
/**
|
|
84
301
|
* Lists all available project profiles defined in core/profiles.
|
|
85
302
|
*
|
|
86
303
|
* @returns {ProfileConfig[]} Array of profile configurations
|
|
87
304
|
*/
|
|
88
305
|
function listProfiles() {
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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;
|
|
98
321
|
}
|
|
99
322
|
|
|
100
323
|
/**
|
|
@@ -114,46 +337,289 @@ function getProfileLockPath(projectDir = process.cwd()) {
|
|
|
114
337
|
return path.join(projectDir, '.agents', 'profile.json');
|
|
115
338
|
}
|
|
116
339
|
|
|
117
|
-
|
|
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) {
|
|
118
348
|
const lockPath = getProfileLockPath(projectDir);
|
|
119
349
|
if (!fs.existsSync(lockPath)) return null;
|
|
350
|
+
let lockData = null;
|
|
120
351
|
try {
|
|
121
352
|
const raw = fs.readFileSync(lockPath, 'utf8');
|
|
122
|
-
|
|
353
|
+
lockData = JSON.parse(raw);
|
|
123
354
|
} catch {
|
|
124
355
|
return null;
|
|
125
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;
|
|
126
416
|
}
|
|
127
417
|
|
|
128
|
-
|
|
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 = {}) {
|
|
129
427
|
const profile = getProfile(profileName);
|
|
130
428
|
if (!profile) {
|
|
131
429
|
throw new Error(`Profile '${profileName}' not found. Available profiles: ${listProfiles().map(p => p.id).join(', ')}`);
|
|
132
430
|
}
|
|
133
431
|
|
|
432
|
+
validateProfile(profile);
|
|
433
|
+
|
|
134
434
|
const lockPath = getProfileLockPath(projectDir);
|
|
135
435
|
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
|
|
136
436
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
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
|
+
}
|
|
145
507
|
|
|
146
|
-
fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2) + '\n');
|
|
147
508
|
return lockData;
|
|
148
509
|
}
|
|
149
510
|
|
|
150
|
-
|
|
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 = {}) {
|
|
151
519
|
const lockPath = getProfileLockPath(projectDir);
|
|
152
|
-
if (fs.existsSync(lockPath))
|
|
153
|
-
|
|
154
|
-
|
|
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;
|
|
155
544
|
}
|
|
156
|
-
|
|
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(', ')}`);
|
|
569
|
+
}
|
|
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');
|
|
157
623
|
}
|
|
158
624
|
|
|
159
625
|
/**
|
|
@@ -237,18 +703,10 @@ function detectStack(projectDir = process.cwd()) {
|
|
|
237
703
|
}
|
|
238
704
|
|
|
239
705
|
// Infer recommended profile
|
|
240
|
-
let recommendedProfile = '
|
|
706
|
+
let recommendedProfile = 'init';
|
|
241
707
|
const hasFrontend = detected.some(d => ['React', 'Next.js', 'Tailwind CSS'].includes(d));
|
|
242
708
|
const hasBackend = detected.some(d => ['Node.js Backend', 'NestJS', 'Python'].includes(d));
|
|
243
709
|
|
|
244
|
-
if (hasFrontend && !hasBackend) {
|
|
245
|
-
recommendedProfile = 'frontend';
|
|
246
|
-
} else if (!hasFrontend && hasBackend) {
|
|
247
|
-
recommendedProfile = 'backend';
|
|
248
|
-
} else if (hasFrontend && hasBackend) {
|
|
249
|
-
recommendedProfile = 'startup';
|
|
250
|
-
}
|
|
251
|
-
|
|
252
710
|
return {
|
|
253
711
|
detected,
|
|
254
712
|
recommendedProfile,
|
|
@@ -264,4 +722,7 @@ module.exports = {
|
|
|
264
722
|
removeActiveProfile,
|
|
265
723
|
detectStack,
|
|
266
724
|
parseYamlProfile,
|
|
725
|
+
validateProfile,
|
|
726
|
+
explainProfile,
|
|
727
|
+
formatProfileExplanation,
|
|
267
728
|
};
|