atris 3.13.0 → 3.15.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.
package/README.md CHANGED
@@ -4,6 +4,16 @@
4
4
 
5
5
  It turns any repo into an AI workspace with shared context, a `plan -> do -> review` loop, daily logs, feature packs, and reusable skills.
6
6
 
7
+ Atris gives every owner persistent AI computers.
8
+
9
+ ```text
10
+ Owner = User | Business
11
+ Owner has many Computers
12
+ Computer = workspace + files + tools + secrets + memory + agents + validation loop
13
+ ```
14
+
15
+ A computer can be personal or shared by a business. It can run a job like code, research, CRM, reporting, recruiting, event ops, support, or business ops.
16
+
7
17
  ## For Coding Agents
8
18
 
9
19
  If you're an agent, run:
@@ -19,6 +29,8 @@ Then read the workspace's `atris/atris.md` and follow it exactly. `atris.md` is
19
29
  ## What Atris Gives You
20
30
 
21
31
  - An AI workspace on top of any repo
32
+ - Persistent AI computers for scoped jobs
33
+ - A local computer card that makes each workspace inspectable
22
34
  - A strict `plan -> do -> review` loop
23
35
  - Daily logs, task tracking, feature packs, and project memory
24
36
  - Skills, team members, integrations, and cloud sync when you need them
@@ -54,6 +66,7 @@ atris --version
54
66
  ```
55
67
 
56
68
  Requires Node.js 18+.
69
+ `atris task` uses built-in SQLite and requires Node.js 22+.
57
70
 
58
71
  If you want Atris cloud workspaces, businesses, or integrations, run `atris setup` after install.
59
72
 
@@ -73,9 +86,9 @@ Core loop: `plan` -> `do` -> `review`
73
86
 
74
87
  Integrates with any agent.
75
88
 
76
- ## Business Workspaces
89
+ ## Business Owners
77
90
 
78
- If you want a real business workspace, use the business command instead of raw `atris init`.
91
+ If you want a shared owner for a company, lab, collective, community, artist, team, or project, use the business command instead of raw `atris init`.
79
92
 
80
93
  ```bash
81
94
  atris business init "BLOND:ISH" --owner-email joel@blondish.world
@@ -84,10 +97,18 @@ atris business onboard --website https://blondish.world --contact "Joel Zimmerma
84
97
  atris align --fix
85
98
  ```
86
99
 
87
- That creates the cloud business, writes `.atris/business.json`, initializes `.atris/state/` for events and run history, and scaffolds the local `atris/` workspace under `~/arena/atris-business/<slug>/` with starter roles, a default recap template, and an initial task queue in `atris/TODO.md`.
100
+ That creates the shared owner, creates its first/default computer, writes `.atris/business.json`, initializes `.atris/state/` for events and run history, and scaffolds the local `atris/` workspace under `~/arena/atris-business/<slug>/` with starter roles, a default recap template, and an initial task queue in `atris/TODO.md`.
88
101
 
89
102
  If you do not have a neat source pack yet, `atris business onboard` is the easiest intake step: give it a website, a named human, a few notes, or run it in a folder with loose files. Atris turns that into raw intake, a starter brief, a first workflow, a safe next action, and a short operator brief.
90
103
 
104
+ Use the owner's language when you talk about it:
105
+
106
+ ```text
107
+ Your business runs on Atris.
108
+ Your lab runs on Atris.
109
+ Your collective runs on Atris.
110
+ ```
111
+
91
112
  You can also use bare input:
92
113
 
93
114
  ```bash
@@ -116,12 +137,14 @@ atris business record atris/reports/2026-04-12-operator-recap.md --outcome mixed
116
137
  | `atris autopilot` | Guided loop with approvals |
117
138
  | `atris log` | Add inbox items to today's journal |
118
139
  | `atris status` | Show active work and completions |
140
+ | `atris task` | Local agent task plane with atomic claims and TODO import |
119
141
  | `atris learn` | Manage structured learnings |
120
142
  | `atris ingest` | Stage raw evidence into `atris/context/` and compile into `atris/wiki/` |
121
143
  | `atris loop` | Refresh wiki health, stale/orphan signals, and next ingest candidates |
122
144
  | `atris wiki` | Full wiki namespace: ingest, query, lint, search, log, and loop |
123
145
  | `atris receipt` | Save evidence from an agent run |
124
146
  | `atris experiments` | Run small experiments and compare results |
147
+ | `atris computer card` | Show or write the local owner/computer card |
125
148
 
126
149
  ## Built-In Systems
127
150
 
@@ -131,8 +154,10 @@ atris business record atris/reports/2026-04-12-operator-recap.md --outcome mixed
131
154
  - `atris wiki --private` stores local-only sensitive notes under `.atris/presidio/`
132
155
  - `atris loop` refreshes `atris/wiki/STATUS.md` and `atris/wiki/log.md`, flags stale/orphan pages, and suggests the next ingest
133
156
  - `atris activate` loads the current wiki status so the next session starts with project memory, not just tasks
157
+ - `atris task` keeps a local SQLite task plane for agents while `atris/TODO.md` remains the readable project board
134
158
  - `atris experiments` runs small test packs in `atris/experiments/`
135
159
  - `atris pull` and `atris push` sync cloud workspaces and journals
160
+ - `atris live` keeps a business brain fresh by checking/fixing the workspace, pushing local state, pulling cloud state, and pushing again after local changes go quiet
136
161
 
137
162
  ## Verifiable Feedback Loop
138
163
 
package/bin/atris.js CHANGED
@@ -204,19 +204,26 @@ function showHelp() {
204
204
  console.log('');
205
205
  console.log('Quick Start:');
206
206
  console.log('');
207
- console.log(' 1. atris Load context, start building');
208
- console.log(' 2. Describe what you want (in your editor or terminal)');
209
- console.log(' 3. Agent shows visualization, you approve, it builds');
207
+ console.log(' 1. atris Open a persistent AI computer for this workspace');
208
+ console.log(' 2. Describe what you want run, built, researched, or validated');
209
+ console.log(' 3. Atris acts with context, memory, tools, and a review loop');
210
210
  console.log('');
211
211
  console.log('Common invocations:');
212
212
  console.log(' atris init');
213
+ console.log(' atris computer');
214
+ console.log(' atris business init "My Company"');
213
215
  console.log(' atris run');
214
216
  console.log(' atris status');
215
217
  console.log(' atris soul');
216
- console.log(' atris fleet');
218
+ console.log(' atris fleet status');
217
219
  console.log('');
218
220
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
219
221
  console.log('');
222
+ console.log('Atris Computers:');
223
+ console.log(' Owner = User | Business');
224
+ console.log(' Owners have Computers: workspace + files + tools + secrets + memory + agents + validation');
225
+ console.log(' Types: code, research, CRM, reporting, recruiting, events, support, business ops');
226
+ console.log('');
220
227
  console.log('Setup:');
221
228
  console.log(' setup - Guided first-time setup (login, pick business, pull)');
222
229
  console.log(' init - Initialize Atris in current project');
@@ -237,6 +244,7 @@ function showHelp() {
237
244
  console.log(' search - Search journal history (atris search <keyword>)');
238
245
  console.log(' clean - Housekeeping (stale tasks, archive journals, broken refs)');
239
246
  console.log(' verify - Validate work is done (tests, MAP.md, changes)');
247
+ console.log(' task - Local agent task plane (atomic claims, TODO import)');
240
248
  console.log(' release - Tag release, bump version, create GitHub release, draft /launch');
241
249
  console.log(' learn - Project learnings (patterns, pitfalls, preferences)');
242
250
  console.log(' ingest - Local-first wiki ingest into atris/wiki/');
@@ -262,6 +270,7 @@ function showHelp() {
262
270
  console.log('Sync:');
263
271
  console.log(' pull - Pull journals + member data from cloud');
264
272
  console.log(' push - Push workspace files to cloud');
273
+ console.log(' live - Keep a business brain fresh (doctor, pull, watch, push)');
265
274
  console.log(' clean-workspace <slug> - Analyze & remove junk files from a workspace (alias: cw)');
266
275
  console.log('');
267
276
  console.log('GitHub for Context:');
@@ -272,7 +281,7 @@ function showHelp() {
272
281
  console.log(' wake [business] - Resume workspace (agents restart)');
273
282
  console.log('');
274
283
  console.log('Business:');
275
- console.log(' business init <name> - RECOMMENDED: create business environment (cloud + local)');
284
+ console.log(' business init <name> - Create shared owner + first/default computer');
276
285
  console.log(' business onboard - Onboard from sparse input (--name, --website, --contact)');
277
286
  console.log(' business add <slug> - Connect a business');
278
287
  console.log(' business list - Show connected businesses');
@@ -280,6 +289,7 @@ function showHelp() {
280
289
  console.log(' business team [slug] - Show members, roles, and admin access');
281
290
  console.log(' business health <slug> - Health report (members, workspace, issues)');
282
291
  console.log(' business audit - One-line health summary of all businesses');
292
+ console.log(' business doctor - Catch stale cache, alias, and folder bindings');
283
293
  console.log(' business create <name> - Cloud-only business record; add --workspace to also scaffold local');
284
294
  console.log(' business connect <svc> - Wire a skill/integration');
285
295
  console.log(' business notify <mode> - Set notification mode (digest/silent/push)');
@@ -290,9 +300,11 @@ function showHelp() {
290
300
  console.log(' cr --all - Audit all backend services');
291
301
  console.log('');
292
302
  console.log('Cloud & agents:');
293
- console.log(' computer - Talk directly to the AI computer (bash or agent exec)');
303
+ console.log(' computer - Open a scoped AI computer (cloud/local, personal/business)');
294
304
  console.log(' receipt - Save evidence from an agent run');
295
305
  console.log(' console - Start/attach always-on coding console (tmux daemon)');
306
+ console.log(' soul - Show, snapshot, or fork workspace identity');
307
+ console.log(' fleet - Inspect local fleet status');
296
308
  console.log(' agent - Select which Atris agent to use');
297
309
  console.log(' chat - Chat with the selected Atris agent');
298
310
  console.log(' login - Sign in or add another account');
@@ -433,8 +445,8 @@ const { planAtris: planCmd, doAtris: doCmd, reviewAtris: reviewCmd } = require('
433
445
  // Check if this is a known command or natural language input
434
446
  const knownCommands = ['init', 'log', 'status', 'analytics', 'visualize', 'brainstorm', 'autopilot', 'run', 'plan', 'do', 'review', 'release',
435
447
  'activate', '_activate', 'agent', 'chat', 'console', 'login', 'logout', 'whoami', 'switch', 'use', 'accounts', '_resolve', '_profile-email', '_switch-session', 'shell-init', 'update', 'upgrade', 'version', 'help', 'next', 'atris',
436
- 'clean', 'verify', 'search', 'skill', 'member', 'app', 'learn', 'plugin', 'experiments', 'receipt', 'proof', 'openclaw', 'pull', 'push', 'align', 'terminal', 'computer', 'diff', 'business', 'sync',
437
- 'ingest', 'query', 'lint', 'loop',
448
+ 'clean', 'verify', 'search', 'skill', 'member', 'app', 'learn', 'plugin', 'experiments', 'receipt', 'proof', 'openclaw', 'pull', 'push', 'live', 'align', 'terminal', 'computer', 'diff', 'business', 'sync',
449
+ 'ingest', 'query', 'lint', 'loop', 'task', 'aeo',
438
450
  'gmail', 'calendar', 'twitter', 'slack', 'integrations', 'setup', 'clean-workspace', 'cw',
439
451
  'fork', 'browse', 'publish', 'sleep', 'wake', 'feedback', 'errors', 'wiki', 'code-review', 'cr', 'soul', 'fleet'];
440
452
 
@@ -780,6 +792,16 @@ if (command === 'init') {
780
792
  console.error(`✗ Error: ${error.message || error}`);
781
793
  process.exit(1);
782
794
  });
795
+ } else if (command === 'task') {
796
+ // SQLite-backed task plane. ~/.atris/tasks.db, gitignored, per-workspace.
797
+ Promise.resolve(require('../commands/task').run(process.argv.slice(3)))
798
+ .then(() => process.exit(0))
799
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
800
+ } else if (command === 'aeo') {
801
+ // AEO: AI Engine Optimization — credit-metered citation drafting against the customer workspace.
802
+ Promise.resolve(require('../commands/aeo').run(process.argv.slice(3)))
803
+ .then(() => process.exit(0))
804
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
783
805
  } else if (command === 'agent') {
784
806
  agentAtris().then(() => process.exit(0)).catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
785
807
  } else if (command === 'log') {
@@ -1088,6 +1110,10 @@ if (command === 'init') {
1088
1110
  require('../commands/push').pushAtris()
1089
1111
  .then(() => process.exit(0))
1090
1112
  .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1113
+ } else if (command === 'live') {
1114
+ require('../commands/live').liveCommand()
1115
+ .then(() => process.exit(0))
1116
+ .catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
1091
1117
  } else if (command === 'align') {
1092
1118
  require('../commands/align').alignAtris()
1093
1119
  .then(() => process.exit(0))
@@ -0,0 +1,197 @@
1
+ /**
2
+ * atris aeo — AI Engine Optimization commands
3
+ *
4
+ * atris aeo init # create entity-graph skeleton in workspace
5
+ * atris aeo draft "<topic>" [opts] # generate citation-optimized article (credit-metered)
6
+ *
7
+ * Hits the backend endpoints registered under:
8
+ * POST /api/business/{id}/workspaces/{ws}/aeo/init
9
+ * POST /api/business/{id}/workspaces/{ws}/aeo/draft
10
+ *
11
+ * Business resolution mirrors `atris terminal`: explicit --workspace slug,
12
+ * else cwd .atris/business.json. The endpoint itself takes care of running
13
+ * Claude Sonnet 4.6 with the 10 AEO rules and writing to /workspace/atris/aeo/drafts/.
14
+ */
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const { loadCredentials } = require('../utils/auth');
19
+ const { apiRequestJson } = require('../utils/api');
20
+ const { loadBusinesses, saveBusinesses } = require('./business');
21
+
22
+ function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); }
23
+
24
+ async function ensureAwake(token, businessId, maxWaitSec = 90) {
25
+ const status = await apiRequestJson(`/business/${businessId}/ai-computer/status`, { method: 'GET', token });
26
+ if (status.ok && status.data && status.data.status === 'running' && status.data.endpoint) return true;
27
+ process.stdout.write(' Waking EC2 computer... ');
28
+ await apiRequestJson(`/business/${businessId}/ai-computer/wake`, { method: 'POST', token });
29
+ const start = Date.now();
30
+ while (Date.now() - start < maxWaitSec * 1000) {
31
+ await sleep(3000);
32
+ const s = await apiRequestJson(`/business/${businessId}/ai-computer/status`, { method: 'GET', token });
33
+ if (s.ok && s.data && s.data.status === 'running' && s.data.endpoint) {
34
+ console.log(`awake (${Math.floor((Date.now() - start) / 1000)}s)`);
35
+ return true;
36
+ }
37
+ }
38
+ console.log('timeout');
39
+ return false;
40
+ }
41
+
42
+ async function resolveBusiness(token, slug) {
43
+ const businesses = loadBusinesses();
44
+ const list = await apiRequestJson('/business/', { method: 'GET', token });
45
+ if (list.ok) {
46
+ const match = (list.data || []).find(
47
+ (b) => b.slug === slug || b.name.toLowerCase() === slug.toLowerCase()
48
+ );
49
+ if (!match) return null;
50
+ businesses[slug] = {
51
+ business_id: match.id,
52
+ workspace_id: match.workspace_id,
53
+ name: match.name,
54
+ slug: match.slug,
55
+ added_at: new Date().toISOString(),
56
+ };
57
+ saveBusinesses(businesses);
58
+ return { businessId: match.id, workspaceId: match.workspace_id, businessName: match.name };
59
+ }
60
+ if (businesses[slug]) {
61
+ return {
62
+ businessId: businesses[slug].business_id,
63
+ workspaceId: businesses[slug].workspace_id,
64
+ businessName: businesses[slug].name || slug,
65
+ };
66
+ }
67
+ return null;
68
+ }
69
+
70
+ function pickSlug(args) {
71
+ const wsIdx = args.findIndex((a) => a === '--workspace' || a === '-w');
72
+ if (wsIdx !== -1 && args[wsIdx + 1]) {
73
+ const slug = args[wsIdx + 1];
74
+ args.splice(wsIdx, 2);
75
+ return slug;
76
+ }
77
+ const bizFile = path.join(process.cwd(), '.atris', 'business.json');
78
+ if (fs.existsSync(bizFile)) {
79
+ try { return JSON.parse(fs.readFileSync(bizFile, 'utf8')).slug; } catch { /* ignore */ }
80
+ }
81
+ return null;
82
+ }
83
+
84
+ function printHelp() {
85
+ console.log('Usage:');
86
+ console.log(' atris aeo init [--workspace <slug>]');
87
+ console.log(' atris aeo draft "<topic>" [--workspace <slug>] [--queries q1,q2] [--slug X] [--url URL]');
88
+ console.log('');
89
+ console.log('Examples:');
90
+ console.log(' atris aeo init');
91
+ console.log(' atris aeo draft "what is pallet" --queries "what is pallet,best freight platform"');
92
+ console.log(' atris aeo draft "how does atris work" --workspace doordash --slug atris-overview');
93
+ }
94
+
95
+ async function aeoInit(args) {
96
+ const slug = pickSlug(args);
97
+ if (!slug) {
98
+ console.error('Cannot determine business. Pass --workspace <slug> or run from a workspace.');
99
+ process.exit(1);
100
+ }
101
+ const creds = loadCredentials();
102
+ if (!creds || !creds.token) { console.error('Not logged in. Run: atris login'); process.exit(1); }
103
+
104
+ const biz = await resolveBusiness(creds.token, slug);
105
+ if (!biz) { console.error(`Business "${slug}" not found.`); process.exit(1); }
106
+ if (!biz.workspaceId) { console.error(`Business "${slug}" has no workspace.`); process.exit(1); }
107
+
108
+ const awake = await ensureAwake(creds.token, biz.businessId);
109
+ if (!awake) { console.error(' EC2 computer did not become ready in time.'); process.exit(1); }
110
+
111
+ const result = await apiRequestJson(
112
+ `/business/${biz.businessId}/workspaces/${biz.workspaceId}/aeo/init`,
113
+ { method: 'POST', token: creds.token, body: {}, timeoutMs: 60000 }
114
+ );
115
+ if (!result.ok) {
116
+ console.error(`✗ aeo init failed: ${result.errorMessage || result.error || result.status}`);
117
+ process.exit(1);
118
+ }
119
+ const data = result.data || {};
120
+ const created = data.created || [];
121
+ const skipped = data.skipped || [];
122
+ console.log(`✓ AEO entity graph @ ${data.dir}`);
123
+ if (created.length) console.log(` created: ${created.map((p) => p.split('/').pop()).join(', ')}`);
124
+ if (skipped.length) console.log(` existed: ${skipped.map((p) => p.split('/').pop()).join(', ')}`);
125
+ }
126
+
127
+ async function aeoDraft(args) {
128
+ // Pull --slug, --url, --queries, --workspace; remainder is the topic.
129
+ const opts = {};
130
+ for (const k of ['slug', 'url', 'queries']) {
131
+ const i = args.findIndex((a) => a === `--${k}`);
132
+ if (i !== -1 && args[i + 1]) {
133
+ opts[k] = args[i + 1];
134
+ args.splice(i, 2);
135
+ }
136
+ }
137
+ const slug = pickSlug(args);
138
+ const topic = args.join(' ').trim();
139
+ if (!topic) {
140
+ console.error('Missing topic. Usage: atris aeo draft "<topic>"');
141
+ process.exit(1);
142
+ }
143
+ if (!slug) {
144
+ console.error('Cannot determine business. Pass --workspace <slug> or run from a workspace.');
145
+ process.exit(1);
146
+ }
147
+ const creds = loadCredentials();
148
+ if (!creds || !creds.token) { console.error('Not logged in. Run: atris login'); process.exit(1); }
149
+
150
+ const biz = await resolveBusiness(creds.token, slug);
151
+ if (!biz) { console.error(`Business "${slug}" not found.`); process.exit(1); }
152
+ if (!biz.workspaceId) { console.error(`Business "${slug}" has no workspace.`); process.exit(1); }
153
+
154
+ const awake = await ensureAwake(creds.token, biz.businessId);
155
+ if (!awake) { console.error(' EC2 computer did not become ready in time.'); process.exit(1); }
156
+
157
+ const body = { topic };
158
+ if (opts.slug) body.slug = opts.slug;
159
+ if (opts.url) body.target_url = opts.url;
160
+ if (opts.queries) body.target_queries = opts.queries.split(',').map((s) => s.trim()).filter(Boolean);
161
+
162
+ process.stdout.write(`Drafting "${topic}" for ${biz.businessName}... `);
163
+ const t0 = Date.now();
164
+ const result = await apiRequestJson(
165
+ `/business/${biz.businessId}/workspaces/${biz.workspaceId}/aeo/draft`,
166
+ { method: 'POST', token: creds.token, body, timeoutMs: 180000 }
167
+ );
168
+ const elapsed = ((Date.now() - t0) / 1000).toFixed(1);
169
+ if (!result.ok) {
170
+ console.log('failed');
171
+ console.error(`✗ aeo draft failed (${result.status}): ${result.errorMessage || result.error}`);
172
+ process.exit(1);
173
+ }
174
+ const data = result.data || {};
175
+ console.log(`done (${elapsed}s)`);
176
+ console.log('');
177
+ console.log(` path: ${data.path}`);
178
+ console.log(` self-score: ${data.self_score ?? '?'}/10`);
179
+ console.log(` credits: ${data.credits_charged ?? '?'}`);
180
+ console.log(` tokens: in=${data.tokens?.input ?? '?'} out=${data.tokens?.output ?? '?'}`);
181
+ console.log(` entity graph: entities=${data.entity_graph?.has_entities ? 'y' : 'n'} defs=${data.entity_graph?.has_definitions ? 'y' : 'n'} stats=${data.entity_graph?.has_stats ? 'y' : 'n'}`);
182
+ if (data.overlay_active) console.log(` overlay: active (${data.overlay_lines} lines)`);
183
+ if (data.hint) console.log(` hint: ${data.hint}`);
184
+ }
185
+
186
+ async function run(args = []) {
187
+ const sub = args[0];
188
+ if (!sub || sub === 'help' || sub === '--help' || sub === '-h') return printHelp();
189
+ const rest = args.slice(1);
190
+ if (sub === 'init') return aeoInit(rest);
191
+ if (sub === 'draft') return aeoDraft(rest);
192
+ console.error(`Unknown aeo subcommand: ${sub}`);
193
+ printHelp();
194
+ process.exit(1);
195
+ }
196
+
197
+ module.exports = { run };