atris 3.58.7 → 3.58.8
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/GETTING_STARTED.md +2 -2
- package/PERSONA.md +11 -11
- package/README.md +1 -1
- package/atris/GEMINI.md +7 -6
- package/atris/PERSONA.md +4 -4
- package/atris/features/README.md +77 -6
- package/atris/skills/README.md +44 -15
- package/atris/skills/aeo/SKILL.md +10 -10
- package/atris/skills/atris/SKILL.md +1 -1
- package/atris/skills/engines/SKILL.md +42 -50
- package/atris/skills/meta/SKILL.md +4 -4
- package/bin/atris.js +5 -6
- package/commands/aeo.js +9 -12
- package/commands/analytics.js +8 -11
- package/commands/autopilot.js +4 -3
- package/commands/avail.js +1 -1
- package/commands/brain.js +3 -1
- package/commands/business.js +13 -8
- package/commands/close.js +2 -1
- package/commands/computer.js +15 -10
- package/commands/context-sync.js +3 -3
- package/commands/doc-health.js +50 -2
- package/commands/experiments.js +13 -4
- package/commands/improve.js +1 -1
- package/commands/integrations.js +2 -5
- package/commands/learn.js +1 -1
- package/commands/member.js +174 -47
- package/commands/mission.js +57 -11
- package/commands/pack.js +2 -2
- package/commands/probe.js +11 -5
- package/commands/pull.js +1 -1
- package/commands/push.js +1 -1
- package/commands/rainmaker.js +3 -16
- package/commands/spaceship.js +1 -1
- package/commands/stream.js +27 -2
- package/commands/study.js +15 -9
- package/commands/sync.js +1 -1
- package/commands/task.js +13 -2
- package/commands/workflow.js +6 -1
- package/lib/codex-flight.js +28 -5
- package/lib/fleet.js +4 -1
- package/lib/improve-vitals-html.js +3 -2
- package/lib/lesson-contradiction.js +2 -2
- package/lib/map-refs.js +183 -0
- package/lib/mission-room.js +4 -4
- package/lib/noninteractive.js +15 -4
- package/lib/policy-lessons.js +1 -1
- package/lib/receipt-block.js +137 -28
- package/lib/runner-command.js +1 -1
- package/lib/scorecard.js +4 -4
- package/lib/sleep-sync.js +11 -0
- package/lib/state-detection.js +28 -9
- package/lib/task-db.js +3 -2
- package/lib/todo-fallback.js +1 -1
- package/lib/wish-delegate.js +19 -5
- package/mcp/atris-mcp/index.mjs +77 -24
- package/package.json +2 -5
- package/scripts/det/det.js +2 -2
- package/scripts/det/pr-description.js +2 -2
- package/scripts/det/test.js +2 -2
- package/utils/api.js +1 -1
- package/utils/auth.js +17 -7
- package/utils/backend-root.js +31 -0
- package/utils/config.js +5 -4
- package/utils/owner-identity.js +61 -0
- package/atris/policies/LESSONS.md +0 -13
- package/atris/skills/autopilot/atris-autopilot.md +0 -71
package/bin/atris.js
CHANGED
|
@@ -47,7 +47,7 @@ const {
|
|
|
47
47
|
requireAccountBound,
|
|
48
48
|
refuseAccountGlobal,
|
|
49
49
|
} = require('../lib/account-bound');
|
|
50
|
-
const { isHelpToken, argsWantHelp, hasYesFlag } = require('../lib/noninteractive');
|
|
50
|
+
const { isHelpToken, argsWantHelp, hasYesFlag, envForcesNonInteractive } = require('../lib/noninteractive');
|
|
51
51
|
|
|
52
52
|
// State detection for smart default
|
|
53
53
|
const { detectWorkspaceState, loadContext } = require('../lib/state-detection');
|
|
@@ -577,7 +577,7 @@ function showHelpAll() {
|
|
|
577
577
|
console.log(' watch - Turn one sentence into an always-on background watcher');
|
|
578
578
|
console.log(' ctop - Show a process-first live agent CPU/memory view');
|
|
579
579
|
console.log(' doctor - Node/task/auth/workspace readiness (--json for agents)');
|
|
580
|
-
console.log(' doc-health - workspace document size, navigation, and
|
|
580
|
+
console.log(' doc-health - workspace document size, navigation, freshness, and map line refs (--json, --fix-refs)');
|
|
581
581
|
console.log(' launchpad - Show the next action from local brain, task, mission, and proof state');
|
|
582
582
|
console.log(' brief - Show the one-glance operator brief');
|
|
583
583
|
console.log(' status - See local work and completions (`atris status <business>` for remote)');
|
|
@@ -1299,8 +1299,7 @@ function useInteractiveAtrisUi() {
|
|
|
1299
1299
|
return Boolean(
|
|
1300
1300
|
process.stdin.isTTY
|
|
1301
1301
|
&& process.stdout.isTTY
|
|
1302
|
-
&& !
|
|
1303
|
-
&& process.env.ATRIS_NONINTERACTIVE !== '1'
|
|
1302
|
+
&& !envForcesNonInteractive()
|
|
1304
1303
|
);
|
|
1305
1304
|
}
|
|
1306
1305
|
|
|
@@ -1464,7 +1463,7 @@ async function interactiveEntry(userInput, options = {}) {
|
|
|
1464
1463
|
: (context.inProgressFeatures || []).length;
|
|
1465
1464
|
|
|
1466
1465
|
// Pull active missions (durable goals), these outrank dev-pipeline state
|
|
1467
|
-
// because a mission with an unverified verifier is
|
|
1466
|
+
// because a mission with an unverified verifier is an owner commitment
|
|
1468
1467
|
// commitment that hasn't been closed yet.
|
|
1469
1468
|
const activeMissions = loadActiveMissions(workspaceDir);
|
|
1470
1469
|
const liveMissionsCount = activeMissions.length;
|
|
@@ -2959,7 +2958,7 @@ if (command === 'guide') {
|
|
|
2959
2958
|
const subcommand = process.argv[3];
|
|
2960
2959
|
const args = process.argv.slice(4);
|
|
2961
2960
|
Promise.resolve(require('../commands/member').memberCommand(subcommand, ...args))
|
|
2962
|
-
.then(() => process.exit(0))
|
|
2961
|
+
.then(() => process.exit(subcommand === 'run' ? (process.exitCode || 0) : 0))
|
|
2963
2962
|
.catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
|
|
2964
2963
|
} else if (command === 'app') {
|
|
2965
2964
|
const subcommand = process.argv[3];
|
package/commands/aeo.js
CHANGED
|
@@ -15,32 +15,29 @@
|
|
|
15
15
|
* atris aeo discover <source> [...] # discovery audit
|
|
16
16
|
* atris aeo audit <source> [...] # agent-usability audit
|
|
17
17
|
*
|
|
18
|
-
* Local workspace resolution: $ATRIS_AEO_ROOT or
|
|
18
|
+
* Local workspace resolution: $ATRIS_AEO_ROOT or the configured backend root.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
const fs = require('fs');
|
|
22
22
|
const os = require('os');
|
|
23
23
|
const path = require('path');
|
|
24
24
|
const { spawnSync } = require('child_process');
|
|
25
|
+
const { BACKEND_ROOT_HINT, resolveBackendRoot: resolveConfiguredBackendRoot } = require('../utils/backend-root');
|
|
25
26
|
const { loadCredentials, abortOnAuthFailure } = require('../utils/auth');
|
|
26
27
|
const { apiRequestJson } = require('../utils/api');
|
|
27
28
|
const { loadBusinesses, saveBusinesses } = require('./business');
|
|
28
29
|
|
|
29
|
-
function
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
for (const root of candidates) {
|
|
35
|
-
if (fs.existsSync(path.join(root, 'atris', 'features', 'aeo'))) return root;
|
|
36
|
-
}
|
|
37
|
-
return null;
|
|
30
|
+
function resolveAeoRoot() {
|
|
31
|
+
const explicit = process.env.ATRIS_AEO_ROOT;
|
|
32
|
+
if (explicit && fs.existsSync(path.join(explicit, 'atris', 'features', 'aeo'))) return explicit;
|
|
33
|
+
const backend = resolveConfiguredBackendRoot();
|
|
34
|
+
return backend && fs.existsSync(path.join(backend, 'atris', 'features', 'aeo')) ? backend : null;
|
|
38
35
|
}
|
|
39
36
|
|
|
40
37
|
function requireBackendRoot() {
|
|
41
|
-
const root =
|
|
38
|
+
const root = resolveAeoRoot();
|
|
42
39
|
if (!root) {
|
|
43
|
-
console.error(
|
|
40
|
+
console.error(`cannot find local aeo workspace; ${BACKEND_ROOT_HINT}`);
|
|
44
41
|
process.exit(1);
|
|
45
42
|
}
|
|
46
43
|
return root;
|
package/commands/analytics.js
CHANGED
|
@@ -68,17 +68,14 @@ function analyticsAtris() {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// Parse timestamps for productivity hours. Match the journal heading
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
hourCounts[hour] = (hourCounts[hour] || 0) + 1;
|
|
80
|
-
}
|
|
81
|
-
});
|
|
71
|
+
// formats `### Title -- HH:MM`, `### Title - HH:MM`, and legacy `**HH:MM:SS**`.
|
|
72
|
+
// Only heading lines count, so a prose range like "10:00 - 12:30" is not a timestamp.
|
|
73
|
+
const timestampPattern = /^#{2,4} .*(?:\u2014|--| - )\s*(\d{2}):\d{2}(?::\d{2})?\s*$|\*\*(\d{2}):\d{2}(?::\d{2})?\*\*/gm;
|
|
74
|
+
for (const match of content.matchAll(timestampPattern)) {
|
|
75
|
+
const hour = parseInt(match[1] || match[2], 10);
|
|
76
|
+
if (Number.isFinite(hour) && hour >= 0 && hour < 24) {
|
|
77
|
+
hourCounts[hour] = (hourCounts[hour] || 0) + 1;
|
|
78
|
+
}
|
|
82
79
|
}
|
|
83
80
|
});
|
|
84
81
|
|
package/commands/autopilot.js
CHANGED
|
@@ -25,14 +25,15 @@ const {
|
|
|
25
25
|
detectEndgameCompletion
|
|
26
26
|
} = require('../lib/scorecard');
|
|
27
27
|
const { REWARD_CONFIG, REWARD_CHECKSUM } = require('../lib/reward-config');
|
|
28
|
+
const { ownerIdentity } = require('../utils/owner-identity');
|
|
28
29
|
|
|
29
30
|
const pkg = require('../package.json');
|
|
30
31
|
|
|
31
32
|
const PHASE_TIMEOUT = 600000; // 10 min per phase
|
|
32
33
|
|
|
33
34
|
function looksOwnerClaimed(claimed) {
|
|
34
|
-
const text = String(claimed || '').toLowerCase();
|
|
35
|
-
return /\
|
|
35
|
+
const text = String(claimed || '').trim().toLowerCase();
|
|
36
|
+
return /\b(owner|human|operator)\b/.test(text) || ownerIdentity().names.includes(text.split(/\s+/)[0]);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
function looksOwnerGatedTitle(title) {
|
|
@@ -640,7 +641,7 @@ function getContextFiles(phase, options = {}) {
|
|
|
640
641
|
// checkout is forbidden (COORDINATION.md Rule 4). Interpolated into the
|
|
641
642
|
// default and self-heal do prompts, never the benchmark prompt (it never
|
|
642
643
|
// commits).
|
|
643
|
-
const SHARED_CHECKOUT_GIT_CONTRACT = `- Shared-checkout git safety (COORDINATION.md Rule 4): edits to any repo OTHER than this tick's cwd (
|
|
644
|
+
const SHARED_CHECKOUT_GIT_CONTRACT = `- Shared-checkout git safety (COORDINATION.md Rule 4): edits to any repo OTHER than this tick's cwd (including sibling repos) go through a per-tick worktree, start with \`atris worktree start --member <member> --task "<task>"\`, land with \`atris worktree ship --message "<msg>" --verify "<cmd>"\`. Never edit a sibling repo's shared checkout directly.
|
|
644
645
|
- On a shared checkout, \`git reset\`, \`git checkout --\`, \`git clean\`, and stashing other agents' work are FORBIDDEN, concurrent ticks' uncommitted work lives there.`;
|
|
645
646
|
|
|
646
647
|
/**
|
package/commands/avail.js
CHANGED
|
@@ -308,7 +308,7 @@ async function slotsAvail(argv) {
|
|
|
308
308
|
if (!username) {
|
|
309
309
|
const identity = await apiRequestJson('/identity/me', { method: 'GET', token });
|
|
310
310
|
if (!identity.ok || !identity.data?.username) {
|
|
311
|
-
console.error('
|
|
311
|
+
console.error('could not resolve username; pass --user <username>.');
|
|
312
312
|
return 1;
|
|
313
313
|
}
|
|
314
314
|
username = identity.data.username;
|
package/commands/brain.js
CHANGED
|
@@ -3,6 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const { spawnSync } = require('child_process');
|
|
5
5
|
const { refreshNowFile } = require('./now');
|
|
6
|
+
const { ownerIdentity } = require('../utils/owner-identity');
|
|
6
7
|
const escapeRegExp = require('../lib/escape-regexp');
|
|
7
8
|
const { hasRenderedSections, isOpenSection, isDoneSection } = require('../lib/todo-sections');
|
|
8
9
|
const { isNonInteractive } = require('../lib/noninteractive');
|
|
@@ -794,7 +795,8 @@ function memberNextMove(member, state = null) {
|
|
|
794
795
|
if (member.slug === 'justin' || /justin/i.test(member.name || '')) {
|
|
795
796
|
return `${name}: run one customer-moving GTM rep, update the relevant workspace state within 10 minutes, and leave a scorecard.`;
|
|
796
797
|
}
|
|
797
|
-
|
|
798
|
+
const ownerNames = ownerIdentity().names;
|
|
799
|
+
if (ownerNames.includes(String(member.slug || '').toLowerCase()) || ownerNames.includes(String(member.name || '').toLowerCase())) {
|
|
798
800
|
return `${name}: act as Customer 0: choose one allocation, narrative, system, or talent leverage move; leave proof and route the next owner.`;
|
|
799
801
|
}
|
|
800
802
|
if (/opus|overnight/i.test(identity)) {
|
package/commands/business.js
CHANGED
|
@@ -13,6 +13,10 @@ const {
|
|
|
13
13
|
renderDryRun,
|
|
14
14
|
} = require('../lib/business-simulate');
|
|
15
15
|
|
|
16
|
+
function businessWorkspaceBase() {
|
|
17
|
+
return process.env.ATRIS_BUSINESS_ROOT || path.join(os.homedir(), 'arena', 'atris-business');
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
function getBusinessConfigPath() {
|
|
17
21
|
const home = require('os').homedir();
|
|
18
22
|
const dir = path.join(home, '.atris');
|
|
@@ -179,7 +183,7 @@ function buildBusinessCacheEntry(business, localSlug, existing = {}) {
|
|
|
179
183
|
return entry;
|
|
180
184
|
}
|
|
181
185
|
|
|
182
|
-
function readBusinessFolderBindings(rootDir =
|
|
186
|
+
function readBusinessFolderBindings(rootDir = businessWorkspaceBase()) {
|
|
183
187
|
const skip = new Set([
|
|
184
188
|
'.git', '.DS_Store', 'archive', 'archives', '_archive', 'bench', 'deals',
|
|
185
189
|
'node_modules', 'shelf', '_shelf', 'templates',
|
|
@@ -510,7 +514,7 @@ function resolveWorkspaceRoot(slug, options = {}) {
|
|
|
510
514
|
if (options.noLocal) return null;
|
|
511
515
|
if (options.here) return options.cwd || process.cwd();
|
|
512
516
|
if (options.root) return path.join(options.root, slug);
|
|
513
|
-
return path.join(
|
|
517
|
+
return path.join(businessWorkspaceBase(), slug);
|
|
514
518
|
}
|
|
515
519
|
|
|
516
520
|
function createCanonicalBusinessWorkspace(targetRoot, bizMeta, options = {}) {
|
|
@@ -1851,7 +1855,7 @@ async function addBusiness(slug) {
|
|
|
1851
1855
|
}
|
|
1852
1856
|
|
|
1853
1857
|
async function listBusinesses(opts = {}) {
|
|
1854
|
-
// --local mode: walk
|
|
1858
|
+
// --local mode: walk the configured business workspace folder
|
|
1855
1859
|
// (no API calls, rate-limit safe). Different from API-mode below which lists
|
|
1856
1860
|
// businesses cached from the API.
|
|
1857
1861
|
if (opts.local) {
|
|
@@ -1877,7 +1881,7 @@ async function listBusinesses(opts = {}) {
|
|
|
1877
1881
|
}
|
|
1878
1882
|
|
|
1879
1883
|
/**
|
|
1880
|
-
* Walk
|
|
1884
|
+
* Walk the configured business folder and print a fleet status table for every
|
|
1881
1885
|
* customer workspace. Pure local, no API calls, no rate-limit risk.
|
|
1882
1886
|
*
|
|
1883
1887
|
* Classifies each dir as: ready, flat, unbound, nested, bare, or superseded.
|
|
@@ -1891,7 +1895,7 @@ function listBusinessesLocal(opts = {}) {
|
|
|
1891
1895
|
const SKIP_DIRS = new Set(['deals', 'archive', 'archives', '_archive', 'templates', 'node_modules', '.git']);
|
|
1892
1896
|
const SKIP_FILES = new Set(['.DS_Store', 'Thumbs.db']);
|
|
1893
1897
|
|
|
1894
|
-
const rootDir = opts.root ||
|
|
1898
|
+
const rootDir = opts.root || businessWorkspaceBase();
|
|
1895
1899
|
const jsonMode = opts.json === true;
|
|
1896
1900
|
|
|
1897
1901
|
if (!fs.existsSync(rootDir)) {
|
|
@@ -2609,7 +2613,7 @@ function parseBusinessDoctorOptions(args = []) {
|
|
|
2609
2613
|
const options = {
|
|
2610
2614
|
fix: args.includes('--fix'),
|
|
2611
2615
|
json: args.includes('--json'),
|
|
2612
|
-
root:
|
|
2616
|
+
root: businessWorkspaceBase(),
|
|
2613
2617
|
};
|
|
2614
2618
|
const rootIdx = args.indexOf('--root');
|
|
2615
2619
|
if (rootIdx !== -1 && args[rootIdx + 1]) {
|
|
@@ -2623,7 +2627,7 @@ function printBusinessDoctorHelp() {
|
|
|
2623
2627
|
console.log('');
|
|
2624
2628
|
console.log('Checks cloud-active businesses against:');
|
|
2625
2629
|
console.log(' - ~/.atris/businesses.json');
|
|
2626
|
-
console.log(
|
|
2630
|
+
console.log(` - ${businessWorkspaceBase()}/*/.atris/business.json`);
|
|
2627
2631
|
console.log(' - canonical slug + alias bindings');
|
|
2628
2632
|
console.log('');
|
|
2629
2633
|
console.log('--fix rewrites only safe local cache entries. It does not rename folders or touch cloud data.');
|
|
@@ -3454,7 +3458,7 @@ async function quickstart() {
|
|
|
3454
3458
|
atris business init "My Company" --template saas
|
|
3455
3459
|
|
|
3456
3460
|
2. Open the local workspace:
|
|
3457
|
-
cd
|
|
3461
|
+
cd ${businessWorkspaceBase()}/my-company
|
|
3458
3462
|
|
|
3459
3463
|
3. See what Atris knows:
|
|
3460
3464
|
atris
|
|
@@ -3675,6 +3679,7 @@ async function businessCommand(subcommand, ...args) {
|
|
|
3675
3679
|
|
|
3676
3680
|
module.exports = {
|
|
3677
3681
|
businessCommand,
|
|
3682
|
+
businessWorkspaceBase,
|
|
3678
3683
|
loadBusinesses,
|
|
3679
3684
|
saveBusinesses,
|
|
3680
3685
|
businessMatchesSlug,
|
package/commands/close.js
CHANGED
|
@@ -5,6 +5,7 @@ const { spawnSync } = require('child_process');
|
|
|
5
5
|
const { knownCommands } = require('../lib/known-commands');
|
|
6
6
|
const { readUsage, usagePath } = require('../lib/usage');
|
|
7
7
|
const pulse = require('../lib/pulse');
|
|
8
|
+
const { isOwner } = require('../utils/owner-identity');
|
|
8
9
|
const { parseLessons } = require('./autopilot');
|
|
9
10
|
|
|
10
11
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
@@ -240,7 +241,7 @@ function parkedFlags(cwd = process.cwd(), options = {}) {
|
|
|
240
241
|
}
|
|
241
242
|
|
|
242
243
|
function ownerWaitRank(flag) {
|
|
243
|
-
return
|
|
244
|
+
return isOwner(flag.owner) ? 0 : 1;
|
|
244
245
|
}
|
|
245
246
|
|
|
246
247
|
function compareOpenFlags(a, b) {
|
package/commands/computer.js
CHANGED
|
@@ -23,8 +23,9 @@ const path = require('path');
|
|
|
23
23
|
const readline = require('readline');
|
|
24
24
|
const { spawnSync } = require('child_process');
|
|
25
25
|
const { loadCredentials, decodeJwtClaims, promptUser, abortOnAuthFailure } = require('../utils/auth');
|
|
26
|
+
const { resolveBackendRoot } = require('../utils/backend-root');
|
|
26
27
|
const { apiRequestJson, getApiBaseUrl, getAppBaseUrl } = require('../utils/api');
|
|
27
|
-
const { loadBusinesses, saveBusinesses } = require('./business');
|
|
28
|
+
const { businessWorkspaceBase, loadBusinesses, saveBusinesses } = require('./business');
|
|
28
29
|
const {
|
|
29
30
|
engineLoginSeatError,
|
|
30
31
|
formatEngineSeats,
|
|
@@ -37,6 +38,7 @@ const {
|
|
|
37
38
|
const { consoleCommand, gatherAtrisContext, buildSystemPrompt } = require('./console');
|
|
38
39
|
const { streamSession } = require('./serve');
|
|
39
40
|
const { buildRemoteAtrisBootstrapCommand } = require('../lib/runtime-bootstrap');
|
|
41
|
+
const { envForcesNonInteractive } = require('../lib/noninteractive');
|
|
40
42
|
|
|
41
43
|
function getToken() {
|
|
42
44
|
const creds = loadCredentials();
|
|
@@ -270,11 +272,11 @@ const ui = {
|
|
|
270
272
|
};
|
|
271
273
|
|
|
272
274
|
function useInteractiveCloudUi() {
|
|
273
|
-
return Boolean(process.stdin.isTTY && process.stdout.isTTY && !
|
|
275
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY && !envForcesNonInteractive());
|
|
274
276
|
}
|
|
275
277
|
|
|
276
278
|
function useInteractiveTerminalUi() {
|
|
277
|
-
return Boolean(process.stdin.isTTY && process.stdout.isTTY && !
|
|
279
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY && !envForcesNonInteractive());
|
|
278
280
|
}
|
|
279
281
|
|
|
280
282
|
async function readPipedStdin() {
|
|
@@ -517,7 +519,7 @@ function displayHomeRelativePath(targetPath) {
|
|
|
517
519
|
}
|
|
518
520
|
|
|
519
521
|
function recruitingBusinessWorkspacePath(slug = RECRUITING_BUSINESS_SLUG) {
|
|
520
|
-
const root =
|
|
522
|
+
const root = businessWorkspaceBase();
|
|
521
523
|
return path.join(root, slug);
|
|
522
524
|
}
|
|
523
525
|
|
|
@@ -1371,11 +1373,12 @@ function printModeBanner(mode, root, lines = []) {
|
|
|
1371
1373
|
|
|
1372
1374
|
function findAtrisCodeTerminal() {
|
|
1373
1375
|
const envPath = process.env.ATRIS_CODE_PY;
|
|
1376
|
+
const backend = resolveBackendRoot();
|
|
1374
1377
|
const candidates = [
|
|
1375
1378
|
envPath,
|
|
1376
1379
|
path.join(__dirname, '..', 'cli', 'atris_code.py'),
|
|
1377
1380
|
path.join(process.cwd(), 'cli', 'atris_code.py'),
|
|
1378
|
-
path.join(
|
|
1381
|
+
...(backend ? [path.join(backend, 'cli', 'atris_code.py')] : []),
|
|
1379
1382
|
].filter(Boolean);
|
|
1380
1383
|
|
|
1381
1384
|
let dir = process.cwd();
|
|
@@ -2534,7 +2537,7 @@ async function computerCreate(token, args = [], defaults = {}) {
|
|
|
2534
2537
|
console.log(` atris computer --business ${owner} --workspace ${workspaceId}`);
|
|
2535
2538
|
console.log('');
|
|
2536
2539
|
console.log('Org workspace:');
|
|
2537
|
-
console.log(` cd
|
|
2540
|
+
console.log(` cd ${businessWorkspaceBase()}/${owner}`);
|
|
2538
2541
|
console.log(' atris member activate operator');
|
|
2539
2542
|
console.log(' atris member activate validator');
|
|
2540
2543
|
console.log('');
|
|
@@ -3500,7 +3503,7 @@ async function sendBusinessChat(token, ctx, message, sessionId, resetContext = f
|
|
|
3500
3503
|
async function computerChat(token, ctx, initialOptions = {}) {
|
|
3501
3504
|
if (!ctx) {
|
|
3502
3505
|
console.error('Cloud computer mode requires a bound business workspace.');
|
|
3503
|
-
console.error('
|
|
3506
|
+
console.error('run this inside a bound business workspace, or use `atris computer --local` for local mode.');
|
|
3504
3507
|
return;
|
|
3505
3508
|
}
|
|
3506
3509
|
|
|
@@ -3741,7 +3744,7 @@ async function computerChat(token, ctx, initialOptions = {}) {
|
|
|
3741
3744
|
async function computerLocalAtris(token, ctx, initialOptions = {}) {
|
|
3742
3745
|
if (!ctx) {
|
|
3743
3746
|
console.error('Atris local mode needs a bound business workspace for the cloud brain.');
|
|
3744
|
-
console.error('
|
|
3747
|
+
console.error('run inside a bound business workspace, or use `atris computer local-byo`.');
|
|
3745
3748
|
return;
|
|
3746
3749
|
}
|
|
3747
3750
|
|
|
@@ -3976,7 +3979,7 @@ async function computerLocalAtris(token, ctx, initialOptions = {}) {
|
|
|
3976
3979
|
async function computerProof(token, ctx, initialOptions = {}) {
|
|
3977
3980
|
if (!ctx) {
|
|
3978
3981
|
console.error('Atris computer proof needs a bound business workspace.');
|
|
3979
|
-
console.error('
|
|
3982
|
+
console.error('run inside a bound business workspace first.');
|
|
3980
3983
|
process.exitCode = 1;
|
|
3981
3984
|
return;
|
|
3982
3985
|
}
|
|
@@ -4290,7 +4293,7 @@ async function runComputer(argv = process.argv.slice(3), deps = {}) {
|
|
|
4290
4293
|
console.log('A business can be a company, lab, collective, community, artist, team, or project.');
|
|
4291
4294
|
console.log('');
|
|
4292
4295
|
console.log('First use:');
|
|
4293
|
-
console.log(
|
|
4296
|
+
console.log(` cd ${businessWorkspaceBase()}/<business>`);
|
|
4294
4297
|
console.log(' atris computer');
|
|
4295
4298
|
console.log(' Choose Cloud workspace or Local folder, then type the outcome in plain English.');
|
|
4296
4299
|
console.log('');
|
|
@@ -4470,6 +4473,8 @@ module.exports = {
|
|
|
4470
4473
|
extractAttachedWorkspaceMismatch,
|
|
4471
4474
|
contextForAttachedWorkspaceMismatch,
|
|
4472
4475
|
printRecruitingLocalSyncOutcome,
|
|
4476
|
+
useInteractiveCloudUi,
|
|
4477
|
+
useInteractiveTerminalUi,
|
|
4473
4478
|
// Hermetic parsing/formatting layer, exported for test/computer.test.js.
|
|
4474
4479
|
parseComputerOptions,
|
|
4475
4480
|
parseComputerCreateArgs,
|
package/commands/context-sync.js
CHANGED
|
@@ -64,7 +64,7 @@ async function businessStatus(slug) {
|
|
|
64
64
|
const timeSince = manifest ? _timeSince(manifest.last_sync) : null;
|
|
65
65
|
|
|
66
66
|
console.log('');
|
|
67
|
-
console.log(`${biz.name}` + (timeSince ?
|
|
67
|
+
console.log(`${biz.name}` + (timeSince ? `: last synced ${timeSince}` : ': never synced'));
|
|
68
68
|
|
|
69
69
|
// Determine local directory
|
|
70
70
|
let localDir = null;
|
|
@@ -254,11 +254,11 @@ async function businessLog(slug) {
|
|
|
254
254
|
|
|
255
255
|
const commits = result.data.commits || [];
|
|
256
256
|
if (commits.length === 0) {
|
|
257
|
-
console.log(`\n ${biz.name}
|
|
257
|
+
console.log(`\n ${biz.name}: no history yet.\n`);
|
|
258
258
|
return;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
-
console.log(`\n ${biz.name}
|
|
261
|
+
console.log(`\n ${biz.name}: history\n`);
|
|
262
262
|
|
|
263
263
|
for (const commit of commits) {
|
|
264
264
|
const date = _timeSince(commit.date) || commit.date;
|
package/commands/doc-health.js
CHANGED
|
@@ -4,6 +4,7 @@ const fs = require('fs');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { resolveWorkspaceRoot } = require('../lib/mission-root');
|
|
6
6
|
const { readText } = require('./brain');
|
|
7
|
+
const { checkMapRefs, fixMapRefs } = require('../lib/map-refs');
|
|
7
8
|
|
|
8
9
|
const BOOT_FILES = [
|
|
9
10
|
'CLAUDE.md', 'AGENTS.md', 'atris/atris.md', 'atris/MAP.md',
|
|
@@ -74,9 +75,17 @@ function collectMap(root, text, featureNames, memberNames) {
|
|
|
74
75
|
}
|
|
75
76
|
const files = [...paths].map(file => ({ path: file, exists: fs.existsSync(path.resolve(root, file)) }));
|
|
76
77
|
const existing = files.filter(file => file.exists).length;
|
|
78
|
+
const pathScore = files.length ? existing / files.length : 0;
|
|
79
|
+
const { refs: all, ...refs } = checkMapRefs(root, text);
|
|
80
|
+
refs.flagged = all.filter(ref => ref.status !== 'ok').map(ref => ({
|
|
81
|
+
map_line: ref.map_line, ref: ref.raw, symbol: ref.symbol, status: ref.status,
|
|
82
|
+
...(ref.line ? { line: ref.line } : {}), ...(ref.lines ? { lines: ref.lines } : {}),
|
|
83
|
+
...(ref.reason ? { reason: ref.reason } : {}),
|
|
84
|
+
}));
|
|
77
85
|
return {
|
|
78
86
|
rows, paths: files.length, existing, files,
|
|
79
|
-
|
|
87
|
+
// A path that exists but a line ref that points at the wrong code is still a wrong map.
|
|
88
|
+
path_score: pathScore, refs, score: pathScore * refs.score,
|
|
80
89
|
features: folderCoverage(text, 'atris/features', featureNames),
|
|
81
90
|
members: folderCoverage(text, 'atris/team', memberNames),
|
|
82
91
|
};
|
|
@@ -272,6 +281,34 @@ function table(headers, rows) {
|
|
|
272
281
|
return cells.map(row => row.map((cell, i) => cell.padEnd(widths[i])).join(' ').trimEnd());
|
|
273
282
|
}
|
|
274
283
|
|
|
284
|
+
function refsLine(refs) {
|
|
285
|
+
const gone = refs.missing + refs.missing_file;
|
|
286
|
+
return `map refs: ${refs.ok} right, ${refs.moved} moved, ${refs.ambiguous} unclear, ${gone} gone`;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function describeLeft(ref) {
|
|
290
|
+
const where = `MAP line ${ref.map_line}: ${ref.raw}${ref.symbol ? ` (${ref.symbol})` : ''}`;
|
|
291
|
+
if (ref.status === 'ambiguous') return `${where} found on lines ${ref.lines.join(', ')}`;
|
|
292
|
+
if (ref.status === 'missing-file') return `${where} file is gone`;
|
|
293
|
+
return `${where} ${ref.reason}`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function fixRefsCommand(root, asJson) {
|
|
297
|
+
const { changes, left } = fixMapRefs(root);
|
|
298
|
+
if (asJson) {
|
|
299
|
+
console.log(JSON.stringify({ ok: true, action: 'doc-health fix-refs', changes, left }, null, 2));
|
|
300
|
+
return 0;
|
|
301
|
+
}
|
|
302
|
+
const lines = [changes.length ? `map refs: moved ${changes.length}` : 'map refs: nothing to move.'];
|
|
303
|
+
for (const change of changes) lines.push(` MAP line ${change.map_line}: ${change.from} -> ${change.to} (${change.symbol})`);
|
|
304
|
+
if (left.length) {
|
|
305
|
+
lines.push(`left for a human: ${left.length}`);
|
|
306
|
+
for (const ref of left) lines.push(` ${describeLeft(ref)}`);
|
|
307
|
+
}
|
|
308
|
+
console.log(lines.join('\n'));
|
|
309
|
+
return 0;
|
|
310
|
+
}
|
|
311
|
+
|
|
275
312
|
function renderDocHealth(payload) {
|
|
276
313
|
if (!payload.ok) return payload.message;
|
|
277
314
|
const { boot_load: boot, map_coverage: map, lookup_hops: lookup, staleness, overall } = payload;
|
|
@@ -286,6 +323,8 @@ function renderDocHealth(payload) {
|
|
|
286
323
|
['routing rows', map.rows, map.rows], ['existing paths', map.existing, map.paths],
|
|
287
324
|
['features mentioned', map.features.mentioned, map.features.total], ['members mentioned', map.members.mentioned, map.members.total],
|
|
288
325
|
]),
|
|
326
|
+
refsLine(map.refs),
|
|
327
|
+
...(map.refs.moved ? ['run atris doc-health --fix-refs to update the moved ones.'] : []),
|
|
289
328
|
'', 'lookup hops',
|
|
290
329
|
];
|
|
291
330
|
if (lookup.missing) {
|
|
@@ -315,9 +354,18 @@ function renderDocHealth(payload) {
|
|
|
315
354
|
|
|
316
355
|
function docHealthCommand(args = [], options = {}) {
|
|
317
356
|
if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
|
|
318
|
-
console.log('usage: atris doc-health [--json] [--questions <path>]');
|
|
357
|
+
console.log('usage: atris doc-health [--json] [--questions <path>] [--fix-refs]');
|
|
358
|
+
console.log(' --fix-refs rewrite map line refs whose code moved; list the ones a human must fix');
|
|
319
359
|
return 0;
|
|
320
360
|
}
|
|
361
|
+
if (args.includes('--fix-refs')) {
|
|
362
|
+
const root = resolveWorkspaceRoot(options.cwd || process.cwd());
|
|
363
|
+
if (!stat(path.join(root, 'atris', 'MAP.md'))?.isFile()) {
|
|
364
|
+
console.log('no atris/MAP.md in this workspace.');
|
|
365
|
+
return 1;
|
|
366
|
+
}
|
|
367
|
+
return fixRefsCommand(root, args.includes('--json'));
|
|
368
|
+
}
|
|
321
369
|
const index = args.indexOf('--questions');
|
|
322
370
|
const questions = index >= 0 && args[index + 1] && !args[index + 1].startsWith('--') ? args[index + 1]
|
|
323
371
|
: args.find(arg => arg.startsWith('--questions='))?.slice('--questions='.length);
|
package/commands/experiments.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { spawnSync } = require('child_process');
|
|
4
|
+
const { BACKEND_ROOT_HINT, resolveBackendRoot } = require('../utils/backend-root');
|
|
4
5
|
const { runTaskOnce } = require('./autopilot');
|
|
5
6
|
const {
|
|
6
7
|
appendResultsRow,
|
|
@@ -391,7 +392,7 @@ function parseRunOptions(args) {
|
|
|
391
392
|
const options = {
|
|
392
393
|
dryRun: false,
|
|
393
394
|
verbose: false,
|
|
394
|
-
backendPath:
|
|
395
|
+
backendPath: null,
|
|
395
396
|
interventions: [],
|
|
396
397
|
};
|
|
397
398
|
|
|
@@ -527,8 +528,10 @@ function buildBenchmarkArtifact(name, packDir, options) {
|
|
|
527
528
|
'atris/features/endstate/contract.md',
|
|
528
529
|
'atris/features/endstate/artifact-schema.json',
|
|
529
530
|
path.relative(cwd, path.join(packDir, 'program.md')),
|
|
530
|
-
|
|
531
|
-
|
|
531
|
+
...(backendPath ? [
|
|
532
|
+
path.relative(cwd, path.join(backendPath, 'atris', 'MAP.md')),
|
|
533
|
+
path.relative(cwd, path.join(backendPath, 'atris', 'TODO.md')),
|
|
534
|
+
] : []),
|
|
532
535
|
],
|
|
533
536
|
contextNote: [
|
|
534
537
|
`Project Endstate track: ${track}`,
|
|
@@ -555,7 +558,7 @@ function buildBenchmarkArtifact(name, packDir, options) {
|
|
|
555
558
|
? []
|
|
556
559
|
: [
|
|
557
560
|
...collectChangedFiles(cwd, beforeCli, afterCli),
|
|
558
|
-
...collectChangedFiles(backendPath, beforeBackend, afterBackend,
|
|
561
|
+
...(backendPath ? collectChangedFiles(backendPath, beforeBackend, afterBackend, `${path.relative(cwd, backendPath)}/`) : []),
|
|
559
562
|
];
|
|
560
563
|
const wikiAfter = readTextIfExists(path.join(cwd, 'atris', 'wiki', 'STATUS.md'));
|
|
561
564
|
|
|
@@ -637,6 +640,12 @@ function experimentsRun(name, ...args) {
|
|
|
637
640
|
return;
|
|
638
641
|
}
|
|
639
642
|
|
|
643
|
+
if (!options.backendPath) options.backendPath = resolveBackendRoot();
|
|
644
|
+
if (!options.backendPath) {
|
|
645
|
+
console.error(BACKEND_ROOT_HINT);
|
|
646
|
+
process.exit(1);
|
|
647
|
+
}
|
|
648
|
+
|
|
640
649
|
const schemaPath = path.join(process.cwd(), 'atris', 'features', 'endstate', 'artifact-schema.json');
|
|
641
650
|
const contractPath = path.join(process.cwd(), 'atris', 'features', 'endstate', 'contract.md');
|
|
642
651
|
if (!fs.existsSync(schemaPath) || !fs.existsSync(contractPath)) {
|
package/commands/improve.js
CHANGED
|
@@ -37,7 +37,7 @@ const rsi = require('../lib/rsi-record');
|
|
|
37
37
|
* writes. The path sent to the backend is left untouched, a remote tick
|
|
38
38
|
* may target a server-side `~/...` workspace the server expands itself, but
|
|
39
39
|
* local scorecard/journal writes must never create a literal `~` directory.
|
|
40
|
-
* (Surfaced by a live plan tick whose
|
|
40
|
+
* (Surfaced by a live plan tick whose home-relative path wrote junk locally.)
|
|
41
41
|
*/
|
|
42
42
|
function expandHome(p) {
|
|
43
43
|
if (typeof p === 'string' && (p === '~' || p.startsWith('~/'))) {
|
package/commands/integrations.js
CHANGED
|
@@ -35,6 +35,7 @@ const os = require('os');
|
|
|
35
35
|
const path = require('path');
|
|
36
36
|
const readline = require('readline');
|
|
37
37
|
const { spawnSync } = require('child_process');
|
|
38
|
+
const { sleepSync } = require('../lib/sleep-sync');
|
|
38
39
|
|
|
39
40
|
const CALENDAR_CACHE_PATH = path.join(os.homedir(), '.atris', 'calendar-events-cache.json');
|
|
40
41
|
const GMAIL_CONNECT_POLL_MS = 3000;
|
|
@@ -1812,10 +1813,6 @@ function writeImessageLookupCache(cache) {
|
|
|
1812
1813
|
return cachePath;
|
|
1813
1814
|
}
|
|
1814
1815
|
|
|
1815
|
-
function sleepMs(ms) {
|
|
1816
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
1816
|
function readLatestOutgoingImessage(handle, sinceMs) {
|
|
1820
1817
|
const chatDb = path.join(os.homedir(), 'Library', 'Messages', 'chat.db');
|
|
1821
1818
|
const sinceUnix = Math.max(0, Math.floor(Number(sinceMs || Date.now()) / 1000) - 5);
|
|
@@ -1873,7 +1870,7 @@ function imessageVerifyLatestOutgoing(handle, sinceMs, options = {}) {
|
|
|
1873
1870
|
&& !(latest.is_sent || latest.is_delivered || latest.is_finished)
|
|
1874
1871
|
&& Date.now() - startedAt < timeoutMs
|
|
1875
1872
|
) {
|
|
1876
|
-
|
|
1873
|
+
sleepSync(intervalMs);
|
|
1877
1874
|
latest = readLatestOutgoingImessage(handle, sinceMs);
|
|
1878
1875
|
}
|
|
1879
1876
|
return {
|
package/commands/learn.js
CHANGED
|
@@ -460,7 +460,7 @@ function harvestFromJournals(deps = {}) {
|
|
|
460
460
|
const lines = notesMatch[1].trim().split('\n').filter(l => l.startsWith('- '));
|
|
461
461
|
for (const line of lines) {
|
|
462
462
|
// Strip bullet and optional timestamp prefix
|
|
463
|
-
const insight = line.replace(/^- (\d{2}:\d{2}
|
|
463
|
+
const insight = line.replace(/^- (\d{2}:\d{2} [-\u2014] )?/, '').trim();
|
|
464
464
|
if (leftoverClaimableInsight(insight)) continue;
|
|
465
465
|
if (insight.length > 10) {
|
|
466
466
|
candidates.push({ insight, source: path.basename(logPath) });
|