atris 3.34.0 → 3.36.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 +35 -0
- package/FOR_AGENTS.md +5 -3
- package/README.md +5 -3
- package/atris/GETTING_STARTED.md +1 -1
- package/atris/atris.md +3 -0
- package/atris/policies/day-loop-voice.md +102 -0
- package/atris/policies/outbound-artifact-gate.md +2 -0
- package/atris/skills/design/SKILL.md +56 -32
- package/atris/skills/endgame/SKILL.md +12 -6
- package/atris/skills/engines/SKILL.md +35 -8
- package/atris/skills/fable-method/SKILL.md +66 -0
- package/atris/skills/improve/SKILL.md +65 -45
- package/atris/skills/render-cli/SKILL.md +88 -0
- package/atris/skills/youtube/SKILL.md +10 -1
- package/atris.md +6 -2
- package/ax +617 -31
- package/bin/atris.js +719 -266
- package/commands/activate.js +194 -88
- package/commands/aeo.js +52 -0
- package/commands/agents.js +166 -0
- package/commands/autoland.js +718 -72
- package/commands/autopilot-front.js +20 -2
- package/commands/autopilot.js +118 -2
- package/commands/avail.js +407 -0
- package/commands/bench.js +188 -0
- package/commands/brain.js +3 -0
- package/commands/brief.js +651 -0
- package/commands/business-sync.js +192 -6
- package/commands/business.js +91 -8
- package/commands/clean.js +50 -24
- package/commands/close.js +1083 -0
- package/commands/cloud.js +245 -0
- package/commands/codex-goal.js +26 -2
- package/commands/compile.js +292 -1
- package/commands/computer.js +150 -3
- package/commands/dream.js +365 -0
- package/commands/drill.js +371 -0
- package/commands/drive.js +187 -0
- package/commands/engine.js +1061 -29
- package/commands/experiments.js +28 -0
- package/commands/feed.js +202 -0
- package/commands/feedback.js +34 -12
- package/commands/fleet-report.js +206 -0
- package/commands/github.js +38 -0
- package/commands/gm.js +285 -3
- package/commands/goal.js +247 -0
- package/commands/improve.js +642 -26
- package/commands/init.js +83 -43
- package/commands/integrations.js +39 -11
- package/commands/interview.js +209 -0
- package/commands/land.js +253 -52
- package/commands/lesson.js +112 -1
- package/commands/lifecycle.js +39 -3
- package/commands/linear.js +38 -0
- package/commands/log.js +84 -1
- package/commands/loops.js +220 -16
- package/commands/meet.js +220 -0
- package/commands/member.js +899 -66
- package/commands/mission.js +3512 -332
- package/commands/next.js +137 -0
- package/commands/now.js +240 -21
- package/commands/one-lap.js +776 -0
- package/commands/orb.js +314 -0
- package/commands/pack-craft.js +179 -0
- package/commands/pack.js +823 -0
- package/commands/play.js +3 -2
- package/commands/probe.js +30 -3
- package/commands/pulse.js +241 -46
- package/commands/push.js +260 -82
- package/commands/radar.js +259 -14
- package/commands/rainmaker.js +49 -0
- package/commands/report.js +415 -0
- package/commands/scout.js +147 -0
- package/commands/search.js +363 -0
- package/commands/serve.js +54 -0
- package/commands/skill.js +47 -3
- package/commands/slop.js +50 -2
- package/commands/soul.js +1 -1
- package/commands/status.js +50 -5
- package/commands/stream.js +861 -0
- package/commands/stripe.js +38 -0
- package/commands/study.js +693 -0
- package/commands/supabase.js +39 -0
- package/commands/sync.js +67 -54
- package/commands/task.js +2275 -182
- package/commands/team.js +73 -0
- package/commands/truth.js +29 -3
- package/commands/unknowns.js +627 -0
- package/commands/update.js +44 -0
- package/commands/vercel.js +38 -0
- package/commands/verify.js +96 -0
- package/commands/watch.js +303 -0
- package/commands/wish.js +500 -0
- package/commands/workflow.js +11 -5
- package/commands/worktree.js +299 -20
- package/commands/write.js +399 -0
- package/commands/xp.js +29 -11
- package/lib/auto-accept-certified.js +391 -43
- package/lib/autoland.js +353 -52
- package/lib/ax-auto-lane.js +79 -0
- package/lib/bench/context.js +147 -0
- package/lib/bench/engines.js +141 -0
- package/lib/bench/report.js +140 -0
- package/lib/bench/runner.js +512 -0
- package/lib/brief-ledger.js +350 -0
- package/lib/cloud-mission.js +259 -0
- package/lib/codex-flight.js +154 -0
- package/lib/default-runner.js +45 -0
- package/lib/default-verifier.js +70 -0
- package/lib/engine-registry.js +232 -0
- package/lib/experiments/daily.js +640 -0
- package/lib/fleet.js +2431 -38
- package/lib/improve-vitals-html.js +171 -0
- package/lib/known-commands.js +58 -0
- package/lib/loop-doctor.js +416 -0
- package/lib/member-switches.js +144 -0
- package/lib/memory-view.js +14 -5
- package/lib/mission-room.js +1 -0
- package/lib/mission-root.js +52 -0
- package/lib/mission-runtime-loop.js +7 -0
- package/lib/next-moves.js +327 -10
- package/lib/official-cli-integration.js +174 -0
- package/lib/one-lap-validator.js +60 -0
- package/lib/orb-context.js +477 -0
- package/lib/orb-scorecard.js +224 -0
- package/lib/outbound-send-gate.js +165 -0
- package/lib/permission-grants.js +293 -0
- package/lib/policy-lessons.js +52 -1
- package/lib/pulse.js +277 -3
- package/lib/receipt-block.js +168 -0
- package/lib/receipt-evidence.js +65 -4
- package/lib/review-integrity.js +147 -0
- package/lib/router-brain.js +352 -0
- package/lib/runner-command.js +33 -0
- package/lib/self-drive.js +258 -0
- package/lib/short-name.js +103 -0
- package/lib/spawn-env.js +18 -0
- package/lib/state-detection.js +56 -1
- package/lib/sync-status.js +59 -0
- package/lib/task-db.js +319 -27
- package/lib/task-proof.js +43 -1
- package/lib/task-receipt.js +93 -0
- package/lib/team-presence.js +260 -0
- package/lib/tool-result-encode.js +7 -0
- package/lib/trust-tiers.js +90 -0
- package/lib/usage.js +107 -0
- package/lib/voice-gate.js +163 -0
- package/lib/wish-audit.js +1368 -0
- package/lib/wish-delegate.js +1840 -0
- package/lib/wish-design.js +110 -0
- package/lib/wish-stats.js +183 -0
- package/lib/wish-store.js +354 -0
- package/lib/zip.js +221 -0
- package/package.json +3 -1
- package/templates/loops/atris/loops/LOOPS.md +55 -0
- package/templates/loops/atris/loops/TICK.md +24 -0
- package/templates/loops/atris/loops/feedback.md +22 -0
- package/templates/loops/atris/loops/quality.md +22 -0
- package/templates/loops/atris/wiki/systems/loops.md +41 -0
- package/utils/api.js +5 -1
- package/utils/auth.js +57 -21
- package/utils/update-check.js +27 -6
- package/atris/learnings.jsonl +0 -1
package/commands/lifecycle.js
CHANGED
|
@@ -3,6 +3,13 @@ const path = require('path');
|
|
|
3
3
|
const { loadCredentials } = require('../utils/auth');
|
|
4
4
|
const { apiRequestJson } = require('../utils/api');
|
|
5
5
|
const { loadBusinesses, saveBusinesses, businessMatchesSlug } = require('./business');
|
|
6
|
+
const {
|
|
7
|
+
isLocalMember,
|
|
8
|
+
memberArgFromArgv,
|
|
9
|
+
readLoopFlag,
|
|
10
|
+
setMemberAwake,
|
|
11
|
+
setMemberLoopAwake,
|
|
12
|
+
} = require('../lib/member-switches');
|
|
6
13
|
|
|
7
14
|
/**
|
|
8
15
|
* Resolve business slug from CLI arg or local .atris/business.json.
|
|
@@ -23,6 +30,29 @@ function resolveSlug() {
|
|
|
23
30
|
return slug;
|
|
24
31
|
}
|
|
25
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Per-member / per-loop switch path for atris sleep|wake <member> [--loop <id>].
|
|
35
|
+
* Returns true when handled so callers skip business-level lifecycle.
|
|
36
|
+
*/
|
|
37
|
+
function handleMemberSwitch(awake) {
|
|
38
|
+
const name = memberArgFromArgv(process.argv);
|
|
39
|
+
if (!name || name === '--help' || name === '-h' || name === 'help') return false;
|
|
40
|
+
if (!isLocalMember(name)) return false;
|
|
41
|
+
|
|
42
|
+
const loopId = readLoopFlag(process.argv);
|
|
43
|
+
if (loopId) {
|
|
44
|
+
setMemberLoopAwake(name, loopId, awake);
|
|
45
|
+
console.log(awake
|
|
46
|
+
? `${name} loop ${loopId} is awake`
|
|
47
|
+
: `${name} loop ${loopId} is asleep`);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setMemberAwake(name, awake);
|
|
52
|
+
console.log(awake ? `${name} is awake` : `${name} is asleep`);
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
|
|
26
56
|
async function resolveBusiness(token, slug) {
|
|
27
57
|
const businesses = loadBusinesses();
|
|
28
58
|
|
|
@@ -91,12 +121,15 @@ async function waitForBusinessComputer(token, business, maxWait = 90000) {
|
|
|
91
121
|
* @returns {Promise<void>}
|
|
92
122
|
*/
|
|
93
123
|
async function sleepAtris() {
|
|
124
|
+
if (handleMemberSwitch(false)) return;
|
|
125
|
+
|
|
94
126
|
const slug = resolveSlug();
|
|
95
127
|
|
|
96
128
|
if (!slug || slug === '--help' || slug === '-h' || slug === 'help') {
|
|
97
|
-
console.log('Usage: atris sleep [business]');
|
|
129
|
+
console.log('Usage: atris sleep [business|member] [--loop <loop-id>]');
|
|
98
130
|
console.log('');
|
|
99
131
|
console.log(' Pause a workspace to save compute. Storage only.');
|
|
132
|
+
console.log(' Or flip a local member / member-loop switch asleep.');
|
|
100
133
|
process.exit(0);
|
|
101
134
|
}
|
|
102
135
|
|
|
@@ -131,7 +164,7 @@ async function sleepAtris() {
|
|
|
131
164
|
}
|
|
132
165
|
|
|
133
166
|
console.log(`Workspace '${slug}' is now sleeping. Context saved. Wake it with: atris wake ${slug}`);
|
|
134
|
-
console.log('Compute paused. Storage only
|
|
167
|
+
console.log('Compute paused. Storage only - pennies/day.');
|
|
135
168
|
}
|
|
136
169
|
|
|
137
170
|
/**
|
|
@@ -139,12 +172,15 @@ async function sleepAtris() {
|
|
|
139
172
|
* @returns {Promise<void>}
|
|
140
173
|
*/
|
|
141
174
|
async function wakeAtris() {
|
|
175
|
+
if (handleMemberSwitch(true)) return;
|
|
176
|
+
|
|
142
177
|
const slug = resolveSlug();
|
|
143
178
|
|
|
144
179
|
if (!slug || slug === '--help' || slug === '-h' || slug === 'help') {
|
|
145
|
-
console.log('Usage: atris wake [business]');
|
|
180
|
+
console.log('Usage: atris wake [business|member] [--loop <loop-id>]');
|
|
146
181
|
console.log('');
|
|
147
182
|
console.log(' Wake a sleeping workspace. Agents resume automatically.');
|
|
183
|
+
console.log(' Or flip a local member / member-loop switch awake.');
|
|
148
184
|
process.exit(0);
|
|
149
185
|
}
|
|
150
186
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const { createOfficialCliCommand } = require('../lib/official-cli-integration');
|
|
2
|
+
|
|
3
|
+
const linearCommand = createOfficialCliCommand({
|
|
4
|
+
name: 'linear',
|
|
5
|
+
binary: 'linear',
|
|
6
|
+
versionArgs: ['--version'],
|
|
7
|
+
authArgs: ['auth', 'status'],
|
|
8
|
+
installHint: 'install the linear cli and ensure `linear` is on PATH',
|
|
9
|
+
loginHint: 'linear auth login',
|
|
10
|
+
commands: [
|
|
11
|
+
{
|
|
12
|
+
usage: 'issue list',
|
|
13
|
+
match: ['issue', 'list'],
|
|
14
|
+
forward: ['issue', 'list'],
|
|
15
|
+
description: 'list issues',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
usage: 'issue create',
|
|
19
|
+
match: ['issue', 'create'],
|
|
20
|
+
forward: ['issue', 'create'],
|
|
21
|
+
description: 'create an issue',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
usage: 'issue view',
|
|
25
|
+
match: ['issue', 'view'],
|
|
26
|
+
forward: ['issue', 'view'],
|
|
27
|
+
description: 'show issue details',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
usage: 'issue update',
|
|
31
|
+
match: ['issue', 'update'],
|
|
32
|
+
forward: ['issue', 'update'],
|
|
33
|
+
description: 'update an issue',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
module.exports = { linearCommand };
|
package/commands/log.js
CHANGED
|
@@ -52,4 +52,87 @@ function logAtris() {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
function localDateString(now = new Date()) {
|
|
56
|
+
const year = String(now.getFullYear());
|
|
57
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
58
|
+
const day = String(now.getDate()).padStart(2, '0');
|
|
59
|
+
return `${year}-${month}-${day}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function digestDate(args) {
|
|
63
|
+
const equalsArg = args.find((arg) => String(arg).startsWith('--date='));
|
|
64
|
+
const dateIndex = args.indexOf('--date');
|
|
65
|
+
const date = equalsArg
|
|
66
|
+
? equalsArg.slice('--date='.length)
|
|
67
|
+
: dateIndex >= 0
|
|
68
|
+
? args[dateIndex + 1]
|
|
69
|
+
: localDateString();
|
|
70
|
+
|
|
71
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(String(date || ''))) {
|
|
72
|
+
throw new Error('Usage: atris logs [today] [--date YYYY-MM-DD] [--json]');
|
|
73
|
+
}
|
|
74
|
+
return date;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function readDigestSection(root, title, relativePath, maxLines = null) {
|
|
78
|
+
const absolutePath = path.join(root, relativePath);
|
|
79
|
+
if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) return null;
|
|
80
|
+
|
|
81
|
+
const content = fs.readFileSync(absolutePath, 'utf8');
|
|
82
|
+
const lines = content.split(/\r?\n/);
|
|
83
|
+
if (lines[lines.length - 1] === '') lines.pop();
|
|
84
|
+
|
|
85
|
+
if (maxLines !== null && lines.length > maxLines) {
|
|
86
|
+
return {
|
|
87
|
+
title,
|
|
88
|
+
path: relativePath.split(path.sep).join('/'),
|
|
89
|
+
lines: [...lines.slice(0, maxLines), `truncated after ${maxLines} lines`],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
title,
|
|
95
|
+
path: relativePath.split(path.sep).join('/'),
|
|
96
|
+
lines,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function logsDigest(args = [], options = {}) {
|
|
101
|
+
const root = options.cwd || process.cwd();
|
|
102
|
+
const date = digestDate(args);
|
|
103
|
+
const sections = [];
|
|
104
|
+
const workspacePath = path.join('atris', 'logs', date.slice(0, 4), `${date}.md`);
|
|
105
|
+
const workspaceSection = readDigestSection(root, 'Workspace journal', workspacePath);
|
|
106
|
+
if (workspaceSection) sections.push(workspaceSection);
|
|
107
|
+
|
|
108
|
+
const teamRoot = path.join(root, 'atris', 'team');
|
|
109
|
+
if (fs.existsSync(teamRoot) && fs.statSync(teamRoot).isDirectory()) {
|
|
110
|
+
const members = fs.readdirSync(teamRoot, { withFileTypes: true })
|
|
111
|
+
.filter((entry) => entry.isDirectory())
|
|
112
|
+
.map((entry) => entry.name)
|
|
113
|
+
.sort((left, right) => left.localeCompare(right));
|
|
114
|
+
|
|
115
|
+
for (const member of members) {
|
|
116
|
+
const relativePath = path.join('atris', 'team', member, 'logs', `${date}.md`);
|
|
117
|
+
const section = readDigestSection(root, `Team: ${member}`, relativePath, 80);
|
|
118
|
+
if (section) sections.push(section);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const payload = { date, sections };
|
|
123
|
+
if (args.includes('--json')) {
|
|
124
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
125
|
+
} else if (sections.length === 0) {
|
|
126
|
+
console.log(`No logs for ${date} yet.`);
|
|
127
|
+
} else {
|
|
128
|
+
console.log(sections.map((section) => [
|
|
129
|
+
`=== ${section.title} ===`,
|
|
130
|
+
section.path,
|
|
131
|
+
...section.lines,
|
|
132
|
+
].join('\n')).join('\n\n'));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return payload;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
module.exports = { logAtris, logsDigest };
|
package/commands/loops.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* atris loops -
|
|
3
|
-
* what you can start or stop.
|
|
2
|
+
* atris loops - project loop audit/scaffold plus legacy background loop board.
|
|
4
3
|
*
|
|
5
|
-
* atris loops -
|
|
4
|
+
* atris loops - audit atris/loops in this project
|
|
5
|
+
* atris loops init - scaffold the loop system into this project
|
|
6
|
+
* atris self-improve - alias for atris loops init
|
|
7
|
+
* atris loops tick - print the loop tick protocol
|
|
8
|
+
* atris loops board - legacy board: registry jobs, launchd agents, mission
|
|
6
9
|
* atris loops stop <id> - disable a registry job or boot out a launchd agent
|
|
7
10
|
* atris loops start <id> - enable a registry job or kickstart a launchd agent
|
|
8
11
|
*/
|
|
@@ -12,6 +15,16 @@ const os = require('os');
|
|
|
12
15
|
const path = require('path');
|
|
13
16
|
const { execSync, spawnSync } = require('child_process');
|
|
14
17
|
|
|
18
|
+
const MAX_LOG_AGE_DAYS = 14;
|
|
19
|
+
const TEMPLATE_ROOT = path.join(__dirname, '..', 'templates', 'loops');
|
|
20
|
+
const SCAFFOLD_FILES = [
|
|
21
|
+
'atris/loops/LOOPS.md',
|
|
22
|
+
'atris/loops/TICK.md',
|
|
23
|
+
'atris/loops/feedback.md',
|
|
24
|
+
'atris/loops/quality.md',
|
|
25
|
+
'atris/wiki/systems/loops.md',
|
|
26
|
+
];
|
|
27
|
+
|
|
15
28
|
function heartbeatDir() {
|
|
16
29
|
return process.env.ATRIS_HEARTBEAT_DIR || path.join(os.homedir(), '.atris', 'heartbeat');
|
|
17
30
|
}
|
|
@@ -127,6 +140,7 @@ function showBoard(cwd) {
|
|
|
127
140
|
console.log('');
|
|
128
141
|
console.log('control: atris loops stop <id> · atris loops start <id>');
|
|
129
142
|
console.log('ids: registry job id, or a com.atris.* launchd label');
|
|
143
|
+
return 0;
|
|
130
144
|
}
|
|
131
145
|
|
|
132
146
|
function saveRegistry(registry) {
|
|
@@ -178,35 +192,225 @@ function controlLoop(id, action) {
|
|
|
178
192
|
process.exit(1);
|
|
179
193
|
}
|
|
180
194
|
console.log(`${action === 'stop' ? 'Stopped' : 'Started'} ${target}.`);
|
|
181
|
-
return;
|
|
195
|
+
return 0;
|
|
182
196
|
}
|
|
183
197
|
const ok = setRegistryJob(target, action === 'start');
|
|
184
198
|
if (!ok) {
|
|
185
|
-
console.error(`No registry job named "${target}". Run: atris loops`);
|
|
199
|
+
console.error(`No registry job named "${target}". Run: atris loops board`);
|
|
186
200
|
process.exit(1);
|
|
187
201
|
}
|
|
188
202
|
console.log(`${action === 'stop' ? 'Disabled' : 'Enabled'} ${target} in the heartbeat registry (backup written).`);
|
|
203
|
+
return 0;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function todayIso() {
|
|
207
|
+
return new Date().toISOString().slice(0, 10);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function renderTemplate(text) {
|
|
211
|
+
return text.split('{{DATE}}').join(todayIso());
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function initLoops(cwd = process.cwd()) {
|
|
215
|
+
const created = [];
|
|
216
|
+
const skipped = [];
|
|
217
|
+
|
|
218
|
+
for (const relPath of SCAFFOLD_FILES) {
|
|
219
|
+
const source = path.join(TEMPLATE_ROOT, relPath);
|
|
220
|
+
const target = path.join(cwd, relPath);
|
|
221
|
+
if (fs.existsSync(target)) {
|
|
222
|
+
skipped.push(relPath);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const content = renderTemplate(fs.readFileSync(source, 'utf8'));
|
|
226
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
227
|
+
fs.writeFileSync(target, content, 'utf8');
|
|
228
|
+
created.push(relPath);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
console.log('atris loops init');
|
|
232
|
+
console.log('');
|
|
233
|
+
console.log('created:');
|
|
234
|
+
if (created.length) created.forEach(file => console.log(` ${file}`));
|
|
235
|
+
else console.log(' none');
|
|
236
|
+
console.log('');
|
|
237
|
+
console.log('skipped:');
|
|
238
|
+
if (skipped.length) skipped.forEach(file => console.log(` ${file}`));
|
|
239
|
+
else console.log(' none');
|
|
240
|
+
console.log('');
|
|
241
|
+
console.log('Next: fill Owner/Wiki/Runner TODOs, then run atris loops audit');
|
|
242
|
+
return 0;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function stripAnchor(linkTarget) {
|
|
246
|
+
return String(linkTarget || '').split('#')[0];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function wikiTargetPath(loopFilePath, wikiLink) {
|
|
250
|
+
const cleaned = stripAnchor(wikiLink);
|
|
251
|
+
if (!cleaned || /^[a-z]+:\/\//i.test(cleaned)) return null;
|
|
252
|
+
return path.resolve(path.dirname(loopFilePath), cleaned);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function checkCommandFrom(text) {
|
|
256
|
+
return text.match(/\*\*Check:\*\*\s*`([^`]+)`/)?.[1]?.trim() || null;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function runLoopCheck(commandText, cwd) {
|
|
260
|
+
const result = spawnSync(commandText, {
|
|
261
|
+
cwd,
|
|
262
|
+
shell: true,
|
|
263
|
+
encoding: 'utf8',
|
|
264
|
+
timeout: 240000,
|
|
265
|
+
});
|
|
266
|
+
return {
|
|
267
|
+
command: commandText,
|
|
268
|
+
exitCode: typeof result.status === 'number' ? result.status : 1,
|
|
269
|
+
error: result.error ? result.error.message : null,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function structuralProblems({ root, filePath, text, nowMs }) {
|
|
274
|
+
const problems = [];
|
|
275
|
+
|
|
276
|
+
const owner = text.match(/\*\*Owner:\*\*\s*`team\/([^`]+)`/)?.[1]?.trim();
|
|
277
|
+
if (!owner) problems.push('no Owner field');
|
|
278
|
+
else if (!fs.existsSync(path.join(root, 'team', owner))) problems.push(`owner team/${owner} does not exist`);
|
|
279
|
+
|
|
280
|
+
const wiki = text.match(/\*\*Wiki:\*\*\s*\[[^\]]+\]\(([^)]+)\)/)?.[1]?.trim();
|
|
281
|
+
if (!wiki) {
|
|
282
|
+
problems.push('no Wiki field');
|
|
283
|
+
} else {
|
|
284
|
+
const wikiPath = wikiTargetPath(filePath, wiki);
|
|
285
|
+
if (!wikiPath || !fs.existsSync(wikiPath)) problems.push(`wiki page ${wiki} missing`);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (!/\*\*Runner:\*\*\s*\S/.test(text)) problems.push('no Runner field');
|
|
289
|
+
if (!/\*\*Protects:\*\*/.test(text)) problems.push('no Protects field');
|
|
290
|
+
if (!/\*\*Signal/.test(text)) problems.push('no Signal field');
|
|
291
|
+
if (!/\*\*Cadence:\*\*/.test(text)) problems.push('no Cadence field');
|
|
292
|
+
|
|
293
|
+
const logDates = [...text.matchAll(/^- (\d{4}-\d{2}-\d{2}):/gm)].map(match => match[1]).sort();
|
|
294
|
+
if (logDates.length === 0) {
|
|
295
|
+
problems.push('no dated Log entries');
|
|
296
|
+
} else {
|
|
297
|
+
const newest = logDates[logDates.length - 1];
|
|
298
|
+
const ageDays = (nowMs - Date.parse(`${newest}T00:00:00Z`)) / 86400000;
|
|
299
|
+
if (ageDays > MAX_LOG_AGE_DAYS) problems.push(`log stale: newest ${newest}`);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return { owner, problems };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function auditLoops(cwd = process.cwd()) {
|
|
306
|
+
const loopsDir = path.join(cwd, 'atris', 'loops');
|
|
307
|
+
if (!fs.existsSync(loopsDir)) {
|
|
308
|
+
console.log('No atris/loops directory found.');
|
|
309
|
+
console.log('Run: atris loops init');
|
|
310
|
+
console.log('');
|
|
311
|
+
console.log('SELF-IMPROVING: NOT YET — 1 open');
|
|
312
|
+
return 1;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const files = fs.readdirSync(loopsDir)
|
|
316
|
+
.filter(file => file.endsWith('.md') && file !== 'LOOPS.md' && file !== 'TICK.md')
|
|
317
|
+
.sort();
|
|
318
|
+
|
|
319
|
+
if (!files.length) {
|
|
320
|
+
console.log('No loop files found in atris/loops.');
|
|
321
|
+
console.log('Run: atris loops init');
|
|
322
|
+
console.log('');
|
|
323
|
+
console.log('SELF-IMPROVING: NOT YET — 1 open');
|
|
324
|
+
return 1;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
console.log('atris loops audit');
|
|
328
|
+
console.log('');
|
|
329
|
+
|
|
330
|
+
let open = 0;
|
|
331
|
+
const nowMs = Date.now();
|
|
332
|
+
for (const file of files) {
|
|
333
|
+
const filePath = path.join(loopsDir, file);
|
|
334
|
+
const text = fs.readFileSync(filePath, 'utf8');
|
|
335
|
+
const { problems } = structuralProblems({ root: cwd, filePath, text, nowMs });
|
|
336
|
+
const checkCommand = checkCommandFrom(text);
|
|
337
|
+
const check = checkCommand ? runLoopCheck(checkCommand, cwd) : null;
|
|
338
|
+
if (check && check.exitCode !== 0) problems.push(`check failed: exit ${check.exitCode}`);
|
|
339
|
+
|
|
340
|
+
if (problems.length) open += 1;
|
|
341
|
+
const loopName = file.replace(/\.md$/, '');
|
|
342
|
+
console.log(`${problems.length ? '❌' : '✅'} ${loopName}`);
|
|
343
|
+
for (const problem of problems) console.log(` ↳ ${problem}`);
|
|
344
|
+
if (check) {
|
|
345
|
+
const errorSuffix = check.error ? ` (${check.error})` : '';
|
|
346
|
+
console.log(` ↳ check: \`${check.command}\` exit ${check.exitCode}${errorSuffix}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
console.log('');
|
|
351
|
+
if (open === 0) {
|
|
352
|
+
console.log('SELF-IMPROVING: YES');
|
|
353
|
+
return 0;
|
|
354
|
+
}
|
|
355
|
+
console.log(`SELF-IMPROVING: NOT YET — ${open} open`);
|
|
356
|
+
return 1;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function tickLoops(cwd = process.cwd()) {
|
|
360
|
+
const localTick = path.join(cwd, 'atris', 'loops', 'TICK.md');
|
|
361
|
+
const packagedTick = path.join(TEMPLATE_ROOT, 'atris', 'loops', 'TICK.md');
|
|
362
|
+
const source = fs.existsSync(localTick) ? localTick : packagedTick;
|
|
363
|
+
console.log(fs.readFileSync(source, 'utf8').trimEnd());
|
|
364
|
+
return 0;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function showLoopsHelp() {
|
|
368
|
+
console.log('Usage: atris loops [audit|init|tick|board|start|stop]');
|
|
369
|
+
console.log('');
|
|
370
|
+
console.log('Self-improving loop commands:');
|
|
371
|
+
console.log(' atris loops - audit atris/loops in this project');
|
|
372
|
+
console.log(' atris loops audit - same audit explicitly');
|
|
373
|
+
console.log(' atris loops init - scaffold atris/loops + loop wiki');
|
|
374
|
+
console.log(' atris self-improve - alias for atris loops init');
|
|
375
|
+
console.log(' atris loops tick - print atris/loops/TICK.md protocol');
|
|
376
|
+
console.log('');
|
|
377
|
+
console.log('Background loop board:');
|
|
378
|
+
console.log(' atris loops board - registry jobs, launchd agents, mission');
|
|
379
|
+
console.log(' atris loops stop <id> - disable a registry job or stop a launchd agent');
|
|
380
|
+
console.log(' atris loops start <id> - enable a registry job or start a launchd agent');
|
|
381
|
+
return 0;
|
|
189
382
|
}
|
|
190
383
|
|
|
191
384
|
function loopsCommand(subcommand, ...args) {
|
|
192
385
|
switch (subcommand) {
|
|
193
386
|
case undefined:
|
|
387
|
+
case 'audit':
|
|
388
|
+
return auditLoops(process.cwd());
|
|
389
|
+
case 'init':
|
|
390
|
+
return initLoops(process.cwd());
|
|
391
|
+
case 'tick':
|
|
392
|
+
return tickLoops(process.cwd());
|
|
194
393
|
case 'board':
|
|
195
394
|
case 'list':
|
|
196
|
-
showBoard(process.cwd());
|
|
197
|
-
break;
|
|
395
|
+
return showBoard(process.cwd());
|
|
198
396
|
case 'stop':
|
|
199
|
-
controlLoop(args[0], 'stop');
|
|
200
|
-
break;
|
|
397
|
+
return controlLoop(args[0], 'stop');
|
|
201
398
|
case 'start':
|
|
202
|
-
controlLoop(args[0], 'start');
|
|
203
|
-
|
|
399
|
+
return controlLoop(args[0], 'start');
|
|
400
|
+
case 'help':
|
|
401
|
+
case '--help':
|
|
402
|
+
case '-h':
|
|
403
|
+
return showLoopsHelp();
|
|
204
404
|
default:
|
|
205
|
-
|
|
206
|
-
console.log(' atris loops - board: registry jobs, launchd agents, mission');
|
|
207
|
-
console.log(' atris loops stop <id> - disable a registry job or stop a launchd agent');
|
|
208
|
-
console.log(' atris loops start <id> - enable a registry job or start a launchd agent');
|
|
405
|
+
return showLoopsHelp();
|
|
209
406
|
}
|
|
210
407
|
}
|
|
211
408
|
|
|
212
|
-
module.exports = {
|
|
409
|
+
module.exports = {
|
|
410
|
+
loopsCommand,
|
|
411
|
+
showBoard,
|
|
412
|
+
initLoops,
|
|
413
|
+
auditLoops,
|
|
414
|
+
tickLoops,
|
|
415
|
+
structuralProblems,
|
|
416
|
+
};
|