@boyingliu01/xp-gate 0.15.3 → 0.15.4
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/adapter-common.sh +0 -0
- package/adapters/cpp.sh +0 -0
- package/adapters/dart.sh +0 -0
- package/adapters/flutter.sh +0 -0
- package/adapters/go.sh +0 -0
- package/adapters/iac.sh +0 -0
- package/adapters/java.sh +0 -0
- package/adapters/kotlin.sh +0 -0
- package/adapters/objectivec.sh +0 -0
- package/adapters/plugins/book299-20132-python/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-20132-python/scripts/install-maven.sh +0 -0
- package/adapters/plugins/book299-4081-c/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-4081-c/scripts/install-maven.sh +0 -0
- package/adapters/plugins/book299-4083-javascriptes5/scripts/install-gradle.sh +0 -0
- package/adapters/plugins/book299-4083-javascriptes5/scripts/install-maven.sh +0 -0
- package/adapters/powershell.sh +0 -0
- package/adapters/python.sh +0 -0
- package/adapters/shell.sh +0 -0
- package/adapters/swift.sh +0 -0
- package/adapters/typescript.sh +0 -0
- package/bin/xp-gate.js +1 -1
- package/gate-3.sh +0 -0
- package/gate-4.sh +0 -0
- package/gate-7.sh +0 -0
- package/gate-8.sh +0 -0
- package/gate-9.sh +0 -0
- package/hooks/adapter-common.sh +0 -0
- package/hooks/gate-3.sh +0 -0
- package/hooks/gate-4.sh +0 -0
- package/hooks/gate-7.sh +0 -0
- package/hooks/gate-8.sh +0 -0
- package/hooks/gate-9.sh +0 -0
- package/hooks/pre-commit +9 -0
- package/hooks/pre-push +0 -0
- package/lib/__tests__/install-cmd.test.js +0 -35
- package/lib/__tests__/phase-transition.test.js +272 -272
- package/lib/__tests__/sprint-audit.test.js +207 -207
- package/lib/doctor-tui.js +198 -0
- package/lib/doctor.js +95 -179
- package/lib/init.js +6 -4
- package/lib/install-cmd.js +31 -42
- package/lib/language-tools.js +833 -833
- package/lib/phase-transition.js +249 -249
- package/lib/sprint-audit.js +235 -235
- package/lib/uninstall.js +22 -0
- package/mock-policy/AGENTS.md +2 -2
- package/mutation/AGENTS.md +2 -2
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/bin/delphi-review-guard.sh +0 -0
- package/plugins/claude-code/bin/xp-gate-check +0 -0
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/claude-code/skills/sprint-flow/references/phase-4-verify.md +10 -0
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/opencode/skills/sprint-flow/references/phase-4-verify.md +10 -0
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +2 -2
- package/plugins/qoder/agents/delphi-architecture.md +48 -48
- package/plugins/qoder/agents/delphi-feasibility.md +48 -48
- package/plugins/qoder/agents/delphi-technical.md +48 -48
- package/plugins/qoder/bin/sprint-flow-guard.sh +0 -0
- package/plugins/qoder/bin/xp-gate-check +0 -0
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +2 -2
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +2 -2
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +2 -2
- package/principles/AGENTS.md +2 -2
- package/scripts/delphi-external-review.cjs +432 -432
- package/skills/delphi-review/AGENTS.md +2 -2
- package/skills/sprint-flow/AGENTS.md +2 -2
- package/skills/sprint-flow/references/phase-4-verify.md +10 -0
- package/skills/test-specification-alignment/AGENTS.md +2 -2
package/lib/doctor.js
CHANGED
|
@@ -15,6 +15,10 @@ const {
|
|
|
15
15
|
} = require('./shared-paths.js');
|
|
16
16
|
const { checkUpgrade, formatUpgradeMsg } = require('./check-version.js');
|
|
17
17
|
const { GATE_CLI_TOOLS, checkCliTool, getToolInstallCmd } = require('./detect-deps.js');
|
|
18
|
+
const {
|
|
19
|
+
diagnoseTuiRegistration, diagnoseInstalledSkills,
|
|
20
|
+
fixTuiRegistration, ensureTuiRegistration, readTuiJson,
|
|
21
|
+
} = require('./doctor-tui.js');
|
|
18
22
|
|
|
19
23
|
// npm package source dir (template hooks/adapters)
|
|
20
24
|
const PKG_DIR = path.dirname(__dirname);
|
|
@@ -779,6 +783,8 @@ function fixIssues(checks, config) {
|
|
|
779
783
|
fixed = fixGlobalHooksPath(config) || fixed;
|
|
780
784
|
fixed = fixMissingAdapters(config.mode, srcDir, getAdaptersDirByMode(config)) || fixed;
|
|
781
785
|
fixed = fixMissingGateScripts(srcDir, getAdaptersDirByMode(config)) || fixed;
|
|
786
|
+
fixed = fixStaleHooks(config) || fixed;
|
|
787
|
+
fixed = fixMissingCliTools() || fixed;
|
|
782
788
|
fixed = fixTuiRegistration() || fixed;
|
|
783
789
|
fixed = printCliToolGuidance() || fixed;
|
|
784
790
|
|
|
@@ -787,6 +793,65 @@ function fixIssues(checks, config) {
|
|
|
787
793
|
}
|
|
788
794
|
}
|
|
789
795
|
|
|
796
|
+
/**
|
|
797
|
+
* Sync hooks/adapters from the installed package to the project/global dir.
|
|
798
|
+
* Catches cases where xp-gate was upgraded (npm update) but hooks are stale.
|
|
799
|
+
* Uses update-hooks logic with --force to overwrite outdated files.
|
|
800
|
+
*
|
|
801
|
+
* @returns {boolean} Whether any hooks were updated
|
|
802
|
+
*/
|
|
803
|
+
function fixStaleHooks(config) {
|
|
804
|
+
try {
|
|
805
|
+
const { updateHooks, detectLocalModifications, getPackageRoot } = require('./update-hooks.js');
|
|
806
|
+
const isGlobal = config.mode === 'global';
|
|
807
|
+
const srcDir = getPackageRoot();
|
|
808
|
+
|
|
809
|
+
let hooksDestDir, adaptersDestDir;
|
|
810
|
+
if (isGlobal) {
|
|
811
|
+
hooksDestDir = GLOBAL_HOOKS_DIR;
|
|
812
|
+
adaptersDestDir = GLOBAL_ADAPTERS_DIR;
|
|
813
|
+
} else {
|
|
814
|
+
const gitDir = getGitDirSync();
|
|
815
|
+
if (!gitDir) return false;
|
|
816
|
+
const projectRoot = path.dirname(gitDir);
|
|
817
|
+
hooksDestDir = path.join(gitDir, 'hooks');
|
|
818
|
+
adaptersDestDir = path.join(projectRoot, 'githooks');
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Check if any hook files are stale (differ from package version)
|
|
822
|
+
const modified = detectLocalModifications(srcDir, hooksDestDir, adaptersDestDir);
|
|
823
|
+
if (modified.length === 0) return false;
|
|
824
|
+
|
|
825
|
+
console.log(` Syncing ${modified.length} outdated hook/adapter file(s)...`);
|
|
826
|
+
updateHooks({ global: isGlobal, force: true, dryRun: false, noBackup: true, scope: 'all' });
|
|
827
|
+
return true;
|
|
828
|
+
} catch {
|
|
829
|
+
return false;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Auto-install missing CLI tools via bootstrap.
|
|
835
|
+
* @returns {boolean} Whether any tools were installed
|
|
836
|
+
*/
|
|
837
|
+
function fixMissingCliTools() {
|
|
838
|
+
try {
|
|
839
|
+
const { bootstrap } = require('./bootstrap.js');
|
|
840
|
+
const missing = [];
|
|
841
|
+
for (const entry of GATE_CLI_TOOLS) {
|
|
842
|
+
const { available } = checkCliTool(entry.tool);
|
|
843
|
+
if (!available) missing.push(entry.tool);
|
|
844
|
+
}
|
|
845
|
+
if (missing.length === 0) return false;
|
|
846
|
+
|
|
847
|
+
console.log(` Auto-installing ${missing.length} missing CLI tool(s)...`);
|
|
848
|
+
const code = bootstrap([]);
|
|
849
|
+
return code === 0;
|
|
850
|
+
} catch {
|
|
851
|
+
return false;
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
790
855
|
/**
|
|
791
856
|
* Print install guidance for missing CLI tools.
|
|
792
857
|
* Does NOT auto-install — just shows commands.
|
|
@@ -880,184 +945,6 @@ function diagnoseOpenCodePlugin(checks) {
|
|
|
880
945
|
return 0;
|
|
881
946
|
}
|
|
882
947
|
|
|
883
|
-
/**
|
|
884
|
-
* TUI registration path and expected plugin entry.
|
|
885
|
-
*/
|
|
886
|
-
const TUI_JSON_PATH = path.join(HOME_DIR, '.config', 'opencode', 'tui.json');
|
|
887
|
-
const TUI_PLUGIN_ENTRY = '@boyingliu01/opencode-plugin/tui';
|
|
888
|
-
|
|
889
|
-
/**
|
|
890
|
-
* Read and parse tui.json, returning { data, error }.
|
|
891
|
-
* data = parsed object on success, null on file missing, undefined on corrupt.
|
|
892
|
-
*/
|
|
893
|
-
function readTuiJson() {
|
|
894
|
-
if (!fs.existsSync(TUI_JSON_PATH)) return { data: null, error: null };
|
|
895
|
-
try {
|
|
896
|
-
const raw = fs.readFileSync(TUI_JSON_PATH, 'utf8');
|
|
897
|
-
const data = JSON.parse(raw);
|
|
898
|
-
return { data, error: null };
|
|
899
|
-
} catch (e) {
|
|
900
|
-
return { data: undefined, error: `Corrupt JSON: ${e.message}` };
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
/**
|
|
905
|
-
* Check 9: TUI auto-registration in ~/.config/opencode/tui.json.
|
|
906
|
-
* @returns {number} issue count
|
|
907
|
-
*/
|
|
908
|
-
function diagnoseTuiRegistration(checks) {
|
|
909
|
-
const { data, error } = readTuiJson();
|
|
910
|
-
|
|
911
|
-
if (error) {
|
|
912
|
-
checks.push({ name: 'TUI registration', status: 'FAIL', detail: error });
|
|
913
|
-
return 1;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
if (data === null) {
|
|
917
|
-
checks.push({ name: 'TUI registration', status: 'FAIL', detail: 'Not registered' });
|
|
918
|
-
return 1;
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
const plugins = Array.isArray(data.plugin) ? data.plugin : [];
|
|
922
|
-
if (plugins.includes(TUI_PLUGIN_ENTRY)) {
|
|
923
|
-
checks.push({ name: 'TUI registration', status: 'PASS', detail: `${TUI_PLUGIN_ENTRY} registered` });
|
|
924
|
-
return 0;
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
checks.push({ name: 'TUI registration', status: 'FAIL', detail: 'Not registered' });
|
|
928
|
-
return 1;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
/**
|
|
932
|
-
* Check 9: Installed skills vs package-bundled skills (#332).
|
|
933
|
-
* Reports WARN for each installed skill whose SKILL.md differs from the bundled version.
|
|
934
|
-
* @param {object} config
|
|
935
|
-
* @param {Array} checks
|
|
936
|
-
* @returns {number} issue count
|
|
937
|
-
*/
|
|
938
|
-
function diagnoseInstalledSkills(config, checks) {
|
|
939
|
-
const installedSkills = config.installedSkills || {};
|
|
940
|
-
const skillNames = Object.keys(installedSkills);
|
|
941
|
-
if (skillNames.length === 0) {
|
|
942
|
-
checks.push({ name: 'Installed skills', status: 'SKIP', detail: 'No skills installed' });
|
|
943
|
-
return 0;
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
const bundledSkillsDir = path.join(PKG_DIR, 'skills');
|
|
947
|
-
if (!fs.existsSync(bundledSkillsDir)) {
|
|
948
|
-
checks.push({ name: 'Installed skills', status: 'SKIP', detail: 'Package skills dir not found' });
|
|
949
|
-
return 0;
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
const platform = detectPlatform();
|
|
953
|
-
let userSkillsDir;
|
|
954
|
-
if (platform === 'qoder') {
|
|
955
|
-
userSkillsDir = path.join(HOME_DIR, '.qoder', 'skills');
|
|
956
|
-
} else if (platform === 'claude-code') {
|
|
957
|
-
userSkillsDir = path.join(HOME_DIR, '.claude', 'skills');
|
|
958
|
-
} else {
|
|
959
|
-
userSkillsDir = path.join(HOME_DIR, '.config', 'opencode', 'skills');
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
let issues = 0;
|
|
963
|
-
for (const name of skillNames) {
|
|
964
|
-
const bundledSkillMd = path.join(bundledSkillsDir, name, 'SKILL.md');
|
|
965
|
-
const userSkillMd = path.join(userSkillsDir, name, 'SKILL.md');
|
|
966
|
-
|
|
967
|
-
if (!fs.existsSync(bundledSkillMd)) continue;
|
|
968
|
-
if (!fs.existsSync(userSkillMd)) {
|
|
969
|
-
checks.push({ name: `Skill: ${name}`, status: 'FAIL', detail: 'Installed SKILL.md missing' });
|
|
970
|
-
issues++;
|
|
971
|
-
continue;
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
const bundledContent = fs.readFileSync(bundledSkillMd, 'utf8');
|
|
975
|
-
const userContent = fs.readFileSync(userSkillMd, 'utf8');
|
|
976
|
-
if (bundledContent !== userContent) {
|
|
977
|
-
checks.push({
|
|
978
|
-
name: `Skill: ${name}`,
|
|
979
|
-
status: 'WARN',
|
|
980
|
-
detail: 'Outdated — run xp-gate update-skill --all',
|
|
981
|
-
});
|
|
982
|
-
issues++;
|
|
983
|
-
} else {
|
|
984
|
-
checks.push({ name: `Skill: ${name}`, status: 'PASS', detail: 'Up to date' });
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
return issues;
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
/**
|
|
992
|
-
* Fix TUI registration: ensure @boyingliu01/opencode-plugin/tui is in tui.json.
|
|
993
|
-
* Uses atomic write (tmp + renameSync) for JSON safety.
|
|
994
|
-
* On corrupt JSON: backup to .corrupt-{timestamp}.bak then rebuild.
|
|
995
|
-
* Idempotent: skips if already registered.
|
|
996
|
-
* @returns {boolean} Whether a fix was applied
|
|
997
|
-
*/
|
|
998
|
-
function fixTuiRegistration() {
|
|
999
|
-
const { data, error } = readTuiJson();
|
|
1000
|
-
|
|
1001
|
-
// Corrupt JSON: backup old file, rebuild from scratch
|
|
1002
|
-
if (error && data === undefined) {
|
|
1003
|
-
const ts = Date.now();
|
|
1004
|
-
const backupPath = `${TUI_JSON_PATH}.corrupt-${ts}.bak`;
|
|
1005
|
-
try {
|
|
1006
|
-
fs.copyFileSync(TUI_JSON_PATH, backupPath);
|
|
1007
|
-
console.log(` ⚠ TUI config corrupted — backed up to ${backupPath}`);
|
|
1008
|
-
} catch { /* non-critical */ }
|
|
1009
|
-
// Rebuild fresh
|
|
1010
|
-
const dir = path.dirname(TUI_JSON_PATH);
|
|
1011
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
1012
|
-
const newConfig = { plugin: [TUI_PLUGIN_ENTRY] };
|
|
1013
|
-
const tmpPath = `${TUI_JSON_PATH}.tmp`;
|
|
1014
|
-
fs.writeFileSync(tmpPath, JSON.stringify(newConfig, null, 2));
|
|
1015
|
-
fs.renameSync(tmpPath, TUI_JSON_PATH);
|
|
1016
|
-
console.log(` ✓ Registered ${TUI_PLUGIN_ENTRY} in TUI (rebuilt after corrupt backup)`);
|
|
1017
|
-
return true;
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
// File doesn't exist: create it
|
|
1021
|
-
if (data === null) {
|
|
1022
|
-
const dir = path.dirname(TUI_JSON_PATH);
|
|
1023
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
1024
|
-
const newConfig = { plugin: [TUI_PLUGIN_ENTRY] };
|
|
1025
|
-
const tmpPath = `${TUI_JSON_PATH}.tmp`;
|
|
1026
|
-
fs.writeFileSync(tmpPath, JSON.stringify(newConfig, null, 2));
|
|
1027
|
-
fs.renameSync(tmpPath, TUI_JSON_PATH);
|
|
1028
|
-
console.log(` ✓ Created TUI config with ${TUI_PLUGIN_ENTRY}`);
|
|
1029
|
-
return true;
|
|
1030
|
-
}
|
|
1031
|
-
|
|
1032
|
-
// File exists, check if plugin already registered (idempotent)
|
|
1033
|
-
const plugins = Array.isArray(data.plugin) ? data.plugin : [];
|
|
1034
|
-
if (plugins.includes(TUI_PLUGIN_ENTRY)) return false;
|
|
1035
|
-
|
|
1036
|
-
// Append plugin entry
|
|
1037
|
-
data.plugin = plugins.concat([TUI_PLUGIN_ENTRY]);
|
|
1038
|
-
const tmpPath = `${TUI_JSON_PATH}.tmp`;
|
|
1039
|
-
fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
|
|
1040
|
-
fs.renameSync(tmpPath, TUI_JSON_PATH);
|
|
1041
|
-
console.log(` ✓ Added ${TUI_PLUGIN_ENTRY} to TUI config`);
|
|
1042
|
-
return true;
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
/**
|
|
1046
|
-
* Wrapper for init.js: ensure TUI is registered without console output.
|
|
1047
|
-
* Returns true if the plugin is already registered or was just added.
|
|
1048
|
-
*/
|
|
1049
|
-
function ensureTuiRegistration() {
|
|
1050
|
-
const { data } = readTuiJson();
|
|
1051
|
-
if (data === null || data === undefined) {
|
|
1052
|
-
fixTuiRegistration();
|
|
1053
|
-
return;
|
|
1054
|
-
}
|
|
1055
|
-
const plugins = Array.isArray(data.plugin) ? data.plugin : [];
|
|
1056
|
-
if (!plugins.includes(TUI_PLUGIN_ENTRY)) {
|
|
1057
|
-
fixTuiRegistration();
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
948
|
/**
|
|
1062
949
|
* Diagnose language-specific tool availability.
|
|
1063
950
|
* @returns {number} Number of issues found (missing required tools)
|
|
@@ -1149,7 +1036,36 @@ async function doctor(args) {
|
|
|
1149
1036
|
issues += diagnoseOpenCodePlugin(checks);
|
|
1150
1037
|
|
|
1151
1038
|
// Language-specific tool status
|
|
1152
|
-
|
|
1039
|
+
const langIssues = diagnoseLanguageTools();
|
|
1040
|
+
issues += langIssues;
|
|
1041
|
+
|
|
1042
|
+
// In --fix mode, auto-install missing language tools
|
|
1043
|
+
if (fixMode && langIssues > 0) {
|
|
1044
|
+
try {
|
|
1045
|
+
const { detectProjectLanguages, getToolsForLanguages, installTool, generateProjectConfig, LANGUAGE_REGISTRY } = require('./language-tools.js');
|
|
1046
|
+
const projectRoot = process.cwd();
|
|
1047
|
+
const { detected } = detectProjectLanguages(projectRoot);
|
|
1048
|
+
if (detected.length > 0) {
|
|
1049
|
+
const toolStatus = getToolsForLanguages(detected);
|
|
1050
|
+
let installed = 0;
|
|
1051
|
+
for (const [lang, tools] of Object.entries(toolStatus)) {
|
|
1052
|
+
for (const t of tools) {
|
|
1053
|
+
if (t.status === 'missing') {
|
|
1054
|
+
const result = installTool(t.tool);
|
|
1055
|
+
if (result.success) {
|
|
1056
|
+
console.log(` \u2713 ${t.tool.name} (${lang}) installed`);
|
|
1057
|
+
installed++;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
if (installed > 0) {
|
|
1063
|
+
console.log(` ${installed} language tool(s) installed.`);
|
|
1064
|
+
generateProjectConfig(projectRoot, detected, toolStatus);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
} catch { /* non-blocking */ }
|
|
1068
|
+
}
|
|
1153
1069
|
|
|
1154
1070
|
if (issues === 0) {
|
|
1155
1071
|
console.log('\n✓ All checks passed');
|
package/lib/init.js
CHANGED
|
@@ -341,14 +341,16 @@ async function init(args) {
|
|
|
341
341
|
if (!installMode) { printUsage(); return 0; }
|
|
342
342
|
if (installMode === 'global') return setupGlobal(args);
|
|
343
343
|
const code = await installLocal(args);
|
|
344
|
-
|
|
344
|
+
// Always create lint baseline for local installs (Boy Scout Rule needs it)
|
|
345
|
+
// --baseline flag is now a no-op (baseline is always created)
|
|
346
|
+
if (code === 0) {
|
|
345
347
|
try {
|
|
346
348
|
const { createBaseline } = require('./baseline.js');
|
|
347
|
-
console.log('\nCreating lint baseline...');
|
|
349
|
+
console.log('\nCreating lint baseline (Boy Scout Rule)...');
|
|
348
350
|
const baseline = await createBaseline();
|
|
349
|
-
console.log(
|
|
351
|
+
console.log(`\u2705 Lint baseline created \u2014 ${Object.keys(baseline).length} files tracked.`);
|
|
350
352
|
} catch (e) {
|
|
351
|
-
console.log(
|
|
353
|
+
console.log(`\u2139\ufe0f Lint baseline creation skipped: ${e.message}`);
|
|
352
354
|
}
|
|
353
355
|
}
|
|
354
356
|
return code;
|
package/lib/install-cmd.js
CHANGED
|
@@ -1,57 +1,46 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* One-step install command — delegates to init + doctor.
|
|
3
|
+
*
|
|
4
|
+
* `xp-gate install` is the single entry point for new users.
|
|
5
|
+
* It wraps init (which already handles hooks, bootstrap, language detection,
|
|
6
|
+
* and baseline creation) and follows up with doctor --fix for health verification.
|
|
7
|
+
*
|
|
8
|
+
* Local mode: init --core-only --yes → auto baseline → doctor --fix
|
|
9
|
+
* Global mode: init --global --yes → doctor --fix
|
|
10
|
+
*/
|
|
2
11
|
const path = require('path');
|
|
3
|
-
const { execSync } = require('child_process');
|
|
4
|
-
const { copyHooks, copyAdapters } = require('./shared-utils');
|
|
5
12
|
|
|
6
|
-
async function install(args = []
|
|
13
|
+
async function install(args = []) {
|
|
7
14
|
const isGlobal = args.includes('--global');
|
|
8
15
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
return setupLocal(cwd);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function setupGlobal(cwd) {
|
|
16
|
-
const srcDir = path.dirname(__dirname);
|
|
17
|
-
|
|
18
|
-
const globalHooksDir = path.join(require('os').homedir(), '.config', 'xp-gate', 'hooks');
|
|
19
|
-
const globalAdaptersDir = path.join(require('os').homedir(), '.config', 'xp-gate', 'adapters');
|
|
16
|
+
console.log('XP-Gate One-Step Install');
|
|
17
|
+
console.log('========================\n');
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
// Build init args: always auto-yes for non-interactive friendliness
|
|
20
|
+
const initArgs = isGlobal ? ['--global', '--yes'] : ['--core-only', '--yes'];
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const { init } = require('./init.js');
|
|
23
|
+
const code = await init(initArgs);
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
if (code !== 0) {
|
|
26
|
+
console.error('\nInstallation encountered errors.');
|
|
27
|
+
console.error('Run "xp-gate doctor" for diagnostics.');
|
|
28
|
+
return code;
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
31
|
+
// Post-install: run doctor --fix to verify and auto-repair
|
|
32
|
+
console.log('\n━━━ Post-Install Health Check ━━━\n');
|
|
33
|
+
const { doctor } = require('./doctor.js');
|
|
34
|
+
const doctorCode = await doctor(['--fix']);
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
console.
|
|
41
|
-
|
|
36
|
+
if (doctorCode === 0) {
|
|
37
|
+
console.log('\n✓ Installation complete and verified!');
|
|
38
|
+
} else {
|
|
39
|
+
console.log('\n⚠ Installation complete, but some issues remain.');
|
|
40
|
+
console.log(' Run "xp-gate doctor" for details.');
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
const hooksDir = path.join(gitDir, 'hooks');
|
|
46
|
-
const githooksDir = path.join(projectRoot, 'githooks');
|
|
47
|
-
|
|
48
|
-
copyHooks(srcDir, hooksDir);
|
|
49
|
-
|
|
50
|
-
fs.mkdirSync(path.join(githooksDir, 'adapters'), { recursive: true });
|
|
51
|
-
copyAdapters(srcDir, githooksDir);
|
|
52
|
-
|
|
53
|
-
console.log('Local installation complete.');
|
|
54
|
-
return 0;
|
|
43
|
+
return doctorCode;
|
|
55
44
|
}
|
|
56
45
|
|
|
57
46
|
module.exports = { install };
|