atris 3.16.1 → 3.22.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 (65) hide show
  1. package/README.md +32 -7
  2. package/atris/skills/atris/SKILL.md +15 -2
  3. package/atris/skills/atris-feedback/SKILL.md +7 -0
  4. package/atris/skills/design/SKILL.md +29 -2
  5. package/atris/skills/engines/SKILL.md +44 -0
  6. package/atris/skills/flow/SKILL.md +1 -1
  7. package/atris/skills/wake/SKILL.md +37 -0
  8. package/atris/skills/youtube/SKILL.md +13 -39
  9. package/atris/team/validator/MEMBER.md +1 -0
  10. package/atris/wiki/concepts/agent-activation-contract.md +3 -3
  11. package/atris/wiki/concepts/workspace-initialization-contract.md +3 -3
  12. package/atris/wiki/index.md +1 -0
  13. package/atris.md +43 -19
  14. package/bin/atris.js +413 -31
  15. package/commands/agent-spawn.js +480 -0
  16. package/commands/analytics.js +6 -3
  17. package/commands/apps.js +11 -0
  18. package/commands/autopilot.js +42 -18
  19. package/commands/brain.js +74 -7
  20. package/commands/brainstorm.js +9 -58
  21. package/commands/clean.js +1 -4
  22. package/commands/compile.js +9 -4
  23. package/commands/console.js +8 -3
  24. package/commands/deck.js +184 -0
  25. package/commands/init.js +22 -11
  26. package/commands/lesson.js +76 -0
  27. package/commands/member.js +252 -48
  28. package/commands/mission.js +405 -13
  29. package/commands/now.js +4 -2
  30. package/commands/probe.js +105 -27
  31. package/commands/pulse.js +504 -0
  32. package/commands/radar.js +1 -0
  33. package/commands/recap.js +71 -25
  34. package/commands/run.js +615 -22
  35. package/commands/site.js +48 -0
  36. package/commands/slop.js +307 -0
  37. package/commands/spaceship.js +39 -0
  38. package/commands/sync.js +0 -2
  39. package/commands/task.js +429 -37
  40. package/commands/theme.js +217 -0
  41. package/commands/verify.js +7 -3
  42. package/lib/activity-stream.js +166 -0
  43. package/lib/auto-accept-certified.js +23 -1
  44. package/lib/context-gatherer.js +170 -0
  45. package/lib/deck-from-md.js +110 -0
  46. package/lib/escape-regexp.js +13 -0
  47. package/lib/file-ops.js +6 -3
  48. package/lib/html-render.js +257 -0
  49. package/lib/journal.js +1 -1
  50. package/lib/lesson-contradiction.js +113 -0
  51. package/lib/memory-view.js +95 -0
  52. package/lib/policy-lessons.js +3 -2
  53. package/lib/pulse.js +401 -0
  54. package/lib/runner-command.js +156 -0
  55. package/lib/site.js +114 -0
  56. package/lib/slides-deck.js +237 -0
  57. package/lib/state-detection.js +1 -4
  58. package/lib/task-db.js +101 -4
  59. package/lib/task-proof.js +1 -1
  60. package/lib/theme.js +264 -0
  61. package/lib/todo-fallback.js +2 -1
  62. package/lib/todo-sections.js +33 -0
  63. package/package.json +1 -2
  64. package/utils/api.js +14 -2
  65. package/atris/atrisDev.md +0 -717
@@ -0,0 +1,480 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const crypto = require('crypto');
4
+ const { spawnSync } = require('child_process');
5
+
6
+ const SPAWN_SCHEMA = 'atris.agent_spawn.v1';
7
+ const DOGFOOD_SCHEMA = 'atris.agent_cli_dogfood.v1';
8
+ const SPAWN_STATE_REL = path.join('.atris', 'state', 'agent_spawns.jsonl');
9
+ const ALLOWED_ENGINES = new Set(['manual', 'codex', 'claude', 'cursor', 'devin', 'droid']);
10
+ const DOGFOOD_ENGINES = new Set(['devin', 'droid']);
11
+
12
+ function nowIso() {
13
+ return new Date().toISOString();
14
+ }
15
+
16
+ function spawnStatePath(root = process.cwd()) {
17
+ return path.join(root, SPAWN_STATE_REL);
18
+ }
19
+
20
+ function ensureSpawnState(root = process.cwd()) {
21
+ const statePath = spawnStatePath(root);
22
+ fs.mkdirSync(path.dirname(statePath), { recursive: true });
23
+ return statePath;
24
+ }
25
+
26
+ function readJsonl(filePath) {
27
+ try {
28
+ if (!fs.existsSync(filePath)) return [];
29
+ return fs.readFileSync(filePath, 'utf8')
30
+ .split(/\r?\n/)
31
+ .map(line => line.trim())
32
+ .filter(Boolean)
33
+ .map((line) => JSON.parse(line));
34
+ } catch {
35
+ return [];
36
+ }
37
+ }
38
+
39
+ function loadSpawnRequests(root = process.cwd()) {
40
+ return readJsonl(spawnStatePath(root));
41
+ }
42
+
43
+ function appendSpawnRequest(root, request) {
44
+ const statePath = ensureSpawnState(root);
45
+ fs.appendFileSync(statePath, `${JSON.stringify(request)}\n`);
46
+ return statePath;
47
+ }
48
+
49
+ function idForSpawn(role) {
50
+ const stamp = new Date().toISOString().replace(/[-:.TZ]/g, '').slice(0, 14);
51
+ const suffix = crypto.randomBytes(3).toString('hex');
52
+ const cleanRole = String(role || 'agent').toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 24) || 'agent';
53
+ return `spawn-${stamp}-${cleanRole}-${suffix}`;
54
+ }
55
+
56
+ function flagValue(args, names) {
57
+ for (const name of names) {
58
+ const inline = args.find(arg => arg.startsWith(`${name}=`));
59
+ if (inline) return inline.slice(name.length + 1);
60
+ const idx = args.indexOf(name);
61
+ if (idx !== -1 && idx < args.length - 1 && !String(args[idx + 1]).startsWith('--')) return args[idx + 1];
62
+ }
63
+ return null;
64
+ }
65
+
66
+ function hasFlag(args, names) {
67
+ return names.some(name => args.includes(name));
68
+ }
69
+
70
+ function positionalArgs(args) {
71
+ const out = [];
72
+ for (let i = 0; i < args.length; i++) {
73
+ const arg = args[i];
74
+ if (arg.startsWith('--')) {
75
+ if (!arg.includes('=') && i < args.length - 1 && !String(args[i + 1]).startsWith('--')) i++;
76
+ continue;
77
+ }
78
+ out.push(arg);
79
+ }
80
+ return out;
81
+ }
82
+
83
+ function shellQuote(value) {
84
+ return `'${String(value).replace(/'/g, `'\\''`)}'`;
85
+ }
86
+
87
+ function timestampForFile(date = new Date()) {
88
+ return date.toISOString().replace(/[:.]/g, '-');
89
+ }
90
+
91
+ function runsDir(root = process.cwd()) {
92
+ const atrisRuns = path.join(root, 'atris', 'runs');
93
+ if (fs.existsSync(path.join(root, 'atris'))) return atrisRuns;
94
+ return path.join(root, '.atris', 'runs');
95
+ }
96
+
97
+ function commandOnPath(name, deps = {}) {
98
+ if (typeof deps.commandOnPath === 'function') return deps.commandOnPath(name);
99
+ const runner = deps.spawnSync || spawnSync;
100
+ const result = runner('which', [name], { encoding: 'utf8', timeout: 1000 });
101
+ return result.status === 0 ? String(result.stdout || '').trim() : null;
102
+ }
103
+
104
+ function runCli(argv, deps = {}, options = {}) {
105
+ const runner = deps.spawnSync || spawnSync;
106
+ const [cmd, ...args] = argv;
107
+ const started = Date.now();
108
+ const result = runner(cmd, args, {
109
+ cwd: options.cwd || deps.root || process.cwd(),
110
+ encoding: 'utf8',
111
+ timeout: options.timeoutMs || 30000,
112
+ maxBuffer: 1024 * 1024 * 4,
113
+ env: options.env || process.env,
114
+ });
115
+ return {
116
+ command: argv.join(' '),
117
+ status: result.status,
118
+ signal: result.signal || null,
119
+ ok: !result.error && result.status === 0,
120
+ duration_ms: Date.now() - started,
121
+ error: result.error ? result.error.message : null,
122
+ stdout: String(result.stdout || ''),
123
+ stderr: String(result.stderr || ''),
124
+ };
125
+ }
126
+
127
+ function buildDelegatePrompt({ role, task, cwd }) {
128
+ return [
129
+ `You are an Atris delegated ${role}.`,
130
+ '',
131
+ `Workspace: ${cwd || process.cwd()}`,
132
+ `Task: ${task}`,
133
+ '',
134
+ 'Do one bounded proof-backed pass.',
135
+ 'Do not revert unrelated edits.',
136
+ 'Return changed files, verifier commands, and remaining risk.',
137
+ ].join('\n');
138
+ }
139
+
140
+ function commandForEngine(request) {
141
+ const prompt = buildDelegatePrompt(request);
142
+ if (request.engine === 'codex') return `codex exec ${shellQuote(prompt)}`;
143
+ if (request.engine === 'claude') return `claude -p ${shellQuote(prompt)}`;
144
+ if (request.engine === 'cursor') return `cursor-agent ${shellQuote(prompt)}`;
145
+ if (request.engine === 'devin') return `devin --model glm-5.2 --permission-mode auto -p ${shellQuote(prompt)}`;
146
+ if (request.engine === 'droid') return `droid exec --model glm-5.2 --reasoning-effort off ${shellQuote(prompt)}`;
147
+ return null;
148
+ }
149
+
150
+ function parseSpawnArgs(args = []) {
151
+ const help = args.length === 0 || hasFlag(args, ['--help', '-h']) || args[0] === 'help';
152
+ if (help) return { help: true };
153
+
154
+ const json = hasFlag(args, ['--json']);
155
+ const dryRun = hasFlag(args, ['--dry-run']);
156
+ const roleFlag = flagValue(args, ['--role']);
157
+ const engine = String(flagValue(args, ['--engine']) || 'manual').toLowerCase();
158
+ if (!ALLOWED_ENGINES.has(engine)) throw new Error(`Unknown engine: ${engine}`);
159
+
160
+ const pos = positionalArgs(args);
161
+ const role = roleFlag || pos[0];
162
+ const task = flagValue(args, ['--task', '--message']) || pos.slice(roleFlag ? 0 : 1).join(' ');
163
+ if (!role) throw new Error('Missing role. Usage: atris agent spawn <role> --task "..."');
164
+ if (!String(task || '').trim()) throw new Error('Missing task. Usage: atris agent spawn <role> --task "..."');
165
+
166
+ return {
167
+ help: false,
168
+ json,
169
+ dryRun,
170
+ role: String(role).trim(),
171
+ task: String(task).trim(),
172
+ engine,
173
+ };
174
+ }
175
+
176
+ function createSpawnRequest(root, options) {
177
+ const request = {
178
+ schema: SPAWN_SCHEMA,
179
+ id: idForSpawn(options.role),
180
+ status: 'requested',
181
+ role: options.role,
182
+ task: options.task,
183
+ engine: options.engine,
184
+ cwd: root,
185
+ created_at: nowIso(),
186
+ updated_at: nowIso(),
187
+ };
188
+ request.command = commandForEngine(request);
189
+ request.next_action = request.command
190
+ ? `Run: ${request.command}`
191
+ : 'Assign this request to a worker runtime, or rerun with --engine codex|claude|cursor|devin|droid.';
192
+ return request;
193
+ }
194
+
195
+ function showSpawnHelp(output = console.log, commandName = 'atris agent spawn') {
196
+ output(`Usage: ${commandName} <role> --task "<bounded task>" [options]`);
197
+ output('');
198
+ output('Create a durable worker request that ax, humans, or another runtime can pick up.');
199
+ output('');
200
+ output('Options:');
201
+ output(' --task <text> Bounded task to delegate');
202
+ output(' --engine <name> manual|codex|claude|cursor|devin|droid (default: manual)');
203
+ output(' --json Machine-readable output');
204
+ output(' --dry-run Preview without writing .atris/state/agent_spawns.jsonl');
205
+ output('');
206
+ output('Examples:');
207
+ output(` ${commandName} worker --task "Fix the failing smoke test"`);
208
+ output(` ${commandName} explorer --engine codex --task "Find where auth is routed"`);
209
+ }
210
+
211
+ function parseDogfoodArgs(args = []) {
212
+ const help = hasFlag(args, ['--help', '-h']) || args[0] === 'help';
213
+ const json = hasFlag(args, ['--json']);
214
+ const live = hasFlag(args, ['--live']);
215
+ const noWrite = hasFlag(args, ['--no-write']);
216
+ const model = flagValue(args, ['--model']) || 'glm-5.2';
217
+ const timeoutRaw = Number(flagValue(args, ['--timeout']) || 45);
218
+ const timeoutMs = Math.max(5, Math.min(300, timeoutRaw)) * 1000;
219
+ const engine = String(flagValue(args, ['--engine']) || positionalArgs(args)[0] || 'all').toLowerCase();
220
+ if (engine !== 'all' && !DOGFOOD_ENGINES.has(engine)) {
221
+ throw new Error(`Unknown dogfood engine: ${engine}. Use devin, droid, or all.`);
222
+ }
223
+ return { help, json, live, noWrite, model, timeoutMs, engine };
224
+ }
225
+
226
+ function livePromptFor(engine) {
227
+ const upper = engine === 'devin' ? 'DEVIN' : 'DROID';
228
+ return `Return exactly ATRIS_${upper}_GLM52_OK. Do not inspect files. Do not run tools.`;
229
+ }
230
+
231
+ function liveCommandFor(engine, model) {
232
+ if (engine === 'devin') {
233
+ return [
234
+ 'devin',
235
+ '--model',
236
+ model,
237
+ '--permission-mode',
238
+ 'auto',
239
+ '-p',
240
+ livePromptFor(engine),
241
+ ];
242
+ }
243
+ return [
244
+ 'droid',
245
+ 'exec',
246
+ '--model',
247
+ model,
248
+ '--reasoning-effort',
249
+ 'off',
250
+ '--output-format',
251
+ 'text',
252
+ '--append-system-prompt',
253
+ 'Do not use tools. Reply with exactly the requested token.',
254
+ livePromptFor(engine),
255
+ ];
256
+ }
257
+
258
+ function dryChecksFor(engine, model, deps = {}, options = {}) {
259
+ const binary = engine === 'devin' ? 'devin' : 'droid';
260
+ const binaryPath = commandOnPath(binary, deps);
261
+ const checks = [
262
+ {
263
+ name: 'binary_on_path',
264
+ ok: Boolean(binaryPath),
265
+ detail: binaryPath || `${binary} not on PATH`,
266
+ },
267
+ ];
268
+ if (!binaryPath) return { binary, binaryPath, checks };
269
+
270
+ const versionArgs = engine === 'devin' ? ['devin', 'version'] : ['droid', '--version'];
271
+ const version = runCli(versionArgs, deps, options);
272
+ checks.push({
273
+ name: 'version_command',
274
+ ok: version.ok,
275
+ command: version.command,
276
+ stdout_head: version.stdout.slice(0, 500),
277
+ stderr_head: version.stderr.slice(0, 500),
278
+ });
279
+
280
+ const helpArgs = engine === 'devin' ? ['devin', '--help'] : ['droid', 'exec', '--help'];
281
+ const help = runCli(helpArgs, deps, options);
282
+ const modelSupport = engine === 'droid'
283
+ ? help.stdout.includes(model)
284
+ : /--model\s+<MODEL>/.test(help.stdout);
285
+ checks.push({
286
+ name: engine === 'droid' ? 'model_list_advertises_glm' : 'model_flag_available',
287
+ ok: help.ok && modelSupport,
288
+ command: help.command,
289
+ detail: engine === 'droid'
290
+ ? `${model} ${modelSupport ? 'listed' : 'missing'}`
291
+ : `--model flag ${modelSupport ? 'available' : 'missing'}`,
292
+ stdout_head: help.stdout.slice(0, 700),
293
+ stderr_head: help.stderr.slice(0, 500),
294
+ });
295
+
296
+ return { binary, binaryPath, checks };
297
+ }
298
+
299
+ function runDogfoodEngine(engine, options = {}, deps = {}) {
300
+ const dry = dryChecksFor(engine, options.model || 'glm-5.2', deps, {
301
+ cwd: deps.root || process.cwd(),
302
+ timeoutMs: options.timeoutMs,
303
+ });
304
+ const result = {
305
+ engine,
306
+ model: options.model || 'glm-5.2',
307
+ binary: dry.binary,
308
+ binary_path: dry.binaryPath,
309
+ live: Boolean(options.live),
310
+ checks: dry.checks,
311
+ ok: dry.checks.every(check => check.ok),
312
+ };
313
+
314
+ if (options.live && result.ok) {
315
+ const command = liveCommandFor(engine, result.model);
316
+ const run = runCli(command, deps, {
317
+ cwd: deps.root || process.cwd(),
318
+ timeoutMs: options.timeoutMs,
319
+ env: {
320
+ ...process.env,
321
+ ATRIS_SKIP_UPDATE_CHECK: '1',
322
+ },
323
+ });
324
+ const expected = `ATRIS_${engine === 'devin' ? 'DEVIN' : 'DROID'}_GLM52_OK`;
325
+ const matched = run.ok && run.stdout.includes(expected);
326
+ result.checks.push({
327
+ name: 'live_sentinel_prompt',
328
+ ok: matched,
329
+ command: run.command,
330
+ expected,
331
+ stdout_head: run.stdout.slice(0, 700),
332
+ stderr_head: run.stderr.slice(0, 700),
333
+ status: run.status,
334
+ signal: run.signal,
335
+ duration_ms: run.duration_ms,
336
+ error: run.error,
337
+ });
338
+ result.ok = result.checks.every(check => check.ok);
339
+ }
340
+
341
+ return result;
342
+ }
343
+
344
+ function writeDogfoodReceipt(root, receipt) {
345
+ const dir = runsDir(root);
346
+ fs.mkdirSync(dir, { recursive: true });
347
+ const filePath = path.join(dir, `agent-dogfood-${timestampForFile()}.json`);
348
+ fs.writeFileSync(filePath, `${JSON.stringify(receipt, null, 2)}\n`, 'utf8');
349
+ return filePath;
350
+ }
351
+
352
+ function showDogfoodHelp(output = console.log) {
353
+ output('Usage: atris agent dogfood [devin|droid|--engine all] [--model glm-5.2] [--live] [--json]');
354
+ output('');
355
+ output('Cheaply verifies external coding CLIs without a model call by default.');
356
+ output('Add --live to run one exact sentinel prompt through Devin/Droid.');
357
+ }
358
+
359
+ function agentDogfoodCommand(args = [], deps = {}) {
360
+ const root = deps.root || process.cwd();
361
+ const output = deps.output || ((line = '') => console.log(line));
362
+ const options = parseDogfoodArgs(args);
363
+ if (options.help) {
364
+ showDogfoodHelp(output);
365
+ return { ok: true, action: 'help' };
366
+ }
367
+
368
+ const engines = options.engine === 'all' ? ['devin', 'droid'] : [options.engine];
369
+ const receipt = {
370
+ schema: DOGFOOD_SCHEMA,
371
+ action: 'agent_dogfood',
372
+ root,
373
+ model: options.model,
374
+ live: options.live,
375
+ started_at: nowIso(),
376
+ finished_at: null,
377
+ engines: engines.map(engine => runDogfoodEngine(engine, options, deps)),
378
+ };
379
+ receipt.finished_at = nowIso();
380
+ receipt.ok = receipt.engines.every(engine => engine.ok);
381
+ if (!options.noWrite) {
382
+ receipt.receipt_path = writeDogfoodReceipt(root, receipt);
383
+ }
384
+
385
+ if (options.json) {
386
+ output(JSON.stringify(receipt, null, 2));
387
+ } else {
388
+ output(`agent dogfood ${receipt.ok ? 'passed' : 'failed'} (${options.live ? 'live' : 'dry'})`);
389
+ for (const engine of receipt.engines) {
390
+ output(`${engine.ok ? '✓' : '✗'} ${engine.engine} ${engine.model}${engine.binary_path ? ` -> ${engine.binary_path}` : ''}`);
391
+ for (const check of engine.checks) {
392
+ output(` ${check.ok ? '✓' : '✗'} ${check.name}${check.detail ? `: ${check.detail}` : ''}`);
393
+ }
394
+ }
395
+ if (receipt.receipt_path) output(`receipt: ${path.relative(root, receipt.receipt_path)}`);
396
+ }
397
+
398
+ return receipt;
399
+ }
400
+
401
+ function printSpawnRequest(request, output = console.log) {
402
+ output(`spawned ${request.id}`);
403
+ output(`role: ${request.role}`);
404
+ output(`engine: ${request.engine}`);
405
+ output(`task: ${request.task}`);
406
+ output(`next: ${request.next_action}`);
407
+ }
408
+
409
+ function agentSpawnCommand(args = [], deps = {}) {
410
+ const root = deps.root || process.cwd();
411
+ const output = deps.output || ((line = '') => console.log(line));
412
+ const options = parseSpawnArgs(args);
413
+ if (options.help) {
414
+ showSpawnHelp(output, deps.commandName || 'atris agent spawn');
415
+ return { ok: true, action: 'help' };
416
+ }
417
+ const request = createSpawnRequest(root, options);
418
+ let statePath = spawnStatePath(root);
419
+ if (!options.dryRun) statePath = appendSpawnRequest(root, request);
420
+ const payload = {
421
+ ok: true,
422
+ action: options.dryRun ? 'spawn_preview' : 'spawn_created',
423
+ request,
424
+ state_path: statePath,
425
+ };
426
+ if (options.json) output(JSON.stringify(payload, null, 2));
427
+ else printSpawnRequest(request, output);
428
+ return payload;
429
+ }
430
+
431
+ function agentSpawnListCommand(args = [], deps = {}) {
432
+ const root = deps.root || process.cwd();
433
+ const output = deps.output || ((line = '') => console.log(line));
434
+ const json = hasFlag(args, ['--json']);
435
+ const requests = loadSpawnRequests(root).reverse();
436
+ const payload = { ok: true, action: 'spawn_list', requests, state_path: spawnStatePath(root) };
437
+ if (json) {
438
+ output(JSON.stringify(payload, null, 2));
439
+ return payload;
440
+ }
441
+ if (!requests.length) {
442
+ output('No agent spawn requests.');
443
+ return payload;
444
+ }
445
+ for (const req of requests.slice(0, 20)) {
446
+ output(`${req.id}\t${req.status}\t${req.engine}\t${req.role}\t${req.task}`);
447
+ }
448
+ return payload;
449
+ }
450
+
451
+ function agentSpawnStatusCommand(args = [], deps = {}) {
452
+ const root = deps.root || process.cwd();
453
+ const output = deps.output || ((line = '') => console.log(line));
454
+ const json = hasFlag(args, ['--json']);
455
+ const id = positionalArgs(args)[0];
456
+ if (!id) throw new Error('Missing spawn id. Usage: atris agent spawn-status <id>');
457
+ const request = loadSpawnRequests(root).find(req => req.id === id);
458
+ if (!request) throw new Error(`Spawn request not found: ${id}`);
459
+ const payload = { ok: true, action: 'spawn_status', request, state_path: spawnStatePath(root) };
460
+ if (json) output(JSON.stringify(payload, null, 2));
461
+ else printSpawnRequest(request, output);
462
+ return payload;
463
+ }
464
+
465
+ module.exports = {
466
+ SPAWN_SCHEMA,
467
+ SPAWN_STATE_REL,
468
+ parseSpawnArgs,
469
+ buildDelegatePrompt,
470
+ commandForEngine,
471
+ createSpawnRequest,
472
+ agentDogfoodCommand,
473
+ parseDogfoodArgs,
474
+ runDogfoodEngine,
475
+ loadSpawnRequests,
476
+ agentSpawnCommand,
477
+ agentSpawnListCommand,
478
+ agentSpawnStatusCommand,
479
+ showSpawnHelp,
480
+ };
@@ -51,7 +51,7 @@ function analyticsAtris() {
51
51
  todayCompletions = completionCount;
52
52
 
53
53
  // Count today's inbox
54
- const inboxMatch = content.match(/## Inbox\n([\s\S]*?)(?=\n##|---|$)/);
54
+ const inboxMatch = content.match(/## Inbox\r?\n([\s\S]*?)(?=\r?\n##|---|$)/);
55
55
  if (inboxMatch && inboxMatch[1].trim()) {
56
56
  const inboxMatches = inboxMatch[1].match(/- \*\*I\d+:/g);
57
57
  todayInbox = inboxMatches ? inboxMatches.length : 0;
@@ -60,7 +60,7 @@ function analyticsAtris() {
60
60
 
61
61
  if (index === 6) {
62
62
  // Count oldest day's inbox for trend
63
- const inboxMatch = content.match(/## Inbox\n([\s\S]*?)(?=\n##|---|$)/);
63
+ const inboxMatch = content.match(/## Inbox\r?\n([\s\S]*?)(?=\r?\n##|---|$)/);
64
64
  if (inboxMatch && inboxMatch[1].trim()) {
65
65
  const inboxMatches = inboxMatch[1].match(/- \*\*I\d+:/g);
66
66
  oldestInbox = inboxMatches ? inboxMatches.length : 0;
@@ -136,9 +136,12 @@ function analyticsAtris() {
136
136
  // Daily breakdown
137
137
  console.log(`📅 Daily Breakdown`);
138
138
  const sortedDates = Object.keys(completionsByDay).sort().reverse();
139
+ // Cap the bar so a high-count day can't overflow the 63-char box; the exact
140
+ // count is still printed numerically after it. Mirrors `mission layers`.
141
+ const BAR_MAX = 40;
139
142
  sortedDates.forEach((date, index) => {
140
143
  const count = completionsByDay[date];
141
- const bar = '█'.repeat(count);
144
+ const bar = '█'.repeat(Math.min(count, BAR_MAX));
142
145
  const label = index === 0 ? ' (today)' : '';
143
146
  console.log(` ${date}: ${bar} ${count}${label}`);
144
147
  });
package/commands/apps.js CHANGED
@@ -34,6 +34,7 @@ function appsUsageLines() {
34
34
  ' owner <slug> [--json] Show owner view: launch, usage, learning, next actions',
35
35
  ' status [--json] Show local app health',
36
36
  ' queue Show app improvement queue',
37
+ ' apply [--target s] [--llm] Fix lowest-quality app; keep only if quality improves',
37
38
  ' rate <slug> <up|down> [note] Record output feedback',
38
39
  ' smoke Run fresh-checkout smoke test',
39
40
  ' doctor [--strict] Audit pack health, smoke, and source cleanliness',
@@ -302,6 +303,16 @@ async function appsCommand(subcommand, ...rawArgs) {
302
303
  }
303
304
  if (subcommand === 'status') runPackScript(packRoot, 'scripts/app_status.py', json ? ['--json'] : []);
304
305
  if (subcommand === 'queue') runPackScript(packRoot, 'scripts/app_improvement_queue.py', []);
306
+ if (subcommand === 'apply') {
307
+ const target = popOption(args, '--target', null);
308
+ const command = ['--workspace', workspace];
309
+ if (target) command.push('--target', target);
310
+ else command.push('--auto');
311
+ if (args.includes('--llm')) command.push('--llm'); // else app_apply defaults to dry-run
312
+ if (args.includes('--dry-run')) command.push('--dry-run');
313
+ if (json) command.push('--json');
314
+ runPackScript(packRoot, 'scripts/app_apply.py', command);
315
+ }
305
316
  if (subcommand === 'smoke') runPackScript(packRoot, 'scripts/install_smoke.py', []);
306
317
  if (subcommand === 'doctor') runPackScript(packRoot, 'scripts/install_smoke.py', []);
307
318
  if (subcommand === 'handoff') {