hero-vibe-kit 0.1.0

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/LICENSE +21 -0
  3. package/README.md +165 -0
  4. package/bin/hero-vibe-kit.js +62 -0
  5. package/package.json +35 -0
  6. package/presets/enterprise.json +6 -0
  7. package/presets/small-team.json +6 -0
  8. package/presets/solo.json +6 -0
  9. package/skills.manifest.json +39 -0
  10. package/src/detect.cjs +42 -0
  11. package/src/doctor.cjs +74 -0
  12. package/src/init.cjs +95 -0
  13. package/src/integrations.cjs +89 -0
  14. package/src/links.cjs +37 -0
  15. package/src/merge.cjs +61 -0
  16. package/src/render.cjs +27 -0
  17. package/src/update.cjs +59 -0
  18. package/src/util.cjs +56 -0
  19. package/templates/AGENTS.md.tmpl +9 -0
  20. package/templates/CLAUDE.md.tmpl +11 -0
  21. package/templates/common/.claude/hooks/git-guard.cjs +55 -0
  22. package/templates/common/.claude/hooks/stop-reminder.cjs +34 -0
  23. package/templates/common/.claude/settings.json +25 -0
  24. package/templates/docs/en/ACTIVE_STATE.md +28 -0
  25. package/templates/docs/en/AGENCY_WORKFLOW.md +126 -0
  26. package/templates/docs/en/BRANCHING.md +44 -0
  27. package/templates/docs/en/COMMUNICATION_PROTOCOL.md +59 -0
  28. package/templates/docs/en/DEFINITION_OF_DONE.md +60 -0
  29. package/templates/docs/en/INTERACTION_PATTERNS.md +58 -0
  30. package/templates/docs/en/PERFORMANCE_STANDARDS.md +31 -0
  31. package/templates/docs/en/SECURITY_STANDARDS.md +37 -0
  32. package/templates/docs/en/TEAM_ROSTER.md +35 -0
  33. package/templates/docs/en/templates/PRD_AI_FEATURE.md +85 -0
  34. package/templates/docs/vi/ACTIVE_STATE.md +28 -0
  35. package/templates/docs/vi/AGENCY_WORKFLOW.md +126 -0
  36. package/templates/docs/vi/BRANCHING.md +44 -0
  37. package/templates/docs/vi/COMMUNICATION_PROTOCOL.md +59 -0
  38. package/templates/docs/vi/DEFINITION_OF_DONE.md +60 -0
  39. package/templates/docs/vi/INTERACTION_PATTERNS.md +58 -0
  40. package/templates/docs/vi/PERFORMANCE_STANDARDS.md +31 -0
  41. package/templates/docs/vi/SECURITY_STANDARDS.md +37 -0
  42. package/templates/docs/vi/TEAM_ROSTER.md +35 -0
  43. package/templates/docs/vi/templates/PRD_AI_FEATURE.md +85 -0
@@ -0,0 +1,89 @@
1
+ 'use strict';
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { spawnSync } = require('child_process');
5
+ const { log, exists, ensureDir, readJSON, writeJSON } = require('./util.cjs');
6
+
7
+ function uniqueSources(group) {
8
+ return Array.from(new Set((group && group.skills || []).map((s) => s.source)));
9
+ }
10
+
11
+ function runCmd(cmd, args, cwd) {
12
+ try {
13
+ const r = spawnSync(cmd, args, { cwd, stdio: 'inherit', shell: process.platform === 'win32' });
14
+ return r.status === 0;
15
+ } catch (_) { return false; }
16
+ }
17
+
18
+ const SERENA_MEMOS = {
19
+ 'agency_workflow.md':
20
+ 'Canonical development process (single source of truth): **docs/AGENCY_WORKFLOW.md**.\n\n' +
21
+ 'Summary: classify every request via the router table → pick a path (Read-only / Fast / Standard / Full). ' +
22
+ 'Do NOT run the full 5-phase process for small tasks. A "Gate" means real Plan Mode (ExitPlanMode), not a promise.\n\n' +
23
+ 'Details (always read the source files, do not duplicate here): docs/AGENCY_WORKFLOW.md, docs/DEFINITION_OF_DONE.md, ' +
24
+ 'docs/BRANCHING.md, docs/TEAM_ROSTER.md, docs/ACTIVE_STATE.md, docs/COMMUNICATION_PROTOCOL.md, ' +
25
+ 'docs/SECURITY_STANDARDS.md, docs/PERFORMANCE_STANDARDS.md.\n',
26
+ 'delegation_rules.md':
27
+ 'Sub-agent delegation rules (canonical): **docs/TEAM_ROSTER.md**.\n\n' +
28
+ 'Summary: large work (Standard/Full path) → the Main Agent delegates via the Agent tool. Sub-agents do NOT inherit the ' +
29
+ 'conversation/skills/context → prompts must be SELF-CONTAINED (PRD/TDD links, skills to invoke, Done criteria, relevant files). ' +
30
+ 'Parallelize FE/BE only after the API contract is locked in Phase 2; use isolation: "worktree" for overlapping edits.\n',
31
+ };
32
+
33
+ async function run(ctx) {
34
+ const { target, cfg, detect, ask, pkgRoot } = ctx;
35
+ const manifest = readJSON(path.join(pkgRoot, 'skills.manifest.json'), { groups: {} });
36
+ cfg.integrations = cfg.integrations || {};
37
+
38
+ log.title('Integrations (optional — Enter to accept default)');
39
+
40
+ // ---- Skills (process — recommended) ----
41
+ const procSources = uniqueSources(manifest.groups && manifest.groups.process);
42
+ if (procSources.length && await ask.yesno(`Install recommended process skills via the \`skills\` CLI? (${procSources.join(', ')})`, true)) {
43
+ let ok = true;
44
+ for (const src of procSources) {
45
+ log.step(`npx skills add ${src}`);
46
+ if (!runCmd('npx', ['--yes', 'skills', 'add', src, '--yes'], target)) ok = false;
47
+ }
48
+ cfg.integrations.skills = ok ? 'installed' : 'partial';
49
+ if (!ok) log.warn('Some skills did not install. Install manually: ' + procSources.map((s) => `npx skills add ${s}`).join(' ; '));
50
+ else log.ok('Process skills installed.');
51
+ } else {
52
+ cfg.integrations.skills = 'skipped';
53
+ log.warn('Skills skipped. Later: ' + procSources.map((s) => `npx skills add ${s}`).join(' ; '));
54
+ }
55
+
56
+ // ---- Skills (design — optional, pick one) ----
57
+ const designSources = uniqueSources(manifest.groups && manifest.groups.design);
58
+ if (designSources.length && await ask.yesno('Install a design/UI skill pack? (pick your ONE direction afterwards)', false)) {
59
+ for (const src of designSources) runCmd('npx', ['--yes', 'skills', 'add', src, '--yes'], target);
60
+ cfg.integrations.design = 'installed';
61
+ log.ok('Design pack installed — set ONE direction in docs/TEAM_ROSTER.md §3.');
62
+ }
63
+
64
+ // ---- GitNexus (optional) ----
65
+ if (await ask.yesno('Index this repo with GitNexus now? (npx gitnexus analyze — enables impact analysis)', detect.hasGitnexus)) {
66
+ log.step('npx gitnexus analyze');
67
+ cfg.integrations.gitnexus = runCmd('npx', ['--yes', 'gitnexus', 'analyze'], target) ? 'indexed' : 'failed';
68
+ if (cfg.integrations.gitnexus === 'failed') log.warn('GitNexus not run. Install/retry later: npx gitnexus analyze');
69
+ } else {
70
+ cfg.integrations.gitnexus = 'skipped';
71
+ }
72
+
73
+ // ---- Serena (optional — seed pointer memories) ----
74
+ if (detect.hasSerena || await ask.yesno('Seed Serena pointer-memories? (only if you use the Serena MCP server)', detect.hasSerena)) {
75
+ const memDir = path.join(target, '.serena', 'memories', 'project');
76
+ ensureDir(memDir);
77
+ for (const [name, content] of Object.entries(SERENA_MEMOS)) {
78
+ fs.writeFileSync(path.join(memDir, name), content);
79
+ }
80
+ cfg.integrations.serena = 'seeded';
81
+ log.ok('Serena pointer-memories seeded (.serena/memories/project/).');
82
+ } else {
83
+ cfg.integrations.serena = 'skipped';
84
+ }
85
+
86
+ writeJSON(path.join(target, '.hero-vibe-kit', 'config.json'), cfg);
87
+ }
88
+
89
+ module.exports = { run };
package/src/links.cjs ADDED
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ function walk(dir) {
6
+ let out = [];
7
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
8
+ const p = path.join(dir, e.name);
9
+ if (e.isDirectory()) out = out.concat(walk(p));
10
+ else out.push(p);
11
+ }
12
+ return out;
13
+ }
14
+
15
+ // Check relative .md links resolve. roots = list of files or dirs.
16
+ function checkLinks(roots) {
17
+ const broken = [];
18
+ let checked = 0;
19
+ for (const root of roots) {
20
+ if (!fs.existsSync(root)) continue;
21
+ const files = fs.statSync(root).isDirectory() ? walk(root).filter((f) => f.endsWith('.md')) : [root];
22
+ for (const f of files) {
23
+ const dir = path.dirname(f);
24
+ const txt = fs.readFileSync(f, 'utf8');
25
+ const re = /\]\((\.{1,2}\/[^)#? ]+\.md)/g;
26
+ let m;
27
+ while ((m = re.exec(txt))) {
28
+ checked++;
29
+ const t = path.normalize(path.join(dir, m[1]));
30
+ if (!fs.existsSync(t)) broken.push({ file: f, link: m[1] });
31
+ }
32
+ }
33
+ }
34
+ return { broken, checked };
35
+ }
36
+
37
+ module.exports = { checkLinks, walk };
package/src/merge.cjs ADDED
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { ensureDir, exists, backup } = require('./util.cjs');
5
+
6
+ const START = '<!-- hero-vibe-kit:start -->';
7
+ const END = '<!-- hero-vibe-kit:end -->';
8
+
9
+ function escapeRe(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
10
+
11
+ function block(inner) {
12
+ return `${START}\n<!-- Managed by hero-vibe-kit. Update via the framework; edits inside this block are overwritten on \`update\`. -->\n${inner.trim()}\n${END}`;
13
+ }
14
+
15
+ // Insert/replace the hero-vibe-kit managed block. Preserves everything outside the markers.
16
+ function mergeManagedBlock(file, inner, titleIfNew) {
17
+ const b = block(inner);
18
+ if (!exists(file)) {
19
+ ensureDir(path.dirname(file));
20
+ const head = titleIfNew ? `# ${titleIfNew}\n\n` : '';
21
+ fs.writeFileSync(file, head + b + '\n');
22
+ return 'created';
23
+ }
24
+ let txt = fs.readFileSync(file, 'utf8');
25
+ backup(file);
26
+ if (txt.includes(START) && txt.includes(END)) {
27
+ txt = txt.replace(new RegExp(escapeRe(START) + '[\\s\\S]*?' + escapeRe(END)), b);
28
+ } else {
29
+ txt = txt.replace(/\s*$/, '') + '\n\n' + b + '\n';
30
+ }
31
+ fs.writeFileSync(file, txt);
32
+ return 'merged';
33
+ }
34
+
35
+ // Deep-merge our hooks + permissions into an existing settings.json without clobbering the user's.
36
+ function mergeSettings(file, templateFile) {
37
+ const tpl = JSON.parse(fs.readFileSync(templateFile, 'utf8'));
38
+ let cur = {};
39
+ if (exists(file)) { cur = JSON.parse(fs.readFileSync(file, 'utf8')); backup(file); }
40
+ else ensureDir(path.dirname(file));
41
+
42
+ cur.hooks = cur.hooks || {};
43
+ for (const ev of Object.keys(tpl.hooks || {})) {
44
+ cur.hooks[ev] = cur.hooks[ev] || [];
45
+ const existingCmds = new Set();
46
+ for (const m of cur.hooks[ev]) for (const h of (m.hooks || [])) existingCmds.add(h.command);
47
+ for (const matcher of tpl.hooks[ev]) {
48
+ const newHooks = (matcher.hooks || []).filter((h) => !existingCmds.has(h.command));
49
+ if (newHooks.length) cur.hooks[ev].push(Object.assign({}, matcher, { hooks: newHooks }));
50
+ }
51
+ }
52
+
53
+ if (tpl.permissions && tpl.permissions.allow) {
54
+ cur.permissions = cur.permissions || {};
55
+ cur.permissions.allow = Array.from(new Set([...(cur.permissions.allow || []), ...tpl.permissions.allow]));
56
+ }
57
+
58
+ fs.writeFileSync(file, JSON.stringify(cur, null, 2) + '\n');
59
+ }
60
+
61
+ module.exports = { mergeManagedBlock, mergeSettings, START, END };
package/src/render.cjs ADDED
@@ -0,0 +1,27 @@
1
+ 'use strict';
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ function renderString(s, vars) {
6
+ return s.replace(/\{\{(\w+)\}\}/g, (m, k) => (k in vars ? vars[k] : m));
7
+ }
8
+
9
+ function walk(dir) {
10
+ let out = [];
11
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
12
+ const p = path.join(dir, e.name);
13
+ if (e.isDirectory()) out = out.concat(walk(p));
14
+ else out.push(p);
15
+ }
16
+ return out;
17
+ }
18
+
19
+ // Returns [{ rel, content }] with placeholders substituted.
20
+ function renderTree(srcDir, vars) {
21
+ return walk(srcDir).map((abs) => ({
22
+ rel: path.relative(srcDir, abs),
23
+ content: renderString(fs.readFileSync(abs, 'utf8'), vars),
24
+ }));
25
+ }
26
+
27
+ module.exports = { renderString, renderTree, walk };
package/src/update.cjs ADDED
@@ -0,0 +1,59 @@
1
+ 'use strict';
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const { log, exists, backup, readJSON, writeJSON, ensureDir } = require('./util.cjs');
5
+ const { renderTree, renderString } = require('./render.cjs');
6
+ const { mergeManagedBlock, mergeSettings } = require('./merge.cjs');
7
+
8
+ const TEAM_LABELS = { solo: 'solo (you + AI)', 'small-team': 'small team (2–5)', enterprise: 'larger team (6+)' };
9
+ const BRANCH_LABELS = { 'gitlab-flow': 'GitLab flow', 'github-flow': 'GitHub flow', trunk: 'trunk-based' };
10
+
11
+ async function update(opts) {
12
+ const { pkgRoot, target } = opts;
13
+ const templates = path.join(pkgRoot, 'templates');
14
+ log.title('hero-vibe-kit · update');
15
+
16
+ const cfg = readJSON(path.join(target, '.hero-vibe-kit', 'config.json'), null);
17
+ if (!cfg) { log.err('Not initialized (.hero-vibe-kit/config.json missing). Run `hero-vibe-kit init`.'); process.exit(1); }
18
+
19
+ const vars = {
20
+ PROJECT_NAME: cfg.projectName,
21
+ DATE: new Date().toISOString().slice(0, 10),
22
+ TEAM_SIZE: TEAM_LABELS[cfg.teamSize] || cfg.teamSize,
23
+ BRANCHING_MODEL: BRANCH_LABELS[cfg.branchingModel] || cfg.branchingModel,
24
+ };
25
+
26
+ // Re-render managed docs; NEVER touch ACTIVE_STATE (working state).
27
+ const srcDocs = path.join(templates, 'docs', cfg.lang);
28
+ let n = 0;
29
+ for (const f of renderTree(srcDocs, vars)) {
30
+ if (path.basename(f.rel) === 'ACTIVE_STATE.md') continue;
31
+ const dest = path.join(target, 'docs', f.rel);
32
+ ensureDir(path.dirname(dest));
33
+ if (exists(dest)) backup(dest);
34
+ fs.writeFileSync(dest, f.content);
35
+ n++;
36
+ }
37
+ log.ok(`Docs re-rendered: ${n} (ACTIVE_STATE.md preserved)`);
38
+
39
+ // Managed blocks (content outside markers preserved)
40
+ const claudeInner = renderString(fs.readFileSync(path.join(templates, 'CLAUDE.md.tmpl'), 'utf8'), vars);
41
+ const agentsInner = renderString(fs.readFileSync(path.join(templates, 'AGENTS.md.tmpl'), 'utf8'), vars);
42
+ log.ok(`CLAUDE.md: ${mergeManagedBlock(path.join(target, 'CLAUDE.md'), claudeInner, cfg.projectName)}`);
43
+ log.ok(`AGENTS.md: ${mergeManagedBlock(path.join(target, 'AGENTS.md'), agentsInner, null)}`);
44
+
45
+ // Refresh hooks + settings
46
+ ensureDir(path.join(target, '.claude', 'hooks'));
47
+ for (const h of ['git-guard.cjs', 'stop-reminder.cjs']) {
48
+ fs.copyFileSync(path.join(templates, 'common', '.claude', 'hooks', h), path.join(target, '.claude', 'hooks', h));
49
+ }
50
+ mergeSettings(path.join(target, '.claude', 'settings.json'), path.join(templates, 'common', '.claude', 'settings.json'));
51
+ log.ok('Hooks + settings.json refreshed');
52
+
53
+ const newVer = JSON.parse(fs.readFileSync(path.join(pkgRoot, 'package.json'), 'utf8')).version;
54
+ cfg.version = newVer;
55
+ writeJSON(path.join(target, '.hero-vibe-kit', 'config.json'), cfg);
56
+ log.ok(`Now at v${newVer}`);
57
+ }
58
+
59
+ module.exports = { update };
package/src/util.cjs ADDED
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const readline = require('readline');
5
+
6
+ const C = { reset: '\x1b[0m', dim: '\x1b[2m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', cyan: '\x1b[36m', bold: '\x1b[1m' };
7
+ const useColor = process.stdout.isTTY && !process.env.NO_COLOR;
8
+ function c(col, s) { return useColor ? col + s + C.reset : s; }
9
+
10
+ const log = {
11
+ info: (m) => console.log(m),
12
+ step: (m) => console.log(c(C.cyan, '» ') + m),
13
+ ok: (m) => console.log(c(C.green, '✓ ') + m),
14
+ warn: (m) => console.log(c(C.yellow, '⚠ ') + m),
15
+ err: (m) => console.error(c(C.red, '✗ ') + m),
16
+ title: (m) => console.log('\n' + c(C.bold, m)),
17
+ };
18
+
19
+ function ensureDir(d) { fs.mkdirSync(d, { recursive: true }); }
20
+ function exists(p) { try { fs.accessSync(p); return true; } catch (_) { return false; } }
21
+ function readJSON(p, fallback) { try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch (_) { return fallback; } }
22
+ function writeJSON(p, obj) { ensureDir(path.dirname(p)); fs.writeFileSync(p, JSON.stringify(obj, null, 2) + '\n'); }
23
+ function backup(p) { if (exists(p)) { const b = p + '.bak'; fs.copyFileSync(p, b); return b; } return null; }
24
+
25
+ // Interactive asker. When `auto` is true (--yes / non-interactive), never prompts: returns defaults.
26
+ function makeAsker(auto) {
27
+ let rl = null;
28
+ function q(question) {
29
+ if (auto) return Promise.resolve('');
30
+ if (!rl) rl = readline.createInterface({ input: process.stdin, output: process.stdout });
31
+ return new Promise((res) => rl.question(question, (a) => res(a)));
32
+ }
33
+ async function text(label, def) {
34
+ const a = (await q(`${label}${def ? ` [${def}]` : ''}: `)).trim();
35
+ return a || def || '';
36
+ }
37
+ async function yesno(label, defYes) {
38
+ if (auto) return defYes;
39
+ const a = (await q(`${label} ${defYes ? '[Y/n]' : '[y/N]'}: `)).trim().toLowerCase();
40
+ if (!a) return defYes;
41
+ return a === 'y' || a === 'yes';
42
+ }
43
+ async function choice(label, opts, defIdx) {
44
+ defIdx = defIdx || 0;
45
+ if (auto) return opts[defIdx];
46
+ console.log(label);
47
+ opts.forEach((o, i) => console.log(` ${i + 1}) ${o}`));
48
+ const a = (await q(`Choose [${defIdx + 1}]: `)).trim();
49
+ const i = parseInt(a, 10);
50
+ return (i >= 1 && i <= opts.length) ? opts[i - 1] : opts[defIdx];
51
+ }
52
+ function close() { if (rl) rl.close(); }
53
+ return { text, yesno, choice, close };
54
+ }
55
+
56
+ module.exports = { C, c, log, ensureDir, exists, readJSON, writeJSON, backup, makeAsker };
@@ -0,0 +1,9 @@
1
+ # Agent Instructions — {{PROJECT_NAME}}
2
+
3
+ > Canonical development process: **[docs/AGENCY_WORKFLOW.md](docs/AGENCY_WORKFLOW.md)** (single source of truth). Applies to any AI agent working in this repo.
4
+ >
5
+ > 1. **Classify the request** via the router table → Read-only / Fast / Standard / Full. Don't run the heavy process for small tasks.
6
+ > 2. **Gate paths** require plan/approval before writing code (Claude Code: Plan Mode).
7
+ > 3. Follow [DEFINITION_OF_DONE](docs/DEFINITION_OF_DONE.md), [BRANCHING](docs/BRANCHING.md), [SECURITY_STANDARDS](docs/SECURITY_STANDARDS.md), [PERFORMANCE_STANDARDS](docs/PERFORMANCE_STANDARDS.md); keep [ACTIVE_STATE](docs/ACTIVE_STATE.md) updated.
8
+ > 4. Clarify requirements per [COMMUNICATION_PROTOCOL](docs/COMMUNICATION_PROTOCOL.md); for AI features use the [PRD_AI_FEATURE template](docs/templates/PRD_AI_FEATURE.md) and [INTERACTION_PATTERNS](docs/INTERACTION_PATTERNS.md).
9
+ > 5. Delegate sub-agents with **self-contained** prompts ([TEAM_ROSTER](docs/TEAM_ROSTER.md)).
@@ -0,0 +1,11 @@
1
+ ## 🧭 hero-vibe-kit workflow
2
+
3
+ > **READ BEFORE DOING ANYTHING:** [`docs/AGENCY_WORKFLOW.md`](docs/AGENCY_WORKFLOW.md) is the **single source of truth** for the {{PROJECT_NAME}} development process. Classify every request via the router table → pick a path (Read-only / Fast / Standard / Full). Don't run the heavy process for small tasks.
4
+
5
+ Docs: [AGENCY_WORKFLOW](docs/AGENCY_WORKFLOW.md) · [DEFINITION_OF_DONE](docs/DEFINITION_OF_DONE.md) · [BRANCHING](docs/BRANCHING.md) · [TEAM_ROSTER](docs/TEAM_ROSTER.md) · [ACTIVE_STATE](docs/ACTIVE_STATE.md) · [COMMUNICATION_PROTOCOL](docs/COMMUNICATION_PROTOCOL.md) · [INTERACTION_PATTERNS](docs/INTERACTION_PATTERNS.md) · [SECURITY_STANDARDS](docs/SECURITY_STANDARDS.md) · [PERFORMANCE_STANDARDS](docs/PERFORMANCE_STANDARDS.md) · [PRD_AI_FEATURE template](docs/templates/PRD_AI_FEATURE.md).
6
+
7
+ - **Workflow state:** read `docs/ACTIVE_STATE.md` at the start of every session and keep it updated.
8
+ - **Gates:** when a path requires a Gate, use Plan Mode (`EnterPlanMode` → `ExitPlanMode`) for real sign-off — not a prose promise.
9
+ - **Enforcement:** `git-guard` (PreToolUse) + `stop-reminder` (Stop) hooks are installed under `.claude/`. Don't bypass them.
10
+ - **GitNexus (if installed):** impact analysis is **conditional** — skip on an empty repo; apply once real code exists (Standard/Full paths) or per the escalation rule. If the index is stale, run `npx gitnexus analyze`.
11
+ - **Skills (if installed):** the workflow references process skills (brainstorming, writing-plans, test-driven-development, systematic-debugging, verification-before-completion, …). Invoke them via the `Skill` tool when relevant.
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /*
4
+ * PreToolUse hook (matcher: Bash) — enforcement "hỗn hợp".
5
+ * CHẶN (exit 2, stderr -> Claude) các lệnh git nguy hiểm.
6
+ * NHẮC (exit 0, stderr -> user) khi commit.
7
+ * Đọc JSON từ stdin theo schema hook của Claude Code: { tool_name, tool_input:{command} }.
8
+ * Tham chiếu quy trình: docs/AGENCY_WORKFLOW.md, docs/BRANCHING.md, docs/DEFINITION_OF_DONE.md
9
+ */
10
+ let raw = '';
11
+ process.stdin.on('data', (d) => { raw += d; });
12
+ process.stdin.on('end', () => {
13
+ let payload = {};
14
+ try { payload = JSON.parse(raw || '{}'); } catch (_) { process.exit(0); }
15
+
16
+ if (payload.tool_name !== 'Bash') process.exit(0);
17
+ const cmd = String((payload.tool_input && payload.tool_input.command) || '');
18
+ if (!/\bgit\b/.test(cmd)) process.exit(0);
19
+
20
+ const block = (msg) => { console.error('⛔ [git-guard] ' + msg); process.exit(2); };
21
+
22
+ const isPush = /\bpush\b/.test(cmd);
23
+ const isCommit = /\bcommit\b/.test(cmd);
24
+
25
+ // 1) Force push (cho phép --force-with-lease)
26
+ if (isPush && /(--force(?!-with-lease)|(^|\s)-f(\s|$))/.test(cmd)) {
27
+ block('Chặn force-push (--force/-f). Nếu thực sự cần, dùng --force-with-lease và xác nhận thủ công. ' +
28
+ '`main` được bảo vệ — đẩy thay đổi qua Merge Request (docs/BRANCHING.md).');
29
+ }
30
+
31
+ // 2) commit --no-verify / -n (bỏ qua hook/CI)
32
+ if (isCommit && /(--no-verify|(^|\s)-n(\s|$))/.test(cmd)) {
33
+ block('Chặn `git commit --no-verify`: không bỏ qua hook/kiểm tra trừ khi User yêu cầu rõ ràng.');
34
+ }
35
+
36
+ // 3) reset --hard (mất thay đổi)
37
+ if (/\breset\b[\s\S]*--hard/.test(cmd)) {
38
+ block('Chặn `git reset --hard` (có thể mất thay đổi). Cân nhắc `git stash`, hoặc chạy thủ công nếu thực sự muốn.');
39
+ }
40
+
41
+ // 4) push thẳng lên main (token "main" độc lập hoặc refspec :main)
42
+ if (isPush && /(^|\s|:)main(\s|$)/.test(cmd) && !/--dry-run/.test(cmd)) {
43
+ block('Chặn push thẳng lên `main` (được bảo vệ). Tạo nhánh + Merge Request (docs/BRANCHING.md).');
44
+ }
45
+
46
+ // NHẮC (không chặn) khi commit
47
+ if (isCommit) {
48
+ console.error('🔔 [git-guard] Trước khi commit, kiểm tra: ' +
49
+ '(1) docs/ACTIVE_STATE.md đã cập nhật? ' +
50
+ '(2) đã chạy gitnexus_detect_changes (khi đã có code)? ' +
51
+ '(3) message theo Conventional Commits? — xem docs/DEFINITION_OF_DONE.md');
52
+ }
53
+
54
+ process.exit(0);
55
+ });
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /*
4
+ * Stop hook — nhắc mềm (non-blocking, exit 0).
5
+ * Nếu có thay đổi chưa commit mà docs/ACTIVE_STATE.md chưa được động tới,
6
+ * in nhắc nhở (hiển thị cho user). KHÔNG chặn việc dừng (không exit 2 -> tránh vòng lặp).
7
+ */
8
+ const { execSync } = require('child_process');
9
+
10
+ let raw = '';
11
+ process.stdin.on('data', (d) => { raw += d; });
12
+ process.stdin.on('end', () => {
13
+ let payload = {};
14
+ try { payload = JSON.parse(raw || '{}'); } catch (_) { /* ignore */ }
15
+ if (payload.stop_hook_active) process.exit(0); // tránh kích hoạt lặp
16
+
17
+ const cwd = payload.cwd || process.cwd();
18
+ let status = '';
19
+ try {
20
+ status = execSync('git status --porcelain', { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
21
+ } catch (_) { process.exit(0); }
22
+
23
+ const lines = status.split('\n').map((l) => l.trim()).filter(Boolean);
24
+ if (lines.length === 0) process.exit(0); // không có gì thay đổi
25
+
26
+ const stateTouched = lines.some((l) => /ACTIVE_STATE\.md$/.test(l));
27
+ const otherChanged = lines.some((l) => !/ACTIVE_STATE\.md$/.test(l));
28
+
29
+ if (otherChanged && !stateTouched) {
30
+ console.error('🔔 [stop-reminder] Có thay đổi chưa commit nhưng docs/ACTIVE_STATE.md chưa được cập nhật. ' +
31
+ 'Nếu trạng thái công việc đã đổi, hãy cập nhật bảng pipeline + resume protocol (docs/AGENCY_WORKFLOW.md §0).');
32
+ }
33
+ process.exit(0);
34
+ });
@@ -0,0 +1,25 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Bash",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "node .claude/hooks/git-guard.cjs"
10
+ }
11
+ ]
12
+ }
13
+ ],
14
+ "Stop": [
15
+ {
16
+ "hooks": [
17
+ {
18
+ "type": "command",
19
+ "command": "node .claude/hooks/stop-reminder.cjs"
20
+ }
21
+ ]
22
+ }
23
+ ]
24
+ }
25
+ }
@@ -0,0 +1,28 @@
1
+ # Project Active State
2
+
3
+ **Last Updated:** {{DATE}}
4
+ **Current Global Phase:** 0 — Initialization & Setup
5
+
6
+ > 📌 **The "Active Features" table below IS the durable cross-session backlog.** It is the primary source of state when resuming.
7
+ > `TaskCreate`/`TaskList` only live **within the current session** (in-memory, lost when the session closes) — use them to track current work, NOT as a long-term backlog. Whenever a feature's status changes → update this table and the corresponding MR/issue (if any).
8
+
9
+ ## Active Features in Pipeline
10
+
11
+ | Feature / Epic | Path | Current phase | Branch / MR | Status | PRD | TDD |
12
+ | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
13
+ | N/A | N/A | N/A | - | Idle | - | - |
14
+
15
+ ## Blockers / Pending Actions
16
+ - Waiting for the Product Owner to propose the first feature/idea.
17
+ - Decide the tech stack → fill the placeholders in [DEFINITION_OF_DONE.md](./DEFINITION_OF_DONE.md).
18
+ - Decide the design direction → update [TEAM_ROSTER.md](./TEAM_ROSTER.md) §3.
19
+
20
+ ## Session Resume Protocol
21
+ *An AI starting a new session READS this section:*
22
+ 1. Read [AGENCY_WORKFLOW.md](./AGENCY_WORKFLOW.md) (SSOT) for the router & paths.
23
+ 2. Look at the "Active Features" table above + any **open MRs** (`git branch`, MR list) — do NOT rely on the previous session's TaskList (it's gone).
24
+ 3. By each feature's path & phase:
25
+ - **Read-only/Fast**: continue/finish then open the MR.
26
+ - **Standard/Full at Phase 1–2**: continue brainstorming/planning with the user (via the Plan Mode gate).
27
+ - **Standard/Full at Phase 3–4**: open the linked PRD/TDD, check code & branch status, recreate tasks with `TaskCreate` for this session, ask the user before resuming code/test.
28
+ 4. Update this table when you start working.
@@ -0,0 +1,126 @@
1
+ # Agency Workflow — Single Source of Truth
2
+
3
+ > **This is the ONLY canonical process document.** Every other file (CLAUDE.md, AGENTS.md, TEAM_ROSTER.md, Serena memories) only **links** here — it must NOT copy the content. To change the process, edit only this file.
4
+
5
+ The AI acts as a lean software agency. Operating scale: **{{TEAM_SIZE}} + AI**, following **{{BRANCHING_MODEL}}** (see [BRANCHING.md](./BRANCHING.md)). Completion criteria: see [DEFINITION_OF_DONE.md](./DEFINITION_OF_DONE.md).
6
+
7
+ The personas (BA / Architect / Developer / QA / Scrum Master) are **thinking lenses**, not mandatory ceremony for every task — details in [TEAM_ROSTER.md](./TEAM_ROSTER.md).
8
+
9
+ ---
10
+
11
+ ## 0. Golden rules
12
+
13
+ 1. **Start every request by CLASSIFYING the task** (table §1) → pick the right *path*. Don't default to the full 5-phase process for everything.
14
+ 2. **Don't jump into code while the request is unclear.** For Standard/Full paths, clarify scope first.
15
+ 3. **A gate is real Plan Mode, not a promise.** When a path requires a "Gate", use `EnterPlanMode` → present the plan → `ExitPlanMode` for the user to approve. This is a harness-enforced block, replacing the prose "wait for sign-off".
16
+ 4. **Update [ACTIVE_STATE.md](./ACTIVE_STATE.md)** when starting / finishing a unit of work. It is the durable cross-session backlog (TaskCreate only lives within the current session).
17
+ 5. **Follow [COMMUNICATION_PROTOCOL.md](./COMMUNICATION_PROTOCOL.md) in every interaction** — especially when clarifying requirements: no silent assumptions, layered certainty, blocking/non-blocking question classification, close the loop on misunderstandings.
18
+
19
+ ---
20
+
21
+ ## 1. Task classification → workflow (ROUTER)
22
+
23
+ When a request arrives, consult this table first to pick path, branch, required steps, and DoD.
24
+
25
+ | # | Task type | Trigger / example | Path | Gate | Branch | Required steps | Skills / Tools | DoD (short) |
26
+ |---|---|---|---|---|---|---|---|---|
27
+ | 1 | **Q&A / Explain / Find code** | "Explain X", "find where Y is handled", "what does this code do" | Read-only | No | — | Answer directly, **don't edit files** | `gitnexus_query`, `gitnexus_context`, serena `find_symbol` | Correct answer + `file:line` citations; no branch/commit |
28
+ | 2 | **Chore / Docs / Config** | edit README, change config, bump version, format | Fast | No | `chore/` `docs/` | edit → MR | — | build/lint green; no runtime behavior change |
29
+ | 3 | **Small bugfix (localized)** | clear root cause, ≤ ~2 files, not a shared symbol | Fast | No (but **repro test required**) | `fix/` | `systematic-debugging` → write a RED test reproducing the bug → fix → GREEN → MR | systematic-debugging, test-driven-development, verification-before-completion | bug repro test (red→green); regression green; root cause noted in MR |
30
+ | 4 | **Hotfix (urgent prod)** | production incident needs an urgent patch | Fast (expedited) | No | `hotfix/` (off `main`) | minimal fix + test → fast MR → backport to the development branch | systematic-debugging, verify | incident patched + test; backported; logged for post-mortem |
31
+ | 5 | **Change logic of an existing feature** | "change how X is computed", "add a condition to Y", "change behavior Z" | Standard | **YES** | `change/` | **impact analysis REQUIRED** → ensure/add regression tests → implement → QA review | `gitnexus_impact` (upstream), test-driven-development, code-review | impact reported; regression + new tests green; `detect_changes` scope correct |
32
+ | 6 | **Refactor (NO behavior change)** | "split this module", "rename", "clean up" | Standard | **YES** | `refactor/` | tests GREEN before → impact analysis → change (use `gitnexus_rename`) → tests GREEN after (unchanged) | gitnexus_rename, gitnexus_impact, simplify | same test suite passes before & after; NO manual find-replace |
33
+ | 7 | **New feature** | "build feature Z" | Full (5-phase) | **YES (2 gates: PRD + TDD)** | `feat/` | full §3 | brainstorming, writing-plans, test-driven-development, design system, code-review, security-review | full [DEFINITION_OF_DONE.md](./DEFINITION_OF_DONE.md) |
34
+ | 8 | **Spike / Research / POC** | "feasibility study", "try an approach" | Timeboxed | No (timebox instead of gate) | `spike/` (throwaway) | clear timebox → output a **recommendation doc**; **do NOT merge POC code** into main | deep-research, gitnexus_exploring | conclusion + recommendation doc; POC code stays out of main |
35
+
36
+ ### Escalation rule
37
+ A task on the **Fast path** that turns out to:
38
+ - touch a symbol with many callers / `gitnexus_impact` returns **MEDIUM or higher**, OR
39
+ - spread across **> 5 files**,
40
+
41
+ → **stop and escalate to the Standard path**: open Plan Mode (gate), run and report impact analysis before continuing.
42
+
43
+ ---
44
+
45
+ ## 2. The four paths (definitions)
46
+
47
+ ### Read-only
48
+ Answer questions, read/explain code. No branch, no gate, no commit. Prefer `gitnexus_query`/`gitnexus_context` over blind grep. Always cite `file:line`.
49
+
50
+ ### Fast path
51
+ For small, low-risk work (chore/docs/localized bugfix/hotfix).
52
+ 1. Create a branch with the right prefix (see [BRANCHING.md](./BRANCHING.md)).
53
+ 2. Make the change. For a **bugfix**: write a failing (red) test that reproduces the bug BEFORE fixing.
54
+ 3. Quick self-review (`code-review` if you want) + run tests/lint.
55
+ 4. Open an MR against the "Fast" DoD in [DEFINITION_OF_DONE.md](./DEFINITION_OF_DONE.md).
56
+ > No plan sign-off needed. But if you hit the escalation rule → move to Standard.
57
+
58
+ ### Standard path
59
+ For changing existing feature logic & refactors.
60
+ 1. **Gate**: `EnterPlanMode` → investigate + run `gitnexus_impact` (upstream) → present a plan stating **blast radius + risk level** → `ExitPlanMode` and wait for approval.
61
+ 2. Warn the user if risk is **HIGH/CRITICAL** before continuing.
62
+ 3. Implement with TDD; for refactors keep tests unchanged.
63
+ 4. QA: spawn a review sub-agent (`code-review` + `security-review` if touching a sensitive surface) + `gitnexus_detect_changes`.
64
+ 5. MR against the "Standard" DoD.
65
+
66
+ ### Full path (5-phase) — new features only
67
+ See §3.
68
+
69
+ ---
70
+
71
+ ## 3. Full path: 5 phases for a new feature
72
+
73
+ > Applies only to task type **#7 (Feature)**. Small tasks do NOT run these phases.
74
+
75
+ ### Phase 1 — Discovery & Scoping (lens: Business Analyst)
76
+ 1. Trigger the `brainstorming` skill. **Apply [COMMUNICATION_PROTOCOL.md](./COMMUNICATION_PROTOCOL.md)** throughout (how to ask, label assumptions, close each round with a summary + open questions + assumptions).
77
+ 2. Clarify: User Personas, Business Flows, Edge Cases, goals, acceptance criteria.
78
+ 3. **If it's an AI/assistant feature:** use the **[PRD_AI_FEATURE.md template](./templates/PRD_AI_FEATURE.md)** — it forces the AI-specific dimensions (behavior under ambiguity, guardrails/refusal, definition of "correct" + golden examples, eval strategy, fallback/HITL), and references [INTERACTION_PATTERNS.md](./INTERACTION_PATTERNS.md) for how the assistant talks to end-users.
79
+ 4. Output: **PRD / Scope Document** (copy to `docs/specs/YYYY-MM-DD-<feature>.md`, link in ACTIVE_STATE) — including a Decision Log + Assumptions Register.
80
+ 5. **GATE 1 (Plan Mode):** present the PRD → `ExitPlanMode` → wait for the user (Product Owner) to approve.
81
+
82
+ ### Phase 2 — Architecture & Planning (lens: System Architect)
83
+ 1. `gitnexus_exploring` to understand the current architecture (skip if the relevant area is empty).
84
+ 2. `gitnexus_impact` for any change to existing code.
85
+ 3. **Lightweight threat modeling (shift-left):** list attack surface / sensitive data / permissions right here, per [SECURITY_STANDARDS.md](./SECURITY_STANDARDS.md) — don't push it all to QA.
86
+ 4. **Set the performance budget:** decide target latency/throughput/token-cost per [PERFORMANCE_STANDARDS.md](./PERFORMANCE_STANDARDS.md) (fill the `<TBD>`s).
87
+ 5. **Lock the API/interface contract** between components (FE↔BE, module↔module). *This is a prerequisite for parallelizing in Phase 3.*
88
+ 6. Trigger `writing-plans` to break down the work → create tasks with `TaskCreate` (session-scoped) + record in ACTIVE_STATE (durable).
89
+ 7. Output: **Technical Design Document (TDD)** + task list.
90
+ 8. **GATE 2 (Plan Mode):** present the TDD → `ExitPlanMode` → wait for approval of the technical approach.
91
+
92
+ ### Phase 3 — Implementation (lens: Developer, possibly a sub-agent)
93
+ 1. Mark tasks `in_progress` (`TaskUpdate`) + update ACTIVE_STATE.
94
+ 2. Large work → **delegate to sub-agents** via the `Agent` tool. **Sub-agent prompts must be self-contained** (sub-agents do NOT inherit the conversation/skills): embed PRD/TDD links, name the skills to invoke, the Done criteria, and the relevant files. Details: [TEAM_ROSTER.md](./TEAM_ROSTER.md).
95
+ 3. **Conditional parallelization:** only spawn FE & BE in parallel **after the API contract (Phase 2.5) is locked**. When multiple agents edit overlapping files → `isolation: "worktree"`.
96
+ 4. Developers apply `test-driven-development`.
97
+ 5. Frontend follows **one** locked design direction (don't invoke multiple design skills at once — see TEAM_ROSTER §design).
98
+
99
+ ### Phase 4 — Quality Assurance (lens: QA, sub-agent)
100
+ 1. Spawn a Code Reviewer / QA sub-agent (self-contained prompt).
101
+ 2. QA runs `verification-before-completion`, `security-review`, `systematic-debugging` as needed. Check against [SECURITY_STANDARDS.md](./SECURITY_STANDARDS.md) + [PERFORMANCE_STANDARDS.md](./PERFORMANCE_STANDARDS.md) (incl. OWASP LLM Top 10 for AI features, and performance budgets).
102
+ 3. `gitnexus_detect_changes` to confirm no unexpected execution flows broke.
103
+ 4. Must meet [DEFINITION_OF_DONE.md](./DEFINITION_OF_DONE.md) (Full level) before merging.
104
+
105
+ ### Phase 5 — Handover & Retro (lens: DevOps / Scrum Master)
106
+ 1. Trigger `finishing-a-development-branch` → open/merge the MR per [BRANCHING.md](./BRANCHING.md).
107
+ 2. Update **CLAUDE.md** if there are new architectural decisions (record only what's new, don't duplicate).
108
+ 3. **Light retro (3 lines):** what went well / what was painful / one improvement for next time — record in ACTIVE_STATE or the MR.
109
+ 4. Save project context to Serena memory (pointers, no content duplication).
110
+ 5. Mark tasks `completed` + update ACTIVE_STATE.
111
+
112
+ ---
113
+
114
+ ## 4. Related documents
115
+ | File | Content |
116
+ |------|---------|
117
+ | [TASK router §1](#1-task-classification--workflow-router) | Pick a path by task type |
118
+ | [DEFINITION_OF_DONE.md](./DEFINITION_OF_DONE.md) | Measurable completion criteria (per path) |
119
+ | [BRANCHING.md](./BRANCHING.md) | Branching model, branch naming, Conventional Commits |
120
+ | [TEAM_ROSTER.md](./TEAM_ROSTER.md) | Personas + sub-agent delegation rules + design direction |
121
+ | [ACTIVE_STATE.md](./ACTIVE_STATE.md) | Pipeline state + resume protocol |
122
+ | [COMMUNICATION_PROTOCOL.md](./COMMUNICATION_PROTOCOL.md) | Human↔AI communication protocol (requirements clarification) |
123
+ | [templates/PRD_AI_FEATURE.md](./templates/PRD_AI_FEATURE.md) | PRD template for AI features (dimensions to clarify) |
124
+ | [INTERACTION_PATTERNS.md](./INTERACTION_PATTERNS.md) | How the product (assistant) talks to end-users |
125
+ | [SECURITY_STANDARDS.md](./SECURITY_STANDARDS.md) | Security baseline + OWASP LLM Top 10 |
126
+ | [PERFORMANCE_STANDARDS.md](./PERFORMANCE_STANDARDS.md) | Performance budgets + AI standards (prompt caching…) |