@lamentis/naome 1.2.1 → 1.3.1
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 +117 -47
- package/bin/naome.js +65 -12
- package/crates/naome-cli/Cargo.toml +1 -1
- package/crates/naome-cli/src/context_commands.rs +47 -0
- package/crates/naome-cli/src/dispatcher.rs +12 -2
- package/crates/naome-cli/src/main.rs +78 -29
- package/crates/naome-cli/src/quality_commands.rs +238 -34
- package/crates/naome-cli/src/quality_output.rs +34 -0
- package/crates/naome-cli/src/quality_reconcile_command.rs +45 -0
- package/crates/naome-cli/src/repository_model_commands.rs +84 -0
- package/crates/naome-cli/src/task_commands.rs +62 -0
- package/crates/naome-cli/src/workflow_commands.rs +120 -3
- package/crates/naome-core/Cargo.toml +1 -1
- package/crates/naome-core/src/context/helpers.rs +75 -0
- package/crates/naome-core/src/context/select.rs +134 -0
- package/crates/naome-core/src/context/types.rs +43 -0
- package/crates/naome-core/src/context.rs +6 -0
- package/crates/naome-core/src/decision/states.rs +1 -1
- package/crates/naome-core/src/decision.rs +4 -1
- package/crates/naome-core/src/git.rs +4 -2
- package/crates/naome-core/src/install_plan.rs +20 -0
- package/crates/naome-core/src/journal.rs +2 -7
- package/crates/naome-core/src/lib.rs +35 -8
- package/crates/naome-core/src/quality/adapter_ios.rs +131 -0
- package/crates/naome-core/src/quality/adapter_support.rs +67 -0
- package/crates/naome-core/src/quality/adapters.rs +81 -18
- package/crates/naome-core/src/quality/baseline.rs +8 -0
- package/crates/naome-core/src/quality/cache.rs +151 -0
- package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +19 -8
- 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/config.rs +21 -3
- package/crates/naome-core/src/quality/mod.rs +189 -10
- package/crates/naome-core/src/quality/reconcile.rs +138 -0
- package/crates/naome-core/src/quality/reconcile_anchors.rs +64 -0
- package/crates/naome-core/src/quality/scanner/analysis/normalize.rs +78 -0
- package/crates/naome-core/src/quality/scanner/analysis.rs +175 -0
- package/crates/naome-core/src/quality/scanner/repo_paths.rs +39 -3
- package/crates/naome-core/src/quality/scanner.rs +235 -217
- package/crates/naome-core/src/quality/semantic/checks.rs +151 -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/adapter_ios.rs +149 -0
- package/crates/naome-core/src/quality/structure/adapters.rs +60 -42
- package/crates/naome-core/src/quality/structure/checks/directory.rs +13 -21
- package/crates/naome-core/src/quality/structure/checks.rs +1 -1
- package/crates/naome-core/src/quality/structure/classify/roles.rs +51 -5
- package/crates/naome-core/src/quality/structure/classify.rs +52 -0
- package/crates/naome-core/src/quality/structure/config.rs +24 -3
- package/crates/naome-core/src/quality/structure/mod.rs +5 -2
- package/crates/naome-core/src/quality/structure/model.rs +8 -1
- package/crates/naome-core/src/quality/types.rs +59 -2
- package/crates/naome-core/src/repository_model/detect.rs +188 -0
- package/crates/naome-core/src/repository_model/explain.rs +121 -0
- package/crates/naome-core/src/repository_model/path_scan.rs +67 -0
- package/crates/naome-core/src/repository_model/path_support.rs +59 -0
- package/crates/naome-core/src/repository_model/types.rs +152 -0
- package/crates/naome-core/src/repository_model/world.rs +48 -0
- package/crates/naome-core/src/repository_model/world_adapters.rs +145 -0
- package/crates/naome-core/src/repository_model/world_path_facts.rs +55 -0
- package/crates/naome-core/src/repository_model/world_paths.rs +168 -0
- package/crates/naome-core/src/repository_model.rs +164 -0
- package/crates/naome-core/src/route/builtin_checks.rs +41 -16
- package/crates/naome-core/src/task_ledger/import.rs +142 -0
- package/crates/naome-core/src/task_ledger/model.rs +13 -0
- package/crates/naome-core/src/task_ledger/proof_record.rs +52 -0
- package/crates/naome-core/src/task_ledger/read.rs +118 -0
- package/crates/naome-core/src/task_ledger/render.rs +55 -0
- package/crates/naome-core/src/task_ledger/write.rs +38 -0
- package/crates/naome-core/src/task_ledger.rs +48 -0
- package/crates/naome-core/src/task_state/api.rs +4 -2
- package/crates/naome-core/src/task_state/completed_refresh.rs +5 -16
- package/crates/naome-core/src/task_state/diff.rs +2 -2
- package/crates/naome-core/src/task_state/evidence.rs +8 -3
- package/crates/naome-core/src/task_state/mod.rs +1 -1
- package/crates/naome-core/src/task_state/progress.rs +13 -0
- package/crates/naome-core/src/task_state/proof_model.rs +8 -8
- package/crates/naome-core/src/task_state/repair.rs +2 -2
- package/crates/naome-core/src/task_state/task_diff_api.rs +9 -18
- package/crates/naome-core/src/task_state/types.rs +24 -0
- package/crates/naome-core/src/verification.rs +29 -18
- package/crates/naome-core/src/workflow/agent/capability.rs +194 -0
- package/crates/naome-core/src/workflow/agent/context_delta.rs +42 -0
- package/crates/naome-core/src/workflow/agent/decision.rs +32 -0
- package/crates/naome-core/src/workflow/agent/execution.rs +80 -0
- package/crates/naome-core/src/workflow/agent/proof.rs +24 -0
- package/crates/naome-core/src/workflow/agent/support.rs +58 -0
- package/crates/naome-core/src/workflow/agent/watchdog.rs +47 -0
- package/crates/naome-core/src/workflow/agent.rs +34 -0
- package/crates/naome-core/src/workflow/agent_types.rs +105 -0
- package/crates/naome-core/src/workflow/doctor.rs +183 -0
- package/crates/naome-core/src/workflow/mod.rs +13 -0
- package/crates/naome-core/src/workflow/mutation.rs +1 -2
- package/crates/naome-core/src/workflow/output.rs +8 -2
- package/crates/naome-core/src/workflow/phase_inference.rs +1 -1
- package/crates/naome-core/tests/context.rs +99 -0
- package/crates/naome-core/tests/harness_health.rs +4 -0
- package/crates/naome-core/tests/install_plan.rs +14 -0
- package/crates/naome-core/tests/quality.rs +190 -5
- package/crates/naome-core/tests/quality_performance.rs +268 -0
- package/crates/naome-core/tests/quality_structure_adapters.rs +39 -0
- package/crates/naome-core/tests/quality_structure_policy.rs +19 -0
- package/crates/naome-core/tests/repo_support/mod.rs +5 -1
- package/crates/naome-core/tests/repo_support/verification_values.rs +55 -0
- package/crates/naome-core/tests/repository_model.rs +281 -0
- package/crates/naome-core/tests/route_user_diff.rs +59 -7
- package/crates/naome-core/tests/semantic_legacy.rs +174 -0
- package/crates/naome-core/tests/task_ledger.rs +328 -0
- package/crates/naome-core/tests/task_state.rs +28 -0
- package/crates/naome-core/tests/verification.rs +29 -36
- package/crates/naome-core/tests/workflow_agent.rs +233 -0
- package/crates/naome-core/tests/workflow_agent_support/mod.rs +159 -0
- package/crates/naome-core/tests/workflow_doctor.rs +45 -0
- package/crates/naome-core/tests/workflow_policy.rs +6 -1
- package/installer/codex-hooks.js +121 -0
- package/installer/context.js +10 -0
- package/installer/filesystem.js +4 -0
- package/installer/flows.js +8 -4
- package/installer/git-boundary.js +1 -0
- package/installer/harness-files.js +6 -0
- package/installer/install-plan.js +4 -0
- package/installer/main.js +1 -1
- package/installer/native.js +1 -1
- package/native/darwin-arm64/naome +0 -0
- package/native/linux-x64/naome +0 -0
- package/package.json +1 -1
- package/templates/naome-root/.codex/config.toml +2 -0
- package/templates/naome-root/.codex/hooks.json +70 -0
- package/templates/naome-root/.naome/bin/check-harness-health.js +8 -6
- package/templates/naome-root/.naome/bin/check-task-state.js +12 -7
- package/templates/naome-root/.naome/bin/codex-hook-io.js +122 -0
- package/templates/naome-root/.naome/bin/codex-hook-policy.js +180 -0
- package/templates/naome-root/.naome/bin/codex-hook-runtime.js +174 -0
- package/templates/naome-root/.naome/bin/codex-hook.js +6 -0
- package/templates/naome-root/.naome/bin/naome.js +45 -7
- package/templates/naome-root/.naome/manifest.json +12 -6
- package/templates/naome-root/.naome/repository-model.json +6 -0
- package/templates/naome-root/.naome/repository-quality.json +3 -1
- package/templates/naome-root/.naome/verification.json +15 -1
- package/templates/naome-root/.naomeignore +1 -0
- package/templates/naome-root/AGENTS.md +38 -83
- package/templates/naome-root/docs/naome/agent-workflow.md +66 -28
- package/templates/naome-root/docs/naome/codex-hooks.md +82 -0
- package/templates/naome-root/docs/naome/context-economy.md +73 -0
- package/templates/naome-root/docs/naome/first-run.md +25 -14
- package/templates/naome-root/docs/naome/index.md +18 -10
- package/templates/naome-root/docs/naome/repository-model.md +92 -0
- package/templates/naome-root/docs/naome/repository-quality.md +104 -5
- package/templates/naome-root/docs/naome/repository-structure.md +10 -3
- package/templates/naome-root/docs/naome/task-ledger.md +71 -0
- package/templates/naome-root/docs/naome/testing.md +16 -3
|
@@ -5,17 +5,21 @@ use crate::quality::adapter_support::{
|
|
|
5
5
|
find_adapter_by_id, validate_ids, AdapterDescriptor, RepoSignals,
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
use super::adapter_ios;
|
|
8
9
|
use super::model::RepositoryStructureConfig;
|
|
9
10
|
|
|
10
11
|
const CONFIG_PATH: &str = ".naome/repository-structure.json";
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
#[derive(Clone, Copy)]
|
|
14
|
+
pub(super) struct StructureAdapter {
|
|
15
|
+
pub(super) id: &'static str,
|
|
16
|
+
pub(super) detect: fn(&RepoSignals<'_>) -> bool,
|
|
17
|
+
pub(super) source_roots: &'static [&'static str],
|
|
18
|
+
pub(super) test_roots: &'static [&'static str],
|
|
19
|
+
pub(super) generated_roots: &'static [&'static str],
|
|
20
|
+
pub(super) artifact_roots: &'static [&'static str],
|
|
21
|
+
pub(super) module_roots: &'static [&'static str],
|
|
22
|
+
pub(super) allowed_root_files: &'static [&'static str],
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
impl AdapterDescriptor for StructureAdapter {
|
|
@@ -29,17 +33,21 @@ impl AdapterDescriptor for StructureAdapter {
|
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
pub fn detected_structure_adapter_ids(paths: &[String]) -> Vec<String> {
|
|
32
|
-
|
|
36
|
+
let registry = registry();
|
|
37
|
+
detected_ids(paths, ®istry)
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
pub fn apply_structure_adapters(
|
|
36
41
|
mut config: RepositoryStructureConfig,
|
|
37
42
|
) -> Result<RepositoryStructureConfig, NaomeError> {
|
|
38
|
-
|
|
43
|
+
let registry = registry();
|
|
44
|
+
validate_ids(&config.enabled_adapters, ®istry, CONFIG_PATH)?;
|
|
39
45
|
for adapter_id in config.enabled_adapters.clone() {
|
|
40
|
-
let adapter = find_adapter_by_id(registry
|
|
46
|
+
let adapter = find_adapter_by_id(®istry, &adapter_id, CONFIG_PATH)?;
|
|
41
47
|
extend_unique(&mut config.source_roots, adapter.source_roots);
|
|
42
48
|
extend_unique(&mut config.test_roots, adapter.test_roots);
|
|
49
|
+
extend_unique(&mut config.generated_roots, adapter.generated_roots);
|
|
50
|
+
extend_unique(&mut config.artifact_roots, adapter.artifact_roots);
|
|
43
51
|
extend_unique(&mut config.module_roots, adapter.module_roots);
|
|
44
52
|
extend_unique(&mut config.allowed_root_files, adapter.allowed_root_files);
|
|
45
53
|
}
|
|
@@ -47,38 +55,48 @@ pub fn apply_structure_adapters(
|
|
|
47
55
|
}
|
|
48
56
|
|
|
49
57
|
pub fn validate_structure_adapter_ids(ids: &[String]) -> Result<(), NaomeError> {
|
|
50
|
-
|
|
58
|
+
let registry = registry();
|
|
59
|
+
validate_ids(ids, ®istry, CONFIG_PATH)
|
|
51
60
|
}
|
|
52
61
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
62
|
+
const BASE_STRUCTURE_ADAPTERS: &[StructureAdapter] = &[
|
|
63
|
+
StructureAdapter {
|
|
64
|
+
id: "rust",
|
|
65
|
+
detect: detects_rust_project,
|
|
66
|
+
source_roots: &["src/**", "crates/*/src/**", "**/crates/*/src/**"],
|
|
67
|
+
test_roots: &["tests/**", "crates/*/tests/**", "**/crates/*/tests/**"],
|
|
68
|
+
generated_roots: &[],
|
|
69
|
+
artifact_roots: &[],
|
|
70
|
+
module_roots: &["src/**", "crates/*/src/**", "**/crates/*/src/**"],
|
|
71
|
+
allowed_root_files: &["Cargo.toml", "Cargo.lock"],
|
|
72
|
+
},
|
|
73
|
+
StructureAdapter {
|
|
74
|
+
id: "javascript-typescript",
|
|
75
|
+
detect: detects_javascript_typescript_project,
|
|
76
|
+
source_roots: &[
|
|
77
|
+
"src/**",
|
|
78
|
+
"app/**",
|
|
79
|
+
"pages/**",
|
|
80
|
+
"components/**",
|
|
81
|
+
"packages/*/src/**",
|
|
82
|
+
"**/packages/*/src/**",
|
|
83
|
+
],
|
|
84
|
+
test_roots: &["test/**", "tests/**", "__tests__/**", "packages/*/tests/**"],
|
|
85
|
+
generated_roots: &[],
|
|
86
|
+
artifact_roots: &["coverage/**", "**/coverage/**", ".next/**", "**/.next/**"],
|
|
87
|
+
module_roots: &["src/**", "app/**", "packages/*/src/**"],
|
|
88
|
+
allowed_root_files: &[
|
|
89
|
+
"package.json",
|
|
90
|
+
"tsconfig.json",
|
|
91
|
+
"vite.config.ts",
|
|
92
|
+
"next.config.js",
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
fn registry() -> Vec<StructureAdapter> {
|
|
98
|
+
let mut adapters = Vec::new();
|
|
99
|
+
adapters.extend_from_slice(BASE_STRUCTURE_ADAPTERS);
|
|
100
|
+
adapters.extend(adapter_ios::adapters());
|
|
101
|
+
adapters
|
|
84
102
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
use std::collections::BTreeMap;
|
|
2
|
-
|
|
3
1
|
use crate::quality::structure::model::{RepositoryStructureModel, StructurePath};
|
|
4
2
|
use crate::quality::types::{QualityMode, QualityViolation};
|
|
5
3
|
|
|
@@ -38,7 +36,7 @@ pub(super) fn directory_size(
|
|
|
38
36
|
if directory.file_count <= model.config.limits.max_directory_files {
|
|
39
37
|
continue;
|
|
40
38
|
}
|
|
41
|
-
if mode
|
|
39
|
+
if mode.is_changed() && directory.direct_changed_paths.is_empty() {
|
|
42
40
|
continue;
|
|
43
41
|
}
|
|
44
42
|
push_with_limit(
|
|
@@ -85,20 +83,13 @@ pub(super) fn case_collisions(
|
|
|
85
83
|
mode: QualityMode,
|
|
86
84
|
violations: &mut Vec<QualityViolation>,
|
|
87
85
|
) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
for group in groups.values().filter(|group| group.len() > 1) {
|
|
96
|
-
let changed_group = group.iter().any(|path| {
|
|
97
|
-
model.paths.iter().any(|candidate| {
|
|
98
|
-
candidate.explanation.path == *path && candidate.explanation.changed
|
|
99
|
-
})
|
|
100
|
-
});
|
|
101
|
-
if mode == QualityMode::Changed && !changed_group {
|
|
86
|
+
for group in model
|
|
87
|
+
.lowercase_paths
|
|
88
|
+
.values()
|
|
89
|
+
.filter(|group| group.len() > 1)
|
|
90
|
+
{
|
|
91
|
+
let changed_group = group.iter().any(|path| model.changed_paths.contains(path));
|
|
92
|
+
if mode.is_changed() && !changed_group {
|
|
102
93
|
continue;
|
|
103
94
|
}
|
|
104
95
|
for path in group {
|
|
@@ -134,10 +125,11 @@ fn related_module_paths(model: &RepositoryStructureModel, path: &StructurePath)
|
|
|
134
125
|
return Vec::new();
|
|
135
126
|
};
|
|
136
127
|
model
|
|
137
|
-
.
|
|
138
|
-
.
|
|
139
|
-
.
|
|
140
|
-
.
|
|
128
|
+
.module_paths
|
|
129
|
+
.get(module)
|
|
130
|
+
.into_iter()
|
|
131
|
+
.flatten()
|
|
132
|
+
.cloned()
|
|
141
133
|
.filter(|candidate| candidate != &path.explanation.path)
|
|
142
134
|
.take(10)
|
|
143
135
|
.collect()
|
|
@@ -53,7 +53,7 @@ fn check_enabled(model: &RepositoryStructureModel, check_id: &str) -> bool {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
fn applies(path: &StructurePath, mode: QualityMode) -> bool {
|
|
56
|
-
mode
|
|
56
|
+
!mode.is_changed() || path.explanation.changed
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
fn push(
|
|
@@ -2,13 +2,37 @@ use crate::paths;
|
|
|
2
2
|
|
|
3
3
|
use crate::quality::structure::model::RepositoryStructureConfig;
|
|
4
4
|
|
|
5
|
+
const ROLE_SEGMENTS: &[&str] = &[
|
|
6
|
+
"src",
|
|
7
|
+
"source",
|
|
8
|
+
"sources",
|
|
9
|
+
"test",
|
|
10
|
+
"tests",
|
|
11
|
+
"docs",
|
|
12
|
+
"doc",
|
|
13
|
+
"scripts",
|
|
14
|
+
"generated",
|
|
15
|
+
"resources",
|
|
16
|
+
"views",
|
|
17
|
+
];
|
|
18
|
+
|
|
5
19
|
pub fn role_for(path: &str, segments: &[String], config: &RepositoryStructureConfig) -> String {
|
|
6
20
|
let lower = path.to_ascii_lowercase();
|
|
7
21
|
if has_segment(segments, &["node_modules", "vendor", "third_party"]) {
|
|
8
22
|
return "dependency/vendor".to_string();
|
|
9
23
|
}
|
|
10
24
|
if paths::matches_any(path, &config.generated_roots)
|
|
11
|
-
|| has_segment(
|
|
25
|
+
|| has_segment(
|
|
26
|
+
segments,
|
|
27
|
+
&[
|
|
28
|
+
"generated",
|
|
29
|
+
"__generated__",
|
|
30
|
+
"codegen",
|
|
31
|
+
"swiftgen",
|
|
32
|
+
"sourcery",
|
|
33
|
+
],
|
|
34
|
+
)
|
|
35
|
+
|| is_generated_ios_path(&lower)
|
|
12
36
|
{
|
|
13
37
|
return "generated".to_string();
|
|
14
38
|
}
|
|
@@ -131,7 +155,13 @@ fn is_test_path(lower: &str, segments: &[String]) -> bool {
|
|
|
131
155
|
lower.contains(".test.")
|
|
132
156
|
|| lower.contains(".spec.")
|
|
133
157
|
|| lower.ends_with("_test.rs")
|
|
158
|
+
|| lower.ends_with("tests.swift")
|
|
159
|
+
|| lower.ends_with("uitests.swift")
|
|
134
160
|
|| has_segment(segments, &["test", "tests", "__tests__"])
|
|
161
|
+
|| segments.iter().any(|segment| {
|
|
162
|
+
let lower = segment.to_ascii_lowercase();
|
|
163
|
+
lower.ends_with("tests") || lower.ends_with("uitests")
|
|
164
|
+
})
|
|
135
165
|
}
|
|
136
166
|
|
|
137
167
|
fn is_docs_path(lower: &str, segments: &[String]) -> bool {
|
|
@@ -150,8 +180,16 @@ fn is_config_path(
|
|
|
150
180
|
paths::matches_any(path, &config.allowed_root_files)
|
|
151
181
|
|| segments.len() == 1 && (lower.starts_with('.') || lower.ends_with(".toml"))
|
|
152
182
|
|| lower.ends_with(".json")
|
|
183
|
+
|| lower.ends_with(".plist")
|
|
184
|
+
|| lower.ends_with(".entitlements")
|
|
185
|
+
|| lower.ends_with(".xcconfig")
|
|
186
|
+
|| lower.ends_with(".strings")
|
|
187
|
+
|| lower.ends_with(".stringsdict")
|
|
153
188
|
|| lower.ends_with(".yaml")
|
|
154
189
|
|| lower.ends_with(".yml")
|
|
190
|
+
|| lower.ends_with("package.swift")
|
|
191
|
+
|| lower.ends_with("podfile")
|
|
192
|
+
|| lower.ends_with("cartfile")
|
|
155
193
|
}
|
|
156
194
|
|
|
157
195
|
fn is_script_path(lower: &str, segments: &[String]) -> bool {
|
|
@@ -163,6 +201,16 @@ fn is_artifact_path(lower: &str) -> bool {
|
|
|
163
201
|
|| lower.ends_with(".log")
|
|
164
202
|
|| lower.ends_with(".map")
|
|
165
203
|
|| lower.ends_with(".min.js")
|
|
204
|
+
|| lower.contains("/deriveddata/")
|
|
205
|
+
|| lower.ends_with(".xcarchive")
|
|
206
|
+
|| lower.ends_with(".dsym")
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
fn is_generated_ios_path(lower: &str) -> bool {
|
|
210
|
+
lower.ends_with(".generated.swift")
|
|
211
|
+
|| lower.ends_with(".pb.swift")
|
|
212
|
+
|| lower.ends_with(".grpc.swift")
|
|
213
|
+
|| lower.ends_with("/r.generated.swift")
|
|
166
214
|
}
|
|
167
215
|
|
|
168
216
|
fn has_segment(segments: &[String], expected: &[&str]) -> bool {
|
|
@@ -173,10 +221,8 @@ fn has_segment(segments: &[String], expected: &[&str]) -> bool {
|
|
|
173
221
|
}
|
|
174
222
|
|
|
175
223
|
fn is_role_segment(segment: &str) -> bool {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
"src" | "test" | "tests" | "docs" | "doc" | "scripts" | "generated"
|
|
179
|
-
)
|
|
224
|
+
let lower = segment.to_ascii_lowercase();
|
|
225
|
+
ROLE_SEGMENTS.contains(&lower.as_str())
|
|
180
226
|
}
|
|
181
227
|
|
|
182
228
|
fn stem(file_name: &str) -> Option<String> {
|
|
@@ -24,10 +24,16 @@ pub fn build_structure_model(
|
|
|
24
24
|
.collect::<Vec<_>>();
|
|
25
25
|
paths.sort_by(|left, right| left.explanation.path.cmp(&right.explanation.path));
|
|
26
26
|
let directories = directories_for(&paths);
|
|
27
|
+
let indexes = indexes_for(&paths);
|
|
27
28
|
RepositoryStructureModel {
|
|
28
29
|
config,
|
|
29
30
|
paths,
|
|
30
31
|
directories,
|
|
32
|
+
module_paths: indexes.module_paths,
|
|
33
|
+
directory_paths: indexes.directory_paths,
|
|
34
|
+
role_paths: indexes.role_paths,
|
|
35
|
+
lowercase_paths: indexes.lowercase_paths,
|
|
36
|
+
changed_paths: indexes.changed_paths,
|
|
31
37
|
}
|
|
32
38
|
}
|
|
33
39
|
|
|
@@ -92,3 +98,49 @@ fn directory_of(path: &str) -> String {
|
|
|
92
98
|
.map(|(directory, _)| directory.to_string())
|
|
93
99
|
.unwrap_or_else(|| ".".to_string())
|
|
94
100
|
}
|
|
101
|
+
|
|
102
|
+
struct StructureIndexes {
|
|
103
|
+
module_paths: BTreeMap<String, Vec<String>>,
|
|
104
|
+
directory_paths: BTreeMap<String, Vec<String>>,
|
|
105
|
+
role_paths: BTreeMap<String, Vec<String>>,
|
|
106
|
+
lowercase_paths: BTreeMap<String, Vec<String>>,
|
|
107
|
+
changed_paths: BTreeSet<String>,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fn indexes_for(paths: &[StructurePath]) -> StructureIndexes {
|
|
111
|
+
let mut module_paths = BTreeMap::new();
|
|
112
|
+
let mut directory_paths = BTreeMap::new();
|
|
113
|
+
let mut role_paths = BTreeMap::new();
|
|
114
|
+
let mut lowercase_paths = BTreeMap::new();
|
|
115
|
+
let mut changed_paths = BTreeSet::new();
|
|
116
|
+
for path in paths {
|
|
117
|
+
let value = path.explanation.path.clone();
|
|
118
|
+
if let Some(module) = &path.explanation.module {
|
|
119
|
+
push_index(&mut module_paths, module, &value);
|
|
120
|
+
}
|
|
121
|
+
push_index(&mut directory_paths, &path.explanation.directory, &value);
|
|
122
|
+
push_index(&mut role_paths, &path.explanation.role, &value);
|
|
123
|
+
push_index(
|
|
124
|
+
&mut lowercase_paths,
|
|
125
|
+
&path.explanation.path.to_ascii_lowercase(),
|
|
126
|
+
&value,
|
|
127
|
+
);
|
|
128
|
+
if path.explanation.changed {
|
|
129
|
+
changed_paths.insert(value);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
StructureIndexes {
|
|
133
|
+
module_paths,
|
|
134
|
+
directory_paths,
|
|
135
|
+
role_paths,
|
|
136
|
+
lowercase_paths,
|
|
137
|
+
changed_paths,
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
fn push_index(index: &mut BTreeMap<String, Vec<String>>, key: &str, value: &str) {
|
|
142
|
+
index
|
|
143
|
+
.entry(key.to_string())
|
|
144
|
+
.or_default()
|
|
145
|
+
.push(value.to_string());
|
|
146
|
+
}
|
|
@@ -19,6 +19,15 @@ pub fn structure_config_relative_path() -> &'static str {
|
|
|
19
19
|
pub fn read_structure_config(
|
|
20
20
|
root: &Path,
|
|
21
21
|
paths: &[String],
|
|
22
|
+
) -> Result<RepositoryStructureConfig, NaomeError> {
|
|
23
|
+
let config = read_policy_structure_config(root, paths)?;
|
|
24
|
+
validate_structure_config(&config)?;
|
|
25
|
+
apply_structure_adapters(config)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pub(crate) fn read_policy_structure_config(
|
|
29
|
+
root: &Path,
|
|
30
|
+
paths: &[String],
|
|
22
31
|
) -> Result<RepositoryStructureConfig, NaomeError> {
|
|
23
32
|
let path = root.join(CONFIG_RELATIVE_PATH);
|
|
24
33
|
let config = if path.is_file() {
|
|
@@ -27,7 +36,20 @@ pub fn read_structure_config(
|
|
|
27
36
|
generated_structure_config(paths)
|
|
28
37
|
};
|
|
29
38
|
validate_structure_config(&config)?;
|
|
30
|
-
|
|
39
|
+
Ok(config)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
pub(crate) fn write_policy_structure_config(
|
|
43
|
+
root: &Path,
|
|
44
|
+
config: &RepositoryStructureConfig,
|
|
45
|
+
) -> Result<(), NaomeError> {
|
|
46
|
+
let path = root.join(CONFIG_RELATIVE_PATH);
|
|
47
|
+
if let Some(parent) = path.parent() {
|
|
48
|
+
fs::create_dir_all(parent)?;
|
|
49
|
+
}
|
|
50
|
+
let content = serde_json::to_string_pretty(config)?;
|
|
51
|
+
fs::write(path, format!("{content}\n"))?;
|
|
52
|
+
Ok(())
|
|
31
53
|
}
|
|
32
54
|
|
|
33
55
|
pub fn write_default_structure_config_if_missing(
|
|
@@ -41,8 +63,7 @@ pub fn write_default_structure_config_if_missing(
|
|
|
41
63
|
if let Some(parent) = path.parent() {
|
|
42
64
|
fs::create_dir_all(parent)?;
|
|
43
65
|
}
|
|
44
|
-
|
|
45
|
-
fs::write(path, format!("{content}\n"))?;
|
|
66
|
+
write_policy_structure_config(root, &generated_structure_config(paths))?;
|
|
46
67
|
Ok(true)
|
|
47
68
|
}
|
|
48
69
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
mod adapter_ios;
|
|
1
2
|
mod adapters;
|
|
2
3
|
mod checks;
|
|
3
4
|
mod classify;
|
|
@@ -11,10 +12,12 @@ use std::path::Path;
|
|
|
11
12
|
use crate::models::NaomeError;
|
|
12
13
|
|
|
13
14
|
use super::scanner::QualityContext;
|
|
14
|
-
use super::types::
|
|
15
|
+
use super::types::QualityViolation;
|
|
16
|
+
pub(crate) use adapters::detected_structure_adapter_ids;
|
|
15
17
|
use checks::run_structure_checks;
|
|
16
18
|
use classify::build_structure_model;
|
|
17
19
|
use config::read_structure_config;
|
|
20
|
+
pub(crate) use config::{read_policy_structure_config, write_policy_structure_config};
|
|
18
21
|
pub use config::{structure_config_relative_path, write_default_structure_config_if_missing};
|
|
19
22
|
pub use model::{RepositoryStructureConfig, StructurePathExplanation};
|
|
20
23
|
|
|
@@ -33,7 +36,7 @@ pub fn run_repository_structure_checks(
|
|
|
33
36
|
.find(|file| file.path == violation.path)
|
|
34
37
|
.is_some_and(|file| !file.symbols.is_empty())
|
|
35
38
|
});
|
|
36
|
-
if context.mode
|
|
39
|
+
if !context.mode.is_changed() {
|
|
37
40
|
for violation in &mut violations {
|
|
38
41
|
violation.baseline = baseline_fingerprints.contains(&violation.fingerprint);
|
|
39
42
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
use std::collections::BTreeSet;
|
|
1
|
+
use std::collections::{BTreeMap, BTreeSet};
|
|
2
2
|
|
|
3
3
|
use serde::{Deserialize, Serialize};
|
|
4
4
|
|
|
@@ -121,4 +121,11 @@ pub struct RepositoryStructureModel {
|
|
|
121
121
|
pub config: RepositoryStructureConfig,
|
|
122
122
|
pub paths: Vec<StructurePath>,
|
|
123
123
|
pub directories: Vec<StructureDirectory>,
|
|
124
|
+
pub module_paths: BTreeMap<String, Vec<String>>,
|
|
125
|
+
#[allow(dead_code)]
|
|
126
|
+
pub directory_paths: BTreeMap<String, Vec<String>>,
|
|
127
|
+
#[allow(dead_code)]
|
|
128
|
+
pub role_paths: BTreeMap<String, Vec<String>>,
|
|
129
|
+
pub lowercase_paths: BTreeMap<String, Vec<String>>,
|
|
130
|
+
pub changed_paths: BTreeSet<String>,
|
|
124
131
|
}
|
|
@@ -4,15 +4,47 @@ use crate::paths;
|
|
|
4
4
|
|
|
5
5
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
6
6
|
pub enum QualityMode {
|
|
7
|
-
|
|
7
|
+
ChangedFast,
|
|
8
|
+
PathScoped,
|
|
8
9
|
Report,
|
|
10
|
+
DeepReport,
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
impl QualityMode {
|
|
14
|
+
#[allow(non_upper_case_globals)]
|
|
15
|
+
pub const Changed: Self = Self::ChangedFast;
|
|
16
|
+
|
|
12
17
|
pub fn as_str(self) -> &'static str {
|
|
13
18
|
match self {
|
|
14
|
-
Self::
|
|
19
|
+
Self::ChangedFast => "changed",
|
|
20
|
+
Self::PathScoped => "path",
|
|
15
21
|
Self::Report => "report",
|
|
22
|
+
Self::DeepReport => "deep-report",
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
pub fn is_changed(self) -> bool {
|
|
27
|
+
matches!(self, Self::ChangedFast | Self::PathScoped)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
pub fn is_deep(self) -> bool {
|
|
31
|
+
self == Self::DeepReport
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
36
|
+
pub enum QualityInitMode {
|
|
37
|
+
SeedOnly,
|
|
38
|
+
Baseline,
|
|
39
|
+
DeepBaseline,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
impl QualityInitMode {
|
|
43
|
+
pub fn as_str(self) -> &'static str {
|
|
44
|
+
match self {
|
|
45
|
+
Self::SeedOnly => "init",
|
|
46
|
+
Self::Baseline => "baseline",
|
|
47
|
+
Self::DeepBaseline => "deep-baseline",
|
|
16
48
|
}
|
|
17
49
|
}
|
|
18
50
|
}
|
|
@@ -187,9 +219,14 @@ pub struct QualityReport {
|
|
|
187
219
|
#[serde(rename_all = "camelCase")]
|
|
188
220
|
pub struct QualitySummary {
|
|
189
221
|
pub scanned_files: usize,
|
|
222
|
+
pub scanned_path_count: usize,
|
|
190
223
|
pub violation_count: usize,
|
|
191
224
|
pub baseline_violation_count: usize,
|
|
192
225
|
pub blocking_violation_count: usize,
|
|
226
|
+
pub truncated: bool,
|
|
227
|
+
pub reason_codes: Vec<String>,
|
|
228
|
+
pub cache_hits: usize,
|
|
229
|
+
pub cache_misses: usize,
|
|
193
230
|
}
|
|
194
231
|
|
|
195
232
|
#[derive(Debug, Clone, Serialize)]
|
|
@@ -211,15 +248,34 @@ pub struct QualityViolation {
|
|
|
211
248
|
#[serde(rename_all = "camelCase")]
|
|
212
249
|
pub struct QualityInitResult {
|
|
213
250
|
pub schema: String,
|
|
251
|
+
pub mode: String,
|
|
214
252
|
pub config_written: bool,
|
|
215
253
|
pub structure_config_written: bool,
|
|
216
254
|
pub baseline_written: bool,
|
|
255
|
+
pub baseline_pending: bool,
|
|
217
256
|
pub baseline_violations: usize,
|
|
218
257
|
pub config_path: String,
|
|
219
258
|
pub structure_config_path: String,
|
|
220
259
|
pub baseline_path: String,
|
|
221
260
|
}
|
|
222
261
|
|
|
262
|
+
#[derive(Debug, Clone, Serialize)]
|
|
263
|
+
#[serde(rename_all = "camelCase")]
|
|
264
|
+
pub struct QualityReconcileReport {
|
|
265
|
+
pub schema: String,
|
|
266
|
+
pub ok: bool,
|
|
267
|
+
pub stale: bool,
|
|
268
|
+
pub write: bool,
|
|
269
|
+
pub detected_quality_adapters: Vec<String>,
|
|
270
|
+
pub enabled_quality_adapters: Vec<String>,
|
|
271
|
+
pub missing_quality_adapters: Vec<String>,
|
|
272
|
+
pub detected_structure_adapters: Vec<String>,
|
|
273
|
+
pub enabled_structure_adapters: Vec<String>,
|
|
274
|
+
pub missing_structure_adapters: Vec<String>,
|
|
275
|
+
pub updated_paths: Vec<String>,
|
|
276
|
+
pub reason_codes: Vec<String>,
|
|
277
|
+
}
|
|
278
|
+
|
|
223
279
|
#[derive(Debug, Clone, Serialize)]
|
|
224
280
|
#[serde(rename_all = "camelCase")]
|
|
225
281
|
pub struct QualityCleanupPlan {
|
|
@@ -258,6 +314,7 @@ pub fn default_generated_paths() -> Vec<String> {
|
|
|
258
314
|
const DEFAULT_IGNORED_PATHS: &str = r#"
|
|
259
315
|
.git/**
|
|
260
316
|
.naome/archive/**
|
|
317
|
+
.naome/cache/**
|
|
261
318
|
.naome/task-state.json
|
|
262
319
|
.naome/task-journal.jsonl
|
|
263
320
|
.naome/repository-quality.json
|