@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
@@ -1,8 +1,10 @@
1
1
  use std::path::Path;
2
2
 
3
3
  use naome_core::{
4
- classify_mutations, refresh_integrity, safe_rg_args, tracked_process_report,
5
- validate_search_command, verification_phase_plan,
4
+ agent_run_plan, classify_mutations, context_delta_report, decision_gate, doctor_report,
5
+ edit_session_watchdog, proof_plan_for_task, refresh_integrity, repository_capability_graph,
6
+ safe_rg_args, summarize_command_output, tracked_process_report, validate_search_command,
7
+ verification_phase_plan,
6
8
  };
7
9
 
8
10
  use crate::cli_args::option_value;
@@ -30,7 +32,7 @@ pub fn run_workflow_command(
30
32
  args: &[String],
31
33
  ) -> Result<(), Box<dyn std::error::Error>> {
32
34
  let Some(subcommand) = args.get(1).map(String::as_str) else {
33
- return Err("naome workflow requires search-profile, check-search, phases, processes, or mutations.".into());
35
+ return Err("naome workflow requires agent-plan, context-delta, proof-plan, capabilities, edit-watchdog, decision-gate, digest, search-profile, check-search, phases, processes, or mutations.".into());
34
36
  };
35
37
  let json = args.iter().any(|arg| arg == "--json");
36
38
 
@@ -50,6 +52,29 @@ pub fn run_workflow_command(
50
52
  }
51
53
  }
52
54
  "check-search" => run_check_search(root, args, json)?,
55
+ "agent-plan" => print_json_or_label(
56
+ serde_json::to_value(agent_run_plan(root)?)?,
57
+ json,
58
+ "NAOME agent plan ready.",
59
+ )?,
60
+ "context-delta" => run_context_delta(root, args, json)?,
61
+ "proof-plan" => print_json_or_label(
62
+ serde_json::to_value(proof_plan_for_task(root, &[])?)?,
63
+ json,
64
+ "NAOME proof plan ready.",
65
+ )?,
66
+ "capabilities" => print_json_or_label(
67
+ serde_json::to_value(repository_capability_graph(root)?)?,
68
+ json,
69
+ "NAOME capability graph ready.",
70
+ )?,
71
+ "edit-watchdog" => run_edit_watchdog(root, args, json)?,
72
+ "decision-gate" => print_json_or_label(
73
+ serde_json::to_value(decision_gate(root)?)?,
74
+ json,
75
+ "NAOME decision gate ready.",
76
+ )?,
77
+ "digest" => run_digest(root, args, json)?,
53
78
  "phases" => {
54
79
  let plan = verification_phase_plan(root, &[])?;
55
80
  if json {
@@ -83,6 +108,26 @@ pub fn run_workflow_command(
83
108
  Ok(())
84
109
  }
85
110
 
111
+ pub fn run_doctor(root: &Path, args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
112
+ let report = doctor_report(root)?;
113
+ if args.iter().any(|arg| arg == "--json") {
114
+ println!("{}", serde_json::to_string_pretty(&report)?);
115
+ } else {
116
+ println!(
117
+ "NAOME doctor: {}",
118
+ if report.ok { "ok" } else { "attention needed" }
119
+ );
120
+ println!("{}", report.next_action);
121
+ if !report.recommended_check_ids.is_empty() {
122
+ println!(
123
+ "Recommended checks: {}",
124
+ report.recommended_check_ids.join(", ")
125
+ );
126
+ }
127
+ }
128
+ Ok(())
129
+ }
130
+
86
131
  fn run_check_search(
87
132
  root: &Path,
88
133
  args: &[String],
@@ -134,6 +179,78 @@ fn run_mutations(
134
179
  Ok(())
135
180
  }
136
181
 
182
+ fn run_context_delta(
183
+ root: &Path,
184
+ args: &[String],
185
+ json: bool,
186
+ ) -> Result<(), Box<dyn std::error::Error>> {
187
+ let read_paths = repeated_values(args, "--read-path");
188
+ print_json_or_label(
189
+ serde_json::to_value(context_delta_report(root, &read_paths)?)?,
190
+ json,
191
+ "NAOME context delta ready.",
192
+ )
193
+ }
194
+
195
+ fn run_edit_watchdog(
196
+ root: &Path,
197
+ args: &[String],
198
+ json: bool,
199
+ ) -> Result<(), Box<dyn std::error::Error>> {
200
+ let paths = repeated_values(args, "--path");
201
+ if paths.is_empty() {
202
+ return Err("naome workflow edit-watchdog requires --path <path>.".into());
203
+ }
204
+ print_json_or_label(
205
+ serde_json::to_value(edit_session_watchdog(root, &paths)?)?,
206
+ json,
207
+ "NAOME edit watchdog ready.",
208
+ )
209
+ }
210
+
211
+ fn run_digest(root: &Path, args: &[String], json: bool) -> Result<(), Box<dyn std::error::Error>> {
212
+ let command = option_value(args, "--command").unwrap_or_else(|| "unknown".to_string());
213
+ let cwd = option_value(args, "--cwd").unwrap_or_else(|| ".".to_string());
214
+ let exit_code = option_value(args, "--exit-code")
215
+ .and_then(|value| value.parse::<i32>().ok())
216
+ .unwrap_or(1);
217
+ let max_lines = option_value(args, "--max-lines")
218
+ .and_then(|value| value.parse::<usize>().ok())
219
+ .unwrap_or(12);
220
+ let output = if let Some(path) = option_value(args, "--output-file") {
221
+ std::fs::read_to_string(root.join(path))?
222
+ } else {
223
+ option_value(args, "--output").unwrap_or_default()
224
+ };
225
+ print_json_or_label(
226
+ serde_json::to_value(summarize_command_output(
227
+ &command, &cwd, exit_code, &output, max_lines,
228
+ ))?,
229
+ json,
230
+ "NAOME output digest ready.",
231
+ )
232
+ }
233
+
234
+ fn repeated_values(args: &[String], flag: &str) -> Vec<String> {
235
+ args.windows(2)
236
+ .filter(|window| window[0] == flag)
237
+ .map(|window| window[1].clone())
238
+ .collect()
239
+ }
240
+
241
+ fn print_json_or_label(
242
+ value: serde_json::Value,
243
+ json: bool,
244
+ label: &str,
245
+ ) -> Result<(), Box<dyn std::error::Error>> {
246
+ if json {
247
+ println!("{}", serde_json::to_string_pretty(&value)?);
248
+ } else {
249
+ println!("{label}");
250
+ }
251
+ Ok(())
252
+ }
253
+
137
254
  fn empty_label(values: &[String]) -> String {
138
255
  if values.is_empty() {
139
256
  "none".to_string()
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "naome-core"
3
- version = "1.2.1"
3
+ version = "1.3.1"
4
4
  edition.workspace = true
5
5
  license.workspace = true
6
6
  repository.workspace = true
@@ -0,0 +1,75 @@
1
+ use std::collections::BTreeSet;
2
+ use std::fs;
3
+ use std::path::Path;
4
+
5
+ use super::types::{ContextCapsule, ContextItem};
6
+
7
+ pub(super) fn normalize_paths(paths: Vec<String>) -> Vec<String> {
8
+ let normalized = paths
9
+ .into_iter()
10
+ .filter(|path| !path.trim().is_empty())
11
+ .map(|path| path.trim_start_matches("./").replace('\\', "/"))
12
+ .collect::<BTreeSet<_>>()
13
+ .into_iter()
14
+ .collect::<Vec<_>>();
15
+ let task_paths = normalized
16
+ .iter()
17
+ .filter(|path| !is_context_noise(path))
18
+ .cloned()
19
+ .collect::<Vec<_>>();
20
+ if task_paths.is_empty() {
21
+ normalized
22
+ } else {
23
+ task_paths
24
+ }
25
+ }
26
+
27
+ pub(super) fn context_item(root: &Path, path: &str, reason: &str, priority: &str) -> ContextItem {
28
+ ContextItem {
29
+ path: path.to_string(),
30
+ reason: reason.to_string(),
31
+ priority: priority.to_string(),
32
+ estimated_lines: fs::read_to_string(root.join(path))
33
+ .map(|content| content.lines().count().max(1))
34
+ .unwrap_or(1),
35
+ }
36
+ }
37
+
38
+ pub(super) fn capsule_for_paths(paths: &[String]) -> ContextCapsule {
39
+ let id = if paths.iter().any(|path| path.contains("quality")) {
40
+ "quality-work"
41
+ } else if paths
42
+ .iter()
43
+ .any(|path| path.contains("/route") || path.contains("/intent"))
44
+ {
45
+ "routing-work"
46
+ } else if paths.iter().any(|path| is_source_path(path)) {
47
+ "source-work"
48
+ } else if paths
49
+ .iter()
50
+ .any(|path| path.ends_with(".md") || path.contains("/docs/"))
51
+ {
52
+ "docs-work"
53
+ } else {
54
+ "general-work"
55
+ };
56
+ ContextCapsule {
57
+ id: id.to_string(),
58
+ task_type: id.trim_end_matches("-work").to_string(),
59
+ summary: format!("Compact context capsule for {id}."),
60
+ }
61
+ }
62
+
63
+ pub(super) fn is_source_path(path: &str) -> bool {
64
+ let lower = path.to_ascii_lowercase();
65
+ [
66
+ ".rs", ".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs", ".py", ".go", ".swift", ".kt",
67
+ ".java", ".c", ".cc", ".cpp", ".h", ".hpp", ".rb", ".php", ".cs", ".sh",
68
+ ]
69
+ .iter()
70
+ .any(|extension| lower.ends_with(extension))
71
+ }
72
+
73
+ fn is_context_noise(path: &str) -> bool {
74
+ matches!(path, ".naome/task-state.json" | ".naome/manifest.json") || path.contains("/native/")
75
+ }
@@ -0,0 +1,134 @@
1
+ use std::path::Path;
2
+
3
+ use crate::{git, models::NaomeError};
4
+
5
+ use super::helpers::{capsule_for_paths, context_item, is_source_path, normalize_paths};
6
+ use super::types::{ContextBudgetLedger, ContextItem, ContextSelection};
7
+
8
+ const MAX_CONTEXT_FILES: usize = 12;
9
+
10
+ pub fn select_context_for_changed_paths(root: &Path) -> Result<ContextSelection, NaomeError> {
11
+ let paths = git::changed_paths(root)?;
12
+ Ok(selection_for_paths(root, "changed", paths))
13
+ }
14
+
15
+ pub fn select_context_for_prompt(
16
+ root: &Path,
17
+ prompt: impl AsRef<str>,
18
+ ) -> Result<ContextSelection, NaomeError> {
19
+ Ok(selection_for_paths(
20
+ root,
21
+ "prompt",
22
+ mentioned_paths(prompt.as_ref()),
23
+ ))
24
+ }
25
+
26
+ fn selection_for_paths(root: &Path, mode: &str, paths: Vec<String>) -> ContextSelection {
27
+ let target_paths = normalize_paths(paths);
28
+ let capsule = capsule_for_paths(&target_paths);
29
+ let mut required_context = target_paths
30
+ .iter()
31
+ .take(MAX_CONTEXT_FILES)
32
+ .map(|path| context_item(root, path, "changed_path", "required"))
33
+ .collect::<Vec<_>>();
34
+ if required_context.is_empty() {
35
+ required_context.push(context_item(
36
+ root,
37
+ ".naome/verification.json",
38
+ "minimal_harness_policy",
39
+ "required",
40
+ ));
41
+ }
42
+
43
+ let avoided_files = target_paths.len().saturating_sub(required_context.len());
44
+ ContextSelection {
45
+ schema: "naome.context-selection.v1".to_string(),
46
+ mode: mode.to_string(),
47
+ optional_context: optional_context(root, &capsule.id),
48
+ forbidden_context: forbidden_context(),
49
+ commands: commands_for_paths(&target_paths),
50
+ budget_ledger: budget_ledger(&required_context, avoided_files),
51
+ notes: vec![
52
+ "Read requiredContext first; read optionalContext only when the task is blocked without it.".to_string(),
53
+ "Use path-scoped checks during implementation and keep the final changed gate before completion.".to_string(),
54
+ ],
55
+ capsule,
56
+ required_context,
57
+ }
58
+ }
59
+
60
+ fn optional_context(root: &Path, capsule_id: &str) -> Vec<ContextItem> {
61
+ let candidates = match capsule_id {
62
+ "quality-work" => vec!["docs/naome/repository-quality.md", "docs/naome/testing.md"],
63
+ "routing-work" => vec!["docs/naome/agent-workflow.md", "docs/naome/testing.md"],
64
+ "docs-work" => vec!["docs/naome/index.md", "docs/naome/testing.md"],
65
+ _ => vec!["docs/naome/repository-quality.md", "docs/naome/testing.md"],
66
+ };
67
+ candidates
68
+ .into_iter()
69
+ .filter(|path| root.join(path).is_file())
70
+ .map(|path| context_item(root, path, "optional_capsule_context", "optional"))
71
+ .collect()
72
+ }
73
+
74
+ fn budget_ledger(required_context: &[ContextItem], avoided_files: usize) -> ContextBudgetLedger {
75
+ let estimated_lines = required_context
76
+ .iter()
77
+ .map(|item| item.estimated_lines)
78
+ .sum::<usize>();
79
+ ContextBudgetLedger {
80
+ max_context_files: MAX_CONTEXT_FILES,
81
+ selected_files: required_context.len(),
82
+ avoided_files,
83
+ estimated_lines,
84
+ estimated_tokens: estimated_lines.saturating_mul(8),
85
+ reason_codes: if avoided_files > 0 {
86
+ vec!["context_file_budget_reached".to_string()]
87
+ } else {
88
+ Vec::new()
89
+ },
90
+ }
91
+ }
92
+
93
+ fn commands_for_paths(paths: &[String]) -> Vec<String> {
94
+ let mut commands = paths
95
+ .iter()
96
+ .filter(|path| is_source_path(path))
97
+ .take(6)
98
+ .map(|path| format!("node .naome/bin/naome.js quality check --path {path}"))
99
+ .collect::<Vec<_>>();
100
+ commands.push("node .naome/bin/naome.js quality check --changed".to_string());
101
+ commands.push("git diff --check".to_string());
102
+ commands.sort();
103
+ commands.dedup();
104
+ commands
105
+ }
106
+
107
+ fn forbidden_context() -> Vec<String> {
108
+ [
109
+ ".git/**",
110
+ ".naome/archive/**",
111
+ ".naome/cache/**",
112
+ "node_modules/**",
113
+ "target/**",
114
+ "dist/**",
115
+ "build/**",
116
+ ]
117
+ .into_iter()
118
+ .map(str::to_string)
119
+ .collect()
120
+ }
121
+
122
+ fn mentioned_paths(prompt: &str) -> Vec<String> {
123
+ prompt
124
+ .split_whitespace()
125
+ .map(|token| {
126
+ token.trim_matches(|ch: char| {
127
+ matches!(ch, '"' | '\'' | '`' | ',' | ';' | ':' | ')' | '(')
128
+ })
129
+ })
130
+ .filter(|token| token.contains('/') || is_source_path(token) || token.ends_with(".md"))
131
+ .filter(|token| !token.starts_with('-') && !token.starts_with("http"))
132
+ .map(|token| token.trim_start_matches("./").replace('\\', "/"))
133
+ .collect()
134
+ }
@@ -0,0 +1,43 @@
1
+ use serde::Serialize;
2
+
3
+ #[derive(Debug, Clone, Serialize, PartialEq, Eq)]
4
+ #[serde(rename_all = "camelCase")]
5
+ pub struct ContextSelection {
6
+ pub schema: String,
7
+ pub mode: String,
8
+ pub capsule: ContextCapsule,
9
+ pub required_context: Vec<ContextItem>,
10
+ pub optional_context: Vec<ContextItem>,
11
+ pub forbidden_context: Vec<String>,
12
+ pub commands: Vec<String>,
13
+ pub budget_ledger: ContextBudgetLedger,
14
+ pub notes: Vec<String>,
15
+ }
16
+
17
+ #[derive(Debug, Clone, Serialize, PartialEq, Eq)]
18
+ #[serde(rename_all = "camelCase")]
19
+ pub struct ContextCapsule {
20
+ pub id: String,
21
+ pub summary: String,
22
+ pub task_type: String,
23
+ }
24
+
25
+ #[derive(Debug, Clone, Serialize, PartialEq, Eq)]
26
+ #[serde(rename_all = "camelCase")]
27
+ pub struct ContextItem {
28
+ pub path: String,
29
+ pub reason: String,
30
+ pub priority: String,
31
+ pub estimated_lines: usize,
32
+ }
33
+
34
+ #[derive(Debug, Clone, Serialize, PartialEq, Eq)]
35
+ #[serde(rename_all = "camelCase")]
36
+ pub struct ContextBudgetLedger {
37
+ pub max_context_files: usize,
38
+ pub selected_files: usize,
39
+ pub avoided_files: usize,
40
+ pub estimated_lines: usize,
41
+ pub estimated_tokens: usize,
42
+ pub reason_codes: Vec<String>,
43
+ }
@@ -0,0 +1,6 @@
1
+ mod helpers;
2
+ mod select;
3
+ mod types;
4
+
5
+ pub use select::{select_context_for_changed_paths, select_context_for_prompt};
6
+ pub use types::{ContextBudgetLedger, ContextCapsule, ContextItem, ContextSelection};
@@ -161,5 +161,5 @@ fn has_completed_task_owned_paths(task: Option<&TaskDecision>, changed_paths: &[
161
161
  }
162
162
 
163
163
  fn is_control_state_path(path: &str) -> bool {
164
- path == ".naome/task-state.json"
164
+ path == ".naome/task-state.json" || path.starts_with(".naome/tasks/")
165
165
  }
@@ -1,7 +1,10 @@
1
1
  use std::path::Path;
2
2
 
3
+ use serde_json::Value;
4
+
3
5
  use crate::git;
4
6
  use crate::models::{CheckDecision, Decision, NaomeError, TaskDecision};
7
+ use crate::task_ledger::read_task_state_projection;
5
8
 
6
9
  mod checks;
7
10
  mod idle;
@@ -40,7 +43,7 @@ pub fn evaluate_decision(root: &Path, options: EvaluationOptions) -> Result<Deci
40
43
  return Ok(decision);
41
44
  }
42
45
 
43
- let task_state = read_json(root, ".naome/task-state.json")?;
46
+ let task_state = read_task_state_projection(root)?.unwrap_or(Value::Null);
44
47
  if let Some(decision) = setup_block_decision(root, &changed_paths, &harness_health)? {
45
48
  return Ok(decision);
46
49
  }
@@ -63,12 +63,14 @@ fn read_naomeignore_patterns(root: &Path) -> Vec<String> {
63
63
  return Vec::new();
64
64
  };
65
65
 
66
- content
66
+ let mut patterns = content
67
67
  .lines()
68
68
  .map(str::trim)
69
69
  .filter(|line| !line.is_empty() && !line.starts_with('#') && !line.starts_with('!'))
70
70
  .map(normalize_ignore_pattern)
71
- .collect()
71
+ .collect::<Vec<_>>();
72
+ patterns.push(".naome/cache/**".to_string());
73
+ patterns
72
74
  }
73
75
 
74
76
  fn normalize_ignore_pattern(pattern: &str) -> String {
@@ -10,6 +10,8 @@ pub const MACHINE_OWNED_PATHS: &[&str] = &[
10
10
  "docs/naome/index.md",
11
11
  "docs/naome/first-run.md",
12
12
  "docs/naome/agent-workflow.md",
13
+ "docs/naome/context-economy.md",
14
+ "docs/naome/task-ledger.md",
13
15
  "docs/naome/execution.md",
14
16
  "docs/naome/upgrade.md",
15
17
  ];
@@ -21,12 +23,14 @@ pub const PROJECT_OWNED_PATHS: &[&str] = &[
21
23
  ".naome/task-state.json",
22
24
  ".naome/upgrade-state.json",
23
25
  ".naome/verification.json",
26
+ ".naome/repository-model.json",
24
27
  ".naome/repository-quality.json",
25
28
  ".naome/repository-structure.json",
26
29
  ".naome/repository-quality-baseline.json",
27
30
  "docs/naome/architecture.md",
28
31
  "docs/naome/decisions.md",
29
32
  "docs/naome/repo-profile.md",
33
+ "docs/naome/repository-model.md",
30
34
  "docs/naome/repository-quality.md",
31
35
  "docs/naome/repository-structure.md",
32
36
  "docs/naome/security.md",
@@ -42,6 +46,8 @@ pub const LOCAL_ONLY_MACHINE_OWNED_PATHS: &[&str] = &[
42
46
  "docs/naome/index.md",
43
47
  "docs/naome/first-run.md",
44
48
  "docs/naome/agent-workflow.md",
49
+ "docs/naome/context-economy.md",
50
+ "docs/naome/task-ledger.md",
45
51
  "docs/naome/execution.md",
46
52
  "docs/naome/upgrade.md",
47
53
  ];
@@ -50,9 +56,20 @@ pub const LOCAL_NATIVE_BINARY_PATHS: &[&str] = &[
50
56
  ".naome/bin/naome-rust",
51
57
  ".naome/bin/naome-rust.exe",
52
58
  ".naome/archive",
59
+ ".naome/cache",
53
60
  ".naome/task-journal.jsonl",
54
61
  ];
55
62
 
63
+ pub const OPTIONAL_CODEX_HOOK_PATHS: &[&str] = &[
64
+ ".codex/config.toml",
65
+ ".codex/hooks.json",
66
+ ".naome/bin/codex-hook.js",
67
+ ".naome/bin/codex-hook-io.js",
68
+ ".naome/bin/codex-hook-policy.js",
69
+ ".naome/bin/codex-hook-runtime.js",
70
+ "docs/naome/codex-hooks.md",
71
+ ];
72
+
56
73
  #[derive(Debug, Clone, Serialize, PartialEq, Eq)]
57
74
  #[serde(rename_all = "camelCase")]
58
75
  pub struct InstallPlan {
@@ -61,6 +78,7 @@ pub struct InstallPlan {
61
78
  pub machine_owned: Vec<&'static str>,
62
79
  pub project_owned: Vec<&'static str>,
63
80
  pub local_only_machine_owned: Vec<&'static str>,
81
+ pub optional_codex_hook_paths: Vec<&'static str>,
64
82
  pub gitignore_entries: Vec<&'static str>,
65
83
  pub git_exclude_entries: Vec<&'static str>,
66
84
  pub git_untrack_paths: Vec<&'static str>,
@@ -72,6 +90,7 @@ pub fn install_plan(harness_version: impl Into<String>) -> InstallPlan {
72
90
  "# NAOME local machine-owned harness files.",
73
91
  ".naome/archive/",
74
92
  ".naome/bin/naome-rust*",
93
+ ".naome/cache/",
75
94
  ".naome/task-journal.jsonl",
76
95
  ];
77
96
  git_exclude_entries.extend_from_slice(LOCAL_ONLY_MACHINE_OWNED_PATHS);
@@ -85,6 +104,7 @@ pub fn install_plan(harness_version: impl Into<String>) -> InstallPlan {
85
104
  machine_owned: MACHINE_OWNED_PATHS.to_vec(),
86
105
  project_owned: PROJECT_OWNED_PATHS.to_vec(),
87
106
  local_only_machine_owned: LOCAL_ONLY_MACHINE_OWNED_PATHS.to_vec(),
107
+ optional_codex_hook_paths: OPTIONAL_CODEX_HOOK_PATHS.to_vec(),
88
108
  gitignore_entries,
89
109
  git_exclude_entries,
90
110
  git_untrack_paths,
@@ -8,6 +8,7 @@ use serde::Serialize;
8
8
  use serde_json::Value;
9
9
 
10
10
  use crate::models::NaomeError;
11
+ use crate::task_ledger::read_task_state_projection;
11
12
  use crate::task_state::canonical_proof_check_ids;
12
13
 
13
14
  const JOURNAL_PATH: &str = ".naome/task-journal.jsonl";
@@ -35,7 +36,7 @@ pub fn append_task_journal(
35
36
  commit_after: Option<String>,
36
37
  ) -> Result<Option<TaskJournalEntry>, NaomeError> {
37
38
  ensure_journal_is_local_only(root)?;
38
- let task_state = read_json(root, ".naome/task-state.json")?;
39
+ let task_state = read_task_state_projection(root)?.unwrap_or(Value::Null);
39
40
  let Some(active_task) = task_state
40
41
  .get("activeTask")
41
42
  .filter(|value| !value.is_null())
@@ -125,12 +126,6 @@ fn proof_summary(active_task: &Value) -> Vec<String> {
125
126
  canonical_proof_check_ids(active_task)
126
127
  }
127
128
 
128
- fn read_json(root: &Path, relative_path: &str) -> Result<Value, NaomeError> {
129
- Ok(serde_json::from_str(&fs::read_to_string(
130
- root.join(relative_path),
131
- )?)?)
132
- }
133
-
134
129
  fn string_at(value: &Value, key: &str) -> Option<String> {
135
130
  value
136
131
  .get(key)
@@ -1,3 +1,4 @@
1
+ mod context;
1
2
  mod decision;
2
3
  mod git;
3
4
  mod harness_health;
@@ -7,12 +8,18 @@ mod journal;
7
8
  mod models;
8
9
  mod paths;
9
10
  mod quality;
11
+ mod repository_model;
10
12
  mod route;
13
+ mod task_ledger;
11
14
  mod task_state;
12
15
  mod verification;
13
16
  mod verification_contract;
14
17
  mod workflow;
15
18
 
19
+ pub use context::{
20
+ select_context_for_changed_paths, select_context_for_prompt, ContextBudgetLedger,
21
+ ContextCapsule, ContextItem, ContextSelection,
22
+ };
16
23
  pub use decision::{evaluate_decision, format_decision, EvaluationOptions};
17
24
  pub use harness_health::{validate_harness_health, HarnessHealthOptions};
18
25
  pub use install_plan::{install_plan, InstallPlan};
@@ -21,12 +28,27 @@ pub use intent::{evaluate_intent, format_intent, IntentDecision, PromptEvidence}
21
28
  pub use journal::{append_task_journal, TaskJournalEntry};
22
29
  pub use models::{Decision, NaomeError};
23
30
  pub use quality::{
24
- check_repository_quality, explain_repository_structure, init_repository_quality,
25
- plan_quality_cleanup, route_quality_cleanup, QualityCleanupPlan, QualityCleanupRoute,
26
- QualityCleanupTask, QualityInitResult, QualityMode, QualityReport, QualitySummary,
27
- QualityViolation, RepositoryQualityConfig, RepositoryStructureConfig, StructurePathExplanation,
31
+ check_repository_quality, check_repository_quality_paths, check_semantic_legacy,
32
+ check_semantic_legacy_paths,
33
+ clear_quality_cache, explain_repository_structure, init_repository_quality,
34
+ init_repository_quality_with_mode, plan_quality_cleanup, quality_cache_status,
35
+ reconcile_repository_quality, route_quality_cleanup, semantic_route_for_finding,
36
+ QualityCacheStatus, QualityCleanupPlan, QualityCleanupRoute, QualityCleanupTask,
37
+ QualityInitMode, QualityInitResult, QualityMode, QualityReconcileReport, QualityReport,
38
+ QualitySummary, QualityViolation, RepositoryQualityConfig, RepositoryStructureConfig,
39
+ SemanticFinding, SemanticReport, StructurePathExplanation,
40
+ };
41
+ pub use repository_model::{
42
+ explain_repository_model_path, refresh_repository_model, repository_model_drift,
43
+ RepositoryEntity, RepositoryFact, RepositoryModel, RepositoryModelDrift,
44
+ RepositoryModelRefresh, RepositoryPathExplanation, RepositoryPathFact, RepositoryRoot,
45
+ RepositoryVerificationCheck, RepositoryWorldSignal,
28
46
  };
29
47
  pub use route::{evaluate_route, explain_route, ExplainDecision, RouteDecision, RouteOptions};
48
+ pub use task_ledger::{
49
+ migrate_task_state_to_ledger, read_task_state_projection, render_task_state_from_ledger,
50
+ TaskLedgerProjection, TaskLedgerStatus,
51
+ };
30
52
  pub use task_state::{
31
53
  completed_task_commit_paths, validate_task_state, TaskStateMode, TaskStateOptions,
32
54
  TaskStateReport,
@@ -34,8 +56,13 @@ pub use task_state::{
34
56
  pub use verification::seed_builtin_verification_checks;
35
57
  pub use verification_contract::validate_verification_contract;
36
58
  pub use workflow::{
37
- classify_mutations, refresh_integrity, safe_rg_args, summarize_command_output,
38
- tracked_process_report, validate_read_boundaries, validate_search_command,
39
- verification_phase_plan, CommandCheckResult, CommandOutputSummary, IntegrityRefreshReport,
40
- MutationClassification, ProcessReport, ReadActivity, VerificationPhasePlan, WorkflowFinding,
59
+ agent_run_plan, classify_mutations, context_delta_report, decision_gate, doctor_report,
60
+ edit_session_watchdog, proof_plan_for_task, refresh_integrity, repository_capability_graph,
61
+ safe_rg_args, summarize_command_output, tracked_process_report, validate_read_boundaries,
62
+ validate_search_command, verification_phase_plan, AgentRunPlan, CapabilityGraph,
63
+ CapabilityRoot, CommandCheckResult, CommandOutputSummary, ContextDeltaReport, DecisionGate,
64
+ DoctorReport, DoctorSection, EditSessionWatchdog, ExecutionPlanLedger, ExecutionPlanStep,
65
+ IntegrityRefreshReport, MutationClassification, ProcessReport, ProofPlan, ReadActivity,
66
+ RepositoryCapability, RepositoryPolicySection, VerificationEdge, VerificationPhasePlan,
67
+ WorkflowFinding,
41
68
  };