@ngocsangairvds/vsaf 3.1.17 → 3.1.19

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.
@@ -0,0 +1,143 @@
1
+ # Codex Instructions
2
+
3
+ This file provides guidance to OpenAI Codex CLI when working with code in this repository.
4
+
5
+ > VSAF v3 — Agentic AI SDLC Framework. 3 integrated tools. 2-layer review.
6
+
7
+ ---
8
+
9
+ ## Prerequisites
10
+
11
+ Node.js ≥18, Git. Run `npx vsaf init` (idempotent) to install all tools.
12
+
13
+ ---
14
+
15
+ ## Architecture
16
+
17
+ VSAF is a meta-framework for AI-driven SDLC — not an application. It has no source code to compile. The 3-tool stack:
18
+
19
+ | Layer | Tools | Purpose |
20
+ |-------|-------|---------|
21
+ | Planning | BMAD Method | PRD, SRS, architecture docs, sprint stories |
22
+ | Code Intelligence | GitNexus | Impact analysis, call graph, blast radius |
23
+ | Implementation | Superpowers, Codex | Brainstorm, TDD execution, code review |
24
+
25
+ Key directories: `.codex/` (skills + instructions), `.vsaf/_bmad/` (BMAD workspace), `.vsaf/docs/` (artifacts), `.vsaf/_gitnexus/` (knowledge graph index).
26
+
27
+ ---
28
+
29
+ ## Commands
30
+
31
+ ```bash
32
+ npx vsaf init # Install all tools (one-time, idempotent)
33
+ vsaf index # Re-index: gitnexus analyze
34
+ vsaf review # 2-layer review coordinator
35
+ vsaf status # Show status of all tools
36
+ vsaf clean # Clean GitNexus index
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Identity
42
+
43
+ This project uses the **VSAF v3 (Agentic AI SDLC Framework)**. All development
44
+ follows the SRS-first workflow below. No code ships without 2-layer review.
45
+
46
+ ---
47
+
48
+ ## Knowledge Graph (GitNexus backbone)
49
+
50
+ - Run impact analysis before any code change: `gitnexus impact <symbol>`
51
+ - Query: "What breaks if I change X?" before touching cross-module code.
52
+ - Re-index after every merge: `gitnexus analyze`
53
+
54
+ ---
55
+
56
+ ## SRS-First Workflow
57
+
58
+ ### Step 0: Setup (one-time)
59
+ ```bash
60
+ npx vsaf init
61
+ ```
62
+
63
+ ### Step 1: Onboard Project
64
+ - Run `gitnexus serve` for web UI exploration.
65
+ - Use `/vsaf-onboard` for the structured onboarding sequence.
66
+ - Do not modify code on day one.
67
+
68
+ ### Step 2: Planning + Requirement → PRD → SRS
69
+ ```
70
+ /vsaf-plan <requirement> # scope + impact + approach
71
+ /vsaf-doc-prd # write PRD from approved scope
72
+ /vsaf-doc-srs # write SRS from PRD
73
+ ```
74
+
75
+ ### Step 3: Testcase from SRS
76
+ ```
77
+ /vsaf-test <path/to/srs>
78
+ ```
79
+
80
+ ### Step 4: Impact Analysis
81
+ ```
82
+ gitnexus impact <symbol> --direction upstream # Blast radius
83
+ ```
84
+ Impact > 3 modules → split PRs.
85
+
86
+ ### Step 5: Implement from SRS + Testcases
87
+ ```
88
+ /vsaf-build <path/to/srs> <path/to/testcases>
89
+ ```
90
+ TDD discipline: 1 commit per task.
91
+
92
+ ### Step 6: Review + Ship
93
+ ```
94
+ /bmad-code-review
95
+ vsaf index
96
+ /vsaf-ship
97
+ ```
98
+ ```bash
99
+ git push origin feature/<name>
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Tool Commands — Quick Reference
105
+
106
+ | Action | Command |
107
+ |---|---|
108
+ | VSAF onboard | `/vsaf-onboard` |
109
+ | VSAF plan | `/vsaf-plan <requirement>` |
110
+ | VSAF doc PRD | `/vsaf-doc-prd` |
111
+ | VSAF doc SRS | `/vsaf-doc-srs` |
112
+ | VSAF testcase | `/vsaf-test <path/to/srs>` |
113
+ | VSAF implement | `/vsaf-build <srs> <testcases>` |
114
+ | VSAF ship | `/vsaf-ship` |
115
+ | GitNexus index | `gitnexus analyze` |
116
+ | GitNexus web | `gitnexus serve` |
117
+
118
+ ---
119
+
120
+ ## Anti-Patterns
121
+
122
+ | Do Not | Instead |
123
+ |---|---|
124
+ | Write code before PRD/SRS/testcase | `/vsaf-plan` → `/vsaf-doc-prd` → `/vsaf-doc-srs` → `/vsaf-test` |
125
+ | Implement without impact gate | `gitnexus impact` before editing any symbol |
126
+ | Push without review | 2-layer: code-review + vsaf index |
127
+ | Forget to re-index | `vsaf index` after every merge |
128
+ | Create PRs > 400 lines | Split into smaller PRs |
129
+ | Trust AI output blindly | AI writes → review → human approves |
130
+
131
+ ---
132
+
133
+ ## Commit Discipline
134
+
135
+ - 1 commit per task from the plan.
136
+ - Each commit message: `<type>: <description>` (feat, fix, refactor, docs, test).
137
+ - Tests must pass after every commit.
138
+
139
+ ---
140
+
141
+ ## Security
142
+
143
+ - Never hardcode credentials. Use environment variables.
@@ -22,7 +22,7 @@ VSAF is a meta-framework for AI-driven SDLC — not an application. It has no so
22
22
  | Code Intelligence | GitNexus (MCP) | Impact analysis, call graph, blast radius |
23
23
  | Implementation | Superpowers, Claude Code | Brainstorm, TDD execution, code review |
24
24
 
25
- Key directories: `.claude/` (settings + skills), `.vsaf/_bmad/` (BMAD workspace), `.vsaf/docs/` (artifacts), `.gitnexus/` (knowledge graph index).
25
+ Key directories: `.claude/` (settings + skills), `.vsaf/_bmad/` (BMAD workspace), `.vsaf/docs/` (artifacts), `.vsaf/_gitnexus/` (knowledge graph index, `.gitnexus/` is a symlink here).
26
26
 
27
27
  ---
28
28
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ngocsangairvds/vsaf",
3
- "version": "3.1.17",
4
- "description": "Fix done bmad working with claude code and gitnexus, and add superpowers to vsaf",
3
+ "version": "3.1.19",
4
+ "description": "add method for codex",
5
5
  "keywords": ["claude", "claude-code", "ai", "sdlc", "framework", "bmad", "gitnexus", "superpowers"],
6
6
  "bin": {
7
7
  "vsaf": "./bin/vsaf.js"
package/src/global.js CHANGED
@@ -1,11 +1,12 @@
1
1
  'use strict';
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
- const { ok, info, warn, step, hasCommand, exec, copyDir, copyFile, CLAUDE_HOME } = require('./utils');
4
+ const { ok, info, warn, step, hasCommand, exec, copyDir, copyFile, CLAUDE_HOME, CODEX_HOME } = require('./utils');
5
5
 
6
- const PKG_ROOT = path.join(__dirname, '..');
7
- const SKILLS_SRC = path.join(PKG_ROOT, 'tools', 'skills');
8
- const SKILLS_DST = path.join(CLAUDE_HOME, 'skills');
6
+ const PKG_ROOT = path.join(__dirname, '..');
7
+ const SKILLS_SRC = path.join(PKG_ROOT, 'tools', 'skills');
8
+ const SKILLS_DST = path.join(CLAUDE_HOME, 'skills');
9
+ const CODEX_SKILLS_DST = path.join(CODEX_HOME, 'skills');
9
10
 
10
11
  async function installGlobal() {
11
12
  console.log('\n\x1b[1m╔══════════════════════════════════════════╗\x1b[0m');
@@ -22,26 +23,26 @@ async function installGlobal() {
22
23
  // ── Skills ──────────────────────────────────────────────────────────────────
23
24
 
24
25
  function installSkills() {
25
- step('Skills → ~/.claude/skills/');
26
+ step('Skills → ~/.claude/skills/ + ~/.codex/skills/');
26
27
 
27
28
  if (!fs.existsSync(SKILLS_SRC)) {
28
29
  warn('Skills directory not found in package — skipping');
29
30
  return;
30
31
  }
31
32
 
32
- fs.mkdirSync(SKILLS_DST, { recursive: true });
33
-
33
+ const entries = fs.readdirSync(SKILLS_SRC, { withFileTypes: true });
34
34
  let installed = 0;
35
35
 
36
- for (const entry of fs.readdirSync(SKILLS_SRC, { withFileTypes: true })) {
37
- const src = path.join(SKILLS_SRC, entry.name);
38
- const dst = path.join(SKILLS_DST, entry.name);
39
-
40
- entry.isDirectory() ? copyDir(src, dst) : copyFile(src, dst);
41
- installed++;
36
+ for (const dst of [SKILLS_DST, CODEX_SKILLS_DST]) {
37
+ fs.mkdirSync(dst, { recursive: true });
38
+ for (const entry of entries) {
39
+ const src = path.join(SKILLS_SRC, entry.name);
40
+ entry.isDirectory() ? copyDir(src, path.join(dst, entry.name)) : copyFile(src, path.join(dst, entry.name));
41
+ }
42
+ installed = entries.length;
42
43
  }
43
44
 
44
- ok(`${installed} skill(s) installed/updated`);
45
+ ok(`${installed} skill(s) installed/updated → ~/.claude/skills + ~/.codex/skills`);
45
46
  }
46
47
 
47
48
  // ── Binary helpers ───────────────────────────────────────────────────────────
package/src/project.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
- const { ok, info, warn, step, hasCommand, exec, copyDir, copyFile } = require('./utils');
4
+ const { ok, info, warn, step, hasCommand, exec, copyDir, copyFile, ensureGitnexusSymlink } = require('./utils');
5
5
 
6
6
  const PKG_ROOT = path.join(__dirname, '..');
7
7
  const TEMPLATES = path.join(PKG_ROOT, 'assets', 'templates');
@@ -23,7 +23,9 @@ async function installProject() {
23
23
  console.log('\n Next steps:');
24
24
  console.log(' vsaf status check all tools');
25
25
  console.log(' vsaf index build/update knowledge graph');
26
- console.log(' /plugin install superpowers@claude-plugins-official\n');
26
+ console.log('\n \x1b[1mActivate Superpowers:\x1b[0m');
27
+ console.log(' \x1b[36mClaude Code\x1b[0m /plugin install superpowers@claude-plugins-official');
28
+ console.log(' \x1b[36mCodex\x1b[0m skills auto-loaded — use /superpowers:brainstorming, /superpowers:code-review, etc.\n');
27
29
  }
28
30
 
29
31
  // ── File scaffold ────────────────────────────────────────────────────────────
@@ -32,10 +34,13 @@ function scaffoldFiles() {
32
34
  step('Scaffolding project files');
33
35
 
34
36
  const files = [
35
- { tpl: '.claude/settings.json', dst: '.claude/settings.json' },
36
- { tpl: 'CLAUDE.md', dst: 'CLAUDE.md' },
37
+ { tpl: '.claude/settings.json', dst: '.claude/settings.json' },
38
+ { tpl: '.codex/instructions.md', dst: '.codex/instructions.md' },
39
+ { tpl: 'CLAUDE.md', dst: 'CLAUDE.md' },
37
40
  ];
38
41
 
42
+ patchGitignore();
43
+
39
44
  for (const { tpl, dst, chmod } of files) {
40
45
  const src = path.join(TEMPLATES, tpl);
41
46
  const dstAbs = path.join(CWD, dst);
@@ -55,6 +60,26 @@ function scaffoldFiles() {
55
60
  }
56
61
  }
57
62
 
63
+ /** Ensures .gitnexus (symlink) is ignored by git — it's a vsaf implementation detail. */
64
+ function patchGitignore() {
65
+ const gitignorePath = path.join(CWD, '.gitignore');
66
+ const entry = '.gitnexus';
67
+
68
+ let content = '';
69
+ if (fs.existsSync(gitignorePath)) {
70
+ content = fs.readFileSync(gitignorePath, 'utf8');
71
+ if (content.split('\n').some((l) => l.trim() === entry)) {
72
+ ok('.gitignore — .gitnexus already ignored');
73
+ return;
74
+ }
75
+ // Append with a newline separator
76
+ content = content.endsWith('\n') ? content : content + '\n';
77
+ }
78
+
79
+ fs.writeFileSync(gitignorePath, content + entry + '\n');
80
+ ok('.gitignore — added .gitnexus');
81
+ }
82
+
58
83
  // ── Tool initialisation ──────────────────────────────────────────────────────
59
84
 
60
85
  function scaffoldBmadWorkspace() {
@@ -135,6 +160,7 @@ function syncLocalBmadSkills() {
135
160
  function initGitNexus() {
136
161
  step('GitNexus');
137
162
  if (!hasCommand('gitnexus')) { warn('gitnexus not found — run: vsaf global'); return; }
163
+ ensureGitnexusSymlink();
138
164
  const analyzeCmd = 'gitnexus analyze --skip-git';
139
165
  info('Indexing repository (gitnexus analyze)...');
140
166
  exec(analyzeCmd, { cwd: CWD }) ? ok('Repository indexed') : warn('gitnexus analyze failed — run manually');
package/src/status.js CHANGED
@@ -1,25 +1,28 @@
1
1
  'use strict';
2
2
  const path = require('path');
3
3
  const fs = require('fs');
4
- const { hasCommand, CLAUDE_HOME } = require('./utils');
4
+ const { hasCommand, CLAUDE_HOME, CODEX_HOME } = require('./utils');
5
5
 
6
6
  async function showStatus() {
7
7
  console.log('\n\x1b[1mVSAF Status\x1b[0m\n');
8
8
 
9
9
  // ── Global ─────────────────────────────────────────────────────────────────
10
- console.log('\x1b[1mGlobal (machine-wide → ~/.claude/)\x1b[0m');
10
+ console.log('\x1b[1mGlobal (machine-wide)\x1b[0m');
11
11
  checkSkills();
12
- checkCmd('gitnexus', 'GitNexus');
12
+ checkCodexSkills();
13
+ checkCmd('gitnexus', 'GitNexus');
13
14
 
14
15
  // ── Project ────────────────────────────────────────────────────────────────
15
16
  console.log('\n\x1b[1mProject (current repo)\x1b[0m');
16
- checkPath('.claude/settings.json', 'Project settings');
17
- checkPath('CLAUDE.md', 'Workflow rules');
17
+ checkPath('.claude/settings.json', 'Claude settings');
18
+ checkPath('.codex/instructions.md', 'Codex instructions');
19
+ checkPath('CLAUDE.md', 'Workflow rules (Claude)');
20
+ checkPath('AGENTS.md', 'Workflow rules (Codex/Gemini)');
18
21
  checkPath('.vsaf/_bmad', 'BMAD workspace');
19
22
  checkPath('.vsaf/docs', 'Project artifact folder');
20
23
  checkProjectSkillDir('.claude/skills', 'Project BMAD skills (.claude)');
21
24
  checkProjectSkillDir('.codex/skills', 'Project BMAD skills (.codex)');
22
- checkPath('.gitnexus', 'GitNexus index');
25
+ checkPath('.vsaf/_gitnexus', 'GitNexus index');
23
26
  console.log('');
24
27
  }
25
28
 
@@ -31,11 +34,21 @@ function checkCmd(cmd, label) {
31
34
  function checkSkills() {
32
35
  const dir = path.join(CLAUDE_HOME, 'skills');
33
36
  if (!fs.existsSync(dir)) {
34
- console.log(' \x1b[31m✗\x1b[0m Skills (not installed)');
37
+ console.log(' \x1b[31m✗\x1b[0m Claude skills (not installed)');
35
38
  return;
36
39
  }
37
40
  const count = fs.readdirSync(dir).length;
38
- console.log(` \x1b[32m✓\x1b[0m Skills (${count} in ~/.claude/skills/)`);
41
+ console.log(` \x1b[32m✓\x1b[0m Claude skills (${count} in ~/.claude/skills/)`);
42
+ }
43
+
44
+ function checkCodexSkills() {
45
+ const dir = path.join(CODEX_HOME, 'skills');
46
+ if (!fs.existsSync(dir)) {
47
+ console.log(' \x1b[31m✗\x1b[0m Codex skills (not installed)');
48
+ return;
49
+ }
50
+ const count = fs.readdirSync(dir).length;
51
+ console.log(` \x1b[32m✓\x1b[0m Codex skills (${count} in ~/.codex/skills/)`);
39
52
  }
40
53
 
41
54
  function checkPath(rel, label) {
package/src/utils.js CHANGED
@@ -5,6 +5,7 @@ const path = require('path');
5
5
  const os = require('os');
6
6
 
7
7
  const CLAUDE_HOME = path.join(os.homedir(), '.claude');
8
+ const CODEX_HOME = path.join(os.homedir(), '.codex');
8
9
 
9
10
  const ok = (msg) => console.log(` \x1b[32m✓\x1b[0m ${msg}`);
10
11
  const info = (msg) => console.log(` \x1b[36m→\x1b[0m ${msg}`);
@@ -53,9 +54,33 @@ function hasGitCommits() {
53
54
  }
54
55
  }
55
56
 
57
+ /**
58
+ * Ensures .gitnexus is a symlink → .vsaf/_gitnexus (actual data dir).
59
+ * Gitnexus hardcodes .gitnexus/ so we keep it at the root as a symlink
60
+ * while the real data lives inside .vsaf/.
61
+ */
62
+ function ensureGitnexusSymlink() {
63
+ const cwd = process.cwd();
64
+ const actual = path.join(cwd, '.vsaf', '_gitnexus');
65
+ const linkPath = path.join(cwd, '.gitnexus');
66
+
67
+ fs.mkdirSync(actual, { recursive: true });
68
+
69
+ try {
70
+ const stat = fs.lstatSync(linkPath);
71
+ if (stat.isSymbolicLink()) return; // already a symlink — nothing to do
72
+ // Real directory left behind (e.g. gitnexus recreated it after clean)
73
+ fs.rmSync(linkPath, { recursive: true, force: true });
74
+ } catch {
75
+ // linkPath does not exist — will create below
76
+ }
77
+
78
+ fs.symlinkSync(actual, linkPath);
79
+ }
80
+
56
81
  function getPlatform() {
57
82
  if (process.platform === 'darwin') return 'macos';
58
83
  return 'linux';
59
84
  }
60
85
 
61
- module.exports = { ok, info, warn, step, hasCommand, exec, copyDir, copyFile, CLAUDE_HOME, getPlatform, hasGitCommits };
86
+ module.exports = { ok, info, warn, step, hasCommand, exec, copyDir, copyFile, CLAUDE_HOME, CODEX_HOME, getPlatform, hasGitCommits, ensureGitnexusSymlink };
package/src/workflow.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const path = require('path');
4
4
  const fs = require('fs');
5
- const { ok, info, warn, step, hasCommand, exec, hasGitCommits } = require('./utils');
5
+ const { ok, info, warn, step, hasCommand, exec, hasGitCommits, ensureGitnexusSymlink } = require('./utils');
6
6
 
7
7
  function preflightCheck() {
8
8
  // Check: is this a git repo?
@@ -37,6 +37,7 @@ function runIndex() {
37
37
  return false;
38
38
  }
39
39
 
40
+ ensureGitnexusSymlink();
40
41
  info('Running gitnexus analyze...');
41
42
  const gitnexusOk = exec('gitnexus analyze --skip-git');
42
43
  if (gitnexusOk) {
@@ -78,7 +79,12 @@ function runClean() {
78
79
  }
79
80
 
80
81
  info('Running gitnexus clean...');
81
- const cleanOk = exec('gitnexus clean');
82
+ const cleanOk = exec('gitnexus clean --force');
83
+ // gitnexus clean deletes the .gitnexus symlink — clear actual data dir too
84
+ const actualDir = path.join(process.cwd(), '.vsaf', '_gitnexus');
85
+ if (fs.existsSync(actualDir)) fs.rmSync(actualDir, { recursive: true, force: true });
86
+ // Restore symlink pointing at the now-empty dir
87
+ ensureGitnexusSymlink();
82
88
  cleanOk ? ok('GitNexus index cleaned') : warn('gitnexus clean failed — run manually');
83
89
  return cleanOk;
84
90
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: vsaf-plan
3
- description: Plan a new feature/task. Use when receiving a requirement that needs scope analysis, impact assessment, and approach strategy. Dual brainstorm (Superpowers + BMAD). Example: /vsaf-plan create user management module
3
+ description: "Plan a new feature/task. Use when receiving a requirement that needs scope analysis, impact assessment, and approach strategy. Dual brainstorm (Superpowers + BMAD). Example: /vsaf-plan create user management module"
4
4
  ---
5
5
 
6
6
  # VSAF Plan