@boyingliu01/xp-gate 0.7.0 → 0.7.1
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/bin/xp-gate.js +4 -9
- package/lib/__tests__/audit-log.test.ts +107 -0
- package/lib/__tests__/detect-deps.test.js +179 -42
- package/lib/__tests__/doctor.test.js +1 -0
- package/lib/__tests__/gate-audit.test.ts +59 -0
- package/lib/__tests__/init.test.js +15 -0
- package/lib/__tests__/install-skill.test.js +1 -45
- package/lib/__tests__/ui-detector.test.ts +122 -8
- package/lib/__tests__/ui-review.test.ts +119 -0
- package/lib/__tests__/uninstall.test.js +1 -0
- package/lib/detect-deps.js +178 -37
- package/lib/doctor.js +7 -9
- package/lib/gate-audit.ts +26 -42
- package/lib/init.js +42 -13
- package/lib/install-skill.js +20 -48
- package/lib/rollback.js +1 -16
- package/lib/shared-paths.js +30 -0
- package/lib/shared-utils.js +25 -0
- package/lib/ui-detector.ts +3 -3
- package/lib/ui-review.ts +58 -53
- package/lib/uninstall.js +8 -9
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/skills/delphi-review/SKILL.md +112 -77
- package/skills/ralph-loop/SKILL.md +165 -34
- package/skills/sprint-flow/SKILL.md +428 -105
- package/skills/sprint-flow/references/components/skill-invocations.md +1 -1
- package/skills/sprint-flow/references/components/tool-descriptions.md +1 -1
- package/skills/sprint-flow/references/force-levels.md +203 -0
- package/skills/sprint-flow/references/phase-1-plan.md +4 -4
- package/skills/sprint-flow/references/phase-minus-0-5-auto-estimate.md +20 -1
- package/skills/sprint-flow/templates/auto-estimate-output-template.md +7 -5
- package/skills/sprint-flow/templates/sprint-progress-template.md +151 -0
- package/skills/test-specification-alignment/SKILL.md +98 -24
- package/plugins/qoder/AGENTS.md +0 -93
- package/plugins/qoder/README.md +0 -87
- package/plugins/qoder/widgets/quality-report.html +0 -163
- package/plugins/qoder/widgets/sprint-dashboard.html +0 -172
- package/skills/delphi-review/references/qoder-multi-model.md +0 -191
- package/skills/sprint-flow/references/qoder-adaptation.md +0 -173
package/lib/init.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
3
|
const crypto = require('crypto');
|
|
5
|
-
const { checkDeps } = require('./detect-deps.js');
|
|
6
|
-
const {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const GLOBAL_HOOKS_DIR = path.join(CONFIG_DIR, 'hooks');
|
|
15
|
-
const GLOBAL_ADAPTERS_DIR = path.join(CONFIG_DIR, 'adapters');
|
|
4
|
+
const { checkDeps, checkBash, autoInstallDeps, detectPlatform } = require('./detect-deps.js');
|
|
5
|
+
const {
|
|
6
|
+
HOME_DIR,
|
|
7
|
+
CONFIG_DIR,
|
|
8
|
+
CONFIG_FILE,
|
|
9
|
+
TEMPLATE_DIR,
|
|
10
|
+
GLOBAL_HOOKS_DIR,
|
|
11
|
+
GLOBAL_ADAPTERS_DIR,
|
|
12
|
+
} = require('./shared-paths.js');
|
|
16
13
|
|
|
17
14
|
function copyHooks(srcDir, destDir) {
|
|
18
15
|
['pre-commit', 'pre-push'].forEach(hook => {
|
|
@@ -54,6 +51,35 @@ function logDeps(depCheck) {
|
|
|
54
51
|
}
|
|
55
52
|
}
|
|
56
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Detect which AI agent platform is in use and check/auto-install deps.
|
|
56
|
+
* @param {string} platform - 'opencode' | 'claude-code' | 'qoder'
|
|
57
|
+
*/
|
|
58
|
+
async function checkAndInstallDeps(platform) {
|
|
59
|
+
const depCheck = await checkDeps(platform);
|
|
60
|
+
if (depCheck.ok) {
|
|
61
|
+
logDeps(depCheck);
|
|
62
|
+
return depCheck;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Auto-install missing deps
|
|
66
|
+
console.log(`Checking dependencies (platform: ${platform})...`);
|
|
67
|
+
const installResult = await autoInstallDeps(platform);
|
|
68
|
+
if (installResult.ok) {
|
|
69
|
+
console.log('Dependencies: OK (auto-installed)\n');
|
|
70
|
+
return { ok: true };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Auto-install failed — report and continue with warning
|
|
74
|
+
logDeps(depCheck);
|
|
75
|
+
if (installResult.errors) {
|
|
76
|
+
for (const err of installResult.errors) {
|
|
77
|
+
console.warn(` Auto-install failed for ${err.name}: ${err.message}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return depCheck;
|
|
81
|
+
}
|
|
82
|
+
|
|
57
83
|
function printUsage() {
|
|
58
84
|
console.log('Choose installation mode:');
|
|
59
85
|
console.log(' 1) Global — all git projects use the same hooks (recommended)');
|
|
@@ -228,7 +254,10 @@ async function init(args) {
|
|
|
228
254
|
console.warn(` ${bashCheck.message}\n`);
|
|
229
255
|
}
|
|
230
256
|
|
|
231
|
-
|
|
257
|
+
// Detect platform and check/auto-install dependencies
|
|
258
|
+
const platform = detectPlatform();
|
|
259
|
+
console.log(`Platform: ${platform}\n`);
|
|
260
|
+
await checkAndInstallDeps(platform);
|
|
232
261
|
|
|
233
262
|
const installMode = args.includes('--global') ? 'global' :
|
|
234
263
|
args.includes('--core-only') ? 'local' :
|
package/lib/install-skill.js
CHANGED
|
@@ -1,25 +1,14 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
3
|
const https = require('https');
|
|
5
4
|
const http = require('http');
|
|
6
5
|
const { execSync } = require('child_process');
|
|
7
|
-
const { checkDeps } = require('./detect-deps.js');
|
|
6
|
+
const { checkDeps, detectPlatform } = require('./detect-deps.js');
|
|
8
7
|
const { downloadFromGitHub } = require('./download-skill.js');
|
|
9
8
|
const { rollback } = require('./rollback.js');
|
|
9
|
+
const { HOME_DIR, CONFIG_DIR } = require('./shared-paths.js');
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
const HOME = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
13
|
-
|
|
14
|
-
const CONFIG_DIR = path.join(HOME, '.config', 'xp-gate');
|
|
15
|
-
const SKILLS_DIR = path.join(HOME, '.config', 'opencode', 'skills');
|
|
16
|
-
const QODER_SKILLS_DIR = path.join(HOME, '.qoder', 'skills');
|
|
17
|
-
|
|
18
|
-
// Platform-specific skill directories
|
|
19
|
-
const PLATFORM_SKILLS_DIRS = {
|
|
20
|
-
opencode: SKILLS_DIR,
|
|
21
|
-
qoder: QODER_SKILLS_DIR,
|
|
22
|
-
};
|
|
11
|
+
const SKILLS_DIR = path.join(HOME_DIR, '.config', 'opencode', 'skills');
|
|
23
12
|
|
|
24
13
|
const SKILLS_REGISTRY = {
|
|
25
14
|
'sprint-flow': { repo: 'boyingliu01/xp-gate', path: 'skills/sprint-flow' },
|
|
@@ -29,23 +18,21 @@ const SKILLS_REGISTRY = {
|
|
|
29
18
|
};
|
|
30
19
|
|
|
31
20
|
async function installSkill(name, options = {}) {
|
|
32
|
-
const { offline = false, verbose = false, force = false
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return 1;
|
|
48
|
-
}
|
|
21
|
+
const { offline = false, verbose = false, force = false } = options;
|
|
22
|
+
|
|
23
|
+
const platform = detectPlatform();
|
|
24
|
+
const depCheck = await checkDeps(platform);
|
|
25
|
+
if (!depCheck.ok) {
|
|
26
|
+
if (depCheck.missing) {
|
|
27
|
+
console.error(`Error: ${depCheck.missing} is required but not installed`);
|
|
28
|
+
console.error('Please install superpowers and gstack first');
|
|
29
|
+
console.error('See: https://github.com/boyingliu01/superpowers');
|
|
30
|
+
return 1;
|
|
31
|
+
}
|
|
32
|
+
if (depCheck.versionMismatch) {
|
|
33
|
+
console.error(`Error: ${depCheck.versionMismatch.name} version too old`);
|
|
34
|
+
console.error(`Need: ${depCheck.versionMismatch.required}, Found: ${depCheck.versionMismatch.found}`);
|
|
35
|
+
return 1;
|
|
49
36
|
}
|
|
50
37
|
}
|
|
51
38
|
|
|
@@ -56,8 +43,7 @@ async function installSkill(name, options = {}) {
|
|
|
56
43
|
return 1;
|
|
57
44
|
}
|
|
58
45
|
|
|
59
|
-
const
|
|
60
|
-
const targetDir = path.join(targetSkillsDir, name);
|
|
46
|
+
const targetDir = path.join(SKILLS_DIR, name);
|
|
61
47
|
if (fs.existsSync(targetDir) && !force) {
|
|
62
48
|
console.error(`Error: ${name} is already installed`);
|
|
63
49
|
console.error('Use --force to overwrite');
|
|
@@ -155,21 +141,7 @@ async function downloadFile(url, dest, verbose) {
|
|
|
155
141
|
});
|
|
156
142
|
}
|
|
157
143
|
|
|
158
|
-
|
|
159
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
160
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
161
|
-
|
|
162
|
-
for (const entry of entries) {
|
|
163
|
-
const srcPath = path.join(src, entry.name);
|
|
164
|
-
const destPath = path.join(dest, entry.name);
|
|
165
|
-
|
|
166
|
-
if (entry.isDirectory()) {
|
|
167
|
-
copyDirRecursive(srcPath, destPath);
|
|
168
|
-
} else {
|
|
169
|
-
fs.copyFileSync(srcPath, destPath);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
144
|
+
const { copyDirRecursive } = require('./shared-utils');
|
|
173
145
|
|
|
174
146
|
function ensureConfigDir() {
|
|
175
147
|
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
package/lib/rollback.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
|
+
const { copyDirRecursive } = require('./shared-utils');
|
|
4
5
|
|
|
5
6
|
// Cross-platform home directory resolution
|
|
6
7
|
const HOME = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
@@ -51,22 +52,6 @@ async function createBackup(installId, skillName) {
|
|
|
51
52
|
return backupDir;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
function copyDirRecursive(src, dest) {
|
|
55
|
-
fs.mkdirSync(dest, { recursive: true });
|
|
56
|
-
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
57
|
-
|
|
58
|
-
for (const entry of entries) {
|
|
59
|
-
const srcPath = path.join(src, entry.name);
|
|
60
|
-
const destPath = path.join(dest, entry.name);
|
|
61
|
-
|
|
62
|
-
if (entry.isDirectory()) {
|
|
63
|
-
copyDirRecursive(srcPath, destPath);
|
|
64
|
-
} else {
|
|
65
|
-
fs.copyFileSync(srcPath, destPath);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
55
|
function cleanupBackup(installId) {
|
|
71
56
|
const backupDir = path.join(BACKUP_DIR, installId);
|
|
72
57
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared filesystem path constants used across CLI modules.
|
|
3
|
+
* Cross-platform home directory resolution with os.homedir() fallback.
|
|
4
|
+
*
|
|
5
|
+
* @intent Eliminate duplicate path constants in init.js / uninstall.js / detect-deps.js
|
|
6
|
+
* @covers Issue #107 — duplicate code between init.js and uninstall.js
|
|
7
|
+
*/
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const os = require('os');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Resolve the user's home directory cross-platform.
|
|
13
|
+
* Fallback chain: HOME → USERPROFILE → os.homedir()
|
|
14
|
+
*/
|
|
15
|
+
const HOME_DIR = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
16
|
+
|
|
17
|
+
const CONFIG_DIR = path.join(HOME_DIR, '.config', 'xp-gate');
|
|
18
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'xp-gate.json');
|
|
19
|
+
const TEMPLATE_DIR = path.join(HOME_DIR, '.config', 'opencode', 'git-hooks-template');
|
|
20
|
+
const GLOBAL_HOOKS_DIR = path.join(CONFIG_DIR, 'hooks');
|
|
21
|
+
const GLOBAL_ADAPTERS_DIR = path.join(CONFIG_DIR, 'adapters');
|
|
22
|
+
|
|
23
|
+
module.exports = {
|
|
24
|
+
HOME_DIR,
|
|
25
|
+
CONFIG_DIR,
|
|
26
|
+
CONFIG_FILE,
|
|
27
|
+
TEMPLATE_DIR,
|
|
28
|
+
GLOBAL_HOOKS_DIR,
|
|
29
|
+
GLOBAL_ADAPTERS_DIR,
|
|
30
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure utility functions shared by CLI modules.
|
|
3
|
+
* No module-level state — safe for tests that mock fs/path/os.
|
|
4
|
+
*/
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Recursively copy a directory.
|
|
9
|
+
* Pure function: only uses fs/path params, no global config.
|
|
10
|
+
*/
|
|
11
|
+
function copyDirRecursive(src, dest) {
|
|
12
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
13
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
14
|
+
for (const entry of entries) {
|
|
15
|
+
const srcPath = require('path').join(src, entry.name);
|
|
16
|
+
const destPath = require('path').join(dest, entry.name);
|
|
17
|
+
if (entry.isDirectory()) {
|
|
18
|
+
copyDirRecursive(srcPath, destPath);
|
|
19
|
+
} else {
|
|
20
|
+
fs.copyFileSync(srcPath, destPath);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = { copyDirRecursive };
|
package/lib/ui-detector.ts
CHANGED
|
@@ -45,11 +45,11 @@ export function isExcluded(filePath: string, exclusions: string[]): boolean {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function matchGlob(filePath: string, pattern: string): boolean {
|
|
48
|
-
//
|
|
48
|
+
// Escape literal dots first, then replace placeholders with regex
|
|
49
49
|
const regex = pattern
|
|
50
|
+
.replace(/\./g, '\\.')
|
|
50
51
|
.replace(/_STARSTAR_/g, '.*')
|
|
51
|
-
.replace(/_STAR_/g, '[^/]*')
|
|
52
|
-
.replace(/\./g, '\\.');
|
|
52
|
+
.replace(/_STAR_/g, '[^/]*');
|
|
53
53
|
const re = new RegExp(`^${regex}$`);
|
|
54
54
|
return re.test(filePath);
|
|
55
55
|
}
|
package/lib/ui-review.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { collectUiMatches, parseRenamedFile } from './ui-detector';
|
|
|
5
5
|
|
|
6
6
|
const RESULT_FILE = '.ui-gate-result.json';
|
|
7
7
|
|
|
8
|
-
interface UiReviewResult {
|
|
8
|
+
export interface UiReviewResult {
|
|
9
9
|
commit: string;
|
|
10
10
|
verdict: string;
|
|
11
11
|
expires: string;
|
|
@@ -14,96 +14,101 @@ interface UiReviewResult {
|
|
|
14
14
|
ui_changes_detected: string[];
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
console.log('═══ xp-gate ui-review ═══');
|
|
20
|
-
|
|
21
|
-
console.log('');
|
|
22
|
-
|
|
23
|
-
// Get staged + modified files
|
|
17
|
+
export function getChangedFilesForReview(): string[] {
|
|
24
18
|
let files = '';
|
|
25
19
|
try {
|
|
26
20
|
files = execSync('git diff --cached --name-only && git diff --name-only', { encoding: 'utf8' }).trim();
|
|
27
21
|
} catch {
|
|
28
|
-
|
|
29
22
|
console.log('⚠️ No git repo or no changes. Running in current directory.');
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
const fileList = files
|
|
25
|
+
const fileList = parseFileList(files);
|
|
26
|
+
if (fileList.length > 0) {
|
|
27
|
+
return fileList;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log('No files staged or modified. Checking all tracked files.');
|
|
31
|
+
return getFallbackFileList();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function parseFileList(files: string): string[] {
|
|
35
|
+
return files
|
|
33
36
|
.split('\n')
|
|
34
37
|
.map(parseRenamedFile)
|
|
35
38
|
.filter(f => f.length > 0);
|
|
39
|
+
}
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
export function getFallbackFileList(): string[] {
|
|
42
|
+
try {
|
|
43
|
+
return parseFileList(execSync('git ls-files', { encoding: 'utf8' }).trim());
|
|
44
|
+
} catch {
|
|
45
|
+
const output = execSync(
|
|
46
|
+
'find . -maxdepth 3 -type f -name "*.ts" -o -name "*.html" -o -name "*.css" -o -name "*.scss" -o -name "*.tsx" -o -name "*.vue" -o -name "*.svelte" 2>/dev/null | grep -v node_modules | grep -v .git',
|
|
47
|
+
{ encoding: 'utf8' },
|
|
48
|
+
).trim();
|
|
49
|
+
return parseFileList(output);
|
|
46
50
|
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function buildUiReviewResult(matchedFiles: string[], now: Date = new Date()): UiReviewResult {
|
|
54
|
+
const commit = getCurrentCommit();
|
|
55
|
+
const expires = new Date(now.getTime() + 24 * 60 * 60 * 1000).toISOString();
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
commit,
|
|
59
|
+
verdict: 'APPROVED',
|
|
60
|
+
expires,
|
|
61
|
+
design_review: 'APPROVED',
|
|
62
|
+
browser_qa: 'APPROVED',
|
|
63
|
+
ui_changes_detected: matchedFiles,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function writeUiReviewResult(result: UiReviewResult, repoRoot: string = process.cwd()): void {
|
|
68
|
+
writeFileSync(join(repoRoot, RESULT_FILE), JSON.stringify(result, null, 2) + '\n', 'utf8');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function getCurrentCommit(): string {
|
|
72
|
+
return execSync('git rev-parse HEAD 2>/dev/null || echo "no-commit"', { encoding: 'utf8' }).trim();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function main(): void {
|
|
76
|
+
console.log('═══ xp-gate ui-review ═══');
|
|
77
|
+
console.log('');
|
|
47
78
|
|
|
79
|
+
const fileList = getChangedFilesForReview();
|
|
48
80
|
const result = collectUiMatches(fileList);
|
|
49
81
|
|
|
50
82
|
if (!result.isUiSprint) {
|
|
51
|
-
|
|
52
83
|
console.log('ℹ️ No UI changes detected in staged/modified files.');
|
|
53
|
-
|
|
54
84
|
console.log(' Nothing to review. Exiting.');
|
|
55
85
|
process.exit(0);
|
|
56
86
|
}
|
|
57
87
|
|
|
58
|
-
|
|
59
88
|
console.log(`🎨 UI changes detected (${result.matchedFiles.length} files):`);
|
|
60
89
|
result.matchedFiles.forEach(f => console.log(` - ${f}`));
|
|
61
|
-
|
|
62
90
|
console.log('');
|
|
63
91
|
|
|
64
|
-
|
|
65
92
|
console.log('Next steps required before push:');
|
|
66
|
-
|
|
67
93
|
console.log(' 1. Run /design-review in your AI agent session');
|
|
68
|
-
|
|
69
94
|
console.log(' 2. Run /qa or /qa-only in your AI agent session');
|
|
70
|
-
|
|
71
95
|
console.log(' 3. Ensure you have .delphi-config.json configured for Delphi review');
|
|
72
|
-
|
|
73
96
|
console.log('');
|
|
74
97
|
|
|
75
|
-
const
|
|
76
|
-
|
|
98
|
+
const uiResult = buildUiReviewResult(result.matchedFiles);
|
|
99
|
+
writeUiReviewResult(uiResult);
|
|
77
100
|
|
|
78
|
-
const uiResult: UiReviewResult = {
|
|
79
|
-
commit,
|
|
80
|
-
verdict: 'APPROVED',
|
|
81
|
-
expires,
|
|
82
|
-
design_review: 'APPROVED',
|
|
83
|
-
browser_qa: 'APPROVED',
|
|
84
|
-
ui_changes_detected: result.matchedFiles,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
writeFileSync(join(process.cwd(), RESULT_FILE), JSON.stringify(uiResult, null, 2) + '\n', 'utf8');
|
|
88
|
-
|
|
89
|
-
|
|
90
101
|
console.log(`✅ Generated ${RESULT_FILE} with APPROVED verdict (template)`);
|
|
91
|
-
|
|
92
|
-
console.log(`
|
|
93
|
-
|
|
94
|
-
console.log(` Expires: ${expires}`);
|
|
95
|
-
|
|
102
|
+
console.log(` Commit: ${uiResult.commit}`);
|
|
103
|
+
console.log(` Expires: ${uiResult.expires}`);
|
|
96
104
|
console.log('');
|
|
97
|
-
|
|
98
105
|
console.log('⚠️ REVIEW THIS FILE before push:');
|
|
99
|
-
|
|
100
106
|
console.log(' - Ensure design_review and browser_qa are actually APPROVED');
|
|
101
|
-
|
|
102
107
|
console.log(' - Edit verdict to REJECTED if issues found');
|
|
103
|
-
|
|
104
108
|
console.log('');
|
|
105
|
-
|
|
106
109
|
console.log('Then: git push (pre-push will validate this file)');
|
|
107
110
|
}
|
|
108
111
|
|
|
109
|
-
main
|
|
112
|
+
if (require.main === module) {
|
|
113
|
+
main();
|
|
114
|
+
}
|
package/lib/uninstall.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
3
|
const crypto = require('crypto');
|
|
4
|
+
const {
|
|
5
|
+
HOME_DIR,
|
|
6
|
+
CONFIG_DIR,
|
|
7
|
+
CONFIG_FILE,
|
|
8
|
+
TEMPLATE_DIR,
|
|
9
|
+
GLOBAL_HOOKS_DIR,
|
|
10
|
+
GLOBAL_ADAPTERS_DIR,
|
|
11
|
+
} = require('./shared-paths.js');
|
|
5
12
|
|
|
6
|
-
// Cross-platform home directory resolution
|
|
7
|
-
const HOME_DIR = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
8
|
-
|
|
9
|
-
const CONFIG_DIR = path.join(HOME_DIR, '.config', 'xp-gate');
|
|
10
|
-
const CONFIG_FILE = path.join(CONFIG_DIR, 'xp-gate.json');
|
|
11
|
-
const TEMPLATE_DIR = path.join(HOME_DIR, '.config', 'opencode', 'git-hooks-template');
|
|
12
|
-
const GLOBAL_HOOKS_DIR = path.join(CONFIG_DIR, 'hooks');
|
|
13
|
-
const GLOBAL_ADAPTERS_DIR = path.join(CONFIG_DIR, 'adapters');
|
|
14
13
|
const BACKUP_DIR = path.join(CONFIG_DIR, '.uninstall-backup');
|
|
15
14
|
|
|
16
15
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xp-gate",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"displayName": "XP-Gate",
|
|
5
5
|
"description": "Extreme Programming quality gates + AI workflow skills for Claude Code. Includes 6 quality gates, Sprint Flow, and Delphi multi-expert review.",
|
|
6
6
|
"author": {
|