@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.
Files changed (155) hide show
  1. package/Cargo.lock +2 -2
  2. package/README.md +117 -47
  3. package/bin/naome.js +65 -12
  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 +12 -2
  7. package/crates/naome-cli/src/main.rs +78 -29
  8. package/crates/naome-cli/src/quality_commands.rs +238 -34
  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 +120 -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/git.rs +4 -2
  22. package/crates/naome-core/src/install_plan.rs +20 -0
  23. package/crates/naome-core/src/journal.rs +2 -7
  24. package/crates/naome-core/src/lib.rs +35 -8
  25. package/crates/naome-core/src/quality/adapter_ios.rs +131 -0
  26. package/crates/naome-core/src/quality/adapter_support.rs +67 -0
  27. package/crates/naome-core/src/quality/adapters.rs +81 -18
  28. package/crates/naome-core/src/quality/baseline.rs +8 -0
  29. package/crates/naome-core/src/quality/cache.rs +151 -0
  30. package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +19 -8
  31. package/crates/naome-core/src/quality/checks/near_duplicates.rs +4 -2
  32. package/crates/naome-core/src/quality/checks.rs +7 -8
  33. package/crates/naome-core/src/quality/cleanup.rs +36 -3
  34. package/crates/naome-core/src/quality/config.rs +21 -3
  35. package/crates/naome-core/src/quality/mod.rs +189 -10
  36. package/crates/naome-core/src/quality/reconcile.rs +138 -0
  37. package/crates/naome-core/src/quality/reconcile_anchors.rs +64 -0
  38. package/crates/naome-core/src/quality/scanner/analysis/normalize.rs +78 -0
  39. package/crates/naome-core/src/quality/scanner/analysis.rs +175 -0
  40. package/crates/naome-core/src/quality/scanner/repo_paths.rs +39 -3
  41. package/crates/naome-core/src/quality/scanner.rs +235 -217
  42. package/crates/naome-core/src/quality/semantic/checks.rs +151 -0
  43. package/crates/naome-core/src/quality/semantic/extract.rs +158 -0
  44. package/crates/naome-core/src/quality/semantic/model.rs +85 -0
  45. package/crates/naome-core/src/quality/semantic/route.rs +52 -0
  46. package/crates/naome-core/src/quality/semantic.rs +68 -0
  47. package/crates/naome-core/src/quality/structure/adapter_ios.rs +149 -0
  48. package/crates/naome-core/src/quality/structure/adapters.rs +60 -42
  49. package/crates/naome-core/src/quality/structure/checks/directory.rs +13 -21
  50. package/crates/naome-core/src/quality/structure/checks.rs +1 -1
  51. package/crates/naome-core/src/quality/structure/classify/roles.rs +51 -5
  52. package/crates/naome-core/src/quality/structure/classify.rs +52 -0
  53. package/crates/naome-core/src/quality/structure/config.rs +24 -3
  54. package/crates/naome-core/src/quality/structure/mod.rs +5 -2
  55. package/crates/naome-core/src/quality/structure/model.rs +8 -1
  56. package/crates/naome-core/src/quality/types.rs +59 -2
  57. package/crates/naome-core/src/repository_model/detect.rs +188 -0
  58. package/crates/naome-core/src/repository_model/explain.rs +121 -0
  59. package/crates/naome-core/src/repository_model/path_scan.rs +67 -0
  60. package/crates/naome-core/src/repository_model/path_support.rs +59 -0
  61. package/crates/naome-core/src/repository_model/types.rs +152 -0
  62. package/crates/naome-core/src/repository_model/world.rs +48 -0
  63. package/crates/naome-core/src/repository_model/world_adapters.rs +145 -0
  64. package/crates/naome-core/src/repository_model/world_path_facts.rs +55 -0
  65. package/crates/naome-core/src/repository_model/world_paths.rs +168 -0
  66. package/crates/naome-core/src/repository_model.rs +164 -0
  67. package/crates/naome-core/src/route/builtin_checks.rs +41 -16
  68. package/crates/naome-core/src/task_ledger/import.rs +142 -0
  69. package/crates/naome-core/src/task_ledger/model.rs +13 -0
  70. package/crates/naome-core/src/task_ledger/proof_record.rs +52 -0
  71. package/crates/naome-core/src/task_ledger/read.rs +118 -0
  72. package/crates/naome-core/src/task_ledger/render.rs +55 -0
  73. package/crates/naome-core/src/task_ledger/write.rs +38 -0
  74. package/crates/naome-core/src/task_ledger.rs +48 -0
  75. package/crates/naome-core/src/task_state/api.rs +4 -2
  76. package/crates/naome-core/src/task_state/completed_refresh.rs +5 -16
  77. package/crates/naome-core/src/task_state/diff.rs +2 -2
  78. package/crates/naome-core/src/task_state/evidence.rs +8 -3
  79. package/crates/naome-core/src/task_state/mod.rs +1 -1
  80. package/crates/naome-core/src/task_state/progress.rs +13 -0
  81. package/crates/naome-core/src/task_state/proof_model.rs +8 -8
  82. package/crates/naome-core/src/task_state/repair.rs +2 -2
  83. package/crates/naome-core/src/task_state/task_diff_api.rs +9 -18
  84. package/crates/naome-core/src/task_state/types.rs +24 -0
  85. package/crates/naome-core/src/verification.rs +29 -18
  86. package/crates/naome-core/src/workflow/agent/capability.rs +194 -0
  87. package/crates/naome-core/src/workflow/agent/context_delta.rs +42 -0
  88. package/crates/naome-core/src/workflow/agent/decision.rs +32 -0
  89. package/crates/naome-core/src/workflow/agent/execution.rs +80 -0
  90. package/crates/naome-core/src/workflow/agent/proof.rs +24 -0
  91. package/crates/naome-core/src/workflow/agent/support.rs +58 -0
  92. package/crates/naome-core/src/workflow/agent/watchdog.rs +47 -0
  93. package/crates/naome-core/src/workflow/agent.rs +34 -0
  94. package/crates/naome-core/src/workflow/agent_types.rs +105 -0
  95. package/crates/naome-core/src/workflow/doctor.rs +183 -0
  96. package/crates/naome-core/src/workflow/mod.rs +13 -0
  97. package/crates/naome-core/src/workflow/mutation.rs +1 -2
  98. package/crates/naome-core/src/workflow/output.rs +8 -2
  99. package/crates/naome-core/src/workflow/phase_inference.rs +1 -1
  100. package/crates/naome-core/tests/context.rs +99 -0
  101. package/crates/naome-core/tests/harness_health.rs +4 -0
  102. package/crates/naome-core/tests/install_plan.rs +14 -0
  103. package/crates/naome-core/tests/quality.rs +190 -5
  104. package/crates/naome-core/tests/quality_performance.rs +268 -0
  105. package/crates/naome-core/tests/quality_structure_adapters.rs +39 -0
  106. package/crates/naome-core/tests/quality_structure_policy.rs +19 -0
  107. package/crates/naome-core/tests/repo_support/mod.rs +5 -1
  108. package/crates/naome-core/tests/repo_support/verification_values.rs +55 -0
  109. package/crates/naome-core/tests/repository_model.rs +281 -0
  110. package/crates/naome-core/tests/route_user_diff.rs +59 -7
  111. package/crates/naome-core/tests/semantic_legacy.rs +174 -0
  112. package/crates/naome-core/tests/task_ledger.rs +328 -0
  113. package/crates/naome-core/tests/task_state.rs +28 -0
  114. package/crates/naome-core/tests/verification.rs +29 -36
  115. package/crates/naome-core/tests/workflow_agent.rs +233 -0
  116. package/crates/naome-core/tests/workflow_agent_support/mod.rs +159 -0
  117. package/crates/naome-core/tests/workflow_doctor.rs +45 -0
  118. package/crates/naome-core/tests/workflow_policy.rs +6 -1
  119. package/installer/codex-hooks.js +121 -0
  120. package/installer/context.js +10 -0
  121. package/installer/filesystem.js +4 -0
  122. package/installer/flows.js +8 -4
  123. package/installer/git-boundary.js +1 -0
  124. package/installer/harness-files.js +6 -0
  125. package/installer/install-plan.js +4 -0
  126. package/installer/main.js +1 -1
  127. package/installer/native.js +1 -1
  128. package/native/darwin-arm64/naome +0 -0
  129. package/native/linux-x64/naome +0 -0
  130. package/package.json +1 -1
  131. package/templates/naome-root/.codex/config.toml +2 -0
  132. package/templates/naome-root/.codex/hooks.json +70 -0
  133. package/templates/naome-root/.naome/bin/check-harness-health.js +8 -6
  134. package/templates/naome-root/.naome/bin/check-task-state.js +12 -7
  135. package/templates/naome-root/.naome/bin/codex-hook-io.js +122 -0
  136. package/templates/naome-root/.naome/bin/codex-hook-policy.js +180 -0
  137. package/templates/naome-root/.naome/bin/codex-hook-runtime.js +174 -0
  138. package/templates/naome-root/.naome/bin/codex-hook.js +6 -0
  139. package/templates/naome-root/.naome/bin/naome.js +45 -7
  140. package/templates/naome-root/.naome/manifest.json +12 -6
  141. package/templates/naome-root/.naome/repository-model.json +6 -0
  142. package/templates/naome-root/.naome/repository-quality.json +3 -1
  143. package/templates/naome-root/.naome/verification.json +15 -1
  144. package/templates/naome-root/.naomeignore +1 -0
  145. package/templates/naome-root/AGENTS.md +38 -83
  146. package/templates/naome-root/docs/naome/agent-workflow.md +66 -28
  147. package/templates/naome-root/docs/naome/codex-hooks.md +82 -0
  148. package/templates/naome-root/docs/naome/context-economy.md +73 -0
  149. package/templates/naome-root/docs/naome/first-run.md +25 -14
  150. package/templates/naome-root/docs/naome/index.md +18 -10
  151. package/templates/naome-root/docs/naome/repository-model.md +92 -0
  152. package/templates/naome-root/docs/naome/repository-quality.md +104 -5
  153. package/templates/naome-root/docs/naome/repository-structure.md +10 -3
  154. package/templates/naome-root/docs/naome/task-ledger.md +71 -0
  155. package/templates/naome-root/docs/naome/testing.md +16 -3
@@ -0,0 +1,175 @@
1
+ mod normalize;
2
+
3
+ use std::collections::{HashMap, HashSet};
4
+ use std::fs;
5
+ use std::path::Path;
6
+
7
+ use super::{FileAnalysis, NormalizedLine, SymbolAnalysis};
8
+ use crate::quality::cache::{content_hash, QualityCache};
9
+ use normalize::{normalize_line, token_set};
10
+
11
+ pub(super) fn analyze_repo_file(
12
+ root: &Path,
13
+ path: &str,
14
+ added_lines: &HashMap<String, usize>,
15
+ blob_hashes: &HashMap<String, String>,
16
+ cache: &QualityCache,
17
+ allow_cache: bool,
18
+ ) -> Option<(FileAnalysis, bool)> {
19
+ let full_path = root.join(path);
20
+ if !full_path.is_file() || is_binary_extension(path) {
21
+ return None;
22
+ }
23
+ if allow_cache {
24
+ if let Some(blob_hash) = blob_hashes.get(path) {
25
+ if let Some(cached) = cache.read(path, blob_hash) {
26
+ return Some((cached, true));
27
+ }
28
+ }
29
+ }
30
+ let content = fs::read_to_string(&full_path).ok()?;
31
+ let file = analyze_file(path, &content, added_lines.get(path).copied().unwrap_or(0));
32
+ let hash = if allow_cache {
33
+ blob_hashes
34
+ .get(path)
35
+ .cloned()
36
+ .unwrap_or_else(|| content_hash(&content))
37
+ } else {
38
+ content_hash(&content)
39
+ };
40
+ let _ = cache.write(path, &hash, &file);
41
+ Some((file, false))
42
+ }
43
+
44
+ fn analyze_file(path: &str, content: &str, added_lines: usize) -> FileAnalysis {
45
+ let lines = content.lines().collect::<Vec<_>>();
46
+ let normalized_lines = lines
47
+ .iter()
48
+ .enumerate()
49
+ .filter_map(|(index, line)| normalize_line(line).map(|value| (index + 1, value)))
50
+ .map(|(line_number, value)| NormalizedLine { line_number, value })
51
+ .collect::<Vec<_>>();
52
+ let symbols = detect_symbols(&lines);
53
+ FileAnalysis {
54
+ path: path.to_string(),
55
+ line_count: lines.len(),
56
+ added_lines,
57
+ raw_lines: lines.iter().map(|line| (*line).to_string()).collect(),
58
+ normalized_lines,
59
+ symbols,
60
+ }
61
+ }
62
+
63
+ fn detect_symbols(lines: &[&str]) -> Vec<SymbolAnalysis> {
64
+ let mut starts = Vec::new();
65
+ for (index, line) in lines.iter().enumerate() {
66
+ let indent = indentation(line);
67
+ if let Some((kind, name)) = symbol_start(line.trim()) {
68
+ starts.push((index, indent, kind, name));
69
+ }
70
+ }
71
+
72
+ let mut symbols = Vec::new();
73
+ for (position, (start_index, indent, kind, name)) in starts.iter().enumerate() {
74
+ let end_index = starts
75
+ .iter()
76
+ .skip(position + 1)
77
+ .find(|(_, next_indent, _, _)| next_indent <= indent)
78
+ .map(|(next_index, _, _, _)| next_index.saturating_sub(1))
79
+ .unwrap_or_else(|| lines.len().saturating_sub(1));
80
+ let normalized_body = lines[*start_index..=end_index]
81
+ .iter()
82
+ .filter_map(|line| normalize_line(line))
83
+ .collect::<Vec<_>>();
84
+ let tokens = normalized_body
85
+ .iter()
86
+ .flat_map(|line| token_set(line))
87
+ .collect::<HashSet<_>>();
88
+ symbols.push(SymbolAnalysis {
89
+ kind: kind.clone(),
90
+ name: name.clone(),
91
+ start_line: start_index + 1,
92
+ end_line: end_index + 1,
93
+ indent: *indent,
94
+ tokens,
95
+ });
96
+ }
97
+ symbols
98
+ }
99
+
100
+ fn symbol_start(trimmed: &str) -> Option<(String, String)> {
101
+ let candidates = [
102
+ ("function", "function "),
103
+ ("function", "export function "),
104
+ ("function", "async function "),
105
+ ("function", "export async function "),
106
+ ("function", "def "),
107
+ ("function", "fn "),
108
+ ("function", "pub fn "),
109
+ ("function", "pub(crate) fn "),
110
+ ("function", "pub(super) fn "),
111
+ ("function", "func "),
112
+ ("class", "class "),
113
+ ("struct", "struct "),
114
+ ("struct", "pub struct "),
115
+ ("enum", "enum "),
116
+ ("enum", "pub enum "),
117
+ ("impl", "impl "),
118
+ ];
119
+ for (kind, prefix) in candidates {
120
+ if let Some(rest) = trimmed.strip_prefix(prefix) {
121
+ return Some((kind.to_string(), symbol_name(rest)));
122
+ }
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
+ }
135
+ for prefix in ["const ", "let ", "export const ", "export let "] {
136
+ if let Some(rest) = trimmed.strip_prefix(prefix) {
137
+ if trimmed.contains("=>") || trimmed.contains("function") || trimmed.contains("React.")
138
+ {
139
+ return Some(("function".to_string(), symbol_name(rest)));
140
+ }
141
+ }
142
+ }
143
+ None
144
+ }
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
+
152
+ fn symbol_name(rest: &str) -> String {
153
+ rest.chars()
154
+ .take_while(|character| character.is_ascii_alphanumeric() || *character == '_')
155
+ .collect::<String>()
156
+ .trim_matches('_')
157
+ .to_string()
158
+ }
159
+
160
+ fn indentation(line: &str) -> usize {
161
+ line.chars()
162
+ .take_while(|character| character.is_whitespace())
163
+ .map(|character| if character == '\t' { 2 } else { 1 })
164
+ .sum()
165
+ }
166
+
167
+ fn is_binary_extension(path: &str) -> bool {
168
+ let lower = path.to_ascii_lowercase();
169
+ [
170
+ ".png", ".jpg", ".jpeg", ".gif", ".webp", ".ico", ".pdf", ".zip", ".gz", ".tgz", ".wasm",
171
+ ".dylib", ".so", ".dll", ".exe", ".bin",
172
+ ]
173
+ .iter()
174
+ .any(|extension| lower.ends_with(extension))
175
+ }
@@ -1,4 +1,4 @@
1
- use std::collections::HashMap;
1
+ use std::collections::{HashMap, HashSet};
2
2
  use std::fs;
3
3
  use std::path::Path;
4
4
  use std::process::Command;
@@ -36,7 +36,10 @@ pub(crate) fn collect_repo_paths(root: &Path) -> Result<Vec<String>, NaomeError>
36
36
  Ok(paths)
37
37
  }
38
38
 
39
- pub(super) fn added_lines_by_path(root: &Path) -> Result<HashMap<String, usize>, NaomeError> {
39
+ pub(super) fn added_lines_by_path(
40
+ root: &Path,
41
+ target_paths: &HashSet<String>,
42
+ ) -> Result<HashMap<String, usize>, NaomeError> {
40
43
  let mut added = HashMap::new();
41
44
  for args in [
42
45
  vec!["diff", "--numstat"],
@@ -53,13 +56,20 @@ pub(super) fn added_lines_by_path(root: &Path) -> Result<HashMap<String, usize>,
53
56
  };
54
57
  let _deletions = parts.next();
55
58
  let Some(path) = parts.next() else { continue };
59
+ let path = path.replace('\\', "/");
60
+ if !target_paths.contains(&path) {
61
+ continue;
62
+ }
56
63
  let Ok(count) = additions.parse::<usize>() else {
57
64
  continue;
58
65
  };
59
- *added.entry(path.replace('\\', "/")).or_insert(0) += count;
66
+ *added.entry(path).or_insert(0) += count;
60
67
  }
61
68
  }
62
69
  for path in untracked_paths(root)? {
70
+ if !target_paths.contains(&path) {
71
+ continue;
72
+ }
63
73
  if let Ok(content) = fs::read_to_string(root.join(&path)) {
64
74
  added.insert(path, content.lines().count());
65
75
  }
@@ -67,6 +77,32 @@ pub(super) fn added_lines_by_path(root: &Path) -> Result<HashMap<String, usize>,
67
77
  Ok(added)
68
78
  }
69
79
 
80
+ pub(super) fn tracked_blob_hashes(root: &Path) -> Result<HashMap<String, String>, NaomeError> {
81
+ let output = Command::new("git")
82
+ .args(["ls-files", "-s", "-z"])
83
+ .current_dir(root)
84
+ .output()?;
85
+ if !output.status.success() {
86
+ return Ok(HashMap::new());
87
+ }
88
+
89
+ let mut hashes = HashMap::new();
90
+ for entry in output.stdout.split(|byte| *byte == 0) {
91
+ if entry.is_empty() {
92
+ continue;
93
+ }
94
+ let entry = String::from_utf8_lossy(entry);
95
+ let Some((metadata, path)) = entry.split_once('\t') else {
96
+ continue;
97
+ };
98
+ let mut parts = metadata.split_whitespace();
99
+ let _mode = parts.next();
100
+ let Some(hash) = parts.next() else { continue };
101
+ hashes.insert(path.replace('\\', "/"), hash.to_string());
102
+ }
103
+ Ok(hashes)
104
+ }
105
+
70
106
  fn collect_files_recursive(
71
107
  root: &Path,
72
108
  dir: &Path,