@lamentis/naome 1.2.1 → 1.3.0

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.
Files changed (57) hide show
  1. package/Cargo.lock +2 -2
  2. package/README.md +108 -47
  3. package/bin/naome.js +16 -1
  4. package/crates/naome-cli/Cargo.toml +1 -1
  5. package/crates/naome-cli/src/dispatcher.rs +6 -2
  6. package/crates/naome-cli/src/main.rs +35 -23
  7. package/crates/naome-cli/src/quality_commands.rs +230 -11
  8. package/crates/naome-cli/src/workflow_commands.rs +21 -1
  9. package/crates/naome-core/Cargo.toml +1 -1
  10. package/crates/naome-core/src/git.rs +4 -2
  11. package/crates/naome-core/src/install_plan.rs +2 -0
  12. package/crates/naome-core/src/lib.rs +11 -7
  13. package/crates/naome-core/src/quality/baseline.rs +8 -0
  14. package/crates/naome-core/src/quality/cache.rs +153 -0
  15. package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +25 -11
  16. package/crates/naome-core/src/quality/checks/near_duplicates.rs +4 -2
  17. package/crates/naome-core/src/quality/checks.rs +7 -8
  18. package/crates/naome-core/src/quality/cleanup.rs +36 -3
  19. package/crates/naome-core/src/quality/mod.rs +57 -9
  20. package/crates/naome-core/src/quality/scanner/analysis/normalize.rs +78 -0
  21. package/crates/naome-core/src/quality/scanner/analysis.rs +160 -0
  22. package/crates/naome-core/src/quality/scanner/repo_paths.rs +39 -3
  23. package/crates/naome-core/src/quality/scanner.rs +193 -220
  24. package/crates/naome-core/src/quality/semantic/checks.rs +134 -0
  25. package/crates/naome-core/src/quality/semantic/extract.rs +158 -0
  26. package/crates/naome-core/src/quality/semantic/model.rs +85 -0
  27. package/crates/naome-core/src/quality/semantic/route.rs +52 -0
  28. package/crates/naome-core/src/quality/semantic.rs +68 -0
  29. package/crates/naome-core/src/quality/structure/checks/directory.rs +9 -19
  30. package/crates/naome-core/src/quality/structure/checks.rs +1 -1
  31. package/crates/naome-core/src/quality/structure/classify.rs +52 -0
  32. package/crates/naome-core/src/quality/structure/mod.rs +2 -2
  33. package/crates/naome-core/src/quality/structure/model.rs +8 -1
  34. package/crates/naome-core/src/quality/types.rs +40 -2
  35. package/crates/naome-core/src/route/builtin_checks.rs +1 -15
  36. package/crates/naome-core/src/workflow/doctor.rs +144 -0
  37. package/crates/naome-core/src/workflow/mod.rs +2 -0
  38. package/crates/naome-core/src/workflow/mutation.rs +1 -2
  39. package/crates/naome-core/tests/install_plan.rs +2 -0
  40. package/crates/naome-core/tests/quality.rs +14 -5
  41. package/crates/naome-core/tests/quality_performance.rs +231 -0
  42. package/crates/naome-core/tests/quality_structure_policy.rs +19 -0
  43. package/crates/naome-core/tests/route_user_diff.rs +10 -6
  44. package/crates/naome-core/tests/semantic_legacy.rs +140 -0
  45. package/crates/naome-core/tests/workflow_doctor.rs +24 -0
  46. package/crates/naome-core/tests/workflow_policy.rs +6 -1
  47. package/installer/git-boundary.js +1 -0
  48. package/native/darwin-arm64/naome +0 -0
  49. package/native/linux-x64/naome +0 -0
  50. package/package.json +1 -1
  51. package/templates/naome-root/.naome/bin/check-harness-health.js +2 -2
  52. package/templates/naome-root/.naome/bin/check-task-state.js +2 -2
  53. package/templates/naome-root/.naome/bin/naome.js +11 -4
  54. package/templates/naome-root/.naome/manifest.json +2 -2
  55. package/templates/naome-root/.naomeignore +1 -0
  56. package/templates/naome-root/docs/naome/agent-workflow.md +16 -14
  57. package/templates/naome-root/docs/naome/repository-quality.md +63 -4
package/Cargo.lock CHANGED
@@ -76,7 +76,7 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
76
76
 
77
77
  [[package]]
78
78
  name = "naome-cli"
79
- version = "1.2.1"
79
+ version = "1.3.0"
80
80
  dependencies = [
81
81
  "naome-core",
82
82
  "serde_json",
@@ -84,7 +84,7 @@ dependencies = [
84
84
 
85
85
  [[package]]
86
86
  name = "naome-core"
87
- version = "1.2.1"
87
+ version = "1.3.0"
88
88
  dependencies = [
89
89
  "serde",
90
90
  "serde_json",
package/README.md CHANGED
@@ -1,70 +1,131 @@
1
- # NAOME
2
-
3
- [![npm version](https://img.shields.io/npm/v/@lamentis/naome.svg)](https://www.npmjs.com/package/@lamentis/naome)
4
- [![license: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-green.svg)](LICENSE)
5
-
6
- NAOME is a deterministic repository harness for AI coding agents. It teaches an
7
- agent how to enter a repository, routes ambiguous user requests through explicit
8
- policy, gates work by task scope, and requires proof before completed changes
9
- are baselined.
10
-
11
- ## What NAOME Provides
12
-
13
- - Repository intake with `.naomeignore`, workflow docs, and health checks.
14
- - Task admission and progress gates that prevent unowned diffs and scope drift.
15
- - Rust-backed `status`, `next`, `route`, `commit`, quality, and workflow
16
- commands.
17
- - Intent routing that separates repository state from structured task intent.
18
- - Repository-quality checks for changed files, with old debt visible through
19
- cleanup flows instead of blocking every legacy codebase.
20
- - Verification phases for health, quality, focused tests, broad tests, package
21
- gates, and final diff checks.
22
- - Compact task proof data so repeated evidence and shared check metadata do not
23
- bloat `.naome/task-state.json`.
24
- - Local Git hooks and commit gates that keep manual commits aligned with the
25
- same harness policy.
26
-
27
- ## Install
28
-
29
- ```sh
1
+ <p align="center">
2
+ <a href="https://www.npmjs.com/package/@lamentis/naome"><img src="https://img.shields.io/npm/v/@lamentis/naome.svg" alt="npm version"></a>
3
+ <a href="../../LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-green.svg" alt="Apache-2.0 license"></a>
4
+ </p>
5
+
6
+ <h1 align="center">NAOME</h1>
7
+
8
+ <p align="center">
9
+ A deterministic repository harness for AI coding agents.
10
+ </p>
11
+
12
+ ```shell
30
13
  npm install -g @lamentis/naome
31
14
  ```
32
15
 
33
- ## Set Up A Repository
16
+ NAOME gives coding agents a repository-local operating protocol: what to read,
17
+ what to ignore, how to admit a task, which files are in scope, which checks are
18
+ required, and when work is safe to commit.
19
+
20
+ ## Quickstart
34
21
 
35
- From the repository root:
22
+ Install the CLI, then sync NAOME into a repository:
36
23
 
37
- ```sh
24
+ ```shell
25
+ npm install -g @lamentis/naome
26
+ cd /path/to/repo
38
27
  naome sync
39
28
  ```
40
29
 
41
- If the CLI reports that an update is available, refresh the global CLI and then
42
- sync the harness files:
30
+ For an initialized repository, start with:
43
31
 
44
- ```sh
45
- naome update
46
- naome sync
32
+ ```shell
33
+ naome status
34
+ naome next
35
+ naome doctor
36
+ ```
37
+
38
+ For agent-driven work, route the user's request through the harness:
39
+
40
+ ```shell
41
+ naome route --prompt-file /path/to/prompt.txt --execute --json
47
42
  ```
48
43
 
49
- Fresh repositories print a first-run prompt for your coding agent. Existing
50
- initialized repositories keep normal sync output compact and do not reprint the
51
- first-run protocol unless setup is still incomplete.
44
+ ## Why NAOME?
45
+
46
+ - Keeps agents inside explicit task scope.
47
+ - Blocks unowned diffs before new work starts.
48
+ - Separates current task work from repository cleanup debt.
49
+ - Runs changed-code quality gates without forcing legacy repositories to be
50
+ perfect on day one.
51
+ - Records verification proof before a task can be treated as complete.
52
+ - Keeps sync fast by making baseline and deep quality scans explicit.
53
+
54
+ ## Safety Model
55
+
56
+ NAOME is repository-local. The files under `.naome/`, `.naomeignore`, and
57
+ `docs/naome/` define the local harness contract for a repository.
52
58
 
53
- ## Daily Workflow
59
+ The harness enforces read boundaries, task admission, scope drift checks,
60
+ repository-quality policy, verification phases, and commit gates. Existing debt
61
+ is reportable through cleanup flows, while changed files are held to the active
62
+ policy.
54
63
 
55
- Use NAOME commands from the target repository:
64
+ ## CLI Reference
56
65
 
57
- ```sh
66
+ Common commands:
67
+
68
+ ```shell
69
+ naome sync
70
+ naome update
58
71
  naome status
59
72
  naome next
73
+ naome doctor
60
74
  naome route --prompt-file /path/to/prompt.txt --execute --json
75
+ naome quality init
76
+ naome quality init --baseline
77
+ naome quality report
78
+ naome quality report --deep
79
+ naome quality check --changed
61
80
  naome commit -m "type(scope): summary"
62
81
  ```
63
82
 
64
- Agents should read the NAOME docs named by `status` or `route`, run harness
65
- health before feature work, keep changes inside the active task scope, and
66
- record verification proof before marking work complete.
83
+ `naome sync` installs or repairs the local harness files. It does not run a
84
+ hidden full-repository quality scan. If quality policy is newly seeded, run
85
+ `naome quality init --baseline` deliberately; use `--deep` or
86
+ `--deep-baseline` only when you want expensive repository-wide checks.
87
+
88
+ ## Repository Docs
89
+
90
+ After sync, NAOME writes the agent-facing workflow into `docs/naome/`:
91
+
92
+ - `docs/naome/index.md` is the entry point.
93
+ - `docs/naome/agent-workflow.md` explains the active task workflow.
94
+ - `docs/naome/testing.md` maps change types to required checks.
95
+ - `docs/naome/repository-quality.md` explains quality, structure, and cleanup
96
+ policy.
97
+
98
+ Agents should follow the repository's NAOME docs instead of guessing workflow
99
+ rules from generic project files.
100
+
101
+ ## Configuration
102
+
103
+ The main local policy files are:
104
+
105
+ - `.naomeignore` for read boundaries.
106
+ - `.naome/verification.json` for check phases and proof requirements.
107
+ - `.naome/repository-quality.json` for file, symbol, duplicate, and semantic
108
+ quality policy.
109
+ - `.naome/repository-structure.json` for path role, module, and directory
110
+ structure policy.
111
+ - `.naome/task-state.json` for active task state and proof.
112
+
113
+ Product defaults stay generic. Repository-specific policy belongs in the local
114
+ `.naome/` config files.
115
+
116
+ ## Development
117
+
118
+ Useful checks for this repository:
119
+
120
+ ```shell
121
+ npm run build:rust
122
+ npm run test:decision-engine
123
+ npm run test:naome-installer
124
+ npm run pack:dry-run
125
+ node .naome/bin/naome.js quality check --changed --json
126
+ git diff --check
127
+ ```
67
128
 
68
129
  ## License
69
130
 
70
- NAOME is licensed under the [Apache License 2.0](LICENSE).
131
+ NAOME is licensed under the [Apache License 2.0](../../LICENSE).
package/bin/naome.js CHANGED
@@ -12,7 +12,7 @@ const packageVersion = packageMetadata.version;
12
12
  const nativeBinaryName = process.platform === "win32" ? "naome.exe" : "naome";
13
13
  const args = process.argv.slice(2);
14
14
  const [command] = args;
15
- const helpCommands = "status [--json]|next [--json]|intent --prompt-file <path> [--json]|intent --prompt <text> [--json]|route --prompt-file <path> [--execute] [--json]|route --prompt <text> [--execute] [--json]|explain --prompt-file <path> [--json]|explain --prompt <text> [--json]|install|sync [--check-update]|update [--json] [--execute]|quality init [--json]|quality check --changed [--json]|quality report [--json]|structure report [--json]|structure explain --path <path> [--json]|cleanup plan [--json]|cleanup route --path <path> [--json]|refresh-integrity [--json]|workflow search-profile|check-search|phases|processes|mutations [--json]|commit -m \"type(scope): message\"".split("|");
15
+ const helpCommands = "status [--json]|next [--json]|intent --prompt-file <path> [--json]|intent --prompt <text> [--json]|route --prompt-file <path> [--execute] [--json]|route --prompt <text> [--execute] [--json]|explain --prompt-file <path> [--json]|explain --prompt <text> [--json]|doctor [--json]|install|sync [--check-update]|update [--json] [--execute]|quality init [--baseline|--deep-baseline] [--json]|quality check --changed [--include-scanned-paths] [--json]|quality report [--deep] [--include-scanned-paths] [--json]|quality cache status [--json]|quality cache clear|semantic report [--deep] [--json]|semantic check --changed [--json]|semantic route --finding <id> [--json]|semantic loop [--json]|structure report [--json]|structure explain --path <path> [--json]|cleanup plan [--json]|cleanup route --path <path> [--json]|refresh-integrity [--json]|workflow search-profile|check-search|phases|processes|mutations [--json]|commit -m \"type(scope): message\"".split("|");
16
16
 
17
17
  if (isHelpRequest(args)) {
18
18
  printHelp();
@@ -300,6 +300,21 @@ function ensureRepositoryQualityInitialized(nativeBinary, qualityConfigExisted)
300
300
  }
301
301
  process.exit(result.status === null ? 1 : result.status);
302
302
  }
303
+
304
+ const output = result.stdout.trim();
305
+ if (output) {
306
+ try {
307
+ const init = JSON.parse(output);
308
+ if (init.configWritten || init.structureConfigWritten) {
309
+ console.log("repository quality policy initialized");
310
+ }
311
+ if (init.baselinePending) {
312
+ console.log("baseline pending: run naome quality init --baseline");
313
+ }
314
+ } catch (_error) {
315
+ console.log(output);
316
+ }
317
+ }
303
318
  }
304
319
 
305
320
  function repositoryQualityConfigPath(root) {
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "naome-cli"
3
- version = "1.2.1"
3
+ version = "1.3.0"
4
4
  edition.workspace = true
5
5
  license.workspace = true
6
6
  repository.workspace = true
@@ -3,11 +3,13 @@ use std::path::Path;
3
3
  use crate::check_commands::{run_harness_health, run_task_state, run_verification_contract};
4
4
  use crate::install_bridge::run_install_bridge;
5
5
  use crate::prompt_commands::{run_explain, run_intent, run_route};
6
- use crate::quality_commands::{run_cleanup_command, run_quality_command, run_structure_command};
6
+ use crate::quality_commands::{
7
+ run_cleanup_command, run_quality_command, run_semantic_command, run_structure_command,
8
+ };
7
9
  use crate::simple_commands::{
8
10
  print_install_plan, run_commit_paths, run_journal_task, seed_verification,
9
11
  };
10
- use crate::workflow_commands::{run_refresh_integrity, run_workflow_command};
12
+ use crate::workflow_commands::{run_doctor, run_refresh_integrity, run_workflow_command};
11
13
 
12
14
  pub fn dispatch_command(
13
15
  root: &Path,
@@ -19,7 +21,9 @@ pub fn dispatch_command(
19
21
  "install-plan" => print_install_plan(args)?,
20
22
  "seed-verification" => seed_verification(root)?,
21
23
  "refresh-integrity" => run_refresh_integrity(root, args)?,
24
+ "doctor" => run_doctor(root, args)?,
22
25
  "quality" => run_quality_command(root, args)?,
26
+ "semantic" => run_semantic_command(root, args)?,
23
27
  "structure" => run_structure_command(root, args)?,
24
28
  "cleanup" => run_cleanup_command(root, args)?,
25
29
  "workflow" => run_workflow_command(root, args)?,
@@ -22,14 +22,21 @@ const HELP: &str = r#"Usage:
22
22
  naome route --prompt <text> [--execute] [--json]
23
23
  naome explain --prompt-file <path> [--json]
24
24
  naome explain --prompt <text> [--json]
25
+ naome doctor [--json]
25
26
  naome install [--package-root <path>] [--installer-js <path>]
26
27
  naome sync [--package-root <path>] [--installer-js <path>]
27
28
  naome install-plan [--harness-version <version>]
28
29
  naome seed-verification
29
30
  naome refresh-integrity [--root <path>] [--json]
30
- naome quality init [--json]
31
- naome quality check --changed [--json]
32
- naome quality report [--json]
31
+ naome quality init [--baseline|--deep-baseline] [--json]
32
+ naome quality check --changed [--include-scanned-paths] [--json]
33
+ naome quality report [--deep] [--include-scanned-paths] [--json]
34
+ naome quality cache status [--json]
35
+ naome quality cache clear
36
+ naome semantic report [--deep] [--json]
37
+ naome semantic check --changed [--json]
38
+ naome semantic route --finding <id> [--json]
39
+ naome semantic loop [--json]
33
40
  naome structure report [--json]
34
41
  naome structure explain --path <path> [--json]
35
42
  naome cleanup plan [--json]
@@ -43,6 +50,30 @@ const HELP: &str = r#"Usage:
43
50
  naome check-task-state [--root <path>] [--admission|--progress|--commit-gate|--push-gate] [--allow-missing-archive]
44
51
  naome validate-verification [--root <path>]"#;
45
52
 
53
+ const PUBLIC_COMMANDS: &[&str] = &[
54
+ "status",
55
+ "next",
56
+ "intent",
57
+ "route",
58
+ "explain",
59
+ "doctor",
60
+ "journal-task",
61
+ "commit-paths",
62
+ "seed-verification",
63
+ "refresh-integrity",
64
+ "workflow",
65
+ "quality",
66
+ "semantic",
67
+ "structure",
68
+ "cleanup",
69
+ "install-plan",
70
+ "install",
71
+ "sync",
72
+ "check-harness-health",
73
+ "check-task-state",
74
+ "validate-verification",
75
+ ];
76
+
46
77
  fn main() {
47
78
  if let Err(error) = run() {
48
79
  eprintln!("NAOME: {error}");
@@ -62,26 +93,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
62
93
  return Ok(());
63
94
  }
64
95
 
65
- if command != "status"
66
- && command != "next"
67
- && command != "intent"
68
- && command != "route"
69
- && command != "explain"
70
- && command != "journal-task"
71
- && command != "commit-paths"
72
- && command != "seed-verification"
73
- && command != "refresh-integrity"
74
- && command != "workflow"
75
- && command != "quality"
76
- && command != "structure"
77
- && command != "cleanup"
78
- && command != "install-plan"
79
- && command != "install"
80
- && command != "sync"
81
- && command != "check-harness-health"
82
- && command != "check-task-state"
83
- && command != "validate-verification"
84
- {
96
+ if !PUBLIC_COMMANDS.contains(&command) {
85
97
  print_help();
86
98
  return Err(format!("unknown command: {command}").into());
87
99
  }
@@ -1,30 +1,38 @@
1
1
  use std::path::Path;
2
2
 
3
3
  use naome_core::{
4
- check_repository_quality, explain_repository_structure, init_repository_quality,
5
- plan_quality_cleanup, route_quality_cleanup, QualityMode,
4
+ check_repository_quality, check_semantic_legacy, clear_quality_cache,
5
+ explain_repository_structure, init_repository_quality_with_mode, plan_quality_cleanup,
6
+ quality_cache_status, route_quality_cleanup, semantic_route_for_finding, QualityInitMode,
7
+ QualityMode,
6
8
  };
7
9
 
8
10
  use crate::cli_args::option_value;
9
11
 
10
12
  pub fn run_quality_command(root: &Path, args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
11
13
  let Some(subcommand) = args.get(1).map(String::as_str) else {
12
- return Err("naome quality requires init, check, or report.".into());
14
+ return Err("naome quality requires init, check, report, or cache.".into());
13
15
  };
14
16
  let json = args.iter().any(|arg| arg == "--json");
15
17
 
16
18
  match subcommand {
17
19
  "init" => {
18
- let result = init_repository_quality(root)?;
20
+ let result = init_repository_quality_with_mode(root, quality_init_mode(args)?)?;
19
21
  if json {
20
22
  println!("{}", serde_json::to_string_pretty(&result)?);
21
23
  } else {
22
24
  println!("NAOME repository quality initialized.");
23
- println!("Baseline violations: {}", result.baseline_violations);
25
+ if result.baseline_pending {
26
+ println!("repository quality policy initialized");
27
+ println!("baseline pending: run naome quality init --baseline");
28
+ } else {
29
+ println!("Baseline violations: {}", result.baseline_violations);
30
+ }
24
31
  }
25
32
  }
26
33
  "check" => run_quality_check(root, args, json)?,
27
- "report" => run_quality_report(root, json)?,
34
+ "report" => run_quality_report(root, args, json)?,
35
+ "cache" => run_quality_cache(root, args, json)?,
28
36
  _ => return Err(format!("unknown naome quality command: {subcommand}").into()),
29
37
  }
30
38
  Ok(())
@@ -61,6 +69,25 @@ pub fn run_structure_command(
61
69
  Ok(())
62
70
  }
63
71
 
72
+ pub fn run_semantic_command(
73
+ root: &Path,
74
+ args: &[String],
75
+ ) -> Result<(), Box<dyn std::error::Error>> {
76
+ let Some(subcommand) = args.get(1).map(String::as_str) else {
77
+ return Err("naome semantic requires report, check, route, or loop.".into());
78
+ };
79
+ let json = args.iter().any(|arg| arg == "--json");
80
+
81
+ match subcommand {
82
+ "report" => run_semantic_report(root, args, json)?,
83
+ "check" => run_semantic_check(root, args, json)?,
84
+ "route" => run_semantic_route(root, args, json)?,
85
+ "loop" => run_semantic_loop(root, json)?,
86
+ _ => return Err(format!("unknown naome semantic command: {subcommand}").into()),
87
+ }
88
+ Ok(())
89
+ }
90
+
64
91
  fn run_quality_check(
65
92
  root: &Path,
66
93
  args: &[String],
@@ -69,9 +96,9 @@ fn run_quality_check(
69
96
  if !args.iter().any(|arg| arg == "--changed") {
70
97
  return Err("naome quality check requires --changed.".into());
71
98
  }
72
- let report = check_repository_quality(root, QualityMode::Changed)?;
99
+ let report = check_repository_quality(root, QualityMode::ChangedFast)?;
73
100
  if json {
74
- println!("{}", serde_json::to_string_pretty(&report)?);
101
+ println!("{}", report_json(&report, args)?);
75
102
  } else if report.ok {
76
103
  println!("NAOME repository quality OK.");
77
104
  } else {
@@ -89,10 +116,19 @@ fn run_quality_check(
89
116
  Ok(())
90
117
  }
91
118
 
92
- fn run_quality_report(root: &Path, json: bool) -> Result<(), Box<dyn std::error::Error>> {
93
- let report = check_repository_quality(root, QualityMode::Report)?;
119
+ fn run_quality_report(
120
+ root: &Path,
121
+ args: &[String],
122
+ json: bool,
123
+ ) -> Result<(), Box<dyn std::error::Error>> {
124
+ let mode = if args.iter().any(|arg| arg == "--deep") {
125
+ QualityMode::DeepReport
126
+ } else {
127
+ QualityMode::Report
128
+ };
129
+ let report = check_repository_quality(root, mode)?;
94
130
  if json {
95
- println!("{}", serde_json::to_string_pretty(&report)?);
131
+ println!("{}", report_json(&report, args)?);
96
132
  } else if report.violations.is_empty() {
97
133
  println!("NAOME repository quality report: no debt found.");
98
134
  } else {
@@ -108,6 +144,32 @@ fn run_quality_report(root: &Path, json: bool) -> Result<(), Box<dyn std::error:
108
144
  Ok(())
109
145
  }
110
146
 
147
+ fn run_quality_cache(
148
+ root: &Path,
149
+ args: &[String],
150
+ json: bool,
151
+ ) -> Result<(), Box<dyn std::error::Error>> {
152
+ let Some(action) = args.get(2).map(String::as_str) else {
153
+ return Err("naome quality cache requires status or clear.".into());
154
+ };
155
+ let status = match action {
156
+ "status" => quality_cache_status(root)?,
157
+ "clear" => clear_quality_cache(root)?,
158
+ _ => return Err(format!("unknown naome quality cache command: {action}").into()),
159
+ };
160
+ if json {
161
+ println!("{}", serde_json::to_string_pretty(&status)?);
162
+ } else if action == "clear" {
163
+ println!("NAOME quality cache cleared.");
164
+ } else {
165
+ println!(
166
+ "NAOME quality cache: {} entrie(s), {} byte(s).",
167
+ status.entry_count, status.bytes
168
+ );
169
+ }
170
+ Ok(())
171
+ }
172
+
111
173
  fn run_cleanup_plan(root: &Path, json: bool) -> Result<(), Box<dyn std::error::Error>> {
112
174
  let plan = plan_quality_cleanup(root)?;
113
175
  if json {
@@ -206,6 +268,135 @@ fn run_structure_explain(
206
268
  Ok(())
207
269
  }
208
270
 
271
+ fn run_semantic_report(
272
+ root: &Path,
273
+ args: &[String],
274
+ json: bool,
275
+ ) -> Result<(), Box<dyn std::error::Error>> {
276
+ let mode = if args.iter().any(|arg| arg == "--deep") {
277
+ QualityMode::DeepReport
278
+ } else {
279
+ QualityMode::Report
280
+ };
281
+ let report = check_semantic_legacy(root, mode)?;
282
+ if json {
283
+ println!("{}", serde_json::to_string_pretty(&report)?);
284
+ } else if report.findings.is_empty() {
285
+ println!("NAOME semantic legacy report: no cleanup candidates found.");
286
+ } else {
287
+ println!(
288
+ "NAOME semantic legacy report: {} finding(s).",
289
+ report.findings.len()
290
+ );
291
+ for finding in report.findings.iter().take(20) {
292
+ println!(
293
+ "- {} {}: {}",
294
+ finding.kind, finding.occurrences[0].path, finding.summary
295
+ );
296
+ }
297
+ }
298
+ Ok(())
299
+ }
300
+
301
+ fn run_semantic_check(
302
+ root: &Path,
303
+ args: &[String],
304
+ json: bool,
305
+ ) -> Result<(), Box<dyn std::error::Error>> {
306
+ if !args.iter().any(|arg| arg == "--changed") {
307
+ return Err("naome semantic check requires --changed.".into());
308
+ }
309
+ let report = check_semantic_legacy(root, QualityMode::ChangedFast)?;
310
+ if json {
311
+ println!("{}", serde_json::to_string_pretty(&report)?);
312
+ } else if report.ok {
313
+ println!("NAOME semantic legacy check OK.");
314
+ } else {
315
+ eprintln!(
316
+ "NAOME semantic legacy check failed with {} finding(s).",
317
+ report.findings.len()
318
+ );
319
+ for finding in report.findings.iter().take(20) {
320
+ eprintln!(
321
+ "- {} {}: {}",
322
+ finding.kind, finding.occurrences[0].path, finding.summary
323
+ );
324
+ }
325
+ }
326
+ if !report.ok {
327
+ std::process::exit(1);
328
+ }
329
+ Ok(())
330
+ }
331
+
332
+ fn run_semantic_route(
333
+ root: &Path,
334
+ args: &[String],
335
+ json: bool,
336
+ ) -> Result<(), Box<dyn std::error::Error>> {
337
+ let Some(finding_id) = option_value(args, "--finding") else {
338
+ return Err("naome semantic route requires --finding <id>.".into());
339
+ };
340
+ let report = check_semantic_legacy(root, QualityMode::DeepReport)?;
341
+ let Some(finding) = semantic_route_for_finding(&report, &finding_id) else {
342
+ return Err(format!("semantic finding not found: {finding_id}").into());
343
+ };
344
+ if json {
345
+ println!("{}", serde_json::to_string_pretty(&finding)?);
346
+ } else {
347
+ println!("NAOME semantic cleanup route for {}", finding.id);
348
+ println!("{}", finding.cleanup_route.intent);
349
+ for instruction in &finding.cleanup_route.agent_instructions {
350
+ println!("- {instruction}");
351
+ }
352
+ let paths = finding
353
+ .occurrences
354
+ .iter()
355
+ .map(|occurrence| occurrence.path.as_str())
356
+ .collect::<Vec<_>>()
357
+ .join(", ");
358
+ println!("Affected paths: {paths}");
359
+ }
360
+ Ok(())
361
+ }
362
+
363
+ fn run_semantic_loop(root: &Path, json: bool) -> Result<(), Box<dyn std::error::Error>> {
364
+ let changed = check_semantic_legacy(root, QualityMode::ChangedFast)?;
365
+ let (action, finding) = if let Some(finding) = changed.findings.first().cloned() {
366
+ ("cleanup_changed_finding", Some(finding))
367
+ } else {
368
+ let report = check_semantic_legacy(root, QualityMode::Report)?;
369
+ if let Some(finding) = report.findings.first().cloned() {
370
+ ("report_legacy_debt", Some(finding))
371
+ } else {
372
+ ("complete", None)
373
+ }
374
+ };
375
+
376
+ if json {
377
+ println!(
378
+ "{}",
379
+ serde_json::to_string_pretty(&serde_json::json!({
380
+ "schema": "naome.semantic-loop.v1",
381
+ "ok": action != "cleanup_changed_finding",
382
+ "action": action,
383
+ "finding": finding
384
+ }))?
385
+ );
386
+ } else {
387
+ match finding {
388
+ Some(finding) => {
389
+ println!("NAOME semantic loop action: {action}");
390
+ println!("{} {}", finding.kind, finding.id);
391
+ println!("{}", finding.summary);
392
+ println!("Run: naome semantic route --finding {} --json", finding.id);
393
+ }
394
+ None => println!("NAOME semantic loop complete: no semantic cleanup candidates found."),
395
+ }
396
+ }
397
+ Ok(())
398
+ }
399
+
209
400
  fn is_structure_check(check_id: &str) -> bool {
210
401
  matches!(
211
402
  check_id,
@@ -220,6 +411,34 @@ fn is_structure_check(check_id: &str) -> bool {
220
411
  )
221
412
  }
222
413
 
414
+ fn quality_init_mode(args: &[String]) -> Result<QualityInitMode, Box<dyn std::error::Error>> {
415
+ let baseline = args.iter().any(|arg| arg == "--baseline");
416
+ let deep_baseline = args.iter().any(|arg| arg == "--deep-baseline");
417
+ if baseline && deep_baseline {
418
+ return Err("naome quality init accepts only one baseline mode.".into());
419
+ }
420
+ Ok(if deep_baseline {
421
+ QualityInitMode::DeepBaseline
422
+ } else if baseline {
423
+ QualityInitMode::Baseline
424
+ } else {
425
+ QualityInitMode::SeedOnly
426
+ })
427
+ }
428
+
429
+ fn report_json(
430
+ report: &naome_core::QualityReport,
431
+ args: &[String],
432
+ ) -> Result<String, Box<dyn std::error::Error>> {
433
+ let mut value = serde_json::to_value(report)?;
434
+ if !args.iter().any(|arg| arg == "--include-scanned-paths") {
435
+ if let Some(object) = value.as_object_mut() {
436
+ object.remove("scannedPaths");
437
+ }
438
+ }
439
+ Ok(serde_json::to_string_pretty(&value)?)
440
+ }
441
+
223
442
  fn print_quality_violation(violation: &naome_core::QualityViolation) {
224
443
  let location = violation
225
444
  .line