@lamentis/naome 1.3.0 → 1.3.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.
Files changed (149) hide show
  1. package/Cargo.lock +2 -2
  2. package/README.md +11 -2
  3. package/bin/naome.js +62 -24
  4. package/crates/naome-cli/Cargo.toml +1 -1
  5. package/crates/naome-cli/src/context_commands.rs +47 -0
  6. package/crates/naome-cli/src/dispatcher.rs +6 -0
  7. package/crates/naome-cli/src/main.rs +43 -6
  8. package/crates/naome-cli/src/quality_commands.rs +31 -46
  9. package/crates/naome-cli/src/quality_output.rs +34 -0
  10. package/crates/naome-cli/src/quality_reconcile_command.rs +45 -0
  11. package/crates/naome-cli/src/repository_model_commands.rs +84 -0
  12. package/crates/naome-cli/src/task_commands.rs +62 -0
  13. package/crates/naome-cli/src/workflow_commands.rs +100 -3
  14. package/crates/naome-core/Cargo.toml +1 -1
  15. package/crates/naome-core/src/context/helpers.rs +75 -0
  16. package/crates/naome-core/src/context/select.rs +134 -0
  17. package/crates/naome-core/src/context/types.rs +43 -0
  18. package/crates/naome-core/src/context.rs +6 -0
  19. package/crates/naome-core/src/decision/states.rs +1 -1
  20. package/crates/naome-core/src/decision.rs +4 -1
  21. package/crates/naome-core/src/install_plan.rs +18 -0
  22. package/crates/naome-core/src/intent/resolver_catalog/active.rs +38 -0
  23. package/crates/naome-core/src/intent/resolver_catalog/baseline.rs +44 -0
  24. package/crates/naome-core/src/intent/resolver_catalog/completed.rs +56 -0
  25. package/crates/naome-core/src/intent/resolver_catalog/dirty.rs +32 -0
  26. package/crates/naome-core/src/intent/resolver_catalog/ready.rs +32 -0
  27. package/crates/naome-core/src/intent/resolver_catalog/system.rs +20 -0
  28. package/crates/naome-core/src/intent/resolver_catalog.rs +12 -166
  29. package/crates/naome-core/src/journal.rs +2 -7
  30. package/crates/naome-core/src/lib.rs +33 -10
  31. package/crates/naome-core/src/quality/adapter_ios.rs +131 -0
  32. package/crates/naome-core/src/quality/adapter_support.rs +67 -0
  33. package/crates/naome-core/src/quality/adapters.rs +81 -18
  34. package/crates/naome-core/src/quality/cache.rs +7 -9
  35. package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +4 -7
  36. package/crates/naome-core/src/quality/config.rs +21 -3
  37. package/crates/naome-core/src/quality/mod.rs +138 -7
  38. package/crates/naome-core/src/quality/reconcile.rs +138 -0
  39. package/crates/naome-core/src/quality/reconcile_anchors.rs +64 -0
  40. package/crates/naome-core/src/quality/scanner/analysis.rs +20 -5
  41. package/crates/naome-core/src/quality/scanner.rs +62 -17
  42. package/crates/naome-core/src/quality/semantic/checks.rs +17 -0
  43. package/crates/naome-core/src/quality/semantic/route.rs +1 -1
  44. package/crates/naome-core/src/quality/structure/adapter_ios.rs +149 -0
  45. package/crates/naome-core/src/quality/structure/adapters.rs +60 -42
  46. package/crates/naome-core/src/quality/structure/checks/directory.rs +6 -4
  47. package/crates/naome-core/src/quality/structure/classify/roles.rs +51 -5
  48. package/crates/naome-core/src/quality/structure/config.rs +24 -3
  49. package/crates/naome-core/src/quality/structure/mod.rs +3 -0
  50. package/crates/naome-core/src/quality/types.rs +20 -1
  51. package/crates/naome-core/src/repository_model/detect.rs +188 -0
  52. package/crates/naome-core/src/repository_model/explain.rs +121 -0
  53. package/crates/naome-core/src/repository_model/path_scan.rs +67 -0
  54. package/crates/naome-core/src/repository_model/path_support.rs +59 -0
  55. package/crates/naome-core/src/repository_model/types.rs +152 -0
  56. package/crates/naome-core/src/repository_model/world.rs +48 -0
  57. package/crates/naome-core/src/repository_model/world_adapters.rs +145 -0
  58. package/crates/naome-core/src/repository_model/world_path_facts.rs +55 -0
  59. package/crates/naome-core/src/repository_model/world_paths.rs +168 -0
  60. package/crates/naome-core/src/repository_model.rs +164 -0
  61. package/crates/naome-core/src/route/builtin_checks.rs +40 -1
  62. package/crates/naome-core/src/task_ledger/import.rs +142 -0
  63. package/crates/naome-core/src/task_ledger/model.rs +13 -0
  64. package/crates/naome-core/src/task_ledger/proof_record.rs +52 -0
  65. package/crates/naome-core/src/task_ledger/read.rs +118 -0
  66. package/crates/naome-core/src/task_ledger/render.rs +55 -0
  67. package/crates/naome-core/src/task_ledger/write.rs +38 -0
  68. package/crates/naome-core/src/task_ledger.rs +48 -0
  69. package/crates/naome-core/src/task_state/api.rs +4 -2
  70. package/crates/naome-core/src/task_state/completed_refresh.rs +5 -16
  71. package/crates/naome-core/src/task_state/diff.rs +2 -2
  72. package/crates/naome-core/src/task_state/evidence.rs +8 -3
  73. package/crates/naome-core/src/task_state/mod.rs +1 -1
  74. package/crates/naome-core/src/task_state/progress.rs +13 -0
  75. package/crates/naome-core/src/task_state/proof_model.rs +8 -8
  76. package/crates/naome-core/src/task_state/repair.rs +2 -2
  77. package/crates/naome-core/src/task_state/task_diff_api.rs +9 -18
  78. package/crates/naome-core/src/task_state/types.rs +24 -0
  79. package/crates/naome-core/src/verification.rs +29 -18
  80. package/crates/naome-core/src/workflow/agent/capability.rs +194 -0
  81. package/crates/naome-core/src/workflow/agent/context_delta.rs +42 -0
  82. package/crates/naome-core/src/workflow/agent/decision.rs +32 -0
  83. package/crates/naome-core/src/workflow/agent/execution.rs +80 -0
  84. package/crates/naome-core/src/workflow/agent/proof.rs +24 -0
  85. package/crates/naome-core/src/workflow/agent/support.rs +58 -0
  86. package/crates/naome-core/src/workflow/agent/watchdog.rs +47 -0
  87. package/crates/naome-core/src/workflow/agent.rs +34 -0
  88. package/crates/naome-core/src/workflow/agent_types.rs +105 -0
  89. package/crates/naome-core/src/workflow/doctor.rs +39 -0
  90. package/crates/naome-core/src/workflow/mod.rs +11 -0
  91. package/crates/naome-core/src/workflow/output.rs +8 -2
  92. package/crates/naome-core/src/workflow/phase_inference.rs +1 -1
  93. package/crates/naome-core/tests/context.rs +99 -0
  94. package/crates/naome-core/tests/harness_health.rs +33 -40
  95. package/crates/naome-core/tests/install_plan.rs +12 -0
  96. package/crates/naome-core/tests/quality.rs +178 -2
  97. package/crates/naome-core/tests/quality_performance.rs +39 -2
  98. package/crates/naome-core/tests/quality_structure_adapters.rs +39 -0
  99. package/crates/naome-core/tests/repo_support/mod.rs +7 -1
  100. package/crates/naome-core/tests/repo_support/verification_values.rs +148 -1
  101. package/crates/naome-core/tests/repository_model.rs +281 -0
  102. package/crates/naome-core/tests/route_user_diff.rs +49 -1
  103. package/crates/naome-core/tests/semantic_legacy.rs +72 -38
  104. package/crates/naome-core/tests/task_ledger.rs +328 -0
  105. package/crates/naome-core/tests/task_state.rs +34 -14
  106. package/crates/naome-core/tests/task_state_support/mod.rs +2 -1
  107. package/crates/naome-core/tests/task_state_support/states.rs +28 -11
  108. package/crates/naome-core/tests/verification.rs +14 -39
  109. package/crates/naome-core/tests/verification_contract.rs +6 -52
  110. package/crates/naome-core/tests/workflow_agent.rs +233 -0
  111. package/crates/naome-core/tests/workflow_agent_support/mod.rs +159 -0
  112. package/crates/naome-core/tests/workflow_doctor.rs +21 -0
  113. package/crates/naome-core/tests/workflow_integrity.rs +2 -20
  114. package/crates/naome-core/tests/workflow_support/mod.rs +59 -20
  115. package/installer/codex-hooks.js +121 -0
  116. package/installer/context.js +10 -0
  117. package/installer/filesystem.js +4 -0
  118. package/installer/flows.js +8 -4
  119. package/installer/harness-files.js +6 -0
  120. package/installer/install-plan.js +4 -0
  121. package/installer/main.js +1 -1
  122. package/installer/native.js +1 -1
  123. package/native/darwin-arm64/naome +0 -0
  124. package/native/linux-x64/naome +0 -0
  125. package/package.json +1 -1
  126. package/templates/naome-root/.codex/config.toml +2 -0
  127. package/templates/naome-root/.codex/hooks.json +70 -0
  128. package/templates/naome-root/.naome/bin/check-harness-health.js +8 -6
  129. package/templates/naome-root/.naome/bin/check-task-state.js +12 -7
  130. package/templates/naome-root/.naome/bin/codex-hook-io.js +122 -0
  131. package/templates/naome-root/.naome/bin/codex-hook-policy.js +180 -0
  132. package/templates/naome-root/.naome/bin/codex-hook-runtime.js +174 -0
  133. package/templates/naome-root/.naome/bin/codex-hook.js +6 -0
  134. package/templates/naome-root/.naome/bin/naome.js +35 -4
  135. package/templates/naome-root/.naome/manifest.json +12 -6
  136. package/templates/naome-root/.naome/repository-model.json +6 -0
  137. package/templates/naome-root/.naome/repository-quality.json +3 -1
  138. package/templates/naome-root/.naome/verification.json +15 -1
  139. package/templates/naome-root/AGENTS.md +38 -83
  140. package/templates/naome-root/docs/naome/agent-workflow.md +54 -18
  141. package/templates/naome-root/docs/naome/codex-hooks.md +82 -0
  142. package/templates/naome-root/docs/naome/context-economy.md +73 -0
  143. package/templates/naome-root/docs/naome/first-run.md +25 -14
  144. package/templates/naome-root/docs/naome/index.md +18 -10
  145. package/templates/naome-root/docs/naome/repository-model.md +92 -0
  146. package/templates/naome-root/docs/naome/repository-quality.md +47 -7
  147. package/templates/naome-root/docs/naome/repository-structure.md +10 -3
  148. package/templates/naome-root/docs/naome/task-ledger.md +71 -0
  149. package/templates/naome-root/docs/naome/testing.md +16 -3
@@ -0,0 +1,64 @@
1
+ pub(super) fn changed_paths_introduce_adapter(adapter: &str, changed_paths: &[String]) -> bool {
2
+ match adapter {
3
+ "rust" => changed_paths
4
+ .iter()
5
+ .any(|path| path.ends_with("Cargo.toml")),
6
+ "javascript-typescript" => changed_paths.iter().any(|path| {
7
+ path.ends_with("package.json")
8
+ || path.ends_with("tsconfig.json")
9
+ || path.ends_with("jsconfig.json")
10
+ }),
11
+ "swift" => changed_paths
12
+ .iter()
13
+ .any(|path| path.ends_with("Package.swift") || path.ends_with(".swift")),
14
+ "xcode" => changed_paths
15
+ .iter()
16
+ .any(|path| path.contains(".xcodeproj/") || path.contains(".xcworkspace/")),
17
+ "xctest" => changed_paths.iter().any(|path| {
18
+ let lower = path.to_ascii_lowercase();
19
+ lower.contains("/tests/")
20
+ && (lower.ends_with("tests.swift") || lower.ends_with("uitests.swift"))
21
+ }),
22
+ "swiftui" => changed_paths.iter().any(|path| {
23
+ path.ends_with("App.swift")
24
+ || path.ends_with("View.swift")
25
+ || path.contains("Preview Content/")
26
+ }),
27
+ "ios-app-structure" => changed_paths.iter().any(|path| {
28
+ path.ends_with("AppDelegate.swift")
29
+ || path.ends_with("SceneDelegate.swift")
30
+ || path.ends_with("Info.plist")
31
+ || path.contains(".xcassets/")
32
+ || path.ends_with(".entitlements")
33
+ }),
34
+ "swift-package" => changed_paths.iter().any(|path| {
35
+ path.ends_with("Package.swift")
36
+ || path.starts_with("Sources/")
37
+ || path.contains("/Sources/")
38
+ || path.starts_with("Tests/")
39
+ || path.contains("/Tests/")
40
+ }),
41
+ "ios-resources" => changed_paths.iter().any(|path| {
42
+ path.contains(".xcassets/")
43
+ || path.ends_with(".strings")
44
+ || path.ends_with(".stringsdict")
45
+ || path.ends_with(".plist")
46
+ || path.ends_with(".entitlements")
47
+ || path.ends_with(".storyboard")
48
+ || path.ends_with(".xib")
49
+ }),
50
+ "generated-ios" => changed_paths.iter().any(generated_ios_path),
51
+ _ => false,
52
+ }
53
+ }
54
+
55
+ fn generated_ios_path(path: &String) -> bool {
56
+ let lower = path.to_ascii_lowercase();
57
+ lower.contains("/generated/")
58
+ || lower.contains("/swiftgen/")
59
+ || lower.contains("/sourcery/")
60
+ || lower.ends_with("r.generated.swift")
61
+ || lower.ends_with(".generated.swift")
62
+ || lower.ends_with(".pb.swift")
63
+ || lower.ends_with(".grpc.swift")
64
+ }
@@ -28,11 +28,7 @@ pub(super) fn analyze_repo_file(
28
28
  }
29
29
  }
30
30
  let content = fs::read_to_string(&full_path).ok()?;
31
- let file = analyze_file(
32
- path,
33
- &content,
34
- added_lines.get(path).copied().unwrap_or(0),
35
- );
31
+ let file = analyze_file(path, &content, added_lines.get(path).copied().unwrap_or(0));
36
32
  let hash = if allow_cache {
37
33
  blob_hashes
38
34
  .get(path)
@@ -110,6 +106,8 @@ fn symbol_start(trimmed: &str) -> Option<(String, String)> {
110
106
  ("function", "def "),
111
107
  ("function", "fn "),
112
108
  ("function", "pub fn "),
109
+ ("function", "pub(crate) fn "),
110
+ ("function", "pub(super) fn "),
113
111
  ("function", "func "),
114
112
  ("class", "class "),
115
113
  ("struct", "struct "),
@@ -123,6 +121,17 @@ fn symbol_start(trimmed: &str) -> Option<(String, String)> {
123
121
  return Some((kind.to_string(), symbol_name(rest)));
124
122
  }
125
123
  }
124
+ for visibility in ["", "pub ", "pub(crate) ", "pub(super) "] {
125
+ for (kind, keyword) in [("const", "const "), ("static", "static ")] {
126
+ let prefix = format!("{visibility}{keyword}");
127
+ if let Some(rest) = trimmed.strip_prefix(&prefix) {
128
+ if !looks_like_typed_rust_item(rest) {
129
+ continue;
130
+ }
131
+ return Some((kind.to_string(), symbol_name(rest)));
132
+ }
133
+ }
134
+ }
126
135
  for prefix in ["const ", "let ", "export const ", "export let "] {
127
136
  if let Some(rest) = trimmed.strip_prefix(prefix) {
128
137
  if trimmed.contains("=>") || trimmed.contains("function") || trimmed.contains("React.")
@@ -134,6 +143,12 @@ fn symbol_start(trimmed: &str) -> Option<(String, String)> {
134
143
  None
135
144
  }
136
145
 
146
+ fn looks_like_typed_rust_item(rest: &str) -> bool {
147
+ rest.split('=').next().is_some_and(|before_assignment| {
148
+ before_assignment.contains(':') && !before_assignment.trim().is_empty()
149
+ })
150
+ }
151
+
137
152
  fn symbol_name(rest: &str) -> String {
138
153
  rest.chars()
139
154
  .take_while(|character| character.is_ascii_alphanumeric() || *character == '_')
@@ -93,12 +93,36 @@ pub fn scan_repository(
93
93
  config: RepositoryQualityConfig,
94
94
  ) -> Result<QualityContext, NaomeError> {
95
95
  let changed_paths = git::changed_paths(root)?;
96
+ scan_repository_with_targets(root, mode, config, changed_paths, true)
97
+ }
98
+
99
+ pub fn scan_repository_paths(
100
+ root: &Path,
101
+ config: RepositoryQualityConfig,
102
+ paths: &[String],
103
+ ) -> Result<QualityContext, NaomeError> {
104
+ let mut target_paths = paths
105
+ .iter()
106
+ .map(|path| normalize_target_path(path))
107
+ .collect::<Result<Vec<_>, _>>()?;
108
+ target_paths.sort();
109
+ target_paths.dedup();
110
+ scan_repository_with_targets(root, QualityMode::PathScoped, config, target_paths, false)
111
+ }
112
+
113
+ fn scan_repository_with_targets(
114
+ root: &Path,
115
+ mode: QualityMode,
116
+ config: RepositoryQualityConfig,
117
+ changed_paths: Vec<String>,
118
+ include_comparison_files: bool,
119
+ ) -> Result<QualityContext, NaomeError> {
96
120
  let target_paths = changed_paths.iter().cloned().collect::<HashSet<_>>();
97
121
  let mut whole_repo_paths = collect_repo_paths(root)?;
98
122
  whole_repo_paths.sort();
99
123
  whole_repo_paths.dedup();
100
124
  let scan_paths = match mode {
101
- QualityMode::ChangedFast => changed_paths.clone(),
125
+ QualityMode::ChangedFast | QualityMode::PathScoped => changed_paths.clone(),
102
126
  QualityMode::Report | QualityMode::DeepReport => whole_repo_paths.clone(),
103
127
  };
104
128
  let added_lines = if mode.is_changed() {
@@ -121,18 +145,22 @@ pub fn scan_repository(
121
145
  &ignore_patterns,
122
146
  &mut state,
123
147
  );
124
- let comparison_files = analyze_comparison_paths(
125
- root,
126
- mode,
127
- &whole_repo_paths,
128
- &target_paths,
129
- &comparison_added_lines,
130
- &blob_hashes,
131
- &cache,
132
- &ignore_patterns,
133
- files.len(),
134
- &mut state,
135
- );
148
+ let comparison_files = if include_comparison_files {
149
+ analyze_comparison_paths(
150
+ root,
151
+ mode,
152
+ &whole_repo_paths,
153
+ &target_paths,
154
+ &comparison_added_lines,
155
+ &blob_hashes,
156
+ &cache,
157
+ &ignore_patterns,
158
+ files.len(),
159
+ &mut state,
160
+ )
161
+ } else {
162
+ Vec::new()
163
+ };
136
164
 
137
165
  files.sort_by(|left, right| left.path.cmp(&right.path));
138
166
  Ok(QualityContext {
@@ -150,6 +178,17 @@ pub fn scan_repository(
150
178
  })
151
179
  }
152
180
 
181
+ fn normalize_target_path(path: &str) -> Result<String, NaomeError> {
182
+ let normalized = path.trim_start_matches("./").replace('\\', "/");
183
+ let has_parent_segment = normalized.split('/').any(|segment| segment == "..");
184
+ if normalized.is_empty() || normalized.starts_with('/') || has_parent_segment {
185
+ return Err(NaomeError::new(format!(
186
+ "quality path must be a repository-relative path: {path}"
187
+ )));
188
+ }
189
+ Ok(normalized)
190
+ }
191
+
153
192
  #[derive(Default)]
154
193
  struct ScanState {
155
194
  cache_hits: usize,
@@ -196,9 +235,14 @@ fn analyze_primary_paths(
196
235
  state.truncate("max_file_bytes");
197
236
  continue;
198
237
  }
199
- if let Some((file, cache_hit)) =
200
- analyze_repo_file(root, path, added_lines, blob_hashes, cache, !mode.is_changed())
201
- {
238
+ if let Some((file, cache_hit)) = analyze_repo_file(
239
+ root,
240
+ path,
241
+ added_lines,
242
+ blob_hashes,
243
+ cache,
244
+ !mode.is_changed(),
245
+ ) {
202
246
  state.record_cache_result(cache_hit);
203
247
  files.push(file);
204
248
  }
@@ -258,6 +302,7 @@ pub fn stable_fingerprint(parts: &[&str]) -> String {
258
302
  fn max_scanned_files(mode: QualityMode) -> usize {
259
303
  match mode {
260
304
  QualityMode::ChangedFast => 2_000,
305
+ QualityMode::PathScoped => 128,
261
306
  QualityMode::Report => 5_000,
262
307
  QualityMode::DeepReport => usize::MAX,
263
308
  }
@@ -265,7 +310,7 @@ fn max_scanned_files(mode: QualityMode) -> usize {
265
310
 
266
311
  fn max_file_bytes(mode: QualityMode) -> u64 {
267
312
  match mode {
268
- QualityMode::ChangedFast => 512 * 1024,
313
+ QualityMode::ChangedFast | QualityMode::PathScoped => 512 * 1024,
269
314
  QualityMode::Report => 1024 * 1024,
270
315
  QualityMode::DeepReport => 8 * 1024 * 1024,
271
316
  }
@@ -112,6 +112,9 @@ fn has_legacy_fixture_signal(keys: &BTreeSet<String>) -> bool {
112
112
  }
113
113
 
114
114
  fn is_shared_fixture_factory(candidate: &ObjectCandidate) -> bool {
115
+ if is_shared_fixture_path(&candidate.path) {
116
+ return true;
117
+ }
115
118
  let Some(symbol) = &candidate.symbol else {
116
119
  return false;
117
120
  };
@@ -123,6 +126,20 @@ fn is_shared_fixture_factory(candidate: &ObjectCandidate) -> bool {
123
126
  .any(|marker| normalized.contains(marker))
124
127
  }
125
128
 
129
+ fn is_shared_fixture_path(path: &str) -> bool {
130
+ let normalized = path.replace('\\', "/").to_ascii_lowercase();
131
+ [
132
+ "/fixtures/",
133
+ "/fixture/",
134
+ "/support/",
135
+ "_support/",
136
+ "-support/",
137
+ "/test-support.",
138
+ ]
139
+ .iter()
140
+ .any(|marker| normalized.contains(marker))
141
+ }
142
+
126
143
  fn group_applies_to_mode(context: &QualityContext, group: &[&ObjectCandidate]) -> bool {
127
144
  if context.mode.is_deep() {
128
145
  return true;
@@ -4,7 +4,7 @@ use crate::quality::types::QualityMode;
4
4
 
5
5
  pub(super) fn finding_mode(context: &QualityContext) -> &'static str {
6
6
  match context.mode {
7
- QualityMode::ChangedFast => "changed-blocking",
7
+ QualityMode::ChangedFast | QualityMode::PathScoped => "changed-blocking",
8
8
  QualityMode::Report => "report",
9
9
  QualityMode::DeepReport => "deep-report",
10
10
  }
@@ -0,0 +1,149 @@
1
+ use crate::quality::adapter_support::{
2
+ detects_generated_ios_project, detects_ios_app_structure_project,
3
+ detects_ios_resources_project, detects_swift_package_project, detects_swift_project,
4
+ detects_swiftui_project, detects_xcode_project, detects_xctest_project,
5
+ };
6
+
7
+ use super::adapters::StructureAdapter;
8
+
9
+ pub(super) fn adapters() -> Vec<StructureAdapter> {
10
+ let mut adapters = Vec::new();
11
+ adapters.extend_from_slice(IOS_CORE_STRUCTURE_ADAPTERS);
12
+ adapters.extend_from_slice(IOS_APP_STRUCTURE_ADAPTERS);
13
+ adapters
14
+ }
15
+
16
+ const IOS_CORE_STRUCTURE_ADAPTERS: &[StructureAdapter] = &[
17
+ StructureAdapter {
18
+ id: "swift",
19
+ detect: detects_swift_project,
20
+ source_roots: &[
21
+ "Sources/**",
22
+ "**/Sources/**",
23
+ "Source/**",
24
+ "App/**",
25
+ "Features/**",
26
+ "Modules/**",
27
+ "Shared/**",
28
+ ],
29
+ test_roots: &[],
30
+ generated_roots: &[],
31
+ artifact_roots: &[],
32
+ module_roots: &[
33
+ "Sources/*/**",
34
+ "**/Sources/*/**",
35
+ "App/**",
36
+ "Features/*/**",
37
+ "Modules/*/**",
38
+ ],
39
+ allowed_root_files: &[],
40
+ },
41
+ StructureAdapter {
42
+ id: "xcode",
43
+ detect: detects_xcode_project,
44
+ source_roots: &[],
45
+ test_roots: &[],
46
+ generated_roots: &[],
47
+ artifact_roots: &[
48
+ "DerivedData/**",
49
+ "**/DerivedData/**",
50
+ "Build/**",
51
+ "**/Build/**",
52
+ "**/*.xcarchive/**",
53
+ ],
54
+ module_roots: &[],
55
+ allowed_root_files: &["*.xcodeproj", "*.xcworkspace"],
56
+ },
57
+ StructureAdapter {
58
+ id: "xctest",
59
+ detect: detects_xctest_project,
60
+ source_roots: &[],
61
+ test_roots: &[
62
+ "Tests/**",
63
+ "**/Tests/**",
64
+ "*Tests/**",
65
+ "**/*Tests/**",
66
+ "*UITests/**",
67
+ "**/*UITests/**",
68
+ ],
69
+ generated_roots: &[],
70
+ artifact_roots: &[],
71
+ module_roots: &[],
72
+ allowed_root_files: &[],
73
+ },
74
+ ];
75
+
76
+ const IOS_APP_STRUCTURE_ADAPTERS: &[StructureAdapter] = &[
77
+ StructureAdapter {
78
+ id: "swiftui",
79
+ detect: detects_swiftui_project,
80
+ source_roots: &[
81
+ "Views/**",
82
+ "**/Views/**",
83
+ "Preview Content/**",
84
+ "**/Preview Content/**",
85
+ ],
86
+ test_roots: &[],
87
+ generated_roots: &[],
88
+ artifact_roots: &[],
89
+ module_roots: &["Views/*/**", "**/Views/*/**"],
90
+ allowed_root_files: &[],
91
+ },
92
+ StructureAdapter {
93
+ id: "ios-app-structure",
94
+ detect: detects_ios_app_structure_project,
95
+ source_roots: &[
96
+ "App/**",
97
+ "Application/**",
98
+ "Features/**",
99
+ "Modules/**",
100
+ "Shared/**",
101
+ ],
102
+ test_roots: &[],
103
+ generated_roots: &[],
104
+ artifact_roots: &[],
105
+ module_roots: &["Features/*/**", "Modules/*/**", "App/**"],
106
+ allowed_root_files: &["Info.plist", "*.entitlements", "Podfile", "Cartfile"],
107
+ },
108
+ StructureAdapter {
109
+ id: "swift-package",
110
+ detect: detects_swift_package_project,
111
+ source_roots: &["Sources/**", "**/Sources/**"],
112
+ test_roots: &["Tests/**", "**/Tests/**"],
113
+ generated_roots: &[],
114
+ artifact_roots: &[],
115
+ module_roots: &["Sources/*/**", "**/Sources/*/**"],
116
+ allowed_root_files: &["Package.swift", "Package.resolved"],
117
+ },
118
+ StructureAdapter {
119
+ id: "ios-resources",
120
+ detect: detects_ios_resources_project,
121
+ source_roots: &["Resources/**", "**/Resources/**"],
122
+ test_roots: &[],
123
+ generated_roots: &[],
124
+ artifact_roots: &[],
125
+ module_roots: &["Resources/**", "**/Resources/**"],
126
+ allowed_root_files: &["Info.plist", "*.entitlements"],
127
+ },
128
+ StructureAdapter {
129
+ id: "generated-ios",
130
+ detect: detects_generated_ios_project,
131
+ source_roots: &[],
132
+ test_roots: &[],
133
+ generated_roots: &[
134
+ "Generated/**",
135
+ "**/Generated/**",
136
+ "SwiftGen/**",
137
+ "**/SwiftGen/**",
138
+ "Sourcery/**",
139
+ "**/Sourcery/**",
140
+ "**/*.generated.swift",
141
+ "**/*.pb.swift",
142
+ "**/*.grpc.swift",
143
+ "**/R.generated.swift",
144
+ ],
145
+ artifact_roots: &[],
146
+ module_roots: &[],
147
+ allowed_root_files: &[],
148
+ },
149
+ ];
@@ -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
- struct StructureAdapter {
13
- id: &'static str,
14
- detect: fn(&RepoSignals<'_>) -> bool,
15
- source_roots: &'static [&'static str],
16
- test_roots: &'static [&'static str],
17
- module_roots: &'static [&'static str],
18
- allowed_root_files: &'static [&'static str],
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
- detected_ids(paths, registry())
36
+ let registry = registry();
37
+ detected_ids(paths, &registry)
33
38
  }
34
39
 
35
40
  pub fn apply_structure_adapters(
36
41
  mut config: RepositoryStructureConfig,
37
42
  ) -> Result<RepositoryStructureConfig, NaomeError> {
38
- validate_structure_adapter_ids(&config.enabled_adapters)?;
43
+ let registry = registry();
44
+ validate_ids(&config.enabled_adapters, &registry, CONFIG_PATH)?;
39
45
  for adapter_id in config.enabled_adapters.clone() {
40
- let adapter = find_adapter_by_id(registry(), &adapter_id, CONFIG_PATH)?;
46
+ let adapter = find_adapter_by_id(&registry, &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
- validate_ids(ids, registry(), CONFIG_PATH)
58
+ let registry = registry();
59
+ validate_ids(ids, &registry, CONFIG_PATH)
51
60
  }
52
61
 
53
- fn registry() -> &'static [StructureAdapter] {
54
- &[
55
- StructureAdapter {
56
- id: "rust",
57
- detect: detects_rust_project,
58
- source_roots: &["src/**", "crates/*/src/**", "**/crates/*/src/**"],
59
- test_roots: &["tests/**", "crates/*/tests/**", "**/crates/*/tests/**"],
60
- module_roots: &["src/**", "crates/*/src/**", "**/crates/*/src/**"],
61
- allowed_root_files: &["Cargo.toml", "Cargo.lock"],
62
- },
63
- StructureAdapter {
64
- id: "javascript-typescript",
65
- detect: detects_javascript_typescript_project,
66
- source_roots: &[
67
- "src/**",
68
- "app/**",
69
- "pages/**",
70
- "components/**",
71
- "packages/*/src/**",
72
- "**/packages/*/src/**",
73
- ],
74
- test_roots: &["test/**", "tests/**", "__tests__/**", "packages/*/tests/**"],
75
- module_roots: &["src/**", "app/**", "packages/*/src/**"],
76
- allowed_root_files: &[
77
- "package.json",
78
- "tsconfig.json",
79
- "vite.config.ts",
80
- "next.config.js",
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
  }
@@ -83,10 +83,12 @@ pub(super) fn case_collisions(
83
83
  mode: QualityMode,
84
84
  violations: &mut Vec<QualityViolation>,
85
85
  ) {
86
- for group in model.lowercase_paths.values().filter(|group| group.len() > 1) {
87
- let changed_group = group.iter().any(|path| {
88
- model.changed_paths.contains(path)
89
- });
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));
90
92
  if mode.is_changed() && !changed_group {
91
93
  continue;
92
94
  }