atris 3.82.25 → 3.83.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/AGENTS.md +10 -12
- package/README.md +77 -0
- package/bin/atris.js +95 -44
- package/commands/activate.js +39 -33
- package/commands/agent-home.js +403 -0
- package/commands/agent-signin.js +214 -0
- package/commands/agent-spawn.js +1 -0
- package/commands/autoland.js +173 -71
- package/commands/brain.js +211 -410
- package/commands/doctor.js +38 -26
- package/commands/init.js +4 -4
- package/commands/join.js +159 -0
- package/commands/memory.js +2 -2
- package/commands/morning.js +132 -0
- package/commands/night.js +178 -0
- package/commands/recap.js +4 -4
- package/commands/task.js +31 -2
- package/commands/worktree.js +18 -0
- package/lib/agent-home.js +289 -0
- package/lib/agent-page.js +1257 -0
- package/lib/cleanup-summary.js +4 -2
- package/lib/first-minute.js +41 -20
- package/lib/gm-phone-page.js +8 -2
- package/lib/known-commands.js +1 -1
- package/lib/member-autoland.js +39 -13
- package/lib/member-export.js +31 -0
- package/lib/memory-folder.js +122 -24
- package/lib/memory-live.js +2 -2
- package/lib/night-learned.js +171 -0
- package/lib/night-shift.js +123 -0
- package/lib/task-core.js +2 -1
- package/lib/task-db.js +32 -0
- package/package.json +1 -1
- package/templates/members/designer-1/MEMBER.md +34 -0
- package/templates/members/designer-1/MISSION.md +11 -0
- package/templates/members/designer-1/SOUL.md +11 -0
- package/templates/members/designer-1/context/why.md +5 -0
- package/templates/members/designer-1/goals.json +5 -0
- package/templates/members/designer-1/goals.md +3 -0
- package/templates/members/designer-1/skills/designer-1/SKILL.md +179 -0
- package/templates/members/designer-1/skills/designer-1/parts.md +55 -0
package/AGENTS.md
CHANGED
|
@@ -208,14 +208,14 @@ named none. These rules are the difference, in priority order:
|
|
|
208
208
|
**Protocol:** See `atris/atris.md` for full spec.
|
|
209
209
|
|
|
210
210
|
<!-- ATRIS_BRAIN_COMPILE:START -->
|
|
211
|
-
## Atris
|
|
211
|
+
## Atris start-up notes
|
|
212
212
|
|
|
213
|
-
This
|
|
213
|
+
This folder keeps notes that give every agent the same starting context.
|
|
214
214
|
|
|
215
|
-
|
|
216
|
-
`atris brain activate --root
|
|
215
|
+
At the start of a session, run this and read what it prints:
|
|
216
|
+
`atris brain activate --root .`
|
|
217
217
|
|
|
218
|
-
|
|
218
|
+
Then read these files in order:
|
|
219
219
|
- `atris/now.md`
|
|
220
220
|
- `atris/brain/STATUS.md`
|
|
221
221
|
- `atris/brain/self_improvement_ledger.md`
|
|
@@ -227,12 +227,10 @@ Load these first:
|
|
|
227
227
|
- `atris/TODO.md`
|
|
228
228
|
- `atris/wiki/index.md`
|
|
229
229
|
|
|
230
|
-
First
|
|
231
|
-
|
|
232
|
-
Shape: `<operator>, today is about <move>` -> `I picked this because <why now>` -> `Ready: <draft/proof/context>` -> `Go deeper: <paths>`.
|
|
233
|
-
Definitions: operator = current person or agent; move = one concrete high-leverage workflow; why now = business reason; ready = prepared action or proof; paths = 2-4 optional deeper views.
|
|
230
|
+
First reply: open with the one thing most worth doing today. Then say why, what is already prepared and where to look for more.
|
|
231
|
+
Shape: `<name>, today is about <the one thing>` -> `I picked this because <why now>` -> `Ready: <what is prepared>` -> `Go deeper: <2 to 4 paths>`.
|
|
234
232
|
|
|
235
|
-
Keep this voice
|
|
233
|
+
Keep this voice in every reply:
|
|
236
234
|
<!-- ATRIS_VOICE_CARD:START -->
|
|
237
235
|
## Voice card
|
|
238
236
|
|
|
@@ -247,6 +245,6 @@ Landing example:
|
|
|
247
245
|
Replies now get a plain-language check before they reach you. The checks passed, and the change is ready.
|
|
248
246
|
<!-- ATRIS_VOICE_CARD:END -->
|
|
249
247
|
|
|
250
|
-
|
|
251
|
-
`atris brain compile --root
|
|
248
|
+
After meaningful work, save fresh notes:
|
|
249
|
+
`atris brain compile --root . --yes`
|
|
252
250
|
<!-- ATRIS_BRAIN_COMPILE:END -->
|
package/README.md
CHANGED
|
@@ -137,6 +137,15 @@ atris host import people.jsonl # bulk add people and who already knows who
|
|
|
137
137
|
atris host room # the host's weekly read
|
|
138
138
|
```
|
|
139
139
|
|
|
140
|
+
### Design screens with Designer-1
|
|
141
|
+
|
|
142
|
+
Designer-1 is a team member that builds web screens from a fixed set of 19 parts (button, field, select, dialog, tabs, row list and the rest). It reuses a part before writing one, keeps one spacing scale, writes very few words, and checks its work by keyboard, by control name, and at phone and desktop widths before it reports. A project's own taste files in `atris/taste/` win over its defaults.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
atris member install designer-1
|
|
146
|
+
atris member install designer-1 --update # pick up the latest skill; your edits to its other files stay
|
|
147
|
+
```
|
|
148
|
+
|
|
140
149
|
### Put members to work
|
|
141
150
|
|
|
142
151
|
Members are helpers that work in their own copy of the repo. Three words cover it.
|
|
@@ -393,6 +402,74 @@ atris business record atris/reports/2026-04-12-operator-recap.md --outcome mixed
|
|
|
393
402
|
- `atris pull` and `atris push` sync cloud workspaces and journals
|
|
394
403
|
- `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
|
|
395
404
|
|
|
405
|
+
## Agent page (beta)
|
|
406
|
+
|
|
407
|
+
Agent page is one small browser file, in beta. An AI agent reads the controls on a page by their names, and presses them, without screenshots. It works on the page a person already has when that page is built from normal form controls and ARIA roles. It has no dependencies. The limits are listed below.
|
|
408
|
+
|
|
409
|
+
The marks are a guardrail for well-behaved agents, not security. A page must still enforce its real rules on its server. Do not put real secrets in a page and rely on a mark to hide them: any software running in that page can read them.
|
|
410
|
+
|
|
411
|
+
Load it with a plain script tag, or `require` it in Node and bundlers:
|
|
412
|
+
|
|
413
|
+
```html
|
|
414
|
+
<script src="node_modules/atris/lib/agent-page.js"></script>
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
```js
|
|
418
|
+
const agent = AgentPage.createAgent({
|
|
419
|
+
allow: (request) => window.confirm(`Let the agent ${request.action} "${request.label}"?`),
|
|
420
|
+
timeout: 5 * 60 * 1000,
|
|
421
|
+
});
|
|
422
|
+
agent.snapshot();
|
|
423
|
+
await agent.act({ steps: [{ label: 'Project name', action: 'set', args: { value: 'Atlas' } }] });
|
|
424
|
+
await agent.query({ text: 'Role', kind: 'select' });
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
Three calls do the work.
|
|
428
|
+
|
|
429
|
+
`snapshot()` lists the controls. Each one has a ref, a kind, a label, its value or checked or open state, and the actions it accepts right now. The list is capped at 100. A disabled or read-only control says so and accepts no actions.
|
|
430
|
+
|
|
431
|
+
`act({ steps })` runs steps in order and stops at the first failure. Each step names a control by `ref` (from a snapshot) or by `label`, and gives an `action`: `click`, `set`, `check`, or `uncheck`. Each result shows the changes, the current value, and any error.
|
|
432
|
+
|
|
433
|
+
After a change, the file finds the control again in the live page (the same element, or else the one visible control with the same kind and name) and reads its value there. A typed value, a tick, or a choice that did not stick reads as a failure, and so does a press on something that should open or close but did not. A plain press on a button or link says ok once it is delivered; the changes list shows what the page did with it. A disabled choice is refused.
|
|
434
|
+
|
|
435
|
+
A select or listbox that holds more than one choice shows every chosen value as a list. To change it, send `set` the full list of choices to keep, for example `args: { value: ['Email', 'Chat'] }`. A single value is refused when it would clear other choices, so nothing is cleared unless the agent names the whole list. Hidden choices in such a list are left as they are.
|
|
436
|
+
|
|
437
|
+
`query({ text, within, kind })` finds controls and lists the choices of a select or listbox. The list is capped at 50. With `text`, only the matching choices are listed, so a choice past the cap can still be found.
|
|
438
|
+
|
|
439
|
+
Three marks on the page set the rules, with the `allow` callback.
|
|
440
|
+
|
|
441
|
+
The `allow` callback decides, and it may return a promise. If you pass one, it runs before every change the agent makes, so the person sees each click, change, or tick first. It gets the control's label, the action and its args, whether the step is marked for confirmation, and the page's own confirm sentences in `reasons`. If the callback throws, the step fails with a fixed sentence; the error's own message is never passed to the agent.
|
|
442
|
+
|
|
443
|
+
Before asking, the file lists the elements the step involves and the confirm marks around them. It lists them again after the answer and refuses the step if anything differs: a mark added, removed or reworded, or the control moved under a different mark. It reads the marks again before each part of the change too (after focus, between events, and before each choice it clicks), and stops with the rest undone if a new mark appears.
|
|
444
|
+
|
|
445
|
+
One approval at a time per page. While a step waits for the person, every other agent on the page is refused, so one agent cannot press another's confirm button. A host's own confirm dialog should still carry `data-agent-hidden`, so no agent can see it at all.
|
|
446
|
+
|
|
447
|
+
`data-agent-confirm` marks a control that needs a person. A step involves the control, its labels (wrapping and `for`), the form it belongs to (also through the `form` attribute), every field of that form when the press submits or resets it, each choice the step picks, and each choice or radio the step clears, hidden ones included, with their labels. A mark on any of these, or around them, counts. Without an `allow` callback, that step fails and tells the agent to ask the person.
|
|
448
|
+
|
|
449
|
+
`data-agent-hidden` leaves a region out entirely. On a single choice, it leaves out that choice. A choice hidden by the `hidden` attribute or by css is left out the same way.
|
|
450
|
+
|
|
451
|
+
`data-agent-secret` hides the value and all state of the marked control and everything inside it: value, checked, selected, open, and its choices all read `[redacted]`, and its actions do not change with its state. Its name stays visible, and comes only from a label element, `aria-label`, or `aria-labelledby`; placeholder, title, alt and the control's own text are never used. To hide the name too, use `data-agent-hidden`. Other text inside a secret mark is not read. The agent can still type into a secret field or pick a secret choice it already knows. Password, card, and one-time-code fields count as secret without a mark.
|
|
452
|
+
|
|
453
|
+
A mark covers the element and everything inside it. Labels, values, changes and error sentences are built when the result is returned, with the marks as they are then: a value that became secret reads `[redacted]` on both sides of a change, and a control that is gone or hidden is reported by its ref only.
|
|
454
|
+
|
|
455
|
+
A page can tell when the agent is acting. While an act step runs, `document.documentElement` has `data-agent-acting="true"`. Every event the agent fires carries `event.agentAct = { ref, action }`.
|
|
456
|
+
|
|
457
|
+
What it cannot do:
|
|
458
|
+
|
|
459
|
+
It reads the main page only. Controls inside iframes and shadow roots are not listed, so the agent cannot reach them. Snapshot and query add an entry of kind `unread` that counts what was skipped, so the agent knows. A custom element may hold a closed shadow root, so each one is counted as possibly unread.
|
|
460
|
+
|
|
461
|
+
These limits are accepted. A closed shadow root on an ordinary element, or one made before this file loads, cannot be detected at all. Frames from other sites cannot be read. The file trusts the page it runs in: page scripts can always change anything, including the marks.
|
|
462
|
+
|
|
463
|
+
A page whose own scripts add, remove or move marks, or swap controls, in the middle of an event is handled only as far as the checks above go: marks read again before each part of a change, and the control found again after it. Beyond that it is out of scope.
|
|
464
|
+
|
|
465
|
+
Editable text regions (`contenteditable`) are not typed into. Plain ones are counted as unread. One with a textbox role is listed with its text and no actions.
|
|
466
|
+
|
|
467
|
+
The tests here drive real Chrome. The controlled-input test imitates React with a hand-built value tracker; a check against real React is planned and not done yet.
|
|
468
|
+
|
|
469
|
+
For agents that speak tools, `agentTools` lists three tools (`page_snapshot`, `page_act`, `page_query`) with JSON schemas. `registerTools(agent, { allow })` registers them on `document.modelContext` when the page offers it, and does nothing otherwise.
|
|
470
|
+
|
|
471
|
+
The mark names and the snapshot, act, and query shape come from Vercel's UI SDK agent bridge. The code here is our own.
|
|
472
|
+
|
|
396
473
|
## Engines
|
|
397
474
|
|
|
398
475
|
An engine is the intelligence that builds a mission. Every installed headless coding CLI is a swappable worker behind one contract: a bounded prompt goes in, verified proof comes out, and engines never self-certify their own work. The house default is `atris-fast` (`ax`); `claude`, `codex`, `cursor`, `devin`, `grok`, and others join when their CLIs are installed.
|
package/bin/atris.js
CHANGED
|
@@ -529,25 +529,25 @@ function showStartHelp() {
|
|
|
529
529
|
}
|
|
530
530
|
|
|
531
531
|
function showHelpShort() {
|
|
532
|
-
console.log('atris:
|
|
532
|
+
console.log('atris: say what you want. see what is already done. get one next step.');
|
|
533
533
|
console.log('');
|
|
534
534
|
console.log('Start here:');
|
|
535
535
|
console.log(' 1. atris init set up this folder');
|
|
536
536
|
console.log(' 2. atris doctor check this computer is ready');
|
|
537
|
-
console.log(' 3. atris "what you want"
|
|
537
|
+
console.log(' 3. atris "what you want" save what you want in plain words');
|
|
538
538
|
console.log('');
|
|
539
|
-
console.log(' atris
|
|
540
|
-
console.log(' atris later "..."
|
|
541
|
-
console.log(' atris idea "..."
|
|
542
|
-
console.log(' atris guide "..."
|
|
543
|
-
console.log(' atris do work
|
|
544
|
-
console.log(' atris done
|
|
545
|
-
console.log(' atris spaceship
|
|
546
|
-
console.log(' atris autopilot
|
|
547
|
-
console.log(' atris mission the
|
|
548
|
-
console.log(' atris gm
|
|
539
|
+
console.log(' atris see where things stand and the next step');
|
|
540
|
+
console.log(' atris later "..." save a thought for later');
|
|
541
|
+
console.log(' atris idea "..." save an idea for later work');
|
|
542
|
+
console.log(' atris guide "..." suggest a command from your words');
|
|
543
|
+
console.log(' atris do see the next step for work here');
|
|
544
|
+
console.log(' atris done finish and keep what your agent built');
|
|
545
|
+
console.log(' atris spaceship plan a few hours of work');
|
|
546
|
+
console.log(' atris autopilot plan work that runs until you stop it');
|
|
547
|
+
console.log(' atris mission see the current goal');
|
|
548
|
+
console.log(' atris gm run your team like a sports front office');
|
|
549
549
|
console.log('');
|
|
550
|
-
console.log('More: atris help --all ·
|
|
550
|
+
console.log('More: atris help --all · command list for scripts: atris help --json');
|
|
551
551
|
}
|
|
552
552
|
|
|
553
553
|
function helpCatalog() {
|
|
@@ -571,24 +571,24 @@ function helpCatalog() {
|
|
|
571
571
|
}
|
|
572
572
|
|
|
573
573
|
function showHelpAll() {
|
|
574
|
-
console.log('
|
|
574
|
+
console.log('atris: say what you want. see what is already done. get one next step.');
|
|
575
575
|
console.log('');
|
|
576
|
-
console.log(' atris later "..."
|
|
577
|
-
console.log(' atris idea "..."
|
|
578
|
-
console.log(' atris guide "..."
|
|
579
|
-
console.log(' atris do work
|
|
580
|
-
console.log(' atris done
|
|
581
|
-
console.log(' atris spaceship
|
|
582
|
-
console.log(' atris autopilot
|
|
583
|
-
console.log(' atris mission the
|
|
576
|
+
console.log(' atris later "..." save a thought for later');
|
|
577
|
+
console.log(' atris idea "..." save an idea for later work');
|
|
578
|
+
console.log(' atris guide "..." suggest a command from your words');
|
|
579
|
+
console.log(' atris do see the next step for work here');
|
|
580
|
+
console.log(' atris done finish and keep what your agent built');
|
|
581
|
+
console.log(' atris spaceship plan a few hours of work');
|
|
582
|
+
console.log(' atris autopilot plan work that runs until you stop it');
|
|
583
|
+
console.log(' atris mission see the current goal');
|
|
584
584
|
console.log(' atris recap what your ai agent did today');
|
|
585
585
|
console.log(' atris review check the work');
|
|
586
586
|
console.log(' atris stop stop the live goal');
|
|
587
587
|
console.log('');
|
|
588
|
-
console.log('
|
|
589
|
-
console.log('
|
|
590
|
-
console.log('
|
|
591
|
-
console.log('
|
|
588
|
+
console.log('How it fits together:');
|
|
589
|
+
console.log(' you or your business can own computers.');
|
|
590
|
+
console.log(' a computer brings a workspace, files, tools, saved logins, memory, agents and checks together.');
|
|
591
|
+
console.log(' computers can be for code, research, sales, reporting, events, support or operations.');
|
|
592
592
|
console.log('');
|
|
593
593
|
console.log('Setup:');
|
|
594
594
|
console.log(' setup - Guided first-time setup (login, pick business, pull)');
|
|
@@ -760,6 +760,9 @@ function showHelpAll() {
|
|
|
760
760
|
console.log('');
|
|
761
761
|
console.log('Cloud & agents:');
|
|
762
762
|
console.log(' computer - Open a scoped AI computer (cloud/local, personal/business)');
|
|
763
|
+
console.log(' night - Give your agent a goal for tonight with a dollar cap (atris night "<goal>" --cap 1)');
|
|
764
|
+
console.log(' morning - How last night went: score, what it learned (saved to your atris/ folder), spend');
|
|
765
|
+
console.log(' join - Join a computer as a named agent (--name), or accept an invite (<code>)');
|
|
763
766
|
console.log(' receipt - Save evidence from an agent run');
|
|
764
767
|
console.log(' console - Start/attach always-on coding console (tmux daemon)');
|
|
765
768
|
console.log(' soul - Show, snapshot, or fork workspace identity');
|
|
@@ -767,6 +770,8 @@ function showHelpAll() {
|
|
|
767
770
|
console.log(' loops - Self-improving loop audit/scaffold (`init`, `audit`, `tick`, `board`)');
|
|
768
771
|
console.log(' self-improve - Alias for `atris loops init`');
|
|
769
772
|
console.log(' agent - Select cloud agent, spawn worker requests, or run `agent doctor`');
|
|
773
|
+
console.log(' agent pull|push|snapshot: carry an agent\'s home folder; agent spawn --budget: make a child agent');
|
|
774
|
+
console.log(' agent signin <agent> --key-file <path>: sign in with the agent\'s own key, print a token');
|
|
770
775
|
console.log(' chat - Chat with Atris 2 Fast in this workspace (--agent for cloud agent; or: atris chat scan)');
|
|
771
776
|
console.log(' fast - Chat with Atris2 Fast');
|
|
772
777
|
console.log(' login - Sign in, add an account, or mint a scoped agent token (--agent)');
|
|
@@ -819,6 +824,7 @@ function showHelpAll() {
|
|
|
819
824
|
console.log('Team:');
|
|
820
825
|
console.log(' member create <name> - Scaffold a new team member (MEMBER.md)');
|
|
821
826
|
console.log(' member list - Show all team members');
|
|
827
|
+
console.log(' member install <n> - Add a ready-made member: host, designer-1');
|
|
822
828
|
console.log(' member activate <n> - Activate a member (link skills, show context)');
|
|
823
829
|
console.log(' member upgrade <n> - Convert flat file to directory format');
|
|
824
830
|
console.log(' member result <n> - Newest run of a member: run record, receipt, artifacts, result number');
|
|
@@ -840,7 +846,7 @@ function showHelpAll() {
|
|
|
840
846
|
console.log(' version - Show Atris version');
|
|
841
847
|
console.log(' help - you say what you want (help --all for every command)');
|
|
842
848
|
console.log('');
|
|
843
|
-
console.log('
|
|
849
|
+
console.log('tip: run "atris" to get started');
|
|
844
850
|
console.log('');
|
|
845
851
|
}
|
|
846
852
|
|
|
@@ -1732,7 +1738,13 @@ function showWelcomeVisualization() {
|
|
|
1732
1738
|
const projectName = path.basename(cwd);
|
|
1733
1739
|
const row = (label, value) => ` ${label.padEnd(9)}${value}`;
|
|
1734
1740
|
const sub = (text) => ` ${' '.repeat(9)}- ${text}`;
|
|
1735
|
-
const trimTitle = (t) =>
|
|
1741
|
+
const trimTitle = (t) => {
|
|
1742
|
+
const title = require('../lib/first-minute').plainTitle(t);
|
|
1743
|
+
if (title.length <= 64) return title;
|
|
1744
|
+
const cut = title.slice(0, 61);
|
|
1745
|
+
const boundary = cut.lastIndexOf(' ');
|
|
1746
|
+
return `${(boundary > 0 ? cut.slice(0, boundary) : cut).trimEnd()}...`;
|
|
1747
|
+
};
|
|
1736
1748
|
|
|
1737
1749
|
let glance = {
|
|
1738
1750
|
backlog: 0, active: 0, review: 0, reviewCertified: 0,
|
|
@@ -1818,7 +1830,7 @@ function showWelcomeVisualization() {
|
|
|
1818
1830
|
console.log('');
|
|
1819
1831
|
|
|
1820
1832
|
if (!isInitialized) {
|
|
1821
|
-
console.log('
|
|
1833
|
+
console.log(' atris is not set up in this folder yet.');
|
|
1822
1834
|
console.log('');
|
|
1823
1835
|
console.log(row('next', 'atris init (set up this folder)'));
|
|
1824
1836
|
console.log('');
|
|
@@ -1866,11 +1878,14 @@ function showWelcomeVisualization() {
|
|
|
1866
1878
|
|
|
1867
1879
|
try {
|
|
1868
1880
|
const health = require('../commands/doc-health').computeDocHealth(cwd);
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1881
|
+
const map = fs.readFileSync(path.join(atrisDir, 'MAP.md'), 'utf8');
|
|
1882
|
+
const starterMap = map.includes('Run your AI agent with atris.md to populate this file.');
|
|
1883
|
+
if (health.ok && map.trim() && !starterMap) {
|
|
1884
|
+
const score = health.lookup_hops.score;
|
|
1885
|
+
const detail = score === null
|
|
1886
|
+
? 'add sample questions to test the map: atris/doc-health/questions.jsonl'
|
|
1887
|
+
: `the map answers ${score >= 1 ? 'every sample question' : `${Math.min(99, Math.round(score * 100))}% of sample questions`} · ${(health.boot_load.approximate_tokens / 1000).toFixed(1)}k tokens to read at start`;
|
|
1888
|
+
console.log(row('docs', `${Math.round(health.overall.total)}/100 · ${detail}`));
|
|
1874
1889
|
}
|
|
1875
1890
|
} catch {
|
|
1876
1891
|
// Document health is advisory and must never prevent startup.
|
|
@@ -1881,7 +1896,10 @@ function showWelcomeVisualization() {
|
|
|
1881
1896
|
// journal, plus agent branches parked for a person.
|
|
1882
1897
|
try {
|
|
1883
1898
|
const lately = require('../lib/cleanup-summary').latestCleanupLine(cwd);
|
|
1884
|
-
if (lately)
|
|
1899
|
+
if (lately) {
|
|
1900
|
+
const plain = lately.replace(/^[a-z][a-z -]{0,30}:\s*/i, '').replace(/\blanded\b/gi, 'completed').replace(/\bland on their own\b/gi, 'finish on their own').replace(/\b([0-9]+) agent branch(es)? parked for you\b/g, (m, n, many) => `${n} ${many ? 'pieces' : 'piece'} of agent work set aside for you`);
|
|
1901
|
+
console.log(row('lately', plain.charAt(0).toLowerCase() + plain.slice(1)));
|
|
1902
|
+
}
|
|
1885
1903
|
} catch {
|
|
1886
1904
|
// Advisory only; never blocks boot.
|
|
1887
1905
|
}
|
|
@@ -1940,7 +1958,7 @@ function showWelcomeVisualization() {
|
|
|
1940
1958
|
if (landInfo.due > 0) landText += ` (${landInfo.due} overdue)`;
|
|
1941
1959
|
tidyBits.push(landText);
|
|
1942
1960
|
}
|
|
1943
|
-
if (rotInfo && rotInfo.worktrees > 0) tidyBits.push(`${rotInfo.worktrees} old cop${rotInfo.worktrees === 1 ? 'y' : 'ies'} to
|
|
1961
|
+
if (rotInfo && rotInfo.worktrees > 0) tidyBits.push(`${rotInfo.worktrees} old cop${rotInfo.worktrees === 1 ? 'y' : 'ies'} to clear out`);
|
|
1944
1962
|
if (rotInfo && rotInfo.lessons > 0) tidyBits.push(`${rotInfo.lessons} open problem${rotInfo.lessons === 1 ? '' : 's'}`);
|
|
1945
1963
|
if (tidyBits.length) {
|
|
1946
1964
|
console.log(row('tidy', tidyBits.join(', ')));
|
|
@@ -1953,7 +1971,7 @@ function showWelcomeVisualization() {
|
|
|
1953
1971
|
const rev = require('../commands/improve').collectRevisionSignals(cwd, { days: 7 });
|
|
1954
1972
|
const fixed = Number.isFinite(rev.fixed) ? rev.fixed : rev.revised;
|
|
1955
1973
|
if (fixed > 0) {
|
|
1956
|
-
console.log(` ${fixed}
|
|
1974
|
+
console.log(` ${fixed} finished change${fixed === 1 ? '' : 's'} needed a human fix this week; run atris improve revisions (see what needed fixing)`);
|
|
1957
1975
|
}
|
|
1958
1976
|
} catch {
|
|
1959
1977
|
// Silent: the gauge never blocks boot.
|
|
@@ -1966,7 +1984,7 @@ function showWelcomeVisualization() {
|
|
|
1966
1984
|
wikiNudge = '';
|
|
1967
1985
|
}
|
|
1968
1986
|
if (wikiNudge) {
|
|
1969
|
-
console.log(` ${wikiNudge}`);
|
|
1987
|
+
console.log(` ${wikiNudge.replace('the wiki has grown a lot since its last pruning; run: atris wiki consolidate', 'the project notes have grown; run atris wiki consolidate (review notes to combine)')}`);
|
|
1970
1988
|
}
|
|
1971
1989
|
|
|
1972
1990
|
console.log(row('logs', journalEntries > 0
|
|
@@ -1990,7 +2008,7 @@ function showWelcomeVisualization() {
|
|
|
1990
2008
|
const teamLine = require('../lib/team-lineup').bootTeamLine(cwd, { width: 80 - 11, jobs });
|
|
1991
2009
|
if (teamLine) console.log(row('team', teamLine));
|
|
1992
2010
|
const endsLine = engineView.bootExpiringLine(engineView.expiringWorkers(jobs));
|
|
1993
|
-
if (endsLine) console.log(row('roster',
|
|
2011
|
+
if (endsLine) console.log(row('tools', endsLine.replace('renew: atris engine roster confirm', 'run atris engine roster confirm (renew the assigned tools)')));
|
|
1994
2012
|
} catch {
|
|
1995
2013
|
// Silent: the lineup is a hint, not a gate.
|
|
1996
2014
|
}
|
|
@@ -2012,7 +2030,7 @@ function showWelcomeVisualization() {
|
|
|
2012
2030
|
.filter((intent) => intent.offer === 'boot_empty')
|
|
2013
2031
|
.map((intent) => intent.say[0])
|
|
2014
2032
|
.slice(0, 2);
|
|
2015
|
-
console.log(row('say', `"${phrases[0]}" or "${phrases[1]}" (your agent
|
|
2033
|
+
console.log(row('say', `"${phrases[0]}" or "${phrases[1]}" (say it to your agent in your own words)`));
|
|
2016
2034
|
}
|
|
2017
2035
|
|
|
2018
2036
|
// The next command always carries a plain-english gloss: a newcomer should
|
|
@@ -2023,7 +2041,7 @@ function showWelcomeVisualization() {
|
|
|
2023
2041
|
} else if (landInfo && landInfo.due > 0) {
|
|
2024
2042
|
next = `atris land --reap (put away the overdue work)`;
|
|
2025
2043
|
} else if (endgameState.slug !== 'unset') {
|
|
2026
|
-
next = 'atris autopilot (
|
|
2044
|
+
next = 'atris autopilot (keep working on the goal; it asks before it starts)';
|
|
2027
2045
|
} else {
|
|
2028
2046
|
next = 'atris plan (plan the first tasks)';
|
|
2029
2047
|
}
|
|
@@ -2287,10 +2305,21 @@ if (command === 'guide') {
|
|
|
2287
2305
|
if (process.argv.slice(3).includes('--json')) {
|
|
2288
2306
|
console.log(JSON.stringify({ ok: false, error: message }, null, 2));
|
|
2289
2307
|
} else {
|
|
2290
|
-
console.error(
|
|
2308
|
+
console.error(message);
|
|
2291
2309
|
}
|
|
2292
2310
|
process.exit(1);
|
|
2293
2311
|
});
|
|
2312
|
+
} else if (command === 'agent' && process.argv[3] === 'signin') {
|
|
2313
|
+
// An agent signs in with its own private key; no owner login, no --account gate.
|
|
2314
|
+
Promise.resolve().then(() => require('../commands/agent-signin').agentSigninCommand(process.argv.slice(4)))
|
|
2315
|
+
.then((code) => exitAfterStdoutDrain(code || 0))
|
|
2316
|
+
.catch((err) => { console.error(`\nerror: ${err.message || err}`); process.exit(1); });
|
|
2317
|
+
} else if (command === 'agent' && require('../commands/agent-home').isHomeCommand(process.argv.slice(3))) {
|
|
2318
|
+
// pull/push/snapshot/spawn --budget name one agent (by id, saved name, or the
|
|
2319
|
+
// pulled folder), and the server checks ownership, so no --account gate.
|
|
2320
|
+
Promise.resolve().then(() => require('../commands/agent-home').agentHomeCommand(process.argv.slice(3)))
|
|
2321
|
+
.then((code) => exitAfterStdoutDrain(code || 0))
|
|
2322
|
+
.catch((err) => { console.error(`\nerror: ${err.message || err}`); process.exit(1); });
|
|
2294
2323
|
} else if (command === 'agent') {
|
|
2295
2324
|
{
|
|
2296
2325
|
const gate = requireAccountBound(process.argv.slice(3));
|
|
@@ -3598,8 +3627,24 @@ if (command === 'guide') {
|
|
|
3598
3627
|
.catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
|
|
3599
3628
|
}
|
|
3600
3629
|
} else if (command === 'join') {
|
|
3601
|
-
|
|
3602
|
-
|
|
3630
|
+
// `atris join --name <name>` joins as an agent; `atris join <code>` accepts an invite.
|
|
3631
|
+
const joinArgs = process.argv.slice(3);
|
|
3632
|
+
if (require('../commands/join').isAgentJoin(joinArgs)) {
|
|
3633
|
+
Promise.resolve().then(() => require('../commands/join').joinAgentCommand(joinArgs))
|
|
3634
|
+
.then((code) => exitAfterStdoutDrain(typeof code === 'number' ? code : 0))
|
|
3635
|
+
.catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
|
|
3636
|
+
} else {
|
|
3637
|
+
require('../commands/social').joinCommand()
|
|
3638
|
+
.then(() => exitAfterStdoutDrain(0))
|
|
3639
|
+
.catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
|
|
3640
|
+
}
|
|
3641
|
+
} else if (command === 'night') {
|
|
3642
|
+
Promise.resolve().then(() => require('../commands/night').nightCommand(process.argv.slice(3)))
|
|
3643
|
+
.then((code) => exitAfterStdoutDrain(typeof code === 'number' ? code : 0))
|
|
3644
|
+
.catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
|
|
3645
|
+
} else if (command === 'morning') {
|
|
3646
|
+
Promise.resolve().then(() => require('../commands/morning').morningCommand(process.argv.slice(3)))
|
|
3647
|
+
.then((code) => exitAfterStdoutDrain(typeof code === 'number' ? code : 0))
|
|
3603
3648
|
.catch((err) => { console.error(`\n✗ Error: ${err.message || err}`); process.exit(1); });
|
|
3604
3649
|
} else if (command === 'invite') {
|
|
3605
3650
|
require('../commands/social').inviteCommand()
|
|
@@ -3780,8 +3825,14 @@ async function agentAtris() {
|
|
|
3780
3825
|
const firstArg = process.argv[3];
|
|
3781
3826
|
if (firstArg === '-h' || firstArg === '--help' || firstArg === 'help') {
|
|
3782
3827
|
console.log('Usage: atris agent [doctor|spawn|spawns|spawn-status]');
|
|
3828
|
+
console.log(' atris agent [pull|push|snapshot] and atris agent spawn <parent> --name <name> --budget <credits>');
|
|
3829
|
+
console.log(' atris agent signin <agent> [--key-file <path>] [--json]');
|
|
3783
3830
|
console.log('');
|
|
3784
3831
|
console.log(' Pick which cloud agent to chat with from this workspace.');
|
|
3832
|
+
console.log(' Run `atris agent pull <agent> --dir <folder>` to copy an agent\'s home here; any engine started there becomes it.');
|
|
3833
|
+
console.log(' Run `atris agent push` inside that folder to send changes back, `atris agent snapshot` to save a version.');
|
|
3834
|
+
console.log(' Run `atris agent spawn <parent> --name <name> --budget <credits>` to make a child agent; its key goes to ~/.atris/agents.');
|
|
3835
|
+
console.log(' Run `atris agent signin <agent> [--key-file <path>]` to sign in with the agent\'s own key; prints only a token.');
|
|
3785
3836
|
console.log(' Run `atris agent spawn <role> --task "..."` to create a worker request.');
|
|
3786
3837
|
console.log(' Run `atris agent spawns` to list worker requests.');
|
|
3787
3838
|
console.log(' Run `atris agent doctor` to verify local AI CLIs can see Atris context.');
|
package/commands/activate.js
CHANGED
|
@@ -5,6 +5,7 @@ const { getLogPath, ensureLogDirectory, createLogFile } = require('../lib/journa
|
|
|
5
5
|
const { detectWorkspaceState, loadContext } = require('../lib/state-detection');
|
|
6
6
|
const { readWikiStatus } = require('../lib/wiki');
|
|
7
7
|
const { gateForHuman, numberWord } = require('../lib/voice-gate');
|
|
8
|
+
const { plainTitle } = require('../lib/first-minute');
|
|
8
9
|
|
|
9
10
|
const CLARITY_FIELDS = [
|
|
10
11
|
{ key: 'focus', label: 'Focus' },
|
|
@@ -120,7 +121,7 @@ function activeWorkSentence(state, context) {
|
|
|
120
121
|
const tasks = Array.isArray(context?.inProgressTasks) ? context.inProgressTasks : [];
|
|
121
122
|
if (tasks.length) {
|
|
122
123
|
const count = tasks.length;
|
|
123
|
-
const focus = focusFragment(tasks[0]);
|
|
124
|
+
const focus = focusFragment(plainTitle(tasks[0]));
|
|
124
125
|
const subject = count === 1 ? 'task is' : 'tasks are';
|
|
125
126
|
return humanSentence(`right now: ${countWord(count)} ${subject} in progress; the focus is ${closeSentence(focus)}`);
|
|
126
127
|
}
|
|
@@ -152,8 +153,7 @@ function activeWorkSentence(state, context) {
|
|
|
152
153
|
return null;
|
|
153
154
|
}
|
|
154
155
|
|
|
155
|
-
function
|
|
156
|
-
const workspaceDir = process.cwd();
|
|
156
|
+
function buildActivationSummary(workspaceDir) {
|
|
157
157
|
const targetDir = path.join(workspaceDir, 'atris');
|
|
158
158
|
|
|
159
159
|
if (!fs.existsSync(targetDir)) {
|
|
@@ -166,12 +166,7 @@ function activateAtris() {
|
|
|
166
166
|
const todoFile = path.join(targetDir, 'TODO.md');
|
|
167
167
|
const legacyTaskContextsFile = path.join(targetDir, 'TASK_CONTEXTS.md');
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
ensureLogDirectory();
|
|
171
|
-
const { logFile, dateFormatted } = getLogPath();
|
|
172
|
-
if (!fs.existsSync(logFile)) {
|
|
173
|
-
createLogFile(logFile, dateFormatted);
|
|
174
|
-
}
|
|
169
|
+
const logFile = path.join(workspaceDir, 'atris', 'logs', path.relative(path.join(process.cwd(), 'atris', 'logs'), getLogPath().logFile));
|
|
175
170
|
|
|
176
171
|
const state = detectWorkspaceState(workspaceDir);
|
|
177
172
|
const context = loadContext(workspaceDir);
|
|
@@ -247,7 +242,7 @@ function activateAtris() {
|
|
|
247
242
|
try {
|
|
248
243
|
const { nextMoves } = require('../lib/next-moves');
|
|
249
244
|
const { profilePaths } = require('../lib/clarity');
|
|
250
|
-
const root =
|
|
245
|
+
const root = workspaceDir;
|
|
251
246
|
moves = nextMoves(root, 3);
|
|
252
247
|
const { profile, mdPath } = readActivationClarityProfile(root, profilePaths);
|
|
253
248
|
clarityLine = formatClarityLine(profile, rel(mdPath));
|
|
@@ -258,19 +253,6 @@ function activateAtris() {
|
|
|
258
253
|
const { buildBriefData } = require('./brief');
|
|
259
254
|
briefData = buildBriefData(workspaceDir);
|
|
260
255
|
} catch { /* brief is best-effort */ }
|
|
261
|
-
try {
|
|
262
|
-
const { syncStatus } = require('../lib/sync-status');
|
|
263
|
-
syncStatus(workspaceDir);
|
|
264
|
-
} catch { /* sync status is best-effort */ }
|
|
265
|
-
|
|
266
|
-
void dateFormatted;
|
|
267
|
-
void learningCount;
|
|
268
|
-
fs.existsSync(personaFile);
|
|
269
|
-
fs.existsSync(mapFile);
|
|
270
|
-
void taskFilePath;
|
|
271
|
-
|
|
272
|
-
console.log(humanSentence('atris is up.'));
|
|
273
|
-
|
|
274
256
|
const statusLines = [
|
|
275
257
|
handoffSentence(handoffContent),
|
|
276
258
|
completionSentence(recentCompletions),
|
|
@@ -286,20 +268,44 @@ function activateAtris() {
|
|
|
286
268
|
statusLines.push(`waiting on you: ${countWord(approvalCount)} ${approvalLabel}. see them: atris task reviews`);
|
|
287
269
|
}
|
|
288
270
|
}
|
|
289
|
-
|
|
271
|
+
const nextMove = moves[0] || briefData?.moves?.[0];
|
|
272
|
+
const nextMoveTitle = focusFragment(plainTitle(nextMove?.title), 100);
|
|
273
|
+
const today = nextMoveTitle ? humanSentence(`today's move: ${closeSentence(nextMoveTitle)}`) : null;
|
|
274
|
+
return {
|
|
275
|
+
lines: statusLines,
|
|
276
|
+
note: statusLines.find(line => line.startsWith('last session left a note:')) || null,
|
|
277
|
+
landed: statusLines.find(line => line.startsWith('since last time:')) || null,
|
|
278
|
+
right_now: statusLines.find(line => line.startsWith('right now:')) || null,
|
|
279
|
+
waiting: statusLines.find(line => line.startsWith('waiting on you:') || line === 'nothing is waiting on you.') || null,
|
|
280
|
+
today, clarityLine,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function activateAtris() {
|
|
285
|
+
const workspaceDir = process.cwd();
|
|
286
|
+
if (!fs.existsSync(path.join(workspaceDir, 'atris'))) {
|
|
287
|
+
console.log('✗ atris/ folder not found. Run "atris init" first.');
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
ensureLogDirectory();
|
|
291
|
+
const { logFile, dateFormatted } = getLogPath();
|
|
292
|
+
if (!fs.existsSync(logFile)) createLogFile(logFile, dateFormatted);
|
|
293
|
+
const summary = buildActivationSummary(workspaceDir);
|
|
294
|
+
try {
|
|
295
|
+
const { syncStatus } = require('../lib/sync-status');
|
|
296
|
+
syncStatus(workspaceDir);
|
|
297
|
+
} catch { /* sync status is best-effort */ }
|
|
298
|
+
console.log(humanSentence('atris is up.'));
|
|
299
|
+
if (summary.lines.length) {
|
|
290
300
|
console.log('');
|
|
291
|
-
|
|
301
|
+
summary.lines.forEach((line) => console.log(line));
|
|
292
302
|
}
|
|
293
|
-
|
|
294
303
|
console.log('');
|
|
295
|
-
console.log(clarityLine || '
|
|
296
|
-
|
|
297
|
-
const nextMove = moves[0] || briefData?.moves?.[0];
|
|
298
|
-
const nextMoveTitle = focusFragment(nextMove?.title, 100);
|
|
299
|
-
if (nextMoveTitle) {
|
|
304
|
+
console.log(summary.clarityLine || 'tip: run atris clarity once so agents learn how you work.');
|
|
305
|
+
if (summary.today) {
|
|
300
306
|
console.log('');
|
|
301
|
-
console.log(
|
|
307
|
+
console.log(summary.today);
|
|
302
308
|
}
|
|
303
309
|
}
|
|
304
310
|
|
|
305
|
-
module.exports = { activateAtris, wikiHealthSentence };
|
|
311
|
+
module.exports = { activateAtris, wikiHealthSentence, buildActivationSummary };
|