mova-claude-import 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -29
- package/control_surface_exclusions_v0.json +8 -0
- package/dist/anthropic_profile_v0.d.ts +1 -1
- package/dist/anthropic_profile_v0.js +1 -0
- package/dist/cli.js +206 -23
- package/dist/control_apply_v0.d.ts +5 -1
- package/dist/control_apply_v0.js +125 -8
- package/dist/control_check_v0.d.ts +1 -0
- package/dist/control_check_v0.js +194 -23
- package/dist/control_prefill_v0.js +128 -9
- package/dist/control_surface_coverage_v0.d.ts +22 -0
- package/dist/control_surface_coverage_v0.js +128 -0
- package/dist/control_v0.d.ts +149 -0
- package/dist/control_v0.js +360 -0
- package/dist/control_v0_schema.d.ts +6 -0
- package/dist/control_v0_schema.js +19 -0
- package/dist/init_v0.d.ts +6 -1
- package/dist/init_v0.js +41 -1
- package/dist/observability_writer_v0.d.ts +1 -0
- package/dist/observability_writer_v0.js +157 -0
- package/dist/observe_v0.d.ts +8 -0
- package/dist/observe_v0.js +57 -0
- package/dist/presets_v0.d.ts +11 -0
- package/dist/presets_v0.js +52 -0
- package/dist/redaction.js +5 -1
- package/dist/run_import.js +111 -26
- package/docs/CLAUDE_CONTROL_SURFACE_MAP_v0.md +78 -0
- package/docs/OPERATOR_GUIDE_v0.md +11 -0
- package/fixtures/pos/basic/mova/control_v0.json +93 -0
- package/fixtures/pos/claude_code_demo_full/.claude/agents/code-reviewer.md +13 -0
- package/fixtures/pos/claude_code_demo_full/.claude/agents/github-workflow.md +10 -0
- package/fixtures/pos/claude_code_demo_full/.claude/commands/code-quality.md +8 -0
- package/fixtures/pos/claude_code_demo_full/.claude/commands/docs-sync.md +8 -0
- package/fixtures/pos/claude_code_demo_full/.claude/commands/onboard.md +8 -0
- package/fixtures/pos/claude_code_demo_full/.claude/commands/pr-review.md +10 -0
- package/fixtures/pos/claude_code_demo_full/.claude/commands/pr-summary.md +8 -0
- package/fixtures/pos/claude_code_demo_full/.claude/commands/ticket.md +10 -0
- package/fixtures/pos/claude_code_demo_full/.claude/hooks/skill-eval.js +13 -0
- package/fixtures/pos/claude_code_demo_full/.claude/hooks/skill-eval.sh +15 -0
- package/fixtures/pos/claude_code_demo_full/.claude/hooks/skill-rules.json +21 -0
- package/fixtures/pos/claude_code_demo_full/.claude/hooks/skill-rules.schema.json +24 -0
- package/fixtures/pos/claude_code_demo_full/.claude/rules/code-style.md +5 -0
- package/fixtures/pos/claude_code_demo_full/.claude/rules/security.md +5 -0
- package/fixtures/pos/claude_code_demo_full/.claude/settings.json +102 -0
- package/fixtures/pos/claude_code_demo_full/.claude/settings.md +6 -0
- package/fixtures/pos/claude_code_demo_full/.claude/skills/core-components/SKILL.md +9 -0
- package/fixtures/pos/claude_code_demo_full/.claude/skills/formik-patterns/SKILL.md +9 -0
- package/fixtures/pos/claude_code_demo_full/.claude/skills/graphql-schema/SKILL.md +10 -0
- package/fixtures/pos/claude_code_demo_full/.claude/skills/react-ui-patterns/SKILL.md +10 -0
- package/fixtures/pos/claude_code_demo_full/.claude/skills/systematic-debugging/SKILL.md +9 -0
- package/fixtures/pos/claude_code_demo_full/.claude/skills/testing-patterns/SKILL.md +10 -0
- package/fixtures/pos/claude_code_demo_full/.github/workflows/pr-claude-code-review.yml +14 -0
- package/fixtures/pos/claude_code_demo_full/.github/workflows/scheduled-claude-code-dependency-audit.yml +14 -0
- package/fixtures/pos/claude_code_demo_full/.github/workflows/scheduled-claude-code-docs-sync.yml +14 -0
- package/fixtures/pos/claude_code_demo_full/.github/workflows/scheduled-claude-code-quality.yml +14 -0
- package/fixtures/pos/claude_code_demo_full/.mcp.json +44 -0
- package/fixtures/pos/claude_code_demo_full/CLAUDE.md +28 -0
- package/fixtures/pos/control_basic_project/mova/control_v0.json +93 -0
- package/fixtures/pos/observability_basic/CLAUDE.md +3 -0
- package/{.tmp_test_zip/out1 → fixtures/pos/preset_safe_observable_v0}/.mcp.json +3 -3
- package/fixtures/pos/preset_safe_observable_v0/CLAUDE.md +3 -0
- package/package.json +2 -1
- package/presets/safe_observable_v0/assets/.claude/agents/code-reviewer.md +9 -0
- package/presets/safe_observable_v0/assets/.claude/commands/finish.md +9 -0
- package/presets/safe_observable_v0/assets/.claude/commands/start.md +9 -0
- package/presets/safe_observable_v0/assets/.claude/hooks/skill-eval.js +15 -0
- package/presets/safe_observable_v0/assets/.claude/hooks/skill-eval.sh +17 -0
- package/presets/safe_observable_v0/assets/.claude/hooks/skill-rules.json +26 -0
- package/presets/safe_observable_v0/assets/.claude/rules/code-style.md +6 -0
- package/presets/safe_observable_v0/assets/.claude/rules/security.md +6 -0
- package/presets/safe_observable_v0/assets/.claude/skills/git-workflow/SKILL.md +9 -0
- package/presets/safe_observable_v0/assets/.claude/skills/security-basics/SKILL.md +9 -0
- package/presets/safe_observable_v0/assets/.claude/skills/systematic-debugging/SKILL.md +9 -0
- package/presets/safe_observable_v0/assets/.claude/skills/testing-patterns/SKILL.md +9 -0
- package/presets/safe_observable_v0/control_v0.json +214 -0
- package/schemas/mova.control_v0.schema.json +252 -0
- package/src/anthropic_profile_v0.ts +1 -0
- package/src/cli.ts +194 -23
- package/src/control_apply_v0.ts +131 -8
- package/src/control_check_v0.ts +203 -23
- package/src/control_prefill_v0.ts +136 -8
- package/src/control_surface_coverage_v0.ts +164 -0
- package/src/control_v0.ts +808 -0
- package/src/control_v0_schema.ts +26 -0
- package/src/init_v0.ts +48 -1
- package/src/observability_writer_v0.ts +157 -0
- package/src/observe_v0.ts +64 -0
- package/src/presets_v0.ts +58 -0
- package/src/redaction.ts +6 -1
- package/src/run_import.ts +132 -26
- package/test/control_demo_full_roundtrip.test.js +92 -0
- package/test/control_surface_coverage_v0.test.js +36 -0
- package/test/control_v0_schema_validation.test.js +69 -0
- package/test/init_v0.test.js +9 -0
- package/test/observability_writer_v0.test.js +59 -0
- package/test/preset_safe_observable_v0.test.js +55 -0
- package/test/profile_v0_output.test.js +1 -0
- package/test/scaffold_v0_output.test.js +1 -0
- package/tools/control_surface_coverage_v0.mjs +27 -0
- package/tools/smoke_v0.mjs +33 -0
- package/.tmp_test_control_apply/proj/.claude/agents/example_agent.md +0 -3
- package/.tmp_test_control_apply/proj/.claude/commands/example_command.md +0 -3
- package/.tmp_test_control_apply/proj/.claude/hooks/example_hook.sh +0 -2
- package/.tmp_test_control_apply/proj/.claude/output-styles/example_style.md +0 -3
- package/.tmp_test_control_apply/proj/.claude/settings.json +0 -30
- package/.tmp_test_control_apply/proj/.claude/settings.local.example.json +0 -3
- package/.tmp_test_control_apply/proj/.mcp.json +0 -3
- package/.tmp_test_control_apply/proj/CLAUDE.md +0 -13
- package/.tmp_test_control_apply/proj/MOVA.md +0 -3
- package/.tmp_test_control_check/proj/.mcp.json +0 -1
- package/.tmp_test_control_check/proj/CLAUDE.md +0 -1
- package/.tmp_test_control_prefill/out1/claude_control_profile_v0.json +0 -114
- package/.tmp_test_control_prefill/out1/prefill_report_v0.json +0 -13
- package/.tmp_test_control_prefill/out2/claude_control_profile_v0.json +0 -114
- package/.tmp_test_control_prefill/out2/prefill_report_v0.json +0 -13
- package/.tmp_test_overlay/proj/.claude/skills/a.md +0 -1
- package/.tmp_test_overlay/proj/.mcp.json +0 -1
- package/.tmp_test_overlay/proj/CLAUDE.md +0 -1
- package/.tmp_test_profile/proj/.claude/skills/a.md +0 -1
- package/.tmp_test_profile/proj/.mcp.json +0 -1
- package/.tmp_test_profile/proj/CLAUDE.md +0 -1
- package/.tmp_test_scaffold_apply/proj/.claude/agents/example_agent.md +0 -3
- package/.tmp_test_scaffold_apply/proj/.claude/commands/example_command.md +0 -3
- package/.tmp_test_scaffold_apply/proj/.claude/hooks/example_hook.sh +0 -2
- package/.tmp_test_scaffold_apply/proj/.claude/output-styles/example_style.md +0 -3
- package/.tmp_test_scaffold_apply/proj/.claude/settings.json +0 -30
- package/.tmp_test_scaffold_apply/proj/.claude/settings.local.example.json +0 -3
- package/.tmp_test_scaffold_apply/proj/.mcp.json +0 -3
- package/.tmp_test_scaffold_apply/proj/CLAUDE.md +0 -13
- package/.tmp_test_scaffold_apply/proj/MOVA.md +0 -3
- package/.tmp_test_strict/mova/claude_import/v0/VERSION.json +0 -10
- package/.tmp_test_strict/mova/claude_import/v0/episode_import_run.json +0 -20
- package/.tmp_test_strict/mova/claude_import/v0/import_manifest.json +0 -20
- package/.tmp_test_strict/mova/claude_import/v0/input_policy_report_v0.json +0 -32
- package/.tmp_test_zip/out1/.claude/agents/example_agent.md +0 -3
- package/.tmp_test_zip/out1/.claude/commands/example_command.md +0 -3
- package/.tmp_test_zip/out1/.claude/commands/mova_context.md +0 -4
- package/.tmp_test_zip/out1/.claude/commands/mova_lint.md +0 -4
- package/.tmp_test_zip/out1/.claude/commands/mova_proof.md +0 -6
- package/.tmp_test_zip/out1/.claude/hooks/example_hook.sh +0 -2
- package/.tmp_test_zip/out1/.claude/output-styles/example_style.md +0 -3
- package/.tmp_test_zip/out1/.claude/settings.json +0 -30
- package/.tmp_test_zip/out1/.claude/settings.local.example.json +0 -3
- package/.tmp_test_zip/out1/.claude/skills/a/SKILL.md +0 -1
- package/.tmp_test_zip/out1/.claude/skills/mova-control-v0/SKILL.md +0 -11
- package/.tmp_test_zip/out1/.claude/skills/mova-layer-v0/SKILL.md +0 -8
- package/.tmp_test_zip/out1/CLAUDE.md +0 -4
- package/.tmp_test_zip/out1/MOVA.md +0 -10
- package/.tmp_test_zip/out1/export.zip +0 -0
- package/.tmp_test_zip/out1/mova/claude_import/v0/VERSION.json +0 -10
- package/.tmp_test_zip/out1/mova/claude_import/v0/contracts/instruction_profile_v0.json +0 -8
- package/.tmp_test_zip/out1/mova/claude_import/v0/contracts/mcp_servers_v0.json +0 -4
- package/.tmp_test_zip/out1/mova/claude_import/v0/contracts/skills_catalog_v0.json +0 -11
- package/.tmp_test_zip/out1/mova/claude_import/v0/episode_import_run.json +0 -80
- package/.tmp_test_zip/out1/mova/claude_import/v0/export_manifest_v0.json +0 -32
- package/.tmp_test_zip/out1/mova/claude_import/v0/import_manifest.json +0 -33
- package/.tmp_test_zip/out1/mova/claude_import/v0/input_policy_report_v0.json +0 -38
- package/.tmp_test_zip/out1/mova/claude_import/v0/lint_report_v0.json +0 -6
- package/.tmp_test_zip/out1/mova/claude_import/v0/redaction_report.json +0 -4
- package/.tmp_test_zip/out2/.claude/agents/example_agent.md +0 -3
- package/.tmp_test_zip/out2/.claude/commands/example_command.md +0 -3
- package/.tmp_test_zip/out2/.claude/commands/mova_context.md +0 -4
- package/.tmp_test_zip/out2/.claude/commands/mova_lint.md +0 -4
- package/.tmp_test_zip/out2/.claude/commands/mova_proof.md +0 -6
- package/.tmp_test_zip/out2/.claude/hooks/example_hook.sh +0 -2
- package/.tmp_test_zip/out2/.claude/output-styles/example_style.md +0 -3
- package/.tmp_test_zip/out2/.claude/settings.json +0 -30
- package/.tmp_test_zip/out2/.claude/settings.local.example.json +0 -3
- package/.tmp_test_zip/out2/.claude/skills/a/SKILL.md +0 -1
- package/.tmp_test_zip/out2/.claude/skills/mova-control-v0/SKILL.md +0 -11
- package/.tmp_test_zip/out2/.claude/skills/mova-layer-v0/SKILL.md +0 -8
- package/.tmp_test_zip/out2/.mcp.json +0 -3
- package/.tmp_test_zip/out2/CLAUDE.md +0 -4
- package/.tmp_test_zip/out2/MOVA.md +0 -10
- package/.tmp_test_zip/out2/export.zip +0 -0
- package/.tmp_test_zip/out2/mova/claude_import/v0/VERSION.json +0 -10
- package/.tmp_test_zip/out2/mova/claude_import/v0/contracts/instruction_profile_v0.json +0 -8
- package/.tmp_test_zip/out2/mova/claude_import/v0/contracts/mcp_servers_v0.json +0 -4
- package/.tmp_test_zip/out2/mova/claude_import/v0/contracts/skills_catalog_v0.json +0 -11
- package/.tmp_test_zip/out2/mova/claude_import/v0/episode_import_run.json +0 -80
- package/.tmp_test_zip/out2/mova/claude_import/v0/export_manifest_v0.json +0 -32
- package/.tmp_test_zip/out2/mova/claude_import/v0/import_manifest.json +0 -33
- package/.tmp_test_zip/out2/mova/claude_import/v0/input_policy_report_v0.json +0 -38
- package/.tmp_test_zip/out2/mova/claude_import/v0/lint_report_v0.json +0 -6
- package/.tmp_test_zip/out2/mova/claude_import/v0/redaction_report.json +0 -4
- package/.tmp_test_zip/proj/.claude/skills/a.md +0 -1
- package/.tmp_test_zip/proj/.mcp.json +0 -1
- package/.tmp_test_zip/proj/CLAUDE.md +0 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
export declare const CONTROL_V0_SCHEMA_ID = "https://mova.dev/schemas/mova.control_v0.schema.json";
|
|
2
|
+
export type AssetMode = "copy_through" | "managed";
|
|
3
|
+
export type AssetItem = {
|
|
4
|
+
path: string;
|
|
5
|
+
mode: AssetMode;
|
|
6
|
+
source_path?: string;
|
|
7
|
+
};
|
|
8
|
+
export type ControlV0 = {
|
|
9
|
+
$schema?: string;
|
|
10
|
+
version: "control_v0";
|
|
11
|
+
claude_md: {
|
|
12
|
+
inject_control_entry: boolean;
|
|
13
|
+
marker: string;
|
|
14
|
+
priority_sources: string[];
|
|
15
|
+
};
|
|
16
|
+
claude_memory: {
|
|
17
|
+
enable: boolean;
|
|
18
|
+
sources: string[];
|
|
19
|
+
};
|
|
20
|
+
overlay: {
|
|
21
|
+
enable: boolean;
|
|
22
|
+
};
|
|
23
|
+
settings: {
|
|
24
|
+
include_co_authored_by: boolean;
|
|
25
|
+
env: Record<string, string>;
|
|
26
|
+
};
|
|
27
|
+
mcp: {
|
|
28
|
+
servers: any;
|
|
29
|
+
enable_all_project_mcp_servers: boolean;
|
|
30
|
+
enabled_mcpjson_servers: string[];
|
|
31
|
+
env_substitutions: {
|
|
32
|
+
format: string;
|
|
33
|
+
default_format: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
policy: {
|
|
37
|
+
mode: string;
|
|
38
|
+
hooks: {
|
|
39
|
+
enable: boolean;
|
|
40
|
+
on_invalid_hook: string;
|
|
41
|
+
definitions: any[];
|
|
42
|
+
events: Record<string, any>;
|
|
43
|
+
};
|
|
44
|
+
permissions: {
|
|
45
|
+
allow: any[];
|
|
46
|
+
deny: any[];
|
|
47
|
+
on_conflict: string;
|
|
48
|
+
on_unknown: string;
|
|
49
|
+
};
|
|
50
|
+
plugins: {
|
|
51
|
+
enable: boolean;
|
|
52
|
+
allowed_plugin_ids: any[];
|
|
53
|
+
denied_plugin_ids: any[];
|
|
54
|
+
on_unknown: string;
|
|
55
|
+
enabled_plugins: Record<string, boolean>;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
lsp: {
|
|
59
|
+
enabled_plugins: string[];
|
|
60
|
+
config_path: string;
|
|
61
|
+
managed: boolean;
|
|
62
|
+
};
|
|
63
|
+
skill_eval: {
|
|
64
|
+
enable: boolean;
|
|
65
|
+
hooks: {
|
|
66
|
+
shell: string;
|
|
67
|
+
node: string;
|
|
68
|
+
};
|
|
69
|
+
rules_path: string;
|
|
70
|
+
scoring: {
|
|
71
|
+
threshold: number;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
observability: {
|
|
75
|
+
enable: boolean;
|
|
76
|
+
mode: string;
|
|
77
|
+
writer: {
|
|
78
|
+
type: "node";
|
|
79
|
+
script_path: string;
|
|
80
|
+
};
|
|
81
|
+
output_dir: string;
|
|
82
|
+
stdout_tail_bytes: number;
|
|
83
|
+
stderr_tail_bytes: number;
|
|
84
|
+
max_event_bytes: number;
|
|
85
|
+
tail_lines: number;
|
|
86
|
+
include_tools: string[];
|
|
87
|
+
include_events: string[];
|
|
88
|
+
};
|
|
89
|
+
assets: {
|
|
90
|
+
skills: AssetItem[];
|
|
91
|
+
agents: AssetItem[];
|
|
92
|
+
commands: AssetItem[];
|
|
93
|
+
rules: AssetItem[];
|
|
94
|
+
hooks: AssetItem[];
|
|
95
|
+
workflows: AssetItem[];
|
|
96
|
+
docs: AssetItem[];
|
|
97
|
+
dotfiles: AssetItem[];
|
|
98
|
+
schemas: AssetItem[];
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
export declare function defaultControlV0(): ControlV0;
|
|
102
|
+
export declare function normalizeControlV0(input: any): {
|
|
103
|
+
control: ControlV0;
|
|
104
|
+
defaults: string[];
|
|
105
|
+
};
|
|
106
|
+
export declare function controlFromSettingsV0(settings: any | undefined, mcp: any | undefined): {
|
|
107
|
+
control: ControlV0;
|
|
108
|
+
defaults: string[];
|
|
109
|
+
};
|
|
110
|
+
export declare function controlToSettingsV0(control: ControlV0): {
|
|
111
|
+
includeCoAuthoredBy: boolean;
|
|
112
|
+
env: Record<string, string>;
|
|
113
|
+
mcp: {
|
|
114
|
+
enableAllProjectMcpServers: boolean;
|
|
115
|
+
enabledMcpjsonServers: string[];
|
|
116
|
+
};
|
|
117
|
+
permissions: {
|
|
118
|
+
allow: any[];
|
|
119
|
+
deny: any[];
|
|
120
|
+
behavior: {
|
|
121
|
+
on_conflict: string;
|
|
122
|
+
on_unknown: string;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
plugins: {
|
|
126
|
+
enable: boolean;
|
|
127
|
+
allowed_plugin_ids: any[];
|
|
128
|
+
denied_plugin_ids: any[];
|
|
129
|
+
behavior: {
|
|
130
|
+
on_unknown: string;
|
|
131
|
+
};
|
|
132
|
+
enabledPlugins: Record<string, boolean>;
|
|
133
|
+
};
|
|
134
|
+
lsp: {
|
|
135
|
+
enabledPlugins: string[];
|
|
136
|
+
};
|
|
137
|
+
hooks: Record<string, any>;
|
|
138
|
+
claude_md: {
|
|
139
|
+
inject_control_entry: boolean;
|
|
140
|
+
marker: string;
|
|
141
|
+
};
|
|
142
|
+
};
|
|
143
|
+
export declare function controlToMcpJson(control: ControlV0): {
|
|
144
|
+
servers: any[];
|
|
145
|
+
mcpServers?: undefined;
|
|
146
|
+
} | {
|
|
147
|
+
mcpServers: Record<string, any>;
|
|
148
|
+
servers?: undefined;
|
|
149
|
+
};
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import { MOVA_CONTROL_ENTRY_MARKER } from "./mova_overlay_v0.js";
|
|
2
|
+
export const CONTROL_V0_SCHEMA_ID = "https://mova.dev/schemas/mova.control_v0.schema.json";
|
|
3
|
+
function isObject(value) {
|
|
4
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5
|
+
}
|
|
6
|
+
export function defaultControlV0() {
|
|
7
|
+
return {
|
|
8
|
+
$schema: CONTROL_V0_SCHEMA_ID,
|
|
9
|
+
version: "control_v0",
|
|
10
|
+
claude_md: {
|
|
11
|
+
inject_control_entry: true,
|
|
12
|
+
marker: MOVA_CONTROL_ENTRY_MARKER,
|
|
13
|
+
priority_sources: ["CLAUDE.md", ".claude/CLAUDE.md", "~/.claude/CLAUDE.md"],
|
|
14
|
+
},
|
|
15
|
+
claude_memory: {
|
|
16
|
+
enable: true,
|
|
17
|
+
sources: ["CLAUDE.md"],
|
|
18
|
+
},
|
|
19
|
+
overlay: {
|
|
20
|
+
enable: true,
|
|
21
|
+
},
|
|
22
|
+
settings: {
|
|
23
|
+
include_co_authored_by: true,
|
|
24
|
+
env: {},
|
|
25
|
+
},
|
|
26
|
+
mcp: {
|
|
27
|
+
servers: {},
|
|
28
|
+
enable_all_project_mcp_servers: false,
|
|
29
|
+
enabled_mcpjson_servers: [],
|
|
30
|
+
env_substitutions: {
|
|
31
|
+
format: "${VAR}",
|
|
32
|
+
default_format: "${VAR:-default}",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
policy: {
|
|
36
|
+
mode: "report_only",
|
|
37
|
+
hooks: {
|
|
38
|
+
enable: true,
|
|
39
|
+
on_invalid_hook: "report_only",
|
|
40
|
+
definitions: [],
|
|
41
|
+
events: {},
|
|
42
|
+
},
|
|
43
|
+
permissions: {
|
|
44
|
+
allow: [],
|
|
45
|
+
deny: [],
|
|
46
|
+
on_conflict: "deny_wins",
|
|
47
|
+
on_unknown: "report_only",
|
|
48
|
+
},
|
|
49
|
+
plugins: {
|
|
50
|
+
enable: true,
|
|
51
|
+
allowed_plugin_ids: [],
|
|
52
|
+
denied_plugin_ids: [],
|
|
53
|
+
on_unknown: "report_only",
|
|
54
|
+
enabled_plugins: {},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
lsp: {
|
|
58
|
+
enabled_plugins: [],
|
|
59
|
+
config_path: ".claude/lsp.json",
|
|
60
|
+
managed: false,
|
|
61
|
+
},
|
|
62
|
+
skill_eval: {
|
|
63
|
+
enable: false,
|
|
64
|
+
hooks: {
|
|
65
|
+
shell: ".claude/hooks/skill-eval.sh",
|
|
66
|
+
node: ".claude/hooks/skill-eval.js",
|
|
67
|
+
},
|
|
68
|
+
rules_path: ".claude/hooks/skill-rules.json",
|
|
69
|
+
scoring: {
|
|
70
|
+
threshold: 0.6,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
observability: {
|
|
74
|
+
enable: true,
|
|
75
|
+
mode: "report_only",
|
|
76
|
+
writer: {
|
|
77
|
+
type: "node",
|
|
78
|
+
script_path: ".claude/hooks/mova-observe.js",
|
|
79
|
+
},
|
|
80
|
+
output_dir: ".mova/episodes",
|
|
81
|
+
stdout_tail_bytes: 4000,
|
|
82
|
+
stderr_tail_bytes: 4000,
|
|
83
|
+
max_event_bytes: 20000,
|
|
84
|
+
tail_lines: 50,
|
|
85
|
+
include_tools: ["Bash", "Read", "Edit", "MultiEdit", "Write"],
|
|
86
|
+
include_events: ["PostToolUse", "UserPromptSubmit", "Stop"],
|
|
87
|
+
},
|
|
88
|
+
assets: {
|
|
89
|
+
skills: [],
|
|
90
|
+
agents: [],
|
|
91
|
+
commands: [],
|
|
92
|
+
rules: [],
|
|
93
|
+
hooks: [],
|
|
94
|
+
workflows: [],
|
|
95
|
+
docs: [],
|
|
96
|
+
dotfiles: [],
|
|
97
|
+
schemas: [],
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function coerceBoolean(value, fallback, defaults, path) {
|
|
102
|
+
if (typeof value === "boolean")
|
|
103
|
+
return value;
|
|
104
|
+
defaults.push(path);
|
|
105
|
+
return fallback;
|
|
106
|
+
}
|
|
107
|
+
function coerceString(value, fallback, defaults, path) {
|
|
108
|
+
if (typeof value === "string")
|
|
109
|
+
return value;
|
|
110
|
+
defaults.push(path);
|
|
111
|
+
return fallback;
|
|
112
|
+
}
|
|
113
|
+
function coerceNumber(value, fallback, defaults, path) {
|
|
114
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
115
|
+
return value;
|
|
116
|
+
defaults.push(path);
|
|
117
|
+
return fallback;
|
|
118
|
+
}
|
|
119
|
+
function coerceArray(value, fallback, defaults, path) {
|
|
120
|
+
if (Array.isArray(value))
|
|
121
|
+
return value;
|
|
122
|
+
defaults.push(path);
|
|
123
|
+
return fallback;
|
|
124
|
+
}
|
|
125
|
+
function coerceRecord(value, fallback, defaults, path) {
|
|
126
|
+
if (isObject(value))
|
|
127
|
+
return value;
|
|
128
|
+
defaults.push(path);
|
|
129
|
+
return fallback;
|
|
130
|
+
}
|
|
131
|
+
function coerceServers(value, fallback, defaults, path) {
|
|
132
|
+
if (Array.isArray(value))
|
|
133
|
+
return value;
|
|
134
|
+
if (isObject(value))
|
|
135
|
+
return value;
|
|
136
|
+
defaults.push(path);
|
|
137
|
+
return fallback;
|
|
138
|
+
}
|
|
139
|
+
function normalizeAssets(value, fallback, defaults, path) {
|
|
140
|
+
if (!Array.isArray(value)) {
|
|
141
|
+
defaults.push(path);
|
|
142
|
+
return fallback;
|
|
143
|
+
}
|
|
144
|
+
const normalized = [];
|
|
145
|
+
for (const item of value) {
|
|
146
|
+
if (!isObject(item) || typeof item.path !== "string")
|
|
147
|
+
continue;
|
|
148
|
+
const mode = item.mode === "managed" ? "managed" : "copy_through";
|
|
149
|
+
const entry = { path: item.path, mode };
|
|
150
|
+
if (typeof item.source_path === "string")
|
|
151
|
+
entry.source_path = item.source_path;
|
|
152
|
+
normalized.push(entry);
|
|
153
|
+
}
|
|
154
|
+
return normalized.sort((a, b) => a.path.localeCompare(b.path));
|
|
155
|
+
}
|
|
156
|
+
export function normalizeControlV0(input) {
|
|
157
|
+
const defaults = [];
|
|
158
|
+
const base = defaultControlV0();
|
|
159
|
+
const src = isObject(input) ? input : {};
|
|
160
|
+
base.$schema = CONTROL_V0_SCHEMA_ID;
|
|
161
|
+
base.version = "control_v0";
|
|
162
|
+
base.claude_md.inject_control_entry = coerceBoolean(src?.claude_md?.inject_control_entry, base.claude_md.inject_control_entry, defaults, "claude_md.inject_control_entry");
|
|
163
|
+
base.claude_md.marker = coerceString(src?.claude_md?.marker, base.claude_md.marker, defaults, "claude_md.marker");
|
|
164
|
+
base.claude_md.priority_sources = coerceArray(src?.claude_md?.priority_sources, base.claude_md.priority_sources, defaults, "claude_md.priority_sources");
|
|
165
|
+
base.claude_memory.enable = coerceBoolean(src?.claude_memory?.enable, base.claude_memory.enable, defaults, "claude_memory.enable");
|
|
166
|
+
base.claude_memory.sources = coerceArray(src?.claude_memory?.sources, base.claude_memory.sources, defaults, "claude_memory.sources");
|
|
167
|
+
base.overlay.enable = coerceBoolean(src?.overlay?.enable, base.overlay.enable, defaults, "overlay.enable");
|
|
168
|
+
base.settings.include_co_authored_by = coerceBoolean(src?.settings?.include_co_authored_by, base.settings.include_co_authored_by, defaults, "settings.include_co_authored_by");
|
|
169
|
+
base.settings.env = coerceRecord(src?.settings?.env, base.settings.env, defaults, "settings.env");
|
|
170
|
+
base.mcp.servers = coerceServers(src?.mcp?.servers, base.mcp.servers, defaults, "mcp.servers");
|
|
171
|
+
base.mcp.enable_all_project_mcp_servers = coerceBoolean(src?.mcp?.enable_all_project_mcp_servers, base.mcp.enable_all_project_mcp_servers, defaults, "mcp.enable_all_project_mcp_servers");
|
|
172
|
+
base.mcp.enabled_mcpjson_servers = coerceArray(src?.mcp?.enabled_mcpjson_servers, base.mcp.enabled_mcpjson_servers, defaults, "mcp.enabled_mcpjson_servers");
|
|
173
|
+
base.mcp.env_substitutions = coerceRecord(src?.mcp?.env_substitutions, base.mcp.env_substitutions, defaults, "mcp.env_substitutions");
|
|
174
|
+
base.mcp.env_substitutions.format = coerceString(base.mcp.env_substitutions.format, "${VAR}", defaults, "mcp.env_substitutions.format");
|
|
175
|
+
base.mcp.env_substitutions.default_format = coerceString(base.mcp.env_substitutions.default_format, "${VAR:-default}", defaults, "mcp.env_substitutions.default_format");
|
|
176
|
+
base.policy.mode = coerceString(src?.policy?.mode, base.policy.mode, defaults, "policy.mode");
|
|
177
|
+
base.policy.hooks.enable = coerceBoolean(src?.policy?.hooks?.enable, base.policy.hooks.enable, defaults, "policy.hooks.enable");
|
|
178
|
+
base.policy.hooks.on_invalid_hook = coerceString(src?.policy?.hooks?.on_invalid_hook, base.policy.hooks.on_invalid_hook, defaults, "policy.hooks.on_invalid_hook");
|
|
179
|
+
base.policy.hooks.definitions = coerceArray(src?.policy?.hooks?.definitions, base.policy.hooks.definitions, defaults, "policy.hooks.definitions");
|
|
180
|
+
base.policy.hooks.events = coerceRecord(src?.policy?.hooks?.events, base.policy.hooks.events, defaults, "policy.hooks.events");
|
|
181
|
+
base.policy.permissions.allow = coerceArray(src?.policy?.permissions?.allow, base.policy.permissions.allow, defaults, "policy.permissions.allow");
|
|
182
|
+
base.policy.permissions.deny = coerceArray(src?.policy?.permissions?.deny, base.policy.permissions.deny, defaults, "policy.permissions.deny");
|
|
183
|
+
base.policy.permissions.on_conflict = coerceString(src?.policy?.permissions?.on_conflict, base.policy.permissions.on_conflict, defaults, "policy.permissions.on_conflict");
|
|
184
|
+
base.policy.permissions.on_unknown = coerceString(src?.policy?.permissions?.on_unknown, base.policy.permissions.on_unknown, defaults, "policy.permissions.on_unknown");
|
|
185
|
+
base.policy.plugins.enable = coerceBoolean(src?.policy?.plugins?.enable, base.policy.plugins.enable, defaults, "policy.plugins.enable");
|
|
186
|
+
base.policy.plugins.allowed_plugin_ids = coerceArray(src?.policy?.plugins?.allowed_plugin_ids, base.policy.plugins.allowed_plugin_ids, defaults, "policy.plugins.allowed_plugin_ids");
|
|
187
|
+
base.policy.plugins.denied_plugin_ids = coerceArray(src?.policy?.plugins?.denied_plugin_ids, base.policy.plugins.denied_plugin_ids, defaults, "policy.plugins.denied_plugin_ids");
|
|
188
|
+
base.policy.plugins.on_unknown = coerceString(src?.policy?.plugins?.on_unknown, base.policy.plugins.on_unknown, defaults, "policy.plugins.on_unknown");
|
|
189
|
+
base.policy.plugins.enabled_plugins = coerceRecord(src?.policy?.plugins?.enabled_plugins, base.policy.plugins.enabled_plugins, defaults, "policy.plugins.enabled_plugins");
|
|
190
|
+
base.lsp.enabled_plugins = coerceArray(src?.lsp?.enabled_plugins, base.lsp.enabled_plugins, defaults, "lsp.enabled_plugins");
|
|
191
|
+
base.lsp.config_path = coerceString(src?.lsp?.config_path, base.lsp.config_path, defaults, "lsp.config_path");
|
|
192
|
+
base.lsp.managed = coerceBoolean(src?.lsp?.managed, base.lsp.managed, defaults, "lsp.managed");
|
|
193
|
+
base.skill_eval.enable = coerceBoolean(src?.skill_eval?.enable, base.skill_eval.enable, defaults, "skill_eval.enable");
|
|
194
|
+
base.skill_eval.hooks = coerceRecord(src?.skill_eval?.hooks, base.skill_eval.hooks, defaults, "skill_eval.hooks");
|
|
195
|
+
base.skill_eval.hooks.shell = coerceString(base.skill_eval.hooks.shell, defaultControlV0().skill_eval.hooks.shell, defaults, "skill_eval.hooks.shell");
|
|
196
|
+
base.skill_eval.hooks.node = coerceString(base.skill_eval.hooks.node, defaultControlV0().skill_eval.hooks.node, defaults, "skill_eval.hooks.node");
|
|
197
|
+
base.skill_eval.rules_path = coerceString(src?.skill_eval?.rules_path, base.skill_eval.rules_path, defaults, "skill_eval.rules_path");
|
|
198
|
+
base.skill_eval.scoring = coerceRecord(src?.skill_eval?.scoring, base.skill_eval.scoring, defaults, "skill_eval.scoring");
|
|
199
|
+
base.skill_eval.scoring.threshold = coerceNumber(base.skill_eval.scoring.threshold, defaultControlV0().skill_eval.scoring.threshold, defaults, "skill_eval.scoring.threshold");
|
|
200
|
+
base.observability.enable = coerceBoolean(src?.observability?.enable, base.observability.enable, defaults, "observability.enable");
|
|
201
|
+
base.observability.mode = coerceString(src?.observability?.mode, base.observability.mode, defaults, "observability.mode");
|
|
202
|
+
base.observability.writer = coerceRecord(src?.observability?.writer, base.observability.writer, defaults, "observability.writer");
|
|
203
|
+
base.observability.writer.type = "node";
|
|
204
|
+
base.observability.writer.script_path = coerceString(base.observability.writer.script_path, defaultControlV0().observability.writer.script_path, defaults, "observability.writer.script_path");
|
|
205
|
+
base.observability.output_dir = coerceString(src?.observability?.output_dir, base.observability.output_dir, defaults, "observability.output_dir");
|
|
206
|
+
base.observability.stdout_tail_bytes = coerceNumber(src?.observability?.stdout_tail_bytes, base.observability.stdout_tail_bytes, defaults, "observability.stdout_tail_bytes");
|
|
207
|
+
base.observability.stderr_tail_bytes = coerceNumber(src?.observability?.stderr_tail_bytes, base.observability.stderr_tail_bytes, defaults, "observability.stderr_tail_bytes");
|
|
208
|
+
base.observability.max_event_bytes = coerceNumber(src?.observability?.max_event_bytes, base.observability.max_event_bytes, defaults, "observability.max_event_bytes");
|
|
209
|
+
base.observability.tail_lines = coerceNumber(src?.observability?.tail_lines, base.observability.tail_lines, defaults, "observability.tail_lines");
|
|
210
|
+
base.observability.include_tools = coerceArray(src?.observability?.include_tools, base.observability.include_tools, defaults, "observability.include_tools");
|
|
211
|
+
base.observability.include_events = coerceArray(src?.observability?.include_events, base.observability.include_events, defaults, "observability.include_events");
|
|
212
|
+
base.assets.skills = normalizeAssets(src?.assets?.skills, base.assets.skills, defaults, "assets.skills");
|
|
213
|
+
base.assets.agents = normalizeAssets(src?.assets?.agents, base.assets.agents, defaults, "assets.agents");
|
|
214
|
+
base.assets.commands = normalizeAssets(src?.assets?.commands, base.assets.commands, defaults, "assets.commands");
|
|
215
|
+
base.assets.rules = normalizeAssets(src?.assets?.rules, base.assets.rules, defaults, "assets.rules");
|
|
216
|
+
base.assets.hooks = normalizeAssets(src?.assets?.hooks, base.assets.hooks, defaults, "assets.hooks");
|
|
217
|
+
base.assets.workflows = normalizeAssets(src?.assets?.workflows, base.assets.workflows, defaults, "assets.workflows");
|
|
218
|
+
base.assets.docs = normalizeAssets(src?.assets?.docs, base.assets.docs, defaults, "assets.docs");
|
|
219
|
+
base.assets.dotfiles = normalizeAssets(src?.assets?.dotfiles, base.assets.dotfiles, defaults, "assets.dotfiles");
|
|
220
|
+
base.assets.schemas = normalizeAssets(src?.assets?.schemas, base.assets.schemas, defaults, "assets.schemas");
|
|
221
|
+
return { control: base, defaults };
|
|
222
|
+
}
|
|
223
|
+
export function controlFromSettingsV0(settings, mcp) {
|
|
224
|
+
const defaults = [];
|
|
225
|
+
const base = defaultControlV0();
|
|
226
|
+
if (!settings) {
|
|
227
|
+
defaults.push("settings", "policy", "claude_md");
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
base.settings.include_co_authored_by = coerceBoolean(settings?.includeCoAuthoredBy, base.settings.include_co_authored_by, defaults, "settings.include_co_authored_by");
|
|
231
|
+
base.settings.env = coerceRecord(settings?.env, base.settings.env, defaults, "settings.env");
|
|
232
|
+
base.policy.hooks.enable = coerceBoolean(settings?.hooks?.enable ?? true, base.policy.hooks.enable, defaults, "policy.hooks.enable");
|
|
233
|
+
base.policy.hooks.on_invalid_hook = coerceString(settings?.hooks?.behavior?.on_invalid_hook, base.policy.hooks.on_invalid_hook, defaults, "policy.hooks.on_invalid_hook");
|
|
234
|
+
base.policy.hooks.definitions = coerceArray(settings?.hooks?.definitions, base.policy.hooks.definitions, defaults, "policy.hooks.definitions");
|
|
235
|
+
const events = {};
|
|
236
|
+
if (isObject(settings?.hooks)) {
|
|
237
|
+
for (const [key, value] of Object.entries(settings.hooks)) {
|
|
238
|
+
if (key === "enable" || key === "behavior" || key === "definitions")
|
|
239
|
+
continue;
|
|
240
|
+
events[key] = value;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
base.policy.hooks.events = events;
|
|
244
|
+
base.policy.permissions.allow = coerceArray(settings?.permissions?.allow, base.policy.permissions.allow, defaults, "policy.permissions.allow");
|
|
245
|
+
base.policy.permissions.deny = coerceArray(settings?.permissions?.deny, base.policy.permissions.deny, defaults, "policy.permissions.deny");
|
|
246
|
+
base.policy.permissions.on_conflict = coerceString(settings?.permissions?.behavior?.on_conflict, base.policy.permissions.on_conflict, defaults, "policy.permissions.on_conflict");
|
|
247
|
+
base.policy.permissions.on_unknown = coerceString(settings?.permissions?.behavior?.on_unknown, base.policy.permissions.on_unknown, defaults, "policy.permissions.on_unknown");
|
|
248
|
+
base.policy.plugins.enable = coerceBoolean(settings?.plugins?.enable, base.policy.plugins.enable, defaults, "policy.plugins.enable");
|
|
249
|
+
base.policy.plugins.allowed_plugin_ids = coerceArray(settings?.plugins?.allowed_plugin_ids, base.policy.plugins.allowed_plugin_ids, defaults, "policy.plugins.allowed_plugin_ids");
|
|
250
|
+
base.policy.plugins.denied_plugin_ids = coerceArray(settings?.plugins?.denied_plugin_ids, base.policy.plugins.denied_plugin_ids, defaults, "policy.plugins.denied_plugin_ids");
|
|
251
|
+
base.policy.plugins.on_unknown = coerceString(settings?.plugins?.behavior?.on_unknown, base.policy.plugins.on_unknown, defaults, "policy.plugins.on_unknown");
|
|
252
|
+
base.policy.plugins.enabled_plugins = coerceRecord(settings?.plugins?.enabledPlugins, base.policy.plugins.enabled_plugins, defaults, "policy.plugins.enabled_plugins");
|
|
253
|
+
base.claude_md.inject_control_entry = coerceBoolean(settings?.claude_md?.inject_control_entry, base.claude_md.inject_control_entry, defaults, "claude_md.inject_control_entry");
|
|
254
|
+
base.claude_md.marker = coerceString(settings?.claude_md?.marker, base.claude_md.marker, defaults, "claude_md.marker");
|
|
255
|
+
base.mcp.enable_all_project_mcp_servers = coerceBoolean(settings?.mcp?.enableAllProjectMcpServers, base.mcp.enable_all_project_mcp_servers, defaults, "mcp.enable_all_project_mcp_servers");
|
|
256
|
+
base.mcp.enabled_mcpjson_servers = coerceArray(settings?.mcp?.enabledMcpjsonServers, base.mcp.enabled_mcpjson_servers, defaults, "mcp.enabled_mcpjson_servers");
|
|
257
|
+
base.lsp.enabled_plugins = coerceArray(settings?.lsp?.enabledPlugins, base.lsp.enabled_plugins, defaults, "lsp.enabled_plugins");
|
|
258
|
+
}
|
|
259
|
+
if (!mcp) {
|
|
260
|
+
defaults.push("mcp.servers");
|
|
261
|
+
}
|
|
262
|
+
else if (isObject(mcp) && isObject(mcp.mcpServers)) {
|
|
263
|
+
base.mcp.servers = mcp.mcpServers;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
base.mcp.servers = coerceServers(mcp?.servers, base.mcp.servers, defaults, "mcp.servers");
|
|
267
|
+
}
|
|
268
|
+
return { control: base, defaults };
|
|
269
|
+
}
|
|
270
|
+
export function controlToSettingsV0(control) {
|
|
271
|
+
const hooks = {
|
|
272
|
+
enable: control.policy.hooks.enable,
|
|
273
|
+
definitions: control.policy.hooks.definitions,
|
|
274
|
+
behavior: {
|
|
275
|
+
on_invalid_hook: control.policy.hooks.on_invalid_hook,
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
for (const [event, value] of Object.entries(control.policy.hooks.events)) {
|
|
279
|
+
hooks[event] = value;
|
|
280
|
+
}
|
|
281
|
+
if (control.observability.enable && Array.isArray(control.observability.include_events)) {
|
|
282
|
+
const scriptRel = control.observability.writer.script_path.replace(/^[\\/]+/, "");
|
|
283
|
+
const baseCommand = `node \"$CLAUDE_PROJECT_DIR/${scriptRel}\"`;
|
|
284
|
+
const commonArgs = [
|
|
285
|
+
`--stdout-tail-bytes ${control.observability.stdout_tail_bytes}`,
|
|
286
|
+
`--stderr-tail-bytes ${control.observability.stderr_tail_bytes}`,
|
|
287
|
+
`--max-event-bytes ${control.observability.max_event_bytes}`,
|
|
288
|
+
`--tail-lines ${control.observability.tail_lines}`,
|
|
289
|
+
`--output-dir ${control.observability.output_dir}`,
|
|
290
|
+
].join(" ");
|
|
291
|
+
const postMatcher = control.observability.include_tools.length
|
|
292
|
+
? control.observability.include_tools.join("|")
|
|
293
|
+
: undefined;
|
|
294
|
+
const ensureEventHook = (event, matcher) => {
|
|
295
|
+
const entry = {
|
|
296
|
+
hooks: [
|
|
297
|
+
{
|
|
298
|
+
type: "command",
|
|
299
|
+
command: `${baseCommand} --event ${event} ${commonArgs}`.trim(),
|
|
300
|
+
timeout: 5,
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
};
|
|
304
|
+
if (matcher)
|
|
305
|
+
entry.matcher = matcher;
|
|
306
|
+
hooks[event] = Array.isArray(hooks[event]) ? [...hooks[event], entry] : [entry];
|
|
307
|
+
};
|
|
308
|
+
if (control.observability.include_events.includes("PostToolUse")) {
|
|
309
|
+
ensureEventHook("PostToolUse", postMatcher);
|
|
310
|
+
}
|
|
311
|
+
if (control.observability.include_events.includes("UserPromptSubmit")) {
|
|
312
|
+
ensureEventHook("UserPromptSubmit");
|
|
313
|
+
}
|
|
314
|
+
if (control.observability.include_events.includes("Stop")) {
|
|
315
|
+
ensureEventHook("Stop");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
includeCoAuthoredBy: control.settings.include_co_authored_by,
|
|
320
|
+
env: control.settings.env,
|
|
321
|
+
mcp: {
|
|
322
|
+
enableAllProjectMcpServers: control.mcp.enable_all_project_mcp_servers,
|
|
323
|
+
enabledMcpjsonServers: control.mcp.enabled_mcpjson_servers,
|
|
324
|
+
},
|
|
325
|
+
permissions: {
|
|
326
|
+
allow: control.policy.permissions.allow,
|
|
327
|
+
deny: control.policy.permissions.deny,
|
|
328
|
+
behavior: {
|
|
329
|
+
on_conflict: control.policy.permissions.on_conflict,
|
|
330
|
+
on_unknown: control.policy.permissions.on_unknown,
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
plugins: {
|
|
334
|
+
enable: control.policy.plugins.enable,
|
|
335
|
+
allowed_plugin_ids: control.policy.plugins.allowed_plugin_ids,
|
|
336
|
+
denied_plugin_ids: control.policy.plugins.denied_plugin_ids,
|
|
337
|
+
behavior: {
|
|
338
|
+
on_unknown: control.policy.plugins.on_unknown,
|
|
339
|
+
},
|
|
340
|
+
enabledPlugins: control.policy.plugins.enabled_plugins,
|
|
341
|
+
},
|
|
342
|
+
lsp: {
|
|
343
|
+
enabledPlugins: control.lsp.enabled_plugins,
|
|
344
|
+
},
|
|
345
|
+
hooks,
|
|
346
|
+
claude_md: {
|
|
347
|
+
inject_control_entry: control.claude_md.inject_control_entry,
|
|
348
|
+
marker: control.claude_md.marker,
|
|
349
|
+
},
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
export function controlToMcpJson(control) {
|
|
353
|
+
if (Array.isArray(control.mcp.servers)) {
|
|
354
|
+
return { servers: control.mcp.servers };
|
|
355
|
+
}
|
|
356
|
+
if (isObject(control.mcp.servers)) {
|
|
357
|
+
return { mcpServers: control.mcp.servers };
|
|
358
|
+
}
|
|
359
|
+
return { servers: [] };
|
|
360
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import Ajv from "ajv";
|
|
4
|
+
import addFormats from "ajv-formats";
|
|
5
|
+
import { CONTROL_V0_SCHEMA_ID } from "./control_v0.js";
|
|
6
|
+
async function loadControlSchema() {
|
|
7
|
+
const schemaPath = fileURLToPath(new URL("../schemas/mova.control_v0.schema.json", import.meta.url));
|
|
8
|
+
const raw = await fs.readFile(schemaPath, "utf8");
|
|
9
|
+
return JSON.parse(raw);
|
|
10
|
+
}
|
|
11
|
+
export async function validateControlV0Schema(control) {
|
|
12
|
+
const schema = await loadControlSchema();
|
|
13
|
+
const ajv = new Ajv({ allErrors: true, strict: true, validateSchema: false });
|
|
14
|
+
addFormats(ajv);
|
|
15
|
+
ajv.addSchema(schema, CONTROL_V0_SCHEMA_ID);
|
|
16
|
+
const validate = ajv.compile(schema);
|
|
17
|
+
const ok = Boolean(validate(control));
|
|
18
|
+
return { ok, errors: validate.errors };
|
|
19
|
+
}
|
package/dist/init_v0.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import { type ControlV0 } from "./control_v0.js";
|
|
1
2
|
type InitResult = {
|
|
2
3
|
createdFiles: string[];
|
|
3
4
|
zipRelPath?: string;
|
|
4
5
|
zipSha256?: string;
|
|
5
6
|
};
|
|
6
|
-
|
|
7
|
+
type InitOptions = {
|
|
8
|
+
controlOverride?: ControlV0;
|
|
9
|
+
assetsRoot?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function initProfileV0(outRoot: string, emitZip: boolean, options?: InitOptions): Promise<InitResult>;
|
|
7
12
|
export {};
|
package/dist/init_v0.js
CHANGED
|
@@ -4,6 +4,7 @@ import { getAnthropicProfileV0Files } from "./anthropic_profile_v0.js";
|
|
|
4
4
|
import { stableStringify } from "./stable_json.js";
|
|
5
5
|
import { createExportZipV0 } from "./export_zip_v0.js";
|
|
6
6
|
import { writeCleanClaudeProfileScaffoldV0 } from "./claude_profile_scaffold_v0.js";
|
|
7
|
+
import { defaultControlV0 } from "./control_v0.js";
|
|
7
8
|
async function writeTextFile(absPath, content) {
|
|
8
9
|
await fs.mkdir(path.dirname(absPath), { recursive: true });
|
|
9
10
|
await fs.writeFile(absPath, content, "utf8");
|
|
@@ -12,7 +13,38 @@ async function writeJsonFile(absPath, obj) {
|
|
|
12
13
|
await fs.mkdir(path.dirname(absPath), { recursive: true });
|
|
13
14
|
await fs.writeFile(absPath, stableStringify(obj) + "\n", "utf8");
|
|
14
15
|
}
|
|
15
|
-
|
|
16
|
+
async function copyPresetAssets(control, assetsRoot, outRoot) {
|
|
17
|
+
const created = [];
|
|
18
|
+
const assets = [
|
|
19
|
+
...control.assets.skills,
|
|
20
|
+
...control.assets.agents,
|
|
21
|
+
...control.assets.commands,
|
|
22
|
+
...control.assets.rules,
|
|
23
|
+
...control.assets.hooks,
|
|
24
|
+
...control.assets.workflows,
|
|
25
|
+
...control.assets.docs,
|
|
26
|
+
...control.assets.dotfiles,
|
|
27
|
+
...control.assets.schemas,
|
|
28
|
+
];
|
|
29
|
+
for (const asset of assets) {
|
|
30
|
+
const sourceRel = asset.source_path ?? asset.path;
|
|
31
|
+
const source = path.isAbsolute(sourceRel) ? sourceRel : path.join(assetsRoot, sourceRel);
|
|
32
|
+
const target = path.join(outRoot, asset.path);
|
|
33
|
+
try {
|
|
34
|
+
await fs.stat(source);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
40
|
+
if (source !== target) {
|
|
41
|
+
await fs.copyFile(source, target);
|
|
42
|
+
}
|
|
43
|
+
created.push(asset.path.replace(/\\/g, "/"));
|
|
44
|
+
}
|
|
45
|
+
return created.sort();
|
|
46
|
+
}
|
|
47
|
+
export async function initProfileV0(outRoot, emitZip, options) {
|
|
16
48
|
const createdFiles = [];
|
|
17
49
|
await writeCleanClaudeProfileScaffoldV0(outRoot);
|
|
18
50
|
const profileFiles = getAnthropicProfileV0Files();
|
|
@@ -22,6 +54,14 @@ export async function initProfileV0(outRoot, emitZip) {
|
|
|
22
54
|
await writeTextFile(path.join(outRoot, rel), content);
|
|
23
55
|
createdFiles.push(rel);
|
|
24
56
|
}
|
|
57
|
+
const controlRel = path.join("mova", "control_v0.json").replace(/\\/g, "/");
|
|
58
|
+
const control = options?.controlOverride ?? defaultControlV0();
|
|
59
|
+
await writeJsonFile(path.join(outRoot, controlRel), control);
|
|
60
|
+
createdFiles.push(controlRel);
|
|
61
|
+
if (options?.assetsRoot) {
|
|
62
|
+
const assetFiles = await copyPresetAssets(control, options.assetsRoot, outRoot);
|
|
63
|
+
createdFiles.push(...assetFiles);
|
|
64
|
+
}
|
|
25
65
|
const movaBase = path.join(outRoot, "mova", "claude_import", "v0");
|
|
26
66
|
const initManifestRel = path.join("mova", "claude_import", "v0", "init_manifest_v0.json").replace(/\\/g, "/");
|
|
27
67
|
let zipRelPath;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getMovaObserveScriptV0(): string;
|