sync-workflow-docs.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. #!/usr/bin/env node
  2. import fs from 'fs';
  3. import os from 'os';
  4. import path from 'path';
  5. import { fileURLToPath } from 'url';
  6. import { DOCCENTER_API_URL } from '../src/data/doc-paths.js';
  7. const __dirname = path.dirname(fileURLToPath(import.meta.url));
  8. const ROOT = path.resolve(__dirname, '..');
  9. const APPLY = process.argv.includes('--apply');
  10. const AUTH_PATH = path.join(os.homedir(), '.vl-code', 'auth.json');
  11. const cookie = process.env.DOCCENTER_COOKIE || (() => {
  12. try {
  13. return JSON.parse(fs.readFileSync(AUTH_PATH, 'utf-8')).cookie;
  14. } catch {
  15. return '';
  16. }
  17. })();
  18. const MANIFEST = [
  19. {
  20. file: '.vl-code/workflows/parallel-codegen.json',
  21. path: '112',
  22. name: 'VL_Parallel_CodeGen_v3.16',
  23. description: 'Fast ProjectMeta-driven code generation with default Theme/Theme.vth and parallel VL file fanout.',
  24. changeNote: 'v3.16.2 — enforce Theme/Theme.vth, Database/<ProjectName>.vdb, and parallel VL file generation',
  25. },
  26. {
  27. file: '.vl-code/workflows/meta-direct-codegen.json',
  28. path: '113',
  29. name: 'VL_MetaDirect_CodeGen_v3.16',
  30. description: 'Small-project ProjectMeta-first code generation with default Theme/Theme.vth and parallel VL file fanout.',
  31. changeNote: 'v3.16.2 — enforce Theme/Theme.vth, Database/<ProjectName>.vdb, and parallel VL file generation',
  32. },
  33. {
  34. file: '.vl-code/workflows/3-file-codegen.json',
  35. path: '114',
  36. name: 'VL_3File_CodeGen_v3.16',
  37. description: '3-file auxiliary workflow using PRD + ServiceMap + UIMap, plus default Theme/Theme.vth.',
  38. changeNote: 'v3.16.2 — seed Theme/Theme.vth, remove main.vdb fallback, and parallelize final VL fanout',
  39. },
  40. {
  41. file: '.vl-code/workflows/6-file-codegen.json',
  42. path: '60',
  43. name: 'VL_FullStack_CodeGen_6Files_v3.16',
  44. description: 'Full-stack code generation using 6 auxiliary spec files and DocCenter agent prompts.',
  45. changeNote: 'v3.16.2 — enforce Theme/Theme.vth, Database/<ProjectName>.vdb, and parallel VL file generation',
  46. },
  47. {
  48. file: '.vl-code/workflows/9-file-codegen.json',
  49. path: '115',
  50. name: 'VL_9File_CodeGen_v3.16',
  51. description: '9-file auxiliary workflow with manifest-driven parallel VL generation and default Theme/Theme.vth.',
  52. changeNote: 'v3.16.2 — enforce Theme/Theme.vth, Database/<ProjectName>.vdb, and parallel VL file generation',
  53. },
  54. {
  55. file: '.vl-code/workflows/add-page.json',
  56. path: '120',
  57. name: 'VL_AddPage_Workflow_v1',
  58. description: 'Workflow to add a new page (section + components + route) to an existing VL project',
  59. changeNote: 'v1.2 — fix DocCenter prompt refs and write updated route to the real target app file',
  60. },
  61. {
  62. file: '.vl-code/workflows/add-service.json',
  63. path: '130',
  64. name: 'VL_AddService_Workflow_v1',
  65. description: 'Workflow to add a new backend service domain to an existing VL project',
  66. changeNote: 'v1.1 — fix prompt refs and write database changes to the ProjectMeta database file',
  67. },
  68. {
  69. file: '.vl-code/workflows/theme-customize.json',
  70. path: '140',
  71. name: 'VL_ThemeCustomize_Workflow_v1',
  72. description: 'Workflow to regenerate theme tokens and cascade updates to affected VL files',
  73. changeNote: 'v1.1 — fix theme prompt path, dynamic theme file resolution, and cascade file writes',
  74. },
  75. {
  76. file: '.vl-code/workflows/incremental-update.json',
  77. path: '141',
  78. name: 'VL_IncrementalUpdate_Workflow_v1',
  79. description: 'General existing-project adjustment workflow using meta diff plus targeted file regeneration.',
  80. changeNote: 'v1.0 — remove missing DocCenter prompt deps and strengthen inline regen instructions',
  81. },
  82. {
  83. file: '.vl-code/workflows/compile-fix.json',
  84. path: '142',
  85. name: 'VL_CompileFix_Workflow_v2',
  86. description: 'Compile-report-driven workflow that applies file fixes from .vl-code/last-compile.json and hands verification back to VLCompile.',
  87. changeNote: 'v2.0 — replace fake compile/recompile steps with report-driven repair flow',
  88. },
  89. {
  90. file: 'docs/workflow-audits/workflow-audit-2026-03-14.md',
  91. path: '161',
  92. name: 'VL-Code Workflow Audit Report 2026-03-14',
  93. description: 'Workflow/doc-center/debug-fix audit report for the 2026-03-14 correction pass.',
  94. changeNote: 'Initial audit report',
  95. },
  96. ];
  97. if (!cookie) {
  98. console.error('Missing DocCenter cookie. Set DOCCENTER_COOKIE or login so ~/.vl-code/auth.json exists.');
  99. process.exit(1);
  100. }
  101. const headers = {
  102. 'Content-Type': 'application/json',
  103. Cookie: `ih5bearer=${cookie}`,
  104. };
  105. async function dcPost(endpoint, body) {
  106. const res = await fetch(`${DOCCENTER_API_URL}/${endpoint}`, {
  107. method: 'POST',
  108. headers,
  109. body: JSON.stringify(body),
  110. });
  111. const text = await res.text();
  112. try {
  113. return JSON.parse(text);
  114. } catch {
  115. throw new Error(`DocCenter returned non-JSON for ${endpoint}: ${text.slice(0, 300)}`);
  116. }
  117. }
  118. async function loadCatalog() {
  119. const res = await dcPost('SERVICE_DocCenter_GetDocList', {
  120. keyword: '',
  121. tagId: 0,
  122. page: 1,
  123. pageSize: 200,
  124. });
  125. return Array.isArray(res?.data) ? res.data : [];
  126. }
  127. async function getDocContent(docId) {
  128. const res = await dcPost('SERVICE_DocCenter_GetDocById', { docId });
  129. return res?.data || null;
  130. }
  131. async function syncOne(entry, catalog) {
  132. const fullPath = path.join(ROOT, entry.file);
  133. const currentContent = fs.readFileSync(fullPath, 'utf-8');
  134. const existing = catalog.find(doc => String(doc.path) === String(entry.path)) ||
  135. catalog.find(doc => doc.name === entry.name);
  136. if (!existing) {
  137. if (!APPLY) {
  138. return { action: 'create', path: entry.path, name: entry.name, docId: null };
  139. }
  140. const createRes = await dcPost('SERVICE_DocCenter_CreateDoc', {
  141. name: entry.name,
  142. description: entry.description,
  143. path: entry.path,
  144. });
  145. const created = createRes?.data || createRes;
  146. const docId = created?._id || created?.id || createRes?.docId || createRes?.data?.docId;
  147. if (!docId) throw new Error(`CreateDoc did not return a docId for path ${entry.path}`);
  148. await dcPost('SERVICE_DocCenter_SaveAsVersion', {
  149. path: entry.path,
  150. name: entry.name,
  151. description: entry.description,
  152. currentContent,
  153. changeNote: entry.changeNote,
  154. });
  155. catalog.push({ _id: docId, path: entry.path, name: entry.name });
  156. return { action: 'created', path: entry.path, name: entry.name, docId };
  157. }
  158. const docId = existing._id || existing.id;
  159. const remote = docId ? await getDocContent(docId) : null;
  160. if (remote?.currentContent === currentContent) {
  161. return { action: 'unchanged', path: entry.path, name: entry.name, docId };
  162. }
  163. if (!APPLY) {
  164. return { action: 'publish', path: entry.path, name: entry.name, docId };
  165. }
  166. await dcPost('SERVICE_DocCenter_SaveAsVersion', {
  167. path: entry.path,
  168. name: entry.name,
  169. description: entry.description,
  170. currentContent,
  171. changeNote: entry.changeNote,
  172. });
  173. return { action: 'published', path: entry.path, name: entry.name, docId };
  174. }
  175. async function main() {
  176. const catalog = await loadCatalog();
  177. const results = [];
  178. console.log(`\nDocCenter workflow sync ${APPLY ? '(apply)' : '(dry-run)'}`);
  179. for (const entry of MANIFEST) {
  180. const result = await syncOne(entry, catalog);
  181. results.push(result);
  182. const label = `${result.action}`.padEnd(9, ' ');
  183. console.log(`${label} path ${result.path} ${result.name}${result.docId ? ` (docId ${result.docId})` : ''}`);
  184. }
  185. const counts = results.reduce((acc, item) => {
  186. acc[item.action] = (acc[item.action] || 0) + 1;
  187. return acc;
  188. }, {});
  189. console.log('\nSummary:', counts);
  190. }
  191. main().catch(err => {
  192. console.error(err.message);
  193. process.exit(1);
  194. });