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.
Files changed (67) hide show
  1. package/GETTING_STARTED.md +2 -2
  2. package/PERSONA.md +11 -11
  3. package/README.md +1 -1
  4. package/atris/GEMINI.md +7 -6
  5. package/atris/PERSONA.md +4 -4
  6. package/atris/features/README.md +77 -6
  7. package/atris/skills/README.md +44 -15
  8. package/atris/skills/aeo/SKILL.md +10 -10
  9. package/atris/skills/atris/SKILL.md +1 -1
  10. package/atris/skills/engines/SKILL.md +42 -50
  11. package/atris/skills/meta/SKILL.md +4 -4
  12. package/bin/atris.js +5 -6
  13. package/commands/aeo.js +9 -12
  14. package/commands/analytics.js +8 -11
  15. package/commands/autopilot.js +4 -3
  16. package/commands/avail.js +1 -1
  17. package/commands/brain.js +3 -1
  18. package/commands/business.js +13 -8
  19. package/commands/close.js +2 -1
  20. package/commands/computer.js +15 -10
  21. package/commands/context-sync.js +3 -3
  22. package/commands/doc-health.js +50 -2
  23. package/commands/experiments.js +13 -4
  24. package/commands/improve.js +1 -1
  25. package/commands/integrations.js +2 -5
  26. package/commands/learn.js +1 -1
  27. package/commands/member.js +174 -47
  28. package/commands/mission.js +57 -11
  29. package/commands/pack.js +2 -2
  30. package/commands/probe.js +11 -5
  31. package/commands/pull.js +1 -1
  32. package/commands/push.js +1 -1
  33. package/commands/rainmaker.js +3 -16
  34. package/commands/spaceship.js +1 -1
  35. package/commands/stream.js +27 -2
  36. package/commands/study.js +15 -9
  37. package/commands/sync.js +1 -1
  38. package/commands/task.js +13 -2
  39. package/commands/workflow.js +6 -1
  40. package/lib/codex-flight.js +28 -5
  41. package/lib/fleet.js +4 -1
  42. package/lib/improve-vitals-html.js +3 -2
  43. package/lib/lesson-contradiction.js +2 -2
  44. package/lib/map-refs.js +183 -0
  45. package/lib/mission-room.js +4 -4
  46. package/lib/noninteractive.js +15 -4
  47. package/lib/policy-lessons.js +1 -1
  48. package/lib/receipt-block.js +137 -28
  49. package/lib/runner-command.js +1 -1
  50. package/lib/scorecard.js +4 -4
  51. package/lib/sleep-sync.js +11 -0
  52. package/lib/state-detection.js +28 -9
  53. package/lib/task-db.js +3 -2
  54. package/lib/todo-fallback.js +1 -1
  55. package/lib/wish-delegate.js +19 -5
  56. package/mcp/atris-mcp/index.mjs +77 -24
  57. package/package.json +2 -5
  58. package/scripts/det/det.js +2 -2
  59. package/scripts/det/pr-description.js +2 -2
  60. package/scripts/det/test.js +2 -2
  61. package/utils/api.js +1 -1
  62. package/utils/auth.js +17 -7
  63. package/utils/backend-root.js +31 -0
  64. package/utils/config.js +5 -4
  65. package/utils/owner-identity.js +61 -0
  66. package/atris/policies/LESSONS.md +0 -13
  67. package/atris/skills/autopilot/atris-autopilot.md +0 -71
package/utils/auth.js CHANGED
@@ -156,6 +156,20 @@ function mkPrivateDir(dir) {
156
156
  return dir;
157
157
  }
158
158
 
159
+ // Credential files hold login tokens. If they cannot be limited to the owner,
160
+ // say so once per file instead of leaving them readable in silence.
161
+ const permissionWarned = new Set();
162
+ function restrictToOwner(filePath) {
163
+ try {
164
+ fs.chmodSync(filePath, 0o600);
165
+ } catch (err) {
166
+ if (permissionWarned.has(filePath)) return;
167
+ permissionWarned.add(filePath);
168
+ const reason = err && err.code ? ` (${err.code})` : '';
169
+ console.error(`atris: could not limit ${filePath} to your user${reason}. other users on this machine may be able to read it.`);
170
+ }
171
+ }
172
+
159
173
  function getAtrisDir() {
160
174
  const homeDir = os.homedir();
161
175
  return mkPrivateDir(path.join(homeDir, '.atris'));
@@ -349,7 +363,7 @@ function getProfilesDir() {
349
363
 
350
364
  function profileNameFromEmail(email) {
351
365
  if (!email) return null;
352
- // "keshav@atrislabs.com" → "keshav"
366
+ // "alex@example.com" becomes "alex".
353
367
  const name = email.split('@')[0].toLowerCase().replace(/[^a-z0-9_-]/g, '-');
354
368
  return name || null; // Guard against emails like "@domain.com"
355
369
  }
@@ -357,7 +371,7 @@ function profileNameFromEmail(email) {
357
371
  function saveProfile(name, credentials) {
358
372
  const profilePath = path.join(getProfilesDir(), `${name}.json`);
359
373
  fs.writeFileSync(profilePath, JSON.stringify(credentials, null, 2));
360
- try { fs.chmodSync(profilePath, 0o600); } catch {}
374
+ restrictToOwner(profilePath);
361
375
  }
362
376
 
363
377
  function loadProfile(name) {
@@ -413,11 +427,7 @@ function saveCredentials(token, refreshToken, email, userId, provider, extras =
413
427
  };
414
428
 
415
429
  fs.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2));
416
- try {
417
- fs.chmodSync(credentialsPath, 0o600);
418
- } catch {
419
- // Best effort: permissions may be unsupported on this platform.
420
- }
430
+ restrictToOwner(credentialsPath);
421
431
 
422
432
  // Auto-save as named profile
423
433
  autoSaveProfile(credentials);
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { resolveWorkspaceRoot } = require('../lib/mission-root');
6
+ const { loadConfig } = require('./config');
7
+
8
+ const BACKEND_ROOT_HINT = 'set ATRIS_BACKEND_ROOT to the backend workspace.';
9
+
10
+ function resolveBackendRoot(cwd = process.cwd()) {
11
+ const workspace = resolveWorkspaceRoot(cwd);
12
+ // ATRIS_BACKEND_DIR is the former spelling, accepted only here.
13
+ const environment = process.env.ATRIS_BACKEND_ROOT || process.env.ATRIS_BACKEND_DIR;
14
+ if (environment) {
15
+ const candidate = path.resolve(environment);
16
+ return fs.existsSync(candidate) && fs.statSync(candidate).isDirectory() ? candidate : null;
17
+ }
18
+
19
+ const config = loadConfig(workspace);
20
+ if (typeof config?.backend_root === 'string' && config.backend_root.trim()) {
21
+ const candidate = path.resolve(workspace, config.backend_root);
22
+ return fs.existsSync(candidate) && fs.statSync(candidate).isDirectory() ? candidate : null;
23
+ }
24
+
25
+ const sibling = path.resolve(workspace, '..', 'atrisos-backend');
26
+ if (fs.existsSync(sibling) && fs.statSync(sibling).isDirectory()) return sibling;
27
+ const installedSibling = path.resolve(__dirname, '..', '..', 'atrisos-backend');
28
+ return fs.existsSync(installedSibling) && fs.statSync(installedSibling).isDirectory() ? installedSibling : null;
29
+ }
30
+
31
+ module.exports = { BACKEND_ROOT_HINT, resolveBackendRoot };
package/utils/config.js CHANGED
@@ -5,17 +5,18 @@ const path = require('path');
5
5
  * Get the path to atris/.config in the current project.
6
6
  * @returns {string} Config file path
7
7
  */
8
- function getConfigPath() {
9
- const targetDir = path.join(process.cwd(), 'atris');
8
+ function getConfigPath(root = process.cwd()) {
9
+ const targetDir = path.join(root, 'atris');
10
10
  return path.join(targetDir, '.config');
11
11
  }
12
12
 
13
13
  /**
14
14
  * Load config from atris/.config, returning empty object if missing/invalid.
15
+ * backend_root may name a backend folder relative to the workspace root.
15
16
  * @returns {Object} Config object
16
17
  */
17
- function loadConfig() {
18
- const configPath = getConfigPath();
18
+ function loadConfig(root = process.cwd()) {
19
+ const configPath = getConfigPath(root);
19
20
 
20
21
  if (!fs.existsSync(configPath)) {
21
22
  return {};
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ const os = require('os');
4
+ const { spawnSync } = require('child_process');
5
+ const { loadCredentials, profileNameFromEmail } = require('./auth');
6
+
7
+ let cachedIdentity = null;
8
+
9
+ function localUsername() {
10
+ try {
11
+ return os.userInfo().username;
12
+ } catch {
13
+ return process.env.USER || process.env.LOGNAME || null;
14
+ }
15
+ }
16
+
17
+ function gitUserName() {
18
+ const result = spawnSync('git', ['config', 'user.name'], { encoding: 'utf8' });
19
+ return result.status === 0 ? String(result.stdout || '').trim() : null;
20
+ }
21
+
22
+ function buildOwnerIdentity(credentials, username) {
23
+ const inheritedNames = String(process.env.ATRIS_OWNER_NAMES || '').split(',').map((name) => name.trim()).filter(Boolean);
24
+ const emailName = profileNameFromEmail(credentials?.email);
25
+ const configured = process.env.ATRIS_OPERATOR;
26
+ const fullName = credentials?.name || credentials?.full_name || credentials?.user_metadata?.full_name;
27
+ const loggedIn = Boolean(emailName || credentials?.username || credentials?.user_id || fullName);
28
+ const gitName = loggedIn ? null : gitUserName();
29
+ const gitFirstName = gitName ? gitName.split(/\s+/)[0] : null;
30
+ // Keep an existing local claim alias when it begins with the signed-in account name.
31
+ const localAlias = username && emailName?.length >= 4 && String(username).toLowerCase().startsWith(emailName)
32
+ ? username : null;
33
+ const values = [
34
+ ...inheritedNames, emailName, configured, credentials?.username, credentials?.user_id, fullName,
35
+ fullName?.replace(/\s+/g, ''), localAlias, gitName, gitName?.replace(/\s+/g, ''),
36
+ gitFirstName, loggedIn ? null : username,
37
+ ]
38
+ .filter(Boolean)
39
+ .map((value) => String(value).toLowerCase());
40
+ const slug = inheritedNames[0] || emailName || configured || gitFirstName || (loggedIn ? null : username);
41
+ return { slug: slug ? String(slug).toLowerCase() : null, names: values };
42
+ }
43
+
44
+ function ownerIdentity(credentials, username) {
45
+ if (arguments.length > 0) return buildOwnerIdentity(credentials, username === undefined ? localUsername() : username);
46
+ if (!cachedIdentity) cachedIdentity = buildOwnerIdentity(loadCredentials(), localUsername());
47
+ return cachedIdentity;
48
+ }
49
+
50
+ function resetOwnerIdentityCache() {
51
+ cachedIdentity = null;
52
+ }
53
+
54
+ function isOwner(value) {
55
+ const candidate = String(value || '').trim().toLowerCase();
56
+ if (['you', 'me', 'operator', 'human', 'owner'].includes(candidate)) return true;
57
+ const identity = ownerIdentity();
58
+ return identity.names.includes(candidate);
59
+ }
60
+
61
+ module.exports = { ownerIdentity, isOwner, resetOwnerIdentityCache };
@@ -1,13 +0,0 @@
1
- # LESSONS
2
-
3
- Purpose: Capture short, actionable lessons after REVIEW misses.
4
-
5
- ## Rules
6
- - Add only after REVIEW when there was a miss, mismatch, or new constraint.
7
- - One line per lesson.
8
- - Format: YYYY-MM-DD | area | issue -> fix
9
- - Promote repeated lessons (2-3 times) into the relevant policy, then delete.
10
-
11
- ## Entries
12
- - 2026-01-04 | meta | JSON edits break easily on quote boundaries → use Read tool to verify after Edit on .json files
13
- - 2026-01-28 | init | Meta-frameworks must be detected before base frameworks → Next.js check before React check (tests caught this)
@@ -1,71 +0,0 @@
1
- ---
2
- description: Start autonomous loop - plan/do/review until done
3
- arguments:
4
- - name: task
5
- description: What to build (e.g., "Add dark mode toggle")
6
- required: true
7
- - name: max-iterations
8
- description: Max loops before stopping (default 10)
9
- required: false
10
- ---
11
-
12
- # Atris Autopilot
13
-
14
- You are entering **autonomous mode**. You will loop until the task is complete or max iterations reached.
15
-
16
- ## Setup
17
-
18
- First, create the state file:
19
-
20
- ```bash
21
- mkdir -p .claude
22
- cat > .claude/atris-autopilot.state.md << 'EOF'
23
- ---
24
- iteration: 1
25
- max_iterations: $ARGUMENTS.max-iterations|default:10
26
- completion_promise: <promise>COMPLETE</promise>
27
- ---
28
-
29
- $ARGUMENTS.task
30
- EOF
31
- ```
32
-
33
- ## Your Task
34
-
35
- **$ARGUMENTS.task**
36
-
37
- ## Process
38
-
39
- Each iteration:
40
- 1. **PLAN** — Read MAP.md, identify files, create approach
41
- 2. **DO** — Implement ONE thing, commit changes
42
- 3. **REVIEW** — Check acceptance criteria
43
-
44
- ## Acceptance Criteria
45
-
46
- - Task implemented and working
47
- - Tests pass (if they exist)
48
- - Build passes
49
- - Code follows patterns in MAP.md
50
-
51
- ## Rules
52
-
53
- - ONE thing per iteration (trust the loop)
54
- - Search MAP.md before grepping
55
- - Search codebase before assuming not implemented
56
- - Update TODO.md with progress
57
- - Log learnings to atris/logs/
58
-
59
- ## Completion
60
-
61
- When ALL acceptance criteria pass, output exactly:
62
-
63
- ```
64
- <promise>COMPLETE</promise>
65
- ```
66
-
67
- This signals the stop hook to end the loop.
68
-
69
- ## Start
70
-
71
- Read atris/MAP.md now. Then begin iteration 1.