atris 3.37.1 → 3.40.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 +25 -6
- package/README.md +2 -0
- package/atris/PERSONA.md +11 -11
- package/atris.md +17 -2
- package/ax +160 -34
- package/bin/atris.js +78 -6
- package/commands/activate.js +16 -3
- package/commands/agent-spawn.js +13 -18
- package/commands/autoland.js +76 -47
- package/commands/autopilot-front.js +0 -4
- package/commands/autopilot.js +0 -12
- package/commands/bench.js +10 -12
- package/commands/brain.js +0 -7
- package/commands/brief.js +0 -2
- package/commands/business-sync.js +0 -1
- package/commands/business.js +452 -12
- package/commands/chat-scan.js +5 -7
- package/commands/clean.js +31 -2
- package/commands/close.js +46 -11
- package/commands/cloud.js +0 -1
- package/commands/codex-goal.js +8 -12
- package/commands/computer.js +6 -5
- package/commands/console.js +64 -11
- package/commands/decide.js +166 -0
- package/commands/deck.js +30 -12
- package/commands/dream.js +0 -7
- package/commands/drill.js +14 -25
- package/commands/engine.js +206 -26
- package/commands/experiments.js +0 -4
- package/commands/fleet-report.js +12 -4
- package/commands/game.js +261 -0
- package/commands/gm.js +17 -7
- package/commands/goal.js +0 -6
- package/commands/harvest.js +1 -5
- package/commands/improve.js +29 -8
- package/commands/init.js +41 -37
- package/commands/integrations.js +0 -7
- package/commands/land.js +77 -20
- package/commands/launchpad.js +1 -16
- package/commands/lesson.js +17 -7
- package/commands/lifecycle.js +5 -5
- package/commands/log.js +55 -5
- package/commands/loop-front.js +0 -2
- package/commands/loops.js +6 -5
- package/commands/meet.js +0 -2
- package/commands/member.js +622 -573
- package/commands/mission.js +790 -314
- package/commands/next.js +0 -1
- package/commands/now.js +0 -12
- package/commands/one-lap.js +8 -8
- package/commands/orb.js +0 -4
- package/commands/pack-craft.js +0 -1
- package/commands/pack.js +3599 -424
- package/commands/play.js +6 -6
- package/commands/probe.js +12 -6
- package/commands/pulse.js +88 -64
- package/commands/push.js +0 -4
- package/commands/radar.js +14 -11
- package/commands/recover.js +340 -0
- package/commands/release.js +10 -9
- package/commands/report.js +0 -1
- package/commands/router.js +320 -0
- package/commands/run-front.js +0 -2
- package/commands/search.js +0 -3
- package/commands/site-deploy.js +870 -0
- package/commands/site.js +11 -2
- package/commands/skill.js +7 -2
- package/commands/slop.js +237 -7
- package/commands/status.js +6 -0
- package/commands/stream.js +4 -20
- package/commands/study.js +0 -3
- package/commands/sync-checkout.js +51 -0
- package/commands/sync.js +11 -40
- package/commands/task.js +999 -595
- package/commands/taste.js +101 -0
- package/commands/teach.js +86 -0
- package/commands/team.js +83 -3
- package/commands/unknowns.js +0 -10
- package/commands/update.js +0 -1
- package/commands/vercel.js +4 -2
- package/commands/visualize.js +0 -5
- package/commands/voice.js +195 -0
- package/commands/watch.js +1 -28
- package/commands/wiki.js +42 -15
- package/commands/wish.js +0 -1
- package/commands/workflow.js +2 -2
- package/commands/worktree.js +88 -28
- package/commands/xp.js +28 -30
- package/commands/youtube.js +0 -2
- package/lib/accept-verify-gate.js +126 -0
- package/lib/activity-stream.js +0 -2
- package/lib/arg-parser.js +41 -0
- package/lib/auto-accept-certified.js +261 -3
- package/lib/autoland.js +66 -3
- package/lib/ax-auto-lane.js +68 -7
- package/lib/bench/context.js +0 -2
- package/lib/bench/report.js +0 -1
- package/lib/bench/runner.js +18 -11
- package/lib/brief-ledger.js +0 -6
- package/lib/business-simulate.js +382 -0
- package/lib/chat-log-scan.js +0 -5
- package/lib/checkout-sync.js +52 -0
- package/lib/claude-boot-block.js +48 -0
- package/lib/cloud-mission.js +0 -6
- package/lib/codex-flight.js +0 -1
- package/lib/conductor-artifacts.js +25 -0
- package/lib/context-gatherer.js +7 -2
- package/lib/deck-compose.js +0 -1
- package/lib/deck-history.js +0 -1
- package/lib/deck-layout.js +0 -5
- package/lib/deck-review.js +0 -7
- package/lib/deck-schema.js +0 -1
- package/lib/default-verifier.js +0 -2
- package/lib/engine-registry.js +157 -28
- package/lib/experiments/daily.js +0 -6
- package/lib/falsifier-probe.js +84 -0
- package/lib/fleet.js +275 -42
- package/lib/functional-owner.js +0 -3
- package/lib/git-spawn.js +15 -0
- package/lib/inspect-fields.js +0 -1
- package/lib/json-file.js +37 -0
- package/lib/known-commands.js +2 -2
- package/lib/learnings.js +0 -2
- package/lib/lesson-preflight.js +146 -0
- package/lib/loop-doctor.js +11 -19
- package/lib/manifest.js +0 -2
- package/lib/member-alive.js +0 -1
- package/lib/member-switches.js +0 -5
- package/lib/mission-artifact.js +0 -1
- package/lib/mission-human-asks.js +28 -0
- package/lib/mission-protected-lane.js +296 -0
- package/lib/mission-room.js +0 -2
- package/lib/mission-runtime-loop.js +0 -1
- package/lib/next-moves.js +0 -18
- package/lib/official-cli-integration.js +47 -6
- package/lib/orb-context.js +14 -4
- package/lib/orb-scorecard.js +0 -1
- package/lib/outbound-send-gate.js +0 -6
- package/lib/pack-capabilities.js +685 -0
- package/lib/permission-grants.js +0 -6
- package/lib/policy-lessons.js +0 -2
- package/lib/pulse.js +68 -15
- package/lib/review-integrity.js +0 -2
- package/lib/router-brain.js +51 -3
- package/lib/runner-command.js +0 -10
- package/lib/runtime-bootstrap.js +0 -1
- package/lib/scorecard.js +0 -1
- package/lib/self-drive.js +45 -14
- package/lib/state-detection.js +0 -6
- package/lib/task-db.js +137 -7
- package/lib/task-decision.js +50 -0
- package/lib/taste-lessons.js +153 -0
- package/lib/team-presence.js +0 -1
- package/lib/tool-result-encode.js +17 -1
- package/lib/voice-gate.js +129 -2
- package/lib/wiki.js +152 -9
- package/lib/wish-audit.js +21 -7
- package/lib/wish-delegate.js +27 -21
- package/lib/wish-stats.js +0 -5
- package/lib/wish-store.js +0 -1
- package/lib/workspace-safety.js +0 -2
- package/lib/zip.js +95 -8
- package/package.json +2 -1
- package/templates/business-starter/persona.md +9 -0
package/commands/wiki.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
+
const { hasFlag } = require('../lib/arg-parser');
|
|
3
4
|
const { loadCredentials } = require('../utils/auth');
|
|
4
5
|
const { apiRequestJson } = require('../utils/api');
|
|
5
6
|
const { loadBusinesses, saveBusinesses } = require('./business');
|
|
@@ -14,8 +15,11 @@ const {
|
|
|
14
15
|
buildIngestPrompt,
|
|
15
16
|
buildQueryPrompt,
|
|
16
17
|
buildLintPrompt,
|
|
18
|
+
buildConsolidatePrompt,
|
|
17
19
|
writeWikiStatus,
|
|
18
20
|
appendWikiLog,
|
|
21
|
+
findWikiMetabolismFindings,
|
|
22
|
+
recordWikiConsolidation,
|
|
19
23
|
validateAgentReadableWikiPages,
|
|
20
24
|
} = require('../lib/wiki');
|
|
21
25
|
|
|
@@ -171,7 +175,7 @@ function hasHelpFlag(args) {
|
|
|
171
175
|
}
|
|
172
176
|
|
|
173
177
|
function printWikiHelp(scope = null) {
|
|
174
|
-
const normalized = scope === 'ingest' || scope === 'query' || scope === 'lint' || scope === 'search' || scope === 'log' || scope === 'loop' || scope === 'verify'
|
|
178
|
+
const normalized = scope === 'ingest' || scope === 'query' || scope === 'lint' || scope === 'consolidate' || scope === 'search' || scope === 'log' || scope === 'loop' || scope === 'verify'
|
|
175
179
|
? scope
|
|
176
180
|
: null;
|
|
177
181
|
|
|
@@ -191,12 +195,17 @@ function printWikiHelp(scope = null) {
|
|
|
191
195
|
console.log('Usage: atris wiki lint [--private|--cloud] [business]');
|
|
192
196
|
console.log('');
|
|
193
197
|
console.log('Build a local or cloud wiki lint prompt.');
|
|
198
|
+
} else if (normalized === 'consolidate') {
|
|
199
|
+
console.log('Usage: atris wiki consolidate [--private]');
|
|
200
|
+
console.log('');
|
|
201
|
+
console.log('Build a local wiki pruning prompt and reset the growth marker.');
|
|
194
202
|
} else {
|
|
195
|
-
console.log('Usage: atris wiki <ingest|query|lint|search|log|loop|verify|entities|related> [business] [args]');
|
|
203
|
+
console.log('Usage: atris wiki <ingest|query|lint|consolidate|search|log|loop|verify|entities|related> [business] [args]');
|
|
196
204
|
console.log('');
|
|
197
205
|
console.log(' ingest <path> Local-first ingest into atris/wiki/');
|
|
198
206
|
console.log(' query "question" Local-first query against atris/wiki/');
|
|
199
207
|
console.log(' lint Local-first lint for atris/wiki/');
|
|
208
|
+
console.log(' consolidate Build a local pruning prompt and reset wiki growth');
|
|
200
209
|
console.log(' search [business] <term> Search local atris/wiki/index.md');
|
|
201
210
|
console.log(' log [business] [N] Show recent atris/wiki/log.md entries');
|
|
202
211
|
console.log(' loop Run local wiki upkeep analysis and refresh STATUS/log');
|
|
@@ -215,10 +224,6 @@ function printWikiHelp(scope = null) {
|
|
|
215
224
|
console.log('');
|
|
216
225
|
}
|
|
217
226
|
|
|
218
|
-
function hasFlag(args, name) {
|
|
219
|
-
return args.includes(name);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
227
|
function optionValue(args, name, fallback = null) {
|
|
223
228
|
const index = args.indexOf(name);
|
|
224
229
|
if (index === -1 || index + 1 >= args.length) return fallback;
|
|
@@ -366,7 +371,11 @@ async function wikiLint(mode, slug) {
|
|
|
366
371
|
console.error(`No local wiki found at ${getWikiRoot(wikiMode)}. Run: atris wiki ingest${wikiMode === 'private' ? ' --private' : ''} <path>`);
|
|
367
372
|
process.exit(1);
|
|
368
373
|
}
|
|
369
|
-
|
|
374
|
+
const metabolismFindings = findWikiMetabolismFindings(process.cwd(), wikiMode);
|
|
375
|
+
printLocalPrompt(mode === 'private' ? 'Private wiki lint' : 'Local wiki lint', buildLintPrompt(wikiMode), getWikiRoot(wikiMode), [
|
|
376
|
+
`Wiki dir: ${wikiDir}`,
|
|
377
|
+
...metabolismFindings.map((finding) => `${finding.code}: ${finding.message}`),
|
|
378
|
+
]);
|
|
370
379
|
return;
|
|
371
380
|
}
|
|
372
381
|
|
|
@@ -376,6 +385,28 @@ async function wikiLint(mode, slug) {
|
|
|
376
385
|
await runChat(business, buildLintPrompt(), creds.token);
|
|
377
386
|
}
|
|
378
387
|
|
|
388
|
+
function wikiConsolidate(mode, slug) {
|
|
389
|
+
if (mode === 'cloud') {
|
|
390
|
+
console.error('Wiki consolidation is local-only. Pull the wiki before consolidating it.');
|
|
391
|
+
process.exit(1);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const wikiMode = mode === 'private' ? 'private' : 'public';
|
|
395
|
+
const wikiDir = findLocalWikiDir(process.cwd(), slug, wikiMode);
|
|
396
|
+
if (!wikiDir) {
|
|
397
|
+
console.error(`No local wiki found at ${getWikiRoot(wikiMode)}.`);
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
printLocalPrompt(
|
|
402
|
+
mode === 'private' ? 'private wiki consolidate' : 'local wiki consolidate',
|
|
403
|
+
buildConsolidatePrompt(wikiMode),
|
|
404
|
+
getWikiRoot(wikiMode),
|
|
405
|
+
[`wiki dir: ${wikiDir}`]
|
|
406
|
+
);
|
|
407
|
+
recordWikiConsolidation(process.cwd(), wikiMode);
|
|
408
|
+
}
|
|
409
|
+
|
|
379
410
|
function wikiSearch(mode, slug, query) {
|
|
380
411
|
if (!query) {
|
|
381
412
|
console.error('Usage: atris wiki search [business] <term>');
|
|
@@ -513,6 +544,10 @@ async function wikiCommand(subcommand, ...args) {
|
|
|
513
544
|
await wikiLint(mode, slug);
|
|
514
545
|
break;
|
|
515
546
|
}
|
|
547
|
+
case 'consolidate': {
|
|
548
|
+
wikiConsolidate(mode, null);
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
516
551
|
case 'search': {
|
|
517
552
|
if (mode === 'private') {
|
|
518
553
|
wikiSearch(mode, null, cleanArgs.join(' '));
|
|
@@ -572,12 +607,4 @@ async function wikiCommand(subcommand, ...args) {
|
|
|
572
607
|
|
|
573
608
|
module.exports = {
|
|
574
609
|
wikiCommand,
|
|
575
|
-
wikiIngest,
|
|
576
|
-
wikiQuery,
|
|
577
|
-
wikiLint,
|
|
578
|
-
wikiSearch,
|
|
579
|
-
wikiLog,
|
|
580
|
-
wikiVerify,
|
|
581
|
-
wikiEntities,
|
|
582
|
-
wikiRelated,
|
|
583
610
|
};
|
package/commands/wish.js
CHANGED
package/commands/workflow.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const { getLogPath } = require('../lib/journal');
|
|
4
|
-
const {
|
|
4
|
+
const { buildToolResultBody } = require('../lib/tool-result-encode');
|
|
5
5
|
|
|
6
6
|
function wrapWorkflowText(text, width = 76) {
|
|
7
7
|
const normalized = String(text || '').replace(/\s+/g, ' ').trim();
|
|
@@ -140,7 +140,7 @@ function postToolResult(callId, result, base = 'http://127.0.0.1:8000') {
|
|
|
140
140
|
const url = new URL('/api/atris2/turn/tool-result', base);
|
|
141
141
|
const transport = url.protocol === 'https:' ? require('https') : require('http');
|
|
142
142
|
return new Promise((resolve, reject) => {
|
|
143
|
-
const postData = JSON.stringify(
|
|
143
|
+
const postData = JSON.stringify(buildToolResultBody(callId, result));
|
|
144
144
|
const req = transport.request({
|
|
145
145
|
hostname: url.hostname,
|
|
146
146
|
port: url.port || (url.protocol === 'https:' ? 443 : 80),
|
package/commands/worktree.js
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { spawnSync } = require('child_process');
|
|
6
|
+
const { hasFlag, readFlag } = require('../lib/arg-parser');
|
|
6
7
|
const { stampLatestOpenBriefForWorktree } = require('../lib/brief-ledger');
|
|
8
|
+
const { isConductorArtifact } = require('../lib/conductor-artifacts');
|
|
7
9
|
|
|
8
10
|
const REGEN_ADAPTER_FILES = ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md'];
|
|
9
11
|
const COMMAND_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
@@ -261,11 +263,16 @@ function baseBranchName(ref) {
|
|
|
261
263
|
return String(ref || '').replace(/^refs\/heads\//, '').replace(/^origin\//, '');
|
|
262
264
|
}
|
|
263
265
|
|
|
264
|
-
function statusCounts(root, {
|
|
266
|
+
function statusCounts(root, {
|
|
267
|
+
ignoredUnstagedFiles = new Set(),
|
|
268
|
+
ignoredUntrackedFiles = new Set(),
|
|
269
|
+
ignoreUntracked = null,
|
|
270
|
+
} = {}) {
|
|
265
271
|
if (!fs.existsSync(root)) return null;
|
|
266
272
|
// -uall expands untracked directories to individual files so ignoredUntrackedFiles
|
|
267
|
-
// can match exact paths (plain porcelain collapses them to "?? dir/").
|
|
268
|
-
const
|
|
273
|
+
// and ignoreUntracked can match exact paths (plain porcelain collapses them to "?? dir/").
|
|
274
|
+
const expandUntracked = ignoredUntrackedFiles.size || typeof ignoreUntracked === 'function';
|
|
275
|
+
const statusArgs = expandUntracked ? ['status', '--porcelain', '-uall'] : ['status', '--porcelain'];
|
|
269
276
|
const result = runGit(statusArgs, { cwd: root, check: false });
|
|
270
277
|
if (result.status !== 0) return null;
|
|
271
278
|
let staged = 0;
|
|
@@ -276,6 +283,7 @@ function statusCounts(root, { ignoredUnstagedFiles = new Set(), ignoredUntracked
|
|
|
276
283
|
if (ignoredUnstagedFiles.has(file) && line[0] === ' ' && line[1] !== ' ') continue;
|
|
277
284
|
if (line.startsWith('??')) {
|
|
278
285
|
if (ignoredUntrackedFiles.has(file)) continue;
|
|
286
|
+
if (typeof ignoreUntracked === 'function' && ignoreUntracked(file)) continue;
|
|
279
287
|
untracked += 1;
|
|
280
288
|
continue;
|
|
281
289
|
}
|
|
@@ -285,6 +293,16 @@ function statusCounts(root, { ignoredUnstagedFiles = new Set(), ignoredUntracked
|
|
|
285
293
|
return { staged, unstaged, untracked };
|
|
286
294
|
}
|
|
287
295
|
|
|
296
|
+
// Remove conductor plumbing from the index after a broad `git add -A`, so the commit
|
|
297
|
+
// records engine output only. Paths that were never staged are simply not listed.
|
|
298
|
+
function unstageConductorArtifacts(root) {
|
|
299
|
+
const staged = runGit(['diff', '--cached', '--name-only'], { cwd: root, check: false });
|
|
300
|
+
if (staged.status !== 0) return [];
|
|
301
|
+
const drop = String(staged.stdout || '').split(/\r?\n/).filter(Boolean).filter(isConductorArtifact);
|
|
302
|
+
if (drop.length) runGit(['reset', '-q', 'HEAD', '--', ...drop], { cwd: root, check: false });
|
|
303
|
+
return drop;
|
|
304
|
+
}
|
|
305
|
+
|
|
288
306
|
function changedFiles(root, args) {
|
|
289
307
|
const result = runGit([...args, '--', ...REGEN_ADAPTER_FILES], { cwd: root, check: false });
|
|
290
308
|
if (result.status !== 0) return new Set();
|
|
@@ -304,20 +322,6 @@ function restoreRegeneratedAdapterChurn(root, message, { dryRun = false } = {})
|
|
|
304
322
|
return skipped;
|
|
305
323
|
}
|
|
306
324
|
|
|
307
|
-
function readFlag(args, name, fallback = '') {
|
|
308
|
-
const prefix = `${name}=`;
|
|
309
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
310
|
-
const arg = String(args[i]);
|
|
311
|
-
if (arg === name && args[i + 1] && !String(args[i + 1]).startsWith('--')) return String(args[i + 1]);
|
|
312
|
-
if (arg.startsWith(prefix)) return arg.slice(prefix.length);
|
|
313
|
-
}
|
|
314
|
-
return fallback;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function hasFlag(args, name) {
|
|
318
|
-
return args.includes(name);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
325
|
function swarloClaim(root, { channel, taskKey, content }) {
|
|
322
326
|
const script = path.join(root, 'scripts', 'swarlo.py');
|
|
323
327
|
if (!fs.existsSync(script)) return 'skip: scripts/swarlo.py not found';
|
|
@@ -472,6 +476,14 @@ function createOrFindPr(root, branch, targetRef, title, dryRun) {
|
|
|
472
476
|
`Target: ${targetBranch}`,
|
|
473
477
|
].join('\n');
|
|
474
478
|
if (dryRun) return `dry-run: gh pr create --base ${targetBranch} --head ${branch}`;
|
|
479
|
+
// An engine that produced nothing leaves a branch level with its base. Asking
|
|
480
|
+
// GitHub for a PR there fails with "No commits between ...", and that throw used
|
|
481
|
+
// to escape the flight and abort the whole run. Say it plainly instead.
|
|
482
|
+
const ahead = runGit(['rev-list', '--count', `${targetRef}..HEAD`], { cwd: root, check: false });
|
|
483
|
+
if (ahead.status === 0 && String(ahead.stdout || '').trim() === '0') {
|
|
484
|
+
console.log('pr: skipped (no commits ahead of base)');
|
|
485
|
+
return '';
|
|
486
|
+
}
|
|
475
487
|
const created = spawnSync(
|
|
476
488
|
'gh',
|
|
477
489
|
['pr', 'create', '--base', targetBranch, '--head', branch, '--title', title, '--body', body],
|
|
@@ -498,6 +510,36 @@ function shipHelp() {
|
|
|
498
510
|
console.log(' recommended flight verify: npm run test:fast && node --test <focused files>');
|
|
499
511
|
}
|
|
500
512
|
|
|
513
|
+
function deniedLaneForShip(root, branch, message) {
|
|
514
|
+
try {
|
|
515
|
+
const { DENIED_TAGS } = require('../lib/fleet');
|
|
516
|
+
const refs = new Set();
|
|
517
|
+
for (const source of [String(branch || ''), String(message || '')]) {
|
|
518
|
+
for (const m of source.toUpperCase().matchAll(/([A-Z]{2,6}-\d+)/g)) refs.add(m[1]);
|
|
519
|
+
for (const m of source.toLowerCase().matchAll(/([a-z]{2,6})-(\d+)/g)) refs.add(`${m[1].toUpperCase()}-${m[2]}`);
|
|
520
|
+
}
|
|
521
|
+
if (!refs.size) return null;
|
|
522
|
+
const projectionPath = path.join(findPrimaryRoot(root), '.atris', 'state', 'tasks.projection.json');
|
|
523
|
+
if (!fs.existsSync(projectionPath)) return null;
|
|
524
|
+
const projection = JSON.parse(fs.readFileSync(projectionPath, 'utf8'));
|
|
525
|
+
for (const task of projection.tasks || []) {
|
|
526
|
+
const ids = [task.display_id, task.legacy_ref].filter(Boolean).map(v => String(v).toUpperCase());
|
|
527
|
+
if (!ids.some(id => refs.has(id))) continue;
|
|
528
|
+
const tags = [task.tag, ...(task.metadata?.todo_tags || [])].filter(Boolean).map(t => String(t).toLowerCase());
|
|
529
|
+
const hit = tags.find(t => DENIED_TAGS.includes(t));
|
|
530
|
+
if (hit) return { taskId: ids[0], tag: hit };
|
|
531
|
+
}
|
|
532
|
+
return null;
|
|
533
|
+
} catch {
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function findPrimaryRoot(root) {
|
|
539
|
+
const worktrees = listWorktrees(root);
|
|
540
|
+
return worktrees[0]?.path || root;
|
|
541
|
+
}
|
|
542
|
+
|
|
501
543
|
function shipWorktree(args) {
|
|
502
544
|
if (hasFlag(args, '--help') || hasFlag(args, '-h') || args[0] === 'help') {
|
|
503
545
|
shipHelp();
|
|
@@ -531,6 +573,17 @@ function shipWorktree(args) {
|
|
|
531
573
|
console.error('blocked: ship from an isolated worktree, not the primary checkout');
|
|
532
574
|
return 2;
|
|
533
575
|
}
|
|
576
|
+
// Denied lanes are checked at dispatch, but a flight that was staffed
|
|
577
|
+
// before a task entered a denied lane (or killed mid-flight) could still
|
|
578
|
+
// ship: a payments command self-landed twice this way on 2026-07-26. The
|
|
579
|
+
// ship gate is the last door, so it re-checks.
|
|
580
|
+
if (!hasFlag(args, '--allow-denied-lane')) {
|
|
581
|
+
const denied = deniedLaneForShip(root, branch, message);
|
|
582
|
+
if (denied) {
|
|
583
|
+
console.error(`blocked: this branch carries ${denied.taskId} which sits in the protected ${denied.tag} lane; a human lands it (or pass --allow-denied-lane on written order)`);
|
|
584
|
+
return 2;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
534
587
|
|
|
535
588
|
let oneLapProof = null;
|
|
536
589
|
try {
|
|
@@ -552,7 +605,10 @@ function shipWorktree(args) {
|
|
|
552
605
|
refreshRemoteRef(root, targetRef);
|
|
553
606
|
const skippedAdapters = restoreRegeneratedAdapterChurn(root, message, { dryRun });
|
|
554
607
|
const ignoredUnstagedFiles = dryRun ? new Set(skippedAdapters) : new Set();
|
|
555
|
-
|
|
608
|
+
// Conductor plumbing must not make the checkout look dirty, or a flight where the
|
|
609
|
+
// engine wrote nothing commits the fleet's own prompt file and calls it work.
|
|
610
|
+
const counts = statusCounts(root, { ignoredUnstagedFiles, ignoreUntracked: isConductorArtifact })
|
|
611
|
+
|| { staged: 0, unstaged: 0, untracked: 0 };
|
|
556
612
|
const dirty = counts.staged || counts.unstaged || counts.untracked;
|
|
557
613
|
if (dirty && !message) {
|
|
558
614
|
console.error('blocked: --message is required when there are local changes to commit');
|
|
@@ -563,6 +619,9 @@ function shipWorktree(args) {
|
|
|
563
619
|
console.log(`commit: ${message}`);
|
|
564
620
|
if (!dryRun) {
|
|
565
621
|
runGit(['add', '-A'], { cwd: root });
|
|
622
|
+
// `add -A` sweeps in the fleet's own prompt file and friends. Drop them from the
|
|
623
|
+
// index so a work commit carries work, not harness plumbing. Safe when absent.
|
|
624
|
+
unstageConductorArtifacts(root);
|
|
566
625
|
if (!proofTreeMatches('index')) return 3;
|
|
567
626
|
runGit(['commit', '-m', message], { cwd: root });
|
|
568
627
|
if (!proofTreeMatches('head')) return 3;
|
|
@@ -578,7 +637,12 @@ function shipWorktree(args) {
|
|
|
578
637
|
if (!dryRun) runCommand(verify, { cwd: root });
|
|
579
638
|
const afterVerifyRows = dryRun ? [] : statusSnapshot(root);
|
|
580
639
|
const verifyDelta = newStatusRows(beforeVerify, afterVerifyRows);
|
|
581
|
-
|
|
640
|
+
// This check exists to catch a verifier that writes files. Conductor plumbing was
|
|
641
|
+
// already present before the verifier ran, so counting it here blocks a clean ship.
|
|
642
|
+
const afterVerify = dryRun
|
|
643
|
+
? { staged: 0, unstaged: 0, untracked: 0 }
|
|
644
|
+
: statusCounts(root, { ignoreUntracked: isConductorArtifact })
|
|
645
|
+
|| { staged: 0, unstaged: 0, untracked: 0 };
|
|
582
646
|
if (!dryRun && (afterVerify.staged || afterVerify.unstaged || afterVerify.untracked)) {
|
|
583
647
|
console.error(
|
|
584
648
|
`blocked: verifier left checkout dirty staged=${afterVerify.staged} ` +
|
|
@@ -687,8 +751,10 @@ function guard(args) {
|
|
|
687
751
|
console.error('run: atris worktree start --member <member>|--agent <name> --task "<short task>" --claim');
|
|
688
752
|
return 2;
|
|
689
753
|
}
|
|
690
|
-
//
|
|
691
|
-
|
|
754
|
+
// Conductor plumbing (worktree metadata, the fleet prompt written for this very
|
|
755
|
+
// engine, runtime scratch, the brief ledger) must not count as dirt — otherwise the
|
|
756
|
+
// harness blocks the engine with the harness's own files.
|
|
757
|
+
const counts = statusCounts(root, { ignoreUntracked: isConductorArtifact });
|
|
692
758
|
if (counts && (counts.staged || counts.unstaged || counts.untracked) && !hasFlag(args, '--allow-dirty')) {
|
|
693
759
|
console.error(`blocked: checkout is dirty staged=${counts.staged} unstaged=${counts.unstaged} untracked=${counts.untracked}`);
|
|
694
760
|
return 3;
|
|
@@ -880,20 +946,14 @@ function worktreeCommand(args = []) {
|
|
|
880
946
|
|
|
881
947
|
module.exports = {
|
|
882
948
|
branchName,
|
|
883
|
-
statusSnapshot,
|
|
884
|
-
newStatusRows,
|
|
885
949
|
createAgentWorktree,
|
|
950
|
+
deniedLaneForShip,
|
|
886
951
|
createOrFindPr,
|
|
887
952
|
cleanupWorktrees,
|
|
888
|
-
defaultShipTarget,
|
|
889
953
|
defaultStartBase,
|
|
890
|
-
defaultWorktreePath,
|
|
891
954
|
listWorktrees,
|
|
892
955
|
parseWorktrees,
|
|
893
|
-
REGEN_ADAPTER_FILES,
|
|
894
956
|
normalizeTargetRef,
|
|
895
|
-
restoreRegeneratedAdapterChurn,
|
|
896
|
-
prMergeRef,
|
|
897
957
|
slugify,
|
|
898
958
|
statusCounts,
|
|
899
959
|
swarloClaim,
|
package/commands/xp.js
CHANGED
|
@@ -5,6 +5,7 @@ const os = require('os');
|
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const crypto = require('crypto');
|
|
7
7
|
const { spawnSync } = require('child_process');
|
|
8
|
+
const { hasFlag: hasExactFlag } = require('../lib/arg-parser');
|
|
8
9
|
|
|
9
10
|
const DEFAULT_GRAPH_DAYS = 365;
|
|
10
11
|
const MAX_SYNC_GRAPH_DAYS = 370;
|
|
@@ -277,7 +278,8 @@ function currentForm(payload) {
|
|
|
277
278
|
};
|
|
278
279
|
}
|
|
279
280
|
|
|
280
|
-
|
|
281
|
+
// Preserve the existing rule that any inline value wins over a split value.
|
|
282
|
+
function readInlineFirstFlag(args, name, fallback = null) {
|
|
281
283
|
const inline = args.find(arg => arg.startsWith(`${name}=`));
|
|
282
284
|
if (inline) {
|
|
283
285
|
return inline.slice(name.length + 1);
|
|
@@ -291,7 +293,7 @@ function readFlag(args, name, fallback = null) {
|
|
|
291
293
|
|
|
292
294
|
function readFirstFlag(args, names, fallback = null) {
|
|
293
295
|
for (const name of names) {
|
|
294
|
-
const value =
|
|
296
|
+
const value = readInlineFirstFlag(args, name, null);
|
|
295
297
|
if (value !== null && value !== undefined && value !== '') return value;
|
|
296
298
|
}
|
|
297
299
|
return fallback;
|
|
@@ -313,8 +315,9 @@ function readFlagValues(args, names) {
|
|
|
313
315
|
return values.filter(Boolean);
|
|
314
316
|
}
|
|
315
317
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
+
// This command treats --name=value as flag presence as well as a standalone flag.
|
|
319
|
+
function hasFlagOrValue(args, name) {
|
|
320
|
+
return hasExactFlag(args, name) || args.some(arg => arg.startsWith(`${name}=`));
|
|
318
321
|
}
|
|
319
322
|
|
|
320
323
|
function levelFromXp(careerXp) {
|
|
@@ -1040,7 +1043,7 @@ function buildCareerXpProjection(receipts, workspace, integrity = {}) {
|
|
|
1040
1043
|
}
|
|
1041
1044
|
|
|
1042
1045
|
function collectLocalXpProjectionState(args = [], { write = true } = {}) {
|
|
1043
|
-
const workspace = path.resolve(
|
|
1046
|
+
const workspace = path.resolve(readInlineFirstFlag(args, '--workspace', defaultXpWorkspace()));
|
|
1044
1047
|
const episodePath = path.join(workspace, TASK_EPISODES_FILE);
|
|
1045
1048
|
const receiptsPath = path.join(workspace, CAREER_XP_RECEIPTS_FILE);
|
|
1046
1049
|
const projectionPath = path.join(workspace, CAREER_XP_PROJECTION_FILE);
|
|
@@ -1170,7 +1173,7 @@ function defaultAllSearchRoots(args = []) {
|
|
|
1170
1173
|
if (explicitRoots.length) return uniquePaths(explicitRoots);
|
|
1171
1174
|
|
|
1172
1175
|
const roots = [];
|
|
1173
|
-
const workspace =
|
|
1176
|
+
const workspace = readInlineFirstFlag(args, '--workspace', null);
|
|
1174
1177
|
if (workspace) roots.push(workspace);
|
|
1175
1178
|
roots.push(process.cwd());
|
|
1176
1179
|
roots.push(path.join(os.homedir(), 'arena'));
|
|
@@ -1575,7 +1578,7 @@ function codexHasThreadsTable(dbPath) {
|
|
|
1575
1578
|
}
|
|
1576
1579
|
|
|
1577
1580
|
function readCodexGoalsForSession(args, workspace, sinceMs, untilMs) {
|
|
1578
|
-
const explicitDb =
|
|
1581
|
+
const explicitDb = readInlineFirstFlag(args, '--codex-state', readInlineFirstFlag(args, '--state', process.env.CODEX_STATE_DB || ''));
|
|
1579
1582
|
const dbPath = explicitDb
|
|
1580
1583
|
? path.resolve(expandHome(explicitDb))
|
|
1581
1584
|
: path.resolve(fs.existsSync(CODEX_GOALS_FILE) ? CODEX_GOALS_FILE : CODEX_STATE_FILE);
|
|
@@ -1592,7 +1595,7 @@ function readCodexGoalsForSession(args, workspace, sinceMs, untilMs) {
|
|
|
1592
1595
|
}
|
|
1593
1596
|
}
|
|
1594
1597
|
|
|
1595
|
-
const threadId =
|
|
1598
|
+
const threadId = readInlineFirstFlag(args, '--thread', process.env.CODEX_THREAD_ID || '');
|
|
1596
1599
|
const clauses = [];
|
|
1597
1600
|
if (threadId) clauses.push(`tg.thread_id = ${sqlString(threadId)}`);
|
|
1598
1601
|
const workspaceCandidates = workspacePathCandidates(workspace);
|
|
@@ -1632,9 +1635,9 @@ LIMIT 25
|
|
|
1632
1635
|
}
|
|
1633
1636
|
|
|
1634
1637
|
function buildCareerXpSessionCapsule(args = []) {
|
|
1635
|
-
const workspace = path.resolve(
|
|
1636
|
-
const sinceInput =
|
|
1637
|
-
const untilInput =
|
|
1638
|
+
const workspace = path.resolve(readInlineFirstFlag(args, '--workspace', defaultXpWorkspace()));
|
|
1639
|
+
const sinceInput = readInlineFirstFlag(args, '--since', 'today');
|
|
1640
|
+
const untilInput = readInlineFirstFlag(args, '--until', null);
|
|
1638
1641
|
const since = parseSessionBoundary(sinceInput, startOfToday());
|
|
1639
1642
|
const until = parseSessionBoundary(untilInput, new Date());
|
|
1640
1643
|
const sinceMs = since.getTime();
|
|
@@ -1643,7 +1646,7 @@ function buildCareerXpSessionCapsule(args = []) {
|
|
|
1643
1646
|
throw new Error('--since must be before --until');
|
|
1644
1647
|
}
|
|
1645
1648
|
|
|
1646
|
-
const writeEnabled = !
|
|
1649
|
+
const writeEnabled = !hasFlagOrValue(args, '--no-write') && !hasFlagOrValue(args, '--dry-run');
|
|
1647
1650
|
const projectionState = collectLocalXpProjectionState(['--workspace', workspace], { write: writeEnabled });
|
|
1648
1651
|
const projection = projectionState.projection;
|
|
1649
1652
|
const receiptsPath = path.join(workspace, CAREER_XP_RECEIPTS_FILE);
|
|
@@ -1675,7 +1678,7 @@ function buildCareerXpSessionCapsule(args = []) {
|
|
|
1675
1678
|
});
|
|
1676
1679
|
const windowXp = acceptedReceipts.reduce((sum, receipt) => sum + asNumber(receipt.xp), 0);
|
|
1677
1680
|
const afterTotalXp = asNumber(projection.total_xp);
|
|
1678
|
-
const missionFlag =
|
|
1681
|
+
const missionFlag = readInlineFirstFlag(args, '--mission', null);
|
|
1679
1682
|
const episodeGoals = readJsonl(path.join(workspace, TASK_EPISODES_FILE), { allowPartialTail: true })
|
|
1680
1683
|
.filter(episode => inWindow(episode?.created_at, sinceMs, untilMs))
|
|
1681
1684
|
.map(episode => episode.goal);
|
|
@@ -1890,8 +1893,8 @@ function syncScopeFields(attribution = {}) {
|
|
|
1890
1893
|
}
|
|
1891
1894
|
|
|
1892
1895
|
function publicAgentXpOverride(args = []) {
|
|
1893
|
-
if (
|
|
1894
|
-
if (
|
|
1896
|
+
if (hasFlagOrValue(args, '--public')) return true;
|
|
1897
|
+
if (hasFlagOrValue(args, '--private') || hasFlagOrValue(args, '--internal')) return false;
|
|
1895
1898
|
return null;
|
|
1896
1899
|
}
|
|
1897
1900
|
|
|
@@ -1946,9 +1949,9 @@ function syncPlayer(args, projection) {
|
|
|
1946
1949
|
}
|
|
1947
1950
|
|
|
1948
1951
|
function buildAgentXpSyncPacket(args = []) {
|
|
1949
|
-
const localMode =
|
|
1952
|
+
const localMode = hasFlagOrValue(args, '--local') || hasFlagOrValue(args, '--workspace') || hasFlagOrValue(args, '--operator');
|
|
1950
1953
|
const projectionArgs = args.filter(arg => !['--dry-run', '--no-post', '--packet', '--public', '--private', '--internal'].includes(arg));
|
|
1951
|
-
const projection =
|
|
1954
|
+
const projection = hasFlagOrValue(args, '--all') || !localMode
|
|
1952
1955
|
? collectAllLocalXpProjection(projectionArgs)
|
|
1953
1956
|
: collectLocalXpProjection(projectionArgs);
|
|
1954
1957
|
const player = syncPlayer(args, projection);
|
|
@@ -2086,10 +2089,10 @@ function buildAgentXpSyncPacket(args = []) {
|
|
|
2086
2089
|
|
|
2087
2090
|
async function syncAgentXp(args = []) {
|
|
2088
2091
|
const preview = buildAgentXpSyncPacket(args);
|
|
2089
|
-
const dryRun =
|
|
2092
|
+
const dryRun = hasFlagOrValue(args, '--dry-run') || hasFlagOrValue(args, '--no-post') || hasFlagOrValue(args, '--packet');
|
|
2090
2093
|
if (dryRun) return preview;
|
|
2091
2094
|
|
|
2092
|
-
const token =
|
|
2095
|
+
const token = readInlineFirstFlag(args, '--token', process.env.ATRIS_AGENTXP_SYNC_TOKEN || process.env.AGENTXP_SYNC_TOKEN || '');
|
|
2093
2096
|
const envToken = process.env.ATRIS_TOKEN && process.env.ATRIS_TOKEN.trim() ? process.env.ATRIS_TOKEN.trim() : '';
|
|
2094
2097
|
const options = {
|
|
2095
2098
|
method: 'POST',
|
|
@@ -2298,11 +2301,11 @@ async function xpCommand(...args) {
|
|
|
2298
2301
|
const commandArgs = args.slice(1);
|
|
2299
2302
|
let payload;
|
|
2300
2303
|
try {
|
|
2301
|
-
const explicitLocal =
|
|
2302
|
-
||
|
|
2303
|
-
||
|
|
2304
|
+
const explicitLocal = hasFlagOrValue(commandArgs, '--local')
|
|
2305
|
+
|| hasFlagOrValue(commandArgs, '--workspace')
|
|
2306
|
+
|| hasFlagOrValue(commandArgs, '--operator');
|
|
2304
2307
|
const accountStatus = (subcommand === 'status' || subcommand === 'card') && !explicitLocal;
|
|
2305
|
-
payload =
|
|
2308
|
+
payload = hasFlagOrValue(commandArgs, '--all') || accountStatus
|
|
2306
2309
|
? collectAllLocalXpProjection(commandArgs)
|
|
2307
2310
|
: collectLocalXpProjection(commandArgs);
|
|
2308
2311
|
} catch (error) {
|
|
@@ -2324,7 +2327,7 @@ async function xpCommand(...args) {
|
|
|
2324
2327
|
}
|
|
2325
2328
|
|
|
2326
2329
|
const jsonMode = args.includes('--json');
|
|
2327
|
-
const localMode =
|
|
2330
|
+
const localMode = hasFlagOrValue(args, '--local') || hasFlagOrValue(args, '--workspace') || hasFlagOrValue(args, '--operator');
|
|
2328
2331
|
if (localMode) {
|
|
2329
2332
|
let payload;
|
|
2330
2333
|
try {
|
|
@@ -2353,6 +2356,7 @@ async function xpCommand(...args) {
|
|
|
2353
2356
|
});
|
|
2354
2357
|
if (!result.ok) {
|
|
2355
2358
|
console.error(`Failed to load XP graph: ${result.error || result.status}`);
|
|
2359
|
+
console.error('Run: atris xp --local to render your AgentXP from proof receipts in this workspace.');
|
|
2356
2360
|
process.exit(1);
|
|
2357
2361
|
}
|
|
2358
2362
|
|
|
@@ -2366,14 +2370,8 @@ async function xpCommand(...args) {
|
|
|
2366
2370
|
|
|
2367
2371
|
module.exports = {
|
|
2368
2372
|
xpCommand,
|
|
2369
|
-
buildContributionRows,
|
|
2370
2373
|
buildCareerXpProjection,
|
|
2371
|
-
buildAllCareerXpProjection,
|
|
2372
|
-
buildCareerXpSessionCapsule,
|
|
2373
|
-
collectAllLocalXpProjection,
|
|
2374
2374
|
collectLocalXpProjection,
|
|
2375
|
-
buildAgentXpSyncPacket,
|
|
2376
2375
|
syncAgentXp,
|
|
2377
|
-
receiptFromTaskEpisode,
|
|
2378
2376
|
render,
|
|
2379
2377
|
};
|
package/commands/youtube.js
CHANGED
|
@@ -452,13 +452,11 @@ async function youtubeCommand(argv = process.argv.slice(3), deps = {}) {
|
|
|
452
452
|
module.exports = {
|
|
453
453
|
DEFAULT_QUERY,
|
|
454
454
|
DEFAULT_TIMEOUT_MS,
|
|
455
|
-
showYoutubeHelp,
|
|
456
455
|
parseYoutubeArgs,
|
|
457
456
|
buildYoutubePayload,
|
|
458
457
|
extractLocalTranscript,
|
|
459
458
|
processYoutube,
|
|
460
459
|
shouldRetryWithLocalTranscript,
|
|
461
460
|
formatYoutubeResult,
|
|
462
|
-
formatTimestampFromMs,
|
|
463
461
|
youtubeCommand,
|
|
464
462
|
};
|