atris 3.68.8 → 3.68.9

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.
Files changed (2) hide show
  1. package/commands/sync.js +41 -3
  2. package/package.json +1 -1
package/commands/sync.js CHANGED
@@ -83,6 +83,23 @@ function ensureRealDirectory(dir) {
83
83
  fs.mkdirSync(dir, { recursive: true });
84
84
  }
85
85
 
86
+ // A hook script the project already has is never replaced. Projects harden
87
+ // their hooks (nonce checks, queues, proof before pop); a sync must not
88
+ // downgrade them to the shipped copy.
89
+ function isProtectedHook(destPath) {
90
+ return path.basename(path.dirname(destPath)) === 'hooks' && fs.existsSync(destPath);
91
+ }
92
+
93
+ // A member retired to team/_archived/<name>-<date>/ must never come back.
94
+ function isArchivedMember(teamDir, name) {
95
+ const archiveDir = path.join(teamDir, '_archived');
96
+ try {
97
+ return fs.readdirSync(archiveDir).some(entry => entry === name || (entry.startsWith(`${name}-`) && /^\d{4}-\d{2}-\d{2}$/.test(entry.slice(name.length + 1))));
98
+ } catch {
99
+ return false;
100
+ }
101
+ }
102
+
86
103
  /**
87
104
  * Sync the canonical skill set from atris-cli/atris/skills/* into a
88
105
  * workspace's atris/skills/* (plus ensure .claude/skills/ symlinks).
@@ -128,7 +145,9 @@ function syncPackageSkills(targetAtrisDir, opts = {}) {
128
145
  } else {
129
146
  const srcContent = fs.readFileSync(srcPath, 'utf8');
130
147
  const destContent = fs.existsSync(destPath) ? fs.readFileSync(destPath, 'utf8') : '';
131
- if (srcContent !== destContent) {
148
+ if (srcContent !== destContent && isProtectedHook(destPath)) {
149
+ if (verbose) console.log(`- Kept atris/skills/${skillName}/${relPath} (differs from shipped hook, not replaced)`);
150
+ } else if (srcContent !== destContent) {
132
151
  if (!dryRun) {
133
152
  fs.writeFileSync(destPath, srcContent);
134
153
  if (entry.endsWith('.sh')) fs.chmodSync(destPath, 0o755);
@@ -348,6 +367,12 @@ function syncWorkspaceTemplate(targetRoot, bizMeta, options = {}) {
348
367
  try { templateContent = fs.readFileSync(templatePath, 'utf-8'); } catch { continue; }
349
368
  const finalContent = _substituteParams(templateContent, params);
350
369
 
370
+ const memberMatch = targetRelPath.split(path.sep).join('/').match(/^team\/([^/_][^/]*)\//);
371
+ if (memberMatch && isArchivedMember(path.join(targetAtrisDir, 'team'), memberMatch[1])) {
372
+ skipped++;
373
+ continue;
374
+ }
375
+
351
376
  if (!fs.existsSync(targetPath)) {
352
377
  addedList.push(targetRelPath); added++;
353
378
  if (!dryRun) {
@@ -358,7 +383,7 @@ function syncWorkspaceTemplate(targetRoot, bizMeta, options = {}) {
358
383
  const existing = fs.readFileSync(targetPath, 'utf-8');
359
384
  if (existing === finalContent) {
360
385
  skipped++;
361
- } else if (force) {
386
+ } else if (force && !memberMatch) {
362
387
  updatedList.push(targetRelPath); updated++;
363
388
  if (!dryRun) fs.writeFileSync(targetPath, finalContent);
364
389
  } else {
@@ -565,6 +590,17 @@ function syncAtris(options = {}) {
565
590
  return;
566
591
  }
567
592
 
593
+ // Team members belong to the project. Update never adds a member (init
594
+ // does that, and a missing member may have been retired to
595
+ // team/_archived/) and never replaces one the project has changed.
596
+ if (target.startsWith('team/')) {
597
+ if (fs.existsSync(targetFile)) {
598
+ console.log(`- Kept ${target} (newer template exists in the package, yours not replaced)`);
599
+ }
600
+ skipped++;
601
+ return;
602
+ }
603
+
568
604
  if (!dryRun) {
569
605
  fs.mkdirSync(path.dirname(targetFile), { recursive: true });
570
606
  fs.copyFileSync(sourceFile, targetFile);
@@ -728,7 +764,9 @@ function syncRecursiveCount(src, dest, label, silent) {
728
764
  } else {
729
765
  const srcBuf = fs.readFileSync(srcPath);
730
766
  const destBuf = fs.existsSync(destPath) ? fs.readFileSync(destPath) : Buffer.alloc(0);
731
- if (!srcBuf.equals(destBuf)) {
767
+ if (!srcBuf.equals(destBuf) && isProtectedHook(destPath)) {
768
+ if (!silent) console.log(`- Kept ${label}/${entry} (differs from shipped hook, not replaced)`);
769
+ } else if (!srcBuf.equals(destBuf)) {
732
770
  fs.writeFileSync(destPath, srcBuf);
733
771
  if (entry.endsWith('.sh')) {
734
772
  fs.chmodSync(destPath, 0o755);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "3.68.8",
3
+ "version": "3.68.9",
4
4
  "description": "you say what you want in plain words. atris builds it, checks it, and shows you proof.",
5
5
  "main": "bin/atris.js",
6
6
  "bin": {