atris 3.35.0 → 3.37.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 (133) hide show
  1. package/AGENTS.md +37 -0
  2. package/README.md +5 -3
  3. package/atris/GETTING_STARTED.md +1 -1
  4. package/atris/atris.md +3 -0
  5. package/atris/policies/day-loop-voice.md +102 -0
  6. package/atris/policies/outbound-artifact-gate.md +2 -0
  7. package/atris/skills/design/SKILL.md +56 -32
  8. package/atris/skills/endgame/SKILL.md +12 -6
  9. package/atris/skills/engines/SKILL.md +22 -4
  10. package/atris/skills/fable-method/SKILL.md +66 -0
  11. package/atris/skills/improve/SKILL.md +65 -45
  12. package/atris/skills/youtube/SKILL.md +10 -1
  13. package/atris.md +2 -0
  14. package/ax +147 -19
  15. package/bin/atris.js +576 -267
  16. package/commands/activate.js +194 -88
  17. package/commands/agents.js +166 -0
  18. package/commands/autoland.js +459 -107
  19. package/commands/autopilot-front.js +20 -2
  20. package/commands/autopilot.js +118 -2
  21. package/commands/avail.js +407 -0
  22. package/commands/bench.js +188 -0
  23. package/commands/brain.js +3 -0
  24. package/commands/brief.js +651 -0
  25. package/commands/business-sync.js +192 -6
  26. package/commands/clean.js +50 -24
  27. package/commands/close.js +1083 -0
  28. package/commands/cloud.js +245 -0
  29. package/commands/compile.js +292 -1
  30. package/commands/computer.js +150 -3
  31. package/commands/dream.js +365 -0
  32. package/commands/drill.js +371 -0
  33. package/commands/engine.js +993 -32
  34. package/commands/experiments.js +28 -0
  35. package/commands/feedback.js +34 -12
  36. package/commands/fleet-report.js +206 -0
  37. package/commands/gm.js +23 -0
  38. package/commands/goal.js +247 -0
  39. package/commands/improve.js +642 -26
  40. package/commands/init.js +72 -44
  41. package/commands/interview.js +67 -1
  42. package/commands/land.js +152 -52
  43. package/commands/lifecycle.js +39 -3
  44. package/commands/log.js +84 -1
  45. package/commands/loops.js +220 -16
  46. package/commands/meet.js +220 -0
  47. package/commands/member.js +511 -34
  48. package/commands/mission.js +3144 -338
  49. package/commands/next.js +137 -0
  50. package/commands/now.js +220 -25
  51. package/commands/one-lap.js +776 -0
  52. package/commands/orb.js +314 -0
  53. package/commands/pack-craft.js +179 -0
  54. package/commands/pack.js +823 -0
  55. package/commands/play.js +3 -2
  56. package/commands/probe.js +30 -3
  57. package/commands/pulse.js +241 -46
  58. package/commands/push.js +260 -82
  59. package/commands/rainmaker.js +49 -0
  60. package/commands/report.js +415 -0
  61. package/commands/scout.js +147 -0
  62. package/commands/search.js +363 -0
  63. package/commands/skill.js +47 -3
  64. package/commands/slop.js +50 -2
  65. package/commands/soul.js +1 -1
  66. package/commands/stream.js +861 -0
  67. package/commands/study.js +693 -0
  68. package/commands/sync.js +67 -54
  69. package/commands/task.js +1346 -117
  70. package/commands/team.js +73 -0
  71. package/commands/verify.js +96 -0
  72. package/commands/watch.js +303 -0
  73. package/commands/wish.js +500 -0
  74. package/commands/workflow.js +11 -5
  75. package/commands/worktree.js +234 -13
  76. package/commands/xp.js +29 -11
  77. package/lib/auto-accept-certified.js +331 -34
  78. package/lib/autoland.js +319 -54
  79. package/lib/ax-auto-lane.js +96 -0
  80. package/lib/bench/context.js +147 -0
  81. package/lib/bench/engines.js +141 -0
  82. package/lib/bench/report.js +140 -0
  83. package/lib/bench/runner.js +512 -0
  84. package/lib/brief-ledger.js +350 -0
  85. package/lib/cloud-mission.js +259 -0
  86. package/lib/codex-flight.js +154 -0
  87. package/lib/default-runner.js +45 -0
  88. package/lib/default-verifier.js +70 -0
  89. package/lib/engine-registry.js +232 -0
  90. package/lib/experiments/daily.js +640 -0
  91. package/lib/fleet.js +2219 -67
  92. package/lib/improve-vitals-html.js +171 -0
  93. package/lib/known-commands.js +58 -0
  94. package/lib/loop-doctor.js +416 -0
  95. package/lib/member-switches.js +144 -0
  96. package/lib/mission-room.js +1 -0
  97. package/lib/mission-root.js +52 -0
  98. package/lib/next-moves.js +327 -10
  99. package/lib/one-lap-validator.js +60 -0
  100. package/lib/orb-context.js +477 -0
  101. package/lib/orb-scorecard.js +224 -0
  102. package/lib/policy-lessons.js +52 -1
  103. package/lib/pulse.js +277 -3
  104. package/lib/receipt-block.js +168 -0
  105. package/lib/receipt-evidence.js +65 -4
  106. package/lib/router-brain.js +352 -0
  107. package/lib/runner-command.js +10 -0
  108. package/lib/self-drive.js +258 -0
  109. package/lib/short-name.js +103 -0
  110. package/lib/spawn-env.js +18 -0
  111. package/lib/state-detection.js +56 -1
  112. package/lib/sync-status.js +59 -0
  113. package/lib/task-db.js +108 -29
  114. package/lib/task-proof.js +23 -1
  115. package/lib/team-presence.js +260 -0
  116. package/lib/tool-result-encode.js +7 -0
  117. package/lib/trust-tiers.js +90 -0
  118. package/lib/usage.js +107 -0
  119. package/lib/voice-gate.js +163 -0
  120. package/lib/wish-audit.js +1368 -0
  121. package/lib/wish-delegate.js +1840 -0
  122. package/lib/wish-design.js +110 -0
  123. package/lib/wish-stats.js +183 -0
  124. package/lib/wish-store.js +354 -0
  125. package/lib/zip.js +221 -0
  126. package/package.json +3 -1
  127. package/templates/loops/atris/loops/LOOPS.md +55 -0
  128. package/templates/loops/atris/loops/TICK.md +24 -0
  129. package/templates/loops/atris/loops/feedback.md +22 -0
  130. package/templates/loops/atris/loops/quality.md +22 -0
  131. package/templates/loops/atris/wiki/systems/loops.md +41 -0
  132. package/utils/api.js +5 -1
  133. package/utils/auth.js +57 -21
@@ -0,0 +1,500 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ analyzeWishParts,
5
+ auditWish,
6
+ deriveVerifyPlan,
7
+ inferBudgetTier,
8
+ missingNamedInputs,
9
+ sharesMeaningfulWords,
10
+ verifyOutcomeText,
11
+ withWishAnswerAuditContext,
12
+ } = require('../lib/wish-audit');
13
+ const {
14
+ appendWishRecord,
15
+ improveWishes,
16
+ nextWishNumber,
17
+ readWishEvents,
18
+ readWishes,
19
+ stampIso,
20
+ stateFile,
21
+ wishLessonsSummary,
22
+ wishId,
23
+ } = require('../lib/wish-store');
24
+ const {
25
+ answerWish,
26
+ captureWishToJournal,
27
+ grantWish,
28
+ printBoard,
29
+ printList,
30
+ printReviewNudges,
31
+ printRewards,
32
+ reviewWish,
33
+ runAgainWish,
34
+ parseMetricExpression,
35
+ runCapturedWish,
36
+ sayWish,
37
+ sweepWishes,
38
+ waitingOperatorWishes,
39
+ REVIEW_WORD_SCORES,
40
+ } = require('../lib/wish-delegate');
41
+ const { enqueueCloudMission, VALID_CLOUD_LANES } = require('../lib/cloud-mission');
42
+ const { printWishStats } = require('../lib/wish-stats');
43
+ const { parseVerifyCommand } = require('../lib/auto-accept-certified');
44
+
45
+ function showHelp() {
46
+ console.log('');
47
+ console.log('Usage: atris wish "<plain sentence>" [--engine <id>] [--verify "<cmd>"] [--as builder] [--metric "<name><op><target>"] [--json] [--no-mission]');
48
+ console.log(' atris wish "<plain sentence>" --cloud [--lane fast|pro|max] [--agent <id>]');
49
+ console.log(' --metric example: --metric "stripe.active_subs>=10" (ops: >=, >, <=, <, ==)');
50
+ console.log(' atris wish list [--all]');
51
+ console.log(' atris wish show <n|id>');
52
+ console.log(' atris wish board');
53
+ console.log(' atris wish answer "<your answer>" [--engine <id>] [--json] [--no-mission]');
54
+ console.log(' atris wish grant <n> "<answer>" [--engine <id>] [--json] [--no-mission]');
55
+ console.log(' atris wish say "<note>" [wish-id]');
56
+ console.log(' atris wish close <n|id> [<n|id> ...]');
57
+ console.log(' atris wish again <id> "<tweak text>" [--engine <id>]');
58
+ console.log(' atris wish review [<id>|latest] "<one sentence>" [--score <-1|0|1 or 1-5>]');
59
+ console.log(' atris wish rewards');
60
+ console.log(' atris wish stats [--write]');
61
+ console.log(' atris wish improve');
62
+ console.log(' atris wish lessons');
63
+ console.log('');
64
+ }
65
+
66
+ function cleanWishShowRef(value) {
67
+ return String(value || '').trim().replace(/^#/, '');
68
+ }
69
+
70
+ function wishShowHint(value) {
71
+ const suffix = cleanWishShowRef(value);
72
+ return suffix && /^\d+$/.test(suffix)
73
+ ? `Use: atris wish show ${suffix}`
74
+ : 'Usage: atris wish show <n|id>';
75
+ }
76
+
77
+ function wishHistoryRows(root, wish) {
78
+ return readWishEvents(root).filter((event) => {
79
+ if (!event) return false;
80
+ if (event.id && event.id === wish.id) return true;
81
+ return event.wish_id && event.wish_id === wish.id;
82
+ });
83
+ }
84
+
85
+ function printWishShow(root, rawRef) {
86
+ const ref = cleanWishShowRef(rawRef);
87
+ if (!ref) {
88
+ console.error(wishShowHint(rawRef));
89
+ return 2;
90
+ }
91
+ const number = Number(ref);
92
+ const wishes = readWishes(root);
93
+ const wish = Number.isInteger(number) && number > 0
94
+ ? wishes.find((row) => Number(row.n) === number)
95
+ : wishes.find((row) => String(row.id || '') === ref);
96
+ if (!wish) {
97
+ console.error(`No wish found for ${rawRef}. Try: atris wish list --all`);
98
+ return 1;
99
+ }
100
+ const questions = Array.isArray(wish.questions) ? wish.questions.filter(Boolean) : [];
101
+ console.log(`Wish #${wish.n || '?'} ${wish.id}`);
102
+ console.log(`ID: ${wish.id}`);
103
+ console.log(`Status: ${wish.status || 'unknown'}`);
104
+ console.log(`Text: ${wish.text || ''}`);
105
+ console.log('Questions:');
106
+ if (questions.length) questions.forEach((question) => console.log(`- ${question}`));
107
+ else console.log('- none');
108
+ console.log('Rows:');
109
+ const history = wishHistoryRows(root, wish);
110
+ if (!history.length) console.log('- none');
111
+ history.forEach((event, index) => {
112
+ const parts = [event.ts || 'unknown-time', event.kind || event.status || 'event'];
113
+ if (event.answer) parts.push(`answer: ${event.answer}`);
114
+ if (event.review_text) parts.push(`review: ${event.review_text}`);
115
+ if (event.note) parts.push(`note: ${event.note}`);
116
+ if (Array.isArray(event.filled_slots) && event.filled_slots.length) {
117
+ parts.push(`filled: ${event.filled_slots.map((slot) => slot.value).join(', ')}`);
118
+ }
119
+ console.log(`- row ${index + 1}: ${parts.join(' | ')}`);
120
+ console.log(` text: ${event.text || wish.text || ''}`);
121
+ console.log(` status: ${event.status || event.kind || 'event'}`);
122
+ const rowQuestions = Array.isArray(event.questions) ? event.questions.filter(Boolean) : [];
123
+ console.log(` questions: ${rowQuestions.length ? rowQuestions.join(' | ') : 'none'}`);
124
+ console.log(` history: ${parts.slice(2).join(' | ') || 'none'}`);
125
+ });
126
+ return 0;
127
+ }
128
+
129
+ function closeWishes(root, refs) {
130
+ const cleaned = refs.map(cleanWishShowRef).filter(Boolean);
131
+ if (!cleaned.length) {
132
+ console.error('Usage: atris wish close <n|id> [<n|id> ...]');
133
+ return 2;
134
+ }
135
+ const wishes = readWishes(root);
136
+ const closedBy = process.env.ATRIS_AGENT_ID || process.env.USER || 'operator';
137
+ let missing = 0;
138
+ for (const ref of cleaned) {
139
+ const number = Number(ref);
140
+ const wish = Number.isInteger(number) && number > 0
141
+ ? wishes.find((row) => Number(row.n) === number)
142
+ : wishes.find((row) => String(row.id || '') === ref);
143
+ if (!wish) {
144
+ console.error(`No wish found for ${ref}. Try: atris wish list --all`);
145
+ missing += 1;
146
+ continue;
147
+ }
148
+ if (String(wish.status || '') === 'closed') {
149
+ console.log(`Wish #${wish.n || '?'} is already closed.`);
150
+ continue;
151
+ }
152
+ appendWishRecord(root, {
153
+ id: wish.id,
154
+ n: wish.n || undefined,
155
+ ts: stampIso(),
156
+ text: wish.text || undefined,
157
+ kind: 'close',
158
+ status: 'closed',
159
+ closed_by: closedBy,
160
+ });
161
+ console.log(`Closed wish #${wish.n || '?'}: ${String(wish.text || '').slice(0, 60)}`);
162
+ }
163
+ return missing ? 1 : 0;
164
+ }
165
+
166
+ function appendFilledAnswerSlot(root, context) {
167
+ if (!context || !context.filled_slot || !context.wish_id) return;
168
+ appendWishRecord(root, {
169
+ id: context.wish_id,
170
+ n: context.n || undefined,
171
+ ts: stampIso(),
172
+ text: context.text || undefined,
173
+ kind: 'slot',
174
+ filled_slots: [context.filled_slot],
175
+ });
176
+ }
177
+
178
+ function unquote(value) {
179
+ const text = String(value);
180
+ if ((text.startsWith('"') && text.endsWith('"')) || (text.startsWith("'") && text.endsWith("'"))) {
181
+ return text.slice(1, -1);
182
+ }
183
+ return text;
184
+ }
185
+
186
+ function parseFlagArgs(args, valueFlagNames = []) {
187
+ const valueFlags = new Set(valueFlagNames);
188
+ const values = {};
189
+ const positionals = [];
190
+ const missingValueFlags = [];
191
+ for (let i = 0; i < args.length; i += 1) {
192
+ const arg = String(args[i] || '');
193
+ if (arg.startsWith('--')) {
194
+ const flagName = arg.split('=')[0];
195
+ if (valueFlags.has(flagName)) {
196
+ const prefix = flagName + '=';
197
+ if (arg.startsWith(prefix)) {
198
+ values[flagName] = unquote(arg.slice(prefix.length));
199
+ if (!String(values[flagName]).trim()) missingValueFlags.push(flagName);
200
+ }
201
+ else if (args[i + 1] && !String(args[i + 1]).startsWith('--')) {
202
+ values[flagName] = unquote(args[i + 1]);
203
+ i += 1;
204
+ } else missingValueFlags.push(flagName);
205
+ }
206
+ continue;
207
+ }
208
+ positionals.push(args[i]);
209
+ }
210
+ return { positionals, values, missingValueFlags };
211
+ }
212
+
213
+ function wishOptions(args) {
214
+ const cloud = args.includes('--cloud');
215
+ const parsed = parseFlagArgs(args, cloud
216
+ ? ['--engine', '--as', '--metric', '--verify', '--lane', '--agent']
217
+ : ['--engine', '--as', '--metric', '--verify']);
218
+ return {
219
+ asJson: args.includes('--json'),
220
+ cloud,
221
+ noMission: args.includes('--no-mission'),
222
+ singleTask: args.includes('--one-lap'),
223
+ asMode: String(parsed.values['--as'] || '').trim(),
224
+ agentId: String(parsed.values['--agent'] || '').trim(),
225
+ engineOverride: String(parsed.values['--engine'] || '').trim(),
226
+ lane: String(parsed.values['--lane'] || '').trim() || 'fast',
227
+ metricRaw: String(parsed.values['--metric'] || '').trim(),
228
+ verifyRaw: String(parsed.values['--verify'] || '').trim(),
229
+ verifyMissing: parsed.missingValueFlags.includes('--verify'),
230
+ positionals: parsed.positionals,
231
+ };
232
+ }
233
+
234
+ function cloudWishOptionError(args, options) {
235
+ if (args.some((arg) => String(arg) === '--lane=')) {
236
+ return '--lane requires one of: fast, pro, max';
237
+ }
238
+ const laneIndex = args.findIndex((arg) => String(arg) === '--lane');
239
+ if (laneIndex >= 0 && (!args[laneIndex + 1] || String(args[laneIndex + 1]).startsWith('--'))) {
240
+ return '--lane requires one of: fast, pro, max';
241
+ }
242
+ const agentIndex = args.findIndex((arg) => String(arg) === '--agent');
243
+ if (args.some((arg) => String(arg) === '--agent=')) return '--agent requires an agent id';
244
+ if (agentIndex >= 0 && (!args[agentIndex + 1] || String(args[agentIndex + 1]).startsWith('--'))) {
245
+ return '--agent requires an agent id';
246
+ }
247
+ if (!VALID_CLOUD_LANES.has(options.lane)) {
248
+ return `invalid --lane "${options.lane}". expected fast, pro, or max`;
249
+ }
250
+ return '';
251
+ }
252
+
253
+ async function runCloudWish(text, root, options, deps = {}) {
254
+ try {
255
+ const mission = await enqueueCloudMission({
256
+ text,
257
+ lane: options.lane,
258
+ ...(options.agentId ? { agentId: options.agentId } : {}),
259
+ }, deps);
260
+ const ts = deps.ts || stampIso();
261
+ const record = {
262
+ id: deps.wishId || wishId(text, ts),
263
+ n: nextWishNumber(root),
264
+ ts,
265
+ text,
266
+ status: 'delegated',
267
+ dispatched_at: ts,
268
+ cloud: true,
269
+ task_id: mission.task_id,
270
+ lane: mission.lane || options.lane,
271
+ };
272
+ captureWishToJournal(text, root);
273
+ appendWishRecord(root, record);
274
+ const log = deps.log || console.log;
275
+ const subject = String(text || '').trim().replace(/[.!?]+/g, '').replace(/\s+/g, ' ').toLowerCase();
276
+ log(`i sent ${JSON.stringify(subject)} to cloud work, and we know it is queued because the cloud accepted it.`);
277
+ log(`task_id: ${record.task_id}`);
278
+ log(`watch: atris mission status --cloud ${record.task_id} --watch`);
279
+ return 0;
280
+ } catch (error) {
281
+ (deps.error || console.error)(error.message || String(error));
282
+ return error.exitCode || 1;
283
+ }
284
+ }
285
+
286
+ function resolveVerifyOption(options) {
287
+ if (options.verifyMissing) return 'wish --verify needs a command.';
288
+ if (!options.verifyRaw) return null;
289
+ const parsed = parseVerifyCommand(options.verifyRaw);
290
+ if (!parsed.ok) return `wish --verify is not allowed (${parsed.reason}).`;
291
+ options.verifyPlan = {
292
+ command: options.verifyRaw,
293
+ outcome: 'the supplied verifier passes',
294
+ status: 'explicit',
295
+ };
296
+ options.userVerifyProvided = true;
297
+ return null;
298
+ }
299
+
300
+ // Parses --metric into options.metric; returns an error message on malformed input.
301
+ function resolveMetricOption(options) {
302
+ if (!options.metricRaw) return null;
303
+ const parsed = parseMetricExpression(options.metricRaw);
304
+ if (!parsed.ok) return parsed.message;
305
+ options.metric = parsed;
306
+ return null;
307
+ }
308
+
309
+ function parseReviewScore(args) {
310
+ const hasScore = args.some((arg) => {
311
+ const text = String(arg || '');
312
+ return text === '--score' || text.startsWith('--score=');
313
+ });
314
+ if (!hasScore) return { ok: true, value: null };
315
+ const parsed = parseFlagArgs(args, ['--score']);
316
+ const raw = String(parsed.values['--score'] || '').trim();
317
+ const allowed = new Set([-1, 0, 1, 2, 3, 4, 5]);
318
+ const value = Number(raw);
319
+ if (!raw || !Number.isInteger(value) || !allowed.has(value)) {
320
+ return {
321
+ ok: false,
322
+ message: 'wish review --score needs one of -1, 0, 1, 2, 3, 4, or 5.',
323
+ };
324
+ }
325
+ return { ok: true, value };
326
+ }
327
+
328
+ function reviewOptions(args) {
329
+ const parsed = parseFlagArgs(args, ['--score']);
330
+ return {
331
+ score: parseReviewScore(args),
332
+ positionals: parsed.positionals,
333
+ };
334
+ }
335
+
336
+ function printLessons(root) {
337
+ const summary = wishLessonsSummary(root);
338
+ console.log('');
339
+ console.log(`Wish lessons: ${summary.reviews} scored reviews, average ${summary.avg_all === null ? 'none yet' : summary.avg_all} (last 5: ${summary.avg_last5 === null ? 'none yet' : summary.avg_last5})`);
340
+ console.log('');
341
+ console.log(summary.lessons.length ? 'Alive:' : 'Alive: none yet. Review a wish and the next run distills your review into a lesson.');
342
+ for (const line of summary.lessons) console.log(` ${line}`);
343
+ if (summary.pruned.length) {
344
+ console.log('');
345
+ console.log('Pruned (did not move the score):');
346
+ for (const line of summary.pruned) console.log(` ${line}`);
347
+ }
348
+ if (summary.inbox) {
349
+ console.log('');
350
+ console.log(`Inbox: ${summary.inbox} raw review${summary.inbox === 1 ? '' : 's'} waiting to be distilled on the next wish.`);
351
+ }
352
+ console.log('');
353
+ return 0;
354
+ }
355
+
356
+ function wishCommand(args = [], deps = {}) {
357
+ const first = String(args[0] || '').trim();
358
+ if (!first) {
359
+ showHelp();
360
+ printReviewNudges(process.cwd());
361
+ return 2;
362
+ }
363
+ if (first === '--help' || first === '-h' || first === 'help') {
364
+ showHelp();
365
+ return 0;
366
+ }
367
+ if (first === 'show') {
368
+ return printWishShow(process.cwd(), args[1]);
369
+ }
370
+ if (first === 'close') {
371
+ return closeWishes(process.cwd(), args.slice(1));
372
+ }
373
+ if ((first === 'status' || first === 'get') && /^\#?\d+$/.test(String(args[1] || '').trim())) {
374
+ console.error(wishShowHint(args[1]));
375
+ return 2;
376
+ }
377
+ if (first === 'get') {
378
+ console.error(wishShowHint(args[1]));
379
+ return 2;
380
+ }
381
+ if (first === 'list' || first === 'ls' || first === 'status') {
382
+ return printList(process.cwd(), { all: args.includes('--all') });
383
+ }
384
+ if (first === 'board') {
385
+ return printBoard(process.cwd());
386
+ }
387
+ if (first === 'rewards') {
388
+ return printRewards(process.cwd());
389
+ }
390
+ if (first === 'stats') {
391
+ return printWishStats(process.cwd(), { write: args.includes('--write') });
392
+ }
393
+ if (first === 'improve') {
394
+ return improveWishes(process.cwd());
395
+ }
396
+ if (first === 'lessons') {
397
+ return printLessons(process.cwd());
398
+ }
399
+ if (first === 'again') {
400
+ const options = wishOptions(args);
401
+ if (options.asMode && options.asMode !== 'builder') {
402
+ console.error('wish --as only supports builder.');
403
+ return 2;
404
+ }
405
+ return runAgainWish(options.positionals, process.cwd(), options);
406
+ }
407
+ if (first === 'answer') {
408
+ const options = wishOptions(args);
409
+ const root = process.cwd();
410
+ const context = {
411
+ root,
412
+ answer: options.positionals.slice(1).join(' ').trim(),
413
+ };
414
+ const code = withWishAnswerAuditContext(context, () => answerWish(options.positionals, root, options));
415
+ if (code !== 2) appendFilledAnswerSlot(root, context);
416
+ return code;
417
+ }
418
+ if (first === 'grant') {
419
+ const options = wishOptions(args);
420
+ return grantWish(options.positionals, process.cwd(), options);
421
+ }
422
+ if (first === 'review') {
423
+ const options = reviewOptions(args);
424
+ if (!options.score.ok) {
425
+ console.error(options.score.message);
426
+ return 2;
427
+ }
428
+ const reviewResult = reviewWish(options.positionals, process.cwd(), { reviewScore: options.score.value });
429
+ if (reviewResult === 0) {
430
+ try {
431
+ improveWishes(process.cwd(), { quiet: true });
432
+ } catch {
433
+ // best-effort: lessons ingest must never fail a review
434
+ }
435
+ }
436
+ return reviewResult;
437
+ }
438
+ if (first === 'say') {
439
+ return sayWish(args, process.cwd());
440
+ }
441
+ const options = wishOptions(args);
442
+ if (options.cloud) {
443
+ const optionError = cloudWishOptionError(args, options);
444
+ if (optionError) {
445
+ (deps.error || console.error)(optionError);
446
+ return 2;
447
+ }
448
+ }
449
+ if (options.asMode && options.asMode !== 'builder') {
450
+ console.error('wish --as only supports builder.');
451
+ return 2;
452
+ }
453
+ const metricError = resolveMetricOption(options);
454
+ if (metricError) {
455
+ console.error(metricError);
456
+ return 2;
457
+ }
458
+ const verifyError = resolveVerifyOption(options);
459
+ if (verifyError) {
460
+ console.error(verifyError);
461
+ return 2;
462
+ }
463
+ // "atris wish claim <text>" is command confusion; the word claim is not part of the wish.
464
+ const positionals = first === 'claim' && options.positionals.length > 1
465
+ ? options.positionals.slice(1)
466
+ : options.positionals;
467
+ const text = positionals.join(' ').trim();
468
+ if (!text) {
469
+ showHelp();
470
+ return 2;
471
+ }
472
+ if (options.cloud) return runCloudWish(text, process.cwd(), options, deps);
473
+ return runCapturedWish(text, process.cwd(), options);
474
+ }
475
+
476
+ module.exports = {
477
+ wishCommand,
478
+ analyzeWishParts,
479
+ auditWish,
480
+ captureWishToJournal,
481
+ deriveVerifyPlan,
482
+ improveWishes,
483
+ inferBudgetTier,
484
+ missingNamedInputs,
485
+ printBoard,
486
+ printReviewNudges,
487
+ printRewards,
488
+ readWishEvents,
489
+ readWishes,
490
+ runCloudWish,
491
+ runAgainWish,
492
+ sayWish,
493
+ sharesMeaningfulWords,
494
+ stateFile,
495
+ sweepWishes,
496
+ verifyOutcomeText,
497
+ withWishAnswerAuditContext,
498
+ waitingOperatorWishes,
499
+ REVIEW_WORD_SCORES,
500
+ };
@@ -1,6 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { getLogPath } = require('../lib/journal');
4
+ const { encodeToolResult } = require('../lib/tool-result-encode');
4
5
 
5
6
  function wrapWorkflowText(text, width = 76) {
6
7
  const normalized = String(text || '').replace(/\s+/g, ' ').trim();
@@ -139,7 +140,7 @@ function postToolResult(callId, result, base = 'http://127.0.0.1:8000') {
139
140
  const url = new URL('/api/atris2/turn/tool-result', base);
140
141
  const transport = url.protocol === 'https:' ? require('https') : require('http');
141
142
  return new Promise((resolve, reject) => {
142
- const postData = JSON.stringify({ call_id: callId, result });
143
+ const postData = JSON.stringify({ call_id: callId, result_base64: encodeToolResult(result) });
143
144
  const req = transport.request({
144
145
  hostname: url.hostname,
145
146
  port: url.port || (url.protocol === 'https:' ? 443 : 80),
@@ -630,9 +631,9 @@ async function planAtris(userInput = null) {
630
631
  userPrompt += ` - Include file:line references from MAP.md\n`;
631
632
  userPrompt += ` - List dependencies between tasks\n`;
632
633
  userPrompt += ` - Add acceptance criteria for each task\n\n`;
633
- userPrompt += `STEP 4: Write tasks to atris/TODO.md\n`;
634
- userPrompt += ` - Add to ## Backlog section\n`;
635
- userPrompt += ` - Format: - **T#:** Description [explore|execute]\n`;
634
+ userPrompt += `STEP 4: Create tasks through the task database\n`;
635
+ userPrompt += ` - Use bash to run 'atris task add' or the task database calls; do not hand-edit TODO.md\n`;
636
+ userPrompt += ` - The CLI will re-render atris/TODO.md after this workflow step completes\n`;
636
637
  userPrompt += ` - Each task: one job, clear exit condition\n`;
637
638
  userPrompt += ` - Include file:line references from MAP.md\n\n`;
638
639
  userPrompt += `STEP 5: Log to your journal\n`;
@@ -651,7 +652,7 @@ async function planAtris(userInput = null) {
651
652
  try {
652
653
  await executeCodeExecution({
653
654
  prompt: userPrompt,
654
- allowedTools: ['Read', 'Write', 'Edit'], // Navigator needs to write TODO.md
655
+ allowedTools: ['Read', 'Bash'], // Task creation must go through atris task / the database
655
656
  permissionMode: 'default',
656
657
  maxTurns: 15,
657
658
  systemPrompt,
@@ -686,6 +687,11 @@ async function planAtris(userInput = null) {
686
687
  console.error(`\n❌ Execution error: ${error.message}`);
687
688
  },
688
689
  });
690
+ try {
691
+ require('./task').autoRenderTodoFromDb(process.cwd());
692
+ } catch {
693
+ // Rendering is best-effort; task-db state remains authoritative.
694
+ }
689
695
 
690
696
  console.log('\n');
691
697
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');