@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,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Core — Shared sync logic for CLI
|
|
3
|
+
*
|
|
4
|
+
* Used by both `v-flow sync` and `v-flow init` commands.
|
|
5
|
+
* Single source of truth for file synchronization.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs-extra');
|
|
10
|
+
const crypto = require('crypto');
|
|
11
|
+
const { TARGETS, convertToMdc, convertAgentToMdc } = require('./sync-targets');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Compute SHA256 hash of content (first 12 chars)
|
|
15
|
+
*/
|
|
16
|
+
function hashContent(content) {
|
|
17
|
+
return crypto.createHash('sha256').update(content).digest('hex').slice(0, 12);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Sync a single file from source to destination
|
|
22
|
+
*
|
|
23
|
+
* @param {string} srcPath - Source file path
|
|
24
|
+
* @param {string} destPath - Destination file path
|
|
25
|
+
* @param {object} options - { dryRun, verbose, transform, stats }
|
|
26
|
+
* @returns {'added'|'updated'|'unchanged'}
|
|
27
|
+
*/
|
|
28
|
+
async function syncFile(srcPath, destPath, options = {}) {
|
|
29
|
+
const { dryRun = false, verbose = false, transform = null, stats = null } = options;
|
|
30
|
+
|
|
31
|
+
const srcContent = await fs.readFile(srcPath, 'utf8');
|
|
32
|
+
const destContent = transform ? transform(srcContent, path.basename(srcPath)) : srcContent;
|
|
33
|
+
|
|
34
|
+
if (fs.existsSync(destPath)) {
|
|
35
|
+
const existingContent = await fs.readFile(destPath, 'utf8');
|
|
36
|
+
const srcHash = hashContent(destContent);
|
|
37
|
+
const destHash = hashContent(existingContent);
|
|
38
|
+
|
|
39
|
+
if (srcHash === destHash) {
|
|
40
|
+
if (stats) stats.skipped++;
|
|
41
|
+
return 'unchanged';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (stats) stats.synced++;
|
|
45
|
+
if (!dryRun) {
|
|
46
|
+
await fs.writeFile(destPath, destContent, 'utf8');
|
|
47
|
+
}
|
|
48
|
+
return 'updated';
|
|
49
|
+
}
|
|
50
|
+
if (stats) stats.added++;
|
|
51
|
+
if (!dryRun) {
|
|
52
|
+
await fs.ensureDir(path.dirname(destPath));
|
|
53
|
+
await fs.writeFile(destPath, destContent, 'utf8');
|
|
54
|
+
}
|
|
55
|
+
return 'added';
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Sync all .md files from a source directory to a destination directory
|
|
61
|
+
*
|
|
62
|
+
* @param {string} srcDir - Source directory
|
|
63
|
+
* @param {string} destDir - Destination directory
|
|
64
|
+
* @param {object} options - { dryRun, verbose, transform, ext, stats }
|
|
65
|
+
* @returns {{ added: number, updated: number, unchanged: number }}
|
|
66
|
+
*/
|
|
67
|
+
async function syncDir(srcDir, destDir, options = {}) {
|
|
68
|
+
const { dryRun = false, verbose = false, transform = null, ext = '.md', stats = null } = options;
|
|
69
|
+
|
|
70
|
+
if (!fs.existsSync(srcDir)) return { added: 0, updated: 0, unchanged: 0 };
|
|
71
|
+
|
|
72
|
+
if (!dryRun) await fs.ensureDir(destDir);
|
|
73
|
+
|
|
74
|
+
const files = await fs.readdir(srcDir);
|
|
75
|
+
const targetFiles = files.filter(f => f.endsWith('.md'));
|
|
76
|
+
|
|
77
|
+
const result = { added: 0, updated: 0, unchanged: 0 };
|
|
78
|
+
|
|
79
|
+
for (const file of targetFiles) {
|
|
80
|
+
const srcPath = path.join(srcDir, file);
|
|
81
|
+
const destFileName = ext === '.mdc' ? file.replace(/\.md$/, '.mdc') : file;
|
|
82
|
+
const destPath = path.join(destDir, destFileName);
|
|
83
|
+
|
|
84
|
+
// Explicit transform wins; else `.mdc` implies the Cursor command transform.
|
|
85
|
+
const fileTransform = transform || (ext === '.mdc' ? convertToMdc : null);
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const status = await syncFile(srcPath, destPath, { dryRun, verbose, transform: fileTransform, stats });
|
|
89
|
+
result[status]++;
|
|
90
|
+
} catch (err) {
|
|
91
|
+
if (stats) stats.errors = (stats.errors || 0) + 1;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Copy framework RUNTIME ASSETS (templates + skills) into .v-flow/.
|
|
100
|
+
* Canonical locations agents reference: `.v-flow/templates/X` and
|
|
101
|
+
* `.v-flow/skills/.../scripts/*.sh`. Populated in EVERY project — INCLUDING the framework
|
|
102
|
+
* repo itself — so those paths resolve everywhere. Independent of AI-tool file sync, so it
|
|
103
|
+
* runs even with `init --skip-ai` (these are framework runtime, not AI integration files).
|
|
104
|
+
*
|
|
105
|
+
* @returns {{ templates: number, skills: number }} source-side file counts
|
|
106
|
+
*/
|
|
107
|
+
async function syncVFlowAssets({ projectRoot, sourceRoot, dryRun = false }) {
|
|
108
|
+
const vFlowDir = path.join(projectRoot, '.v-flow');
|
|
109
|
+
const out = { templates: 0, skills: 0 };
|
|
110
|
+
|
|
111
|
+
const countFiles = async (dir) => {
|
|
112
|
+
if (!fs.existsSync(dir)) return 0;
|
|
113
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
114
|
+
let n = 0;
|
|
115
|
+
for (const e of entries) {
|
|
116
|
+
if (e.isFile()) n++;
|
|
117
|
+
else if (e.isDirectory()) n += await countFiles(path.join(dir, e.name));
|
|
118
|
+
}
|
|
119
|
+
return n;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
for (const name of ['templates', 'skills']) {
|
|
123
|
+
const src = path.join(sourceRoot, name);
|
|
124
|
+
if (!fs.existsSync(src)) continue;
|
|
125
|
+
out[name] = await countFiles(src);
|
|
126
|
+
if (!dryRun) {
|
|
127
|
+
const dest = path.join(vFlowDir, name);
|
|
128
|
+
await fs.ensureDir(dest);
|
|
129
|
+
await fs.copy(src, dest, { overwrite: true });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return out;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Select target descriptors from the registry for a given selector.
|
|
137
|
+
*
|
|
138
|
+
* @param {string} target - 'all' (or falsy) → every target; else match by key
|
|
139
|
+
* @returns {Array<object>} matching entries from TARGETS
|
|
140
|
+
*/
|
|
141
|
+
function selectTargets(target) {
|
|
142
|
+
return TARGETS.filter((t) => !target || target === 'all' || t.key === target);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Run full sync operation — sync commands, agents, skills, and global files
|
|
147
|
+
* for every selected target. Target specifics live in the config-driven
|
|
148
|
+
* registry (`sync-targets.js`); this function only walks it.
|
|
149
|
+
*
|
|
150
|
+
* @param {object} config
|
|
151
|
+
* @param {string} config.projectRoot - Target project root
|
|
152
|
+
* @param {string} config.sourceRoot - ADLC source root
|
|
153
|
+
* @param {boolean} [config.dryRun=false] - Dry run mode
|
|
154
|
+
* @param {boolean} [config.verbose=false] - Verbose output
|
|
155
|
+
* @param {string} [config.target='all'] - 'all' or a target key (antigravity, copilot, cursor, claude)
|
|
156
|
+
* @returns {{ added: number, synced: number, skipped: number, errors: number }}
|
|
157
|
+
*/
|
|
158
|
+
async function runSync(config) {
|
|
159
|
+
const {
|
|
160
|
+
projectRoot,
|
|
161
|
+
sourceRoot,
|
|
162
|
+
dryRun = false,
|
|
163
|
+
verbose = false,
|
|
164
|
+
target = 'all',
|
|
165
|
+
} = config;
|
|
166
|
+
|
|
167
|
+
const stats = { synced: 0, skipped: 0, added: 0, errors: 0 };
|
|
168
|
+
const syncOpts = { dryRun, verbose, stats };
|
|
169
|
+
|
|
170
|
+
// Source directories — vendor-neutral single source of truth.
|
|
171
|
+
const COMMANDS_DIR = path.join(sourceRoot, 'commands');
|
|
172
|
+
const AGENTS_DIR = path.join(sourceRoot, 'agents');
|
|
173
|
+
const SKILLS_DIR = path.join(sourceRoot, 'skills');
|
|
174
|
+
|
|
175
|
+
for (const t of selectTargets(target)) {
|
|
176
|
+
const dest = (segs) => path.join(projectRoot, ...segs);
|
|
177
|
+
|
|
178
|
+
// ── Commands ──
|
|
179
|
+
await syncDir(COMMANDS_DIR, dest(t.commands.dir), {
|
|
180
|
+
...syncOpts,
|
|
181
|
+
ext: t.commands.ext || '.md',
|
|
182
|
+
transform: t.commands.transform || null,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// ── Agents ──
|
|
186
|
+
await syncDir(AGENTS_DIR, dest(t.agents.dir), {
|
|
187
|
+
...syncOpts,
|
|
188
|
+
ext: t.agents.ext || '.md',
|
|
189
|
+
transform: t.agents.transform || null,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// ── Skills (bulk copy — preserves nested SKILL.md structure) ──
|
|
193
|
+
if (fs.existsSync(SKILLS_DIR) && !dryRun) {
|
|
194
|
+
await fs.copy(SKILLS_DIR, dest(t.skills.dir), { overwrite: true });
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ── Global instruction file(s) ──
|
|
198
|
+
for (const g of t.globals || []) {
|
|
199
|
+
const srcPath = path.join(sourceRoot, ...g.src);
|
|
200
|
+
if (!fs.existsSync(srcPath)) continue;
|
|
201
|
+
const destPath = dest(g.dest);
|
|
202
|
+
// noClobber: never overwrite a hand-authored file (e.g. CLAUDE.md).
|
|
203
|
+
if (g.noClobber && fs.existsSync(destPath)) {
|
|
204
|
+
stats.skipped++;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
await syncFile(srcPath, destPath, syncOpts);
|
|
209
|
+
} catch (err) {
|
|
210
|
+
stats.errors++;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ── SYNC RUNTIME ASSETS (templates + skills) → .v-flow/ ──
|
|
216
|
+
// Extracted to syncVFlowAssets so `init --skip-ai` and `v-flow sync` install them too.
|
|
217
|
+
await syncVFlowAssets({ projectRoot, sourceRoot, dryRun });
|
|
218
|
+
|
|
219
|
+
return stats;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = {
|
|
223
|
+
hashContent,
|
|
224
|
+
convertToMdc,
|
|
225
|
+
convertAgentToMdc,
|
|
226
|
+
syncFile,
|
|
227
|
+
syncDir,
|
|
228
|
+
runSync,
|
|
229
|
+
syncVFlowAssets,
|
|
230
|
+
selectTargets,
|
|
231
|
+
TARGETS,
|
|
232
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Targets — Config-driven registry of supported AI tools.
|
|
3
|
+
*
|
|
4
|
+
* V Flow's single source of truth is vendor-neutral markdown (`commands/`,
|
|
5
|
+
* `agents/`, `skills/`, `AGENTS.md`). Each entry below declares HOW that
|
|
6
|
+
* canonical form maps into one AI tool's conventional folders.
|
|
7
|
+
*
|
|
8
|
+
* ➕ Adding a new vendor = add ONE entry here. No changes to sync-core.js.
|
|
9
|
+
*
|
|
10
|
+
* Target shape:
|
|
11
|
+
* {
|
|
12
|
+
* key: string // CLI selector (`--target <key>`)
|
|
13
|
+
* label: string // human-readable name for output
|
|
14
|
+
* commands: { dir: string[], ext?, transform? } // slash commands
|
|
15
|
+
* agents: { dir: string[], ext?, transform? } // agent personas
|
|
16
|
+
* skills: { dir: string[] } // bulk-copied
|
|
17
|
+
* globals: [{ src: string[], dest: string[], noClobber? }] // instruction file(s)
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* `dir`/`src`/`dest` are path segments joined under projectRoot (or sourceRoot
|
|
21
|
+
* for `src`). `ext: '.mdc'` renames `.md` → `.mdc` on write. `transform` is a
|
|
22
|
+
* (content, fileName) => string applied per file. `noClobber: true` means
|
|
23
|
+
* never overwrite an existing destination (for hand-authored files).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Convert .md content to .mdc format for Cursor (inject `globs: *`).
|
|
28
|
+
*/
|
|
29
|
+
function convertToMdc(content, fileName) {
|
|
30
|
+
if (content.startsWith('---')) {
|
|
31
|
+
return content.replace(/^---\r?\n/, '---\nglobs: *\n');
|
|
32
|
+
}
|
|
33
|
+
const cmdName = fileName.replace(/\.md$/, '');
|
|
34
|
+
return `---\ndescription: Execute /${cmdName}\nglobs: *\n---\n${content}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Convert agent .md to .mdc for Cursor with persona frontmatter.
|
|
39
|
+
*/
|
|
40
|
+
function convertAgentToMdc(content, fileName) {
|
|
41
|
+
const agentName = fileName.replace(/\.md$/, '');
|
|
42
|
+
return `---\ndescription: Agent persona - ${agentName}\nglobs: *\nalwaysApply: false\n---\n${content}`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const TARGETS = [
|
|
46
|
+
{
|
|
47
|
+
key: 'antigravity',
|
|
48
|
+
label: 'Antigravity',
|
|
49
|
+
commands: { dir: ['.agents', 'workflows'] },
|
|
50
|
+
agents: { dir: ['.agents', 'agents'] },
|
|
51
|
+
skills: { dir: ['.agents', 'skills'] },
|
|
52
|
+
globals: [{ src: ['AGENTS.md'], dest: ['AGENTS.md'] }],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
key: 'copilot',
|
|
56
|
+
label: 'GitHub Copilot',
|
|
57
|
+
commands: { dir: ['.github', 'prompts'] },
|
|
58
|
+
agents: { dir: ['.github', 'agents'] },
|
|
59
|
+
skills: { dir: ['.github', 'skills'] },
|
|
60
|
+
globals: [
|
|
61
|
+
{ src: ['.github', 'copilot-instructions.md'], dest: ['.github', 'copilot-instructions.md'] },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
key: 'cursor',
|
|
66
|
+
label: 'Cursor',
|
|
67
|
+
commands: { dir: ['.cursor', 'rules'], ext: '.mdc', transform: convertToMdc },
|
|
68
|
+
agents: { dir: ['.cursor', 'agents'], ext: '.mdc', transform: convertAgentToMdc },
|
|
69
|
+
skills: { dir: ['.cursor', 'skills'] },
|
|
70
|
+
globals: [{ src: ['AGENTS.md'], dest: ['.cursorrules'] }],
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
key: 'claude',
|
|
74
|
+
label: 'Claude Code',
|
|
75
|
+
commands: { dir: ['.claude', 'commands'] },
|
|
76
|
+
agents: { dir: ['.claude', 'agents'] },
|
|
77
|
+
skills: { dir: ['.claude', 'skills'] },
|
|
78
|
+
// CLAUDE.md is Claude Code's primary memory file and is very often
|
|
79
|
+
// hand-authored — so never clobber it. Created only when absent.
|
|
80
|
+
globals: [{ src: ['AGENTS.md'], dest: ['CLAUDE.md'], noClobber: true }],
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
module.exports = { TARGETS, convertToMdc, convertAgentToMdc };
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
|
|
4
|
+
const MASTER_FLOW_DIR = '.v-flow';
|
|
5
|
+
|
|
6
|
+
// ============================================================
|
|
7
|
+
// CODEBASE SCANNER — Delegated to scanner.js (Phase 3 refactor)
|
|
8
|
+
// ============================================================
|
|
9
|
+
const { scanCodebase, generateContextMd } = require('./scanner');
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
const COMMANDS_SOURCE = path.resolve(__dirname, '../../commands');
|
|
13
|
+
const TEMPLATES_SOURCE = path.resolve(__dirname, '../../templates');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get the .v-flow directory path for a given project root
|
|
17
|
+
*/
|
|
18
|
+
function getVFlowDir(projectRoot) {
|
|
19
|
+
return path.join(projectRoot, MASTER_FLOW_DIR);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check if v-flow is initialized in the given directory
|
|
24
|
+
*/
|
|
25
|
+
function isInitialized(projectRoot) {
|
|
26
|
+
return fs.existsSync(getVFlowDir(projectRoot));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Create the .v-flow directory structure
|
|
31
|
+
*/
|
|
32
|
+
async function scaffoldDirectories(projectRoot) {
|
|
33
|
+
const mfDir = getVFlowDir(projectRoot);
|
|
34
|
+
const dirs = [
|
|
35
|
+
mfDir,
|
|
36
|
+
path.join(mfDir, 'features'),
|
|
37
|
+
path.join(mfDir, 'modules'),
|
|
38
|
+
path.join(mfDir, 'knowledge'),
|
|
39
|
+
path.join(mfDir, 'knowledge', 'architecture-decisions'),
|
|
40
|
+
path.join(mfDir, 'knowledge', 'patterns'),
|
|
41
|
+
path.join(mfDir, 'knowledge', 'lessons-learned'),
|
|
42
|
+
path.join(mfDir, 'knowledge', 'golden-tests'),
|
|
43
|
+
path.join(mfDir, 'incidents'),
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
for (const dir of dirs) {
|
|
47
|
+
await fs.ensureDir(dir);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Copy a template file to destination, replacing placeholders
|
|
53
|
+
*/
|
|
54
|
+
async function copyTemplate(templateName, destPath, replacements = {}) {
|
|
55
|
+
const srcPath = path.join(TEMPLATES_SOURCE, templateName);
|
|
56
|
+
if (!fs.existsSync(srcPath)) {
|
|
57
|
+
throw new Error(`Template not found: ${templateName}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let content = await fs.readFile(srcPath, 'utf8');
|
|
61
|
+
|
|
62
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
63
|
+
content = content.replace(new RegExp(`\\[${key}\\]`, 'g'), value);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
await fs.writeFile(destPath, content, 'utf8');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const AGENTS_SOURCE = path.resolve(__dirname, '../../agents');
|
|
70
|
+
const SKILLS_SOURCE = path.resolve(__dirname, '../../skills');
|
|
71
|
+
const MODULE_CARD_TEMPLATES_SOURCE = path.resolve(__dirname, '../../templates/module-card');
|
|
72
|
+
const PTYC_SCRIPTS_SOURCE = path.resolve(__dirname, '../../scripts/ptyc');
|
|
73
|
+
const PTYC_TEMPLATES_SOURCE = path.resolve(__dirname, '../../templates/ptyc');
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Setup AI agent integration files (workflows, skills, agents)
|
|
77
|
+
*/
|
|
78
|
+
async function setupAgentFiles(projectRoot) {
|
|
79
|
+
// ==============================
|
|
80
|
+
// Antigravity: .agents/
|
|
81
|
+
// ==============================
|
|
82
|
+
|
|
83
|
+
// 1. Workflows (slash commands)
|
|
84
|
+
const agentsWorkflowDir = path.join(projectRoot, '.agents', 'workflows');
|
|
85
|
+
await fs.ensureDir(agentsWorkflowDir);
|
|
86
|
+
|
|
87
|
+
// 2. Skills (agent definitions as skills with SKILL.md)
|
|
88
|
+
const agentsSkillsDir = path.join(projectRoot, '.agents', 'skills');
|
|
89
|
+
await fs.ensureDir(agentsSkillsDir);
|
|
90
|
+
|
|
91
|
+
// ==============================
|
|
92
|
+
// GitHub Copilot: .github/
|
|
93
|
+
// ==============================
|
|
94
|
+
|
|
95
|
+
// 1. Prompts (slash commands)
|
|
96
|
+
const githubPromptsDir = path.join(projectRoot, '.github', 'prompts');
|
|
97
|
+
await fs.ensureDir(githubPromptsDir);
|
|
98
|
+
|
|
99
|
+
// 2. Agents (custom agent definitions)
|
|
100
|
+
const githubAgentsDir = path.join(projectRoot, '.github', 'agents');
|
|
101
|
+
await fs.ensureDir(githubAgentsDir);
|
|
102
|
+
|
|
103
|
+
// ==============================
|
|
104
|
+
// Cursor: .cursor/
|
|
105
|
+
// ==============================
|
|
106
|
+
|
|
107
|
+
// 1. Rules (slash commands)
|
|
108
|
+
const cursorRulesDir = path.join(projectRoot, '.cursor', 'rules');
|
|
109
|
+
await fs.ensureDir(cursorRulesDir);
|
|
110
|
+
|
|
111
|
+
// --- Copy command files to workflows & prompts ---
|
|
112
|
+
if (fs.existsSync(COMMANDS_SOURCE)) {
|
|
113
|
+
const commandFiles = await fs.readdir(COMMANDS_SOURCE);
|
|
114
|
+
for (const file of commandFiles) {
|
|
115
|
+
if (file.endsWith('.md')) {
|
|
116
|
+
const srcPath = path.join(COMMANDS_SOURCE, file);
|
|
117
|
+
await fs.copy(srcPath, path.join(agentsWorkflowDir, file));
|
|
118
|
+
await fs.copy(srcPath, path.join(githubPromptsDir, file));
|
|
119
|
+
|
|
120
|
+
// Cursor expects .mdc extension for rules
|
|
121
|
+
const mdcName = file.replace(/\.md$/, '.mdc');
|
|
122
|
+
let content = await fs.readFile(srcPath, 'utf8');
|
|
123
|
+
|
|
124
|
+
// If it already has YAML frontmatter, inject globs: *
|
|
125
|
+
if (content.startsWith('---')) {
|
|
126
|
+
content = content.replace(/^---\r?\n/, '---\nglobs: *\n');
|
|
127
|
+
} else {
|
|
128
|
+
// Otherwise prepend a basic frontmatter
|
|
129
|
+
const cmdName = file.replace(/\.md$/, '');
|
|
130
|
+
content = `---
|
|
131
|
+
description: Execute /${cmdName}
|
|
132
|
+
globs: *
|
|
133
|
+
---
|
|
134
|
+
${content}`;
|
|
135
|
+
}
|
|
136
|
+
await fs.writeFile(path.join(cursorRulesDir, mdcName), content, 'utf8');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// --- Copy skills to .agents/skills/ ---
|
|
142
|
+
if (fs.existsSync(SKILLS_SOURCE)) {
|
|
143
|
+
await fs.copy(SKILLS_SOURCE, agentsSkillsDir);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// --- Copy module-card templates to .v-flow/templates/module-card/ ---
|
|
147
|
+
// These templates are used by understand-agent to generate module cards
|
|
148
|
+
// with the correct structure (headings, tables, mermaid diagrams)
|
|
149
|
+
const moduleCardTemplatesDir = path.join(getVFlowDir(projectRoot), 'templates', 'module-card');
|
|
150
|
+
if (fs.existsSync(MODULE_CARD_TEMPLATES_SOURCE)) {
|
|
151
|
+
await fs.ensureDir(moduleCardTemplatesDir);
|
|
152
|
+
await fs.copy(MODULE_CARD_TEMPLATES_SOURCE, moduleCardTemplatesDir);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// --- Copy PTYC doc-export tooling INTO .v-flow/ (scripts + template) ---
|
|
156
|
+
// Used by /v.ba-to-ptyc (ba-to-ptyc-agent). Paths MUST live under .v-flow so they
|
|
157
|
+
// resolve in the target project — not the framework repo. render_ptyc.py finds its
|
|
158
|
+
// template via ../../templates/ptyc, which holds inside .v-flow too. Skips sample/ fixture.
|
|
159
|
+
const vflowRoot = getVFlowDir(projectRoot);
|
|
160
|
+
if (fs.existsSync(PTYC_SCRIPTS_SOURCE)) {
|
|
161
|
+
await fs.copy(PTYC_SCRIPTS_SOURCE, path.join(vflowRoot, 'scripts', 'ptyc'), {
|
|
162
|
+
filter: (src) => path.basename(src) !== 'sample' && !src.includes('__pycache__'),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
if (fs.existsSync(PTYC_TEMPLATES_SOURCE)) {
|
|
166
|
+
await fs.copy(PTYC_TEMPLATES_SOURCE, path.join(vflowRoot, 'templates', 'ptyc'));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// --- Copy agent definitions to .agents/agents/ & .github/agents/ & .cursor/agents/ ---
|
|
170
|
+
if (fs.existsSync(AGENTS_SOURCE)) {
|
|
171
|
+
// Antigravity: .agents/agents/ (flat .md files — personas)
|
|
172
|
+
const antigravityAgentsDir = path.join(projectRoot, '.agents', 'agents');
|
|
173
|
+
await fs.ensureDir(antigravityAgentsDir);
|
|
174
|
+
|
|
175
|
+
// Cursor: .cursor/agents/ (.mdc files & agent personas)
|
|
176
|
+
const cursorAgentsDir = path.join(projectRoot, '.cursor', 'agents');
|
|
177
|
+
await fs.ensureDir(cursorAgentsDir);
|
|
178
|
+
|
|
179
|
+
const agentFiles = await fs.readdir(AGENTS_SOURCE);
|
|
180
|
+
for (const file of agentFiles) {
|
|
181
|
+
if (file.endsWith('.md')) {
|
|
182
|
+
const srcPath = path.join(AGENTS_SOURCE, file);
|
|
183
|
+
|
|
184
|
+
// Antigravity: flat .md file in .agents/agents/
|
|
185
|
+
await fs.copy(srcPath, path.join(antigravityAgentsDir, file));
|
|
186
|
+
|
|
187
|
+
// Copilot: flat .md file in .github/agents/
|
|
188
|
+
await fs.copy(srcPath, path.join(githubAgentsDir, file));
|
|
189
|
+
|
|
190
|
+
// Cursor: .mdc file with frontmatter in .cursor/agents/
|
|
191
|
+
const mdcName = file.replace(/\.md$/, '.mdc');
|
|
192
|
+
let content = await fs.readFile(srcPath, 'utf8');
|
|
193
|
+
const agentName = file.replace(/\.md$/, '');
|
|
194
|
+
content = `---\ndescription: Agent persona - ${agentName}\nglobs: *\nalwaysApply: false\n---\n${content}`;
|
|
195
|
+
await fs.writeFile(path.join(cursorAgentsDir, mdcName), content, 'utf8');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Copy AGENTS.md and copilot-instructions.md to project root
|
|
203
|
+
*/
|
|
204
|
+
async function setupAgentInstructionFiles(projectRoot, sourceRoot) {
|
|
205
|
+
const agentsFile = path.join(sourceRoot, 'AGENTS.md');
|
|
206
|
+
const copilotFile = path.join(sourceRoot, '.github', 'copilot-instructions.md');
|
|
207
|
+
|
|
208
|
+
if (fs.existsSync(agentsFile)) {
|
|
209
|
+
await fs.copy(agentsFile, path.join(projectRoot, 'AGENTS.md'));
|
|
210
|
+
// For Cursor, we use AGENTS.md as the global .cursorrules
|
|
211
|
+
await fs.copy(agentsFile, path.join(projectRoot, '.cursorrules'));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (fs.existsSync(copilotFile)) {
|
|
215
|
+
const destDir = path.join(projectRoot, '.github');
|
|
216
|
+
await fs.ensureDir(destDir);
|
|
217
|
+
await fs.copy(copilotFile, path.join(destDir, 'copilot-instructions.md'));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* List all features in .v-flow/features/
|
|
223
|
+
*/
|
|
224
|
+
async function listFeatures(projectRoot) {
|
|
225
|
+
const featuresDir = path.join(getVFlowDir(projectRoot), 'features');
|
|
226
|
+
if (!fs.existsSync(featuresDir)) return [];
|
|
227
|
+
|
|
228
|
+
const entries = await fs.readdir(featuresDir, { withFileTypes: true });
|
|
229
|
+
return entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Check which artifacts exist for a feature
|
|
234
|
+
*/
|
|
235
|
+
async function checkFeatureArtifacts(projectRoot, featureName) {
|
|
236
|
+
const featureDir = path.join(getVFlowDir(projectRoot), 'features', featureName);
|
|
237
|
+
const artifacts = [
|
|
238
|
+
{ name: 'spec.md', phase: '[S.1] Specification' },
|
|
239
|
+
{ name: '.flow-state.yaml', phase: '[S.1] Pipeline State' },
|
|
240
|
+
{ name: 'plan.md', phase: '[B.0a] Planning' },
|
|
241
|
+
{ name: 'tasks.md', phase: '[B.0a] Task Breakdown' },
|
|
242
|
+
{ name: 'test-cases.md', phase: '[B.0b] Test Cases' },
|
|
243
|
+
{ name: 'validation-report.md', phase: '[R.0] Master Check' },
|
|
244
|
+
{ name: 'metrics-report.md', phase: '[M.0] Metrics' },
|
|
245
|
+
{ name: 'operations-report.md', phase: '[O.0] Operations' },
|
|
246
|
+
];
|
|
247
|
+
|
|
248
|
+
return artifacts.map(a => ({
|
|
249
|
+
...a,
|
|
250
|
+
exists: fs.existsSync(path.join(featureDir, a.name)),
|
|
251
|
+
}));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
module.exports = {
|
|
255
|
+
MASTER_FLOW_DIR,
|
|
256
|
+
getVFlowDir,
|
|
257
|
+
isInitialized,
|
|
258
|
+
scaffoldDirectories,
|
|
259
|
+
copyTemplate,
|
|
260
|
+
setupAgentFiles,
|
|
261
|
+
setupAgentInstructionFiles,
|
|
262
|
+
listFeatures,
|
|
263
|
+
checkFeatureArtifacts,
|
|
264
|
+
scanCodebase,
|
|
265
|
+
generateContextMd,
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
|