@lyupro/codex-bridge 0.6.7 → 0.6.9

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 (101) hide show
  1. package/CHANGELOG.md +148 -0
  2. package/README.md +10 -6
  3. package/bin/codex-bridge.mjs +35 -172
  4. package/cli/command-help.mjs +49 -0
  5. package/cli/command-registry.mjs +284 -0
  6. package/cli/copy.mjs +42 -5
  7. package/cli/dispatcher-contract-record.mjs +21 -11
  8. package/cli/dispatcher-contract.mjs +6 -5
  9. package/cli/doctor-host-side.mjs +110 -0
  10. package/cli/doctor-installation.mjs +111 -111
  11. package/cli/doctor.mjs +35 -7
  12. package/cli/handback-witness-check.mjs +10 -18
  13. package/cli/home-inspection.mjs +122 -0
  14. package/cli/home-plan-execute.mjs +140 -0
  15. package/cli/home-removal-plan.mjs +61 -0
  16. package/cli/hook-recognizer.mjs +78 -0
  17. package/cli/host-contract.mjs +36 -21
  18. package/cli/host-inspection.mjs +156 -0
  19. package/cli/host-removal.mjs +91 -0
  20. package/cli/image-evidence.mjs +44 -0
  21. package/cli/install-owners.mjs +244 -0
  22. package/cli/install-record.mjs +71 -9
  23. package/cli/install.mjs +101 -77
  24. package/cli/inventory-removal.mjs +84 -0
  25. package/cli/inventory-transition.mjs +66 -0
  26. package/cli/lifecycle-lock-clear.mjs +104 -0
  27. package/cli/lifecycle-lock-inspect.mjs +91 -0
  28. package/cli/lifecycle-lock.mjs +335 -0
  29. package/cli/lifecycle-transaction.mjs +57 -0
  30. package/cli/link-segments.mjs +45 -0
  31. package/cli/model-set.mjs +4 -3
  32. package/cli/model-speed.mjs +9 -2
  33. package/cli/model.mjs +5 -3
  34. package/cli/owner-record.mjs +40 -0
  35. package/cli/owner-sync-apply.mjs +108 -0
  36. package/cli/owner-sync.mjs +115 -0
  37. package/cli/probe-contract.mjs +26 -10
  38. package/cli/probe-target.mjs +33 -0
  39. package/cli/projects.mjs +5 -2
  40. package/cli/prune-args.mjs +9 -4
  41. package/cli/prune.mjs +23 -72
  42. package/cli/purge-live-runs.mjs +98 -0
  43. package/cli/purge-preflight.mjs +103 -0
  44. package/cli/read.mjs +2 -1
  45. package/cli/record-removal.mjs +43 -0
  46. package/cli/removal-outcomes.mjs +115 -0
  47. package/cli/removal-plan.mjs +187 -0
  48. package/cli/remove-layout.mjs +40 -1
  49. package/cli/rules-owners.mjs +4 -2
  50. package/cli/session-hosts.mjs +51 -0
  51. package/cli/terminal-question.mjs +41 -0
  52. package/cli/uninstall-purge.mjs +139 -0
  53. package/cli/uninstall-rules.mjs +72 -0
  54. package/cli/uninstall.mjs +208 -126
  55. package/cli/unlock-lifecycle.mjs +102 -0
  56. package/cli/unlock.mjs +7 -4
  57. package/cli/update.mjs +102 -27
  58. package/package.json +4 -3
  59. package/scripts/home-write-audit.mjs +286 -0
  60. package/scripts/home-write-inventory.mjs +49 -0
  61. package/scripts/home-write-lexer.mjs +182 -0
  62. package/scripts/isolated-roots.mjs +6 -1
  63. package/scripts/open-work/check.mjs +180 -0
  64. package/scripts/open-work/live-thresholds.mjs +155 -0
  65. package/scripts/open-work/register.mjs +56 -0
  66. package/scripts/root-guard/root-entries.mjs +15 -0
  67. package/scripts/run-tests.mjs +33 -1
  68. package/src/agents/codex-advisor.md +5 -2
  69. package/src/home/conventions.md +9 -0
  70. package/src/home/hooks/dispatcher-gate.mjs +15 -2
  71. package/src/home/hooks/guard-tries.mjs +6 -4
  72. package/src/home/hooks/order-gate.mjs +2 -8
  73. package/src/home/hooks/reply-guard.mjs +24 -9
  74. package/src/home/hooks/worktree-witness.mjs +6 -6
  75. package/src/home/lib/agents.mjs +1 -1
  76. package/src/home/lib/atomic-json.mjs +24 -6
  77. package/src/home/lib/config-edit.mjs +25 -10
  78. package/src/home/lib/dispatcher-state.mjs +25 -10
  79. package/src/home/lib/file-lock.mjs +41 -10
  80. package/src/home/lib/handback-witness.mjs +33 -25
  81. package/src/home/lib/home-registry.mjs +211 -0
  82. package/src/home/lib/home-write.mjs +189 -0
  83. package/src/home/lib/hook-diagnostics.mjs +20 -0
  84. package/src/home/lib/host-observations.mjs +60 -0
  85. package/src/home/lib/host-version.mjs +27 -0
  86. package/src/home/lib/meta/advice-status.mjs +2 -2
  87. package/src/home/lib/meta/advice-verdict.mjs +30 -1
  88. package/src/home/lib/meta/events.mjs +2 -1
  89. package/src/home/lib/meta/paths.mjs +24 -24
  90. package/src/home/lib/meta/run-snapshots.mjs +29 -0
  91. package/src/home/lib/meta/snapshot-format.mjs +96 -0
  92. package/src/home/lib/meta/verdict.mjs +14 -16
  93. package/src/home/lib/run-config.mjs +10 -1
  94. package/src/home/lib/runner/git-paths.mjs +74 -0
  95. package/src/home/lib/runner/git-state.mjs +38 -34
  96. package/src/home/lib/runner/launcher.mjs +2 -2
  97. package/src/home/lib/runner/preflight.mjs +7 -0
  98. package/src/home/lib/runner/prompts.mjs +35 -2
  99. package/src/home/lib/runner/schemas.mjs +4 -1
  100. package/src/home/lib/runner/scope-check.mjs +29 -20
  101. package/src/home/lib/runner/worker.mjs +3 -3
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import fs from 'node:fs';
8
8
  import path from 'node:path';
9
- import { spawnSync } from 'node:child_process';
9
+ import { listRepositoryPaths } from './git-paths.mjs';
10
10
  import { globToRegExp, normalizePath, SERVICE_RE } from '../meta/paths.mjs';
11
11
 
12
12
  // Directories a self-walk must never descend into. Only reached when the repository has no git:
@@ -25,6 +25,13 @@ function absolutePattern(pattern) {
25
25
  }
26
26
 
27
27
  function structuralRefusal(repoRoot, pattern) {
28
+ // Plan_73 D7: scope lists store one pattern per line; literal line breaks cannot round-trip.
29
+ if (/[\n\r]/.test(pattern)) {
30
+ return {
31
+ reason: 'contains a newline or carriage return',
32
+ action: 'use a glob instead of a literal path containing a line break',
33
+ };
34
+ }
28
35
  if (absolutePattern(pattern)) {
29
36
  return {
30
37
  reason: 'is an absolute or drive-qualified path',
@@ -87,21 +94,6 @@ function repositoryPath(repoRoot, absolutePath) {
87
94
  return normalizePath(relative);
88
95
  }
89
96
 
90
- /**
91
- * The repository's own answer to "which files are there": tracked plus new-but-uncommitted, minus
92
- * everything ignored. It is the same boundary the run is judged by afterwards, it costs one process
93
- * instead of a full walk, and it cannot wander into `.git` or a dependency tree.
94
- */
95
- function gitPaths(repoRoot) {
96
- const result = spawnSync(
97
- 'git',
98
- ['-C', repoRoot, 'ls-files', '--cached', '--others', '--exclude-standard'],
99
- { encoding: 'utf8', windowsHide: true, maxBuffer: 64 * 1024 * 1024 },
100
- );
101
- if (result.error || result.status !== 0) return null;
102
- return result.stdout.split('\n').map((line) => line.trim()).filter(Boolean);
103
- }
104
-
105
97
  /** Walks a directory that has no git, skipping dependency trees and anything unreadable. */
106
98
  function walkPaths(repoRoot) {
107
99
  const pending = [repoRoot];
@@ -129,9 +121,13 @@ function walkPaths(repoRoot) {
129
121
  return found;
130
122
  }
131
123
 
124
+ /**
125
+ * Names come from git-paths.mjs so Plan_73's 2026-09-30 Cyrillic incident cannot recur through
126
+ * quoted git output or trimmed names. Its tracked-plus-untracked, non-ignored list costs one
127
+ * process instead of a walk and cannot wander into `.git` or a dependency tree.
128
+ */
132
129
  function matchingPaths(repoRoot, matchers) {
133
- const candidates = gitPaths(repoRoot)?.map((relative) => normalizePath(relative))
134
- ?? walkPaths(repoRoot);
130
+ const candidates = listRepositoryPaths(repoRoot) ?? walkPaths(repoRoot);
135
131
  const matched = new Set();
136
132
  for (const candidate of candidates) {
137
133
  for (const matcher of matchers) {
@@ -146,7 +142,10 @@ function matchingPaths(repoRoot, matchers) {
146
142
 
147
143
  function patternList(value, label) {
148
144
  if (!Array.isArray(value)) throw new TypeError(`${label} must be an array`);
149
- return value.map((pattern) => String(pattern ?? '').trim());
145
+ return value.map((pattern) => {
146
+ const text = String(pattern ?? '');
147
+ return /[\n\r]/.test(text) ? text : text.trim();
148
+ });
150
149
  }
151
150
 
152
151
  /**
@@ -181,7 +180,17 @@ export function validateScope(repoRoot, patterns, scopeNewPatterns = []) {
181
180
  };
182
181
  }
183
182
 
184
- const matched = matchingPaths(repoRoot, matchers);
183
+ let matched;
184
+ try {
185
+ matched = matchingPaths(repoRoot, matchers);
186
+ } catch (error) {
187
+ if (error.code !== 'ERR_GIT_PATH_NOT_UTF8') throw error;
188
+ return {
189
+ pattern: required[0],
190
+ reason: error.message,
191
+ action: 'rename the non-UTF-8 file using a UTF-8 file name and retry',
192
+ };
193
+ }
185
194
  const missing = matchers.find((matcher) => !matched.has(matcher.pattern));
186
195
  return missing ? noMatchRefusal(missing.pattern) : null;
187
196
  }
@@ -55,9 +55,9 @@ export async function worker(runDir) {
55
55
  if (cfg.agent === 'codex-build') {
56
56
  fs.writeFileSync(path.join(runDir, 'head-after.txt'), `${cfg.is_git_repo ? headSha(repoRoot) : ''}\n`);
57
57
  fs.writeFileSync(path.join(runDir, 'branch-after.txt'), `${cfg.is_git_repo ? branchName(repoRoot) : ''}\n`);
58
- fs.writeFileSync(path.join(runDir, 'git-after.txt'), git(repoRoot, ['status', '--porcelain']).stdout || '');
59
- fs.writeFileSync(path.join(runDir, 'state-after.txt'), `${worktreeSnapshot(repoRoot)}\n`);
60
- fs.writeFileSync(path.join(runDir, 'diff.stat'), git(repoRoot, ['diff', '--stat']).stdout || '');
58
+ fs.writeFileSync(path.join(runDir, 'git-after.txt'), git(repoRoot, ['-c', 'core.quotepath=false', 'status', '--porcelain']).stdout || '');
59
+ fs.writeFileSync(path.join(runDir, 'state-after.txt'), worktreeSnapshot(repoRoot));
60
+ fs.writeFileSync(path.join(runDir, 'diff.stat'), git(repoRoot, ['-c', 'core.quotepath=false', 'diff', '--stat']).stdout || '');
61
61
  fs.writeFileSync(path.join(runDir, 'flags.txt'), findFakeDone(repoRoot));
62
62
  }
63
63