atris 3.57.3 → 3.58.5
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 +12 -1
- package/atris/skills/clawhub/member-runtime/SKILL.md +9 -9
- package/atris/skills/create-member/SKILL.md +16 -13
- package/atris/skills/design/SKILL.md +27 -4
- package/atris/skills/engines/SKILL.md +57 -13
- package/atris/skills/x-search/SKILL.md +13 -5
- package/atris/skills/youtube/SKILL.md +39 -8
- package/atris/team/brainstormer/MEMBER.md +2 -2
- package/atris/team/executor/MEMBER.md +3 -3
- package/atris/team/launcher/MEMBER.md +4 -4
- package/atris/team/navigator/MEMBER.md +3 -3
- package/atris/team/researcher/MEMBER.md +2 -2
- package/atris/team/validator/MEMBER.md +1 -1
- package/atris.md +14 -0
- package/ax +157 -24
- package/bin/atris.js +79 -31
- package/commands/aeo.js +4 -4
- package/commands/agents.js +1 -1
- package/commands/align.js +12 -12
- package/commands/analytics.js +3 -3
- package/commands/app.js +5 -5
- package/commands/ask.js +175 -0
- package/commands/auth.js +7 -7
- package/commands/autoland.js +29 -5
- package/commands/autopilot-front.js +6 -6
- package/commands/autopilot.js +90 -90
- package/commands/avail.js +5 -5
- package/commands/bench.js +63 -1
- package/commands/browse.js +2 -2
- package/commands/business.js +12 -12
- package/commands/card.js +2 -2
- package/commands/clean.js +13 -13
- package/commands/close.js +297 -15
- package/commands/compile.js +13 -13
- package/commands/computer.js +94 -27
- package/commands/console.js +5 -5
- package/commands/decide.js +1 -1
- package/commands/deck.js +9 -9
- package/commands/drive.js +4 -4
- package/commands/engine.js +173 -19
- package/commands/errors.js +3 -3
- package/commands/experiments.js +18 -1
- package/commands/feed.js +2 -2
- package/commands/feedback.js +4 -4
- package/commands/fleet.js +12 -12
- package/commands/game.js +1 -1
- package/commands/guide.js +49 -0
- package/commands/improve.js +19 -17
- package/commands/init.js +67 -19
- package/commands/interview.js +7 -7
- package/commands/land.js +40 -15
- package/commands/learn.js +12 -12
- package/commands/lesson.js +2 -2
- package/commands/log.js +1 -1
- package/commands/loops.js +4 -4
- package/commands/member.js +75 -50
- package/commands/mission.js +97 -98
- package/commands/one-lap.js +1 -1
- package/commands/pack.js +74 -8
- package/commands/plugin.js +6 -6
- package/commands/probe.js +9 -9
- package/commands/pull.js +35 -35
- package/commands/pulse.js +1 -1
- package/commands/push.js +21 -21
- package/commands/rainmaker.js +1 -1
- package/commands/recap.js +7 -7
- package/commands/reel.js +2 -2
- package/commands/release.js +3 -3
- package/commands/report.js +1 -1
- package/commands/review.js +5 -5
- package/commands/revisions.js +1 -1
- package/commands/run.js +1 -1
- package/commands/scout.js +3 -3
- package/commands/security-review.js +7 -7
- package/commands/serve.js +9 -9
- package/commands/setup.js +1 -1
- package/commands/sign.js +4 -4
- package/commands/signup.js +4 -4
- package/commands/site.js +8 -3
- package/commands/skill.js +6 -6
- package/commands/slop.js +18 -18
- package/commands/social.js +3 -3
- package/commands/soul.js +14 -14
- package/commands/spaceship.js +1 -1
- package/commands/status.js +2 -2
- package/commands/strings.js +13 -13
- package/commands/study.js +4 -4
- package/commands/sync.js +8 -8
- package/commands/task.js +50 -25
- package/commands/teach.js +2 -2
- package/commands/team.js +1 -1
- package/commands/theme.js +2 -2
- package/commands/tree.js +34 -0
- package/commands/truth.js +7 -7
- package/commands/verify.js +12 -12
- package/commands/visualize.js +2 -2
- package/commands/workflow.js +97 -40
- package/commands/worktree.js +57 -1
- package/commands/write.js +9 -9
- package/commands/x-search.js +304 -15
- package/commands/xp.js +6 -3
- package/commands/youtube.js +916 -65
- package/lib/accept-verify-gate.js +3 -3
- package/lib/activity-stream.js +1 -1
- package/lib/apply-gate.js +13 -0
- package/lib/auto-accept-certified.js +28 -12
- package/lib/autoland.js +6 -6
- package/lib/bench/engines.js +59 -8
- package/lib/bench/report.js +1 -1
- package/lib/bench/runner.js +204 -5
- package/lib/bench/tree-render.js +76 -0
- package/lib/card.js +1 -1
- package/lib/chat-log-scan.js +1 -1
- package/lib/claude-boot-block.js +2 -0
- package/lib/deck-compose.js +1 -1
- package/lib/deck-history.js +2 -2
- package/lib/deck-layout.js +1 -1
- package/lib/deck-review.js +5 -5
- package/lib/deck-schema.js +1 -1
- package/lib/default-verifier.js +1 -1
- package/lib/developer-api.js +3 -0
- package/lib/engine-ask.js +66 -9
- package/lib/engine-registry.js +15 -2
- package/lib/file-ops.js +1 -1
- package/lib/fleet.js +71 -35
- package/lib/html-render.js +1 -1
- package/lib/intents.js +172 -0
- package/lib/journal.js +1 -1
- package/lib/known-commands.js +2 -2
- package/lib/land-green.js +364 -0
- package/lib/lesson-contradiction.js +2 -2
- package/lib/lesson-preflight.js +1 -1
- package/lib/manifest.js +5 -5
- package/lib/member-alive.js +9 -5
- package/lib/memory-view.js +3 -3
- package/lib/mission-protected-lane.js +1 -1
- package/lib/mission-root.js +2 -2
- package/lib/orb-context.js +10 -10
- package/lib/pack-capabilities.js +861 -3
- package/lib/permission-grants.js +2 -2
- package/lib/policy-lessons.js +6 -6
- package/lib/pulse.js +8 -8
- package/lib/receipt-evidence.js +1 -1
- package/lib/reel.js +1 -1
- package/lib/review-integrity.js +1 -1
- package/lib/runner-command.js +35 -5
- package/lib/scorecard.js +4 -4
- package/lib/security-scan.js +5 -5
- package/lib/self-drive.js +1 -1
- package/lib/site-publish.js +372 -0
- package/lib/site.js +1 -1
- package/lib/slides-deck.js +2 -2
- package/lib/state-detection.js +2 -2
- package/lib/sync-telemetry.js +2 -2
- package/lib/task-db.js +17 -10
- package/lib/task-explanation.js +1 -1
- package/lib/task-proof.js +2 -2
- package/lib/task-receipt.js +3 -3
- package/lib/theme.js +12 -12
- package/lib/todo-fallback.js +3 -3
- package/lib/todo-sections.js +1 -1
- package/lib/tree-hash.js +79 -0
- package/lib/usage.js +1 -1
- package/lib/wish-delegate.js +1 -1
- package/lib/workspace-safety.js +4 -4
- package/lib/workspace-scaffold.js +3 -3
- package/package.json +1 -1
- package/scripts/det/concierge-bakeoff.sh +26 -0
- package/scripts/det/ytquote-repair.js +0 -1
- package/scripts/det/ytrail-race.js +0 -5
- package/scripts/member-operate.mjs +52 -14
- package/scripts/outbound-artifact-gate.js +0 -3
- package/utils/auth.js +0 -3
- package/utils/config.js +0 -1
- package/scripts/det/checklist-score.js +0 -191
package/commands/x-search.js
CHANGED
|
@@ -1,26 +1,44 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
3
5
|
const { apiRequestJson } = require('../utils/api');
|
|
4
6
|
const { ensureBilledCommandAuth } = require('./auth');
|
|
5
7
|
const applyGate = require('../lib/apply-gate');
|
|
8
|
+
const {
|
|
9
|
+
fileTeachExperiment,
|
|
10
|
+
extractTeachNumbers,
|
|
11
|
+
extractTeachMechanisms,
|
|
12
|
+
isThinTeachLesson,
|
|
13
|
+
TEACH_THIN_REFUSE,
|
|
14
|
+
printLearnerCheckGate,
|
|
15
|
+
proveSavedLearnerBaseline,
|
|
16
|
+
} = require('./youtube');
|
|
6
17
|
|
|
7
18
|
const DEFAULT_TIMEOUT_MS = 120000;
|
|
8
19
|
const COST_HINT = '5 credits per search';
|
|
9
20
|
const APPLY_NEXT_MESSAGE =
|
|
10
21
|
'next: write one apply (change + receipt) for this query.';
|
|
22
|
+
const KEEP_RULE = 'keep only if measure.py moves 0→1. scores 1 only when the fixture contains the check tokens.';
|
|
11
23
|
|
|
12
24
|
function showXSearchHelp(output = console.log, commandName = 'atris x-search') {
|
|
13
25
|
output('');
|
|
14
|
-
output(`Usage: ${commandName} "<query>" [--limit N] [--days N] [--json]`);
|
|
15
|
-
output(` ${commandName} person --name <name> [--handle <h>] [--company <c>] [--context <text>] [--json]`);
|
|
26
|
+
output(`Usage: ${commandName} "<query>" [--limit N] [--days N] [--save] [--json]`);
|
|
27
|
+
output(` ${commandName} person --name <name> [--handle <h>] [--company <c>] [--context <text>] [--save] [--json]`);
|
|
28
|
+
output(` ${commandName} unsave <query-or-source>`);
|
|
16
29
|
output('');
|
|
17
30
|
output(`Search X/Twitter via Atris (${COST_HINT}).`);
|
|
18
31
|
output('Requires login. Same auth path as atris youtube process.');
|
|
32
|
+
output('Prints to stdout. Rich ephemeral prints one apply next-step, then hands off to atris youtube search (no files).');
|
|
33
|
+
output('--save files a brief only when the result is rich.');
|
|
34
|
+
output('unsave deletes the filed brief, apply stub, and matching experiment pack (no paid calls).');
|
|
19
35
|
output('Empty or failed search refunds the credits.');
|
|
20
36
|
output('');
|
|
21
37
|
output('Options:');
|
|
22
38
|
output(' --limit <n> Max results hint (search only)');
|
|
23
39
|
output(' --days <n> Only tweets from the last N days (search only)');
|
|
40
|
+
output(' --save File brief, journal, apply; rich results mint a keep/revert experiment');
|
|
41
|
+
output(' --unsave Delete filed brief, apply stub, and matching experiment pack (no paid calls)');
|
|
24
42
|
output(' --json Print the raw JSON response');
|
|
25
43
|
output(' -h, --help This help');
|
|
26
44
|
output('');
|
|
@@ -33,7 +51,10 @@ function showXSearchHelp(output = console.log, commandName = 'atris x-search') {
|
|
|
33
51
|
output('Examples:');
|
|
34
52
|
output(` ${commandName} "MCP agents"`);
|
|
35
53
|
output(` ${commandName} "MCP agents" --limit 5 --days 2`);
|
|
54
|
+
output(` ${commandName} "MCP agents" --save`);
|
|
36
55
|
output(` ${commandName} person --name "Leah Bonvissuto" --handle leahbon`);
|
|
56
|
+
output(` ${commandName} unsave "MCP agents"`);
|
|
57
|
+
output(` ${commandName} --unsave "MCP agents"`);
|
|
37
58
|
output('');
|
|
38
59
|
}
|
|
39
60
|
|
|
@@ -58,6 +79,8 @@ function parseSearchArgs(argv = []) {
|
|
|
58
79
|
mode: 'search',
|
|
59
80
|
help: false,
|
|
60
81
|
json: false,
|
|
82
|
+
save: false,
|
|
83
|
+
unsave: false,
|
|
61
84
|
query: null,
|
|
62
85
|
limit: null,
|
|
63
86
|
daysBack: null,
|
|
@@ -75,6 +98,10 @@ function parseSearchArgs(argv = []) {
|
|
|
75
98
|
options.help = true;
|
|
76
99
|
} else if (arg === '--json') {
|
|
77
100
|
options.json = true;
|
|
101
|
+
} else if (arg === '--save') {
|
|
102
|
+
options.save = true;
|
|
103
|
+
} else if (arg === '--unsave') {
|
|
104
|
+
options.unsave = true;
|
|
78
105
|
} else if (arg === '--limit') {
|
|
79
106
|
options.limit = parsePositiveInt(readValue(args, i, arg), '--limit');
|
|
80
107
|
i++;
|
|
@@ -111,7 +138,11 @@ function parseSearchArgs(argv = []) {
|
|
|
111
138
|
}
|
|
112
139
|
|
|
113
140
|
if (options.help) return options;
|
|
114
|
-
if (!options.query)
|
|
141
|
+
if (!options.query) {
|
|
142
|
+
throw new Error(options.unsave
|
|
143
|
+
? 'usage: atris x-search unsave <query-or-source>'
|
|
144
|
+
: 'Missing query. Run "atris x-search --help".');
|
|
145
|
+
}
|
|
115
146
|
return options;
|
|
116
147
|
}
|
|
117
148
|
|
|
@@ -121,6 +152,7 @@ function parsePersonArgs(argv = []) {
|
|
|
121
152
|
mode: 'person',
|
|
122
153
|
help: false,
|
|
123
154
|
json: false,
|
|
155
|
+
save: false,
|
|
124
156
|
name: null,
|
|
125
157
|
handle: null,
|
|
126
158
|
company: null,
|
|
@@ -139,6 +171,8 @@ function parsePersonArgs(argv = []) {
|
|
|
139
171
|
options.help = true;
|
|
140
172
|
} else if (arg === '--json') {
|
|
141
173
|
options.json = true;
|
|
174
|
+
} else if (arg === '--save') {
|
|
175
|
+
options.save = true;
|
|
142
176
|
} else if (arg === '--name') {
|
|
143
177
|
options.name = readValue(args, i, arg);
|
|
144
178
|
i++;
|
|
@@ -184,11 +218,53 @@ function parsePersonArgs(argv = []) {
|
|
|
184
218
|
return options;
|
|
185
219
|
}
|
|
186
220
|
|
|
221
|
+
function parseUnsaveArgs(argv = []) {
|
|
222
|
+
const args = [...argv];
|
|
223
|
+
const options = {
|
|
224
|
+
mode: 'unsave',
|
|
225
|
+
help: false,
|
|
226
|
+
json: false,
|
|
227
|
+
source: null,
|
|
228
|
+
sources: [],
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
if (args.length === 0 || ['help', '--help', '-h'].includes(args[0])) {
|
|
232
|
+
if (args.length === 0) {
|
|
233
|
+
throw new Error('usage: atris x-search unsave <query-or-source>');
|
|
234
|
+
}
|
|
235
|
+
options.help = true;
|
|
236
|
+
return options;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
for (let i = 0; i < args.length; i++) {
|
|
240
|
+
const arg = args[i];
|
|
241
|
+
if (arg === '--help' || arg === '-h' || arg === 'help') {
|
|
242
|
+
options.help = true;
|
|
243
|
+
} else if (arg === '--json') {
|
|
244
|
+
options.json = true;
|
|
245
|
+
} else if (arg === '--unsave') {
|
|
246
|
+
continue;
|
|
247
|
+
} else if (arg.startsWith('-')) {
|
|
248
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
249
|
+
} else {
|
|
250
|
+
options.sources.push(arg);
|
|
251
|
+
if (!options.source) options.source = arg;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (options.help) return options;
|
|
256
|
+
if (!options.sources.length) throw new Error('usage: atris x-search unsave <query-or-source>');
|
|
257
|
+
return options;
|
|
258
|
+
}
|
|
259
|
+
|
|
187
260
|
function parseXSearchArgs(argv = []) {
|
|
188
261
|
const args = [...argv];
|
|
189
262
|
if (args[0] === 'person') {
|
|
190
263
|
return parsePersonArgs(args.slice(1));
|
|
191
264
|
}
|
|
265
|
+
if (args[0] === 'unsave') {
|
|
266
|
+
return parseUnsaveArgs(args.slice(1));
|
|
267
|
+
}
|
|
192
268
|
return parseSearchArgs(args);
|
|
193
269
|
}
|
|
194
270
|
|
|
@@ -258,6 +334,12 @@ function creditsWereRefunded(credits) {
|
|
|
258
334
|
return typeof credits.refunded === 'number' && credits.refunded > 0;
|
|
259
335
|
}
|
|
260
336
|
|
|
337
|
+
function creditsRefundedExplicitly(credits) {
|
|
338
|
+
if (!credits) return false;
|
|
339
|
+
if (credits.refunded === true) return true;
|
|
340
|
+
return typeof credits.refunded === 'number' && credits.refunded > 0;
|
|
341
|
+
}
|
|
342
|
+
|
|
261
343
|
function formatCreditsLines(credits) {
|
|
262
344
|
const lines = [];
|
|
263
345
|
if (credits.used !== undefined || credits.remaining !== undefined) {
|
|
@@ -332,23 +414,182 @@ function xSearchApplySource(options) {
|
|
|
332
414
|
return options?.query;
|
|
333
415
|
}
|
|
334
416
|
|
|
417
|
+
function quoteYoutubeSearchQuery(source) {
|
|
418
|
+
return `"${String(source || '').replace(/"/g, '')}"`;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function printYoutubeSearchNext(source, output) {
|
|
422
|
+
if (!source) return;
|
|
423
|
+
output(`next: atris youtube search ${quoteYoutubeSearchQuery(source)}`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const EMPTY_YOUTUBE_SEARCH_NEXT = 'next: atris youtube search " "';
|
|
427
|
+
|
|
428
|
+
function printEmptyYoutubeSearchNext(output) {
|
|
429
|
+
output(EMPTY_YOUTUBE_SEARCH_NEXT);
|
|
430
|
+
}
|
|
431
|
+
|
|
335
432
|
function xSearchApplyRel(source) {
|
|
336
433
|
return applyGate.applySidecarRel('x-search', applyGate.applySlug(source));
|
|
337
434
|
}
|
|
338
435
|
|
|
436
|
+
function xSearchExperimentSlug(source) {
|
|
437
|
+
return `x-search-${applyGate.applySlug(source)}`;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function xSearchExperimentRel(source) {
|
|
441
|
+
return `atris/experiments/${xSearchExperimentSlug(source)}`;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function xSearchBriefRel(source) {
|
|
445
|
+
return `atris/wiki/briefs/x-search-${applyGate.applySlug(source)}.md`;
|
|
446
|
+
}
|
|
447
|
+
|
|
339
448
|
function xSearchHasResults(data) {
|
|
340
449
|
return Boolean(xSearchContent(data)) || xSearchCitations(data).length > 0;
|
|
341
450
|
}
|
|
342
451
|
|
|
343
|
-
function
|
|
452
|
+
function xSearchLessonFromText(text) {
|
|
453
|
+
const body = String(text || '');
|
|
454
|
+
return {
|
|
455
|
+
numbers: extractTeachNumbers(body),
|
|
456
|
+
mechanisms: extractTeachMechanisms(body),
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function dateStamp(now) {
|
|
461
|
+
if (typeof now === 'string' && /^\d{4}-\d{2}-\d{2}/.test(now)) {
|
|
462
|
+
return now.slice(0, 10);
|
|
463
|
+
}
|
|
464
|
+
const value = now instanceof Date ? now : new Date(now || Date.now());
|
|
465
|
+
if (Number.isNaN(value.getTime())) return new Date().toISOString().slice(0, 10);
|
|
466
|
+
return value.toISOString().slice(0, 10);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
function fileXSearchBrief({ cwd, source, text, now } = {}) {
|
|
470
|
+
try {
|
|
471
|
+
if (!source || !cwd) return null;
|
|
472
|
+
const wikiDir = path.join(cwd, 'atris', 'wiki');
|
|
473
|
+
if (!fs.existsSync(wikiDir)) return null;
|
|
474
|
+
const rel = xSearchBriefRel(source);
|
|
475
|
+
fs.mkdirSync(path.join(cwd, 'atris', 'wiki', 'briefs'), { recursive: true });
|
|
476
|
+
const date = dateStamp(now);
|
|
477
|
+
const heading = String(source).trim().toLowerCase() || 'x search';
|
|
478
|
+
const header = [
|
|
479
|
+
heading,
|
|
480
|
+
'',
|
|
481
|
+
`date: ${date}`,
|
|
482
|
+
`source: ${source}`,
|
|
483
|
+
'rail: atris x-search',
|
|
484
|
+
].join('\n');
|
|
485
|
+
fs.writeFileSync(path.join(cwd, rel), `${header}\n\n${String(text || '').trim()}\n`);
|
|
486
|
+
|
|
487
|
+
const journalPath = path.join(cwd, 'atris', 'logs', date.slice(0, 4), `${date}.md`);
|
|
488
|
+
fs.mkdirSync(path.dirname(journalPath), { recursive: true });
|
|
489
|
+
let existing = '';
|
|
490
|
+
if (fs.existsSync(journalPath)) existing = fs.readFileSync(journalPath, 'utf8');
|
|
491
|
+
const line = `- [claimable] searched: ${heading} -> ${rel}`;
|
|
492
|
+
if (!existing.includes(line)) {
|
|
493
|
+
const prefix = existing && !existing.endsWith('\n') ? '\n' : '';
|
|
494
|
+
fs.writeFileSync(journalPath, `${existing}${prefix}${line}\n`);
|
|
495
|
+
}
|
|
496
|
+
return rel;
|
|
497
|
+
} catch {
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function removeUnsaveRel(cwd, rel, removed) {
|
|
503
|
+
const abs = path.join(cwd, rel);
|
|
504
|
+
try {
|
|
505
|
+
if (!fs.existsSync(abs)) return;
|
|
506
|
+
const st = fs.lstatSync(abs);
|
|
507
|
+
if (st.isDirectory()) fs.rmSync(abs, { recursive: true, force: true });
|
|
508
|
+
else fs.unlinkSync(abs);
|
|
509
|
+
removed.push(rel);
|
|
510
|
+
} catch {
|
|
511
|
+
// already gone or unreadable: do not error
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function unsaveXSearch(target, deps = {}) {
|
|
516
|
+
const output = deps.output || ((line = '') => console.log(line));
|
|
517
|
+
const cwd = deps.cwd || process.cwd();
|
|
518
|
+
const source = String(target || '').trim();
|
|
519
|
+
if (!source) {
|
|
520
|
+
output('usage: atris x-search unsave <query-or-source>');
|
|
521
|
+
return 2;
|
|
522
|
+
}
|
|
523
|
+
const briefRel = xSearchBriefRel(source);
|
|
524
|
+
const applyRel = xSearchApplyRel(source);
|
|
525
|
+
const packRel = xSearchExperimentRel(source);
|
|
526
|
+
const removed = [];
|
|
527
|
+
for (const rel of [briefRel, applyRel, packRel]) {
|
|
528
|
+
removeUnsaveRel(cwd, rel, removed);
|
|
529
|
+
}
|
|
530
|
+
if (!removed.length) {
|
|
531
|
+
output(`already gone: ${briefRel} and ${applyRel}`);
|
|
532
|
+
return 0;
|
|
533
|
+
}
|
|
534
|
+
output(`removed ${removed.join(' and ')}`);
|
|
535
|
+
return 0;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function unsaveTargets(options) {
|
|
539
|
+
if (Array.isArray(options?.sources) && options.sources.length) return options.sources;
|
|
540
|
+
const single = options?.source || options?.query;
|
|
541
|
+
return single ? [single] : [];
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function runXSearchUnsave(options, deps = {}) {
|
|
545
|
+
const output = deps.output || ((line = '') => console.log(line));
|
|
546
|
+
const targets = unsaveTargets(options);
|
|
547
|
+
if (!targets.length) {
|
|
548
|
+
output('usage: atris x-search unsave <query-or-source>');
|
|
549
|
+
return 2;
|
|
550
|
+
}
|
|
551
|
+
let code = 0;
|
|
552
|
+
for (const target of targets) {
|
|
553
|
+
const status = unsaveXSearch(target, deps);
|
|
554
|
+
if (status !== 0) code = status;
|
|
555
|
+
}
|
|
556
|
+
if (code === 0 && options.json !== true && deps.json !== true) {
|
|
557
|
+
printEmptyYoutubeSearchNext(output);
|
|
558
|
+
}
|
|
559
|
+
return code;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function saveRichXSearch({ cwd, source, text, now } = {}) {
|
|
563
|
+
const lesson = xSearchLessonFromText(text);
|
|
564
|
+
if (isThinTeachLesson(lesson)) {
|
|
565
|
+
return { thin: true, brief: null, packRel: null, lesson };
|
|
566
|
+
}
|
|
567
|
+
const brief = fileXSearchBrief({ cwd, source, text, now });
|
|
568
|
+
const packRel = fileTeachExperiment({
|
|
569
|
+
cwd,
|
|
570
|
+
lesson,
|
|
571
|
+
slug: source ? xSearchExperimentSlug(source) : null,
|
|
572
|
+
applyRel: source ? xSearchApplyRel(source) : null,
|
|
573
|
+
});
|
|
574
|
+
return { thin: false, brief, packRel, lesson };
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function ensureXSearchApply({ cwd, source, packRel, now, output } = {}) {
|
|
578
|
+
const pack = packRel || (source ? xSearchExperimentRel(source) : null);
|
|
579
|
+
const slug = pack ? path.basename(pack) : null;
|
|
344
580
|
return applyGate.ensureApply({
|
|
345
581
|
cwd,
|
|
346
582
|
source,
|
|
347
583
|
rel: source ? xSearchApplyRel(source) : null,
|
|
348
584
|
now,
|
|
349
585
|
output,
|
|
350
|
-
incompleteMessage:
|
|
586
|
+
incompleteMessage: slug
|
|
587
|
+
? `next: atris experiments keep ${slug}`
|
|
588
|
+
: APPLY_NEXT_MESSAGE,
|
|
351
589
|
required: false,
|
|
590
|
+
change: pack ? `apply ${pack}` : undefined,
|
|
591
|
+
receipt: pack ? KEEP_RULE : undefined,
|
|
592
|
+
journalLine: pack ? `- [claimable] apply: ${pack}. ${KEEP_RULE}` : undefined,
|
|
352
593
|
});
|
|
353
594
|
}
|
|
354
595
|
|
|
@@ -407,6 +648,10 @@ async function xSearchCommand(argv = process.argv.slice(3), deps = {}) {
|
|
|
407
648
|
return 0;
|
|
408
649
|
}
|
|
409
650
|
|
|
651
|
+
if (options.mode === 'unsave' || options.unsave) {
|
|
652
|
+
return runXSearchUnsave(options, deps);
|
|
653
|
+
}
|
|
654
|
+
|
|
410
655
|
let status = 0;
|
|
411
656
|
try {
|
|
412
657
|
const data = await runXSearch(options, deps);
|
|
@@ -416,16 +661,58 @@ async function xSearchCommand(argv = process.argv.slice(3), deps = {}) {
|
|
|
416
661
|
} else {
|
|
417
662
|
output(hasResults ? formatXSearchResult(data) : formatEmptyXSearchResult(data));
|
|
418
663
|
}
|
|
419
|
-
if (hasResults) {
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
output
|
|
664
|
+
if (!hasResults) {
|
|
665
|
+
if (
|
|
666
|
+
!options.json
|
|
667
|
+
&& !options.save
|
|
668
|
+
&& !creditsRefundedExplicitly(xSearchCredits(data))
|
|
669
|
+
) {
|
|
670
|
+
printEmptyYoutubeSearchNext(output);
|
|
671
|
+
}
|
|
672
|
+
status = 2;
|
|
673
|
+
} else if (options.save) {
|
|
674
|
+
const source = xSearchApplySource(options);
|
|
675
|
+
const cwd = deps.cwd || process.cwd();
|
|
676
|
+
const saved = saveRichXSearch({
|
|
677
|
+
cwd,
|
|
678
|
+
source,
|
|
679
|
+
text: xSearchContent(data),
|
|
680
|
+
now: deps.applyNow || deps.now,
|
|
426
681
|
});
|
|
682
|
+
if (saved.thin) {
|
|
683
|
+
output(TEACH_THIN_REFUSE);
|
|
684
|
+
status = 2;
|
|
685
|
+
} else {
|
|
686
|
+
const ensureApply = deps.ensureApply || ensureXSearchApply;
|
|
687
|
+
const applyCode = ensureApply({
|
|
688
|
+
cwd,
|
|
689
|
+
source,
|
|
690
|
+
packRel: saved.packRel,
|
|
691
|
+
now: deps.applyNow || deps.now,
|
|
692
|
+
output,
|
|
693
|
+
});
|
|
694
|
+
if (deps.ensureApply) {
|
|
695
|
+
status = applyCode;
|
|
696
|
+
} else {
|
|
697
|
+
const baseline = proveSavedLearnerBaseline({
|
|
698
|
+
cwd,
|
|
699
|
+
applyRel: source ? xSearchApplyRel(source) : null,
|
|
700
|
+
lesson: saved.lesson,
|
|
701
|
+
output,
|
|
702
|
+
json: options.json === true,
|
|
703
|
+
});
|
|
704
|
+
status = baseline !== 0 ? baseline : applyCode;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
427
707
|
} else {
|
|
428
|
-
|
|
708
|
+
const lesson = xSearchLessonFromText(xSearchContent(data));
|
|
709
|
+
const json = options.json === true;
|
|
710
|
+
if (!json && !isThinTeachLesson(lesson)) {
|
|
711
|
+
applyGate.hintEphemeralApply(output, 'x-search');
|
|
712
|
+
}
|
|
713
|
+
printLearnerCheckGate(output, lesson, { includeCheck: true, json });
|
|
714
|
+
if (!json) printYoutubeSearchNext(xSearchApplySource(options), output);
|
|
715
|
+
status = 0;
|
|
429
716
|
}
|
|
430
717
|
} catch (err) {
|
|
431
718
|
output(err.message);
|
|
@@ -439,12 +726,14 @@ async function xSearchCommand(argv = process.argv.slice(3), deps = {}) {
|
|
|
439
726
|
|
|
440
727
|
module.exports = {
|
|
441
728
|
DEFAULT_TIMEOUT_MS,
|
|
442
|
-
APPLY_NEXT_MESSAGE,
|
|
443
729
|
parseXSearchArgs,
|
|
444
730
|
buildSearchPayload,
|
|
445
731
|
buildPersonPayload,
|
|
446
|
-
formatXSearchResult,
|
|
447
732
|
xSearchHasResults,
|
|
448
733
|
xSearchApplyRel,
|
|
734
|
+
xSearchBriefRel,
|
|
735
|
+
xSearchExperimentSlug,
|
|
736
|
+
xSearchExperimentRel,
|
|
737
|
+
unsaveXSearch,
|
|
449
738
|
xSearchCommand,
|
|
450
739
|
};
|
package/commands/xp.js
CHANGED
|
@@ -6,6 +6,7 @@ const path = require('path');
|
|
|
6
6
|
const crypto = require('crypto');
|
|
7
7
|
const { spawnSync } = require('child_process');
|
|
8
8
|
const { hasFlag: hasExactFlag } = require('../lib/arg-parser');
|
|
9
|
+
const { treeHashFor } = require('../lib/tree-hash');
|
|
9
10
|
|
|
10
11
|
const DEFAULT_GRAPH_DAYS = 365;
|
|
11
12
|
const MAX_SYNC_GRAPH_DAYS = 370;
|
|
@@ -22,7 +23,7 @@ const CODEX_STATE_FILE = path.join(os.homedir(), '.codex', 'state_5.sqlite');
|
|
|
22
23
|
|
|
23
24
|
// Default single-workspace XP root. Local XP reads task_episodes.jsonl (written
|
|
24
25
|
// by task-db at the resolved workspace root) and writes its projection beside
|
|
25
|
-
// it, so the `--workspace` default must resolve the same shared root
|
|
26
|
+
// it, so the `--workspace` default must resolve the same shared root, otherwise
|
|
26
27
|
// `xp --local` from a subdir (e.g. backend/) read the wrong/empty episodes and
|
|
27
28
|
// split the projection into a nested .atris. Explicit --workspace still wins;
|
|
28
29
|
// the multi-root roster path (discoverCareerXpWorkspaces) is untouched. Falls
|
|
@@ -908,7 +909,7 @@ function readTaskEpisodeTail(episodePath, cursorPath, options = {}) {
|
|
|
908
909
|
}
|
|
909
910
|
}
|
|
910
911
|
|
|
911
|
-
function receiptFromTaskEpisode(episode) {
|
|
912
|
+
function receiptFromTaskEpisode(episode, treeHash) {
|
|
912
913
|
const episodeId = episode?.episode_id;
|
|
913
914
|
const label = episode?.rl?.label;
|
|
914
915
|
const rejected = label === 'rework_requested' || label === 'rejected';
|
|
@@ -924,6 +925,7 @@ function receiptFromTaskEpisode(episode) {
|
|
|
924
925
|
|
|
925
926
|
return {
|
|
926
927
|
schema: 'atris.career_xp_receipt.v1',
|
|
928
|
+
tree_hash: treeHash,
|
|
927
929
|
receipt_id: `task_review:${episodeId}${rejected ? ':rejected' : ''}`,
|
|
928
930
|
source: 'atris-cli',
|
|
929
931
|
source_type: 'task_review',
|
|
@@ -1071,10 +1073,11 @@ function collectLocalXpProjectionState(args = [], { write = true } = {}) {
|
|
|
1071
1073
|
&& fileSize(episodePath) > 0
|
|
1072
1074
|
&& fileSize(receiptsPath) === 0;
|
|
1073
1075
|
const tail = readTaskEpisodeTail(episodePath, cursorPath, { forceReset: replayFromStart });
|
|
1076
|
+
const treeHash = write && tail.episodes.length ? treeHashFor(workspace) : null;
|
|
1074
1077
|
const seen = new Set(existingChain.receipts.map(receipt => receipt.receipt_id).filter(Boolean));
|
|
1075
1078
|
let previousHash = existingChain.integrity.head_hash || null;
|
|
1076
1079
|
const newReceipts = tail.episodes
|
|
1077
|
-
.map(receiptFromTaskEpisode)
|
|
1080
|
+
.map((episode) => receiptFromTaskEpisode(episode, treeHash))
|
|
1078
1081
|
.filter(Boolean)
|
|
1079
1082
|
.filter((receipt) => {
|
|
1080
1083
|
if (seen.has(receipt.receipt_id)) return false;
|