atris 3.38.0 → 3.41.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 (78) hide show
  1. package/AGENTS.md +25 -6
  2. package/atris/PERSONA.md +8 -4
  3. package/atris.md +7 -0
  4. package/ax +2 -1
  5. package/bin/atris.js +31 -6
  6. package/commands/agent-spawn.js +13 -11
  7. package/commands/autoland.js +28 -77
  8. package/commands/bench.js +10 -12
  9. package/commands/business.js +345 -0
  10. package/commands/chat-scan.js +5 -7
  11. package/commands/codex-goal.js +8 -10
  12. package/commands/computer.js +20 -0
  13. package/commands/console.js +19 -3
  14. package/commands/decide.js +166 -0
  15. package/commands/deck.js +1 -4
  16. package/commands/drill.js +14 -24
  17. package/commands/engine.js +196 -8
  18. package/commands/gm.js +8 -6
  19. package/commands/harvest.js +1 -4
  20. package/commands/init.js +23 -3
  21. package/commands/land.js +8 -14
  22. package/commands/launchpad.js +1 -14
  23. package/commands/lifecycle.js +5 -5
  24. package/commands/log.js +55 -5
  25. package/commands/member.js +558 -574
  26. package/commands/mission.js +456 -237
  27. package/commands/pack.js +2746 -164
  28. package/commands/play.js +6 -4
  29. package/commands/probe.js +2 -2
  30. package/commands/pulse.js +15 -16
  31. package/commands/release.js +10 -9
  32. package/commands/router.js +5 -4
  33. package/commands/site-deploy.js +885 -0
  34. package/commands/site.js +11 -2
  35. package/commands/slop.js +14 -2
  36. package/commands/stream.js +4 -18
  37. package/commands/task.js +880 -558
  38. package/commands/taste.js +101 -0
  39. package/commands/team.js +176 -3
  40. package/commands/vercel.js +4 -2
  41. package/commands/voice.js +195 -0
  42. package/commands/watch.js +1 -22
  43. package/commands/wiki.js +1 -4
  44. package/commands/workflow.js +2 -2
  45. package/commands/worktree.js +1 -14
  46. package/commands/xp.js +27 -24
  47. package/lib/accept-verify-gate.js +5 -1
  48. package/lib/arg-parser.js +41 -0
  49. package/lib/auto-accept-certified.js +116 -1
  50. package/lib/autoland.js +66 -0
  51. package/lib/bench/runner.js +19 -1
  52. package/lib/context-gatherer.js +7 -1
  53. package/lib/engine-registry.js +141 -20
  54. package/lib/falsifier-probe.js +84 -0
  55. package/lib/fleet.js +65 -15
  56. package/lib/git-spawn.js +15 -0
  57. package/lib/json-file.js +37 -0
  58. package/lib/known-commands.js +2 -2
  59. package/lib/lesson-preflight.js +146 -0
  60. package/lib/loop-doctor.js +0 -2
  61. package/lib/mission-human-asks.js +28 -0
  62. package/lib/mission-protected-lane.js +4 -1
  63. package/lib/official-cli-integration.js +47 -2
  64. package/lib/orb-context.js +8 -1
  65. package/lib/pack-capabilities.js +685 -0
  66. package/lib/router-brain.js +51 -1
  67. package/lib/runner-command.js +0 -6
  68. package/lib/self-drive.js +44 -13
  69. package/lib/task-db.js +137 -3
  70. package/lib/task-decision.js +50 -0
  71. package/lib/taste-lessons.js +153 -0
  72. package/lib/tool-result-encode.js +17 -1
  73. package/lib/voice-gate.js +66 -0
  74. package/lib/wish-audit.js +1 -1
  75. package/lib/wish-delegate.js +1 -1
  76. package/lib/zip.js +95 -7
  77. package/package.json +2 -1
  78. package/templates/business-starter/persona.md +9 -0
package/commands/init.js CHANGED
@@ -282,6 +282,7 @@ function initAtris() {
282
282
  const printReadyCounts = () => {
283
283
  console.log(`workspace files ready (${ready.workspace.size})`);
284
284
  console.log(`team members ready (${ready.team.size})`);
285
+ console.log('more members are available with atris member create <name>');
285
286
  console.log(`agent adapters ready (${ready.adapters.size})`);
286
287
  console.log(`skills installed (${ready.skills.size})`);
287
288
  };
@@ -601,8 +602,8 @@ function initAtris() {
601
602
 
602
603
 
603
604
  // Copy team members (MEMBER.md format — directory per member with skills/tools/context)
604
- const members = ['navigator', 'executor', 'validator', 'launcher', 'brainstormer', 'researcher'];
605
- members.forEach(name => {
605
+ const starterMembers = ['navigator', 'executor', 'validator', 'mission-lead', 'improver'];
606
+ starterMembers.forEach(name => {
606
607
  const sourceFile = path.join(__dirname, '..', 'atris', 'team', name, 'MEMBER.md');
607
608
  const targetMemberDir = path.join(teamDir, name);
608
609
  const targetFile = path.join(targetMemberDir, 'MEMBER.md');
@@ -644,7 +645,7 @@ tools: []
644
645
  # Insert persona, workflow, and rules below
645
646
  `;
646
647
  fs.writeFileSync(path.join(templateTargetDir, 'MEMBER.md'), templateContent);
647
- markReady('team', '_template', '✓ Created team/_template/MEMBER.md');
648
+ markReady('workspace', 'team/_template/MEMBER.md', '✓ Created team/_template/MEMBER.md');
648
649
  }
649
650
 
650
651
  // Detect project context and generate profile
@@ -676,6 +677,25 @@ atris atris.md
676
677
 
677
678
  This is the Atris boot sequence. Show the output to the user, then respond naturally.
678
679
 
680
+ ## The way of talking (every reply, no exceptions)
681
+
682
+ Talk like a person. This governs every message you send the operator: boot, status, summaries, everything.
683
+
684
+ - Plain words. Say what happened and what it means for the reader, cause and effect. Not the machinery.
685
+ - No task codes, branch names, commit hashes, PR numbers, or system nouns (worktree, verifier, projection, tick) in the message body. If the reader needs a command, put ONE copyable line at the end.
686
+ - No insider terms without defining them in the same breath. A sharp non-engineer must follow it on first read.
687
+ - Lead with the outcome. Complete sentences, one or two per paragraph, blank line between. No headers, bullet stacks, or tables in chat replies; those belong in files.
688
+ - Detail lives in files and receipts. Offer "want the detail?" instead of dumping it.
689
+ - No em dashes anywhere; use a comma, colon, or period.
690
+ - The test before sending: read it fried at 2am. If decoding takes work, rewrite it.
691
+
692
+ copy these shapes:
693
+ - **Status:** "The reply check is built. I am running the final checks now, so the result is not ready yet."
694
+ - **Landing:** "Replies now get a plain-language check before they reach you. The checks passed, and the change is ready."
695
+ - **Failure:** "The plain-language check could not run because its model was unavailable. Your reply still went through, so no work was blocked."
696
+
697
+ Canonical source: \`atris/atris.md\` \`## voice\`. This block is a copy for tools that only read \`AGENTS.md\`; if they differ, atris.md wins.
698
+
679
699
  ## Core Files
680
700
 
681
701
  Atris is the source of truth. This file is only an adapter for tools that read
package/commands/land.js CHANGED
@@ -1,7 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const { spawnSync } = require('child_process');
4
-
3
+ const { runGit } = require('../lib/git-spawn');
5
4
  const { listWorktrees, statusCounts } = require('./worktree');
6
5
 
7
6
  const DEFAULT_TTL_DAYS = 7;
@@ -14,14 +13,6 @@ function countLabel(n, word) {
14
13
  return `${n} ${word}${n === 1 ? '' : 's'}`;
15
14
  }
16
15
 
17
- function runGit(args, { cwd = process.cwd(), check = true, maxBuffer } = {}) {
18
- const result = spawnSync('git', args, { cwd, encoding: 'utf8', ...(maxBuffer ? { maxBuffer } : {}) });
19
- if (check && result.status !== 0) {
20
- throw new Error(`git ${args.join(' ')} failed: ${(result.stderr || result.stdout || '').trim()}`);
21
- }
22
- return result;
23
- }
24
-
25
16
  function repoRoot(cwd = process.cwd()) {
26
17
  const result = runGit(['rev-parse', '--show-toplevel'], { cwd, check: false });
27
18
  if (result.status !== 0) return '';
@@ -614,7 +605,9 @@ function printReceipt(receipt) {
614
605
  // fresh-grace keeps are healthy active work, not a call to action —
615
606
  // "needs a human" on those trained readers to ignore the real ones.
616
607
  for (const k of receipt.keptWorktrees || []) {
608
+ // The receipt keeps the raw codes; the human line speaks in sentences.
617
609
  if (String(k).includes('(fresh_worktree_grace)')) console.log(` in use, left alone: ${k.replace(' (fresh_worktree_grace)', '')}`);
610
+ else if (String(k).includes('(current_checkout)')) console.log(` you are standing in it, left alone: ${k.replace(' (current_checkout)', '')}`);
618
611
  else console.log(` ✋ kept, needs a human: ${k}`);
619
612
  }
620
613
  for (const m of receipt.keptMovedBranches || []) console.log(` moved since scan, left alone: ${m}`);
@@ -624,7 +617,8 @@ function printReceipt(receipt) {
624
617
  console.log('');
625
618
  }
626
619
 
627
- function readFlag(args, name, fallback = '') {
620
+ // Preserve split-only parsing and its fallback for missing or falsey values.
621
+ function readFollowingFlag(args, name, fallback = '') {
628
622
  const idx = args.indexOf(name);
629
623
  if (idx === -1) return fallback;
630
624
  return args[idx + 1] || fallback;
@@ -665,10 +659,10 @@ function landCommand(args = []) {
665
659
  console.error('not a git repository');
666
660
  return 1;
667
661
  }
668
- const ttlRaw = readFlag(args, '--ttl', '');
662
+ const ttlRaw = readFollowingFlag(args, '--ttl', '');
669
663
  const ttlParsed = Number(ttlRaw);
670
664
  const ttlDays = ttlRaw !== '' && Number.isFinite(ttlParsed) && ttlParsed >= 0 ? ttlParsed : DEFAULT_TTL_DAYS;
671
- const base = readFlag(args, '--base', '');
665
+ const base = readFollowingFlag(args, '--base', '');
672
666
  const json = args.includes('--json');
673
667
 
674
668
  if (args.includes('--reap')) {
@@ -679,7 +673,7 @@ function landCommand(args = []) {
679
673
  }
680
674
 
681
675
  const lookupArgs = args[0] === 'status' ? args.slice(1) : args;
682
- const name = lookupArgs.find((a) => !a.startsWith('-') && a !== readFlag(lookupArgs, '--ttl') && a !== readFlag(lookupArgs, '--base'));
676
+ const name = lookupArgs.find((a) => !a.startsWith('-') && a !== readFollowingFlag(lookupArgs, '--ttl') && a !== readFollowingFlag(lookupArgs, '--base'));
683
677
  if (name) {
684
678
  const story = collectStory(root, name, { base });
685
679
  if (!story) {
@@ -2,23 +2,10 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
+ const { hasFlag, readFlag } = require('../lib/arg-parser');
5
6
 
6
7
  const ACTIVE_MISSION_STATUSES = new Set(['planning', 'running', 'ready']);
7
8
 
8
- function hasFlag(args, name) {
9
- return args.includes(name);
10
- }
11
-
12
- function readFlag(args, name, fallback = '') {
13
- const prefix = `${name}=`;
14
- for (let i = 0; i < args.length; i += 1) {
15
- const arg = String(args[i]);
16
- if (arg === name && args[i + 1] && !String(args[i + 1]).startsWith('--')) return String(args[i + 1]);
17
- if (arg.startsWith(prefix)) return arg.slice(prefix.length);
18
- }
19
- return fallback;
20
- }
21
-
22
9
  function readJson(file) {
23
10
  try {
24
11
  return JSON.parse(fs.readFileSync(file, 'utf8'));
@@ -168,7 +168,7 @@ async function sleepAtris() {
168
168
  }
169
169
 
170
170
  /**
171
- * Wake a sleeping workspace so agents resume automatically.
171
+ * Wake a sleeping workspace: remote wake request, or local wake flag.
172
172
  * @returns {Promise<void>}
173
173
  */
174
174
  async function wakeAtris() {
@@ -179,7 +179,7 @@ async function wakeAtris() {
179
179
  if (!slug || slug === '--help' || slug === '-h' || slug === 'help') {
180
180
  console.log('Usage: atris wake [business|member] [--loop <loop-id>]');
181
181
  console.log('');
182
- console.log(' Wake a sleeping workspace. Agents resume automatically.');
182
+ console.log(' Wake a sleeping workspace. Wake signal sent; loops pick it up on their next pass.');
183
183
  console.log(' Or flip a local member / member-loop switch awake.');
184
184
  process.exit(0);
185
185
  }
@@ -207,13 +207,13 @@ async function wakeAtris() {
207
207
  console.log(`Waking business computer '${business.name || slug}'...`);
208
208
 
209
209
  if (result.data && result.data.status === 'running' && result.data.endpoint) {
210
- console.log(`Business computer '${business.name || slug}' is alive. Agents resuming.`);
210
+ console.log(`Business computer '${business.name || slug}' is alive. Wake signal sent; loops pick it up on their next pass.`);
211
211
  return;
212
212
  }
213
213
 
214
214
  const running = await waitForBusinessComputer(creds.token, business);
215
215
  if (running) {
216
- console.log(`Business computer '${business.name || slug}' is alive. Agents resuming.`);
216
+ console.log(`Business computer '${business.name || slug}' is alive. Wake signal sent; loops pick it up on their next pass.`);
217
217
  return;
218
218
  }
219
219
 
@@ -246,7 +246,7 @@ async function wakeAtris() {
246
246
  });
247
247
 
248
248
  if (status.ok && status.data && status.data.status === 'running') {
249
- console.log(`Workspace '${slug}' is alive. Agents resuming.`);
249
+ console.log(`Workspace '${slug}' is alive. Wake signal sent; loops pick it up on their next pass.`);
250
250
  return;
251
251
  }
252
252
  }
package/commands/log.js CHANGED
@@ -97,6 +97,56 @@ function readDigestSection(root, title, relativePath, maxLines = null) {
97
97
  };
98
98
  }
99
99
 
100
+ // Splits a journal body into its ## groups so the digest can show
101
+ // completed / inbox / notes as short labeled blocks instead of a raw dump.
102
+ function journalGroups(lines) {
103
+ const groups = [];
104
+ let current = null;
105
+ for (const line of lines) {
106
+ const heading = line.match(/^##\s+(.+?)\s*$/);
107
+ if (heading) {
108
+ current = { heading: heading[1], lines: [] };
109
+ groups.push(current);
110
+ } else if (current) {
111
+ current.lines.push(line);
112
+ }
113
+ }
114
+ return groups;
115
+ }
116
+
117
+ function bulletCount(group) {
118
+ return group.lines.filter((line) => /^\s*-\s+/.test(line)).length;
119
+ }
120
+
121
+ function digestSummaryLine(date, sections) {
122
+ let completed = 0;
123
+ let inbox = 0;
124
+ for (const section of sections) {
125
+ for (const group of journalGroups(section.lines)) {
126
+ if (/^completed/i.test(group.heading)) completed += bulletCount(group);
127
+ else if (/^inbox/i.test(group.heading)) inbox += bulletCount(group);
128
+ }
129
+ }
130
+ const parts = [`${sections.length} log${sections.length === 1 ? '' : 's'}`];
131
+ if (completed) parts.push(`${completed} completed`);
132
+ if (inbox) parts.push(`${inbox} inbox`);
133
+ return `${date}: ${parts.join(', ')}`;
134
+ }
135
+
136
+ function renderDigestSection(section) {
137
+ const groups = journalGroups(section.lines);
138
+ if (!groups.length) {
139
+ return [`=== ${section.title} ===`, section.path, ...section.lines].join('\n');
140
+ }
141
+ const out = [`=== ${section.title} ===`, section.path];
142
+ for (const group of groups) {
143
+ const body = group.lines.filter((line) => line.trim() && line.trim() !== '---');
144
+ if (!body.length) continue;
145
+ out.push('', `${group.heading.toLowerCase()} (${bulletCount(group)})`, ...body);
146
+ }
147
+ return out.join('\n');
148
+ }
149
+
100
150
  function logsDigest(args = [], options = {}) {
101
151
  const root = options.cwd || process.cwd();
102
152
  const date = digestDate(args);
@@ -125,11 +175,11 @@ function logsDigest(args = [], options = {}) {
125
175
  } else if (sections.length === 0) {
126
176
  console.log(`No logs for ${date} yet.`);
127
177
  } else {
128
- console.log(sections.map((section) => [
129
- `=== ${section.title} ===`,
130
- section.path,
131
- ...section.lines,
132
- ].join('\n')).join('\n\n'));
178
+ console.log([
179
+ digestSummaryLine(date, sections),
180
+ '',
181
+ ...sections.map(renderDigestSection),
182
+ ].join('\n'));
133
183
  }
134
184
 
135
185
  return payload;