mova-claude-import 0.1.1 → 0.1.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/README.md +68 -4
- 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 +49 -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 +55 -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
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
- создаёт полный “эталонный” скелет Claude Code‑проекта;
|
|
8
8
|
- импортирует существующий проект в чистую структуру;
|
|
9
|
-
- добавляет слой
|
|
9
|
+
- добавляет слой контроля, наблюдаемости и отчёты без “магии” и без LLM.
|
|
10
10
|
|
|
11
11
|
## Что было → что стало
|
|
12
12
|
|
|
@@ -26,23 +26,40 @@
|
|
|
26
26
|
hooks/
|
|
27
27
|
.mcp.json
|
|
28
28
|
mova/
|
|
29
|
+
control_v0.json
|
|
29
30
|
claude_import/v0/...
|
|
30
31
|
claude_control/v0/runs/...
|
|
31
32
|
```
|
|
32
33
|
|
|
33
34
|
## Быстрый старт
|
|
34
35
|
|
|
36
|
+
### Recommended flow (control_v0)
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
npx mova-claude-import init --out <dir>
|
|
40
|
+
npx mova-claude-import control prefill --project <dir> --out <dir>
|
|
41
|
+
npx mova-claude-import control apply --project <dir> --profile <dir>/mova/control_v0.json --mode apply
|
|
42
|
+
npx mova-claude-import control check --project <dir> --profile <dir>/mova/control_v0.json
|
|
43
|
+
```
|
|
44
|
+
|
|
35
45
|
### У меня уже есть папка Claude Code‑проекта
|
|
36
46
|
|
|
37
47
|
```
|
|
38
48
|
npx mova-claude-import --project <in> --out <out> --zip
|
|
39
49
|
```
|
|
40
50
|
|
|
41
|
-
|
|
51
|
+
### Quick start (existing Claude folder + preset)
|
|
42
52
|
|
|
43
53
|
```
|
|
44
|
-
npx mova-claude-import
|
|
45
|
-
npx mova-claude-import control
|
|
54
|
+
npx -y mova-claude-import@<version> preset list
|
|
55
|
+
npx -y mova-claude-import@<version> control apply --preset safe_observable_v0 --project . --mode overlay
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Дальше используйте единый контрольный файл и выполните rebuild/import:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
<out>/mova/control_v0.json
|
|
62
|
+
npx mova-claude-import --project <in> --out <out> --zip
|
|
46
63
|
```
|
|
47
64
|
|
|
48
65
|
### Я хочу создать эталонный профиль с нуля (init)
|
|
@@ -51,6 +68,18 @@ npx mova-claude-import control check --project <in> --profile <out>/claude_contr
|
|
|
51
68
|
npx mova-claude-import init --out <dir> --zip
|
|
52
69
|
```
|
|
53
70
|
|
|
71
|
+
Заполните единый контрольный файл:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
<dir>/mova/control_v0.json
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Затем выполните rebuild/import:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
npx mova-claude-import --project <dir> --out <out> --zip
|
|
81
|
+
```
|
|
82
|
+
|
|
54
83
|
### Контроль (preview по умолчанию)
|
|
55
84
|
|
|
56
85
|
```
|
|
@@ -69,17 +98,52 @@ npm run demo
|
|
|
69
98
|
Руководство: `docs/CONTROL_PROFILE_GUIDE_v0.md`.
|
|
70
99
|
Примеры: `examples/control_profile_min.json`, `examples/control_profile_standard.json`, `examples/control_profile_strict.json`.
|
|
71
100
|
|
|
101
|
+
Единый контрольный файл для rebuild/import: `mova/control_v0.json`.
|
|
102
|
+
Schema: `schemas/mova.control_v0.schema.json`.
|
|
103
|
+
|
|
72
104
|
Канон схем control‑слоя: `schemas/claude_control/v0/{ds,env,global}`.
|
|
73
105
|
|
|
74
106
|
## Где смотреть отчёты/доказательства
|
|
75
107
|
|
|
76
108
|
- `mova/claude_import/v0/*` — отчёты импорта и контроля качества
|
|
77
109
|
- `mova/claude_control/v0/runs/*` — планы/отчёты control‑команд
|
|
110
|
+
- `.mova/episodes/index.jsonl` — индекс наблюдаемости
|
|
111
|
+
- `.mova/episodes/<run_id>/summary.json` — краткая сводка последнего прогона
|
|
112
|
+
|
|
113
|
+
## Наблюдаемость (Observability Writer)
|
|
114
|
+
|
|
115
|
+
Writer включается из `mova/control_v0.json` и через hooks пишет эпизоды:
|
|
116
|
+
|
|
117
|
+
- события в `.mova/episodes/<run_id>/events.jsonl`
|
|
118
|
+
- сводка в `.mova/episodes/<run_id>/summary.json`
|
|
119
|
+
- индекс прогонов в `.mova/episodes/index.jsonl`
|
|
120
|
+
|
|
121
|
+
Отключить можно через `observability.enable=false` в `mova/control_v0.json`.
|
|
122
|
+
|
|
123
|
+
Минимальные команды:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
npx mova-claude-import observe list --project <dir>
|
|
127
|
+
npx mova-claude-import observe tail --project <dir> --run <id>
|
|
128
|
+
npx mova-claude-import observe summary --project <dir> --run <id>
|
|
129
|
+
```
|
|
78
130
|
|
|
79
131
|
## Для автоматизации
|
|
80
132
|
|
|
81
133
|
Подробности по `--strict`, кодам завершения и CI‑проверкам — в `docs/OPERATOR_GUIDE_v0.md`.
|
|
82
134
|
|
|
135
|
+
## Preset: safe_observable_v0
|
|
136
|
+
|
|
137
|
+
Что делает:
|
|
138
|
+
- включает observability writer и hooks
|
|
139
|
+
- добавляет guardrails на опасные команды и изменения в `main`
|
|
140
|
+
- добавляет start/finish команды и базовые skills
|
|
141
|
+
- включает skill-eval hook и правила
|
|
142
|
+
|
|
143
|
+
Что не делает:
|
|
144
|
+
- не включает MCP (по умолчанию пусто)
|
|
145
|
+
- не трогает секреты или локальные креды
|
|
146
|
+
|
|
83
147
|
## Ссылки
|
|
84
148
|
|
|
85
149
|
- `docs/COMPATIBILITY_MATRIX.md`
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const anthropicProfileV0RequiredFiles: readonly ["CLAUDE.md", "MOVA.md", ".claude/settings.json", ".claude/commands/mova_context.md", ".claude/commands/mova_lint.md", ".claude/skills/mova-layer-v0/SKILL.md"];
|
|
1
|
+
export declare const anthropicProfileV0RequiredFiles: readonly ["CLAUDE.md", "MOVA.md", ".claude/settings.json", ".claude/commands/mova_context.md", ".claude/commands/mova_lint.md", ".claude/skills/mova-layer-v0/SKILL.md", "mova/control_v0.json"];
|
|
2
2
|
export declare function getAnthropicProfileV0Files(): Record<string, string>;
|
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,8 @@ import { initProfileV0 } from "./init_v0.js";
|
|
|
3
3
|
import { controlPrefillV0 } from "./control_prefill_v0.js";
|
|
4
4
|
import { controlCheckV0 } from "./control_check_v0.js";
|
|
5
5
|
import { controlApplyV0 } from "./control_apply_v0.js";
|
|
6
|
+
import { listObservabilityRuns, readObservabilitySummary, tailObservabilityEvents } from "./observe_v0.js";
|
|
7
|
+
import { listPresets, readPresetControlRaw, resolvePreset } from "./presets_v0.js";
|
|
6
8
|
function getArg(name) {
|
|
7
9
|
const idx = process.argv.indexOf(name);
|
|
8
10
|
if (idx === -1)
|
|
@@ -18,10 +20,15 @@ function usage(exitCode = 0) {
|
|
|
18
20
|
"",
|
|
19
21
|
"Usage:",
|
|
20
22
|
" mova-claude-import --project <dir> [--out <dir>] [--dry-run] [--strict] [--include-local] [--include-user-settings] [--no-emit-profile] [--no-emit-overlay] [--zip] [--zip-name <name>]",
|
|
21
|
-
" mova-claude-import init --out <dir> [--zip]",
|
|
23
|
+
" mova-claude-import init --out <dir> [--zip] [--preset <name>]",
|
|
22
24
|
" mova-claude-import control prefill --project <dir> --out <dir> [--include-local]",
|
|
23
25
|
" mova-claude-import control check --project <dir> --profile <file>",
|
|
24
|
-
" mova-claude-import control apply --project <dir> --profile <file> [--mode preview|apply]",
|
|
26
|
+
" mova-claude-import control apply --project <dir> --profile <file> [--mode preview|apply|overlay] [--preset <name>]",
|
|
27
|
+
" mova-claude-import preset list",
|
|
28
|
+
" mova-claude-import preset show <name>",
|
|
29
|
+
" mova-claude-import observe list --project <dir>",
|
|
30
|
+
" mova-claude-import observe tail --project <dir> --run <id> [--limit <n>]",
|
|
31
|
+
" mova-claude-import observe summary --project <dir> --run <id>",
|
|
25
32
|
"",
|
|
26
33
|
"Notes:",
|
|
27
34
|
" - CLAUDE.local.md and *.local.* are excluded unless --include-local",
|
|
@@ -30,6 +37,7 @@ function usage(exitCode = 0) {
|
|
|
30
37
|
" - overlay emission is enabled by default; use --no-emit-overlay to skip",
|
|
31
38
|
" - zip export is disabled by default; use --zip to enable",
|
|
32
39
|
" - init creates a clean Anthropic profile v0 scaffold",
|
|
40
|
+
" - init --preset uses preset control_v0.json + assets",
|
|
33
41
|
" - control commands run in preview by default",
|
|
34
42
|
].join("\n"));
|
|
35
43
|
process.exit(exitCode);
|
|
@@ -48,15 +56,42 @@ if (subcommand === "init") {
|
|
|
48
56
|
process.exit(2);
|
|
49
57
|
}
|
|
50
58
|
const emitZip = hasFlag("--zip");
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
const presetName = getArg("--preset");
|
|
60
|
+
if (presetName) {
|
|
61
|
+
resolvePreset(presetName)
|
|
62
|
+
.then(async (preset) => {
|
|
63
|
+
if (!preset) {
|
|
64
|
+
console.error(`Preset not found: ${presetName}`);
|
|
65
|
+
process.exit(2);
|
|
66
|
+
}
|
|
67
|
+
const raw = await readPresetControlRaw(presetName);
|
|
68
|
+
const control = raw ? JSON.parse(raw) : null;
|
|
69
|
+
if (!control) {
|
|
70
|
+
console.error(`Preset control missing: ${presetName}`);
|
|
71
|
+
process.exit(2);
|
|
72
|
+
}
|
|
73
|
+
return initProfileV0(out, emitZip, { controlOverride: control, assetsRoot: preset.assets_root });
|
|
74
|
+
})
|
|
75
|
+
.then((res) => {
|
|
76
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
77
|
+
process.exit(0);
|
|
78
|
+
})
|
|
79
|
+
.catch((err) => {
|
|
80
|
+
console.error(err);
|
|
81
|
+
process.exit(1);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
initProfileV0(out, emitZip)
|
|
86
|
+
.then((res) => {
|
|
87
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
88
|
+
process.exit(0);
|
|
89
|
+
})
|
|
90
|
+
.catch((err) => {
|
|
91
|
+
console.error(err);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
60
95
|
}
|
|
61
96
|
else if (subcommand === "control") {
|
|
62
97
|
const action = process.argv[3];
|
|
@@ -95,10 +130,13 @@ else if (subcommand === "control") {
|
|
|
95
130
|
controlCheckV0(project, profile, out)
|
|
96
131
|
.then((res) => {
|
|
97
132
|
console.log([
|
|
98
|
-
"control check: ok",
|
|
133
|
+
res.exit_code && res.exit_code !== 0 ? "control check: issues" : "control check: ok",
|
|
99
134
|
`plan: ${res.plan_path}`,
|
|
100
135
|
`summary: ${res.summary_path}`,
|
|
101
|
-
|
|
136
|
+
res.exit_code ? `exit_code: ${res.exit_code}` : null,
|
|
137
|
+
].filter(Boolean).join("\n"));
|
|
138
|
+
if (typeof res.exit_code === "number")
|
|
139
|
+
process.exit(res.exit_code);
|
|
102
140
|
process.exit(0);
|
|
103
141
|
})
|
|
104
142
|
.catch((err) => {
|
|
@@ -107,19 +145,164 @@ else if (subcommand === "control") {
|
|
|
107
145
|
});
|
|
108
146
|
}
|
|
109
147
|
else if (action === "apply") {
|
|
110
|
-
const
|
|
111
|
-
|
|
148
|
+
const out = getArg("--out") || project;
|
|
149
|
+
const mode = getArg("--mode");
|
|
150
|
+
const presetName = getArg("--preset");
|
|
151
|
+
if (presetName) {
|
|
152
|
+
resolvePreset(presetName)
|
|
153
|
+
.then(async (preset) => {
|
|
154
|
+
if (!preset) {
|
|
155
|
+
console.error(`Preset not found: ${presetName}`);
|
|
156
|
+
process.exit(2);
|
|
157
|
+
}
|
|
158
|
+
return controlApplyV0(project, preset.control_path, out, mode, {
|
|
159
|
+
assetSourceRoot: preset.assets_root,
|
|
160
|
+
});
|
|
161
|
+
})
|
|
162
|
+
.then((res) => {
|
|
163
|
+
console.log([
|
|
164
|
+
res.exit_code && res.exit_code !== 0 ? "control apply: issues" : "control apply: ok",
|
|
165
|
+
`report: ${res.report_path}`,
|
|
166
|
+
res.exit_code ? `exit_code: ${res.exit_code}` : null,
|
|
167
|
+
].filter(Boolean).join("\n"));
|
|
168
|
+
if (typeof res.exit_code === "number")
|
|
169
|
+
process.exit(res.exit_code);
|
|
170
|
+
process.exit(0);
|
|
171
|
+
})
|
|
172
|
+
.catch((err) => {
|
|
173
|
+
console.error(err);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
const profile = getArg("--profile");
|
|
179
|
+
if (!profile) {
|
|
180
|
+
usage(2);
|
|
181
|
+
process.exit(2);
|
|
182
|
+
}
|
|
183
|
+
controlApplyV0(project, profile, out, mode)
|
|
184
|
+
.then((res) => {
|
|
185
|
+
console.log([
|
|
186
|
+
res.exit_code && res.exit_code !== 0 ? "control apply: issues" : "control apply: ok",
|
|
187
|
+
`report: ${res.report_path}`,
|
|
188
|
+
res.exit_code ? `exit_code: ${res.exit_code}` : null,
|
|
189
|
+
].filter(Boolean).join("\n"));
|
|
190
|
+
if (typeof res.exit_code === "number")
|
|
191
|
+
process.exit(res.exit_code);
|
|
192
|
+
process.exit(0);
|
|
193
|
+
})
|
|
194
|
+
.catch((err) => {
|
|
195
|
+
console.error(err);
|
|
196
|
+
process.exit(1);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
usage(2);
|
|
202
|
+
process.exit(2);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else if (subcommand === "preset") {
|
|
206
|
+
const action = process.argv[3];
|
|
207
|
+
if (action === "list") {
|
|
208
|
+
listPresets()
|
|
209
|
+
.then((presets) => {
|
|
210
|
+
for (const name of presets) {
|
|
211
|
+
console.log(name);
|
|
212
|
+
}
|
|
213
|
+
process.exit(0);
|
|
214
|
+
})
|
|
215
|
+
.catch((err) => {
|
|
216
|
+
console.error(err);
|
|
217
|
+
process.exit(1);
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
else if (action === "show") {
|
|
221
|
+
const name = process.argv[4];
|
|
222
|
+
if (!name) {
|
|
112
223
|
usage(2);
|
|
113
224
|
process.exit(2);
|
|
114
225
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
226
|
+
readPresetControlRaw(name)
|
|
227
|
+
.then((raw) => {
|
|
228
|
+
if (!raw) {
|
|
229
|
+
console.error(`Preset not found: ${name}`);
|
|
230
|
+
process.exit(2);
|
|
231
|
+
}
|
|
232
|
+
process.stdout.write(raw + "\n");
|
|
233
|
+
process.exit(0);
|
|
234
|
+
})
|
|
235
|
+
.catch((err) => {
|
|
236
|
+
console.error(err);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
usage(2);
|
|
242
|
+
process.exit(2);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
else if (subcommand === "observe") {
|
|
246
|
+
const action = process.argv[3];
|
|
247
|
+
const project = getArg("--project");
|
|
248
|
+
if (!project) {
|
|
249
|
+
usage(2);
|
|
250
|
+
process.exit(2);
|
|
251
|
+
}
|
|
252
|
+
if (action === "list") {
|
|
253
|
+
listObservabilityRuns(project)
|
|
254
|
+
.then((runs) => {
|
|
255
|
+
if (!runs.length) {
|
|
256
|
+
console.log("observe list: no runs");
|
|
257
|
+
process.exit(0);
|
|
258
|
+
}
|
|
259
|
+
for (const run of runs) {
|
|
260
|
+
console.log(JSON.stringify(run));
|
|
261
|
+
}
|
|
262
|
+
process.exit(0);
|
|
263
|
+
})
|
|
264
|
+
.catch((err) => {
|
|
265
|
+
console.error(err);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
else if (action === "tail") {
|
|
270
|
+
const runId = getArg("--run");
|
|
271
|
+
if (!runId) {
|
|
272
|
+
usage(2);
|
|
273
|
+
process.exit(2);
|
|
274
|
+
}
|
|
275
|
+
const limitRaw = getArg("--limit");
|
|
276
|
+
const limit = limitRaw ? Number(limitRaw) : 20;
|
|
277
|
+
tailObservabilityEvents(project, runId, Number.isFinite(limit) ? limit : 20)
|
|
278
|
+
.then((lines) => {
|
|
279
|
+
if (!lines.length) {
|
|
280
|
+
console.log("observe tail: no events");
|
|
281
|
+
process.exit(0);
|
|
282
|
+
}
|
|
283
|
+
for (const line of lines) {
|
|
284
|
+
console.log(line);
|
|
285
|
+
}
|
|
286
|
+
process.exit(0);
|
|
287
|
+
})
|
|
288
|
+
.catch((err) => {
|
|
289
|
+
console.error(err);
|
|
290
|
+
process.exit(1);
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
else if (action === "summary") {
|
|
294
|
+
const runId = getArg("--run");
|
|
295
|
+
if (!runId) {
|
|
296
|
+
usage(2);
|
|
297
|
+
process.exit(2);
|
|
298
|
+
}
|
|
299
|
+
readObservabilitySummary(project, runId)
|
|
300
|
+
.then((summary) => {
|
|
301
|
+
if (!summary) {
|
|
302
|
+
console.log("observe summary: not found");
|
|
303
|
+
process.exit(0);
|
|
304
|
+
}
|
|
305
|
+
process.stdout.write(JSON.stringify(summary, null, 2) + "\n");
|
|
123
306
|
process.exit(0);
|
|
124
307
|
})
|
|
125
308
|
.catch((err) => {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
type ApplyResult = {
|
|
2
2
|
run_id: string;
|
|
3
3
|
report_path: string;
|
|
4
|
+
exit_code?: number;
|
|
4
5
|
};
|
|
5
|
-
|
|
6
|
+
type ApplyOptions = {
|
|
7
|
+
assetSourceRoot?: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function controlApplyV0(projectDir: string, profilePath: string, outDir: string, mode?: string, options?: ApplyOptions): Promise<ApplyResult>;
|
|
6
10
|
export {};
|
package/dist/control_apply_v0.js
CHANGED
|
@@ -4,6 +4,9 @@ import { stableStringify } from "./stable_json.js";
|
|
|
4
4
|
import { stableSha256 } from "./redaction.js";
|
|
5
5
|
import { buildMovaControlEntryV0, MOVA_CONTROL_ENTRY_MARKER } from "./mova_overlay_v0.js";
|
|
6
6
|
import { ensureClaudeControlSurfacesV0 } from "./claude_profile_scaffold_v0.js";
|
|
7
|
+
import { controlToMcpJson, controlToSettingsV0, normalizeControlV0 } from "./control_v0.js";
|
|
8
|
+
import { validateControlV0Schema } from "./control_v0_schema.js";
|
|
9
|
+
import { getMovaObserveScriptV0 } from "./observability_writer_v0.js";
|
|
7
10
|
async function exists(p) {
|
|
8
11
|
try {
|
|
9
12
|
await fs.stat(p);
|
|
@@ -24,6 +27,37 @@ async function writeJson(p, obj) {
|
|
|
24
27
|
function computeRunId(parts) {
|
|
25
28
|
return stableSha256(parts.join("|")).slice(0, 16);
|
|
26
29
|
}
|
|
30
|
+
function mergeOverlayValue(existing, incoming) {
|
|
31
|
+
if (existing === undefined)
|
|
32
|
+
return incoming;
|
|
33
|
+
const existingArr = Array.isArray(existing) ? existing : null;
|
|
34
|
+
const incomingArr = Array.isArray(incoming) ? incoming : null;
|
|
35
|
+
if (existingArr || incomingArr) {
|
|
36
|
+
const left = existingArr ?? (existing === undefined ? [] : [existing]);
|
|
37
|
+
const right = incomingArr ?? (incoming === undefined ? [] : [incoming]);
|
|
38
|
+
const seen = new Set(left.map((item) => stableStringify(item)));
|
|
39
|
+
const merged = left.slice();
|
|
40
|
+
for (const item of right) {
|
|
41
|
+
const key = stableStringify(item);
|
|
42
|
+
if (seen.has(key))
|
|
43
|
+
continue;
|
|
44
|
+
seen.add(key);
|
|
45
|
+
merged.push(item);
|
|
46
|
+
}
|
|
47
|
+
return merged;
|
|
48
|
+
}
|
|
49
|
+
if (existing && typeof existing === "object" && incoming && typeof incoming === "object") {
|
|
50
|
+
const out = { ...existing };
|
|
51
|
+
for (const [key, value] of Object.entries(incoming)) {
|
|
52
|
+
out[key] = mergeOverlayValue(out[key], value);
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
return existing;
|
|
57
|
+
}
|
|
58
|
+
function mergeSettingsOverlay(existing, incoming) {
|
|
59
|
+
return mergeOverlayValue(existing ?? {}, incoming ?? {});
|
|
60
|
+
}
|
|
27
61
|
function updateClaude(content, marker, block) {
|
|
28
62
|
if (content.includes(marker)) {
|
|
29
63
|
const idx = content.indexOf(marker);
|
|
@@ -34,15 +68,38 @@ function updateClaude(content, marker, block) {
|
|
|
34
68
|
}
|
|
35
69
|
return `${block}\n${content}`;
|
|
36
70
|
}
|
|
37
|
-
export async function controlApplyV0(projectDir, profilePath, outDir, mode) {
|
|
71
|
+
export async function controlApplyV0(projectDir, profilePath, outDir, mode, options) {
|
|
38
72
|
await ensureClaudeControlSurfacesV0(projectDir);
|
|
39
73
|
const profile = await readJson(profilePath);
|
|
40
|
-
const
|
|
74
|
+
const isControlV0 = profile?.version === "control_v0";
|
|
75
|
+
const control = isControlV0 ? normalizeControlV0(profile).control : null;
|
|
76
|
+
if (isControlV0) {
|
|
77
|
+
const validation = await validateControlV0Schema(profile);
|
|
78
|
+
if (!validation.ok) {
|
|
79
|
+
const runId = computeRunId([profilePath, "invalid_schema"]);
|
|
80
|
+
const runBase = path.join(outDir, "mova", "claude_control", "v0", "runs", runId);
|
|
81
|
+
const reportPath = path.join(runBase, "control_apply_report_v0.json");
|
|
82
|
+
const report = {
|
|
83
|
+
profile_version: "v0",
|
|
84
|
+
run_id: runId,
|
|
85
|
+
project_dir: projectDir,
|
|
86
|
+
profile_path: profilePath,
|
|
87
|
+
mode: mode ?? "preview",
|
|
88
|
+
outcome_code: "INVALID_SCHEMA",
|
|
89
|
+
errors: validation.errors ?? [],
|
|
90
|
+
};
|
|
91
|
+
await writeJson(reportPath, report);
|
|
92
|
+
return { run_id: runId, report_path: reportPath, exit_code: 2 };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const applyMode = mode ?? (isControlV0 && control?.policy?.mode === "report_only" ? "preview" : profile?.apply?.default_apply_mode) ?? "preview";
|
|
96
|
+
const isOverlay = applyMode === "overlay";
|
|
97
|
+
const assetSourceRoot = options?.assetSourceRoot ?? projectDir;
|
|
41
98
|
const claudePath = path.join(projectDir, "CLAUDE.md");
|
|
42
99
|
const mcpPath = path.join(projectDir, ".mcp.json");
|
|
43
100
|
const claudeExists = await exists(claudePath);
|
|
44
101
|
const mcpExists = await exists(mcpPath);
|
|
45
|
-
const marker = profile?.anthropic?.claude_md?.marker ?? MOVA_CONTROL_ENTRY_MARKER;
|
|
102
|
+
const marker = control?.claude_md?.marker ?? profile?.anthropic?.claude_md?.marker ?? MOVA_CONTROL_ENTRY_MARKER;
|
|
46
103
|
const runId = computeRunId([profilePath, claudeExists ? "claude" : "", mcpExists ? "mcp" : "", marker, applyMode]);
|
|
47
104
|
const runBase = path.join(outDir, "mova", "claude_control", "v0", "runs", runId);
|
|
48
105
|
const overlayParams = {
|
|
@@ -56,15 +113,75 @@ export async function controlApplyV0(projectDir, profilePath, outDir, mode) {
|
|
|
56
113
|
exportManifestFile: "export_manifest_v0.json",
|
|
57
114
|
};
|
|
58
115
|
const controlEntry = buildMovaControlEntryV0(overlayParams);
|
|
59
|
-
const applied = { claude_md: false, mcp_json: false, settings: false };
|
|
60
|
-
if (applyMode === "apply") {
|
|
61
|
-
if (profile?.anthropic?.claude_md?.inject_control_entry && claudeExists) {
|
|
116
|
+
const applied = { claude_md: false, mcp_json: false, settings: false, assets: false, lsp: false };
|
|
117
|
+
if (applyMode === "apply" || applyMode === "overlay") {
|
|
118
|
+
if ((control?.claude_md?.inject_control_entry ?? profile?.anthropic?.claude_md?.inject_control_entry) && claudeExists) {
|
|
62
119
|
const raw = await fs.readFile(claudePath, "utf8");
|
|
63
120
|
const updated = updateClaude(raw, marker, controlEntry);
|
|
64
121
|
await fs.writeFile(claudePath, updated, "utf8");
|
|
65
122
|
applied.claude_md = true;
|
|
66
123
|
}
|
|
67
|
-
if (
|
|
124
|
+
if (control) {
|
|
125
|
+
const settingsPath = path.join(projectDir, ".claude", "settings.json");
|
|
126
|
+
const settingsGenerated = controlToSettingsV0(control);
|
|
127
|
+
if (isOverlay && (await exists(settingsPath))) {
|
|
128
|
+
const current = await readJson(settingsPath);
|
|
129
|
+
const merged = mergeSettingsOverlay(current, settingsGenerated);
|
|
130
|
+
await writeJson(settingsPath, merged);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
await writeJson(settingsPath, settingsGenerated);
|
|
134
|
+
}
|
|
135
|
+
applied.settings = true;
|
|
136
|
+
if (!(isOverlay && (await exists(mcpPath)))) {
|
|
137
|
+
await writeJson(path.join(projectDir, ".mcp.json"), controlToMcpJson(control));
|
|
138
|
+
applied.mcp_json = true;
|
|
139
|
+
}
|
|
140
|
+
const assets = [
|
|
141
|
+
...control.assets.skills,
|
|
142
|
+
...control.assets.agents,
|
|
143
|
+
...control.assets.commands,
|
|
144
|
+
...control.assets.rules,
|
|
145
|
+
...control.assets.hooks,
|
|
146
|
+
...control.assets.workflows,
|
|
147
|
+
...control.assets.docs,
|
|
148
|
+
...control.assets.dotfiles,
|
|
149
|
+
...control.assets.schemas,
|
|
150
|
+
];
|
|
151
|
+
for (const asset of assets) {
|
|
152
|
+
const target = path.join(projectDir, asset.path);
|
|
153
|
+
if (isOverlay && (await exists(target)))
|
|
154
|
+
continue;
|
|
155
|
+
const sourceRel = asset.source_path ?? asset.path;
|
|
156
|
+
const source = path.isAbsolute(sourceRel) ? sourceRel : path.join(assetSourceRoot, sourceRel);
|
|
157
|
+
try {
|
|
158
|
+
await fs.stat(source);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
await fs.mkdir(path.dirname(target), { recursive: true });
|
|
164
|
+
if (source !== target) {
|
|
165
|
+
await fs.copyFile(source, target);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
applied.assets = assets.length > 0;
|
|
169
|
+
if (control.lsp.managed && Array.isArray(control.lsp.enabled_plugins)) {
|
|
170
|
+
const lspPath = path.join(projectDir, control.lsp.config_path);
|
|
171
|
+
if (!(isOverlay && (await exists(lspPath)))) {
|
|
172
|
+
await writeJson(lspPath, { enabled_plugins: control.lsp.enabled_plugins });
|
|
173
|
+
}
|
|
174
|
+
applied.lsp = true;
|
|
175
|
+
}
|
|
176
|
+
if (control.observability.enable && control.observability.writer?.script_path) {
|
|
177
|
+
const scriptPath = path.join(projectDir, control.observability.writer.script_path);
|
|
178
|
+
if (!(isOverlay && (await exists(scriptPath)))) {
|
|
179
|
+
await fs.mkdir(path.dirname(scriptPath), { recursive: true });
|
|
180
|
+
await fs.writeFile(scriptPath, getMovaObserveScriptV0(), "utf8");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else if (profile?.anthropic?.mcp?.servers && mcpExists) {
|
|
68
185
|
const mcp = await readJson(mcpPath);
|
|
69
186
|
const merged = { ...mcp, servers: profile.anthropic.mcp.servers };
|
|
70
187
|
await fs.writeFile(mcpPath, stableStringify(merged) + "\n", "utf8");
|
|
@@ -77,7 +194,7 @@ export async function controlApplyV0(projectDir, profilePath, outDir, mode) {
|
|
|
77
194
|
project_dir: projectDir,
|
|
78
195
|
profile_path: profilePath,
|
|
79
196
|
mode: applyMode,
|
|
80
|
-
outcome_code: applyMode === "apply" ? "APPLIED" : "PREVIEW",
|
|
197
|
+
outcome_code: applyMode === "apply" || applyMode === "overlay" ? "APPLIED" : "PREVIEW",
|
|
81
198
|
applied,
|
|
82
199
|
};
|
|
83
200
|
const reportPath = path.join(runBase, "control_apply_report_v0.json");
|