@lamentis/naome 1.3.0 → 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 (137) 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/journal.rs +2 -7
  23. package/crates/naome-core/src/lib.rs +33 -10
  24. package/crates/naome-core/src/quality/adapter_ios.rs +131 -0
  25. package/crates/naome-core/src/quality/adapter_support.rs +67 -0
  26. package/crates/naome-core/src/quality/adapters.rs +81 -18
  27. package/crates/naome-core/src/quality/cache.rs +7 -9
  28. package/crates/naome-core/src/quality/checks/duplicate_blocks.rs +4 -7
  29. package/crates/naome-core/src/quality/config.rs +21 -3
  30. package/crates/naome-core/src/quality/mod.rs +138 -7
  31. package/crates/naome-core/src/quality/reconcile.rs +138 -0
  32. package/crates/naome-core/src/quality/reconcile_anchors.rs +64 -0
  33. package/crates/naome-core/src/quality/scanner/analysis.rs +20 -5
  34. package/crates/naome-core/src/quality/scanner.rs +62 -17
  35. package/crates/naome-core/src/quality/semantic/checks.rs +17 -0
  36. package/crates/naome-core/src/quality/semantic/route.rs +1 -1
  37. package/crates/naome-core/src/quality/structure/adapter_ios.rs +149 -0
  38. package/crates/naome-core/src/quality/structure/adapters.rs +60 -42
  39. package/crates/naome-core/src/quality/structure/checks/directory.rs +6 -4
  40. package/crates/naome-core/src/quality/structure/classify/roles.rs +51 -5
  41. package/crates/naome-core/src/quality/structure/config.rs +24 -3
  42. package/crates/naome-core/src/quality/structure/mod.rs +3 -0
  43. package/crates/naome-core/src/quality/types.rs +20 -1
  44. package/crates/naome-core/src/repository_model/detect.rs +188 -0
  45. package/crates/naome-core/src/repository_model/explain.rs +121 -0
  46. package/crates/naome-core/src/repository_model/path_scan.rs +67 -0
  47. package/crates/naome-core/src/repository_model/path_support.rs +59 -0
  48. package/crates/naome-core/src/repository_model/types.rs +152 -0
  49. package/crates/naome-core/src/repository_model/world.rs +48 -0
  50. package/crates/naome-core/src/repository_model/world_adapters.rs +145 -0
  51. package/crates/naome-core/src/repository_model/world_path_facts.rs +55 -0
  52. package/crates/naome-core/src/repository_model/world_paths.rs +168 -0
  53. package/crates/naome-core/src/repository_model.rs +164 -0
  54. package/crates/naome-core/src/route/builtin_checks.rs +40 -1
  55. package/crates/naome-core/src/task_ledger/import.rs +142 -0
  56. package/crates/naome-core/src/task_ledger/model.rs +13 -0
  57. package/crates/naome-core/src/task_ledger/proof_record.rs +52 -0
  58. package/crates/naome-core/src/task_ledger/read.rs +118 -0
  59. package/crates/naome-core/src/task_ledger/render.rs +55 -0
  60. package/crates/naome-core/src/task_ledger/write.rs +38 -0
  61. package/crates/naome-core/src/task_ledger.rs +48 -0
  62. package/crates/naome-core/src/task_state/api.rs +4 -2
  63. package/crates/naome-core/src/task_state/completed_refresh.rs +5 -16
  64. package/crates/naome-core/src/task_state/diff.rs +2 -2
  65. package/crates/naome-core/src/task_state/evidence.rs +8 -3
  66. package/crates/naome-core/src/task_state/mod.rs +1 -1
  67. package/crates/naome-core/src/task_state/progress.rs +13 -0
  68. package/crates/naome-core/src/task_state/proof_model.rs +8 -8
  69. package/crates/naome-core/src/task_state/repair.rs +2 -2
  70. package/crates/naome-core/src/task_state/task_diff_api.rs +9 -18
  71. package/crates/naome-core/src/task_state/types.rs +24 -0
  72. package/crates/naome-core/src/verification.rs +29 -18
  73. package/crates/naome-core/src/workflow/agent/capability.rs +194 -0
  74. package/crates/naome-core/src/workflow/agent/context_delta.rs +42 -0
  75. package/crates/naome-core/src/workflow/agent/decision.rs +32 -0
  76. package/crates/naome-core/src/workflow/agent/execution.rs +80 -0
  77. package/crates/naome-core/src/workflow/agent/proof.rs +24 -0
  78. package/crates/naome-core/src/workflow/agent/support.rs +58 -0
  79. package/crates/naome-core/src/workflow/agent/watchdog.rs +47 -0
  80. package/crates/naome-core/src/workflow/agent.rs +34 -0
  81. package/crates/naome-core/src/workflow/agent_types.rs +105 -0
  82. package/crates/naome-core/src/workflow/doctor.rs +39 -0
  83. package/crates/naome-core/src/workflow/mod.rs +11 -0
  84. package/crates/naome-core/src/workflow/output.rs +8 -2
  85. package/crates/naome-core/src/workflow/phase_inference.rs +1 -1
  86. package/crates/naome-core/tests/context.rs +99 -0
  87. package/crates/naome-core/tests/harness_health.rs +4 -0
  88. package/crates/naome-core/tests/install_plan.rs +12 -0
  89. package/crates/naome-core/tests/quality.rs +178 -2
  90. package/crates/naome-core/tests/quality_performance.rs +39 -2
  91. package/crates/naome-core/tests/quality_structure_adapters.rs +39 -0
  92. package/crates/naome-core/tests/repo_support/mod.rs +5 -1
  93. package/crates/naome-core/tests/repo_support/verification_values.rs +55 -0
  94. package/crates/naome-core/tests/repository_model.rs +281 -0
  95. package/crates/naome-core/tests/route_user_diff.rs +49 -1
  96. package/crates/naome-core/tests/semantic_legacy.rs +72 -38
  97. package/crates/naome-core/tests/task_ledger.rs +328 -0
  98. package/crates/naome-core/tests/task_state.rs +28 -0
  99. package/crates/naome-core/tests/verification.rs +29 -36
  100. package/crates/naome-core/tests/workflow_agent.rs +233 -0
  101. package/crates/naome-core/tests/workflow_agent_support/mod.rs +159 -0
  102. package/crates/naome-core/tests/workflow_doctor.rs +21 -0
  103. package/installer/codex-hooks.js +121 -0
  104. package/installer/context.js +10 -0
  105. package/installer/filesystem.js +4 -0
  106. package/installer/flows.js +8 -4
  107. package/installer/harness-files.js +6 -0
  108. package/installer/install-plan.js +4 -0
  109. package/installer/main.js +1 -1
  110. package/installer/native.js +1 -1
  111. package/native/darwin-arm64/naome +0 -0
  112. package/native/linux-x64/naome +0 -0
  113. package/package.json +1 -1
  114. package/templates/naome-root/.codex/config.toml +2 -0
  115. package/templates/naome-root/.codex/hooks.json +70 -0
  116. package/templates/naome-root/.naome/bin/check-harness-health.js +8 -6
  117. package/templates/naome-root/.naome/bin/check-task-state.js +12 -7
  118. package/templates/naome-root/.naome/bin/codex-hook-io.js +122 -0
  119. package/templates/naome-root/.naome/bin/codex-hook-policy.js +180 -0
  120. package/templates/naome-root/.naome/bin/codex-hook-runtime.js +174 -0
  121. package/templates/naome-root/.naome/bin/codex-hook.js +6 -0
  122. package/templates/naome-root/.naome/bin/naome.js +35 -4
  123. package/templates/naome-root/.naome/manifest.json +12 -6
  124. package/templates/naome-root/.naome/repository-model.json +6 -0
  125. package/templates/naome-root/.naome/repository-quality.json +3 -1
  126. package/templates/naome-root/.naome/verification.json +15 -1
  127. package/templates/naome-root/AGENTS.md +38 -83
  128. package/templates/naome-root/docs/naome/agent-workflow.md +54 -18
  129. package/templates/naome-root/docs/naome/codex-hooks.md +82 -0
  130. package/templates/naome-root/docs/naome/context-economy.md +73 -0
  131. package/templates/naome-root/docs/naome/first-run.md +25 -14
  132. package/templates/naome-root/docs/naome/index.md +18 -10
  133. package/templates/naome-root/docs/naome/repository-model.md +92 -0
  134. package/templates/naome-root/docs/naome/repository-quality.md +47 -7
  135. package/templates/naome-root/docs/naome/repository-structure.md +10 -3
  136. package/templates/naome-root/docs/naome/task-ledger.md +71 -0
  137. package/templates/naome-root/docs/naome/testing.md +16 -3
@@ -0,0 +1,159 @@
1
+ use std::fs;
2
+ use std::path::{Path, PathBuf};
3
+ use std::process::Command;
4
+ use std::sync::atomic::{AtomicU64, Ordering};
5
+ use std::time::{SystemTime, UNIX_EPOCH};
6
+
7
+ use serde_json::json;
8
+
9
+ pub struct Repo {
10
+ root: PathBuf,
11
+ }
12
+
13
+ impl Repo {
14
+ pub fn new(name: &str) -> Self {
15
+ let nonce = SystemTime::now()
16
+ .duration_since(UNIX_EPOCH)
17
+ .unwrap()
18
+ .as_nanos();
19
+ let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
20
+ let root =
21
+ std::env::temp_dir().join(format!("naome-workflow-agent-{name}-{nonce}-{counter}"));
22
+ fs::create_dir_all(root.join(".naome")).unwrap();
23
+ Self { root }
24
+ }
25
+
26
+ pub fn path(&self) -> &Path {
27
+ &self.root
28
+ }
29
+
30
+ pub fn write_harness_state(&self, status: &str, allowed_paths: &[&str], checks: &[&str]) {
31
+ self.write_file(".naomeignore", ".naome/archive/\n");
32
+ self.write_json(".naome/init-state.json", json!({ "initialized": true }));
33
+ self.write_json(".naome/upgrade-state.json", json!({ "status": "complete" }));
34
+ self.write_json(
35
+ ".naome/task-state.json",
36
+ task_state(status, allowed_paths, checks),
37
+ );
38
+ self.write_file("docs/naome/testing.md", "# NAOME Testing\n");
39
+ }
40
+
41
+ pub fn write_verification(&self, checks: &[(&str, &str, &str)]) {
42
+ self.write_json(
43
+ ".naome/verification.json",
44
+ json!({
45
+ "schema": "naome.verification.v1",
46
+ "version": 1,
47
+ "status": "ready",
48
+ "checks": checks.iter().map(|(id, command, cost)| json!({
49
+ "id": id,
50
+ "command": command,
51
+ "cwd": ".",
52
+ "purpose": "test check",
53
+ "cost": cost,
54
+ "source": "test",
55
+ "evidence": ["src/lib.rs"],
56
+ "lastVerified": null
57
+ })).collect::<Vec<_>>(),
58
+ "changeTypes": [],
59
+ "releaseGates": []
60
+ }),
61
+ );
62
+ }
63
+
64
+ pub fn write_phased_verification(&self) {
65
+ self.write_verification(&[
66
+ (
67
+ "shape",
68
+ "node .naome/bin/check-task-state.js --progress",
69
+ "fast",
70
+ ),
71
+ ("unit", "cargo test", "fast"),
72
+ ("pack", "npm run pack:dry-run", "slow"),
73
+ ]);
74
+ let mut verification: serde_json::Value = serde_json::from_str(
75
+ &fs::read_to_string(self.root.join(".naome/verification.json")).unwrap(),
76
+ )
77
+ .unwrap();
78
+ verification["phases"] = json!([
79
+ { "id": "shape", "order": 1, "checkIds": ["shape"] },
80
+ { "id": "focused-tests", "order": 2, "checkIds": ["unit"] },
81
+ { "id": "package-release", "order": 3, "checkIds": ["pack"] }
82
+ ]);
83
+ self.write_json(".naome/verification.json", verification);
84
+ }
85
+
86
+ pub fn init_git(&self) {
87
+ self.git(&["init"]);
88
+ self.git(&["config", "user.email", "naome@example.com"]);
89
+ self.git(&["config", "user.name", "NAOME Test"]);
90
+ self.git(&["add", "."]);
91
+ self.git(&["commit", "-m", "baseline"]);
92
+ }
93
+
94
+ pub fn write_file(&self, relative_path: &str, content: &str) {
95
+ let path = self.root.join(relative_path);
96
+ fs::create_dir_all(path.parent().unwrap()).unwrap();
97
+ fs::write(path, content).unwrap();
98
+ }
99
+
100
+ fn git(&self, args: &[&str]) {
101
+ let output = Command::new("git")
102
+ .args(args)
103
+ .current_dir(&self.root)
104
+ .output()
105
+ .unwrap();
106
+ assert!(
107
+ output.status.success(),
108
+ "git {:?} failed\nstdout:\n{}\nstderr:\n{}",
109
+ args,
110
+ String::from_utf8_lossy(&output.stdout),
111
+ String::from_utf8_lossy(&output.stderr)
112
+ );
113
+ }
114
+
115
+ fn write_json(&self, relative_path: &str, value: serde_json::Value) {
116
+ self.write_file(
117
+ relative_path,
118
+ &format!("{}\n", serde_json::to_string_pretty(&value).unwrap()),
119
+ );
120
+ }
121
+ }
122
+
123
+ fn task_state(status: &str, allowed_paths: &[&str], checks: &[&str]) -> serde_json::Value {
124
+ json!({
125
+ "schema": "naome.task-state.v1",
126
+ "version": 1,
127
+ "status": status,
128
+ "activeTask": {
129
+ "id": "demo-task",
130
+ "request": "Update demo.",
131
+ "userPrompt": {
132
+ "receivedAt": "2026-05-08T00:00:00.000Z",
133
+ "text": "Update demo."
134
+ },
135
+ "admission": {
136
+ "command": "node .naome/bin/check-task-state.js --admission",
137
+ "cwd": ".",
138
+ "exitCode": 0,
139
+ "checkedAt": "2026-05-08T00:00:00.000Z",
140
+ "gitHead": "",
141
+ "changedPaths": []
142
+ },
143
+ "allowedPaths": allowed_paths,
144
+ "declaredChangeTypes": ["harness-core"],
145
+ "requiredCheckIds": checks,
146
+ "proofResults": [],
147
+ "revisions": [],
148
+ "humanReview": {
149
+ "required": false,
150
+ "approved": false,
151
+ "reason": null
152
+ }
153
+ },
154
+ "blocker": null,
155
+ "updatedAt": "2026-05-08T00:00:00.000Z"
156
+ })
157
+ }
158
+
159
+ static COUNTER: AtomicU64 = AtomicU64::new(0);
@@ -22,3 +22,24 @@ fn doctor_report_explains_active_task_and_policy_files() {
22
22
  .next_action
23
23
  .contains("Finish or resolve the active task"));
24
24
  }
25
+
26
+ #[test]
27
+ fn doctor_report_marks_stale_repository_model() {
28
+ let repo = WorkflowFixture::new("doctor-repository-model-stale");
29
+ repo.init_git();
30
+ repo.write(".naome/repository-quality.json", "{}\n");
31
+ repo.write(".naome/repository-structure.json", "{}\n");
32
+ repo.write(".naome/repository-model.json", "{\"schema\":\"naome.repository-model.v1\",\"version\":1,\"status\":\"ready\",\"facts\":[]}\n");
33
+ repo.write("Package.swift", "// swift-tools-version: 5.9\n");
34
+
35
+ let report = doctor_report(repo.path()).unwrap();
36
+
37
+ assert!(!report.ok);
38
+ assert_eq!(report.repository_model.config_present, true);
39
+ assert_eq!(report.repository_model.status, "stale");
40
+ assert!(report
41
+ .repository_model
42
+ .messages
43
+ .iter()
44
+ .any(|message| message.contains("naome repo model --write")));
45
+ }
@@ -0,0 +1,121 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { stdin as input, stdout as output } from "node:process";
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+
6
+ import { replaceHarnessFile } from "./harness-file-ops.js";
7
+ import { printSection } from "./output.js";
8
+
9
+ const HOOK_CONFIG_PATH = ".codex/hooks.json";
10
+ const HOOK_FEATURE_CONFIG_PATH = ".codex/config.toml";
11
+ const HOOK_DISPATCHER_PATH = ".naome/bin/codex-hook.js";
12
+ const HOOK_IO_PATH = ".naome/bin/codex-hook-io.js";
13
+ const HOOK_POLICY_PATH = ".naome/bin/codex-hook-policy.js";
14
+ const HOOK_RUNTIME_PATH = ".naome/bin/codex-hook-runtime.js";
15
+ const HOOK_DOC_PATH = "docs/naome/codex-hooks.md";
16
+
17
+ const CODEX_HOOK_MACHINE_PATHS = [
18
+ HOOK_FEATURE_CONFIG_PATH,
19
+ HOOK_CONFIG_PATH,
20
+ HOOK_DISPATCHER_PATH,
21
+ HOOK_IO_PATH,
22
+ HOOK_POLICY_PATH,
23
+ HOOK_RUNTIME_PATH,
24
+ HOOK_DOC_PATH,
25
+ ];
26
+ const CODEX_HOOK_EXECUTABLE_PATHS = [HOOK_DISPATCHER_PATH];
27
+
28
+ export async function resolveCodexHooksPreference(ctx) {
29
+ const override = parseCodexHooksOverride(process.env.NAOME_CODEX_HOOKS);
30
+ if (override !== null) {
31
+ setCodexHooksEnabled(ctx, override);
32
+ return;
33
+ }
34
+
35
+ if (hasManagedCodexHooks(ctx)) {
36
+ setCodexHooksEnabled(ctx, true);
37
+ return;
38
+ }
39
+
40
+ if (!process.stdin.isTTY || process.env.CI === "true") {
41
+ setCodexHooksEnabled(ctx, false);
42
+ return;
43
+ }
44
+
45
+ printSection(ctx, "Optional Codex Hooks");
46
+ console.log(`${ctx.color.dim("purpose")} earlier NAOME feedback for Codex agents`);
47
+ console.log(`${ctx.color.dim("scope ")} repo-local acceleration only; normal NAOME gates remain authoritative`);
48
+ console.log(`${ctx.color.dim("human ")} optional; declining keeps install and sync working`);
49
+ console.log("");
50
+
51
+ const rl = createInterface({ input, output });
52
+ const answer = await rl.question(`${ctx.color.yellow("?")} Install optional Codex Hooks for this repository? (y/N) `);
53
+ rl.close();
54
+
55
+ setCodexHooksEnabled(ctx, answer.trim().toLowerCase() === "y");
56
+ }
57
+
58
+ export function ensureCodexHooks(ctx, archiveDirName) {
59
+ if (!ctx.codexHooksEnabled) {
60
+ return;
61
+ }
62
+
63
+ ensureCodexHookManifestPaths(ctx);
64
+ for (const relativePath of ctx.optionalCodexHookPaths) {
65
+ replaceHarnessFile(ctx, relativePath, archiveDirName);
66
+ }
67
+ }
68
+
69
+ export function ensureCodexHookManifestPaths(ctx) {
70
+ for (const relativePath of CODEX_HOOK_MACHINE_PATHS) {
71
+ appendUnique(ctx.machineOwnedPaths, relativePath);
72
+ appendUnique(ctx.localOnlyMachineOwnedPaths, relativePath);
73
+ appendUnique(ctx.localOnlyGitExcludeEntries, relativePath);
74
+ appendUnique(ctx.localOnlyGitUntrackPaths, relativePath);
75
+ }
76
+ for (const relativePath of CODEX_HOOK_EXECUTABLE_PATHS) {
77
+ ctx.executableMachineOwnedPaths.add(relativePath);
78
+ }
79
+ }
80
+
81
+ function setCodexHooksEnabled(ctx, enabled) {
82
+ ctx.codexHooksEnabled = enabled;
83
+ if (enabled) {
84
+ ensureCodexHookManifestPaths(ctx);
85
+ }
86
+ }
87
+
88
+ function hasManagedCodexHooks(ctx) {
89
+ const hookConfigPath = join(ctx.targetRoot, HOOK_CONFIG_PATH);
90
+ if (!existsSync(hookConfigPath)) {
91
+ return false;
92
+ }
93
+
94
+ try {
95
+ const hooks = JSON.parse(readFileSync(hookConfigPath, "utf8"));
96
+ return hooks.schema === "naome.codex-hooks.v1";
97
+ } catch {
98
+ return false;
99
+ }
100
+ }
101
+
102
+ function parseCodexHooksOverride(value) {
103
+ if (value === undefined) {
104
+ return null;
105
+ }
106
+
107
+ const normalized = value.trim().toLowerCase();
108
+ if (["1", "true", "yes", "y", "on"].includes(normalized)) {
109
+ return true;
110
+ }
111
+ if (["0", "false", "no", "n", "off"].includes(normalized)) {
112
+ return false;
113
+ }
114
+ return null;
115
+ }
116
+
117
+ function appendUnique(values, value) {
118
+ if (!values.includes(value)) {
119
+ values.push(value);
120
+ }
121
+ }
@@ -34,6 +34,16 @@ export function createInstallerContext() {
34
34
  installPlan: null,
35
35
  machineOwnedPaths: [],
36
36
  projectOwnedPaths: [],
37
+ optionalCodexHookPaths: [
38
+ ".codex/config.toml",
39
+ ".codex/hooks.json",
40
+ ".naome/bin/codex-hook.js",
41
+ ".naome/bin/codex-hook-io.js",
42
+ ".naome/bin/codex-hook-policy.js",
43
+ ".naome/bin/codex-hook-runtime.js",
44
+ "docs/naome/codex-hooks.md",
45
+ ],
46
+ codexHooksEnabled: false,
37
47
  localOnlyMachineOwnedPaths: [],
38
48
  localOnlyGitIgnoreEntries: [],
39
49
  localOnlyGitExcludeEntries: [],
@@ -30,6 +30,10 @@ export function copyTemplateFile(ctx, sourcePath) {
30
30
  const relativePath = relative(ctx.templateRoot, sourcePath);
31
31
  const targetPath = join(ctx.targetRoot, relativePath);
32
32
 
33
+ if (ctx.optionalCodexHookPaths.includes(relativePath) && !ctx.codexHooksEnabled) {
34
+ return;
35
+ }
36
+
33
37
  if (hasSymlinkInTargetPath(ctx, relativePath)) {
34
38
  ctx.skipped.push(relativePath);
35
39
  ctx.unsafeSkipped.push(relativePath);
@@ -17,9 +17,11 @@ import { installNativeDecisionBinary, patchInstalledMachineOwnedIntegrity } from
17
17
  import { printError } from "./output.js";
18
18
  import { compareVersions } from "./version.js";
19
19
  import { confirmAgentsTakeover, takeoverExistingAgents } from "./agents.js";
20
+ import { ensureCodexHooks, resolveCodexHooksPreference } from "./codex-hooks.js";
20
21
 
21
22
  export async function runFreshInstall(ctx) {
22
23
  await confirmAgentsTakeover(ctx);
24
+ await resolveCodexHooksPreference(ctx);
23
25
 
24
26
  for (const sourcePath of walk(ctx.templateRoot)) {
25
27
  copyTemplateFile(ctx, sourcePath);
@@ -35,7 +37,7 @@ export async function runFreshInstall(ctx) {
35
37
  ensureLocalOnlySourceControlBoundary(ctx);
36
38
  }
37
39
 
38
- export function runExistingInstall(ctx, existingInstall) {
40
+ export async function runExistingInstall(ctx, existingInstall) {
39
41
  const comparison = compareVersions(ctx, existingInstall.version, ctx.packageVersion);
40
42
 
41
43
  if (comparison > 0) {
@@ -52,10 +54,10 @@ export function runExistingInstall(ctx, existingInstall) {
52
54
  }
53
55
 
54
56
  ensureArchiveDirectory(ctx);
55
- runRepair(ctx, existingInstall.version, { fromVersion: existingInstall.version });
57
+ await runRepair(ctx, existingInstall.version, { fromVersion: existingInstall.version });
56
58
  } else {
57
59
  ensureArchiveDirectory(ctx);
58
- runRepair(ctx, existingInstall.version);
60
+ await runRepair(ctx, existingInstall.version);
59
61
  }
60
62
  }
61
63
 
@@ -68,11 +70,13 @@ function rejectUnsupportedHistoricalInstall(ctx, version) {
68
70
  process.exit(1);
69
71
  }
70
72
 
71
- function runRepair(ctx, version, options = {}) {
73
+ async function runRepair(ctx, version, options = {}) {
72
74
  ctx.summaryTitle = "NAOME harness checked";
75
+ await resolveCodexHooksPreference(ctx);
73
76
  ensureCoreHarnessFiles(ctx, `repair-${version}`);
74
77
  ensureTaskControlHarnessFiles(ctx, `repair-${version}`);
75
78
  ensureHarnessHealthFiles(ctx, `repair-${version}`);
79
+ ensureCodexHooks(ctx, `repair-${version}`);
76
80
  installNativeDecisionBinary(ctx);
77
81
  patchInstalledMachineOwnedIntegrity(ctx);
78
82
  ensureBuiltInVerificationChecks(ctx);
@@ -13,10 +13,14 @@ export {
13
13
  export function ensureCoreHarnessFiles(ctx, archiveDirName) {
14
14
  ensureNaomeIgnore(ctx);
15
15
  ensureTemplateFile(ctx, ".naome/verification.json");
16
+ ensureTemplateFile(ctx, ".naome/repository-model.json");
16
17
  replaceHarnessFile(ctx, "AGENTS.md", archiveDirName);
17
18
  replaceHarnessFile(ctx, "docs/naome/index.md", archiveDirName);
18
19
  replaceHarnessFile(ctx, "docs/naome/first-run.md", archiveDirName);
19
20
  replaceHarnessFile(ctx, "docs/naome/agent-workflow.md", archiveDirName);
21
+ replaceHarnessFile(ctx, "docs/naome/context-economy.md", archiveDirName);
22
+ replaceHarnessFile(ctx, "docs/naome/task-ledger.md", archiveDirName);
23
+ ensureTemplateFile(ctx, "docs/naome/repository-model.md");
20
24
  ensureTemplateFile(ctx, "docs/naome/security.md");
21
25
  replaceHarnessFile(ctx, "docs/naome/upgrade.md", archiveDirName);
22
26
  }
@@ -33,6 +37,7 @@ export function ensureTaskControlHarnessFiles(ctx, archiveDirName) {
33
37
  replaceHarnessFile(ctx, "AGENTS.md", archiveDirName);
34
38
  replaceHarnessFile(ctx, "docs/naome/index.md", archiveDirName);
35
39
  replaceHarnessFile(ctx, "docs/naome/agent-workflow.md", archiveDirName);
40
+ replaceHarnessFile(ctx, "docs/naome/context-economy.md", archiveDirName);
36
41
  replaceHarnessFile(ctx, "docs/naome/execution.md", archiveDirName);
37
42
  replaceHarnessFile(ctx, "docs/naome/upgrade.md", archiveDirName);
38
43
  }
@@ -49,6 +54,7 @@ export function ensureHarnessHealthFiles(ctx, archiveDirName) {
49
54
  replaceHarnessFile(ctx, "docs/naome/index.md", archiveDirName);
50
55
  replaceHarnessFile(ctx, "docs/naome/first-run.md", archiveDirName);
51
56
  replaceHarnessFile(ctx, "docs/naome/agent-workflow.md", archiveDirName);
57
+ replaceHarnessFile(ctx, "docs/naome/context-economy.md", archiveDirName);
52
58
  replaceHarnessFile(ctx, "docs/naome/execution.md", archiveDirName);
53
59
  ensureTemplateFile(ctx, "docs/naome/security.md");
54
60
  replaceHarnessFile(ctx, "docs/naome/upgrade.md", archiveDirName);
@@ -34,6 +34,9 @@ function parseInstallPlan(ctx, output) {
34
34
  assertInstallPlanArray(ctx, installPlan, "machineOwned");
35
35
  assertInstallPlanArray(ctx, installPlan, "projectOwned");
36
36
  assertInstallPlanArray(ctx, installPlan, "localOnlyMachineOwned");
37
+ if (installPlan.optionalCodexHookPaths !== undefined) {
38
+ assertInstallPlanArray(ctx, installPlan, "optionalCodexHookPaths");
39
+ }
37
40
  assertInstallPlanArray(ctx, installPlan, "gitignoreEntries");
38
41
  assertInstallPlanArray(ctx, installPlan, "gitExcludeEntries");
39
42
  assertInstallPlanArray(ctx, installPlan, "gitUntrackPaths");
@@ -60,6 +63,7 @@ function assignInstallPlan(ctx, installPlan) {
60
63
  ctx.machineOwnedPaths = installPlan.machineOwned;
61
64
  ctx.projectOwnedPaths = installPlan.projectOwned;
62
65
  ctx.localOnlyMachineOwnedPaths = installPlan.localOnlyMachineOwned;
66
+ ctx.optionalCodexHookPaths = installPlan.optionalCodexHookPaths ?? ctx.optionalCodexHookPaths;
63
67
  ctx.localOnlyGitIgnoreEntries = installPlan.gitignoreEntries;
64
68
  ctx.localOnlyGitExcludeEntries = installPlan.gitExcludeEntries;
65
69
  ctx.localOnlyGitUntrackPaths = installPlan.gitUntrackPaths;
package/installer/main.js CHANGED
@@ -14,7 +14,7 @@ export async function runNaomeNodeCli() {
14
14
  printHeader(ctx);
15
15
  const existingInstall = readExistingInstall(ctx);
16
16
  if (existingInstall) {
17
- runExistingInstall(ctx, existingInstall);
17
+ await runExistingInstall(ctx, existingInstall);
18
18
  } else {
19
19
  await runFreshInstall(ctx);
20
20
  }
@@ -62,8 +62,8 @@ export function findNativeDecisionBinary(ctx) {
62
62
  candidates.push(resolve(ctx.targetRoot, process.env.NAOME_NATIVE_BIN));
63
63
  }
64
64
 
65
- candidates.push(join(ctx.packageRoot, "native", `${process.platform}-${process.arch}`, ctx.nativeBinaryName));
66
65
  candidates.push(join(ctx.packageRoot, "target", "release", ctx.nativeBinaryName));
66
+ candidates.push(join(ctx.packageRoot, "native", `${process.platform}-${process.arch}`, ctx.nativeBinaryName));
67
67
 
68
68
  for (const candidate of candidates) {
69
69
  if (existsSync(candidate) && lstatSync(candidate).isFile()) {
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lamentis/naome",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Native-first CLI for the NAOME agent harness.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -0,0 +1,2 @@
1
+ [features]
2
+ codex_hooks = true
@@ -0,0 +1,70 @@
1
+ {
2
+ "schema": "naome.codex-hooks.v1",
3
+ "version": 1,
4
+ "hooks": {
5
+ "SessionStart": [
6
+ {
7
+ "matcher": "startup|resume|clear",
8
+ "hooks": [
9
+ {
10
+ "type": "command",
11
+ "command": "node .naome/bin/codex-hook.js --event SessionStart"
12
+ }
13
+ ]
14
+ }
15
+ ],
16
+ "UserPromptSubmit": [
17
+ {
18
+ "hooks": [
19
+ {
20
+ "type": "command",
21
+ "command": "node .naome/bin/codex-hook.js --event UserPromptSubmit"
22
+ }
23
+ ]
24
+ }
25
+ ],
26
+ "PreToolUse": [
27
+ {
28
+ "matcher": "*",
29
+ "hooks": [
30
+ {
31
+ "type": "command",
32
+ "command": "node .naome/bin/codex-hook.js --event PreToolUse"
33
+ }
34
+ ]
35
+ }
36
+ ],
37
+ "PermissionRequest": [
38
+ {
39
+ "matcher": "*",
40
+ "hooks": [
41
+ {
42
+ "type": "command",
43
+ "command": "node .naome/bin/codex-hook.js --event PermissionRequest"
44
+ }
45
+ ]
46
+ }
47
+ ],
48
+ "PostToolUse": [
49
+ {
50
+ "matcher": "*",
51
+ "hooks": [
52
+ {
53
+ "type": "command",
54
+ "command": "node .naome/bin/codex-hook.js --event PostToolUse"
55
+ }
56
+ ]
57
+ }
58
+ ],
59
+ "Stop": [
60
+ {
61
+ "hooks": [
62
+ {
63
+ "type": "command",
64
+ "command": "node .naome/bin/codex-hook.js --event Stop"
65
+ }
66
+ ]
67
+ }
68
+ ]
69
+ }
70
+ }
@@ -10,15 +10,17 @@ const nativeBinaryName = process.platform === "win32" ? "naome.exe" : "naome";
10
10
 
11
11
  const expectedMachineOwnedIntegrity = Object.freeze({
12
12
  ".naome/bin/check-harness-health.js": "sha256:dc4de52b79c69600b9ba47b924e2c2b8de61a2cbfab6d1ccc0f1924d963db657",
13
- ".naome/bin/check-task-state.js": "sha256:43c02868072d0d13499aefba2e9a5ec9517d59539fd19ff0f11e3e4623a51b44",
14
- ".naome/bin/naome.js": "sha256:3d2edd9cf7b04ffb8845db2c55ce1b9c243bd3aba112f53af4af3c11fae5b8e1",
13
+ ".naome/bin/check-task-state.js": "sha256:df54489a22b426180266e5e0fb5f9ec381477419f688435248afbf2b9b38ea81",
14
+ ".naome/bin/naome.js": "sha256:d343f367da21bf45272331eec2ea34862a0bf056978780c62d6cae02e0f7993a",
15
15
  ".naome/package.json": "sha256:8005a3491db7d92f36ac66369861589f9c47123d3a7c71e643fc2c06168cd45a",
16
16
  ".naome/task-contract.schema.json": "sha256:1b3b62350328d0d6d660e36d1d1baaa2b88718530db774f9ab2a9e2fcba369c8",
17
- "AGENTS.md": "sha256:9192ea81f90bb19f8043513c49b5da9e9598ee694da8356f345e7ccbca0e28df",
18
- "docs/naome/agent-workflow.md": "sha256:97788255e26282ca1b7fcaaf86c9408c9040246727e3de96b4126fcb68c10b38",
17
+ "AGENTS.md": "sha256:e8b2fc786c1c72b69ba8f2b2ffce4f459e799c7453ce9ff4a9f6448a8f9e6b4f",
18
+ "docs/naome/agent-workflow.md": "sha256:78faeb4eed157b60f0b60bc43da36c713fc4a3436b93bd963ce5f3c12dc91f22",
19
+ "docs/naome/context-economy.md": "sha256:3ed5075815ecf4ada46a5e65438769310307c35759fcd46b13dc0b96e02bebd9",
19
20
  "docs/naome/execution.md": "sha256:bfc5d55838942ec8e3d790b59e3c634ff5bf6a2298265cef3dca9788a097eafb",
20
- "docs/naome/first-run.md": "sha256:a1dd0bd17ec9d71955a473cd2c4a615538e89a7d81e8f4e1015a50ab9efe3558",
21
- "docs/naome/index.md": "sha256:a674102cc801702dc77102afb59be0f5ab189dc638caf0bef358b9d6087d0742",
21
+ "docs/naome/first-run.md": "sha256:f1a2412f1b542e61c79b5ba65a3fdaa810b0f95cf79d9f734256418cdcfb19aa",
22
+ "docs/naome/index.md": "sha256:07ef776f49130319a5280bdb3ae38af22141708253f38eb983a4336fbae1b25a",
23
+ "docs/naome/task-ledger.md": "sha256:1e524085a2f88811941fd9f2f48ca826bc3e0e4816039d07e795637847714cbd",
22
24
  "docs/naome/upgrade.md": "sha256:2c60f0441bbd98bd528d109b30a7ded4b0ad55d61ffb9f52edac9e93b7999cb1"
23
25
  });
24
26
 
@@ -10,15 +10,17 @@ const nativeBinaryName = process.platform === "win32" ? "naome.exe" : "naome";
10
10
 
11
11
  const expectedMachineOwnedIntegrity = Object.freeze({
12
12
  ".naome/bin/check-harness-health.js": "sha256:dc4de52b79c69600b9ba47b924e2c2b8de61a2cbfab6d1ccc0f1924d963db657",
13
- ".naome/bin/check-task-state.js": "sha256:43c02868072d0d13499aefba2e9a5ec9517d59539fd19ff0f11e3e4623a51b44",
14
- ".naome/bin/naome.js": "sha256:3d2edd9cf7b04ffb8845db2c55ce1b9c243bd3aba112f53af4af3c11fae5b8e1",
13
+ ".naome/bin/check-task-state.js": "sha256:df54489a22b426180266e5e0fb5f9ec381477419f688435248afbf2b9b38ea81",
14
+ ".naome/bin/naome.js": "sha256:d343f367da21bf45272331eec2ea34862a0bf056978780c62d6cae02e0f7993a",
15
15
  ".naome/package.json": "sha256:8005a3491db7d92f36ac66369861589f9c47123d3a7c71e643fc2c06168cd45a",
16
16
  ".naome/task-contract.schema.json": "sha256:1b3b62350328d0d6d660e36d1d1baaa2b88718530db774f9ab2a9e2fcba369c8",
17
- "AGENTS.md": "sha256:9192ea81f90bb19f8043513c49b5da9e9598ee694da8356f345e7ccbca0e28df",
18
- "docs/naome/agent-workflow.md": "sha256:97788255e26282ca1b7fcaaf86c9408c9040246727e3de96b4126fcb68c10b38",
17
+ "AGENTS.md": "sha256:e8b2fc786c1c72b69ba8f2b2ffce4f459e799c7453ce9ff4a9f6448a8f9e6b4f",
18
+ "docs/naome/agent-workflow.md": "sha256:78faeb4eed157b60f0b60bc43da36c713fc4a3436b93bd963ce5f3c12dc91f22",
19
+ "docs/naome/context-economy.md": "sha256:3ed5075815ecf4ada46a5e65438769310307c35759fcd46b13dc0b96e02bebd9",
19
20
  "docs/naome/execution.md": "sha256:bfc5d55838942ec8e3d790b59e3c634ff5bf6a2298265cef3dca9788a097eafb",
20
- "docs/naome/first-run.md": "sha256:a1dd0bd17ec9d71955a473cd2c4a615538e89a7d81e8f4e1015a50ab9efe3558",
21
- "docs/naome/index.md": "sha256:a674102cc801702dc77102afb59be0f5ab189dc638caf0bef358b9d6087d0742",
21
+ "docs/naome/first-run.md": "sha256:f1a2412f1b542e61c79b5ba65a3fdaa810b0f95cf79d9f734256418cdcfb19aa",
22
+ "docs/naome/index.md": "sha256:07ef776f49130319a5280bdb3ae38af22141708253f38eb983a4336fbae1b25a",
23
+ "docs/naome/task-ledger.md": "sha256:1e524085a2f88811941fd9f2f48ca826bc3e0e4816039d07e795637847714cbd",
22
24
  "docs/naome/upgrade.md": "sha256:2c60f0441bbd98bd528d109b30a7ded4b0ad55d61ffb9f52edac9e93b7999cb1"
23
25
  });
24
26
 
@@ -154,7 +156,10 @@ function findHarnessRoot(startPath) {
154
156
  let current = resolve(startPath);
155
157
 
156
158
  while (true) {
157
- if (existsSync(join(current, ".naome", "task-state.json"))) {
159
+ if (
160
+ existsSync(join(current, ".naome", "task-state.json")) ||
161
+ existsSync(join(current, ".naome", "tasks", "active.json"))
162
+ ) {
158
163
  return current;
159
164
  }
160
165