@lamentis/naome 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/Cargo.lock +2 -2
  2. package/Cargo.toml +1 -1
  3. package/LICENSE +180 -21
  4. package/README.md +49 -6
  5. package/bin/naome.js +54 -16
  6. package/crates/naome-cli/Cargo.toml +1 -1
  7. package/crates/naome-cli/src/check_commands.rs +135 -0
  8. package/crates/naome-cli/src/cli_args.rs +5 -0
  9. package/crates/naome-cli/src/dispatcher.rs +36 -0
  10. package/crates/naome-cli/src/install_bridge.rs +83 -0
  11. package/crates/naome-cli/src/main.rs +57 -341
  12. package/crates/naome-cli/src/prompt_commands.rs +68 -0
  13. package/crates/naome-cli/src/quality_commands.rs +141 -0
  14. package/crates/naome-cli/src/simple_commands.rs +53 -0
  15. package/crates/naome-cli/src/workflow_commands.rs +153 -0
  16. package/crates/naome-core/Cargo.toml +1 -1
  17. package/crates/naome-core/src/harness_health/integrity.rs +96 -0
  18. package/crates/naome-core/src/harness_health.rs +14 -126
  19. package/crates/naome-core/src/install_plan.rs +3 -0
  20. package/crates/naome-core/src/intent/classifier.rs +171 -0
  21. package/crates/naome-core/src/intent/envelope.rs +108 -0
  22. package/crates/naome-core/src/intent/legacy.rs +138 -0
  23. package/crates/naome-core/src/intent/legacy_response.rs +76 -0
  24. package/crates/naome-core/src/intent/model.rs +71 -0
  25. package/crates/naome-core/src/intent/patterns.rs +170 -0
  26. package/crates/naome-core/src/intent/resolver.rs +162 -0
  27. package/crates/naome-core/src/intent/resolver_active.rs +17 -0
  28. package/crates/naome-core/src/intent/resolver_baseline.rs +55 -0
  29. package/crates/naome-core/src/intent/resolver_catalog.rs +167 -0
  30. package/crates/naome-core/src/intent/resolver_policy.rs +72 -0
  31. package/crates/naome-core/src/intent/resolver_shared.rs +55 -0
  32. package/crates/naome-core/src/intent/risk.rs +40 -0
  33. package/crates/naome-core/src/intent/segment.rs +170 -0
  34. package/crates/naome-core/src/intent.rs +64 -879
  35. package/crates/naome-core/src/journal.rs +9 -20
  36. package/crates/naome-core/src/lib.rs +13 -0
  37. package/crates/naome-core/src/quality/adapters.rs +178 -0
  38. package/crates/naome-core/src/quality/baseline.rs +75 -0
  39. package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +175 -0
  40. package/crates/naome-core/src/quality/checks/near_duplicates.rs +130 -0
  41. package/crates/naome-core/src/quality/checks.rs +228 -0
  42. package/crates/naome-core/src/quality/cleanup.rs +72 -0
  43. package/crates/naome-core/src/quality/config.rs +109 -0
  44. package/crates/naome-core/src/quality/mod.rs +90 -0
  45. package/crates/naome-core/src/quality/scanner/repo_paths.rs +103 -0
  46. package/crates/naome-core/src/quality/scanner.rs +367 -0
  47. package/crates/naome-core/src/quality/types.rs +289 -0
  48. package/crates/naome-core/src/route.rs +62 -0
  49. package/crates/naome-core/src/task_state/admission.rs +63 -0
  50. package/crates/naome-core/src/task_state/admission_proof.rs +72 -0
  51. package/crates/naome-core/src/task_state/api.rs +130 -0
  52. package/crates/naome-core/src/task_state/commit_gate.rs +138 -0
  53. package/crates/naome-core/src/task_state/compact_proof.rs +160 -0
  54. package/crates/naome-core/src/task_state/completed_refresh.rs +89 -0
  55. package/crates/naome-core/src/task_state/completion.rs +72 -0
  56. package/crates/naome-core/src/task_state/deleted_paths.rs +47 -0
  57. package/crates/naome-core/src/task_state/diff.rs +95 -0
  58. package/crates/naome-core/src/task_state/evidence.rs +154 -0
  59. package/crates/naome-core/src/task_state/git_io.rs +86 -0
  60. package/crates/naome-core/src/task_state/git_parse.rs +86 -0
  61. package/crates/naome-core/src/task_state/git_refs.rs +37 -0
  62. package/crates/naome-core/src/task_state/human_review_state.rs +31 -0
  63. package/crates/naome-core/src/task_state/mod.rs +38 -0
  64. package/crates/naome-core/src/task_state/process_guard.rs +40 -0
  65. package/crates/naome-core/src/task_state/progress.rs +123 -0
  66. package/crates/naome-core/src/task_state/proof.rs +139 -0
  67. package/crates/naome-core/src/task_state/proof_entry.rs +66 -0
  68. package/crates/naome-core/src/task_state/proof_model.rs +70 -0
  69. package/crates/naome-core/src/task_state/proof_sources.rs +76 -0
  70. package/crates/naome-core/src/task_state/push_gate.rs +49 -0
  71. package/crates/naome-core/src/task_state/reconcile.rs +7 -0
  72. package/crates/naome-core/src/task_state/repair.rs +168 -0
  73. package/crates/naome-core/src/task_state/shape.rs +117 -0
  74. package/crates/naome-core/src/task_state/task_diff_api.rs +170 -0
  75. package/crates/naome-core/src/task_state/task_records.rs +131 -0
  76. package/crates/naome-core/src/task_state/task_references.rs +126 -0
  77. package/crates/naome-core/src/task_state/types.rs +87 -0
  78. package/crates/naome-core/src/task_state/util.rs +137 -0
  79. package/crates/naome-core/src/verification/render.rs +122 -0
  80. package/crates/naome-core/src/verification.rs +176 -58
  81. package/crates/naome-core/src/verification_contract.rs +49 -21
  82. package/crates/naome-core/src/workflow/integrity.rs +123 -0
  83. package/crates/naome-core/src/workflow/integrity_normalize.rs +7 -0
  84. package/crates/naome-core/src/workflow/integrity_support.rs +110 -0
  85. package/crates/naome-core/src/workflow/mod.rs +18 -0
  86. package/crates/naome-core/src/workflow/mutation.rs +68 -0
  87. package/crates/naome-core/src/workflow/output.rs +111 -0
  88. package/crates/naome-core/src/workflow/phase_inference.rs +73 -0
  89. package/crates/naome-core/src/workflow/phases.rs +169 -0
  90. package/crates/naome-core/src/workflow/policy.rs +156 -0
  91. package/crates/naome-core/src/workflow/processes.rs +91 -0
  92. package/crates/naome-core/src/workflow/types.rs +42 -0
  93. package/crates/naome-core/tests/harness_health.rs +3 -0
  94. package/crates/naome-core/tests/intent.rs +97 -792
  95. package/crates/naome-core/tests/intent_support/mod.rs +133 -0
  96. package/crates/naome-core/tests/intent_v2.rs +90 -0
  97. package/crates/naome-core/tests/quality.rs +425 -0
  98. package/crates/naome-core/tests/route.rs +88 -188
  99. package/crates/naome-core/tests/task_state.rs +3 -0
  100. package/crates/naome-core/tests/task_state_compact.rs +110 -0
  101. package/crates/naome-core/tests/task_state_compact_support/mod.rs +5 -0
  102. package/crates/naome-core/tests/task_state_compact_support/repo.rs +130 -0
  103. package/crates/naome-core/tests/task_state_compact_support/states.rs +151 -0
  104. package/crates/naome-core/tests/workflow_integrity.rs +85 -0
  105. package/crates/naome-core/tests/workflow_policy.rs +139 -0
  106. package/crates/naome-core/tests/workflow_support/mod.rs +194 -0
  107. package/native/darwin-arm64/naome +0 -0
  108. package/native/linux-x64/naome +0 -0
  109. package/package.json +2 -2
  110. package/templates/naome-root/.naome/bin/check-harness-health.js +66 -85
  111. package/templates/naome-root/.naome/bin/check-task-state.js +9 -10
  112. package/templates/naome-root/.naome/bin/naome.js +34 -63
  113. package/templates/naome-root/.naome/manifest.json +20 -18
  114. package/templates/naome-root/.naome/repository-quality-baseline.json +5 -0
  115. package/templates/naome-root/.naome/repository-quality.json +24 -0
  116. package/templates/naome-root/.naome/task-contract.schema.json +93 -11
  117. package/templates/naome-root/.naome/upgrade-state.json +1 -1
  118. package/templates/naome-root/.naome/verification.json +37 -0
  119. package/templates/naome-root/AGENTS.md +3 -0
  120. package/templates/naome-root/docs/naome/agent-workflow.md +25 -12
  121. package/templates/naome-root/docs/naome/execution.md +25 -21
  122. package/templates/naome-root/docs/naome/index.md +4 -3
  123. package/templates/naome-root/docs/naome/repository-quality.md +43 -0
  124. package/templates/naome-root/docs/naome/testing.md +12 -0
  125. package/crates/naome-core/src/task_state.rs +0 -2210
@@ -0,0 +1,55 @@
1
+ pub(crate) type Policy = (&'static str, bool, &'static str, &'static str);
2
+
3
+ pub(crate) fn policy(
4
+ action: &'static str,
5
+ allowed: bool,
6
+ summary: &'static str,
7
+ next_action: &'static str,
8
+ ) -> Policy {
9
+ (action, allowed, summary, next_action)
10
+ }
11
+
12
+ pub(crate) fn harness_refresh_policy() -> Policy {
13
+ policy(
14
+ "auto_commit_harness_refresh_baseline",
15
+ true,
16
+ "The repository has only deterministic harness refresh changes.",
17
+ "Baseline the harness refresh before feature work.",
18
+ )
19
+ }
20
+
21
+ pub(crate) fn ambiguous_empty() -> Policy {
22
+ policy(
23
+ "block_ambiguous_intent",
24
+ false,
25
+ "The prompt is empty, so NAOME cannot infer a safe task transition.",
26
+ "Ask for a concrete request before mutating the repository.",
27
+ )
28
+ }
29
+
30
+ pub(crate) fn ambiguous_conflict() -> Policy {
31
+ policy(
32
+ "block_ambiguous_intent",
33
+ false,
34
+ "The prompt contains conflicting workflow actions.",
35
+ "Ask which workflow action should run before mutating the repository.",
36
+ )
37
+ }
38
+
39
+ pub(crate) fn ambiguous_policy() -> Policy {
40
+ policy(
41
+ "block_ambiguous_intent",
42
+ false,
43
+ "The prompt does not clearly map to a safe NAOME task transition.",
44
+ "Ask for a narrower request or inspect status.",
45
+ )
46
+ }
47
+
48
+ pub(crate) fn unsafe_policy() -> Policy {
49
+ policy(
50
+ "block_unsafe_intent",
51
+ false,
52
+ "The prompt contains risky terms or asks to bypass guardrails.",
53
+ "Ask for a narrower request that does not expose secrets or bypass guardrails.",
54
+ )
55
+ }
@@ -0,0 +1,40 @@
1
+ use super::model::{PromptSegment, RiskContext, SegmentKind};
2
+ use super::patterns::lexical_tokens;
3
+
4
+ pub(crate) fn detect_risk(segments: &[PromptSegment]) -> RiskContext {
5
+ let mut codes = Vec::new();
6
+ for segment in segments
7
+ .iter()
8
+ .filter(|segment| segment.kind == SegmentKind::CommandLike)
9
+ {
10
+ let tokens = lexical_tokens(&segment.text);
11
+ if is_bypass_command(&tokens) {
12
+ codes.push("structured_risk:bypass_command".to_string());
13
+ }
14
+ if is_publish_command(&tokens) {
15
+ codes.push("structured_risk:publish_command".to_string());
16
+ }
17
+ }
18
+
19
+ codes.sort();
20
+ codes.dedup();
21
+ RiskContext {
22
+ has_risky_terms: !codes.is_empty(),
23
+ risk_codes: codes,
24
+ }
25
+ }
26
+
27
+ fn is_bypass_command(tokens: &[String]) -> bool {
28
+ tokens
29
+ .iter()
30
+ .any(|token| matches!(token.as_str(), "--no-verify" | "no-verify"))
31
+ || tokens
32
+ .windows(2)
33
+ .any(|window| window[0] == "git" && window[1] == "push")
34
+ }
35
+
36
+ fn is_publish_command(tokens: &[String]) -> bool {
37
+ tokens
38
+ .windows(2)
39
+ .any(|window| window[0] == "npm" && window[1] == "publish")
40
+ }
@@ -0,0 +1,170 @@
1
+ use super::model::{PromptSegment, SegmentKind};
2
+
3
+ pub(crate) fn segment_prompt(prompt: &str) -> Vec<PromptSegment> {
4
+ let mut segments = Vec::new();
5
+ let mut rest = prompt;
6
+
7
+ loop {
8
+ let Some(start) = rest.find("```") else {
9
+ segment_plain_block(&mut segments, rest);
10
+ break;
11
+ };
12
+
13
+ segment_plain_block(&mut segments, &rest[..start]);
14
+ let after_open = &rest[start + 3..];
15
+ let Some(end) = after_open.find("```") else {
16
+ push_segment(&mut segments, SegmentKind::CodeFence, after_open);
17
+ break;
18
+ };
19
+
20
+ push_segment(
21
+ &mut segments,
22
+ SegmentKind::CodeFence,
23
+ code_fence_body(&after_open[..end]),
24
+ );
25
+ rest = &after_open[end + 3..];
26
+ }
27
+
28
+ segments
29
+ }
30
+
31
+ fn segment_plain_block(segments: &mut Vec<PromptSegment>, text: &str) {
32
+ for line in text.lines() {
33
+ let trimmed = line.trim();
34
+ if !trimmed.is_empty() {
35
+ segment_line(segments, line);
36
+ }
37
+ }
38
+ }
39
+
40
+ fn code_fence_body(text: &str) -> &str {
41
+ if let Some((language, body)) = text.split_once('\n') {
42
+ if language.trim().chars().all(|ch| ch.is_ascii_alphanumeric()) {
43
+ return body;
44
+ }
45
+ }
46
+ text
47
+ }
48
+
49
+ pub(crate) fn actionable_text(segments: &[PromptSegment]) -> String {
50
+ segments
51
+ .iter()
52
+ .filter(|segment| {
53
+ matches!(
54
+ segment.kind,
55
+ SegmentKind::Prose | SegmentKind::ListItem | SegmentKind::CommandLike
56
+ )
57
+ })
58
+ .map(|segment| segment.text.as_str())
59
+ .collect::<Vec<_>>()
60
+ .join(" ")
61
+ }
62
+
63
+ fn segment_line(segments: &mut Vec<PromptSegment>, line: &str) {
64
+ let trimmed = line.trim();
65
+ if trimmed.is_empty() {
66
+ return;
67
+ }
68
+
69
+ let (kind, text) = if let Some(item) = list_item_text(trimmed) {
70
+ (SegmentKind::ListItem, item)
71
+ } else if is_command_like(trimmed) {
72
+ (SegmentKind::CommandLike, trimmed)
73
+ } else if is_path_like(trimmed) {
74
+ (SegmentKind::FilePath, trimmed)
75
+ } else {
76
+ (SegmentKind::Prose, trimmed)
77
+ };
78
+
79
+ segment_inline(segments, kind, text);
80
+ }
81
+
82
+ fn segment_inline(segments: &mut Vec<PromptSegment>, default_kind: SegmentKind, text: &str) {
83
+ let mut rest = text;
84
+ loop {
85
+ let Some(start) = rest.find('`') else {
86
+ segment_quotes(segments, default_kind, rest);
87
+ break;
88
+ };
89
+ segment_quotes(segments, default_kind, &rest[..start]);
90
+ let after_tick = &rest[start + 1..];
91
+ let Some(end) = after_tick.find('`') else {
92
+ segment_quotes(segments, default_kind, rest);
93
+ break;
94
+ };
95
+ push_segment(segments, SegmentKind::InlineCode, &after_tick[..end]);
96
+ rest = &after_tick[end + 1..];
97
+ }
98
+ }
99
+
100
+ fn segment_quotes(segments: &mut Vec<PromptSegment>, default_kind: SegmentKind, text: &str) {
101
+ let mut current = String::new();
102
+ let mut quoted = String::new();
103
+ let mut in_quote = false;
104
+ for ch in text.chars() {
105
+ if matches!(ch, '"' | '\'' | '“' | '”' | '‘' | '’') {
106
+ if in_quote {
107
+ push_segment(segments, SegmentKind::QuotedText, &quoted);
108
+ quoted.clear();
109
+ } else {
110
+ push_segment(segments, default_kind, &current);
111
+ current.clear();
112
+ }
113
+ in_quote = !in_quote;
114
+ } else if in_quote {
115
+ quoted.push(ch);
116
+ } else {
117
+ current.push(ch);
118
+ }
119
+ }
120
+
121
+ let kind = if in_quote {
122
+ default_kind
123
+ } else {
124
+ SegmentKind::QuotedText
125
+ };
126
+ push_segment(segments, kind, &quoted);
127
+ push_segment(segments, default_kind, &current);
128
+ }
129
+
130
+ fn push_segment(segments: &mut Vec<PromptSegment>, kind: SegmentKind, text: &str) {
131
+ let trimmed = text.trim();
132
+ if !trimmed.is_empty() {
133
+ segments.push(PromptSegment {
134
+ kind,
135
+ text: trimmed.to_string(),
136
+ });
137
+ }
138
+ }
139
+
140
+ fn list_item_text(trimmed: &str) -> Option<&str> {
141
+ if let Some(rest) = trimmed
142
+ .strip_prefix("- ")
143
+ .or_else(|| trimmed.strip_prefix("* "))
144
+ {
145
+ return Some(rest.trim());
146
+ }
147
+
148
+ let (number, rest) = trimmed.split_once(". ")?;
149
+ number
150
+ .chars()
151
+ .all(|ch| ch.is_ascii_digit())
152
+ .then_some(rest.trim())
153
+ }
154
+
155
+ fn is_command_like(trimmed: &str) -> bool {
156
+ let first = trimmed.split_whitespace().next().unwrap_or_default();
157
+ matches!(
158
+ first,
159
+ "git" | "npm" | "node" | "cargo" | "naome" | "rg" | "python" | "python3"
160
+ )
161
+ }
162
+
163
+ fn is_path_like(trimmed: &str) -> bool {
164
+ !trimmed.contains(' ')
165
+ && (trimmed.contains('/')
166
+ || trimmed.ends_with(".rs")
167
+ || trimmed.ends_with(".js")
168
+ || trimmed.ends_with(".json")
169
+ || trimmed.ends_with(".md"))
170
+ }