atris 3.71.2 → 3.72.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/atris/skills/campaigns/SKILL.md +1 -1
- package/bin/atris.js +8 -2
- package/commands/autoland.js +102 -2
- package/commands/campaign.js +263 -11
- package/commands/clean.js +60 -4
- package/commands/gm.js +1273 -7
- package/commands/improve.js +3 -2
- package/commands/member.js +28 -3
- package/commands/mission.js +82 -27
- package/commands/pulse.js +89 -5
- package/commands/sync.js +2 -2
- package/commands/task.js +49 -13
- package/commands/worktree.js +24 -2
- package/commands/zen.js +183 -0
- package/lib/auto-accept-certified.js +9 -4
- package/lib/campaign-cadence.js +53 -14
- package/lib/campaign-core.js +98 -17
- package/lib/campaign-keep.js +91 -0
- package/lib/campaign-lanes.js +11 -6
- package/lib/campaign-loop.js +173 -32
- package/lib/campaign-reading.js +14 -1
- package/lib/campaign-rsi.js +697 -0
- package/lib/campaign-run-lock.js +9 -1
- package/lib/campaign-schedule.js +124 -0
- package/lib/campaign-schema.js +3 -1
- package/lib/campaign-store.js +34 -2
- package/lib/campaign-trace.js +77 -29
- package/lib/engine-resource.js +5 -1
- package/lib/gm-bet.js +2219 -0
- package/lib/gm-phone-page.js +1102 -0
- package/lib/gm-phone.js +682 -0
- package/lib/gm-season.js +1162 -0
- package/lib/gm-target.js +292 -0
- package/lib/job-code-verify.js +169 -0
- package/lib/known-commands.js +1 -1
- package/lib/member-autoland.js +105 -14
- package/lib/member-progress.js +2 -2
- package/lib/member-report.js +11 -1
- package/lib/member-self-next-job.js +6 -1
- package/lib/member-up.js +17 -8
- package/lib/merged-review-close.js +206 -0
- package/lib/mission-code-provenance.js +118 -0
- package/lib/mission-protected-lane.js +52 -4
- package/lib/pulse.js +2 -1
- package/lib/qr.js +338 -0
- package/lib/rsi-record.js +88 -0
- package/lib/task-receipt.js +24 -6
- package/lib/yes-lock.js +530 -0
- package/lib/zen-apply.js +511 -0
- package/lib/zen-workspace.js +121 -0
- package/lib/zen.js +395 -0
- package/package.json +1 -1
package/commands/clean.js
CHANGED
|
@@ -634,6 +634,42 @@ function findStalePages(cwd, atrisDir) {
|
|
|
634
634
|
return stalePages;
|
|
635
635
|
}
|
|
636
636
|
|
|
637
|
+
/**
|
|
638
|
+
* When a source last changed. A checkout or rebase touches mtimes without
|
|
639
|
+
* changing content, so a file git holds unchanged counts from its last commit.
|
|
640
|
+
*/
|
|
641
|
+
function contentChangedAt(sourcePath, mtime) {
|
|
642
|
+
const dir = path.dirname(sourcePath);
|
|
643
|
+
const name = path.basename(sourcePath);
|
|
644
|
+
const status = spawnSync('git', ['status', '--porcelain', '--', name], { cwd: dir, encoding: 'utf8' });
|
|
645
|
+
if (status.status !== 0 || status.stdout.trim()) return mtime;
|
|
646
|
+
const log = spawnSync('git', ['log', '-1', '--format=%cI', '--', name], { cwd: dir, encoding: 'utf8' });
|
|
647
|
+
const committed = log.status === 0 ? log.stdout.trim() : '';
|
|
648
|
+
return committed ? new Date(committed) : mtime;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function looksLikePath(text) {
|
|
652
|
+
if (!text || /\s/.test(text) || /^[a-z][a-z0-9+.-]*:\/\//i.test(text)) return false;
|
|
653
|
+
return text.includes('/') || /\.[A-Za-z0-9]+$/.test(text);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// A relative source can be written from the repo root, the page's own folder,
|
|
657
|
+
// or any folder in between (a brief citing "briefs/x.md" from the wiki root).
|
|
658
|
+
// The first place it exists wins; if none, the repo-root path is reported.
|
|
659
|
+
function resolveSource(cwd, pagePath, filePart) {
|
|
660
|
+
if (path.isAbsolute(filePart)) return filePart;
|
|
661
|
+
const root = path.resolve(cwd);
|
|
662
|
+
const fromRoot = path.join(root, filePart);
|
|
663
|
+
if (fs.existsSync(fromRoot)) return fromRoot;
|
|
664
|
+
let dir = path.dirname(path.resolve(pagePath));
|
|
665
|
+
while (dir.startsWith(root + path.sep)) {
|
|
666
|
+
const candidate = path.join(dir, filePart);
|
|
667
|
+
if (fs.existsSync(candidate)) return candidate;
|
|
668
|
+
dir = path.dirname(dir);
|
|
669
|
+
}
|
|
670
|
+
return fromRoot;
|
|
671
|
+
}
|
|
672
|
+
|
|
637
673
|
/**
|
|
638
674
|
* Parse frontmatter from a markdown file and check if any source is newer than last_compiled.
|
|
639
675
|
* Returns { page, staleSource, compiledDate, sourceDate } or null if not stale.
|
|
@@ -675,15 +711,19 @@ function checkPageStaleness(cwd, filePath) {
|
|
|
675
711
|
.replace(/\s*\([^)]*\)\s*$/, '') // drop trailing "(annotation)"
|
|
676
712
|
.replace(/:\d+(-\d+)?$/, '') // drop trailing ":N" or ":N-M"
|
|
677
713
|
.trim();
|
|
678
|
-
|
|
714
|
+
// A plain-language source ("Voyage AI documentation") or a web link names
|
|
715
|
+
// no file, so it can never be missing on disk. Only paths are checked.
|
|
716
|
+
if (!looksLikePath(filePart)) continue;
|
|
717
|
+
const sourcePath = resolveSource(cwd, filePath, filePart);
|
|
679
718
|
try {
|
|
680
719
|
const stat = fs.statSync(sourcePath);
|
|
681
|
-
|
|
720
|
+
const changed = stat.mtime > compiledDate ? contentChangedAt(sourcePath, stat.mtime) : null;
|
|
721
|
+
if (changed && changed > compiledDate) {
|
|
682
722
|
return {
|
|
683
723
|
page: filePath,
|
|
684
724
|
staleSource: source,
|
|
685
725
|
compiledDate: compiledMatch[1],
|
|
686
|
-
sourceDate:
|
|
726
|
+
sourceDate: changed.toISOString().split('T')[0]
|
|
687
727
|
};
|
|
688
728
|
}
|
|
689
729
|
} catch {
|
|
@@ -700,6 +740,22 @@ function checkPageStaleness(cwd, filePath) {
|
|
|
700
740
|
return null;
|
|
701
741
|
}
|
|
702
742
|
|
|
743
|
+
// A day can already be archived when a late entry lands in its journal.
|
|
744
|
+
// Renaming over the archive would erase it, so the late entry is added to the
|
|
745
|
+
// end instead, unless the archive already holds it word for word.
|
|
746
|
+
function moveIntoArchive(src, dest) {
|
|
747
|
+
if (!fs.existsSync(dest)) {
|
|
748
|
+
fs.renameSync(src, dest);
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
const late = fs.readFileSync(src, 'utf8');
|
|
752
|
+
const kept = fs.readFileSync(dest, 'utf8');
|
|
753
|
+
if (late.trim() && !kept.includes(late.trim())) {
|
|
754
|
+
fs.writeFileSync(dest, `${kept.replace(/\n*$/, '\n')}\n${late}`);
|
|
755
|
+
}
|
|
756
|
+
fs.rmSync(src);
|
|
757
|
+
}
|
|
758
|
+
|
|
703
759
|
/**
|
|
704
760
|
* Archive journals older than 30 days
|
|
705
761
|
*/
|
|
@@ -734,7 +790,7 @@ function archiveOldJournals(atrisDir, dryRun = false) {
|
|
|
734
790
|
}
|
|
735
791
|
const src = path.join(yearDir, file);
|
|
736
792
|
const dest = path.join(archiveYearDir, file);
|
|
737
|
-
|
|
793
|
+
moveIntoArchive(src, dest);
|
|
738
794
|
}
|
|
739
795
|
archived++;
|
|
740
796
|
}
|