@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.
- package/Cargo.lock +2 -2
- package/README.md +108 -47
- package/bin/naome.js +16 -1
- package/crates/naome-cli/Cargo.toml +1 -1
- package/crates/naome-cli/src/dispatcher.rs +6 -2
- package/crates/naome-cli/src/main.rs +35 -23
- package/crates/naome-cli/src/quality_commands.rs +230 -11
- package/crates/naome-cli/src/workflow_commands.rs +21 -1
- package/crates/naome-core/Cargo.toml +1 -1
- package/crates/naome-core/src/git.rs +4 -2
- package/crates/naome-core/src/install_plan.rs +2 -0
- package/crates/naome-core/src/lib.rs +11 -7
- package/crates/naome-core/src/quality/baseline.rs +8 -0
- package/crates/naome-core/src/quality/cache.rs +153 -0
- package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +25 -11
- package/crates/naome-core/src/quality/checks/near_duplicates.rs +4 -2
- package/crates/naome-core/src/quality/checks.rs +7 -8
- package/crates/naome-core/src/quality/cleanup.rs +36 -3
- package/crates/naome-core/src/quality/mod.rs +57 -9
- package/crates/naome-core/src/quality/scanner/analysis/normalize.rs +78 -0
- package/crates/naome-core/src/quality/scanner/analysis.rs +160 -0
- package/crates/naome-core/src/quality/scanner/repo_paths.rs +39 -3
- package/crates/naome-core/src/quality/scanner.rs +193 -220
- package/crates/naome-core/src/quality/semantic/checks.rs +134 -0
- package/crates/naome-core/src/quality/semantic/extract.rs +158 -0
- package/crates/naome-core/src/quality/semantic/model.rs +85 -0
- package/crates/naome-core/src/quality/semantic/route.rs +52 -0
- package/crates/naome-core/src/quality/semantic.rs +68 -0
- package/crates/naome-core/src/quality/structure/checks/directory.rs +9 -19
- package/crates/naome-core/src/quality/structure/checks.rs +1 -1
- package/crates/naome-core/src/quality/structure/classify.rs +52 -0
- package/crates/naome-core/src/quality/structure/mod.rs +2 -2
- package/crates/naome-core/src/quality/structure/model.rs +8 -1
- package/crates/naome-core/src/quality/types.rs +40 -2
- package/crates/naome-core/src/route/builtin_checks.rs +1 -15
- package/crates/naome-core/src/workflow/doctor.rs +144 -0
- package/crates/naome-core/src/workflow/mod.rs +2 -0
- package/crates/naome-core/src/workflow/mutation.rs +1 -2
- package/crates/naome-core/tests/install_plan.rs +2 -0
- package/crates/naome-core/tests/quality.rs +14 -5
- package/crates/naome-core/tests/quality_performance.rs +231 -0
- package/crates/naome-core/tests/quality_structure_policy.rs +19 -0
- package/crates/naome-core/tests/route_user_diff.rs +10 -6
- package/crates/naome-core/tests/semantic_legacy.rs +140 -0
- package/crates/naome-core/tests/workflow_doctor.rs +24 -0
- package/crates/naome-core/tests/workflow_policy.rs +6 -1
- package/installer/git-boundary.js +1 -0
- package/native/darwin-arm64/naome +0 -0
- package/native/linux-x64/naome +0 -0
- package/package.json +1 -1
- package/templates/naome-root/.naome/bin/check-harness-health.js +2 -2
- package/templates/naome-root/.naome/bin/check-task-state.js +2 -2
- package/templates/naome-root/.naome/bin/naome.js +11 -4
- package/templates/naome-root/.naome/manifest.json +2 -2
- package/templates/naome-root/.naomeignore +1 -0
- package/templates/naome-root/docs/naome/agent-workflow.md +16 -14
- 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.
|
|
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.
|
|
87
|
+
version = "1.3.0"
|
|
88
88
|
dependencies = [
|
|
89
89
|
"serde",
|
|
90
90
|
"serde_json",
|
package/README.md
CHANGED
|
@@ -1,70 +1,131 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
NAOME
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22
|
+
Install the CLI, then sync NAOME into a repository:
|
|
36
23
|
|
|
37
|
-
```
|
|
24
|
+
```shell
|
|
25
|
+
npm install -g @lamentis/naome
|
|
26
|
+
cd /path/to/repo
|
|
38
27
|
naome sync
|
|
39
28
|
```
|
|
40
29
|
|
|
41
|
-
|
|
42
|
-
sync the harness files:
|
|
30
|
+
For an initialized repository, start with:
|
|
43
31
|
|
|
44
|
-
```
|
|
45
|
-
naome
|
|
46
|
-
naome
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
64
|
+
## CLI Reference
|
|
56
65
|
|
|
57
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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) {
|
|
@@ -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::{
|
|
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
|
|
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,
|
|
5
|
-
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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::
|
|
99
|
+
let report = check_repository_quality(root, QualityMode::ChangedFast)?;
|
|
73
100
|
if json {
|
|
74
|
-
println!("{}",
|
|
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(
|
|
93
|
-
|
|
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!("{}",
|
|
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
|