@gadmin2n/cli 0.0.103 → 0.0.105
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/actions/update.action.js +292 -264
- package/commands/update.command.js +14 -6
- package/package.json +2 -2
package/actions/update.action.js
CHANGED
|
@@ -388,8 +388,8 @@ function copyTreeOverwrite(srcDir, destDir) {
|
|
|
388
388
|
* Resolve which git URL + branch to use for the .claude context sync.
|
|
389
389
|
*
|
|
390
390
|
* Priority (highest first):
|
|
391
|
-
* 1. CLI --
|
|
392
|
-
* 2. CLI --
|
|
391
|
+
* 1. CLI --coding-context-repo (full URL override)
|
|
392
|
+
* 2. CLI --coding-context-protocol (https | ssh | skip)
|
|
393
393
|
* 3. Env var GADMIN2_CLAUDE_CONTEXT_REPO (full URL)
|
|
394
394
|
* 4. Env var GADMIN2_CLAUDE_CONTEXT_PROTOCOL (https | ssh | skip)
|
|
395
395
|
* 5. --yes (auto mode): default to built-in HTTPS, no prompt
|
|
@@ -638,6 +638,18 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
638
638
|
console.error(chalk.red(`Unknown subcommand "${subcommand}". Expected: (none) | diff`));
|
|
639
639
|
process.exit(1);
|
|
640
640
|
}
|
|
641
|
+
// Guard: refuse to run anywhere except a gadmin2 instance project root.
|
|
642
|
+
// The canonical marker is server/gadmin-cli.json (created by `gadmin2 n`).
|
|
643
|
+
// We check this up-front so commands like `gadmin2 update --no-template`
|
|
644
|
+
// can't silently sync .claude into an unrelated directory.
|
|
645
|
+
const instanceMarker = path.join(process.cwd(), 'server', 'gadmin-cli.json');
|
|
646
|
+
if (!fs.existsSync(instanceMarker)) {
|
|
647
|
+
console.error(chalk.red('\nNot at a gadmin2 instance project root.\n' +
|
|
648
|
+
` Expected to find: server/gadmin-cli.json\n` +
|
|
649
|
+
` Current directory: ${process.cwd()}\n\n` +
|
|
650
|
+
'Run this command from the project root created by `gadmin2 n <name>`.\n'));
|
|
651
|
+
process.exit(1);
|
|
652
|
+
}
|
|
641
653
|
// Pre-flight version check (B): silent network call to npm registry,
|
|
642
654
|
// hard 1.5s timeout, never blocks. Prints a yellow banner if outdated.
|
|
643
655
|
const versionCheckEnabled = ((_b = options.find((o) => o.name === 'version-check')) === null || _b === void 0 ? void 0 : _b.value) !==
|
|
@@ -747,313 +759,329 @@ class UpdateAction extends abstract_action_1.AbstractAction {
|
|
|
747
759
|
});
|
|
748
760
|
}
|
|
749
761
|
handleUpdate(options) {
|
|
750
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
762
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
751
763
|
return __awaiter(this, void 0, void 0, function* () {
|
|
752
764
|
const dryRun = (_a = options.find((o) => o.name === 'dry-run')) === null || _a === void 0 ? void 0 : _a.value;
|
|
753
765
|
const autoYes = (_b = options.find((o) => o.name === 'yes')) === null || _b === void 0 ? void 0 : _b.value;
|
|
754
766
|
const smartMergeEnabled = (_d = (_c = options.find((o) => o.name === 'smart-merge')) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : true;
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
excludes = config.excludes;
|
|
761
|
-
}
|
|
762
|
-
catch (err) {
|
|
763
|
-
console.error(chalk.red(err.message));
|
|
767
|
+
const templateEnabled = ((_e = options.find((o) => o.name === 'template')) === null || _e === void 0 ? void 0 : _e.value) !== false;
|
|
768
|
+
const claudeEnabled = ((_f = options.find((o) => o.name === 'coding-context')) === null || _f === void 0 ? void 0 : _f.value) !==
|
|
769
|
+
false;
|
|
770
|
+
if (!templateEnabled && !claudeEnabled) {
|
|
771
|
+
console.error(chalk.red('\nNothing to do: both --no-template and --no-coding-context were specified.\n'));
|
|
764
772
|
process.exit(1);
|
|
765
773
|
}
|
|
766
774
|
const instanceDir = process.cwd();
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
// Snapshot package.json files BEFORE any writes. We watch:
|
|
778
|
-
// - <instanceDir>/package.json (root, if present — workspace / husky / etc.)
|
|
779
|
-
// - <instanceDir>/web/package.json
|
|
780
|
-
// - <instanceDir>/server/package.json
|
|
781
|
-
const pkgSnapshots = [
|
|
782
|
-
snapshotPackageJson(instanceDir, '(root)'),
|
|
783
|
-
snapshotPackageJson(path.join(instanceDir, 'web'), 'web'),
|
|
784
|
-
snapshotPackageJson(path.join(instanceDir, 'server'), 'server'),
|
|
785
|
-
];
|
|
786
|
-
const templateFiles = collectFiles(templateDir, excludes);
|
|
787
|
-
const instanceFiles = collectFiles(instanceDir, excludes);
|
|
788
|
-
const instanceSet = new Set(instanceFiles);
|
|
789
|
-
const toUpdate = [];
|
|
790
|
-
const toAdd = [];
|
|
791
|
-
for (const file of templateFiles) {
|
|
792
|
-
if (instanceSet.has(file)) {
|
|
793
|
-
const tContent = fs.readFileSync(path.join(templateDir, file), 'utf-8');
|
|
794
|
-
const iContent = fs.readFileSync(path.join(instanceDir, file), 'utf-8');
|
|
795
|
-
if (tContent !== iContent) {
|
|
796
|
-
toUpdate.push(file);
|
|
797
|
-
}
|
|
775
|
+
// Hoisted so stage 3 (auto-install) can see snapshots taken in stage 1.
|
|
776
|
+
// Remains null when --no-template, which is also the signal to skip stage 3.
|
|
777
|
+
let pkgSnapshots = null;
|
|
778
|
+
if (templateEnabled) {
|
|
779
|
+
let templateDir;
|
|
780
|
+
let excludes;
|
|
781
|
+
try {
|
|
782
|
+
const config = loadTemplateConfig();
|
|
783
|
+
templateDir = resolveTemplatePath(config.templateName);
|
|
784
|
+
excludes = config.excludes;
|
|
798
785
|
}
|
|
799
|
-
|
|
800
|
-
|
|
786
|
+
catch (err) {
|
|
787
|
+
console.error(chalk.red(err.message));
|
|
788
|
+
process.exit(1);
|
|
801
789
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
790
|
+
console.info(`\n${chalk.gray('Template:')} ${templateDir}`);
|
|
791
|
+
console.info(`${chalk.gray('Instance:')} ${instanceDir}\n`);
|
|
792
|
+
// Build smart-merger registry (or null if disabled)
|
|
793
|
+
const userTemplateUpdate = smartMergeEnabled
|
|
794
|
+
? (0, template_merge_1.loadTemplateUpdateConfig)(instanceDir)
|
|
795
|
+
: null;
|
|
796
|
+
const registry = smartMergeEnabled
|
|
797
|
+
? buildMergerRegistry(userTemplateUpdate)
|
|
798
|
+
: null;
|
|
799
|
+
const userPolicies = userTemplateUpdate === null || userTemplateUpdate === void 0 ? void 0 : userTemplateUpdate.policies;
|
|
800
|
+
// Snapshot package.json files BEFORE any writes. We watch:
|
|
801
|
+
// - <instanceDir>/package.json (root, if present — workspace / husky / etc.)
|
|
802
|
+
// - <instanceDir>/web/package.json
|
|
803
|
+
// - <instanceDir>/server/package.json
|
|
804
|
+
pkgSnapshots = [
|
|
805
|
+
snapshotPackageJson(instanceDir, '(root)'),
|
|
806
|
+
snapshotPackageJson(path.join(instanceDir, 'web'), 'web'),
|
|
807
|
+
snapshotPackageJson(path.join(instanceDir, 'server'), 'server'),
|
|
808
|
+
];
|
|
809
|
+
const templateFiles = collectFiles(templateDir, excludes);
|
|
810
|
+
const instanceFiles = collectFiles(instanceDir, excludes);
|
|
811
|
+
const instanceSet = new Set(instanceFiles);
|
|
812
|
+
const toUpdate = [];
|
|
813
|
+
const toAdd = [];
|
|
814
|
+
for (const file of templateFiles) {
|
|
815
|
+
if (instanceSet.has(file)) {
|
|
816
|
+
const tContent = fs.readFileSync(path.join(templateDir, file), 'utf-8');
|
|
817
|
+
const iContent = fs.readFileSync(path.join(instanceDir, file), 'utf-8');
|
|
818
|
+
if (tContent !== iContent) {
|
|
819
|
+
toUpdate.push(file);
|
|
820
|
+
}
|
|
814
821
|
}
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
if (toAdd.length > 0) {
|
|
818
|
-
console.info(chalk.green(`➕ To add (${toAdd.length} files)`));
|
|
819
|
-
for (const f of toAdd) {
|
|
820
|
-
console.info(` ${f}`);
|
|
822
|
+
else {
|
|
823
|
+
toAdd.push(file);
|
|
821
824
|
}
|
|
822
|
-
console.info();
|
|
823
825
|
}
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
},
|
|
836
|
-
]);
|
|
837
|
-
if (!confirm) {
|
|
838
|
-
console.info('Cancelled.\n');
|
|
839
|
-
return;
|
|
826
|
+
if (toUpdate.length === 0 && toAdd.length === 0) {
|
|
827
|
+
console.info(chalk.green('✅ Instance is up to date with template.\n'));
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
830
|
+
console.info(chalk.bold('═══════════════════════════════════════════════════'));
|
|
831
|
+
console.info(chalk.bold(' Template Update Summary'));
|
|
832
|
+
console.info(chalk.bold('═══════════════════════════════════════════════════\n'));
|
|
833
|
+
if (toUpdate.length > 0) {
|
|
834
|
+
console.info(chalk.yellow(`📝 To update (${toUpdate.length} files)`));
|
|
835
|
+
for (const f of toUpdate) {
|
|
836
|
+
console.info(` ${f}`);
|
|
840
837
|
}
|
|
838
|
+
console.info();
|
|
841
839
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
let smartCount = 0;
|
|
847
|
-
let applyAll = null;
|
|
848
|
-
for (let i = 0; i < toUpdate.length; i++) {
|
|
849
|
-
const file = toUpdate[i];
|
|
850
|
-
const tContent = fs.readFileSync(path.join(templateDir, file), 'utf-8');
|
|
851
|
-
const iContent = fs.readFileSync(path.join(instanceDir, file), 'utf-8');
|
|
852
|
-
// 1. Try smart merger if registered for this file
|
|
853
|
-
const smartMerger = registry
|
|
854
|
-
? registry.resolve(file)
|
|
855
|
-
: null;
|
|
856
|
-
let smartResult = null;
|
|
857
|
-
if (smartMerger) {
|
|
858
|
-
try {
|
|
859
|
-
smartResult = yield smartMerger.merge({
|
|
860
|
-
filePath: file,
|
|
861
|
-
instancePath: path.join(instanceDir, file),
|
|
862
|
-
templatePath: path.join(templateDir, file),
|
|
863
|
-
instanceContent: iContent,
|
|
864
|
-
templateContent: tContent,
|
|
865
|
-
policies: userPolicies,
|
|
866
|
-
});
|
|
867
|
-
}
|
|
868
|
-
catch (err) {
|
|
869
|
-
smartResult = {
|
|
870
|
-
ok: false,
|
|
871
|
-
reason: (err === null || err === void 0 ? void 0 : err.message) || String(err),
|
|
872
|
-
};
|
|
873
|
-
}
|
|
840
|
+
if (toAdd.length > 0) {
|
|
841
|
+
console.info(chalk.green(`➕ To add (${toAdd.length} files)`));
|
|
842
|
+
for (const f of toAdd) {
|
|
843
|
+
console.info(` ${f}`);
|
|
874
844
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
845
|
+
console.info();
|
|
846
|
+
}
|
|
847
|
+
console.info(chalk.gray('───────────────────────────────────────────────────'));
|
|
848
|
+
console.info(chalk.gray(` Total: ${toUpdate.length} update | ${toAdd.length} add`));
|
|
849
|
+
console.info(chalk.gray('───────────────────────────────────────────────────\n'));
|
|
850
|
+
if (!dryRun) {
|
|
851
|
+
if (!autoYes) {
|
|
852
|
+
const { confirm } = yield inquirer.prompt([
|
|
853
|
+
{
|
|
854
|
+
type: 'confirm',
|
|
855
|
+
name: 'confirm',
|
|
856
|
+
message: 'Proceed with template update?',
|
|
857
|
+
default: false,
|
|
858
|
+
},
|
|
859
|
+
]);
|
|
860
|
+
if (!confirm) {
|
|
861
|
+
console.info('Cancelled.\n');
|
|
862
|
+
return;
|
|
881
863
|
}
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
864
|
+
}
|
|
865
|
+
let skippedCount = 0;
|
|
866
|
+
let overwrittenCount = 0;
|
|
867
|
+
let mergedCount = 0;
|
|
868
|
+
let conflictCount = 0;
|
|
869
|
+
let smartCount = 0;
|
|
870
|
+
let applyAll = null;
|
|
871
|
+
for (let i = 0; i < toUpdate.length; i++) {
|
|
872
|
+
const file = toUpdate[i];
|
|
873
|
+
const tContent = fs.readFileSync(path.join(templateDir, file), 'utf-8');
|
|
874
|
+
const iContent = fs.readFileSync(path.join(instanceDir, file), 'utf-8');
|
|
875
|
+
// 1. Try smart merger if registered for this file
|
|
876
|
+
const smartMerger = registry
|
|
877
|
+
? registry.resolve(file)
|
|
878
|
+
: null;
|
|
879
|
+
let smartResult = null;
|
|
880
|
+
if (smartMerger) {
|
|
881
|
+
try {
|
|
882
|
+
smartResult = yield smartMerger.merge({
|
|
883
|
+
filePath: file,
|
|
884
|
+
instancePath: path.join(instanceDir, file),
|
|
885
|
+
templatePath: path.join(templateDir, file),
|
|
886
|
+
instanceContent: iContent,
|
|
887
|
+
templateContent: tContent,
|
|
888
|
+
policies: userPolicies,
|
|
906
889
|
});
|
|
907
890
|
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
name: 'Overwrite — use template version',
|
|
914
|
-
value: 'overwrite',
|
|
915
|
-
short: 'overwrite',
|
|
916
|
-
}, {
|
|
917
|
-
name: 'Auto merge — attempt git merge',
|
|
918
|
-
value: 'merge',
|
|
919
|
-
short: 'merge',
|
|
920
|
-
}, {
|
|
921
|
-
name: 'Conflict markers — write both versions for manual resolve',
|
|
922
|
-
value: 'conflict',
|
|
923
|
-
short: 'conflict',
|
|
924
|
-
}, new inquirer.Separator());
|
|
925
|
-
if (smartResult && smartResult.ok) {
|
|
926
|
-
choices.push({
|
|
927
|
-
name: 'Smart-merge ALL remaining (where supported)',
|
|
928
|
-
value: 'smart-all',
|
|
929
|
-
short: 'smart all',
|
|
930
|
-
});
|
|
891
|
+
catch (err) {
|
|
892
|
+
smartResult = {
|
|
893
|
+
ok: false,
|
|
894
|
+
reason: (err === null || err === void 0 ? void 0 : err.message) || String(err),
|
|
895
|
+
};
|
|
931
896
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
short: 'overwrite all',
|
|
940
|
-
}, {
|
|
941
|
-
name: 'Conflict markers ALL remaining',
|
|
942
|
-
value: 'conflict-all',
|
|
943
|
-
short: 'conflict all',
|
|
944
|
-
});
|
|
945
|
-
const { action } = yield inquirer.prompt([
|
|
946
|
-
{
|
|
947
|
-
type: 'list',
|
|
948
|
-
name: 'action',
|
|
949
|
-
message: `How to handle ${file}?`,
|
|
950
|
-
default: smartResult && smartResult.ok ? 'smart' : 'skip',
|
|
951
|
-
choices,
|
|
952
|
-
},
|
|
953
|
-
]);
|
|
954
|
-
if (action.endsWith('-all')) {
|
|
955
|
-
applyAll = action.replace('-all', '');
|
|
956
|
-
strategy = applyAll;
|
|
897
|
+
}
|
|
898
|
+
let strategy = applyAll;
|
|
899
|
+
if (!strategy) {
|
|
900
|
+
if (autoYes) {
|
|
901
|
+
// autoYes: prefer smart merge when available, else conflict markers
|
|
902
|
+
strategy =
|
|
903
|
+
smartResult && smartResult.ok ? 'smart' : 'conflict';
|
|
957
904
|
}
|
|
958
905
|
else {
|
|
959
|
-
|
|
906
|
+
const diff = createUnifiedDiff(file, iContent, tContent);
|
|
907
|
+
console.info(`\n${chalk.bold(`[${i + 1}/${toUpdate.length}]`)} ${chalk.yellow(file)}`);
|
|
908
|
+
console.info(diff);
|
|
909
|
+
console.info();
|
|
910
|
+
const choices = [];
|
|
911
|
+
if (smartResult && smartResult.ok) {
|
|
912
|
+
const summary = smartResult.notes.slice(0, 3).join('; ');
|
|
913
|
+
const more = smartResult.notes.length > 3 ? '...' : '';
|
|
914
|
+
choices.push({
|
|
915
|
+
name: `Smart merge — ${smartMerger.name} (recommended): ${summary}${more}`,
|
|
916
|
+
value: 'smart',
|
|
917
|
+
short: 'smart',
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
else if (smartMerger) {
|
|
921
|
+
const reason = smartResult && !smartResult.ok
|
|
922
|
+
? smartResult.reason
|
|
923
|
+
: 'no result';
|
|
924
|
+
choices.push({
|
|
925
|
+
name: `Smart merge — ${smartMerger.name} (UNAVAILABLE: ${reason})`,
|
|
926
|
+
value: 'smart-unavailable',
|
|
927
|
+
short: 'smart-unavailable',
|
|
928
|
+
disabled: reason,
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
choices.push({
|
|
932
|
+
name: 'Skip — keep instance as-is',
|
|
933
|
+
value: 'skip',
|
|
934
|
+
short: 'skip',
|
|
935
|
+
}, {
|
|
936
|
+
name: 'Overwrite — use template version',
|
|
937
|
+
value: 'overwrite',
|
|
938
|
+
short: 'overwrite',
|
|
939
|
+
}, {
|
|
940
|
+
name: 'Auto merge — attempt git merge',
|
|
941
|
+
value: 'merge',
|
|
942
|
+
short: 'merge',
|
|
943
|
+
}, {
|
|
944
|
+
name: 'Conflict markers — write both versions for manual resolve',
|
|
945
|
+
value: 'conflict',
|
|
946
|
+
short: 'conflict',
|
|
947
|
+
}, new inquirer.Separator());
|
|
948
|
+
if (smartResult && smartResult.ok) {
|
|
949
|
+
choices.push({
|
|
950
|
+
name: 'Smart-merge ALL remaining (where supported)',
|
|
951
|
+
value: 'smart-all',
|
|
952
|
+
short: 'smart all',
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
choices.push({
|
|
956
|
+
name: 'Skip ALL remaining',
|
|
957
|
+
value: 'skip-all',
|
|
958
|
+
short: 'skip all',
|
|
959
|
+
}, {
|
|
960
|
+
name: 'Overwrite ALL remaining',
|
|
961
|
+
value: 'overwrite-all',
|
|
962
|
+
short: 'overwrite all',
|
|
963
|
+
}, {
|
|
964
|
+
name: 'Conflict markers ALL remaining',
|
|
965
|
+
value: 'conflict-all',
|
|
966
|
+
short: 'conflict all',
|
|
967
|
+
});
|
|
968
|
+
const { action } = yield inquirer.prompt([
|
|
969
|
+
{
|
|
970
|
+
type: 'list',
|
|
971
|
+
name: 'action',
|
|
972
|
+
message: `How to handle ${file}?`,
|
|
973
|
+
default: smartResult && smartResult.ok ? 'smart' : 'skip',
|
|
974
|
+
choices,
|
|
975
|
+
},
|
|
976
|
+
]);
|
|
977
|
+
if (action.endsWith('-all')) {
|
|
978
|
+
applyAll = action.replace('-all', '');
|
|
979
|
+
strategy = applyAll;
|
|
980
|
+
}
|
|
981
|
+
else {
|
|
982
|
+
strategy = action;
|
|
983
|
+
}
|
|
960
984
|
}
|
|
961
985
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
986
|
+
const destPath = path.join(instanceDir, file);
|
|
987
|
+
switch (strategy) {
|
|
988
|
+
case 'smart':
|
|
989
|
+
if (smartResult && smartResult.ok) {
|
|
990
|
+
fs.writeFileSync(destPath, smartResult.merged, 'utf-8');
|
|
991
|
+
smartCount++;
|
|
992
|
+
console.info(chalk.green(` ✓ ${file} — smart merge (${smartMerger.name})`));
|
|
993
|
+
for (const note of smartResult.notes) {
|
|
994
|
+
console.info(chalk.gray(` • ${note}`));
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
else {
|
|
998
|
+
// No smart merger usable: fallback to conflict markers
|
|
999
|
+
fs.writeFileSync(destPath, createConflictContent(iContent, tContent), 'utf-8');
|
|
1000
|
+
conflictCount++;
|
|
1001
|
+
console.info(chalk.yellow(` ⚠️ ${file} — no smart merger available, wrote conflict markers`));
|
|
1002
|
+
}
|
|
1003
|
+
break;
|
|
1004
|
+
case 'skip':
|
|
1005
|
+
skippedCount++;
|
|
1006
|
+
break;
|
|
1007
|
+
case 'overwrite':
|
|
1008
|
+
fs.writeFileSync(destPath, tContent, 'utf-8');
|
|
1009
|
+
overwrittenCount++;
|
|
1010
|
+
break;
|
|
1011
|
+
case 'merge': {
|
|
1012
|
+
const result = tryAutoMerge(iContent, tContent);
|
|
1013
|
+
fs.writeFileSync(destPath, result.merged, 'utf-8');
|
|
1014
|
+
if (result.hasConflicts) {
|
|
1015
|
+
conflictCount++;
|
|
1016
|
+
console.info(chalk.yellow(` ⚠️ ${file} — merge has conflicts`));
|
|
972
1017
|
}
|
|
1018
|
+
else {
|
|
1019
|
+
mergedCount++;
|
|
1020
|
+
console.info(chalk.green(` ✓ ${file} — merged cleanly`));
|
|
1021
|
+
}
|
|
1022
|
+
break;
|
|
973
1023
|
}
|
|
974
|
-
|
|
975
|
-
|
|
1024
|
+
case 'conflict':
|
|
1025
|
+
default:
|
|
976
1026
|
fs.writeFileSync(destPath, createConflictContent(iContent, tContent), 'utf-8');
|
|
977
1027
|
conflictCount++;
|
|
978
|
-
|
|
979
|
-
}
|
|
980
|
-
break;
|
|
981
|
-
case 'skip':
|
|
982
|
-
skippedCount++;
|
|
983
|
-
break;
|
|
984
|
-
case 'overwrite':
|
|
985
|
-
fs.writeFileSync(destPath, tContent, 'utf-8');
|
|
986
|
-
overwrittenCount++;
|
|
987
|
-
break;
|
|
988
|
-
case 'merge': {
|
|
989
|
-
const result = tryAutoMerge(iContent, tContent);
|
|
990
|
-
fs.writeFileSync(destPath, result.merged, 'utf-8');
|
|
991
|
-
if (result.hasConflicts) {
|
|
992
|
-
conflictCount++;
|
|
993
|
-
console.info(chalk.yellow(` ⚠️ ${file} — merge has conflicts`));
|
|
994
|
-
}
|
|
995
|
-
else {
|
|
996
|
-
mergedCount++;
|
|
997
|
-
console.info(chalk.green(` ✓ ${file} — merged cleanly`));
|
|
998
|
-
}
|
|
999
|
-
break;
|
|
1028
|
+
break;
|
|
1000
1029
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1030
|
+
}
|
|
1031
|
+
for (const file of toAdd) {
|
|
1032
|
+
const destPath = path.join(instanceDir, file);
|
|
1033
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
1034
|
+
fs.copyFileSync(path.join(templateDir, file), destPath);
|
|
1035
|
+
}
|
|
1036
|
+
console.info(chalk.bold('\n✅ Template update complete!\n'));
|
|
1037
|
+
const stats = [];
|
|
1038
|
+
if (overwrittenCount > 0)
|
|
1039
|
+
stats.push(`${overwrittenCount} overwritten`);
|
|
1040
|
+
if (smartCount > 0)
|
|
1041
|
+
stats.push(`${smartCount} smart-merged`);
|
|
1042
|
+
if (mergedCount > 0)
|
|
1043
|
+
stats.push(`${mergedCount} merged`);
|
|
1044
|
+
if (conflictCount > 0)
|
|
1045
|
+
stats.push(`${conflictCount} with conflicts`);
|
|
1046
|
+
if (skippedCount > 0)
|
|
1047
|
+
stats.push(`${skippedCount} skipped`);
|
|
1048
|
+
if (toAdd.length > 0)
|
|
1049
|
+
stats.push(`${toAdd.length} added`);
|
|
1050
|
+
console.info(` ${stats.join(' | ')}\n`);
|
|
1051
|
+
if (conflictCount > 0) {
|
|
1052
|
+
console.info(chalk.yellow(`⚠️ ${conflictCount} files have conflict markers. ` +
|
|
1053
|
+
`Search for "<<<<<<< INSTANCE" to resolve them.\n`));
|
|
1006
1054
|
}
|
|
1007
1055
|
}
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
1011
|
-
fs.copyFileSync(path.join(templateDir, file), destPath);
|
|
1012
|
-
}
|
|
1013
|
-
console.info(chalk.bold('\n✅ Template update complete!\n'));
|
|
1014
|
-
const stats = [];
|
|
1015
|
-
if (overwrittenCount > 0)
|
|
1016
|
-
stats.push(`${overwrittenCount} overwritten`);
|
|
1017
|
-
if (smartCount > 0)
|
|
1018
|
-
stats.push(`${smartCount} smart-merged`);
|
|
1019
|
-
if (mergedCount > 0)
|
|
1020
|
-
stats.push(`${mergedCount} merged`);
|
|
1021
|
-
if (conflictCount > 0)
|
|
1022
|
-
stats.push(`${conflictCount} with conflicts`);
|
|
1023
|
-
if (skippedCount > 0)
|
|
1024
|
-
stats.push(`${skippedCount} skipped`);
|
|
1025
|
-
if (toAdd.length > 0)
|
|
1026
|
-
stats.push(`${toAdd.length} added`);
|
|
1027
|
-
console.info(` ${stats.join(' | ')}\n`);
|
|
1028
|
-
if (conflictCount > 0) {
|
|
1029
|
-
console.info(chalk.yellow(`⚠️ ${conflictCount} files have conflict markers. ` +
|
|
1030
|
-
`Search for "<<<<<<< INSTANCE" to resolve them.\n`));
|
|
1056
|
+
else {
|
|
1057
|
+
console.info(chalk.gray('(dry-run mode, no template changes made)\n'));
|
|
1031
1058
|
}
|
|
1032
1059
|
}
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1060
|
+
}
|
|
1061
|
+
else {
|
|
1062
|
+
console.info(chalk.gray('\nSkipping template update (--no-template).\n'));
|
|
1036
1063
|
}
|
|
1037
1064
|
// 2. Sync .claude context (regardless of whether template had changes)
|
|
1038
|
-
const claudeEnabled = ((_e = options.find((o) => o.name === 'claude-context')) === null || _e === void 0 ? void 0 : _e.value) !==
|
|
1039
|
-
false;
|
|
1040
1065
|
if (claudeEnabled) {
|
|
1041
|
-
const cliRepo = ((
|
|
1066
|
+
const cliRepo = ((_g = options.find((o) => o.name === 'coding-context-repo')) === null || _g === void 0 ? void 0 : _g.value) ||
|
|
1042
1067
|
'';
|
|
1043
|
-
const cliProtocol = ((
|
|
1068
|
+
const cliProtocol = ((_h = options.find((o) => o.name === 'coding-context-protocol')) === null || _h === void 0 ? void 0 : _h.value) || '';
|
|
1044
1069
|
const claudeSource = yield resolveClaudeContextSource(cliRepo, cliProtocol, autoYes);
|
|
1045
1070
|
if (claudeSource) {
|
|
1046
1071
|
yield syncClaudeContext(instanceDir, dryRun, claudeSource);
|
|
1047
1072
|
}
|
|
1048
1073
|
else {
|
|
1049
|
-
console.info(chalk.gray('\nSkipping .claude context sync (per user choice / --no-
|
|
1074
|
+
console.info(chalk.gray('\nSkipping .claude context sync (per user choice / --no-coding-context).\n'));
|
|
1050
1075
|
}
|
|
1051
1076
|
}
|
|
1052
1077
|
else {
|
|
1053
|
-
console.info(chalk.gray('\nSkipping .claude context sync (--no-
|
|
1078
|
+
console.info(chalk.gray('\nSkipping .claude context sync (--no-coding-context).\n'));
|
|
1079
|
+
}
|
|
1080
|
+
// 3. If web/server package.json changed during template update, install deps.
|
|
1081
|
+
// Skipped entirely under --no-template (no snapshots taken → nothing to compare).
|
|
1082
|
+
if (pkgSnapshots) {
|
|
1083
|
+
yield maybeInstallChangedDeps(pkgSnapshots, dryRun);
|
|
1054
1084
|
}
|
|
1055
|
-
// 3. If web/server package.json changed during template update, install deps
|
|
1056
|
-
yield maybeInstallChangedDeps(pkgSnapshots, dryRun);
|
|
1057
1085
|
});
|
|
1058
1086
|
}
|
|
1059
1087
|
}
|
|
@@ -21,6 +21,9 @@ class UpdateCommand extends abstract_command_1.AbstractCommand {
|
|
|
21
21
|
' (none) Pull updates from template into instance, sync .claude → .claude-internal/.agent,\n' +
|
|
22
22
|
' and run install in web/ or server/ if their package.json changed.\n' +
|
|
23
23
|
' diff Show differences between instance and template (read-only).\n\n' +
|
|
24
|
+
' Stage toggles (combine to run only what you want):\n' +
|
|
25
|
+
' --no-template Skip stage 1 (template merge) and stage 3 (auto-install).\n' +
|
|
26
|
+
' --no-coding-context Skip stage 2 (.claude context sync).\n\n' +
|
|
24
27
|
' Update strategies (per-file interactive prompt):\n' +
|
|
25
28
|
' skip Keep instance file as-is\n' +
|
|
26
29
|
' overwrite Replace with template version\n' +
|
|
@@ -31,9 +34,10 @@ class UpdateCommand extends abstract_command_1.AbstractCommand {
|
|
|
31
34
|
.option('-y, --yes', '[update] Skip per-file prompt, use conflict markers for all.')
|
|
32
35
|
.option('--no-smart-merge', '[update] Disable smart structural mergers; use line-level prompt for every file.')
|
|
33
36
|
.option('--no-version-check', '[update | diff] Skip the npm registry version check at startup.')
|
|
34
|
-
.option('--
|
|
35
|
-
.option('--
|
|
36
|
-
.option('--no-
|
|
37
|
+
.option('--coding-context-repo <url>', '[update] Fully override the .claude context repo URL (highest priority).')
|
|
38
|
+
.option('--coding-context-protocol <proto>', '[update] Force protocol for the built-in context repo. One of: https | ssh | skip.')
|
|
39
|
+
.option('--no-coding-context', '[update] Skip the .claude context sync stage entirely.')
|
|
40
|
+
.option('--no-template', '[update] Skip the template merge stage and the auto-install stage; only run .claude context sync.')
|
|
37
41
|
.action((subcommand, command) => __awaiter(this, void 0, void 0, function* () {
|
|
38
42
|
const inputs = [];
|
|
39
43
|
inputs.push({ name: 'subcommand', value: subcommand || '' });
|
|
@@ -44,17 +48,21 @@ class UpdateCommand extends abstract_command_1.AbstractCommand {
|
|
|
44
48
|
options.push({ name: 'smart-merge', value: command.smartMerge !== false });
|
|
45
49
|
options.push({ name: 'version-check', value: command.versionCheck !== false });
|
|
46
50
|
options.push({
|
|
47
|
-
name: '
|
|
51
|
+
name: 'coding-context-repo',
|
|
48
52
|
value: command.claudeContextRepo || '',
|
|
49
53
|
});
|
|
50
54
|
options.push({
|
|
51
|
-
name: '
|
|
55
|
+
name: 'coding-context-protocol',
|
|
52
56
|
value: command.claudeContextProtocol || '',
|
|
53
57
|
});
|
|
54
58
|
options.push({
|
|
55
|
-
name: '
|
|
59
|
+
name: 'coding-context',
|
|
56
60
|
value: command.claudeContext !== false,
|
|
57
61
|
});
|
|
62
|
+
options.push({
|
|
63
|
+
name: 'template',
|
|
64
|
+
value: command.template !== false,
|
|
65
|
+
});
|
|
58
66
|
yield this.action.handle(inputs, options);
|
|
59
67
|
}));
|
|
60
68
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gadmin2n/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.105",
|
|
4
4
|
"description": "Gadmin - modern, fast, powerful node.js web framework (@cli)",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@angular-devkit/core": "13.3.2",
|
|
48
48
|
"@angular-devkit/schematics": "13.3.2",
|
|
49
49
|
"@angular-devkit/schematics-cli": "13.3.2",
|
|
50
|
-
"@gadmin2n/schematics": "^0.0.
|
|
50
|
+
"@gadmin2n/schematics": "^0.0.86",
|
|
51
51
|
"abc": "^0.6.1",
|
|
52
52
|
"chalk": "3.0.0",
|
|
53
53
|
"chokidar": "3.5.3",
|