@longph2102/v-flow 1.5.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.
- package/AGENTS.md +265 -0
- package/CHANGELOG.md +318 -0
- package/LICENSE +21 -0
- package/README.md +326 -0
- package/agents/ba-agent.md +437 -0
- package/agents/ba-critic-agent.md +156 -0
- package/agents/ba-to-ptyc-agent.md +112 -0
- package/agents/bugfix-analyst-agent.md +221 -0
- package/agents/constitute-agent.md +155 -0
- package/agents/help-agent.md +168 -0
- package/agents/implement-agent.md +220 -0
- package/agents/import-ba-docs-agent.md +164 -0
- package/agents/master-check-agent.md +228 -0
- package/agents/metrics-agent.md +180 -0
- package/agents/operations-agent.md +123 -0
- package/agents/plan-agent.md +218 -0
- package/agents/prototype-agent.md +191 -0
- package/agents/retrospective-agent.md +196 -0
- package/agents/review-agent.md +210 -0
- package/agents/sprint-agent.md +191 -0
- package/agents/status-agent.md +186 -0
- package/agents/sync-agent.md +201 -0
- package/agents/test-agent.md +166 -0
- package/agents/understand-agent.md +339 -0
- package/cli/commands/check.js +96 -0
- package/cli/commands/dev-quiz.js +107 -0
- package/cli/commands/doctor.js +348 -0
- package/cli/commands/feature.js +259 -0
- package/cli/commands/hooks.js +163 -0
- package/cli/commands/init.js +189 -0
- package/cli/commands/log.js +199 -0
- package/cli/commands/plugin.js +230 -0
- package/cli/commands/score-card.js +203 -0
- package/cli/commands/status.js +269 -0
- package/cli/commands/sync.js +59 -0
- package/cli/commands/upgrade.js +150 -0
- package/cli/commands/validate.js +1259 -0
- package/cli/commands/watch.js +151 -0
- package/cli/index.js +46 -0
- package/cli/lib/ac-test-gate.js +89 -0
- package/cli/lib/activity-log.js +209 -0
- package/cli/lib/cli-error.js +183 -0
- package/cli/lib/constitution-lint.js +561 -0
- package/cli/lib/dev-quiz-grade.js +127 -0
- package/cli/lib/governance.js +78 -0
- package/cli/lib/hook-targets.js +167 -0
- package/cli/lib/i18n.js +375 -0
- package/cli/lib/knowledge-oracle.js +379 -0
- package/cli/lib/logger.js +203 -0
- package/cli/lib/module-card-lint.js +304 -0
- package/cli/lib/module-card-score.js +223 -0
- package/cli/lib/plugins.js +481 -0
- package/cli/lib/scanner.js +692 -0
- package/cli/lib/sync-core.js +232 -0
- package/cli/lib/sync-targets.js +84 -0
- package/cli/lib/templates.js +268 -0
- package/cli/lib/yaml-parser.js +203 -0
- package/commands/v.ba-critic.md +101 -0
- package/commands/v.ba-to-ptyc.md +71 -0
- package/commands/v.bugfix.md +86 -0
- package/commands/v.check.md +131 -0
- package/commands/v.constitute.md +87 -0
- package/commands/v.constitution.md +84 -0
- package/commands/v.fork.md +127 -0
- package/commands/v.help.md +73 -0
- package/commands/v.hotfix.md +200 -0
- package/commands/v.implement.md +92 -0
- package/commands/v.import-ba-docs.md +222 -0
- package/commands/v.metrics.md +74 -0
- package/commands/v.operations.md +70 -0
- package/commands/v.plan.md +78 -0
- package/commands/v.prototype.md +121 -0
- package/commands/v.quickfix.md +169 -0
- package/commands/v.retrospective.md +80 -0
- package/commands/v.review.md +78 -0
- package/commands/v.rewind.md +127 -0
- package/commands/v.specify.md +118 -0
- package/commands/v.sprint.md +75 -0
- package/commands/v.status.md +62 -0
- package/commands/v.sync.md +81 -0
- package/commands/v.test.md +67 -0
- package/commands/v.understand.md +112 -0
- package/package.json +65 -0
- package/skills/_shared/constitution-reader/SKILL.md +109 -0
- package/skills/_shared/constitution-reader/config.json +52 -0
- package/skills/_shared/constitution-reader/examples/good/b1-phase-output.md +48 -0
- package/skills/_shared/constitution-reader/gotchas.md +46 -0
- package/skills/_shared/context-reader/SKILL.md +111 -0
- package/skills/_shared/context-reader/config.json +54 -0
- package/skills/_shared/context-reader/examples/good/legacy-nodejs-output.md +35 -0
- package/skills/_shared/context-reader/gotchas.md +49 -0
- package/skills/_shared/ears-notation/SKILL.md +63 -0
- package/skills/_shared/ears-notation/config.json +55 -0
- package/skills/_shared/ears-notation/examples/good/plan-test-interpretation.md +29 -0
- package/skills/_shared/ears-notation/gotchas.md +43 -0
- package/skills/check/cross-validator/SKILL.md +206 -0
- package/skills/check/cross-validator/config.json +33 -0
- package/skills/check/cross-validator/examples/good/validation-report-pass-with-concerns.md +105 -0
- package/skills/check/cross-validator/gotchas.md +43 -0
- package/skills/implement/constitution-enforcer/SKILL.md +134 -0
- package/skills/implement/constitution-enforcer/config.json +16 -0
- package/skills/implement/constitution-enforcer/examples/bad/vague-report.md +42 -0
- package/skills/implement/constitution-enforcer/examples/good/compliance-report.md +57 -0
- package/skills/implement/constitution-enforcer/gotchas.md +26 -0
- package/skills/implement/constitution-enforcer/scripts/check-constitution.sh +88 -0
- package/skills/implement/no-go-zone-guard/SKILL.md +173 -0
- package/skills/implement/no-go-zone-guard/config.json +28 -0
- package/skills/implement/no-go-zone-guard/examples/good/adapter-workaround.md +46 -0
- package/skills/implement/no-go-zone-guard/gotchas.md +27 -0
- package/skills/implement/no-go-zone-guard/scripts/check-nogo-zones.sh +148 -0
- package/skills/implement/no-go-zone-guard/scripts/nogo-precommit.sh +96 -0
- package/skills/implement/tdd-driver/SKILL.md +159 -0
- package/skills/implement/tdd-driver/config.json +33 -0
- package/skills/implement/tdd-driver/examples/good/tdd-cycle-product-repo.md +81 -0
- package/skills/implement/tdd-driver/gotchas.md +34 -0
- package/skills/metrics/metrics-collector/SKILL.md +133 -0
- package/skills/metrics/metrics-collector/config.json +16 -0
- package/skills/metrics/metrics-collector/examples/bad/incomplete-report.md +48 -0
- package/skills/metrics/metrics-collector/examples/good/full-metrics-report.md +101 -0
- package/skills/metrics/metrics-collector/gotchas.md +26 -0
- package/skills/operations/incident-runbook/SKILL.md +167 -0
- package/skills/operations/incident-runbook/config.json +21 -0
- package/skills/operations/incident-runbook/examples/bad/vague-incident-report.md +48 -0
- package/skills/operations/incident-runbook/examples/good/p1-hotfix-response.md +119 -0
- package/skills/operations/incident-runbook/gotchas.md +26 -0
- package/skills/plan/architecture-designer/SKILL.md +228 -0
- package/skills/plan/architecture-designer/config.json +32 -0
- package/skills/plan/architecture-designer/examples/bad/vague-plan.md +62 -0
- package/skills/plan/architecture-designer/examples/good/expand-contract-migration.md +56 -0
- package/skills/plan/architecture-designer/examples/good/plan-structure.md +58 -0
- package/skills/plan/architecture-designer/gotchas.md +45 -0
- package/skills/plan/task-breakdown/SKILL.md +208 -0
- package/skills/plan/task-breakdown/config.json +26 -0
- package/skills/plan/task-breakdown/examples/bad/vague-tasks.md +77 -0
- package/skills/plan/task-breakdown/examples/good/spike-clarify-tasks.md +66 -0
- package/skills/plan/task-breakdown/examples/good/tasks-login-feature.md +111 -0
- package/skills/plan/task-breakdown/gotchas.md +39 -0
- package/skills/prototype/LOGIC.md +240 -0
- package/skills/prototype/SKILL.md +185 -0
- package/skills/prototype/UI.md +407 -0
- package/skills/prototype/config.json +104 -0
- package/skills/prototype/examples/bad/prototype-notes.md +68 -0
- package/skills/prototype/examples/good/prototype-notes-ui.md +109 -0
- package/skills/prototype/examples/good/prototype-notes.md +67 -0
- package/skills/prototype/gotchas.md +128 -0
- package/skills/prototype/scripts/check-flow-state.ps1 +112 -0
- package/skills/prototype/scripts/check-flow-state.sh +104 -0
- package/skills/prototype/scripts/check-prototype-cleanup.ps1 +124 -0
- package/skills/prototype/scripts/check-prototype-cleanup.sh +109 -0
- package/skills/prototype/scripts/check-prototype-notes.ps1 +107 -0
- package/skills/prototype/scripts/check-prototype-notes.sh +102 -0
- package/skills/review/adversarial-reviewer/SKILL.md +137 -0
- package/skills/review/adversarial-reviewer/config.json +32 -0
- package/skills/review/adversarial-reviewer/examples/good/review-report-template.md +56 -0
- package/skills/review/adversarial-reviewer/gotchas.md +46 -0
- package/skills/review/adversarial-reviewer/scripts/quick-security-scan.sh +52 -0
- package/skills/specify/ba-bpmn-doc-gen/SKILL.md +108 -0
- package/skills/specify/ba-bpmn-doc-gen/reference/reference-bpmn-generation.md +528 -0
- package/skills/specify/ba-bpmn-doc-gen/reference/reference-drawio-flowchart.md +466 -0
- package/skills/specify/ba-critic/SKILL.md +172 -0
- package/skills/specify/ba-critic/config.json +32 -0
- package/skills/specify/ba-critic/examples/good/critic-report-round1.md +51 -0
- package/skills/specify/ba-critic/gotchas.md +40 -0
- package/skills/specify/ba-critic/scripts/check-spec-quality.sh +72 -0
- package/skills/specify/ba-doc-generator/SKILL.md +102 -0
- package/skills/specify/ba-doc-generator/references/template-clevel.md +84 -0
- package/skills/specify/ba-doc-generator/references/template-compliance.md +83 -0
- package/skills/specify/ba-doc-generator/references/template-dev.md +138 -0
- package/skills/specify/ba-doc-generator/references/template-partner.md +167 -0
- package/skills/specify/ba-doc-generator/references/template-pm.md +92 -0
- package/skills/specify/ba-doc-generator/references/template-review.md +114 -0
- package/skills/specify/ba-doc-generator/references/template-tester.md +108 -0
- package/skills/specify/ba-doc-generator/references/template-user.md +98 -0
- package/skills/specify/bugfix-analyst/SKILL.md +296 -0
- package/skills/specify/bugfix-analyst/config.json +41 -0
- package/skills/specify/bugfix-analyst/examples/bad/common-mistakes.md +71 -0
- package/skills/specify/bugfix-analyst/examples/good/email-validation-bugfix.md +53 -0
- package/skills/specify/bugfix-analyst/gotchas.md +51 -0
- package/skills/specify/ears-writer/SKILL.md +129 -0
- package/skills/specify/ears-writer/config.json +20 -0
- package/skills/specify/ears-writer/examples/bad/common-mistakes.md +17 -0
- package/skills/specify/ears-writer/examples/good/login-requirements.md +41 -0
- package/skills/specify/ears-writer/gotchas.md +43 -0
- package/skills/specify/ears-writer/scripts/check-ears-compliance.sh +51 -0
- package/skills/test/test-case-generator/SKILL.md +161 -0
- package/skills/test/test-case-generator/config.json +33 -0
- package/skills/test/test-case-generator/examples/good/test-cases-login.md +104 -0
- package/skills/test/test-case-generator/gotchas.md +43 -0
- package/skills/understand/ba-docs-scanner/SKILL.md +239 -0
- package/skills/understand/ba-docs-scanner/config.json +47 -0
- package/skills/understand/ba-docs-scanner/examples/good/work-order-br-extract.md +28 -0
- package/skills/understand/ba-docs-scanner/gotchas.md +44 -0
- package/skills/understand/ba-docs-scanner/merge-rules.md +47 -0
- package/skills/understand/codebase-scanner/SKILL.md +260 -0
- package/skills/understand/codebase-scanner/config.json +56 -0
- package/skills/understand/codebase-scanner/examples/good/menu-module-output.md +44 -0
- package/skills/understand/codebase-scanner/gotchas.md +42 -0
- package/skills/understand/codebase-scanner/scripts/scan-project-structure.sh +64 -0
- package/templates/DESIGN.md +456 -0
- package/templates/agent-command-template.yaml +240 -0
- package/templates/agent-config-template.md +170 -0
- package/templates/agent-definition-template.md +145 -0
- package/templates/agent-metrics-template.md +150 -0
- package/templates/api-contract-template.md +72 -0
- package/templates/bugfix-report-template.md +195 -0
- package/templates/bugfix-spec-template.md +134 -0
- package/templates/code-review-report-template.md +119 -0
- package/templates/constitution-template.md +234 -0
- package/templates/context-template.md +94 -0
- package/templates/data-model-template.md +95 -0
- package/templates/decision-log-template.md +92 -0
- package/templates/flow-state-template.yaml +208 -0
- package/templates/github/workflows/v-flow-validate.yml +30 -0
- package/templates/knowledge/adr-template.md +70 -0
- package/templates/knowledge/api-contract-template.md +140 -0
- package/templates/knowledge/domain-glossary.md +29 -0
- package/templates/knowledge/golden-tests-readme.md +115 -0
- package/templates/knowledge/lessons-learned.md +41 -0
- package/templates/knowledge/patterns.md +103 -0
- package/templates/module-card/SKILL.md +85 -0
- package/templates/module-card/api-specs.md +96 -0
- package/templates/module-card/business-quiz.md +119 -0
- package/templates/module-card/cross-service.md +125 -0
- package/templates/module-card/db.md +85 -0
- package/templates/module-card/dev-quiz.md +62 -0
- package/templates/module-card/permissions.md +83 -0
- package/templates/module-card/state-diagram.md +64 -0
- package/templates/module-card/tech-context.md +90 -0
- package/templates/module-card/ui-flows.md +91 -0
- package/templates/module-card/use-cases.md +142 -0
- package/templates/module-template.yaml +161 -0
- package/templates/operations-report-template.md +108 -0
- package/templates/plan-template.md +308 -0
- package/templates/prototype-notes-template.md +116 -0
- package/templates/ptyc/PTYC.template.docx +0 -0
- package/templates/ptyc/ptyc.meta.example.yaml +44 -0
- package/templates/retrospective-report-template.md +136 -0
- package/templates/security-review-template.md +84 -0
- package/templates/session-template.md +167 -0
- package/templates/spec-review-log-template.md +75 -0
- package/templates/spec-template.md +229 -0
- package/templates/sprint-status-template.md +101 -0
- package/templates/tasks-template.md +275 -0
- package/templates/test-cases-template.md +124 -0
- package/templates/ux-checklist-template.md +79 -0
- package/templates/validation-report-template.md +125 -0
- package/templates/vflow-config-template.yaml +22 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governance â risk-tier gate + approval metadata (lightweight schema check).
|
|
3
|
+
*
|
|
4
|
+
* Declared in a feature's .flow-state.yaml:
|
|
5
|
+
* risk_tier: low | medium | high | critical
|
|
6
|
+
* approvals:
|
|
7
|
+
* - role: spec # which gate this approval covers
|
|
8
|
+
* actor: "Nguyen Van A"
|
|
9
|
+
* decision: approved
|
|
10
|
+
* timestamp: "2026-06-06T10:00:00Z"
|
|
11
|
+
*
|
|
12
|
+
* GATE_MATRIX maps each tier â the approval roles it requires. checkGovernance
|
|
13
|
+
* returns warnings (surfaced by `v-flow validate`; `--strict` blocks CI). This is
|
|
14
|
+
* intentionally lightweight â it checks the PRESENCE + structural validity of
|
|
15
|
+
* approvals (actor/timestamp), NOT whether an approval was semantically warranted.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const TIERS = ['low', 'medium', 'high', 'critical'];
|
|
19
|
+
|
|
20
|
+
// Required approval roles per risk tier (escalating). Tune per organisation.
|
|
21
|
+
const GATE_MATRIX = {
|
|
22
|
+
low: [],
|
|
23
|
+
medium: ['spec'],
|
|
24
|
+
high: ['spec', 'security'],
|
|
25
|
+
critical: ['spec', 'security', 'tech_lead'],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {object} state - normalized flow state ({ riskTier, approvals })
|
|
30
|
+
* @returns {{ tier: string|null, issues: string[], warnings: string[], ok: string[] }}
|
|
31
|
+
*/
|
|
32
|
+
function checkGovernance(state) {
|
|
33
|
+
const issues = [];
|
|
34
|
+
const warnings = [];
|
|
35
|
+
const ok = [];
|
|
36
|
+
|
|
37
|
+
const tier = state && state.riskTier ? String(state.riskTier).toLowerCase() : null;
|
|
38
|
+
|
|
39
|
+
if (!tier) {
|
|
40
|
+
warnings.push('Chưa khai bÃĄo risk_tier trong .flow-state.yaml (low|medium|high|critical) â không xÃĄc Äáģnh ÄÆ°áģŖc gate báē¯t buáģc');
|
|
41
|
+
return { tier: null, issues, warnings, ok };
|
|
42
|
+
}
|
|
43
|
+
if (!TIERS.includes(tier)) {
|
|
44
|
+
warnings.push(`risk_tier không háģŖp láģ: "${tier}" â dÚng low|medium|high|critical`);
|
|
45
|
+
return { tier, issues, warnings, ok };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const required = GATE_MATRIX[tier];
|
|
49
|
+
const approvals = Array.isArray(state.approvals) ? state.approvals : [];
|
|
50
|
+
|
|
51
|
+
// Structural validity â an approval without actor + timestamp is not auditable.
|
|
52
|
+
for (const a of approvals) {
|
|
53
|
+
const role = a.role || a.gate || '(không rÃĩ role)';
|
|
54
|
+
if (!a.actor || !a.timestamp) {
|
|
55
|
+
warnings.push(`Approval "${role}" thiáēŋu actor/timestamp â không audit ÄÆ°áģŖc`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Each required gate must have an "approved" approval entry.
|
|
60
|
+
const approvedRoles = new Set(
|
|
61
|
+
approvals
|
|
62
|
+
.filter((a) => !a.decision || String(a.decision).toLowerCase() === 'approved')
|
|
63
|
+
.map((a) => a.role || a.gate)
|
|
64
|
+
);
|
|
65
|
+
for (const role of required) {
|
|
66
|
+
if (!approvedRoles.has(role)) {
|
|
67
|
+
warnings.push(`Tier "${tier}" yÃĒu cáē§u approval "${role}" â chưa cÃŗ (cáē§n actor + timestamp)`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (warnings.length === 0) {
|
|
72
|
+
ok.push(`Governance: tier "${tier}" Äáģ§ approval báē¯t buáģc (${required.join(', ') || 'không yÃĒu cáē§u'})`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { tier, issues, warnings, ok };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { TIERS, GATE_MATRIX, checkGovernance };
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook Targets â per-provider lifecycle-hook config descriptors.
|
|
3
|
+
*
|
|
4
|
+
* Each AI coding tool now ships a hooks system, but the config shapes differ
|
|
5
|
+
* (event-name casing, `command` vs `bash`, nested-with-matcher vs flat, a named
|
|
6
|
+
* group for Antigravity). This module declares those shapes once so the
|
|
7
|
+
* `v-flow hooks` command can install / remove / inspect a single logging hook
|
|
8
|
+
* (`v-flow log --hook <provider>`) for every tool, merging into existing config
|
|
9
|
+
* without clobbering the user's own hooks.
|
|
10
|
+
*
|
|
11
|
+
* Verified against vendor docs (mid-2026):
|
|
12
|
+
* Claude Code .claude/settings.json â hooks.{PostToolUse,Stop,SessionStart} (nested, matcher)
|
|
13
|
+
* OpenAI Codex .codex/hooks.json â hooks.{PostToolUse,Stop,SessionStart} (nested)
|
|
14
|
+
* Antigravity .agents/hooks.json â vflow-log.{PostToolUse,Stop} (named group, nested, matcher)
|
|
15
|
+
* Cursor .cursor/hooks.json â hooks.{postToolUse,stop,sessionStart} (flat, `command`, version 1)
|
|
16
|
+
* Copilot .github/hooks/vflow-log.json â hooks.{postToolUse,agentStop,sessionStart} (flat, `bash`, version 1)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const PROVIDERS = [
|
|
20
|
+
{
|
|
21
|
+
key: 'claude',
|
|
22
|
+
label: 'Claude Code',
|
|
23
|
+
file: ['.claude', 'settings.json'],
|
|
24
|
+
root: 'hooks',
|
|
25
|
+
form: 'nested',
|
|
26
|
+
version: null,
|
|
27
|
+
events: ['PostToolUse', 'Stop', 'SessionStart'],
|
|
28
|
+
matcherEvents: ['PostToolUse'],
|
|
29
|
+
timeout: null,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
key: 'codex',
|
|
33
|
+
label: 'OpenAI Codex',
|
|
34
|
+
file: ['.codex', 'hooks.json'],
|
|
35
|
+
root: 'hooks',
|
|
36
|
+
form: 'nested',
|
|
37
|
+
version: null,
|
|
38
|
+
events: ['PostToolUse', 'Stop', 'SessionStart'],
|
|
39
|
+
matcherEvents: [],
|
|
40
|
+
timeout: null,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
key: 'antigravity',
|
|
44
|
+
label: 'Antigravity',
|
|
45
|
+
file: ['.agents', 'hooks.json'],
|
|
46
|
+
root: 'vflow-log',
|
|
47
|
+
form: 'nested',
|
|
48
|
+
version: null,
|
|
49
|
+
events: ['PostToolUse', 'Stop'],
|
|
50
|
+
matcherEvents: ['PostToolUse', 'Stop'],
|
|
51
|
+
timeout: 15,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
key: 'cursor',
|
|
55
|
+
label: 'Cursor',
|
|
56
|
+
file: ['.cursor', 'hooks.json'],
|
|
57
|
+
root: 'hooks',
|
|
58
|
+
form: 'flat-command',
|
|
59
|
+
version: 1,
|
|
60
|
+
events: ['postToolUse', 'stop', 'sessionStart'],
|
|
61
|
+
matcherEvents: [],
|
|
62
|
+
timeout: null,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
key: 'copilot',
|
|
66
|
+
label: 'GitHub Copilot',
|
|
67
|
+
file: ['.github', 'hooks', 'vflow-log.json'],
|
|
68
|
+
root: 'hooks',
|
|
69
|
+
form: 'flat-bash',
|
|
70
|
+
version: 1,
|
|
71
|
+
events: ['postToolUse', 'agentStop', 'sessionStart'],
|
|
72
|
+
matcherEvents: [],
|
|
73
|
+
timeout: null,
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
function getProvider(key) {
|
|
78
|
+
return PROVIDERS.find((p) => p.key === key) || null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The single logging command each hook invokes. `bin` defaults to PATH lookup. */
|
|
82
|
+
function hookCommand(provider, event, bin = 'v-flow') {
|
|
83
|
+
return `${bin} log --hook ${provider.key} --event ${event}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Build one event entry in the provider's native shape. */
|
|
87
|
+
function buildEntry(provider, event, bin) {
|
|
88
|
+
const command = hookCommand(provider, event, bin);
|
|
89
|
+
if (provider.form === 'flat-command') {
|
|
90
|
+
return { command };
|
|
91
|
+
}
|
|
92
|
+
if (provider.form === 'flat-bash') {
|
|
93
|
+
return { type: 'command', bash: command };
|
|
94
|
+
}
|
|
95
|
+
// nested (claude / codex / antigravity)
|
|
96
|
+
const inner = { type: 'command', command };
|
|
97
|
+
if (provider.timeout) inner.timeout = provider.timeout;
|
|
98
|
+
const group = { hooks: [inner] };
|
|
99
|
+
if (provider.matcherEvents.includes(event)) group.matcher = '*';
|
|
100
|
+
return group;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Does an existing entry belong to v-flow (so we can replace/remove it)? */
|
|
104
|
+
function entryIsOurs(provider, entry) {
|
|
105
|
+
if (!entry || typeof entry !== 'object') return false;
|
|
106
|
+
const needle = `--hook ${provider.key}`;
|
|
107
|
+
const hasNeedle = (s) => typeof s === 'string' && s.includes(needle);
|
|
108
|
+
if (hasNeedle(entry.command) || hasNeedle(entry.bash)) return true;
|
|
109
|
+
if (Array.isArray(entry.hooks)) {
|
|
110
|
+
return entry.hooks.some((h) => h && (hasNeedle(h.command) || hasNeedle(h.bash)));
|
|
111
|
+
}
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Merge our hooks into an existing parsed config (or {}). Idempotent: replaces
|
|
117
|
+
* any prior v-flow entries, preserves everything else.
|
|
118
|
+
*/
|
|
119
|
+
function mergeConfig(provider, existing, opts = {}) {
|
|
120
|
+
const cfg = existing && typeof existing === 'object' ? existing : {};
|
|
121
|
+
if (provider.version !== null && cfg.version === undefined) {
|
|
122
|
+
cfg.version = provider.version;
|
|
123
|
+
}
|
|
124
|
+
cfg[provider.root] = cfg[provider.root] || {};
|
|
125
|
+
const container = cfg[provider.root];
|
|
126
|
+
for (const event of provider.events) {
|
|
127
|
+
const arr = Array.isArray(container[event]) ? container[event] : [];
|
|
128
|
+
const cleaned = arr.filter((e) => !entryIsOurs(provider, e));
|
|
129
|
+
cleaned.push(buildEntry(provider, event, opts.bin));
|
|
130
|
+
container[event] = cleaned;
|
|
131
|
+
}
|
|
132
|
+
return cfg;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Strip our hooks from an existing parsed config; preserve user hooks. */
|
|
136
|
+
function removeConfig(provider, existing) {
|
|
137
|
+
if (!existing || typeof existing !== 'object') return existing;
|
|
138
|
+
const container = existing[provider.root];
|
|
139
|
+
if (!container || typeof container !== 'object') return existing;
|
|
140
|
+
for (const event of Object.keys(container)) {
|
|
141
|
+
if (Array.isArray(container[event])) {
|
|
142
|
+
container[event] = container[event].filter((e) => !entryIsOurs(provider, e));
|
|
143
|
+
if (container[event].length === 0) delete container[event];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (Object.keys(container).length === 0) delete existing[provider.root];
|
|
147
|
+
return existing;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Is our hook currently present in an existing parsed config? */
|
|
151
|
+
function isInstalled(provider, existing) {
|
|
152
|
+
const container = existing && existing[provider.root];
|
|
153
|
+
if (!container || typeof container !== 'object') return false;
|
|
154
|
+
return provider.events.some(
|
|
155
|
+
(ev) => Array.isArray(container[ev]) && container[ev].some((e) => entryIsOurs(provider, e))
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = {
|
|
160
|
+
PROVIDERS,
|
|
161
|
+
getProvider,
|
|
162
|
+
hookCommand,
|
|
163
|
+
buildEntry,
|
|
164
|
+
mergeConfig,
|
|
165
|
+
removeConfig,
|
|
166
|
+
isInstalled,
|
|
167
|
+
};
|
package/cli/lib/i18n.js
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n â Internationalization for V-Flow CLI
|
|
3
|
+
*
|
|
4
|
+
* Supports: vi (default), en
|
|
5
|
+
* Language selection: --lang flag, VFLOW_LANG env var, or system locale
|
|
6
|
+
*
|
|
7
|
+
* STATUS: Active â All 9 commands use i18n for user-facing strings.
|
|
8
|
+
* Integration: Global --lang flag sets process.env.VFLOW_LANG via cli/index.js preAction hook.
|
|
9
|
+
*
|
|
10
|
+
* Usage in commands:
|
|
11
|
+
* const { createT } = require('../lib/i18n');
|
|
12
|
+
* const t = createT();
|
|
13
|
+
* console.log(t('init.created', { dir: '.v-flow' }));
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const vi = {
|
|
17
|
+
// Common
|
|
18
|
+
'common.success': 'â
Thà nh công',
|
|
19
|
+
'common.error': 'â Láģi',
|
|
20
|
+
'common.warning': 'â ī¸ CáēŖnh bÃĄo',
|
|
21
|
+
'common.done': 'Hoà n táēĨt',
|
|
22
|
+
'common.skipped': 'ÄÃŖ báģ qua',
|
|
23
|
+
'common.not_found': 'Không tÃŦm tháēĨy',
|
|
24
|
+
'common.created': 'ÄÃŖ táēĄo',
|
|
25
|
+
'common.updated': 'ÄÃŖ cáēp nháēt',
|
|
26
|
+
'common.unchanged': 'Không thay Äáģi',
|
|
27
|
+
'common.dry_run': '(cháēŋ Äáģ xem trưáģc â không thay Äáģi gÃŦ)',
|
|
28
|
+
'common.version': 'PhiÃĒn báēŖn',
|
|
29
|
+
'common.summary': 'ââ Táģng káēŋt ââ',
|
|
30
|
+
'common.added': 'Máģi',
|
|
31
|
+
'common.errors': 'Láģi',
|
|
32
|
+
|
|
33
|
+
// Init
|
|
34
|
+
'init.title': 'đ V Flow â Äang kháģi táēĄo...',
|
|
35
|
+
'init.already_exists': 'â ī¸ .v-flow/ ÄÃŖ táģn táēĄi. DÚng --force Äáģ kháģi táēĄo láēĄi.',
|
|
36
|
+
'init.already_exists_hint': ' Dáģ¯ liáģu hiáģn táēĄi sáēŊ không báģ ghi Äè.',
|
|
37
|
+
'init.creating_dirs': 'đ Äang táēĄo cáēĨu trÃēc thư máģĨc...',
|
|
38
|
+
'init.dir_created': 'â
.v-flow/ ÄÃŖ táēĄo',
|
|
39
|
+
'init.constitution_created': 'â
constitution.md ÄÃŖ táēĄo (táģĢ template)',
|
|
40
|
+
'init.constitution_exists': 'âī¸ constitution.md ÄÃŖ táģn táēĄi',
|
|
41
|
+
'init.agent_config_created': 'â
agent-config.md ÄÃŖ táēĄo (táģĢ template)',
|
|
42
|
+
'init.agent_config_exists': 'âī¸ agent-config.md ÄÃŖ táģn táēĄi',
|
|
43
|
+
'init.scanning': 'đ Äang quÊt codebase cho context.md...',
|
|
44
|
+
'init.scan_complete': 'â
context.md ÄÃŖ táēĄo (U.0 auto-scan: {count} files)',
|
|
45
|
+
'init.scan_languages': ' Ngôn ngáģ¯: {languages}',
|
|
46
|
+
'init.scan_frameworks': ' Frameworks: {frameworks}',
|
|
47
|
+
'init.scan_warning': 'â ī¸ Tech Lead cáē§n báģ sung: No-Go Zones, Technical Debt, Architecture',
|
|
48
|
+
'init.scan_failed': 'â ī¸ (quÊt tháēĨt báēĄi, dÚng template)',
|
|
49
|
+
'init.scan_template': 'đ context.md ÄÃŖ táēĄo táģĢ template (Äiáģn tháģ§ công)',
|
|
50
|
+
'init.context_exists': 'âī¸ context.md ÄÃŖ táģn táēĄi',
|
|
51
|
+
'init.knowledge_seeded': 'â
knowledge/ ÄÃŖ táēĄo ({count} files: glossary, patterns, lessons-learned)',
|
|
52
|
+
'init.knowledge_exists': 'âī¸ knowledge/ ÄÃŖ táģn táēĄi',
|
|
53
|
+
'init.syncing': 'đ¤ Äang Äáģng báģ AI agent files...',
|
|
54
|
+
'init.sync_stats': 'đ Äáģng báģ: {added} máģi, {synced} cáēp nháēt, {skipped} không Äáģi',
|
|
55
|
+
'init.complete': 'đ V Flow ÄÃŖ kháģi táēĄo thà nh công!',
|
|
56
|
+
'init.next_steps': 'Bưáģc tiáēŋp theo:',
|
|
57
|
+
'init.review_context': '1. â ī¸ Review .v-flow/context.md â Äiáģn No-Go Zones & Technical Debt',
|
|
58
|
+
'init.edit_constitution': '2. Sáģa .v-flow/constitution.md váģi quy táē¯c dáģą ÃĄn',
|
|
59
|
+
'init.edit_agent_config': '3. Sáģa .v-flow/agent-config.md váģi model preferences',
|
|
60
|
+
'init.import_ba_docs': '4. (TÚy cháģn) CÃŗ tà i liáģu BA trÃĒn NotebookLM? /v.import-ba-docs',
|
|
61
|
+
'init.start_feature': '5. Báē¯t Äáē§u feature: /v.specify',
|
|
62
|
+
'init.sync_hint': '6. Náēŋu spec thay Äáģi: v-flow sync --apply Äáģ giáģ¯ artifacts Äáģng báģ',
|
|
63
|
+
|
|
64
|
+
// Check
|
|
65
|
+
'check.title': 'đ V Flow â Kiáģm tra sáģŠc kháģe',
|
|
66
|
+
'check.vflow_found': 'â
.v-flow/ directory found',
|
|
67
|
+
'check.core_files': 'đ Core Files:',
|
|
68
|
+
'check.knowledge': 'đ Knowledge Base:',
|
|
69
|
+
'check.features': 'đˇī¸ Features: {count} found',
|
|
70
|
+
'check.no_features': 'Chưa cÃŗ feature. TáēĄo: .v-flow/features/001-feature-name/',
|
|
71
|
+
'check.ai_agents': 'đ¤ AI Agent Integration:',
|
|
72
|
+
|
|
73
|
+
// Status
|
|
74
|
+
'status.title': 'đ V Flow â TráēĄng thÃĄi Pipeline',
|
|
75
|
+
'status.no_features': 'Chưa cÃŗ feature.',
|
|
76
|
+
'status.no_features_hint': 'TáēĄo feature: v-flow feature create <tÃĒn>',
|
|
77
|
+
'status.feature_not_found': 'Feature "{name}" không tÃŦm tháēĨy.',
|
|
78
|
+
'status.available_features': 'Features hiáģn cÃŗ: {features}',
|
|
79
|
+
'status.blocked': 'đĢ BLOCKED: {reason}',
|
|
80
|
+
'status.awaiting': 'đ¤ Äang cháģ: {checkpoint}',
|
|
81
|
+
'status.concerns': '⥠Concerns: {count} Äang máģ',
|
|
82
|
+
'status.next': 'âļ Tiáēŋp theo:',
|
|
83
|
+
'status.all_complete': 'đ TáēĨt cáēŖ phases hoà n thà nh!',
|
|
84
|
+
|
|
85
|
+
// Sync
|
|
86
|
+
'sync.title': 'đ V Flow â Äáģng báģ',
|
|
87
|
+
'sync.dry_run_note': ' âšī¸ Cháēŋ Äáģ xem trưáģc: không thay Äáģi file nà o.',
|
|
88
|
+
'sync.dry_run_hint': ' ThÃĒm --apply Äáģ tháģąc thi.',
|
|
89
|
+
'sync.commands': 'ââ Commands ââ',
|
|
90
|
+
'sync.agents': 'ââ Agents ââ',
|
|
91
|
+
'sync.skills': 'ââ Skills ââ',
|
|
92
|
+
'sync.global_files': 'ââ Global Files ââ',
|
|
93
|
+
'sync.module_templates': 'ââ Module Card Templates ââ',
|
|
94
|
+
'sync.skills_synced': 'â
Skills directory synced',
|
|
95
|
+
'sync.mc_synced': 'â
Module card templates synced',
|
|
96
|
+
'sync.mc_skipped': 'âī¸ Báģ qua â Äang cháēĄy táģĢ source root (templates/ ÄÃŖ cÃŗ sáēĩn)',
|
|
97
|
+
'sync.summary': 'ââ Táģng káēŋt{mode} ââ',
|
|
98
|
+
'sync.added': ' ⨠Máģi: {count}',
|
|
99
|
+
'sync.updated': ' đ Cáēp nháēt: {count}',
|
|
100
|
+
'sync.unchanged': ' âī¸ Không Äáģi: {count}',
|
|
101
|
+
'sync.errors': ' â Láģi: {count}',
|
|
102
|
+
'sync.apply_hint': ' âšī¸ CháēĄy {cmd} Äáģ tháģąc thi nháģ¯ng thay Äáģi nà y.',
|
|
103
|
+
'sync.all_synced': ' â
TáēĨt cáēŖ files ÄÃŖ Äáģng báģ.',
|
|
104
|
+
'sync.all_uptodate': ' â
TáēĨt cáēŖ files ÄÃŖ cáēp nháēt.',
|
|
105
|
+
|
|
106
|
+
// Doctor
|
|
107
|
+
'doctor.title': 'đŠē V Flow â Doctor',
|
|
108
|
+
'doctor.core_structure': 'đ Core Structure',
|
|
109
|
+
'doctor.core_files': 'đ Core Files',
|
|
110
|
+
'doctor.ai_sync': 'đ¤ AI Agent Sync Status',
|
|
111
|
+
'doctor.feature_health': 'đˇī¸ Feature Health ({count} features)',
|
|
112
|
+
'doctor.templates': 'đ Templates',
|
|
113
|
+
'doctor.diagnosis': 'ââ CháēŠn ÄoÃĄn ââ',
|
|
114
|
+
'doctor.health': 'SáģŠc kháģe',
|
|
115
|
+
'doctor.errors': 'â {count} láģi â cáē§n sáģa:',
|
|
116
|
+
'doctor.warnings': 'â ī¸ {count} cáēŖnh bÃĄo â nÃĒn sáģa:',
|
|
117
|
+
'doctor.passed': 'â
{count} kiáģm tra ÄÃŖ qua',
|
|
118
|
+
'doctor.recommendations': 'đĄ Khuyáēŋn ngháģ:',
|
|
119
|
+
'doctor.run_init': 'âĸ CháēĄy: v-flow init',
|
|
120
|
+
'doctor.run_sync': 'âĸ CháēĄy: v-flow sync --apply',
|
|
121
|
+
'doctor.review_context': 'âĸ Review: .v-flow/context.md â Äiáģn cÃĄc máģĨc Tech Lead',
|
|
122
|
+
'doctor.run_feature': 'âĸ CháēĄy: v-flow feature create <tÃĒn> (Äáģ scaffolding ÄÃēng)',
|
|
123
|
+
|
|
124
|
+
// Validate
|
|
125
|
+
'validate.title': 'đ V Flow â Kiáģm tra cháēĨt lưáģŖng',
|
|
126
|
+
'validate.no_features': 'Chưa cÃŗ feature. TáēĄo báēąng: v-flow feature create <tÃĒn>',
|
|
127
|
+
'validate.summary': 'ââ Táģng káēŋt ââ',
|
|
128
|
+
'validate.quality_score': 'đ Äiáģm cháēĨt lưáģŖng: {score}%',
|
|
129
|
+
|
|
130
|
+
// Plugin
|
|
131
|
+
'plugin.title': 'đ V Flow â Plugins',
|
|
132
|
+
'plugin.no_plugins': 'Chưa cÃŗ plugin.',
|
|
133
|
+
'plugin.create_hint': 'TáēĄo plugin: v-flow plugin create <tÃĒn>',
|
|
134
|
+
'plugin.created': 'Plugin ÄÃŖ táēĄo: .v-flow/plugins/{name}/',
|
|
135
|
+
'plugin.installed': 'â
ÄÃŖ cà i {count} asset(s) táģĢ {plugins} plugin(s)',
|
|
136
|
+
'plugin.create_title': 'đ V Flow â TáēĄo Plugin',
|
|
137
|
+
'plugin.install_title': 'đ V Flow â CÃ i Plugin',
|
|
138
|
+
'plugin.validate_title': 'đ V Flow â Validate Plugins',
|
|
139
|
+
'plugin.all_valid': 'â
TáēĨt cáēŖ {count} plugin(s) háģŖp láģ',
|
|
140
|
+
'plugin.dry_run': 'đ Dry run â {count} asset(s) sáēŊ ÄÆ°áģŖc cà i',
|
|
141
|
+
|
|
142
|
+
// Feature
|
|
143
|
+
'feature.title': 'đˇī¸ Feature',
|
|
144
|
+
'feature.created': 'Feature ÄÃŖ táēĄo: {name}',
|
|
145
|
+
'feature.no_features': 'Chưa cÃŗ feature.',
|
|
146
|
+
|
|
147
|
+
// Upgrade
|
|
148
|
+
'upgrade.title': 'âŦī¸ V Flow â NÃĸng cáēĨp{mode}',
|
|
149
|
+
'upgrade.source_version': ' PhiÃĒn báēŖn nguáģn: {version}',
|
|
150
|
+
'upgrade.installed_version': ' PhiÃĒn báēŖn ÄÃŖ cà i: {version}',
|
|
151
|
+
'upgrade.up_to_date': 'â
ÄÃŖ cáēp nháēt!',
|
|
152
|
+
'upgrade.templates': 'đ Templates',
|
|
153
|
+
'upgrade.agents': 'đ¤ Agent Definitions & Commands',
|
|
154
|
+
'upgrade.agents_hint': ' (Äáģng báģ táģi .agents/, .github/, .cursor/)',
|
|
155
|
+
'upgrade.synced': 'â
ÄÃŖ Äáģng báģ: {added} máģi, {synced} cáēp nháēt, {skipped} không Äáģi',
|
|
156
|
+
'upgrade.sync_errors': 'â ī¸ {count} láģi Äáģng báģ',
|
|
157
|
+
'upgrade.upgraded': 'â
ÄÃŖ nÃĸng cáēĨp lÃĒn v{version}',
|
|
158
|
+
'upgrade.verify_hint': 'â CháēĄy: v-flow doctor Äáģ kiáģm tra',
|
|
159
|
+
'upgrade.apply_hint': 'âšī¸ CháēĄy láēĄi không cÃŗ --dry-run Äáģ ÃĄp dáģĨng.',
|
|
160
|
+
'upgrade.templates_uptodate': 'â
TáēĨt cáēŖ templates ÄÃŖ cáēp nháēt.',
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const en = {
|
|
164
|
+
// Common
|
|
165
|
+
'common.success': 'â
Success',
|
|
166
|
+
'common.error': 'â Error',
|
|
167
|
+
'common.warning': 'â ī¸ Warning',
|
|
168
|
+
'common.done': 'Done',
|
|
169
|
+
'common.skipped': 'Skipped',
|
|
170
|
+
'common.not_found': 'Not found',
|
|
171
|
+
'common.created': 'Created',
|
|
172
|
+
'common.updated': 'Updated',
|
|
173
|
+
'common.unchanged': 'Unchanged',
|
|
174
|
+
'common.dry_run': '(preview mode â no changes made)',
|
|
175
|
+
'common.version': 'Version',
|
|
176
|
+
'common.summary': 'ââ Summary ââ',
|
|
177
|
+
'common.added': 'Added',
|
|
178
|
+
'common.errors': 'Errors',
|
|
179
|
+
|
|
180
|
+
// Init
|
|
181
|
+
'init.title': 'đ V Flow â Initializing...',
|
|
182
|
+
'init.already_exists': 'â ī¸ .v-flow/ already exists. Use --force to reinitialize.',
|
|
183
|
+
'init.already_exists_hint': ' Existing data will not be overwritten.',
|
|
184
|
+
'init.creating_dirs': 'đ Creating directory structure...',
|
|
185
|
+
'init.dir_created': 'â
.v-flow/ directory created',
|
|
186
|
+
'init.constitution_created': 'â
constitution.md created (from template)',
|
|
187
|
+
'init.constitution_exists': 'âī¸ constitution.md already exists',
|
|
188
|
+
'init.agent_config_created': 'â
agent-config.md created (from template)',
|
|
189
|
+
'init.agent_config_exists': 'âī¸ agent-config.md already exists',
|
|
190
|
+
'init.scanning': 'đ Scanning codebase for context.md...',
|
|
191
|
+
'init.scan_complete': 'â
context.md created (U.0 auto-scan: {count} files)',
|
|
192
|
+
'init.scan_languages': ' Languages: {languages}',
|
|
193
|
+
'init.scan_frameworks': ' Frameworks: {frameworks}',
|
|
194
|
+
'init.scan_warning': 'â ī¸ Tech Lead should fill in: No-Go Zones, Technical Debt, Architecture',
|
|
195
|
+
'init.scan_failed': 'â ī¸ (scan failed, using template)',
|
|
196
|
+
'init.scan_template': 'đ context.md created from template (fill in manually)',
|
|
197
|
+
'init.context_exists': 'âī¸ context.md already exists',
|
|
198
|
+
'init.knowledge_seeded': 'â
knowledge/ seeded ({count} files: glossary, patterns, lessons-learned)',
|
|
199
|
+
'init.knowledge_exists': 'âī¸ knowledge/ already exists',
|
|
200
|
+
'init.syncing': 'đ¤ Syncing AI agent integration files...',
|
|
201
|
+
'init.sync_stats': 'đ Sync stats: {added} new, {synced} updated, {skipped} unchanged',
|
|
202
|
+
'init.complete': 'đ V Flow initialized successfully!',
|
|
203
|
+
'init.next_steps': 'Next steps:',
|
|
204
|
+
'init.review_context': '1. â ī¸ Review .v-flow/context.md â fill in No-Go Zones & Technical Debt',
|
|
205
|
+
'init.edit_constitution': '2. Edit .v-flow/constitution.md with your project rules',
|
|
206
|
+
'init.edit_agent_config': '3. Edit .v-flow/agent-config.md with model preferences',
|
|
207
|
+
'init.import_ba_docs': '4. (Optional) BA docs on NotebookLM? /v.import-ba-docs',
|
|
208
|
+
'init.start_feature': '5. Start a feature: /v.specify',
|
|
209
|
+
'init.sync_hint': '6. If spec changes later: v-flow sync --apply to keep artifacts in sync',
|
|
210
|
+
|
|
211
|
+
// Check
|
|
212
|
+
'check.title': 'đ V Flow â Health Check',
|
|
213
|
+
'check.vflow_found': 'â
.v-flow/ directory found',
|
|
214
|
+
'check.core_files': 'đ Core Files:',
|
|
215
|
+
'check.knowledge': 'đ Knowledge Base:',
|
|
216
|
+
'check.features': 'đˇī¸ Features: {count} found',
|
|
217
|
+
'check.no_features': 'No features yet. Create: .v-flow/features/001-feature-name/',
|
|
218
|
+
'check.ai_agents': 'đ¤ AI Agent Integration:',
|
|
219
|
+
|
|
220
|
+
// Status
|
|
221
|
+
'status.title': 'đ V Flow â Pipeline Status',
|
|
222
|
+
'status.no_features': 'No features found.',
|
|
223
|
+
'status.no_features_hint': 'Create a feature: v-flow feature create <name>',
|
|
224
|
+
'status.feature_not_found': 'Feature "{name}" not found.',
|
|
225
|
+
'status.available_features': 'Available features: {features}',
|
|
226
|
+
'status.blocked': 'đĢ BLOCKED: {reason}',
|
|
227
|
+
'status.awaiting': 'đ¤ Awaiting: {checkpoint}',
|
|
228
|
+
'status.concerns': '⥠Concerns: {count} active',
|
|
229
|
+
'status.next': 'âļ Next:',
|
|
230
|
+
'status.all_complete': 'đ All phases complete!',
|
|
231
|
+
|
|
232
|
+
// Sync
|
|
233
|
+
'sync.title': 'đ V Flow â Sync',
|
|
234
|
+
'sync.dry_run_note': ' âšī¸ Dry-run mode: no files will be changed.',
|
|
235
|
+
'sync.dry_run_hint': ' Add --apply to execute changes.',
|
|
236
|
+
'sync.commands': 'ââ Commands ââ',
|
|
237
|
+
'sync.agents': 'ââ Agents ââ',
|
|
238
|
+
'sync.skills': 'ââ Skills ââ',
|
|
239
|
+
'sync.global_files': 'ââ Global Files ââ',
|
|
240
|
+
'sync.module_templates': 'ââ Module Card Templates ââ',
|
|
241
|
+
'sync.skills_synced': 'â
Skills directory synced',
|
|
242
|
+
'sync.mc_synced': 'â
Module card templates synced',
|
|
243
|
+
'sync.mc_skipped': 'âī¸ Skipped â running from source root (templates/ already exists)',
|
|
244
|
+
'sync.summary': 'ââ Summary{mode} ââ',
|
|
245
|
+
'sync.added': ' ⨠Added: {count}',
|
|
246
|
+
'sync.updated': ' đ Updated: {count}',
|
|
247
|
+
'sync.unchanged': ' âī¸ Unchanged: {count}',
|
|
248
|
+
'sync.errors': ' â Errors: {count}',
|
|
249
|
+
'sync.apply_hint': ' âšī¸ Run {cmd} to execute these changes.',
|
|
250
|
+
'sync.all_synced': ' â
All files synced successfully.',
|
|
251
|
+
'sync.all_uptodate': ' â
All files are up-to-date.',
|
|
252
|
+
|
|
253
|
+
// Doctor
|
|
254
|
+
'doctor.title': 'đŠē V Flow â Doctor',
|
|
255
|
+
'doctor.core_structure': 'đ Core Structure',
|
|
256
|
+
'doctor.core_files': 'đ Core Files',
|
|
257
|
+
'doctor.ai_sync': 'đ¤ AI Agent Sync Status',
|
|
258
|
+
'doctor.feature_health': 'đˇī¸ Feature Health ({count} features)',
|
|
259
|
+
'doctor.templates': 'đ Templates',
|
|
260
|
+
'doctor.diagnosis': 'ââ Diagnosis ââ',
|
|
261
|
+
'doctor.health': 'Health',
|
|
262
|
+
'doctor.errors': 'â {count} error(s) â must fix:',
|
|
263
|
+
'doctor.warnings': 'â ī¸ {count} warning(s) â should fix:',
|
|
264
|
+
'doctor.passed': 'â
{count} check(s) passed',
|
|
265
|
+
'doctor.recommendations': 'đĄ Recommendations:',
|
|
266
|
+
'doctor.run_init': 'âĸ Run: v-flow init',
|
|
267
|
+
'doctor.run_sync': 'âĸ Run: v-flow sync --apply',
|
|
268
|
+
'doctor.review_context': 'âĸ Review: .v-flow/context.md â fill in Tech Lead sections',
|
|
269
|
+
'doctor.run_feature': 'âĸ Run: v-flow feature create <name> (for proper scaffolding)',
|
|
270
|
+
|
|
271
|
+
// Validate
|
|
272
|
+
'validate.title': 'đ V Flow â Quality Validation',
|
|
273
|
+
'validate.no_features': 'No features found. Create one with: v-flow feature create <name>',
|
|
274
|
+
'validate.summary': 'ââ Summary ââ',
|
|
275
|
+
'validate.quality_score': 'đ Quality score: {score}%',
|
|
276
|
+
|
|
277
|
+
// Plugin
|
|
278
|
+
'plugin.title': 'đ V Flow â Plugins',
|
|
279
|
+
'plugin.no_plugins': 'No plugins found.',
|
|
280
|
+
'plugin.create_hint': 'Create one with: v-flow plugin create <name>',
|
|
281
|
+
'plugin.created': 'Plugin created: .v-flow/plugins/{name}/',
|
|
282
|
+
'plugin.installed': 'â
Installed {count} asset(s) from {plugins} plugin(s)',
|
|
283
|
+
'plugin.create_title': 'đ V Flow â Create Plugin',
|
|
284
|
+
'plugin.install_title': 'đ V Flow â Install Plugins',
|
|
285
|
+
'plugin.validate_title': 'đ V Flow â Validate Plugins',
|
|
286
|
+
'plugin.all_valid': 'â
All {count} plugin(s) valid',
|
|
287
|
+
'plugin.dry_run': 'đ Dry run â {count} asset(s) would be installed',
|
|
288
|
+
|
|
289
|
+
// Feature
|
|
290
|
+
'feature.title': 'đˇī¸ Feature',
|
|
291
|
+
'feature.created': 'Feature created: {name}',
|
|
292
|
+
'feature.no_features': 'No features yet.',
|
|
293
|
+
|
|
294
|
+
// Upgrade
|
|
295
|
+
'upgrade.title': 'âŦī¸ V Flow â Upgrade{mode}',
|
|
296
|
+
'upgrade.source_version': ' Source version: {version}',
|
|
297
|
+
'upgrade.installed_version': ' Installed version: {version}',
|
|
298
|
+
'upgrade.up_to_date': 'â
Already up-to-date!',
|
|
299
|
+
'upgrade.templates': 'đ Templates',
|
|
300
|
+
'upgrade.agents': 'đ¤ Agent Definitions & Commands',
|
|
301
|
+
'upgrade.agents_hint': ' (Syncing to .agents/, .github/, .cursor/)',
|
|
302
|
+
'upgrade.synced': 'â
Synced: {added} new, {synced} updated, {skipped} unchanged',
|
|
303
|
+
'upgrade.sync_errors': 'â ī¸ {count} sync error(s)',
|
|
304
|
+
'upgrade.upgraded': 'â
Upgraded to v{version}',
|
|
305
|
+
'upgrade.verify_hint': 'â Run: v-flow doctor to verify integrity',
|
|
306
|
+
'upgrade.apply_hint': 'âšī¸ Run without --dry-run to apply changes.',
|
|
307
|
+
'upgrade.templates_uptodate': 'â
All templates are up-to-date.',
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const locales = { vi, en };
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Detect language from environment
|
|
314
|
+
* Priority: --lang flag > VFLOW_LANG env > system locale > default (vi)
|
|
315
|
+
*/
|
|
316
|
+
function detectLanguage() {
|
|
317
|
+
// Check process args for --lang
|
|
318
|
+
const langArgIndex = process.argv.indexOf('--lang');
|
|
319
|
+
if (langArgIndex !== -1 && process.argv[langArgIndex + 1]) {
|
|
320
|
+
const lang = process.argv[langArgIndex + 1].toLowerCase();
|
|
321
|
+
if (locales[lang]) return lang;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Check environment variable
|
|
325
|
+
if (process.env.VFLOW_LANG) {
|
|
326
|
+
const lang = process.env.VFLOW_LANG.toLowerCase();
|
|
327
|
+
if (locales[lang]) return lang;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Check system locale
|
|
331
|
+
const sysLang = (process.env.LANG || process.env.LC_ALL || '').toLowerCase();
|
|
332
|
+
if (sysLang.startsWith('en')) return 'en';
|
|
333
|
+
|
|
334
|
+
return 'vi'; // Default
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Get translation function for current language
|
|
339
|
+
*
|
|
340
|
+
* @param {string} [lang] - Language override
|
|
341
|
+
* @returns {function(key: string, vars?: object): string}
|
|
342
|
+
*/
|
|
343
|
+
function createT(lang) {
|
|
344
|
+
const currentLang = lang || detectLanguage();
|
|
345
|
+
const messages = locales[currentLang] || locales.vi;
|
|
346
|
+
const fallback = locales.vi;
|
|
347
|
+
|
|
348
|
+
return function t(key, vars = {}) {
|
|
349
|
+
let msg = messages[key] || fallback[key] || key;
|
|
350
|
+
for (const [k, v] of Object.entries(vars)) {
|
|
351
|
+
msg = msg.replace(new RegExp(`\\{${k}\\}`, 'g'), String(v));
|
|
352
|
+
}
|
|
353
|
+
return msg;
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Get number of translation keys available
|
|
359
|
+
* @returns {{ vi: number, en: number }}
|
|
360
|
+
*/
|
|
361
|
+
function getKeyCount() {
|
|
362
|
+
return {
|
|
363
|
+
vi: Object.keys(vi).length,
|
|
364
|
+
en: Object.keys(en).length,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
module.exports = {
|
|
369
|
+
locales,
|
|
370
|
+
detectLanguage,
|
|
371
|
+
createT,
|
|
372
|
+
getKeyCount,
|
|
373
|
+
vi,
|
|
374
|
+
en,
|
|
375
|
+
};
|