@ngocsangairvds/vsaf 4.0.8 → 4.0.10
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/package.json +1 -1
- package/packages/cli/dist/commands/install.d.ts.map +1 -1
- package/packages/cli/dist/commands/install.js +12 -0
- package/packages/cli/dist/commands/install.js.map +1 -1
- package/skills/sdlc/install-deps.mjs +19 -3
- package/skills/vds-skill/_shared/config-check.js +76 -0
- package/skills/vds-skill/_shared/credentials.js +139 -0
- package/skills/vds-skill/create-bitbucket-pr/SKILL.md +2 -18
- package/skills/vds-skill/create-bitbucket-pr/scripts/create-pr.js +125 -0
- package/skills/vds-skill/create-jira-epic/SKILL.md +2 -20
- package/skills/vds-skill/create-jira-epic/scripts/create-epic.js +120 -0
- package/skills/vds-skill/install-deps.mjs +34 -32
- package/skills/vds-skill/pull/SKILL.md +1 -1
- package/skills/vds-skill/pull/scripts/pull.js +59 -0
- package/skills/vds-skill/push-prd/SKILL.md +11 -30
- package/skills/vds-skill/push-srs/SKILL.md +4 -23
- package/skills/vds-skill/search-confluence/SKILL.md +2 -22
- package/skills/vds-skill/search-confluence/scripts/search.js +114 -0
- package/skills/vds-skill/vds-scripts-skill/SKILL.md +3 -3
- package/skills/vds-skill/_shared/credentials.sh +0 -79
- package/skills/vds-skill/create-bitbucket-pr/scripts/create-pr.sh +0 -105
- package/skills/vds-skill/create-jira-epic/scripts/create-epic.sh +0 -113
- package/skills/vds-skill/pull/scripts/pull.sh +0 -52
- package/skills/vds-skill/search-confluence/scripts/search.sh +0 -128
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAGD,MAAM,WAAW,sBAAuB,SAAQ,cAAc;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAOD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/C,YAAY,CA4Bd;AAUD,wBAAgB,aAAa,IAAI,WAAW,EAAE,CAU7C;AAqCD,wBAAgB,eAAe,IAAI,cAAc,CAqDhD;
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,cAAc,CAAC;CAC1B;AAGD,MAAM,WAAW,sBAAuB,SAAQ,cAAc;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAOD,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC/C,YAAY,CA4Bd;AAUD,wBAAgB,aAAa,IAAI,WAAW,EAAE,CAU7C;AAqCD,wBAAgB,eAAe,IAAI,cAAc,CAqDhD;AA+BD,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,sBAA2B,GACnC,aAAa,CAoGf"}
|
|
@@ -160,8 +160,20 @@ const PLUGIN_GUIDE = `
|
|
|
160
160
|
│ apt install gh (Linux) │
|
|
161
161
|
│ winget install GitHub.cli (Windows) │
|
|
162
162
|
└──────────────────────────────────────────────────────────┘`;
|
|
163
|
+
// ── Node.js LTS Check ──
|
|
164
|
+
function checkNodeLTS() {
|
|
165
|
+
const major = parseInt(process.versions.node.split('.')[0], 10);
|
|
166
|
+
if (major % 2 !== 0) {
|
|
167
|
+
console.warn(`\n⚠️ Node.js v${process.versions.node} is a non-LTS (Current) release.`);
|
|
168
|
+
console.warn(' Native npm packages (e.g. gitnexus) may fail to build.');
|
|
169
|
+
console.warn(' Recommended: switch to an LTS version (v22 or v24).');
|
|
170
|
+
console.warn(' → nvm install --lts OR brew install node@24\n');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
163
173
|
// ── Main Install ──
|
|
164
174
|
function execInstall(pack, projectPath, options = {}) {
|
|
175
|
+
// Check Node.js version early — native modules break on non-LTS
|
|
176
|
+
checkNodeLTS();
|
|
165
177
|
// Validate pack is a simple name (no URLs or paths)
|
|
166
178
|
if (pack.includes('/') || pack.includes('\\') || pack.startsWith('.') || pack.startsWith('http')) {
|
|
167
179
|
throw new Error(`"${pack}" is not a valid pack name. Use a built-in pack name (e.g. "sdlc").`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":";;AAkDA,oCAkCC;AAUD,sCAUC;AAqCD,0CAqDC;
|
|
1
|
+
{"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,16 +22,28 @@ import { execSync } from 'child_process';
|
|
|
22
22
|
const projectPath = process.argv[2] || process.cwd();
|
|
23
23
|
const isWin = process.platform === 'win32';
|
|
24
24
|
|
|
25
|
+
// ── Node.js LTS Check ──
|
|
26
|
+
// Even-numbered major versions (22, 24, 26…) are LTS; odd (23, 25…) are Current/non-LTS.
|
|
27
|
+
// Native modules (e.g. @ladybugdb/core in gitnexus) may not build on non-LTS versions.
|
|
28
|
+
|
|
29
|
+
const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
|
|
30
|
+
if (nodeMajor % 2 !== 0) {
|
|
31
|
+
console.warn(`\n⚠️ Node.js v${process.versions.node} is a non-LTS (Current) release.`);
|
|
32
|
+
console.warn(' Native npm packages (e.g. gitnexus) may fail to build.');
|
|
33
|
+
console.warn(' Recommended: switch to an LTS version (v22 or v24).');
|
|
34
|
+
console.warn(' → nvm install --lts OR brew install node@24\n');
|
|
35
|
+
}
|
|
36
|
+
|
|
25
37
|
// ── Config ──
|
|
26
38
|
|
|
27
39
|
const NPM_DEPS = [
|
|
28
|
-
{ pkg: 'gitnexus', bin: 'gitnexus' },
|
|
40
|
+
{ pkg: 'gitnexus', bin: 'gitnexus', installAs: 'gitnexus@latest' },
|
|
29
41
|
];
|
|
30
42
|
|
|
31
43
|
const MCP_ENTRY = {
|
|
32
44
|
gitnexus: {
|
|
33
45
|
command: 'npx',
|
|
34
|
-
args: ['gitnexus', 'mcp'],
|
|
46
|
+
args: ['-y', 'gitnexus@latest', 'mcp'],
|
|
35
47
|
env: {},
|
|
36
48
|
},
|
|
37
49
|
};
|
|
@@ -87,7 +99,11 @@ if (missing.length > 0) {
|
|
|
87
99
|
try {
|
|
88
100
|
// --ignore-scripts avoids rebuilding unrelated project deps (e.g. onnxruntime-node)
|
|
89
101
|
// Then rebuild only our deps to run their postinstall scripts
|
|
90
|
-
|
|
102
|
+
const installNames = missing.map(pkg => {
|
|
103
|
+
const dep = NPM_DEPS.find(d => d.pkg === pkg);
|
|
104
|
+
return dep?.installAs || pkg;
|
|
105
|
+
});
|
|
106
|
+
execSync(`npm install --save-dev --ignore-scripts ${installNames.join(' ')}`, {
|
|
91
107
|
cwd: projectPath,
|
|
92
108
|
stdio: 'inherit',
|
|
93
109
|
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* config-check.js — reusable pre-flight checker for vds-skill-* SKILL.md files.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node config-check.js --cmd vds-cli --env VDS_CONFLUENCE_TOKEN,VDS_CONFLUENCE_SPACE_DEFAULT
|
|
9
|
+
*
|
|
10
|
+
* Output (matches legacy bash format):
|
|
11
|
+
* OK — all checks pass
|
|
12
|
+
* BLOCKED — missing: vds-cli VDS_JIRA_TOKEN — something missing
|
|
13
|
+
* NOTE: /path/to/cmd exists but fails to run — broken shim detected
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { join } = require('path');
|
|
17
|
+
const { loadCredentials, requireCommand } = require('./credentials.js');
|
|
18
|
+
|
|
19
|
+
// ── Parse args ──
|
|
20
|
+
|
|
21
|
+
const args = process.argv.slice(2);
|
|
22
|
+
const cmds = [];
|
|
23
|
+
const envVars = [];
|
|
24
|
+
|
|
25
|
+
for (let i = 0; i < args.length; i++) {
|
|
26
|
+
if (args[i] === '--cmd' && args[i + 1]) {
|
|
27
|
+
cmds.push(args[++i]);
|
|
28
|
+
} else if (args[i] === '--env' && args[i + 1]) {
|
|
29
|
+
envVars.push(...args[++i].split(',').map(s => s.trim()).filter(Boolean));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ── Load credentials ──
|
|
34
|
+
|
|
35
|
+
loadCredentials();
|
|
36
|
+
|
|
37
|
+
// Prepend .claude/bin to PATH so vds-cli wrapper is found
|
|
38
|
+
const localBin = join('.claude', 'bin');
|
|
39
|
+
const sep = process.platform === 'win32' ? ';' : ':';
|
|
40
|
+
process.env.PATH = `${localBin}${sep}${process.env.PATH}`;
|
|
41
|
+
|
|
42
|
+
// ── Check commands ──
|
|
43
|
+
|
|
44
|
+
const missing = [];
|
|
45
|
+
const notes = [];
|
|
46
|
+
|
|
47
|
+
for (const cmd of cmds) {
|
|
48
|
+
const result = requireCommand(cmd);
|
|
49
|
+
if (!result.found) {
|
|
50
|
+
missing.push(cmd);
|
|
51
|
+
if (result.broken && result.path) {
|
|
52
|
+
notes.push(`NOTE: ${result.path} exists but fails to run (broken shim or missing venv)`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ── Check env vars ──
|
|
58
|
+
|
|
59
|
+
for (const varName of envVars) {
|
|
60
|
+
if (!process.env[varName]) {
|
|
61
|
+
missing.push(varName);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── Output ──
|
|
66
|
+
|
|
67
|
+
for (const note of notes) {
|
|
68
|
+
console.log(note);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (missing.length === 0) {
|
|
72
|
+
console.log('OK');
|
|
73
|
+
} else {
|
|
74
|
+
console.log(`BLOCKED — missing: ${missing.join(' ')}`);
|
|
75
|
+
console.log('Fix: edit ~/.vds/sdlc-config.env (or run: vsaf install vds-skill)');
|
|
76
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync, chmodSync, unlinkSync } = require('fs');
|
|
5
|
+
const { join, dirname } = require('path');
|
|
6
|
+
const { homedir } = require('os');
|
|
7
|
+
const { execFileSync } = require('child_process');
|
|
8
|
+
|
|
9
|
+
const IS_WIN = process.platform === 'win32';
|
|
10
|
+
|
|
11
|
+
function getConfigPath() {
|
|
12
|
+
return process.env.VSAF_CONFIG_FILE || join(homedir(), '.vds', 'sdlc-config.env');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function parseEnvFile(filePath) {
|
|
16
|
+
if (!existsSync(filePath)) return {};
|
|
17
|
+
const vars = {};
|
|
18
|
+
for (const line of readFileSync(filePath, 'utf-8').split('\n')) {
|
|
19
|
+
const trimmed = line.trim();
|
|
20
|
+
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
21
|
+
const eq = trimmed.indexOf('=');
|
|
22
|
+
if (eq <= 0) continue;
|
|
23
|
+
const key = trimmed.slice(0, eq);
|
|
24
|
+
let val = trimmed.slice(eq + 1);
|
|
25
|
+
if ((val.startsWith("'") && val.endsWith("'")) ||
|
|
26
|
+
(val.startsWith('"') && val.endsWith('"'))) {
|
|
27
|
+
val = val.slice(1, -1);
|
|
28
|
+
}
|
|
29
|
+
if (val) vars[key] = val;
|
|
30
|
+
}
|
|
31
|
+
return vars;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function loadCredentials() {
|
|
35
|
+
const configPath = getConfigPath();
|
|
36
|
+
const vdsEnvPath = join(homedir(), '.vds', '.env');
|
|
37
|
+
|
|
38
|
+
const primary = parseEnvFile(configPath);
|
|
39
|
+
for (const [key, val] of Object.entries(primary)) {
|
|
40
|
+
if (!process.env[key]) process.env[key] = val;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const fallback = parseEnvFile(vdsEnvPath);
|
|
44
|
+
for (const [key, val] of Object.entries(fallback)) {
|
|
45
|
+
if (!process.env[key]) process.env[key] = val;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function persistToConfig(varName, value) {
|
|
50
|
+
const configPath = getConfigPath();
|
|
51
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
52
|
+
|
|
53
|
+
let content = '';
|
|
54
|
+
if (existsSync(configPath)) {
|
|
55
|
+
content = readFileSync(configPath, 'utf-8')
|
|
56
|
+
.split('\n')
|
|
57
|
+
.filter(line => !line.startsWith(`${varName}=`))
|
|
58
|
+
.join('\n');
|
|
59
|
+
if (content && !content.endsWith('\n')) content += '\n';
|
|
60
|
+
}
|
|
61
|
+
content += `${varName}=${value}\n`;
|
|
62
|
+
|
|
63
|
+
const tmp = configPath + '.tmp';
|
|
64
|
+
writeFileSync(tmp, content);
|
|
65
|
+
if (!IS_WIN) {
|
|
66
|
+
try { chmodSync(tmp, 0o600); } catch { /* warn but don't fail */ }
|
|
67
|
+
}
|
|
68
|
+
renameSync(tmp, configPath);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function ensureEnv(varName, prompt, isSecret = true) {
|
|
72
|
+
if (process.env[varName]) return true;
|
|
73
|
+
|
|
74
|
+
if (!process.stdin.isTTY) return false;
|
|
75
|
+
|
|
76
|
+
const readline = require('readline');
|
|
77
|
+
const rl = readline.createInterface({
|
|
78
|
+
input: process.stdin,
|
|
79
|
+
output: process.stderr,
|
|
80
|
+
terminal: true,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const value = await new Promise((resolve) => {
|
|
84
|
+
if (isSecret) {
|
|
85
|
+
process.stderr.write(`${prompt}: `);
|
|
86
|
+
const origWrite = process.stdout.write;
|
|
87
|
+
process.stdout.write = () => true;
|
|
88
|
+
rl.question('', (answer) => {
|
|
89
|
+
process.stdout.write = origWrite;
|
|
90
|
+
process.stderr.write('\n');
|
|
91
|
+
rl.close();
|
|
92
|
+
resolve(answer.trim());
|
|
93
|
+
});
|
|
94
|
+
} else {
|
|
95
|
+
rl.question(`${prompt}: `, (answer) => {
|
|
96
|
+
rl.close();
|
|
97
|
+
resolve(answer.trim());
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (!value) {
|
|
103
|
+
process.stderr.write(`ERROR: ${varName} is required\n`);
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
process.env[varName] = value;
|
|
108
|
+
persistToConfig(varName, value);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function requireCommand(name) {
|
|
113
|
+
const localBin = join('.claude', 'bin', name);
|
|
114
|
+
if (existsSync(localBin)) {
|
|
115
|
+
try {
|
|
116
|
+
execFileSync(localBin, ['--version'], { stdio: 'pipe', timeout: 15000 });
|
|
117
|
+
return { found: true, path: localBin };
|
|
118
|
+
} catch {
|
|
119
|
+
return { found: false, path: localBin, broken: true };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const whichCmd = IS_WIN ? 'where' : 'which';
|
|
124
|
+
let cmdPath;
|
|
125
|
+
try {
|
|
126
|
+
cmdPath = execFileSync(whichCmd, [name], { stdio: 'pipe', encoding: 'utf-8' }).trim().split('\n')[0];
|
|
127
|
+
} catch {
|
|
128
|
+
return { found: false };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
execFileSync(name, ['--version'], { stdio: 'pipe', timeout: 15000 });
|
|
133
|
+
return { found: true, path: cmdPath };
|
|
134
|
+
} catch {
|
|
135
|
+
return { found: false, path: cmdPath, broken: true };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
module.exports = { loadCredentials, ensureEnv, requireCommand, getConfigPath, parseEnvFile, persistToConfig };
|
|
@@ -12,23 +12,7 @@ Create a Bitbucket PR on Viettel internal Bitbucket via `vds-cli`.
|
|
|
12
12
|
Before doing anything, run this check via Bash tool:
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
|
|
16
|
-
MISSING=""
|
|
17
|
-
[[ -x .claude/bin/vds-cli ]] && export PATH=".claude/bin:$PATH"
|
|
18
|
-
if ! vds-cli --version >/dev/null 2>&1; then
|
|
19
|
-
MISSING="$MISSING vds-cli"
|
|
20
|
-
VDS_PATH=$(command -v vds-cli 2>/dev/null)
|
|
21
|
-
if [[ -n "$VDS_PATH" ]]; then
|
|
22
|
-
echo "NOTE: $VDS_PATH exists but fails to run (broken shim or missing venv)"
|
|
23
|
-
fi
|
|
24
|
-
fi
|
|
25
|
-
[[ -z "${VDS_BITBUCKET_TOKEN:-}" ]] && MISSING="$MISSING VDS_BITBUCKET_TOKEN"
|
|
26
|
-
if [[ -n "$MISSING" ]]; then
|
|
27
|
-
echo "BLOCKED — missing:$MISSING"
|
|
28
|
-
echo "Fix: edit ~/.vds/sdlc-config.env (or run: vsaf install vds-skill)"
|
|
29
|
-
else
|
|
30
|
-
echo "OK"
|
|
31
|
-
fi
|
|
15
|
+
node .claude/skills/_shared/vds-skill/config-check.js --cmd vds-cli --env VDS_BITBUCKET_TOKEN
|
|
32
16
|
```
|
|
33
17
|
|
|
34
18
|
If BLOCKED: tell the user exactly what's missing and how to fix, then STOP. Do NOT fabricate output. `--dry-run` mode skips credential + vds-cli checks.
|
|
@@ -63,7 +47,7 @@ If BLOCKED: tell the user exactly what's missing and how to fix, then STOP. Do N
|
|
|
63
47
|
## Implementation
|
|
64
48
|
|
|
65
49
|
```bash
|
|
66
|
-
|
|
50
|
+
node .claude/skills/vds-skill-create-bitbucket-pr/scripts/create-pr.js "$@"
|
|
67
51
|
```
|
|
68
52
|
|
|
69
53
|
## Notes
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { execFileSync } = require('child_process');
|
|
5
|
+
const { existsSync, readdirSync } = require('fs');
|
|
6
|
+
const { join } = require('path');
|
|
7
|
+
// NOTE: path matches DEPLOYED location (.claude/skills/_shared/vds-skill/), not source repo
|
|
8
|
+
const { loadCredentials, ensureEnv, requireCommand } = require('../../_shared/vds-skill/credentials.js');
|
|
9
|
+
|
|
10
|
+
let dryRun = false;
|
|
11
|
+
let targetBranch = 'master';
|
|
12
|
+
let descFile = '';
|
|
13
|
+
let title = '';
|
|
14
|
+
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
for (let i = 0; i < args.length; i++) {
|
|
17
|
+
switch (args[i]) {
|
|
18
|
+
case '--dry-run': dryRun = true; break;
|
|
19
|
+
case '--target': targetBranch = args[++i]; break;
|
|
20
|
+
case '--description-file': descFile = args[++i]; break;
|
|
21
|
+
case '--title': title = args[++i]; break;
|
|
22
|
+
default:
|
|
23
|
+
process.stderr.write(`Unknown arg: ${args[i]}\n`);
|
|
24
|
+
process.exit(2);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let remoteUrl;
|
|
29
|
+
try {
|
|
30
|
+
remoteUrl = execFileSync('git', ['remote', 'get-url', 'origin'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
31
|
+
} catch {
|
|
32
|
+
process.stderr.write('ERROR: No git remote \'origin\' configured\n');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!remoteUrl.includes('bitbucket.digital.vn')) {
|
|
37
|
+
process.stderr.write(`WARNING: Remote URL does not match bitbucket.digital.vn pattern:\n ${remoteUrl}\n`);
|
|
38
|
+
process.stderr.write(' This skill is for Viettel Bitbucket. For GitHub, use \'gh pr create\'.\n');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const scmMatch = remoteUrl.match(/\/scm\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
43
|
+
const sshMatch = remoteUrl.match(/\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
44
|
+
const match = scmMatch || sshMatch;
|
|
45
|
+
|
|
46
|
+
if (!match) {
|
|
47
|
+
process.stderr.write(`ERROR: Cannot parse PROJECT/REPO from remote URL: ${remoteUrl}\n`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const project = match[1];
|
|
52
|
+
const repo = match[2];
|
|
53
|
+
|
|
54
|
+
let sourceBranch;
|
|
55
|
+
try {
|
|
56
|
+
sourceBranch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
57
|
+
} catch {
|
|
58
|
+
process.stderr.write('ERROR: Cannot determine current branch\n');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!sourceBranch || sourceBranch === 'HEAD') {
|
|
63
|
+
process.stderr.write('ERROR: Not on a named branch\n');
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (!title) {
|
|
68
|
+
try {
|
|
69
|
+
title = execFileSync('git', ['log', '-1', '--pretty=%s'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
70
|
+
} catch { title = sourceBranch; }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!descFile) {
|
|
74
|
+
const searchPaths = [
|
|
75
|
+
{ dir: join('.vsaf', 'docs', 'features'), file: '09-ship.md' },
|
|
76
|
+
{ dir: join('.vsaf', 'docs', 'hotfixes'), file: '03-ship.md' },
|
|
77
|
+
];
|
|
78
|
+
for (const { dir, file } of searchPaths) {
|
|
79
|
+
if (existsSync(dir)) {
|
|
80
|
+
for (const subdir of readdirSync(dir)) {
|
|
81
|
+
const candidate = join(dir, subdir, file);
|
|
82
|
+
if (existsSync(candidate)) { descFile = candidate; break; }
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (descFile) break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const cmdArgs = ['bitbucket', 'pr', 'create', `${project}/${repo}`,
|
|
90
|
+
'--source', sourceBranch, '--target', targetBranch, '--title', title];
|
|
91
|
+
if (descFile && existsSync(descFile)) cmdArgs.push('--description-file', descFile);
|
|
92
|
+
|
|
93
|
+
if (dryRun) {
|
|
94
|
+
console.log('DRY-RUN — would execute:');
|
|
95
|
+
console.log(` vds-cli ${cmdArgs.join(' ')} --yes --json-only`);
|
|
96
|
+
process.exit(0);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
loadCredentials();
|
|
100
|
+
|
|
101
|
+
const cmdCheck = requireCommand('vds-cli');
|
|
102
|
+
if (!cmdCheck.found) {
|
|
103
|
+
process.stderr.write('ERROR: vds-cli not found in PATH\n');
|
|
104
|
+
if (cmdCheck.broken) process.stderr.write(`NOTE: ${cmdCheck.path} exists but fails to run\n`);
|
|
105
|
+
process.exit(127);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
(async () => {
|
|
109
|
+
if (!await ensureEnv('VDS_BITBUCKET_TOKEN', 'Enter VDS Bitbucket personal access token')) process.exit(1);
|
|
110
|
+
|
|
111
|
+
console.log('About to create PR:');
|
|
112
|
+
console.log(` Project/Repo: ${project}/${repo}`);
|
|
113
|
+
console.log(` Source: ${sourceBranch}`);
|
|
114
|
+
console.log(` Target: ${targetBranch}`);
|
|
115
|
+
console.log(` Title: ${title}`);
|
|
116
|
+
console.log(` Description: ${descFile || '<empty>'}`);
|
|
117
|
+
console.log('');
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
execFileSync('vds-cli', [...cmdArgs, '--yes', '--json-only'], { stdio: 'inherit' });
|
|
121
|
+
} catch (err) {
|
|
122
|
+
process.stderr.write(`ERROR: vds-cli failed: ${err.message}\n`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
})();
|
|
@@ -12,24 +12,7 @@ Create a Jira Epic on Viettel Jira based on a PRD markdown file.
|
|
|
12
12
|
Before doing anything, run this check via Bash tool:
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
|
|
16
|
-
MISSING=""
|
|
17
|
-
[[ -x .claude/bin/vds-cli ]] && export PATH=".claude/bin:$PATH"
|
|
18
|
-
if ! vds-cli --version >/dev/null 2>&1; then
|
|
19
|
-
MISSING="$MISSING vds-cli"
|
|
20
|
-
VDS_PATH=$(command -v vds-cli 2>/dev/null)
|
|
21
|
-
if [[ -n "$VDS_PATH" ]]; then
|
|
22
|
-
echo "NOTE: $VDS_PATH exists but fails to run (broken shim or missing venv)"
|
|
23
|
-
fi
|
|
24
|
-
fi
|
|
25
|
-
[[ -z "${VDS_JIRA_TOKEN:-}" ]] && MISSING="$MISSING VDS_JIRA_TOKEN"
|
|
26
|
-
[[ -z "${VDS_JIRA_PROJECT_DEFAULT:-}" ]] && MISSING="$MISSING VDS_JIRA_PROJECT_DEFAULT"
|
|
27
|
-
if [[ -n "$MISSING" ]]; then
|
|
28
|
-
echo "BLOCKED — missing:$MISSING"
|
|
29
|
-
echo "Fix: edit ~/.vds/sdlc-config.env (or run: vsaf install vds-skill)"
|
|
30
|
-
else
|
|
31
|
-
echo "OK"
|
|
32
|
-
fi
|
|
15
|
+
node .claude/skills/_shared/vds-skill/config-check.js --cmd vds-cli --env VDS_JIRA_TOKEN,VDS_JIRA_PROJECT_DEFAULT
|
|
33
16
|
```
|
|
34
17
|
|
|
35
18
|
If BLOCKED: tell the user exactly what's missing and how to fix, then STOP. Do NOT fabricate output. `--dry-run` mode skips credential + vds-cli checks.
|
|
@@ -37,7 +20,6 @@ If BLOCKED: tell the user exactly what's missing and how to fix, then STOP. Do N
|
|
|
37
20
|
## Prerequisites
|
|
38
21
|
|
|
39
22
|
- `vds-cli` installed (for non-dry-run)
|
|
40
|
-
- `python3` installed
|
|
41
23
|
- A PRD file exists at `.vsaf/docs/features/{feature}/02-prd.md`
|
|
42
24
|
- `VDS_JIRA_TOKEN` + `VDS_JIRA_PROJECT_DEFAULT` in `~/.vds/sdlc-config.env`
|
|
43
25
|
|
|
@@ -63,7 +45,7 @@ If BLOCKED: tell the user exactly what's missing and how to fix, then STOP. Do N
|
|
|
63
45
|
## Implementation
|
|
64
46
|
|
|
65
47
|
```bash
|
|
66
|
-
|
|
48
|
+
node .claude/skills/vds-skill-create-jira-epic/scripts/create-epic.js "$@"
|
|
67
49
|
```
|
|
68
50
|
|
|
69
51
|
## Notes
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const { execFileSync } = require('child_process');
|
|
5
|
+
const { existsSync, readFileSync, writeFileSync, readdirSync, statSync } = require('fs');
|
|
6
|
+
const { join, dirname, basename } = require('path');
|
|
7
|
+
// NOTE: path matches DEPLOYED location (.claude/skills/_shared/vds-skill/), not source repo
|
|
8
|
+
const { loadCredentials, ensureEnv, requireCommand } = require('../../_shared/vds-skill/credentials.js');
|
|
9
|
+
|
|
10
|
+
let dryRun = false;
|
|
11
|
+
let projectKey = '';
|
|
12
|
+
let descFile = '';
|
|
13
|
+
let featureName = '';
|
|
14
|
+
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
for (let i = 0; i < args.length; i++) {
|
|
17
|
+
switch (args[i]) {
|
|
18
|
+
case '--dry-run': dryRun = true; break;
|
|
19
|
+
case '--project': projectKey = args[++i]; break;
|
|
20
|
+
case '--description-file': descFile = args[++i]; break;
|
|
21
|
+
case '--feature': featureName = args[++i]; break;
|
|
22
|
+
default:
|
|
23
|
+
process.stderr.write(`Unknown arg: ${args[i]}\n`);
|
|
24
|
+
process.exit(2);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!descFile) {
|
|
29
|
+
if (featureName) {
|
|
30
|
+
const candidate = join('.vsaf', 'docs', 'features', featureName, '02-prd.md');
|
|
31
|
+
if (existsSync(candidate)) descFile = candidate;
|
|
32
|
+
}
|
|
33
|
+
if (!descFile) {
|
|
34
|
+
const featuresDir = join('.vsaf', 'docs', 'features');
|
|
35
|
+
if (existsSync(featuresDir)) {
|
|
36
|
+
let newest = null;
|
|
37
|
+
let newestMtime = 0;
|
|
38
|
+
for (const dir of readdirSync(featuresDir)) {
|
|
39
|
+
const prd = join(featuresDir, dir, '02-prd.md');
|
|
40
|
+
if (existsSync(prd)) {
|
|
41
|
+
const mtime = statSync(prd).mtimeMs;
|
|
42
|
+
if (mtime > newestMtime) { newest = prd; newestMtime = mtime; }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (newest) descFile = newest;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!descFile || !existsSync(descFile)) {
|
|
51
|
+
process.stderr.write('ERROR: PRD file not found. Run /sdlc-prd first or specify --description-file\n');
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!featureName) featureName = basename(dirname(descFile));
|
|
56
|
+
|
|
57
|
+
const prdContent = readFileSync(descFile, 'utf-8');
|
|
58
|
+
const h1Match = prdContent.match(/^# (.+)/m);
|
|
59
|
+
const summary = h1Match ? h1Match[1].trim() : featureName;
|
|
60
|
+
|
|
61
|
+
if (dryRun) {
|
|
62
|
+
loadCredentials();
|
|
63
|
+
const displayProject = projectKey || process.env.VDS_JIRA_PROJECT_DEFAULT || '<VDS_JIRA_PROJECT_DEFAULT>';
|
|
64
|
+
console.log('DRY-RUN — would execute:');
|
|
65
|
+
console.log(` vds-cli jira create --project ${displayProject} --issuetype Epic --summary "${summary}" --description-file ${descFile} --yes --json-only`);
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
loadCredentials();
|
|
70
|
+
|
|
71
|
+
const cmdCheck = requireCommand('vds-cli');
|
|
72
|
+
if (!cmdCheck.found) {
|
|
73
|
+
process.stderr.write('ERROR: vds-cli not found in PATH\n');
|
|
74
|
+
if (cmdCheck.broken) process.stderr.write(`NOTE: ${cmdCheck.path} exists but fails to run\n`);
|
|
75
|
+
process.exit(127);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
(async () => {
|
|
79
|
+
if (!await ensureEnv('VDS_JIRA_TOKEN', 'Enter VDS Jira personal access token')) process.exit(1);
|
|
80
|
+
if (!await ensureEnv('VDS_JIRA_PROJECT_DEFAULT', 'Enter default Jira project key (e.g. NTTC)', false)) process.exit(1);
|
|
81
|
+
|
|
82
|
+
projectKey = projectKey || process.env.VDS_JIRA_PROJECT_DEFAULT;
|
|
83
|
+
|
|
84
|
+
console.log('About to create Jira Epic:');
|
|
85
|
+
console.log(` Project: ${projectKey}`);
|
|
86
|
+
console.log(` Summary: ${summary}`);
|
|
87
|
+
console.log(` Description: ${descFile}`);
|
|
88
|
+
console.log(` Feature: ${featureName}`);
|
|
89
|
+
console.log('');
|
|
90
|
+
|
|
91
|
+
const cmdArgs = ['jira', 'create', '--project', projectKey, '--issuetype', 'Epic',
|
|
92
|
+
'--summary', summary, '--description-file', descFile, '--yes', '--json-only'];
|
|
93
|
+
|
|
94
|
+
let result;
|
|
95
|
+
try {
|
|
96
|
+
result = execFileSync('vds-cli', cmdArgs, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
97
|
+
} catch (err) {
|
|
98
|
+
process.stderr.write(`ERROR: vds-cli failed: ${err.stderr || err.message}\n`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let epicKey;
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(result);
|
|
105
|
+
epicKey = parsed.key || '';
|
|
106
|
+
} catch {
|
|
107
|
+
process.stderr.write(`ERROR: Failed to parse vds-cli response\nResponse: ${result}\n`);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!epicKey) {
|
|
112
|
+
process.stderr.write(`ERROR: No epic key in response\nResponse: ${result}\n`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const epicKeyFile = join(dirname(descFile), 'jira-epic-key.txt');
|
|
117
|
+
writeFileSync(epicKeyFile, epicKey + '\n');
|
|
118
|
+
console.log(`Created Jira Epic: ${epicKey}`);
|
|
119
|
+
console.log(` Saved key to: ${epicKeyFile}`);
|
|
120
|
+
})();
|