atris 3.16.0 → 3.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -7
- package/atris/skills/atris/SKILL.md +15 -2
- package/atris/skills/atris-feedback/SKILL.md +7 -0
- package/atris/skills/design/SKILL.md +29 -2
- package/atris/skills/engines/SKILL.md +44 -0
- package/atris/skills/flow/SKILL.md +1 -1
- package/atris/skills/wake/SKILL.md +37 -0
- package/atris/skills/youtube/SKILL.md +13 -39
- package/atris/team/validator/MEMBER.md +1 -0
- package/atris/wiki/concepts/agent-activation-contract.md +3 -3
- package/atris/wiki/concepts/workspace-initialization-contract.md +3 -3
- package/atris/wiki/index.md +1 -0
- package/atris.md +43 -19
- package/bin/atris.js +446 -43
- package/commands/agent-spawn.js +480 -0
- package/commands/analytics.js +6 -3
- package/commands/apps.js +11 -0
- package/commands/autopilot.js +466 -20
- package/commands/brain.js +74 -7
- package/commands/brainstorm.js +9 -58
- package/commands/clean.js +1 -4
- package/commands/compile.js +574 -0
- package/commands/console.js +8 -3
- package/commands/deck.js +135 -0
- package/commands/init.js +22 -11
- package/commands/lesson.js +76 -0
- package/commands/member.js +252 -48
- package/commands/mission.js +405 -13
- package/commands/now.js +4 -2
- package/commands/probe.js +444 -0
- package/commands/pulse.js +504 -0
- package/commands/radar.js +1 -0
- package/commands/recap.js +233 -0
- package/commands/run.js +615 -22
- package/commands/skill.js +6 -2
- package/commands/slop.js +173 -0
- package/commands/spaceship.js +39 -0
- package/commands/sync.js +0 -2
- package/commands/task.js +458 -43
- package/commands/verify.js +7 -3
- package/lib/activity-stream.js +166 -0
- package/lib/auto-accept-certified.js +23 -1
- package/lib/context-gatherer.js +170 -0
- package/lib/escape-regexp.js +13 -0
- package/lib/file-ops.js +6 -3
- package/lib/journal.js +1 -1
- package/lib/lesson-contradiction.js +113 -0
- package/lib/policy-lessons.js +3 -2
- package/lib/pulse.js +401 -0
- package/lib/runner-command.js +156 -0
- package/lib/slides-deck.js +236 -0
- package/lib/state-detection.js +40 -3
- package/lib/task-db.js +101 -4
- package/lib/task-proof.js +1 -1
- package/lib/todo-fallback.js +2 -1
- package/lib/todo-sections.js +33 -0
- package/package.json +1 -2
- package/utils/api.js +14 -2
- package/atris/atrisDev.md +0 -717
package/commands/brain.js
CHANGED
|
@@ -3,6 +3,8 @@ 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 escapeRegExp = require('../lib/escape-regexp');
|
|
7
|
+
const { hasRenderedSections, isOpenSection, isDoneSection } = require('../lib/todo-sections');
|
|
6
8
|
|
|
7
9
|
const GENERATED_START = '<!-- ATRIS_BRAIN_COMPILE:START -->';
|
|
8
10
|
const GENERATED_END = '<!-- ATRIS_BRAIN_COMPILE:END -->';
|
|
@@ -296,7 +298,8 @@ function resolveStateRoot(root) {
|
|
|
296
298
|
|
|
297
299
|
function countTodoItems(todoText) {
|
|
298
300
|
const text = String(todoText || '');
|
|
299
|
-
|
|
301
|
+
// Section classification (incl. emoji-decorated headings) lives in lib/todo-sections.
|
|
302
|
+
const rendered = hasRenderedSections(text);
|
|
300
303
|
let section = null;
|
|
301
304
|
let unchecked = 0;
|
|
302
305
|
let checked = 0;
|
|
@@ -317,22 +320,46 @@ function countTodoItems(todoText) {
|
|
|
317
320
|
const isTitled = /^\s*-\s+(?:\[[ xX]\]\s+)?\*\*[^*]+:?\*\*/.test(line);
|
|
318
321
|
if (isUnchecked) unchecked += 1;
|
|
319
322
|
if (isChecked) checked += 1;
|
|
320
|
-
if (!
|
|
323
|
+
if (!rendered && (isUnchecked || (isTitled && !isChecked))) legacyOpen += 1;
|
|
321
324
|
if (!isTitled) continue;
|
|
322
325
|
|
|
323
326
|
titled += 1;
|
|
324
|
-
if (
|
|
325
|
-
if (
|
|
327
|
+
if (rendered && isOpenSection(section)) renderedOpen += 1;
|
|
328
|
+
if (rendered && isDoneSection(section)) renderedDone += 1;
|
|
326
329
|
}
|
|
327
330
|
|
|
328
331
|
return {
|
|
329
|
-
open:
|
|
332
|
+
open: rendered ? renderedOpen : legacyOpen,
|
|
330
333
|
checked,
|
|
331
334
|
titled,
|
|
332
|
-
done:
|
|
335
|
+
done: rendered ? renderedDone : checked + (text.match(/~~|DONE|✅/g) || []).length,
|
|
333
336
|
};
|
|
334
337
|
}
|
|
335
338
|
|
|
339
|
+
function parseTodoEndgame(todoText) {
|
|
340
|
+
const text = String(todoText || '');
|
|
341
|
+
const fields = {};
|
|
342
|
+
let inEndgame = false;
|
|
343
|
+
|
|
344
|
+
for (const line of text.split(/\r?\n/)) {
|
|
345
|
+
const heading = line.match(/^##\s+(.+?)\s*$/);
|
|
346
|
+
if (heading) {
|
|
347
|
+
inEndgame = heading[1].trim().toLowerCase() === 'endgame';
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
if (!inEndgame) continue;
|
|
351
|
+
|
|
352
|
+
const field = line.match(/^\*\*(Slug|Horizon|Source):\*\*\s*(.*?)\s*$/i);
|
|
353
|
+
if (field) fields[field[1].toLowerCase()] = field[2].trim();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const slug = fields.slug || null;
|
|
357
|
+
const horizon = fields.horizon || null;
|
|
358
|
+
const source = fields.source || null;
|
|
359
|
+
if (!slug && !horizon && !source) return null;
|
|
360
|
+
return { slug, horizon, source };
|
|
361
|
+
}
|
|
362
|
+
|
|
336
363
|
const EXECUTABLE_TASK_STATUSES = new Set(['open', 'claimed']);
|
|
337
364
|
const COMPLETED_TASK_STATUSES = new Set(['done', 'completed', 'accepted']);
|
|
338
365
|
|
|
@@ -350,15 +377,33 @@ function isCertifiedReviewTask(task) {
|
|
|
350
377
|
return Boolean(metadata.agent_certified || review.agent_certified || passCount >= 2);
|
|
351
378
|
}
|
|
352
379
|
|
|
380
|
+
function isAgentNeededReviewTask(task) {
|
|
381
|
+
if (String(task?.status || '').toLowerCase() !== 'review') return false;
|
|
382
|
+
if (isCertifiedReviewTask(task)) return false;
|
|
383
|
+
const metadata = task.metadata || {};
|
|
384
|
+
const review = task.review || {};
|
|
385
|
+
const approvalStatus = String(metadata.approval_status || review.approval_status || 'pending').toLowerCase();
|
|
386
|
+
if (approvalStatus && approvalStatus !== 'pending') return false;
|
|
387
|
+
const passCount = Number(metadata.agent_review_pass_count || review.agent_review_pass_count || 0);
|
|
388
|
+
return passCount < 2;
|
|
389
|
+
}
|
|
390
|
+
|
|
353
391
|
function summarizeTaskProjection(root) {
|
|
354
392
|
const tasks = readTaskProjectionTasks(root);
|
|
355
393
|
if (!tasks) return null;
|
|
356
394
|
|
|
357
395
|
const counts = {};
|
|
358
396
|
const certifiedReviewTasks = [];
|
|
397
|
+
const agentNeededReviewTasks = [];
|
|
359
398
|
for (const task of tasks) {
|
|
360
399
|
const status = String(task?.status || '').toLowerCase();
|
|
361
400
|
counts[status] = (counts[status] || 0) + 1;
|
|
401
|
+
if (isAgentNeededReviewTask(task)) {
|
|
402
|
+
agentNeededReviewTasks.push({
|
|
403
|
+
ref: task.display_id || task.legacy_ref || task.id,
|
|
404
|
+
title: task.title || 'Untitled task',
|
|
405
|
+
});
|
|
406
|
+
}
|
|
362
407
|
if (isCertifiedReviewTask(task)) {
|
|
363
408
|
certifiedReviewTasks.push({
|
|
364
409
|
ref: task.display_id || task.legacy_ref || task.id,
|
|
@@ -370,6 +415,7 @@ function summarizeTaskProjection(root) {
|
|
|
370
415
|
return {
|
|
371
416
|
tasks,
|
|
372
417
|
counts,
|
|
418
|
+
agentNeededReviewTasks,
|
|
373
419
|
certifiedReviewTasks,
|
|
374
420
|
};
|
|
375
421
|
}
|
|
@@ -466,6 +512,7 @@ function collectState(root) {
|
|
|
466
512
|
slug: business.slug || path.basename(root),
|
|
467
513
|
business,
|
|
468
514
|
todo: countWorkItems(root, todoText),
|
|
515
|
+
endgame: parseTodoEndgame(todoText),
|
|
469
516
|
taskProjection: summarizeTaskProjection(root),
|
|
470
517
|
hasNow: nowText.length > 0,
|
|
471
518
|
nowHeading: firstHeading(nowText, null),
|
|
@@ -628,7 +675,7 @@ function parseContributionCard(text, member) {
|
|
|
628
675
|
if (!text || !member) return null;
|
|
629
676
|
const firstName = String(member.name || member.slug || '').split(/\s+/)[0].toLowerCase();
|
|
630
677
|
const sections = String(text).split(/\n(?=##\s+)/);
|
|
631
|
-
const memberSections = sections.filter(section => new RegExp(`^##\\s+${firstName}\\b`, 'i').test(section.trim()));
|
|
678
|
+
const memberSections = sections.filter(section => new RegExp(`^##\\s+${escapeRegExp(firstName)}\\b`, 'i').test(section.trim()));
|
|
632
679
|
const section = (
|
|
633
680
|
memberSections.find(candidate => /current_score_signal\s*:/i.test(candidate))
|
|
634
681
|
|| memberSections[0]
|
|
@@ -708,12 +755,25 @@ function memberNextMove(member, state = null) {
|
|
|
708
755
|
const name = member.name || member.slug;
|
|
709
756
|
const context = `${member.startHere}\n${member.goals}`;
|
|
710
757
|
const identity = `${member.slug}\n${member.name}`;
|
|
758
|
+
const agentNeededReview = state?.taskProjection?.agentNeededReviewTasks?.[0] || null;
|
|
759
|
+
const agentNeededReviewMove = agentNeededReview
|
|
760
|
+
? `${name}: run the agent-safe review lane for ${agentNeededReview.ref}: ` +
|
|
761
|
+
`\`atris task review-chat ${agentNeededReview.ref} --as codex-review\`, then run the verifier named in the review packet and certify or revise without accepting XP.`
|
|
762
|
+
: null;
|
|
711
763
|
const certifiedReview = state?.taskProjection?.certifiedReviewTasks?.[0] || null;
|
|
764
|
+
const certifiedReviewRefs = (state?.taskProjection?.certifiedReviewTasks || [])
|
|
765
|
+
.map(task => task.ref)
|
|
766
|
+
.filter(Boolean);
|
|
712
767
|
const certifiedReviewMove = certifiedReview
|
|
713
768
|
? `${name}: hand off certified review ${certifiedReview.ref} to the operator: run ` +
|
|
714
769
|
`\`atris task accept ${certifiedReview.ref}\` if approved or ` +
|
|
715
770
|
`\`atris task revise ${certifiedReview.ref} --note "<what must change>"\` if not; do not create new work until this checkpoint is clear.`
|
|
716
771
|
: null;
|
|
772
|
+
const codexEndgameMove = certifiedReviewRefs.length > 0 && member.slug === 'codex-executor'
|
|
773
|
+
? `${name}: certified reviews ${certifiedReviewRefs.slice(0, 3).join(', ')} are human-only; do not accept XP. ` +
|
|
774
|
+
`Create the next bounded Codex task from Endgame ${state?.endgame?.slug || 'current-horizon'}: ` +
|
|
775
|
+
`${state?.endgame?.horizon || 'the highest-leverage system gap'}; include files, verifier, and stop rule before editing.`
|
|
776
|
+
: null;
|
|
717
777
|
if (member.slug === 'justin' || /justin/i.test(member.name || '')) {
|
|
718
778
|
return `${name}: run one customer-moving GTM rep, update the relevant workspace state within 10 minutes, and leave a scorecard.`;
|
|
719
779
|
}
|
|
@@ -728,6 +788,7 @@ function memberNextMove(member, state = null) {
|
|
|
728
788
|
return `${name}: choose or create one bounded mission step, run its verifier, and close it with proof, a scorecard, and the next move.`;
|
|
729
789
|
}
|
|
730
790
|
if (/validator|reviewer/i.test(identity)) {
|
|
791
|
+
if (agentNeededReviewMove) return agentNeededReviewMove;
|
|
731
792
|
if (certifiedReviewMove) return certifiedReviewMove;
|
|
732
793
|
if ((state?.todo?.open || 0) === 0 && (state?.todo?.done || 0) === 0) {
|
|
733
794
|
return `${name}: wait for one concrete artifact or ask Navigator to create a reviewable task with verifier, proof target, and residual-risk checklist.`;
|
|
@@ -736,6 +797,8 @@ function memberNextMove(member, state = null) {
|
|
|
736
797
|
}
|
|
737
798
|
if (/executor|builder/i.test(identity)) {
|
|
738
799
|
if ((state?.todo?.open || 0) === 0) {
|
|
800
|
+
if (agentNeededReviewMove) return agentNeededReviewMove;
|
|
801
|
+
if (codexEndgameMove) return codexEndgameMove;
|
|
739
802
|
if (certifiedReviewMove) return certifiedReviewMove;
|
|
740
803
|
return `${name}: ask Navigator to create one bounded task with files, verifier, and stop rule before making a patch.`;
|
|
741
804
|
}
|
|
@@ -745,6 +808,7 @@ function memberNextMove(member, state = null) {
|
|
|
745
808
|
return `${name}: turn one messy or unclaimed intent into a MAP-backed plan with ASCII visualization, exact files, verifier, rollback, and a review-ready task.`;
|
|
746
809
|
}
|
|
747
810
|
if (/launcher|closer/i.test(identity)) {
|
|
811
|
+
if (agentNeededReviewMove) return agentNeededReviewMove;
|
|
748
812
|
if (certifiedReviewMove) return certifiedReviewMove;
|
|
749
813
|
if ((state?.todo?.done || 0) === 0) {
|
|
750
814
|
return `${name}: wait for one validated task receipt before closeout, or ask Validator to produce a review decision with proof.`;
|
|
@@ -1571,4 +1635,7 @@ module.exports = {
|
|
|
1571
1635
|
verifyActivationCard,
|
|
1572
1636
|
verifyActivationGallery,
|
|
1573
1637
|
verifyBrain,
|
|
1638
|
+
parseContributionCard,
|
|
1639
|
+
escapeRegExp,
|
|
1640
|
+
countTodoItems,
|
|
1574
1641
|
};
|
package/commands/brainstorm.js
CHANGED
|
@@ -2,6 +2,15 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const readline = require('readline');
|
|
4
4
|
const { getLogPath, ensureLogDirectory, createLogFile } = require('../lib/journal');
|
|
5
|
+
// Inbox helpers live canonically (and CRLF-tolerant) in lib/file-ops; brainstorm
|
|
6
|
+
// used to carry byte-identical local copies that silently missed CRLF journals.
|
|
7
|
+
const {
|
|
8
|
+
parseInboxItems,
|
|
9
|
+
replaceInboxSection,
|
|
10
|
+
addInboxItemToContent,
|
|
11
|
+
getNextInboxId,
|
|
12
|
+
addInboxIdea,
|
|
13
|
+
} = require('../lib/file-ops');
|
|
5
14
|
const { loadConfig } = require('../utils/config');
|
|
6
15
|
const { loadCredentials, ensureValidCredentials } = require('../utils/auth');
|
|
7
16
|
const { apiRequestJson } = require('../utils/api');
|
|
@@ -351,69 +360,11 @@ function brainstormAbortError() {
|
|
|
351
360
|
return error;
|
|
352
361
|
}
|
|
353
362
|
|
|
354
|
-
function addInboxIdea(logFile, summary) {
|
|
355
|
-
const content = fs.readFileSync(logFile, 'utf8');
|
|
356
|
-
const nextId = getNextInboxId(content);
|
|
357
|
-
const updated = addInboxItemToContent(content, nextId, summary);
|
|
358
|
-
fs.writeFileSync(logFile, updated);
|
|
359
|
-
return nextId;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
function parseInboxItems(content) {
|
|
363
|
-
const match = content.match(/## Inbox\n([\s\S]*?)(?=\n##|\n---|$)/);
|
|
364
|
-
if (!match) {
|
|
365
|
-
return [];
|
|
366
|
-
}
|
|
367
|
-
const body = match[1];
|
|
368
|
-
const lines = body.split('\n');
|
|
369
|
-
const items = [];
|
|
370
|
-
lines.forEach((line) => {
|
|
371
|
-
const trimmed = line.trim();
|
|
372
|
-
if (!trimmed) return;
|
|
373
|
-
if (trimmed.startsWith('(Empty')) return;
|
|
374
|
-
const parsed = trimmed.match(/^- \*\*I(\d+):\*\*\s*(.+)$|^- \*\*I(\d+):\s+(.+)$/);
|
|
375
|
-
if (parsed) {
|
|
376
|
-
const id = parseInt(parsed[1] || parsed[3], 10);
|
|
377
|
-
const text = parsed[2] || parsed[4];
|
|
378
|
-
items.push({ id, text, line: trimmed });
|
|
379
|
-
}
|
|
380
|
-
});
|
|
381
|
-
return items;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
function replaceInboxSection(content, items) {
|
|
385
|
-
const regex = /(## Inbox\n)([\s\S]*?)(\n---|\n##|$)/;
|
|
386
|
-
if (!regex.test(content)) {
|
|
387
|
-
const lines = items.length ? items.map((item) => item.line).join('\n') : '(Empty - inbox zero achieved)';
|
|
388
|
-
return `${content}\n\n## Inbox\n\n${lines}\n`;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
return content.replace(regex, (match, header, body, suffix) => {
|
|
392
|
-
const inner = items.length
|
|
393
|
-
? `\n${items.map((item) => item.line).join('\n')}\n`
|
|
394
|
-
: '\n(Empty - inbox zero achieved)\n';
|
|
395
|
-
return `${header}${inner}${suffix}`;
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function addInboxItemToContent(content, id, summary) {
|
|
400
|
-
const items = parseInboxItems(content).filter((item) => item.id !== id);
|
|
401
|
-
const newItem = { id, text: summary, line: `- **I${id}:** ${summary}` };
|
|
402
|
-
const updatedItems = [newItem, ...items];
|
|
403
|
-
return replaceInboxSection(content, updatedItems);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
363
|
function removeInboxItemFromContent(content, id) {
|
|
407
364
|
const items = parseInboxItems(content).filter((item) => item.id !== id);
|
|
408
365
|
return replaceInboxSection(content, items);
|
|
409
366
|
}
|
|
410
367
|
|
|
411
|
-
function getNextInboxId(content) {
|
|
412
|
-
const items = parseInboxItems(content);
|
|
413
|
-
if (items.length === 0) return 1;
|
|
414
|
-
return items.reduce((max, item) => (item.id > max ? item.id : max), 0) + 1;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
368
|
function insertIntoNotesSection(content, block) {
|
|
418
369
|
const regex = /(## Notes\n)([\s\S]*?)(\n---|\n##|$)/;
|
|
419
370
|
const match = content.match(regex);
|
package/commands/clean.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const escapeRegExp = require('../lib/escape-regexp');
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* atris clean - Workspace housekeeping with auto-heal
|
|
@@ -396,10 +397,6 @@ function findSymbolLine(fileContent, symbol) {
|
|
|
396
397
|
return null;
|
|
397
398
|
}
|
|
398
399
|
|
|
399
|
-
function escapeRegExp(string) {
|
|
400
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
401
|
-
}
|
|
402
|
-
|
|
403
400
|
/**
|
|
404
401
|
* Find wiki pages whose sources have been modified after last_compiled.
|
|
405
402
|
* Scans all .md files under atris/ for frontmatter with last_compiled + sources.
|