@lamentis/naome 1.0.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 (49) hide show
  1. package/Cargo.lock +199 -0
  2. package/Cargo.toml +11 -0
  3. package/LICENSE +21 -0
  4. package/README.md +6 -0
  5. package/bin/naome-node.js +1424 -0
  6. package/bin/naome.js +129 -0
  7. package/crates/naome-cli/Cargo.toml +14 -0
  8. package/crates/naome-cli/src/main.rs +341 -0
  9. package/crates/naome-core/Cargo.toml +11 -0
  10. package/crates/naome-core/src/decision.rs +432 -0
  11. package/crates/naome-core/src/git.rs +70 -0
  12. package/crates/naome-core/src/harness_health.rs +557 -0
  13. package/crates/naome-core/src/install_plan.rs +82 -0
  14. package/crates/naome-core/src/lib.rs +17 -0
  15. package/crates/naome-core/src/models.rs +99 -0
  16. package/crates/naome-core/src/paths.rs +72 -0
  17. package/crates/naome-core/src/task_state.rs +1859 -0
  18. package/crates/naome-core/src/verification.rs +217 -0
  19. package/crates/naome-core/src/verification_contract.rs +406 -0
  20. package/crates/naome-core/tests/decision.rs +297 -0
  21. package/crates/naome-core/tests/harness_health.rs +232 -0
  22. package/crates/naome-core/tests/install_plan.rs +35 -0
  23. package/crates/naome-core/tests/task_state.rs +588 -0
  24. package/crates/naome-core/tests/verification.rs +165 -0
  25. package/crates/naome-core/tests/verification_contract.rs +181 -0
  26. package/native/darwin-arm64/naome +0 -0
  27. package/package.json +44 -0
  28. package/templates/naome-root/.naome/bin/check-harness-health.js +163 -0
  29. package/templates/naome-root/.naome/bin/check-task-state.js +180 -0
  30. package/templates/naome-root/.naome/bin/naome.js +306 -0
  31. package/templates/naome-root/.naome/init-state.json +13 -0
  32. package/templates/naome-root/.naome/manifest.json +45 -0
  33. package/templates/naome-root/.naome/package.json +3 -0
  34. package/templates/naome-root/.naome/task-contract.schema.json +174 -0
  35. package/templates/naome-root/.naome/task-state.json +8 -0
  36. package/templates/naome-root/.naome/upgrade-state.json +7 -0
  37. package/templates/naome-root/.naome/verification.json +45 -0
  38. package/templates/naome-root/.naomeignore +4 -0
  39. package/templates/naome-root/AGENTS.md +77 -0
  40. package/templates/naome-root/docs/naome/agent-workflow.md +82 -0
  41. package/templates/naome-root/docs/naome/architecture.md +37 -0
  42. package/templates/naome-root/docs/naome/decisions.md +18 -0
  43. package/templates/naome-root/docs/naome/execution.md +192 -0
  44. package/templates/naome-root/docs/naome/first-run.md +135 -0
  45. package/templates/naome-root/docs/naome/index.md +67 -0
  46. package/templates/naome-root/docs/naome/repo-profile.md +51 -0
  47. package/templates/naome-root/docs/naome/security.md +60 -0
  48. package/templates/naome-root/docs/naome/testing.md +51 -0
  49. package/templates/naome-root/docs/naome/upgrade.md +20 -0
package/bin/naome.js ADDED
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync } from "node:fs";
4
+ import { spawnSync } from "node:child_process";
5
+ import { dirname, join, resolve } from "node:path";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
9
+ const nativeBinaryName = process.platform === "win32" ? "naome.exe" : "naome";
10
+ const args = process.argv.slice(2);
11
+ const [command] = args;
12
+
13
+ if (isHelpRequest(args)) {
14
+ printHelp();
15
+ process.exit(0);
16
+ }
17
+
18
+ if (command === "install" || command === "sync") {
19
+ runNativePackageCommand(args);
20
+ }
21
+
22
+ const commandPath = findHarnessCommand(process.cwd());
23
+
24
+ if (!commandPath) {
25
+ console.error("NAOME: installed harness command not found. Run this inside a repository with .naome/bin/naome.js.");
26
+ process.exit(1);
27
+ }
28
+
29
+ const result = spawnSync(process.execPath, [commandPath, ...process.argv.slice(2)], {
30
+ cwd: process.cwd(),
31
+ encoding: "utf8",
32
+ stdio: "inherit",
33
+ });
34
+
35
+ process.exit(result.status === null ? 1 : result.status);
36
+
37
+ function isHelpRequest(args) {
38
+ return ["help", "--help", "-h"].includes(args[0]) || ["help", "--help", "-h"].includes(args[1]);
39
+ }
40
+
41
+ function printHelp() {
42
+ console.log("Usage:");
43
+ console.log(" naome status [--json]");
44
+ console.log(" naome next [--json]");
45
+ console.log(" naome install");
46
+ console.log(" naome sync");
47
+ console.log(" naome commit -m \"type(scope): message\"");
48
+ }
49
+
50
+ function runNativePackageCommand(args) {
51
+ const nativeBinary = resolveNativePackageBinary();
52
+ if (!nativeBinary) {
53
+ console.error("NAOME: native CLI is unavailable.");
54
+ console.error("Install Cargo or provide NAOME_NATIVE_BIN with a built naome binary.");
55
+ process.exit(1);
56
+ }
57
+
58
+ const result = spawnSync(nativeBinary, [
59
+ ...args,
60
+ "--package-root",
61
+ packageRoot,
62
+ "--installer-js",
63
+ join(packageRoot, "bin", "naome-node.js")
64
+ ], {
65
+ cwd: process.cwd(),
66
+ encoding: "utf8",
67
+ env: {
68
+ ...process.env,
69
+ NAOME_NODE_BIN: process.execPath
70
+ },
71
+ stdio: "inherit"
72
+ });
73
+
74
+ process.exit(result.status === null ? 1 : result.status);
75
+ }
76
+
77
+ function resolveNativePackageBinary() {
78
+ const candidates = [
79
+ process.env.NAOME_NATIVE_BIN && resolve(process.cwd(), process.env.NAOME_NATIVE_BIN),
80
+ join(packageRoot, "native", `${process.platform}-${process.arch}`, nativeBinaryName),
81
+ join(packageRoot, "target", "release", nativeBinaryName)
82
+ ].filter(Boolean);
83
+
84
+ for (const candidate of candidates) {
85
+ if (existsSync(candidate)) {
86
+ return candidate;
87
+ }
88
+ }
89
+
90
+ const built = buildNativePackageBinary();
91
+ return built && existsSync(built) ? built : null;
92
+ }
93
+
94
+ function buildNativePackageBinary() {
95
+ const manifestPath = join(packageRoot, "Cargo.toml");
96
+ if (!existsSync(manifestPath)) {
97
+ return null;
98
+ }
99
+
100
+ const result = spawnSync("cargo", ["build", "--release", "--manifest-path", manifestPath, "-p", "naome-cli"], {
101
+ cwd: packageRoot,
102
+ encoding: "utf8",
103
+ stdio: "inherit"
104
+ });
105
+
106
+ if (result.status !== 0) {
107
+ return null;
108
+ }
109
+
110
+ return join(packageRoot, "target", "release", nativeBinaryName);
111
+ }
112
+
113
+ function findHarnessCommand(startPath) {
114
+ let current = resolve(startPath);
115
+
116
+ while (true) {
117
+ const candidate = join(current, ".naome", "bin", "naome.js");
118
+ if (existsSync(candidate)) {
119
+ return candidate;
120
+ }
121
+
122
+ const parent = dirname(current);
123
+ if (parent === current) {
124
+ return null;
125
+ }
126
+
127
+ current = parent;
128
+ }
129
+ }
@@ -0,0 +1,14 @@
1
+ [package]
2
+ name = "naome-cli"
3
+ version = "1.0.0"
4
+ edition.workspace = true
5
+ license.workspace = true
6
+ repository.workspace = true
7
+
8
+ [[bin]]
9
+ name = "naome"
10
+ path = "src/main.rs"
11
+
12
+ [dependencies]
13
+ naome-core = { path = "../naome-core" }
14
+ serde_json = "1"
@@ -0,0 +1,341 @@
1
+ use std::collections::HashMap;
2
+ use std::path::{Path, PathBuf};
3
+ use std::process::Command;
4
+
5
+ use naome_core::{
6
+ evaluate_decision, format_decision, install_plan, seed_builtin_verification_checks,
7
+ validate_harness_health, validate_task_state, validate_verification_contract,
8
+ EvaluationOptions, HarnessHealthOptions, TaskStateMode, TaskStateOptions,
9
+ };
10
+
11
+ fn main() {
12
+ if let Err(error) = run() {
13
+ eprintln!("NAOME: {error}");
14
+ std::process::exit(1);
15
+ }
16
+ }
17
+
18
+ fn run() -> Result<(), Box<dyn std::error::Error>> {
19
+ let args: Vec<String> = std::env::args().skip(1).collect();
20
+ let Some(command) = args.first().map(String::as_str) else {
21
+ print_help();
22
+ return Ok(());
23
+ };
24
+
25
+ if is_help_request(&args) {
26
+ print_help();
27
+ return Ok(());
28
+ }
29
+
30
+ if command != "status"
31
+ && command != "next"
32
+ && command != "seed-verification"
33
+ && command != "install-plan"
34
+ && command != "install"
35
+ && command != "sync"
36
+ && command != "check-harness-health"
37
+ && command != "check-task-state"
38
+ && command != "validate-verification"
39
+ {
40
+ print_help();
41
+ return Err(format!("unknown command: {command}").into());
42
+ }
43
+
44
+ let root = if command == "install-plan" || command == "install" || command == "sync" {
45
+ std::env::current_dir()?
46
+ } else if command == "validate-verification"
47
+ || command == "check-harness-health"
48
+ || command == "check-task-state"
49
+ {
50
+ option_value(&args, "--root")
51
+ .map(PathBuf::from)
52
+ .or_else(|| find_manifest_root(&std::env::current_dir().ok()?))
53
+ .unwrap_or(std::env::current_dir()?)
54
+ } else {
55
+ find_harness_root(&std::env::current_dir()?).ok_or(
56
+ "NAOME harness root not found. Run this inside a repository with .naome/task-state.json.",
57
+ )?
58
+ };
59
+
60
+ if command == "install" || command == "sync" {
61
+ run_install_bridge(command, &args)?;
62
+ return Ok(());
63
+ }
64
+
65
+ if command == "install-plan" {
66
+ let harness_version = option_value(&args, "--harness-version")
67
+ .or_else(|| option_value(&args, "--version"))
68
+ .unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string());
69
+ println!(
70
+ "{}",
71
+ serde_json::to_string_pretty(&install_plan(harness_version))?
72
+ );
73
+ return Ok(());
74
+ }
75
+
76
+ if command == "seed-verification" {
77
+ if seed_builtin_verification_checks(&root)? {
78
+ println!("NAOME verification checks updated.");
79
+ } else {
80
+ println!("NAOME verification checks already present.");
81
+ }
82
+ return Ok(());
83
+ }
84
+
85
+ if command == "check-harness-health" {
86
+ let options = HarnessHealthOptions {
87
+ expected_integrity: expected_integrity_from_env()?,
88
+ allow_missing_integrity: std::env::var("NAOME_ALLOW_MISSING_INTEGRITY")
89
+ .is_ok_and(|value| value == "1"),
90
+ allow_missing_archive: args.iter().any(|arg| arg == "--allow-missing-archive"),
91
+ };
92
+ let errors = validate_harness_health(&root, options)?;
93
+ if errors.is_empty() {
94
+ println!("NAOME harness health OK.");
95
+ return Ok(());
96
+ }
97
+
98
+ eprintln!("NAOME harness health check failed.");
99
+ for error in errors {
100
+ eprintln!("- {error}");
101
+ }
102
+ std::process::exit(1);
103
+ }
104
+
105
+ if command == "check-task-state" {
106
+ let mode = if args.iter().any(|arg| arg == "--admission") {
107
+ TaskStateMode::Admission
108
+ } else if args.iter().any(|arg| arg == "--progress") {
109
+ TaskStateMode::Progress
110
+ } else if args.iter().any(|arg| arg == "--commit-gate") {
111
+ TaskStateMode::CommitGate
112
+ } else if args.iter().any(|arg| arg == "--push-gate") {
113
+ TaskStateMode::PushGate
114
+ } else {
115
+ TaskStateMode::State
116
+ };
117
+ let expected_integrity = expected_integrity_from_env()?;
118
+ let harness_health = if expected_integrity.is_empty()
119
+ && std::env::var("NAOME_ALLOW_MISSING_INTEGRITY").is_err()
120
+ {
121
+ None
122
+ } else {
123
+ Some(HarnessHealthOptions {
124
+ expected_integrity,
125
+ allow_missing_integrity: std::env::var("NAOME_ALLOW_MISSING_INTEGRITY")
126
+ .is_ok_and(|value| value == "1"),
127
+ allow_missing_archive: args.iter().any(|arg| arg == "--allow-missing-archive"),
128
+ })
129
+ };
130
+ let report = validate_task_state(
131
+ &root,
132
+ TaskStateOptions {
133
+ mode,
134
+ harness_health,
135
+ },
136
+ )?;
137
+ if report.errors.is_empty() {
138
+ println!("{}", task_state_success_message(mode));
139
+ for notice in report.notices {
140
+ println!("- {notice}");
141
+ }
142
+ return Ok(());
143
+ }
144
+
145
+ eprintln!("{}", task_state_failure_message(mode));
146
+ for error in report.errors {
147
+ eprintln!("- {error}");
148
+ }
149
+ std::process::exit(1);
150
+ }
151
+
152
+ if command == "validate-verification" {
153
+ let errors = validate_verification_contract(&root)?;
154
+ if errors.is_empty() {
155
+ println!("NAOME verification contract OK.");
156
+ return Ok(());
157
+ }
158
+
159
+ eprintln!("NAOME verification contract failed.");
160
+ for error in errors {
161
+ eprintln!("- {error}");
162
+ }
163
+ std::process::exit(1);
164
+ }
165
+
166
+ let decision = evaluate_decision(&root, EvaluationOptions::online())?;
167
+
168
+ if args.iter().any(|arg| arg == "--json") {
169
+ println!("{}", serde_json::to_string_pretty(&decision)?);
170
+ } else {
171
+ print!("{}", format_decision(&decision, command));
172
+ }
173
+
174
+ Ok(())
175
+ }
176
+
177
+ fn is_help_request(args: &[String]) -> bool {
178
+ matches!(args.first().map(String::as_str), Some("help" | "--help" | "-h"))
179
+ || args
180
+ .get(1)
181
+ .is_some_and(|arg| arg == "--help" || arg == "-h" || arg == "help")
182
+ }
183
+
184
+ fn find_harness_root(start: &Path) -> Option<PathBuf> {
185
+ let mut current = start.to_path_buf();
186
+ loop {
187
+ if current.join(".naome").join("task-state.json").is_file() {
188
+ return Some(current);
189
+ }
190
+
191
+ if !current.pop() {
192
+ return None;
193
+ }
194
+ }
195
+ }
196
+
197
+ fn find_manifest_root(start: &Path) -> Option<PathBuf> {
198
+ let mut current = start.to_path_buf();
199
+ loop {
200
+ if current.join(".naome").join("manifest.json").is_file() {
201
+ return Some(current);
202
+ }
203
+
204
+ if !current.pop() {
205
+ return None;
206
+ }
207
+ }
208
+ }
209
+
210
+ fn print_help() {
211
+ println!("Usage:");
212
+ println!(" naome status [--json]");
213
+ println!(" naome next [--json]");
214
+ println!(" naome install [--package-root <path>] [--installer-js <path>]");
215
+ println!(" naome sync [--package-root <path>] [--installer-js <path>]");
216
+ println!(" naome install-plan [--harness-version <version>]");
217
+ println!(" naome seed-verification");
218
+ println!(" naome check-harness-health [--root <path>] [--allow-missing-archive]");
219
+ println!(" naome check-task-state [--root <path>] [--admission|--progress|--commit-gate|--push-gate] [--allow-missing-archive]");
220
+ println!(" naome validate-verification [--root <path>]");
221
+ }
222
+
223
+ fn option_value(args: &[String], option: &str) -> Option<String> {
224
+ args.windows(2)
225
+ .find(|window| window[0] == option)
226
+ .map(|window| window[1].clone())
227
+ }
228
+
229
+ fn run_install_bridge(command: &str, args: &[String]) -> Result<(), Box<dyn std::error::Error>> {
230
+ let package_root = option_value(args, "--package-root")
231
+ .map(PathBuf::from)
232
+ .or_else(|| std::env::var("NAOME_PACKAGE_ROOT").ok().map(PathBuf::from))
233
+ .or_else(resolve_package_root_from_exe)
234
+ .or_else(resolve_package_root_from_cwd);
235
+ let installer_js = option_value(args, "--installer-js")
236
+ .map(PathBuf::from)
237
+ .or_else(|| std::env::var("NAOME_INSTALLER_JS").ok().map(PathBuf::from))
238
+ .or_else(|| {
239
+ package_root
240
+ .as_ref()
241
+ .map(|root| root.join("bin").join("naome-node.js"))
242
+ });
243
+ let Some(installer_js) = installer_js.filter(|path| path.is_file()) else {
244
+ return Err(format!(
245
+ "native {command} needs naome-node.js. Install the naome npm package, or pass --package-root/--installer-js."
246
+ )
247
+ .into());
248
+ };
249
+
250
+ let node_bin = std::env::var("NAOME_NODE_BIN").unwrap_or_else(|_| "node".to_string());
251
+ let pass_through_args = strip_install_bridge_options(args);
252
+ let status = Command::new(node_bin)
253
+ .arg(installer_js)
254
+ .args(pass_through_args)
255
+ .status()?;
256
+
257
+ if status.success() {
258
+ return Ok(());
259
+ }
260
+
261
+ std::process::exit(status.code().unwrap_or(1));
262
+ }
263
+
264
+ fn strip_install_bridge_options(args: &[String]) -> Vec<String> {
265
+ let mut result = Vec::new();
266
+ let mut index = 1;
267
+ while index < args.len() {
268
+ let arg = &args[index];
269
+ if arg == "--package-root" || arg == "--installer-js" {
270
+ index += 2;
271
+ continue;
272
+ }
273
+
274
+ if arg.starts_with("--package-root=") || arg.starts_with("--installer-js=") {
275
+ index += 1;
276
+ continue;
277
+ }
278
+
279
+ result.push(arg.clone());
280
+ index += 1;
281
+ }
282
+ result
283
+ }
284
+
285
+ fn resolve_package_root_from_exe() -> Option<PathBuf> {
286
+ let exe = std::env::current_exe().ok()?;
287
+ let platform_dir = exe.parent()?;
288
+ let native_dir = platform_dir.parent()?;
289
+ if native_dir.file_name().and_then(|value| value.to_str()) == Some("native") {
290
+ let package_root = native_dir.parent()?.to_path_buf();
291
+ if package_root
292
+ .join("bin")
293
+ .join("naome-node.js")
294
+ .is_file()
295
+ {
296
+ return Some(package_root);
297
+ }
298
+ }
299
+ None
300
+ }
301
+
302
+ fn resolve_package_root_from_cwd() -> Option<PathBuf> {
303
+ let current = std::env::current_dir().ok()?;
304
+ for candidate in [
305
+ current.join("packages").join("naome"),
306
+ current.clone(),
307
+ ] {
308
+ if candidate.join("bin").join("naome-node.js").is_file() {
309
+ return Some(candidate);
310
+ }
311
+ }
312
+ None
313
+ }
314
+
315
+ fn expected_integrity_from_env() -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
316
+ let Ok(value) = std::env::var("NAOME_EXPECTED_INTEGRITY_JSON") else {
317
+ return Ok(HashMap::new());
318
+ };
319
+
320
+ Ok(serde_json::from_str(&value)?)
321
+ }
322
+
323
+ fn task_state_failure_message(mode: TaskStateMode) -> &'static str {
324
+ match mode {
325
+ TaskStateMode::Admission => "NAOME task admission check failed.",
326
+ TaskStateMode::Progress => "NAOME task progress check failed.",
327
+ TaskStateMode::CommitGate => "NAOME commit gate failed.",
328
+ TaskStateMode::PushGate => "NAOME push gate failed.",
329
+ TaskStateMode::State => "NAOME task state check failed.",
330
+ }
331
+ }
332
+
333
+ fn task_state_success_message(mode: TaskStateMode) -> &'static str {
334
+ match mode {
335
+ TaskStateMode::Admission => "NAOME task admission OK.",
336
+ TaskStateMode::Progress => "NAOME task progress OK.",
337
+ TaskStateMode::CommitGate => "NAOME commit gate OK.",
338
+ TaskStateMode::PushGate => "NAOME push gate OK.",
339
+ TaskStateMode::State => "NAOME task state OK.",
340
+ }
341
+ }
@@ -0,0 +1,11 @@
1
+ [package]
2
+ name = "naome-core"
3
+ version = "1.0.0"
4
+ edition.workspace = true
5
+ license.workspace = true
6
+ repository.workspace = true
7
+
8
+ [dependencies]
9
+ serde = { version = "1", features = ["derive"] }
10
+ serde_json = "1"
11
+ sha2 = "0.10"