atris 3.37.0 → 3.38.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 (139) hide show
  1. package/README.md +2 -0
  2. package/atris/PERSONA.md +3 -7
  3. package/atris.md +10 -2
  4. package/ax +158 -33
  5. package/bin/atris.js +49 -2
  6. package/commands/activate.js +16 -3
  7. package/commands/agent-spawn.js +0 -7
  8. package/commands/autoland.js +92 -14
  9. package/commands/autopilot-front.js +0 -4
  10. package/commands/autopilot.js +0 -12
  11. package/commands/brain.js +0 -7
  12. package/commands/brief.js +0 -2
  13. package/commands/business-sync.js +0 -1
  14. package/commands/business.js +107 -12
  15. package/commands/clean.js +31 -2
  16. package/commands/close.js +46 -11
  17. package/commands/cloud.js +0 -1
  18. package/commands/codex-goal.js +0 -2
  19. package/commands/computer.js +6 -5
  20. package/commands/console.js +46 -9
  21. package/commands/deck.js +29 -8
  22. package/commands/dream.js +0 -7
  23. package/commands/drill.js +0 -1
  24. package/commands/engine.js +10 -18
  25. package/commands/experiments.js +0 -4
  26. package/commands/fleet-report.js +12 -4
  27. package/commands/game.js +261 -0
  28. package/commands/gm.js +10 -2
  29. package/commands/goal.js +0 -6
  30. package/commands/harvest.js +0 -1
  31. package/commands/improve.js +29 -8
  32. package/commands/init.js +18 -34
  33. package/commands/integrations.js +0 -7
  34. package/commands/land.js +71 -8
  35. package/commands/launchpad.js +0 -2
  36. package/commands/lesson.js +17 -7
  37. package/commands/loop-front.js +0 -2
  38. package/commands/loops.js +6 -5
  39. package/commands/meet.js +0 -2
  40. package/commands/member.js +68 -3
  41. package/commands/mission.js +359 -102
  42. package/commands/next.js +0 -1
  43. package/commands/now.js +0 -12
  44. package/commands/one-lap.js +8 -8
  45. package/commands/orb.js +0 -4
  46. package/commands/pack-craft.js +0 -1
  47. package/commands/pack.js +670 -77
  48. package/commands/play.js +0 -2
  49. package/commands/probe.js +10 -4
  50. package/commands/pulse.js +73 -48
  51. package/commands/push.js +0 -4
  52. package/commands/radar.js +14 -11
  53. package/commands/recover.js +340 -0
  54. package/commands/report.js +0 -1
  55. package/commands/router.js +319 -0
  56. package/commands/run-front.js +0 -2
  57. package/commands/search.js +0 -3
  58. package/commands/skill.js +7 -2
  59. package/commands/slop.js +225 -7
  60. package/commands/status.js +6 -0
  61. package/commands/stream.js +0 -2
  62. package/commands/study.js +0 -3
  63. package/commands/sync-checkout.js +51 -0
  64. package/commands/sync.js +11 -40
  65. package/commands/task.js +125 -39
  66. package/commands/teach.js +86 -0
  67. package/commands/unknowns.js +0 -10
  68. package/commands/update.js +0 -1
  69. package/commands/visualize.js +0 -5
  70. package/commands/watch.js +0 -6
  71. package/commands/wiki.js +41 -11
  72. package/commands/wish.js +0 -1
  73. package/commands/worktree.js +87 -14
  74. package/commands/xp.js +1 -6
  75. package/commands/youtube.js +0 -2
  76. package/lib/accept-verify-gate.js +122 -0
  77. package/lib/activity-stream.js +0 -2
  78. package/lib/auto-accept-certified.js +145 -2
  79. package/lib/autoland.js +0 -3
  80. package/lib/ax-auto-lane.js +94 -10
  81. package/lib/bench/context.js +0 -2
  82. package/lib/bench/report.js +0 -1
  83. package/lib/bench/runner.js +0 -11
  84. package/lib/brief-ledger.js +0 -6
  85. package/lib/business-simulate.js +382 -0
  86. package/lib/chat-log-scan.js +0 -5
  87. package/lib/checkout-sync.js +52 -0
  88. package/lib/claude-boot-block.js +48 -0
  89. package/lib/cloud-mission.js +0 -6
  90. package/lib/codex-flight.js +0 -1
  91. package/lib/conductor-artifacts.js +25 -0
  92. package/lib/context-gatherer.js +0 -1
  93. package/lib/deck-compose.js +0 -1
  94. package/lib/deck-history.js +0 -1
  95. package/lib/deck-layout.js +0 -5
  96. package/lib/deck-review.js +0 -7
  97. package/lib/deck-schema.js +0 -1
  98. package/lib/default-verifier.js +0 -2
  99. package/lib/engine-registry.js +16 -8
  100. package/lib/experiments/daily.js +0 -6
  101. package/lib/fleet.js +210 -27
  102. package/lib/functional-owner.js +0 -3
  103. package/lib/inspect-fields.js +0 -1
  104. package/lib/known-commands.js +2 -2
  105. package/lib/learnings.js +0 -2
  106. package/lib/loop-doctor.js +11 -17
  107. package/lib/manifest.js +0 -2
  108. package/lib/member-alive.js +0 -1
  109. package/lib/member-switches.js +0 -5
  110. package/lib/mission-artifact.js +0 -1
  111. package/lib/mission-protected-lane.js +293 -0
  112. package/lib/mission-room.js +0 -2
  113. package/lib/mission-runtime-loop.js +0 -1
  114. package/lib/next-moves.js +0 -18
  115. package/lib/official-cli-integration.js +0 -4
  116. package/lib/orb-context.js +6 -3
  117. package/lib/orb-scorecard.js +0 -1
  118. package/lib/outbound-send-gate.js +0 -6
  119. package/lib/permission-grants.js +0 -6
  120. package/lib/policy-lessons.js +0 -2
  121. package/lib/pulse.js +68 -15
  122. package/lib/review-integrity.js +0 -2
  123. package/lib/router-brain.js +0 -2
  124. package/lib/runner-command.js +0 -4
  125. package/lib/runtime-bootstrap.js +0 -1
  126. package/lib/scorecard.js +0 -1
  127. package/lib/self-drive.js +1 -1
  128. package/lib/state-detection.js +0 -6
  129. package/lib/task-db.js +0 -4
  130. package/lib/team-presence.js +0 -1
  131. package/lib/voice-gate.js +63 -2
  132. package/lib/wiki.js +152 -9
  133. package/lib/wish-audit.js +20 -6
  134. package/lib/wish-delegate.js +26 -20
  135. package/lib/wish-stats.js +0 -5
  136. package/lib/wish-store.js +0 -1
  137. package/lib/workspace-safety.js +0 -2
  138. package/lib/zip.js +0 -1
  139. package/package.json +1 -1
@@ -179,11 +179,8 @@ function resolveFunctionalOwner({ requestedOwner, title, tag, note, goal, root,
179
179
  }
180
180
 
181
181
  module.exports = {
182
- ENGINE_OWNER_IDS,
183
182
  FUNCTIONAL_MEMBER_TOPICS,
184
183
  normalizeOwnerSlug,
185
- isEngineOwner,
186
184
  listWorkspaceMemberSlugs,
187
- proposedMemberSlugFromTask,
188
185
  resolveFunctionalOwner,
189
186
  };
@@ -168,7 +168,6 @@ module.exports = {
168
168
  missionAckInspectValue,
169
169
  missionPingsInspectValue,
170
170
  missionInspectFieldValues,
171
- formatInspectFieldText,
172
171
  inspectTextLines,
173
172
  buildInspectPayload,
174
173
  };
@@ -2,8 +2,8 @@
2
2
 
3
3
  const knownCommands = ['init', 'log', 'logs', 'wish', 'drill', 'dream', 'now', 'goal', 'wtf', 'orb', 'radar', 'stream', 'ctop', 'launchpad', 'status', 'analytics', 'visualize', 'brain', 'brainstorm', 'autopilot', 'run', '_start', 'plan', 'do', 'review', 'release',
4
4
  'activate', '_activate', 'agent', 'team', 'chat', 'fast', 'ax', 'console', 'serve', 'login', 'logout', 'whoami', 'switch', 'use', 'accounts', '_resolve', '_profile-email', '_switch-session', 'shell-init', 'update', 'upgrade', 'version', 'help', 'next', 'atris',
5
- 'clean', 'close', 'harvest', 'verify', 'search', 'scout', 'skill', 'member', 'codex-goal', 'app', 'apps', 'learn', 'lesson', 'plugin', 'experiments', 'bench', 'receipt', 'proof', 'openclaw', 'pull', 'push', 'watch', 'cloud', 'live', 'align', 'terminal', 'computer', 'diff', 'business', 'sync', 'youtube',
6
- 'ingest', 'query', 'lint', 'loop', 'pulse', 'task', 'mission', 'agents', 'probe', 'worktree', 'land', 'autoland', 'drive', 'aeo', 'slop', 'strings', 'write', 'security-review', 'secure', 'deck', 'site', 'theme', 'card', 'reel', 'improve', 'study', 'rainmaker', 'xp', 'play', 'gm', 'x', 'recap', 'report', 'signup', 'clarity', 'interview', 'meet', 'moves', 'unknowns', 'avail',
5
+ 'clean', 'close', 'harvest', 'verify', 'recover', 'search', 'scout', 'skill', 'member', 'codex-goal', 'app', 'apps', 'learn', 'lesson', 'teach', 'plugin', 'experiments', 'bench', 'router', 'receipt', 'proof', 'openclaw', 'pull', 'push', 'watch', 'cloud', 'live', 'align', 'terminal', 'computer', 'diff', 'business', 'sync', 'youtube',
6
+ 'ingest', 'query', 'lint', 'loop', 'pulse', 'task', 'mission', 'agents', 'probe', 'worktree', 'land', 'autoland', 'drive', 'aeo', 'slop', 'strings', 'write', 'security-review', 'secure', 'deck', 'site', 'theme', 'card', 'reel', 'improve', 'study', 'rainmaker', 'xp', 'play', 'gm', 'game', 'x', 'recap', 'report', 'signup', 'clarity', 'interview', 'meet', 'moves', 'unknowns', 'avail', 'sync-checkout',
7
7
  'github', 'vercel', 'supabase', 'linear', 'stripe', 'gmail', 'calendar', 'twitter', 'slack', 'imessage', 'integrations', 'setup', 'clean-workspace', 'cw',
8
8
  'fork', 'browse', 'publish', 'pack', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet', 'fleet-report', 'loops', 'self-improve', 'compile', 'spaceship', 'truth', 'sign', 'engine', 'engines', 'feed', 'brief'];
9
9
 
package/lib/learnings.js CHANGED
@@ -222,8 +222,6 @@ function exportMarkdown() {
222
222
  module.exports = {
223
223
  TYPES,
224
224
  SOURCES,
225
- getLearningsPath,
226
- ensureLearningsFile,
227
225
  effectiveConfidence,
228
226
  loadLearnings,
229
227
  addLearning,
@@ -311,7 +311,7 @@ function missionLockPidAlive(pid) {
311
311
  }
312
312
  }
313
313
 
314
- function staleMissionLockFinding(root, nowIso, pidAlive = missionLockPidAlive) {
314
+ function missionLockRecords(root, pidAlive = missionLockPidAlive) {
315
315
  const stateDir = path.join(root, '.atris', 'state');
316
316
  let names = [];
317
317
  try {
@@ -319,18 +319,21 @@ function staleMissionLockFinding(root, nowIso, pidAlive = missionLockPidAlive) {
319
319
  } catch {
320
320
  return [];
321
321
  }
322
- const stale = [];
323
- for (const name of names) {
322
+ return names.map((name) => {
324
323
  const file = path.join(stateDir, name);
325
324
  let lock = null;
326
325
  try { lock = JSON.parse(fs.readFileSync(file, 'utf8')); } catch {}
327
326
  const pid = Number(lock && lock.pid);
328
327
  if (!Number.isInteger(pid) || pid <= 0) {
329
- stale.push({ lock: name, reason: 'unparseable', pid: null });
330
- } else if (!pidAlive(pid)) {
331
- stale.push({ lock: name, reason: 'dead-pid', pid });
328
+ return { lock: name, reason: 'unparseable', pid: null, stale: true };
332
329
  }
333
- }
330
+ const alive = pidAlive(pid);
331
+ return { lock: name, reason: alive ? 'live-pid' : 'dead-pid', pid, stale: !alive };
332
+ });
333
+ }
334
+
335
+ function staleMissionLockFinding(root, nowIso, pidAlive = missionLockPidAlive) {
336
+ const stale = missionLockRecords(root, pidAlive).filter((lock) => lock.stale);
334
337
  if (!stale.length) return [];
335
338
  return [{
336
339
  kind: 'stale_mission_lock',
@@ -400,17 +403,8 @@ function scanLoopReceipts({ root = process.cwd(), now = new Date(), freshnessMs
400
403
 
401
404
  module.exports = {
402
405
  scanLoopReceipts,
403
- repeatedFailureFindings,
404
- authFailureFindings,
405
- rewardFlatlineFinding,
406
- zeroDueCapableFinding,
407
- staleMissionLockFinding,
408
- isDueCapableMission,
406
+ missionLockRecords,
409
407
  latestMissionRecords,
410
- orderFindings,
411
408
  nonPositiveRewardPressure,
412
- applyRewardPressure,
413
409
  FINDING_PRIORITY,
414
- DEFAULT_RECEIPT_LIMIT,
415
- DEFAULT_FINDING_FRESHNESS_MS,
416
410
  };
package/lib/manifest.js CHANGED
@@ -251,9 +251,7 @@ module.exports = {
251
251
  buildManifest,
252
252
  computeLocalHashes,
253
253
  threeWayCompare,
254
- getManifestPath,
255
254
  SKIP_DIRS,
256
255
  isIgnoredSyncPath,
257
256
  filterSyncFiles,
258
- isSyncTextBuffer,
259
257
  };
@@ -227,6 +227,5 @@ function runAliveTick(name, opts = {}) {
227
227
 
228
228
  module.exports = {
229
229
  runAliveTick,
230
- resolveMemberOperateScript,
231
230
  resolveAtrisBin,
232
231
  };
@@ -129,14 +129,9 @@ function memberArgFromArgv(argv = process.argv) {
129
129
  }
130
130
 
131
131
  module.exports = {
132
- SWITCHES_REL,
133
- switchesPath,
134
- loadMemberSwitches,
135
- saveMemberSwitches,
136
132
  isLocalMember,
137
133
  getMemberSwitch,
138
134
  isMemberAwake,
139
- isMemberLoopAwake,
140
135
  setMemberAwake,
141
136
  setMemberLoopAwake,
142
137
  readLoopFlag,
@@ -498,7 +498,6 @@ function writeMissionArtifact(mission, options = {}) {
498
498
  }
499
499
 
500
500
  module.exports = {
501
- buildMissionArtifactBlocks,
502
501
  missionArtifactPaths,
503
502
  writeMissionArtifact,
504
503
  };
@@ -0,0 +1,293 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+ const path = require('path');
6
+ const { spawnSync } = require('child_process');
7
+ const { DENIED_TAGS } = require('./fleet');
8
+
9
+ const PATH_SURFACES = [
10
+ ['middleware/proxy', /(?:^|[/_.-])(?:middleware|proxy)(?:[/_.-]|$)/i],
11
+ ['csp', /(?:^|[/_.-])csp(?:[/_.-]|$)/i],
12
+ ['auth', /(?:^|[/_.-])auth(?:entication|orization)?(?:[/_.-]|$)/i],
13
+ ['session', /(?:^|[/_.-])sessions?(?:[/_.-]|$)/i],
14
+ ['cookie', /(?:^|[/_.-])cookies?(?:[/_.-]|$)/i],
15
+ ['cors', /(?:^|[/_.-])cors(?:[/_.-]|$)/i],
16
+ ['sandbox', /(?:^|[/_.-])sandbox(?:[/_.-]|$)/i],
17
+ ['permission', /(?:^|[/_.-])permissions?(?:[/_.-]|$)/i],
18
+ ];
19
+
20
+ const CONTENT_SURFACES = [
21
+ ['csp', /\bContent-Security-Policy\b/i],
22
+ ['csp nonce', /\bnonce\b/i],
23
+ ['iframe sandbox', /\bsandbox\s*=/i],
24
+ ['cookie', /\bSet-Cookie\b/i],
25
+ ['cors', /\bAccess-Control-Allow-[A-Za-z-]+\b/i],
26
+ ['public dynamic rendering', /\bforce-dynamic\b/i],
27
+ ['auth header', /\b(?:Authorization\s*[:=].*\bBearer\b|headers?\b[^\n]*\bauthorization\b|\bauthorization\b[^\n]*headers?\b)/i],
28
+ ];
29
+
30
+ function normalizedTags(tags) {
31
+ const list = Array.isArray(tags) ? tags : [tags];
32
+ return list
33
+ .flatMap((tag) => String(tag || '').split(','))
34
+ .map((tag) => tag.trim().toLowerCase().replace(/_/g, '-'))
35
+ .filter(Boolean);
36
+ }
37
+
38
+ function changedPathsFromDiff(diff) {
39
+ const paths = [];
40
+ for (const line of String(diff || '').split(/\r?\n/)) {
41
+ const header = line.match(/^diff --git a\/(.+?) b\/(.+)$/);
42
+ if (header) {
43
+ paths.push(header[1], header[2]);
44
+ continue;
45
+ }
46
+ const file = line.match(/^(?:---|\+\+\+) (?:[ab]\/)?(.+)$/);
47
+ if (file && file[1] !== '/dev/null') paths.push(file[1]);
48
+ }
49
+ return Array.from(new Set(paths));
50
+ }
51
+
52
+ // Prose is not a protected surface. Docs, judge cards, wikis, journals and run
53
+ // receipts routinely *describe* CSP or force-dynamic without changing any
54
+ // behaviour — replaying this guard over 60 real commits, 5 of 6 hits were
55
+ // markdown that merely mentioned the words. A guard that pauses on every judge
56
+ // card gets switched off, which is worse than no guard. Content signals
57
+ // therefore apply to code only; PATH signals still apply to every file.
58
+ const CODE_FILE = /\.(?:[cm]?[jt]sx?|py|rb|go|rs|java|kt|swift|php|sh|bash|zsh|sql|ya?ml|toml|env)$/i;
59
+
60
+ function isCodeFile(file) {
61
+ const f = String(file || '').trim();
62
+ if (!f || f === '/dev/null') return false;
63
+ if (/\.(?:md|mdx|markdown|txt|rst|adoc)$/i.test(f)) return false;
64
+ return CODE_FILE.test(f);
65
+ }
66
+
67
+ // Walk the diff file-by-file so a changed line is attributed to the file it
68
+ // came from, then keep only lines from code files.
69
+ function changedCodeLinesFromDiff(diff) {
70
+ const lines = [];
71
+ let inCode = false;
72
+ for (const line of String(diff || '').split(/\r?\n/)) {
73
+ const header = line.match(/^diff --git a\/(.+?) b\/(.+)$/);
74
+ if (header) {
75
+ inCode = isCodeFile(header[1]) || isCodeFile(header[2]);
76
+ continue;
77
+ }
78
+ if (/^(?:---|\+\+\+) /.test(line)) continue;
79
+ if (!inCode) continue;
80
+ if (/^[+-]/.test(line)) lines.push(line.slice(1));
81
+ }
82
+ return lines;
83
+ }
84
+
85
+ function matchProtectedMissionDiff(diff, { tags = [] } = {}) {
86
+ const matches = [];
87
+ const add = (surface, signal, detail) => {
88
+ if (matches.some((match) => match.surface === surface && match.signal === signal && match.detail === detail)) return;
89
+ matches.push({ surface, signal, detail });
90
+ };
91
+
92
+ for (const tag of normalizedTags(tags)) {
93
+ if (DENIED_TAGS.includes(tag)) add(tag, 'tag', tag);
94
+ }
95
+
96
+ for (const file of changedPathsFromDiff(diff)) {
97
+ for (const [surface, pattern] of PATH_SURFACES) {
98
+ if (pattern.test(file)) add(surface, 'path', file);
99
+ }
100
+ }
101
+
102
+ for (const line of changedCodeLinesFromDiff(diff)) {
103
+ for (const [surface, pattern] of CONTENT_SURFACES) {
104
+ if (pattern.test(line)) add(surface, 'content', 'changed line');
105
+ }
106
+ }
107
+
108
+ return {
109
+ protected: matches.length > 0,
110
+ surfaces: Array.from(new Set(matches.map((match) => match.surface))),
111
+ matches,
112
+ };
113
+ }
114
+
115
+ function runGitDiff(git, args, root) {
116
+ const result = git(args, root);
117
+ if (!result || result.error || result.status !== 0) {
118
+ const detail = result?.error?.message
119
+ || String(result?.stderr || result?.stdout || `git ${args.join(' ')} failed`).trim();
120
+ return { ok: false, detail: detail || 'git diff could not be read' };
121
+ }
122
+ return { ok: true, diff: String(result.stdout || '') };
123
+ }
124
+
125
+ function defaultGit(gitBin = 'git') {
126
+ return (args, root) => spawnSync(gitBin, args, {
127
+ cwd: root,
128
+ encoding: 'utf8',
129
+ timeout: 10000,
130
+ });
131
+ }
132
+
133
+ function readMissionDiff({
134
+ root = process.cwd(),
135
+ baseRef = '',
136
+ git = defaultGit(),
137
+ includeUnstaged = true,
138
+ } = {}) {
139
+ const parts = [];
140
+ const staged = runGitDiff(git, ['diff', '--cached', '--no-ext-diff', '--unified=0', '--no-color', '--'], root);
141
+ if (!staged.ok) return staged;
142
+ parts.push(staged.diff);
143
+
144
+ if (includeUnstaged) {
145
+ const unstaged = runGitDiff(git, ['diff', '--no-ext-diff', '--unified=0', '--no-color', '--'], root);
146
+ if (!unstaged.ok) return unstaged;
147
+ parts.push(unstaged.diff);
148
+ }
149
+
150
+ if (baseRef) {
151
+ const committed = runGitDiff(git, ['diff', '--no-ext-diff', '--unified=0', '--no-color', baseRef, 'HEAD', '--'], root);
152
+ if (!committed.ok) return committed;
153
+ parts.push(committed.diff);
154
+ }
155
+
156
+ return { ok: true, diff: parts.filter(Boolean).join('\n') };
157
+ }
158
+
159
+ function protectedPause(reason, detail = {}) {
160
+ return {
161
+ ok: false,
162
+ allowed: false,
163
+ status: 'paused-for-review',
164
+ reason,
165
+ ...detail,
166
+ };
167
+ }
168
+
169
+ function unreadableMissionGuard(error) {
170
+ return protectedPause('mission diff could not be read; human review is required', {
171
+ unreadable: true,
172
+ detail: error?.message || String(error || 'unknown diff read failure'),
173
+ surfaces: ['unreadable diff'],
174
+ matches: [],
175
+ });
176
+ }
177
+
178
+ function inspectMissionProtectedDiff({
179
+ root = process.cwd(),
180
+ baseRef = '',
181
+ tags = [],
182
+ readDiff = readMissionDiff,
183
+ git,
184
+ includeUnstaged = true,
185
+ } = {}) {
186
+ let read;
187
+ try {
188
+ read = readDiff({ root, baseRef, tags, git, includeUnstaged });
189
+ } catch (error) {
190
+ return unreadableMissionGuard(error);
191
+ }
192
+ if (!read || read.ok !== true) {
193
+ return unreadableMissionGuard(read?.detail);
194
+ }
195
+
196
+ const matched = matchProtectedMissionDiff(read.diff, { tags });
197
+ if (matched.protected) {
198
+ return protectedPause(`protected mission surface requires human review: ${matched.surfaces.join(', ')}`, {
199
+ unreadable: false,
200
+ surfaces: matched.surfaces,
201
+ matches: matched.matches,
202
+ });
203
+ }
204
+ return {
205
+ ok: true,
206
+ allowed: true,
207
+ status: 'clear',
208
+ reason: 'mission diff contains no protected surface',
209
+ unreadable: false,
210
+ surfaces: [],
211
+ matches: [],
212
+ };
213
+ }
214
+
215
+ function guardMissionLanding(options = {}) {
216
+ const inspection = inspectMissionProtectedDiff(options);
217
+ if (!inspection.allowed) return inspection;
218
+ const land = typeof options.land === 'function' ? options.land : () => ({ ok: true, landed: true });
219
+ return { ...land(), protected_lane_guard: inspection };
220
+ }
221
+
222
+ function gitSubcommand(args) {
223
+ const consumesValue = new Set(['-C', '-c', '--git-dir', '--work-tree', '--namespace', '--config-env']);
224
+ for (let index = 0; index < args.length; index += 1) {
225
+ const arg = String(args[index] || '');
226
+ if (consumesValue.has(arg)) {
227
+ index += 1;
228
+ continue;
229
+ }
230
+ if (arg.startsWith('-')) continue;
231
+ return arg;
232
+ }
233
+ return '';
234
+ }
235
+
236
+ function prepareMissionGitGuard({
237
+ root = process.cwd(),
238
+ tags = [],
239
+ pathValue = process.env.PATH || '',
240
+ } = {}) {
241
+ const resolved = spawnSync('sh', ['-c', 'command -v git'], {
242
+ cwd: root,
243
+ encoding: 'utf8',
244
+ timeout: 5000,
245
+ });
246
+ const gitBin = String(resolved.stdout || '').trim();
247
+ if (resolved.status !== 0 || !gitBin) {
248
+ throw new Error('mission git guard could not resolve git');
249
+ }
250
+
251
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'atris-mission-git-'));
252
+ const wrapper = path.join(dir, 'git');
253
+ const modulePath = __filename;
254
+ const config = { root: path.resolve(root), tags: normalizedTags(tags), gitBin };
255
+ const source = [
256
+ '#!/usr/bin/env node',
257
+ "'use strict';",
258
+ `const { spawnSync } = require('child_process');`,
259
+ `const guard = require(${JSON.stringify(modulePath)});`,
260
+ `const config = ${JSON.stringify(config)};`,
261
+ gitSubcommand.toString(),
262
+ defaultGit.toString(),
263
+ `const args = process.argv.slice(2);`,
264
+ `if (gitSubcommand(args) === 'commit') {`,
265
+ ` const result = guard.inspectMissionProtectedDiff({ root: config.root, tags: config.tags, includeUnstaged: false, git: defaultGit(config.gitBin) });`,
266
+ ` if (!result.allowed) {`,
267
+ ` process.stderr.write(result.reason + '\\n');`,
268
+ ` process.exit(78);`,
269
+ ` }`,
270
+ `}`,
271
+ `const child = spawnSync(config.gitBin, args, { cwd: process.cwd(), stdio: 'inherit' });`,
272
+ `if (child.error) { process.stderr.write(child.error.message + '\\n'); process.exit(1); }`,
273
+ `process.exit(child.status == null ? 1 : child.status);`,
274
+ '',
275
+ ].join('\n');
276
+ fs.writeFileSync(wrapper, source, { encoding: 'utf8', mode: 0o755 });
277
+
278
+ return {
279
+ env: { ...process.env, PATH: `${dir}${path.delimiter}${pathValue}` },
280
+ pathPrefix: dir,
281
+ cleanup() {
282
+ try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
283
+ },
284
+ };
285
+ }
286
+
287
+ module.exports = {
288
+ matchProtectedMissionDiff,
289
+ unreadableMissionGuard,
290
+ inspectMissionProtectedDiff,
291
+ guardMissionLanding,
292
+ prepareMissionGitGuard,
293
+ };
@@ -842,6 +842,4 @@ module.exports = {
842
842
  buildMissionRoom,
843
843
  writeMissionRoomReceipt,
844
844
  missionRoomLines,
845
- writeThinkingMemory,
846
- collectMissionRoomContext,
847
845
  };
@@ -322,6 +322,5 @@ function readAtrisGoalState(cwd = process.cwd()) {
322
322
  module.exports = {
323
323
  missionRunIntentFromMessage,
324
324
  runRuntimeMissionLoop,
325
- formatRuntimeMissionLoopReceipt,
326
325
  readAtrisGoalState,
327
326
  };
package/lib/next-moves.js CHANGED
@@ -844,37 +844,19 @@ function nextMoves(root = process.cwd(), limit = 3) {
844
844
  module.exports = {
845
845
  moveId,
846
846
  norm,
847
- titleKey,
848
847
  isGenericInboxPlaceholder,
849
- WEIGHT,
850
848
  SELF_IMPROVEMENT_SEED_TITLE,
851
- cleanSuggestedTargetTitle,
852
- suggestedTargetFromReportText,
853
849
  latestSuggestedTarget,
854
- parseInboxTitles,
855
- cleanInboxMoveTitle,
856
850
  readRoadmapOpenItems,
857
- readWaitingWishCards,
858
- readActionableMissionCards,
859
- readActiveMissions,
860
- readUnreviewedShippedWishCards,
861
- readDreamCards,
862
851
  markDreamCardConsumed,
863
- dreamCardId,
864
852
  readEndgameMove,
865
- readActiveTasks,
866
- isInternalNextMoveTask,
867
853
  readHandledTaskTitles,
868
854
  latestInboxItems,
869
855
  todayInboxItems,
870
- gatherCandidates,
871
856
  pickNextMoves,
872
- readNextParked,
873
857
  parkNextCard,
874
858
  nextCards,
875
- addMissionOnlyFallback,
876
859
  nextMoves,
877
- readDecisions,
878
860
  recordDecision,
879
861
  seedInboxFromMove,
880
862
  pickRoadmapSeed,
@@ -166,9 +166,5 @@ function createOfficialCliCommand(config) {
166
166
 
167
167
  module.exports = {
168
168
  createOfficialCliCommand,
169
- ensureBinary,
170
- hasHelp,
171
- matchCommand,
172
169
  printHelp,
173
- runAuthCheck,
174
170
  };
@@ -455,7 +455,8 @@ async function collectOrbContext(root, { threads = [], now = Date.now() } = {})
455
455
  }
456
456
 
457
457
  // The accumulate-context flywheel: each chosen option appends one visible line to the
458
- // project's now.md. Append-only; creates now.md on first choice.
458
+ // project's operating truth at atris/now.md. Append-only; creates the file on first
459
+ // choice. (It used to write <root>/now.md, a stray file nothing reads.)
459
460
  async function appendOrbChoice(root, label, when = new Date()) {
460
461
  const safeRoot = typeof root === 'string' && root ? path.resolve(root) : null;
461
462
  const cleanLabel = cleanTitle(label, 120);
@@ -463,7 +464,10 @@ async function appendOrbChoice(root, label, when = new Date()) {
463
464
  const day = when.toISOString().slice(0, 10);
464
465
  const line = `orb: ${cleanLabel} · ${day}\n`;
465
466
  try {
466
- await fs.promises.appendFile(path.join(safeRoot, 'now.md'), line, 'utf8');
467
+ const nowPath = fs.existsSync(path.join(safeRoot, 'atris'))
468
+ ? path.join(safeRoot, 'atris', 'now.md')
469
+ : path.join(safeRoot, 'now.md'); // pre-init workspaces keep the old spot
470
+ await fs.promises.appendFile(nowPath, line, 'utf8');
467
471
  return { ok: true, line: line.trim() };
468
472
  } catch (error) {
469
473
  return { ok: false, error: String((error && error.message) || error) };
@@ -471,7 +475,6 @@ async function appendOrbChoice(root, label, when = new Date()) {
471
475
  }
472
476
 
473
477
  module.exports = {
474
- buildOrbContext,
475
478
  collectOrbContext,
476
479
  appendOrbChoice,
477
480
  };
@@ -217,7 +217,6 @@ function parseOrbScorecardDays(args = []) {
217
217
  module.exports = {
218
218
  DAY_MS,
219
219
  DEFAULT_DAYS,
220
- emptyOrbScorecard,
221
220
  readOrbScorecard,
222
221
  renderOrbScorecard,
223
222
  parseOrbScorecardDays,
@@ -153,13 +153,7 @@ function prepareOutboundSendTextOrExit(channel, args = [], options = {}) {
153
153
  }
154
154
 
155
155
  module.exports = {
156
- OutboundArtifactGateError,
157
- assertOutboundSendAllowed,
158
156
  assertOutboundSendAllowedOrExit,
159
- defaultGateOptions,
160
157
  extractOutboundGateOptions,
161
- parseOutboundSendArgs,
162
158
  prepareOutboundSendTextOrExit,
163
- readOutboundBodyFile,
164
- shouldRunOutboundGate,
165
159
  };
@@ -277,17 +277,11 @@ async function syncGrants({ apiRequestJson, token, file, endpoint = GRANTS_SYNC_
277
277
 
278
278
  module.exports = {
279
279
  GRANTS_SCHEMA,
280
- GRANTS_SYNC_ENDPOINT,
281
- grantsFilePath,
282
- parseArgv,
283
- commandIsGrantable,
284
280
  loadGrants,
285
281
  saveGrants,
286
282
  addGrant,
287
283
  revokeGrant,
288
284
  matchGrant,
289
285
  recordUse,
290
- normalizeRemoteGrant,
291
- mergeRemoteGrants,
292
286
  syncGrants,
293
287
  };
@@ -320,12 +320,10 @@ function syncLessonsMd(root, mined) {
320
320
  }
321
321
 
322
322
  module.exports = {
323
- AGENT_ACTOR_PATTERN,
324
323
  VERIFY_COMMAND_PATTERN,
325
324
  loadHistory,
326
325
  mineProofPolicy,
327
326
  policyHintsForProof,
328
- policyLessonsPath,
329
327
  readPolicyLessons,
330
328
  writePolicyLessons,
331
329
  syncLessonsMd,