@ktpartners/dgs-platform 2.7.5 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +15 -12
  3. package/agents/dgs-executor.md +0 -52
  4. package/deliver-great-systems/bin/dgs-tools.cjs +66 -10
  5. package/deliver-great-systems/bin/lib/commands.cjs +1 -8
  6. package/deliver-great-systems/bin/lib/config.cjs +9 -90
  7. package/deliver-great-systems/bin/lib/context.cjs +2 -2
  8. package/deliver-great-systems/bin/lib/context.test.cjs +100 -100
  9. package/deliver-great-systems/bin/lib/core.cjs +17 -57
  10. package/deliver-great-systems/bin/lib/core.test.cjs +166 -170
  11. package/deliver-great-systems/bin/lib/docs.cjs +3 -3
  12. package/deliver-great-systems/bin/lib/docs.test.cjs +14 -7
  13. package/deliver-great-systems/bin/lib/execution.cjs +2 -2
  14. package/deliver-great-systems/bin/lib/execution.test.cjs +65 -67
  15. package/deliver-great-systems/bin/lib/ideas.cjs +4 -4
  16. package/deliver-great-systems/bin/lib/ideas.test.cjs +45 -44
  17. package/deliver-great-systems/bin/lib/init.cjs +9 -4
  18. package/deliver-great-systems/bin/lib/init.test.cjs +242 -175
  19. package/deliver-great-systems/bin/lib/jobs.cjs +1 -1
  20. package/deliver-great-systems/bin/lib/jobs.test.cjs +203 -202
  21. package/deliver-great-systems/bin/lib/migration.cjs +256 -281
  22. package/deliver-great-systems/bin/lib/migration.test.cjs +385 -440
  23. package/deliver-great-systems/bin/lib/milestone.cjs +1 -1
  24. package/deliver-great-systems/bin/lib/overlap.cjs +4 -4
  25. package/deliver-great-systems/bin/lib/overlap.test.cjs +45 -44
  26. package/deliver-great-systems/bin/lib/path-audit.test.cjs +16 -22
  27. package/deliver-great-systems/bin/lib/paths.cjs +60 -59
  28. package/deliver-great-systems/bin/lib/paths.test.cjs +192 -225
  29. package/deliver-great-systems/bin/lib/phase.cjs +5 -4
  30. package/deliver-great-systems/bin/lib/projects.cjs +8 -8
  31. package/deliver-great-systems/bin/lib/projects.test.cjs +75 -74
  32. package/deliver-great-systems/bin/lib/repos.cjs +94 -230
  33. package/deliver-great-systems/bin/lib/repos.test.cjs +84 -75
  34. package/deliver-great-systems/bin/lib/search.cjs +4 -4
  35. package/deliver-great-systems/bin/lib/specs.cjs +2 -2
  36. package/deliver-great-systems/bin/lib/sync.cjs +1 -1
  37. package/deliver-great-systems/bin/lib/template.cjs +3 -3
  38. package/deliver-great-systems/bin/lib/test-helpers.cjs +59 -162
  39. package/deliver-great-systems/bin/lib/verify.cjs +3 -3
  40. package/deliver-great-systems/references/planning-config.md +7 -8
  41. package/deliver-great-systems/workflows/add-tests.md +1 -1
  42. package/deliver-great-systems/workflows/approve-spec.md +1 -11
  43. package/deliver-great-systems/workflows/complete-milestone.md +2 -2
  44. package/deliver-great-systems/workflows/consolidate-ideas.md +1 -1
  45. package/deliver-great-systems/workflows/create-milestone-job.md +2 -2
  46. package/deliver-great-systems/workflows/discuss-phase.md +2 -2
  47. package/deliver-great-systems/workflows/execute-phase.md +63 -4
  48. package/deliver-great-systems/workflows/execute-plan.md +0 -51
  49. package/deliver-great-systems/workflows/find-related-ideas.md +1 -1
  50. package/deliver-great-systems/workflows/help.md +55 -84
  51. package/deliver-great-systems/workflows/init-product.md +14 -451
  52. package/deliver-great-systems/workflows/map-codebase.md +109 -0
  53. package/deliver-great-systems/workflows/new-milestone.md +16 -6
  54. package/deliver-great-systems/workflows/new-project.md +22 -681
  55. package/deliver-great-systems/workflows/quick.md +2 -2
  56. package/deliver-great-systems/workflows/run-job.md +56 -0
  57. package/package.json +1 -1
@@ -11,17 +11,11 @@ const path = require('path');
11
11
  const { safeReadFile, execGit, isV2Install, output, error } = require('./core.cjs');
12
12
  const { writeConfigField } = require('./config.cjs');
13
13
  const { getPlanningRoot, resetPaths } = require('./paths.cjs');
14
- // Lazy-loaded to avoid circular dependency (migration.cjs requires writeReposMd from this file)
15
- let _migration;
16
- function migration() {
17
- if (!_migration) _migration = require('./migration.cjs');
18
- return _migration;
19
- }
20
14
 
21
15
  // ─── REPOS.md Parse / Write ──────────────────────────────────────────────────
22
16
 
23
17
  /**
24
- * Parse .planning/REPOS.md into structured data.
18
+ * Parse REPOS.md into structured data.
25
19
  *
26
20
  * Returns null if the file doesn't exist or doesn't start with '# Repos'.
27
21
  * Returns { repos: [] } if the header exists but the table has no data rows.
@@ -76,7 +70,7 @@ function parseReposMd(cwd) {
76
70
  }
77
71
 
78
72
  /**
79
- * Write repos array to .planning/REPOS.md with standard format.
73
+ * Write repos array to REPOS.md with standard format.
80
74
  *
81
75
  * Creates the '# Repos' header (v2 marker), explanatory text, and
82
76
  * a properly formatted markdown table.
@@ -268,7 +262,6 @@ function extractRepoDescription(repoPath) {
268
262
  *
269
263
  * Scans for subdirectories containing .git/, excluding:
270
264
  * - node_modules
271
- * - .planning
272
265
  * - Dot-directories (starting with '.')
273
266
  * - Non-directories (files)
274
267
  *
@@ -513,7 +506,7 @@ function hasPathConflict(newPath, existingRepos) {
513
506
  // ─── PROJECTS.md Scaffold ────────────────────────────────────────────────────
514
507
 
515
508
  /**
516
- * Write .planning/PROJECTS.md with standard scaffold format.
509
+ * Write PROJECTS.MD with standard scaffold format.
517
510
  *
518
511
  * Creates the '# Projects' header (v2 marker), Active and Completed
519
512
  * sections with table headers.
@@ -689,124 +682,81 @@ function validateReposConsistency(planContent, repos) {
689
682
  // ─── Layout Detection ─────────────────────────────────────────────────────────
690
683
 
691
684
  /**
692
- * Detect the suggested layout mode for a repo based on its contents.
685
+ * Detect whether a repo has existing root-layout planning or is fresh.
693
686
  *
694
687
  * Heuristic (checked in order):
695
- * 1. .planning/ with config files -> dotplanning (existing setup)
696
- * 2. dgs.config.json at root with planningRoot:'.' -> root (already root setup)
697
- * 3. Repo is "empty-ish" (no source code indicators) AND no .planning/ -> root
698
- * 4. Planning artifacts at root (PROJECT.md, ROADMAP.md) without .planning/ -> root
699
- * 5. Default -> dotplanning (safer default)
688
+ * 1. config.local.json at root -> root (already configured)
689
+ * 2. Repo is empty-ish (no source code indicators at depth 1) -> root
690
+ * 3. Planning artifacts at root (PROJECT.md, ROADMAP.md) -> root
691
+ * 4. Default -> fresh (no planning setup detected)
700
692
  *
701
693
  * @param {string} cwd - Working directory (repo root)
702
694
  * @param {boolean} raw - Raw output mode
703
695
  */
704
696
  function cmdDetectLayout(cwd, raw) {
705
697
  const signals = [];
706
- // Pre-init detection: must check .planning/ directly (not via getPlanningRoot)
707
- const DOT_PLANNING_DIR = '.planning';
708
- const dotPlanning = path.join(cwd, DOT_PLANNING_DIR);
709
-
710
- // Signal 1: .planning/ directory with config files
711
- if (
712
- fs.existsSync(path.join(dotPlanning, 'config.json')) ||
713
- fs.existsSync(path.join(dotPlanning, 'config.local.json')) ||
714
- fs.existsSync(path.join(dotPlanning, 'dgs.config.json'))
715
- ) {
716
- signals.push('existing .planning/ directory with config files');
717
- output({ suggested: 'dotplanning', signals }, raw);
718
- return;
719
- }
720
698
 
721
- // Signal 2: config.local.json at root with planningRoot: '.'
699
+ // Signal 1: config.local.json at root
722
700
  const rootLocalConfigPath = path.join(cwd, 'config.local.json');
723
701
  if (fs.existsSync(rootLocalConfigPath)) {
724
- try {
725
- const config = JSON.parse(fs.readFileSync(rootLocalConfigPath, 'utf-8'));
726
- if (config.planningRoot === '.') {
727
- signals.push('config.local.json at root with planningRoot: "."');
728
- output({ suggested: 'root', signals }, raw);
729
- return;
730
- }
731
- } catch { /* malformed config -- continue detection */ }
702
+ signals.push('config.local.json at root');
703
+ output({ suggested: 'root', signals }, raw);
704
+ return;
732
705
  }
733
706
 
734
- // Signal 2b: Legacy dgs.config.json at root with planningRoot: '.'
735
- const rootConfigPath = path.join(cwd, 'dgs.config.json');
736
- if (fs.existsSync(rootConfigPath)) {
737
- try {
738
- const config = JSON.parse(fs.readFileSync(rootConfigPath, 'utf-8'));
739
- if (config.planningRoot === '.') {
740
- signals.push('dgs.config.json at root with planningRoot: "."');
741
- output({ suggested: 'root', signals }, raw);
742
- return;
707
+ // Signal 2: Empty-ish repo (no source code indicators at depth 1)
708
+ let hasSourceIndicators = false;
709
+ try {
710
+ const entries = fs.readdirSync(cwd, { withFileTypes: true });
711
+ const SOURCE_EXTENSIONS = ['.ts', '.js', '.py', '.go', '.rs', '.java'];
712
+
713
+ for (const entry of entries) {
714
+ const name = entry.name;
715
+ // Skip hidden files/dirs
716
+ if (name.startsWith('.')) continue;
717
+
718
+ // Check for source code directories
719
+ if (entry.isDirectory() && (name === 'src' || name === 'lib')) {
720
+ hasSourceIndicators = true;
721
+ break;
743
722
  }
744
- } catch { /* malformed config -- continue detection */ }
745
- }
746
-
747
- // Signal 3: Empty-ish repo (no source code indicators at depth 1)
748
- const hasDotPlanning = fs.existsSync(dotPlanning);
749
- if (!hasDotPlanning) {
750
- let hasSourceIndicators = false;
751
- try {
752
- const entries = fs.readdirSync(cwd, { withFileTypes: true });
753
- const SOURCE_EXTENSIONS = ['.ts', '.js', '.py', '.go', '.rs', '.java'];
754
723
 
755
- for (const entry of entries) {
756
- const name = entry.name;
757
- // Skip hidden files/dirs
758
- if (name.startsWith('.')) continue;
759
-
760
- // Check for source code directories
761
- if (entry.isDirectory() && (name === 'src' || name === 'lib')) {
762
- hasSourceIndicators = true;
763
- break;
764
- }
724
+ // Check for package.json
725
+ if (name === 'package.json') {
726
+ hasSourceIndicators = true;
727
+ break;
728
+ }
765
729
 
766
- // Check for package.json
767
- if (name === 'package.json') {
730
+ // Check for source code files at depth 1
731
+ if (entry.isFile()) {
732
+ const ext = path.extname(name);
733
+ if (SOURCE_EXTENSIONS.includes(ext)) {
768
734
  hasSourceIndicators = true;
769
735
  break;
770
736
  }
771
-
772
- // Check for source code files at depth 1
773
- if (entry.isFile()) {
774
- const ext = path.extname(name);
775
- if (SOURCE_EXTENSIONS.includes(ext)) {
776
- hasSourceIndicators = true;
777
- break;
778
- }
779
- }
780
737
  }
781
- } catch { /* can't read dir — treat as not empty-ish */ hasSourceIndicators = true; }
782
-
783
- if (!hasSourceIndicators) {
784
- signals.push('repo is empty-ish (no source code indicators at depth 1)');
785
- output({ suggested: 'root', signals }, raw);
786
- return;
787
738
  }
788
- }
739
+ } catch { /* can't read dir -- treat as not empty-ish */ hasSourceIndicators = true; }
789
740
 
790
- // Signal 4: Planning artifacts at root without .planning/
791
- if (!hasDotPlanning) {
792
- const hasPlanningArtifacts =
793
- fs.existsSync(path.join(cwd, 'PROJECT.md')) ||
794
- fs.existsSync(path.join(cwd, 'ROADMAP.md'));
795
- if (hasPlanningArtifacts) {
796
- signals.push('planning artifacts (PROJECT.md or ROADMAP.md) at root without .planning/');
797
- output({ suggested: 'root', signals }, raw);
798
- return;
799
- }
741
+ if (!hasSourceIndicators) {
742
+ signals.push('repo is empty-ish (no source code indicators at depth 1)');
743
+ output({ suggested: 'root', signals }, raw);
744
+ return;
800
745
  }
801
746
 
802
- // Signal 5: .planning/ directory exists (even without config files)
803
- if (hasDotPlanning) {
804
- signals.push('existing .planning/ directory');
747
+ // Signal 3: Planning artifacts at root
748
+ const hasPlanningArtifacts =
749
+ fs.existsSync(path.join(cwd, 'PROJECT.md')) ||
750
+ fs.existsSync(path.join(cwd, 'ROADMAP.md'));
751
+ if (hasPlanningArtifacts) {
752
+ signals.push('planning artifacts (PROJECT.md or ROADMAP.md) at root');
753
+ output({ suggested: 'root', signals }, raw);
754
+ return;
805
755
  }
806
756
 
807
- // Default: dotplanning (safer default)
808
- signals.push('default (no root-mode signals detected)');
809
- output({ suggested: 'dotplanning', signals }, raw);
757
+ // Default: fresh (no planning setup detected)
758
+ signals.push('default (no planning setup detected)');
759
+ output({ suggested: 'fresh', signals }, raw);
810
760
  }
811
761
 
812
762
  // ─── Root-Layout .gitignore ───────────────────────────────────────────────────
@@ -1126,101 +1076,40 @@ function cmdReposResolve(cwd, filePath, raw) {
1126
1076
  }
1127
1077
 
1128
1078
  /**
1129
- * Initialize a product folder structure (v2 mode).
1130
- * Creates REPOS.md, PROJECTS.md, syncs .gitignore, updates config.
1079
+ * Initialize a product folder structure (v2 mode, root layout).
1080
+ * Creates REPOS.md, PROJECTS.md, generates .gitignore, updates config.
1131
1081
  *
1132
- * Supports layout option:
1133
- * - layout='root': Config-first pattern writes dgs.config.json at repo root
1134
- * with planningRoot:'.', then creates all artifacts at repo root (no .planning/).
1135
- * - layout=null (default): Standard .planning/ layout (unchanged).
1082
+ * All initialization uses root layout -- config.local.json at repo root,
1083
+ * planning artifacts at repo root (no subdirectory).
1136
1084
  *
1137
1085
  * @param {string} cwd - Working directory
1138
1086
  * @param {Object} options - { productName?: string, layout?: string }
1139
1087
  * @param {boolean} raw - Raw output mode
1140
1088
  */
1141
1089
  function cmdReposInitProduct(cwd, options, raw) {
1142
- const isRootMode = options.layout === 'root';
1143
-
1144
- // ── Root-mode: Config-first pattern ───────────────────────────────────────
1145
- if (isRootMode) {
1146
- // Write config.local.json FIRST so getPlanningRoot returns repo root
1147
- const rootLocalConfigPath = path.join(cwd, 'config.local.json');
1148
- fs.writeFileSync(rootLocalConfigPath, JSON.stringify({ planningRoot: '.' }, null, 2));
1149
- // Write empty shared config.json at root
1150
- const rootSharedConfigPath = path.join(cwd, 'config.json');
1151
- if (!fs.existsSync(rootSharedConfigPath)) {
1152
- fs.writeFileSync(rootSharedConfigPath, JSON.stringify({}, null, 2));
1153
- }
1154
- resetPaths(); // Clear stale cache so getPlanningRoot resolves to cwd
1155
-
1156
- // Check if already initialized (idempotency)
1157
- if (isV2Install(cwd)) {
1158
- // Silently ensure v3.0 directories exist (backfill for upgrades)
1159
- const ideasStates = ['pending', 'rejected', 'done'];
1160
- for (const st of ideasStates) {
1161
- fs.mkdirSync(path.join(getPlanningRoot(cwd), 'ideas', st), { recursive: true });
1162
- }
1163
- fs.mkdirSync(path.join(getPlanningRoot(cwd), 'specs'), { recursive: true });
1164
- fs.mkdirSync(path.join(getPlanningRoot(cwd), 'docs', 'product'), { recursive: true });
1165
- error('Product already initialized. Use /dgs:progress to see status.');
1166
- }
1167
- } else {
1168
- // ── Standard layout: existing v1/v2 checks ────────────────────────────
1169
- // Check if user previously declined v1-to-v2 migration
1170
- const configPath = path.join(getPlanningRoot(cwd), 'config.json');
1171
- let existingConfig = {};
1172
- try {
1173
- existingConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
1174
- } catch { /* no config yet */ }
1175
- if (existingConfig.v1_decline_migration) {
1176
- output({
1177
- v1_declined: true,
1178
- message: 'v1 mode preserved (migration was previously declined). Your v1 setup continues to work unchanged.',
1179
- }, raw);
1180
- return;
1181
- }
1182
-
1183
- // Check for v1 install (existing .planning/PROJECT.md without v2 markers)
1184
- const v1Check = migration().detectV1Install(cwd);
1185
- if (v1Check.isV1) {
1186
- // Return v1 detection info — workflow handles the user prompt
1187
- output({
1188
- v1_detected: true,
1189
- project_name: v1Check.projectName,
1190
- slug: v1Check.slug,
1191
- }, raw);
1192
- return;
1193
- }
1194
-
1195
- // Check if already v2
1196
- if (isV2Install(cwd)) {
1197
- // Silently ensure v3.0 directories exist (backfill for upgrades)
1198
- const ideasStates = ['pending', 'rejected', 'done'];
1199
- for (const st of ideasStates) {
1200
- fs.mkdirSync(path.join(getPlanningRoot(cwd), 'ideas', st), { recursive: true });
1201
- }
1202
- fs.mkdirSync(path.join(getPlanningRoot(cwd), 'specs'), { recursive: true });
1203
- fs.mkdirSync(path.join(getPlanningRoot(cwd), 'docs', 'product'), { recursive: true });
1204
- // Add .gitkeep files if directories are empty
1205
- const dirsToKeep = [
1206
- path.join(getPlanningRoot(cwd), 'ideas', 'pending', '.gitkeep'),
1207
- path.join(getPlanningRoot(cwd), 'ideas', 'rejected', '.gitkeep'),
1208
- path.join(getPlanningRoot(cwd), 'ideas', 'done', '.gitkeep'),
1209
- path.join(getPlanningRoot(cwd), 'specs', '.gitkeep'),
1210
- path.join(getPlanningRoot(cwd), 'docs', 'product', '.gitkeep'),
1211
- ];
1212
- for (const gk of dirsToKeep) {
1213
- if (!fs.existsSync(gk)) {
1214
- fs.writeFileSync(gk, '', 'utf-8');
1215
- }
1216
- }
1217
- error('Product already initialized. Use /dgs:progress to see status.');
1090
+ // Write config.local.json FIRST so getPlanningRoot returns repo root
1091
+ const rootLocalConfigPath = path.join(cwd, 'config.local.json');
1092
+ fs.writeFileSync(rootLocalConfigPath, JSON.stringify({}, null, 2));
1093
+ // Write empty shared config.json at root
1094
+ const rootSharedConfigPath = path.join(cwd, 'config.json');
1095
+ if (!fs.existsSync(rootSharedConfigPath)) {
1096
+ fs.writeFileSync(rootSharedConfigPath, JSON.stringify({}, null, 2));
1097
+ }
1098
+ resetPaths(); // Clear stale cache so getPlanningRoot resolves to cwd
1099
+
1100
+ // Check if already initialized (idempotency)
1101
+ if (isV2Install(cwd)) {
1102
+ // Silently ensure v3.0 directories exist (backfill for upgrades)
1103
+ const ideasStates = ['pending', 'rejected', 'done'];
1104
+ for (const st of ideasStates) {
1105
+ fs.mkdirSync(path.join(getPlanningRoot(cwd), 'ideas', st), { recursive: true });
1218
1106
  }
1107
+ fs.mkdirSync(path.join(getPlanningRoot(cwd), 'specs'), { recursive: true });
1108
+ fs.mkdirSync(path.join(getPlanningRoot(cwd), 'docs', 'product'), { recursive: true });
1109
+ error('Product already initialized. Use /dgs:progress to see status.');
1219
1110
  }
1220
1111
 
1221
- // ── Shared scaffolding (layout-aware via getPlanningRoot) ─────────────────
1222
-
1223
- // Ensure planning root exists (for standard layout creates .planning/; for root it's cwd)
1112
+ // Planning root is cwd (root layout)
1224
1113
  fs.mkdirSync(getPlanningRoot(cwd), { recursive: true });
1225
1114
 
1226
1115
  // Create ideas, specs, and docs directories for v3.0 features
@@ -1270,52 +1159,27 @@ function cmdReposInitProduct(cwd, options, raw) {
1270
1159
  // Write REPOS.md
1271
1160
  writeReposMd(cwd, allRepos);
1272
1161
 
1273
- // Write PROJECTS.md (empty no projects yet)
1162
+ // Write PROJECTS.md (empty -- no projects yet)
1274
1163
  writeProjectsMd(cwd, []);
1275
1164
 
1276
- if (isRootMode) {
1277
- // Root-mode: generate .gitignore for root-layout repo
1278
- generateRootGitignore(cwd);
1279
-
1280
- // Update config with product_name (config already exists from config-first step)
1281
- writeConfigField(cwd, 'product_name', productName);
1282
-
1283
- output({
1284
- initialized: true,
1285
- layout: 'root',
1286
- product_name: productName,
1287
- repos_found: allRepos.length,
1288
- repos: allRepos,
1289
- needs_git_init: needsGitInit,
1290
- ideas_dirs_created: true,
1291
- specs_dir_created: true,
1292
- docs_dir_created: true,
1293
- files_created: ['config.json', 'config.local.json', 'REPOS.md', 'PROJECTS.md', 'ideas/', 'specs/', 'docs/', '.gitignore', 'review-keys.json'],
1294
- }, raw);
1295
- } else {
1296
- // Standard layout: sync .gitignore for local repo paths
1297
- const localPaths = allRepos.map(r => r.path).filter(p => p.startsWith('./'));
1298
- if (localPaths.length > 0) {
1299
- syncGitignore(cwd, localPaths);
1300
- }
1165
+ // Generate .gitignore for root-layout repo
1166
+ generateRootGitignore(cwd);
1301
1167
 
1302
- // Update config with product_name
1303
- writeConfigField(cwd, 'product_name', productName);
1304
-
1305
- output({
1306
- initialized: true,
1307
- layout: 'dotplanning',
1308
- product_name: productName,
1309
- repos_found: allRepos.length,
1310
- repos: allRepos,
1311
- needs_git_init: needsGitInit,
1312
- gitignore_synced: localPaths.length > 0,
1313
- ideas_dirs_created: true,
1314
- specs_dir_created: true,
1315
- docs_dir_created: true,
1316
- files_created: ['.planning/', '.planning/REPOS.md', '.planning/PROJECTS.md', '.planning/ideas/', '.planning/specs/', '.planning/docs/', '.planning/review-keys.json'],
1317
- }, raw);
1318
- }
1168
+ // Update config with product_name (config already exists from config-first step)
1169
+ writeConfigField(cwd, 'product_name', productName);
1170
+
1171
+ output({
1172
+ initialized: true,
1173
+ layout: 'root',
1174
+ product_name: productName,
1175
+ repos_found: allRepos.length,
1176
+ repos: allRepos,
1177
+ needs_git_init: needsGitInit,
1178
+ ideas_dirs_created: true,
1179
+ specs_dir_created: true,
1180
+ docs_dir_created: true,
1181
+ files_created: ['config.json', 'config.local.json', 'REPOS.md', 'PROJECTS.md', 'ideas/', 'specs/', 'docs/', '.gitignore', 'review-keys.json'],
1182
+ }, raw);
1319
1183
  }
1320
1184
 
1321
1185
  /**