codex-genesis-harness 0.1.7 → 0.1.8

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 (93) hide show
  1. package/.codebase/COMPRESSED_CONTEXT.md +80 -0
  2. package/.codebase/CURRENT_STATE.md +37 -11
  3. package/.codebase/DEPENDENCY_GRAPH.md +14 -1
  4. package/.codebase/IMPLEMENTATION_HANDOFF.md +34 -336
  5. package/.codebase/KNOWN_PROBLEMS.md +54 -3
  6. package/.codebase/MODULE_INDEX.md +8 -0
  7. package/.codebase/PIPELINE_FLOW.md +7 -5
  8. package/.codebase/RECOVERY_POINTS.md +17 -78
  9. package/.codebase/TECH_DEBT.md +6 -0
  10. package/.codebase/TEST_MATRIX.md +4 -3
  11. package/.codebase/VISUAL_GRAPH.md +127 -0
  12. package/.codebase/context-policy.json +68 -0
  13. package/.codebase/memories/lessons_learned.md +21 -0
  14. package/.codebase/memories/preferences.md +17 -0
  15. package/.codebase/state.json +45 -24
  16. package/.codex/skills/genesis-architecture/SKILL.md +5 -0
  17. package/.codex/skills/genesis-debug-guide/SKILL.md +10 -4
  18. package/.codex/skills/genesis-docs-automation/SKILL.md +52 -973
  19. package/.codex/skills/genesis-executing-plans/SKILL.md +54 -0
  20. package/.codex/skills/genesis-executing-plans/agents/openai.yaml +6 -0
  21. package/.codex/skills/genesis-executing-plans/checklists/.gitkeep +0 -0
  22. package/.codex/skills/genesis-executing-plans/examples/.gitkeep +0 -0
  23. package/.codex/skills/genesis-executing-plans/templates/.gitkeep +0 -0
  24. package/.codex/skills/genesis-harness/SKILL.md +64 -1385
  25. package/.codex/skills/genesis-harness/scripts/check-docs-sync.sh +3 -3
  26. package/.codex/skills/genesis-harness/scripts/init-planning.sh +1 -1
  27. package/.codex/skills/genesis-new-design/SKILL.md +4 -1
  28. package/.codex/skills/genesis-new-design/agents/openai.yaml +2 -0
  29. package/.codex/skills/genesis-observability-automation/SKILL.md +69 -303
  30. package/.codex/skills/genesis-observability-automation/references/common-mistakes-and-recovery.md +84 -0
  31. package/.codex/skills/genesis-observability-automation/references/workflow-phases.md +78 -0
  32. package/.codex/skills/genesis-performance-profiling/SKILL.md +1 -22
  33. package/.codex/skills/genesis-performance-profiling/agents/openai.yaml +1 -1
  34. package/.codex/skills/genesis-planning/SKILL.md +6 -1
  35. package/.codex/skills/genesis-release/SKILL.md +5 -0
  36. package/.codex/skills/genesis-research-first/SKILL.md +6 -0
  37. package/.codex/skills/genesis-spec-propagation/SKILL.md +52 -504
  38. package/.codex/skills/genesis-test-driven-development/SKILL.md +55 -0
  39. package/.codex/skills/genesis-test-driven-development/agents/openai.yaml +6 -0
  40. package/.codex/skills/genesis-test-driven-development/checklists/.gitkeep +0 -0
  41. package/.codex/skills/genesis-test-driven-development/examples/.gitkeep +0 -0
  42. package/.codex/skills/genesis-test-driven-development/templates/.gitkeep +0 -0
  43. package/.codex/skills/genesis-upgrade-design/SKILL.md +4 -2
  44. package/.codex/skills/genesis-upgrade-design/agents/openai.yaml +2 -0
  45. package/.codex/skills/genesis-using-git-worktrees/SKILL.md +54 -0
  46. package/.codex/skills/genesis-using-git-worktrees/agents/openai.yaml +6 -0
  47. package/.codex/skills/genesis-using-git-worktrees/checklists/.gitkeep +0 -0
  48. package/.codex/skills/genesis-using-git-worktrees/examples/.gitkeep +0 -0
  49. package/.codex/skills/genesis-using-git-worktrees/templates/.gitkeep +0 -0
  50. package/.codex/skills/genesis-verification-before-completion/SKILL.md +53 -0
  51. package/.codex/skills/genesis-verification-before-completion/agents/openai.yaml +6 -0
  52. package/.codex/skills/genesis-verification-before-completion/checklists/.gitkeep +0 -0
  53. package/.codex/skills/genesis-verification-before-completion/examples/.gitkeep +0 -0
  54. package/.codex/skills/genesis-verification-before-completion/templates/.gitkeep +0 -0
  55. package/.codex/skills/spec-impact-engine/SKILL.md +77 -500
  56. package/.codex/skills/spec-impact-engine/checklists/checklist.md +10 -0
  57. package/.codex-plugin/plugin.json +3 -4
  58. package/CHANGELOG.md +4 -1
  59. package/README.EN.md +32 -17
  60. package/README.VI.md +35 -19
  61. package/README.md +48 -10
  62. package/VERSION +1 -1
  63. package/bin/genesis-harness.js +735 -5
  64. package/contracts/features/registry-schema.json +15 -0
  65. package/contracts/observability/agent-run-schema.json +34 -0
  66. package/contracts/observability/failure-schema.json +35 -0
  67. package/contracts/ui/auth/login-screen-contract.json +43 -0
  68. package/features/REGISTRY.md +63 -0
  69. package/features/SCOPE-template.md +65 -0
  70. package/fixtures/planning/MOCKUP_PROMPT_TEMPLATE.md +16 -0
  71. package/observability/agent-runs/sample-run.json +13 -0
  72. package/observability/decision-logs/sample-decision.md +43 -0
  73. package/observability/failures/sample-failure.json +12 -0
  74. package/package.json +9 -3
  75. package/playwright/e2e/app-template.spec.js +37 -0
  76. package/playwright/e2e/auth/login-screen.spec.js +65 -0
  77. package/playwright/e2e/web-template.spec.js +28 -0
  78. package/scripts/check-scope.sh +100 -0
  79. package/scripts/cold-start-check.js +133 -0
  80. package/scripts/install.sh +4 -0
  81. package/scripts/prompt_sentinel.js +35 -4
  82. package/scripts/run-evals.sh +119 -3
  83. package/scripts/scratch_parser.js +49 -0
  84. package/scripts/spec_visual_sync.js +1 -1
  85. package/scripts/test_generator.js +2 -2
  86. package/scripts/uninstall.sh +4 -0
  87. package/scripts/verify.sh +16 -1
  88. package/tests/integration/cli-smoke.test.js +103 -0
  89. package/tests/unit/feature_registry.test.js +152 -0
  90. package/tests/unit/prompt_sentinel.test.js +1 -1
  91. package/tests/unit/spec_visual_sync.test.js +1 -1
  92. package/tests/unit/test_generator.test.js +1 -1
  93. package/playwright/e2e/e2e-template.md +0 -4
@@ -27,7 +27,11 @@ const skillNames = [
27
27
  "genesis-observability-automation",
28
28
  "genesis-research-first",
29
29
  "genesis-release",
30
- "spec-impact-engine"
30
+ "spec-impact-engine",
31
+ "genesis-executing-plans",
32
+ "genesis-test-driven-development",
33
+ "genesis-verification-before-completion",
34
+ "genesis-using-git-worktrees"
31
35
  ];
32
36
  const legacySkillNames = ["project-genesis-harness"];
33
37
  const sourceRoot = path.join(packageRoot, ".codex", "skills");
@@ -47,16 +51,26 @@ Usage:
47
51
  genesis-harness path
48
52
  genesis-harness status Show implementation status & skills inventory
49
53
  genesis-harness docs Show API contracts & documentation sync report
54
+ genesis-harness docs-gate Run pre-commit documentation drift checks
55
+ genesis-harness verify-gate Run ALL verification gates before claiming done (L09 blocker)
56
+ genesis-harness cold-start Run automated cold-start L03 checklist
50
57
  genesis-harness remember [cat] "<msg>" Remember a persistent project fact/insight (Bead)
51
58
  genesis-harness recall [query] Recall and search remembered project facts
52
59
  genesis-harness forget <id> Forget/delete a fact by its unique 6-char ID
53
60
  genesis-harness prime Generate the token-minimized Agent Priming Prompt
61
+ genesis-harness leanctx Show token budget policy and portable command guidance
54
62
  genesis-harness view-mockup [slug] Interactive console UI to search & view mockups
63
+ genesis-harness mcp Interactive MCP installer
64
+ genesis-harness sync Compress and sync codebase context (AST/Regex)
65
+ genesis-harness setup-hooks Install auto-sync git pre-commit hook
66
+ genesis-harness heal <command> Run test & print agent directive on failure
55
67
 
56
68
  Environment:
57
69
  CODEX_HOME=/custom/.codex Override Codex home
58
70
  GENESIS_HARNESS_HOME=/custom/.agents Override modern skills home
59
71
  GENESIS_HARNESS_SKIP_POSTINSTALL=1 Skip npm postinstall auto-install
72
+ GENESIS_HARNESS_COMMAND_WRAPPER=rtk Optional local command wrapper override
73
+ GENESIS_HARNESS_DISABLE_RTK=1 Disable automatic rtk detection
60
74
  `;
61
75
  console.log(text.trim());
62
76
  process.exit(exitCode);
@@ -122,6 +136,49 @@ function copySkills({ quiet = false, target = "both" } = {}) {
122
136
  if (!quiet) console.log("Restart Codex, then invoke: Use $genesis-harness");
123
137
  }
124
138
 
139
+ function shouldSeedProjectRoot(rootPath) {
140
+ if (!rootPath) return false;
141
+ const resolvedRoot = path.resolve(rootPath);
142
+ if (resolvedRoot === packageRoot) return false;
143
+ const markers = [
144
+ "package.json",
145
+ "AGENTS.md",
146
+ "pyproject.toml",
147
+ "Cargo.toml",
148
+ "go.mod",
149
+ ".git",
150
+ ".codebase"
151
+ ];
152
+ return markers.some(marker => fs.existsSync(path.join(resolvedRoot, marker)));
153
+ }
154
+
155
+ function seedLeanCtxPolicy(rootPath = process.cwd(), { quiet = false } = {}) {
156
+ if (!shouldSeedProjectRoot(rootPath)) return false;
157
+
158
+ const sourcePolicy = path.join(packageRoot, ".codebase", "context-policy.json");
159
+ if (!fs.existsSync(sourcePolicy)) return false;
160
+
161
+ const codebaseDir = path.join(rootPath, ".codebase");
162
+ const targetPolicy = path.join(codebaseDir, "context-policy.json");
163
+ fs.mkdirSync(codebaseDir, { recursive: true });
164
+
165
+ if (fs.existsSync(targetPolicy)) {
166
+ if (!quiet) console.log(`[genesis-harness] LeanCTX policy already exists: ${targetPolicy}`);
167
+ return false;
168
+ }
169
+
170
+ fs.copyFileSync(sourcePolicy, targetPolicy);
171
+ if (!quiet) console.log(`[genesis-harness] LeanCTX policy installed: ${targetPolicy}`);
172
+ return true;
173
+ }
174
+
175
+ function resolvePostinstallProjectRoot() {
176
+ const initCwd = process.env.INIT_CWD;
177
+ if (shouldSeedProjectRoot(initCwd)) return initCwd;
178
+ if (shouldSeedProjectRoot(process.cwd())) return process.cwd();
179
+ return null;
180
+ }
181
+
125
182
  function uninstallSkills(target = "both") {
126
183
  for (const root of targetRoots(target)) {
127
184
  for (const skillName of [...skillNames, ...legacySkillNames]) {
@@ -165,6 +222,119 @@ function resolveBash() {
165
222
  return "bash";
166
223
  }
167
224
 
225
+ function commandExists(commandName) {
226
+ if (!/^[A-Za-z0-9._-]+$/.test(commandName)) return false;
227
+ const result = process.platform === "win32"
228
+ ? spawnSync("where", [commandName], { stdio: "ignore" })
229
+ : spawnSync("sh", ["-c", `command -v ${commandName}`], { stdio: "ignore" });
230
+ return result.status === 0;
231
+ }
232
+
233
+ function detectCommandWrapper() {
234
+ if (process.env.GENESIS_HARNESS_COMMAND_WRAPPER) {
235
+ return {
236
+ command: process.env.GENESIS_HARNESS_COMMAND_WRAPPER,
237
+ source: "GENESIS_HARNESS_COMMAND_WRAPPER"
238
+ };
239
+ }
240
+
241
+ if (process.env.GENESIS_HARNESS_DISABLE_RTK === "1") {
242
+ return { command: null, source: "disabled" };
243
+ }
244
+
245
+ if (commandExists("rtk")) {
246
+ return { command: "rtk", source: "auto-detected" };
247
+ }
248
+
249
+ return { command: null, source: "not detected" };
250
+ }
251
+
252
+ function readJsonIfExists(filePath) {
253
+ if (!fs.existsSync(filePath)) return null;
254
+ try {
255
+ return JSON.parse(fs.readFileSync(filePath, "utf8"));
256
+ } catch (error) {
257
+ return null;
258
+ }
259
+ }
260
+
261
+ function defaultContextPolicy() {
262
+ return {
263
+ name: "leanctx-default",
264
+ token_budget: 12000,
265
+ warn_at: 0.6,
266
+ compact_at: 0.7,
267
+ hard_stop_at: 0.85,
268
+ portable_commands: [
269
+ "genesis-harness leanctx",
270
+ "genesis-harness sync",
271
+ "genesis-harness docs-gate",
272
+ "npm run verify",
273
+ "npm run eval"
274
+ ],
275
+ wrapper_policy: "rtk optional when installed locally; public docs and CI must use portable commands.",
276
+ layers: []
277
+ };
278
+ }
279
+
280
+ function loadContextPolicy(rootPath = process.cwd()) {
281
+ const projectPolicy = readJsonIfExists(path.join(rootPath, ".codebase", "context-policy.json"));
282
+ const packagedPolicy = readJsonIfExists(path.join(packageRoot, ".codebase", "context-policy.json"));
283
+ return {
284
+ ...defaultContextPolicy(),
285
+ ...(packagedPolicy || {}),
286
+ ...(projectPolicy || {})
287
+ };
288
+ }
289
+
290
+ function formatCommand(command, wrapper) {
291
+ if (!wrapper.command) return command;
292
+ return `${wrapper.command} ${command}`;
293
+ }
294
+
295
+ function buildLeanCtxReport(rootPath = process.cwd()) {
296
+ const policy = loadContextPolicy(rootPath);
297
+ const wrapper = detectCommandWrapper();
298
+ const tokenBudget = Number(policy.token_budget || 12000);
299
+ const compactAt = Number(policy.compact_at || 0.7);
300
+ const hardStopAt = Number(policy.hard_stop_at || 0.85);
301
+ const lines = [];
302
+
303
+ lines.push("# LeanCTX Policy");
304
+ lines.push("");
305
+ lines.push(`- Policy: ${policy.name || "leanctx-default"}`);
306
+ lines.push(`- Token budget: ${tokenBudget}`);
307
+ lines.push(`- Compact at: ${Math.round(tokenBudget * compactAt)} tokens (${compactAt})`);
308
+ lines.push(`- Hard stop at: ${Math.round(tokenBudget * hardStopAt)} tokens (${hardStopAt})`);
309
+ lines.push(`- Command wrapper: ${wrapper.command ? `${wrapper.command} (${wrapper.source})` : `none (${wrapper.source})`} - rtk optional`);
310
+ lines.push(`- Wrapper policy: ${policy.wrapper_policy || "rtk optional; keep public commands portable."}`);
311
+ lines.push("");
312
+ lines.push("## Portable Commands");
313
+ for (const command of policy.portable_commands || []) {
314
+ lines.push(`- \`${command}\``);
315
+ }
316
+
317
+ if (wrapper.command) {
318
+ lines.push("");
319
+ lines.push("## Local Wrapper Commands");
320
+ for (const command of policy.portable_commands || []) {
321
+ lines.push(`- \`${formatCommand(command, wrapper)}\``);
322
+ }
323
+ }
324
+
325
+ if (Array.isArray(policy.layers) && policy.layers.length > 0) {
326
+ lines.push("");
327
+ lines.push("## Context Layers");
328
+ for (const layer of policy.layers) {
329
+ lines.push(`- ${layer.name}: ${layer.max_tokens || "unbounded"} tokens`);
330
+ }
331
+ }
332
+
333
+ lines.push("");
334
+ lines.push("Use LeanCTX by loading core state first, then active context, then deferred references only when needed.");
335
+ return lines.join("\n");
336
+ }
337
+
168
338
  function chmodScripts(dir) {
169
339
  if (!fs.existsSync(dir)) return;
170
340
  for (const entry of fs.readdirSync(dir)) {
@@ -240,7 +410,7 @@ function showStatus() {
240
410
  }
241
411
 
242
412
  // 3. Skills Inventory
243
- console.log("\n\x1b[1m\x1b[32m[+] Skills Inventory Check (Exactly 21 core skills):\x1b[0m");
413
+ console.log("\n\x1b[1m\x1b[32m[+] Skills Inventory Check (Exactly 25 core skills):\x1b[0m");
244
414
  let found = 0;
245
415
  let mismatched = 0;
246
416
  for (const skillName of skillNames) {
@@ -304,7 +474,7 @@ function showDocsStatus() {
304
474
  }
305
475
  }
306
476
  console.log(` Files: \x1b[33m${found.join(", ")}\x1b[0m`);
307
-
477
+
308
478
  // Print request structure preview if request.json exists
309
479
  const reqPath = path.join(apiDir, endpoint, "request.json");
310
480
  if (fs.existsSync(reqPath)) {
@@ -596,12 +766,19 @@ function primeContext() {
596
766
  out.push("3. **Single source**: Avoid duplicating plans across multi-line markdown logs; use `genesis-harness remember` to store critical project coordinates.");
597
767
  out.push("4. **TDD Pattern**: Create or update failing tests in `tests/` before making changes to public behaviors.");
598
768
  out.push("");
769
+ out.push("## 🪶 7. LeanCTX Policy");
770
+ out.push(buildLeanCtxReport(process.cwd()));
771
+ out.push("");
599
772
  out.push("---");
600
773
  out.push("");
601
774
 
602
775
  console.log(out.join("\n"));
603
776
  }
604
777
 
778
+ function showLeanCtx() {
779
+ console.log(buildLeanCtxReport(process.cwd()));
780
+ }
781
+
605
782
  function openFileNatively(filePath) {
606
783
  if (process.platform === "win32") {
607
784
  const cp = spawnSync("cmd.exe", ["/c", "start", "", filePath], { shell: true });
@@ -611,7 +788,7 @@ function openFileNatively(filePath) {
611
788
  if (process.platform === "linux") {
612
789
  cmd = "xdg-open";
613
790
  }
614
-
791
+
615
792
  const cp = spawnSync(cmd, [filePath]);
616
793
  return cp.status === 0;
617
794
  }
@@ -703,7 +880,7 @@ function viewMockupsInteractive(arg) {
703
880
  console.log(" \x1b[1m\x1b[32m[+] LAUNCHED SYSTEM VIEW FOR:\x1b[0m \x1b[1m" + selected.title + "\x1b[0m\n");
704
881
  console.log(` - \x1b[1mMockup File:\x1b[0m ${selected.fileName}`);
705
882
  console.log(` - \x1b[1mFolder Path:\x1b[0m ${path.dirname(selected.fullPath)}`);
706
-
883
+
707
884
  let sizeText = "Unknown";
708
885
  try {
709
886
  const stats = fs.statSync(selected.fullPath);
@@ -764,18 +941,547 @@ function viewMockupsInteractive(arg) {
764
941
  });
765
942
  }
766
943
 
944
+ function syncContext() {
945
+ const srcDirs = ['src', 'lib', 'tests', 'bin'];
946
+ const codebaseDir = path.join(process.cwd(), '.codebase');
947
+ const contextFile = path.join(codebaseDir, 'COMPRESSED_CONTEXT.md');
948
+ const visualFile = path.join(codebaseDir, 'VISUAL_GRAPH.md');
949
+
950
+ if (!fs.existsSync(codebaseDir)) {
951
+ fs.mkdirSync(codebaseDir, { recursive: true });
952
+ }
953
+
954
+ let output = '# Compressed Context & Dependency Graph\n\n';
955
+ let visualOutput = '# Visual Project Graph\n\n';
956
+ const depEdges = [];
957
+
958
+ let parser, traverse;
959
+ try {
960
+ parser = require('@babel/parser');
961
+ traverse = require('@babel/traverse').default;
962
+ } catch (e) {
963
+ console.error('[genesis-harness] AST parser dependencies missing. Run: npm install');
964
+ process.exit(1);
965
+ }
966
+
967
+ function walk(dir) {
968
+ if (!fs.existsSync(dir)) return;
969
+ const files = fs.readdirSync(dir);
970
+ for (const file of files) {
971
+ const fullPath = path.join(dir, file);
972
+ const stat = fs.statSync(fullPath);
973
+ if (stat.isDirectory() && file !== 'node_modules') {
974
+ walk(fullPath);
975
+ } else if (file.endsWith('.js') || file.endsWith('.ts')) {
976
+ const content = fs.readFileSync(fullPath, 'utf8');
977
+ const exportsList = [];
978
+ const importsList = [];
979
+ const relativePath = fullPath.replace(process.cwd() + '/', '');
980
+ const featuresList = [];
981
+
982
+ try {
983
+ const ast = parser.parse(content, {
984
+ sourceType: 'module',
985
+ plugins: ['typescript', 'jsx']
986
+ });
987
+
988
+ if (ast.comments) {
989
+ ast.comments.forEach(comment => {
990
+ const match = comment.value.match(/@feature:\s*(.+)/i);
991
+ if (match) {
992
+ featuresList.push(match[1].trim());
993
+ }
994
+ });
995
+ }
996
+
997
+ traverse(ast, {
998
+ ExportNamedDeclaration(path) {
999
+ const decl = path.node.declaration;
1000
+ if (decl) {
1001
+ if (decl.type === 'ClassDeclaration' && decl.id) {
1002
+ exportsList.push('class ' + decl.id.name);
1003
+ } else if (decl.type === 'FunctionDeclaration' && decl.id) {
1004
+ exportsList.push('function ' + decl.id.name);
1005
+ } else if (decl.type === 'VariableDeclaration') {
1006
+ decl.declarations.forEach(d => {
1007
+ if (d.id) exportsList.push('const ' + d.id.name);
1008
+ });
1009
+ }
1010
+ }
1011
+ },
1012
+ ImportDeclaration(path) {
1013
+ importsList.push(path.node.source.value);
1014
+ depEdges.push(` "${relativePath}" --> "${path.node.source.value}"`);
1015
+ },
1016
+ CallExpression(path) {
1017
+ if (path.node.callee.name === 'require' && path.node.arguments.length > 0) {
1018
+ if (path.node.arguments[0].type === 'StringLiteral') {
1019
+ importsList.push(path.node.arguments[0].value);
1020
+ depEdges.push(` "${relativePath}" --> "${path.node.arguments[0].value}"`);
1021
+ }
1022
+ }
1023
+ }
1024
+ });
1025
+ } catch (err) {
1026
+ exportsList.push('// AST Parse Error: ' + err.message);
1027
+ }
1028
+
1029
+ if (exportsList.length > 0 || importsList.length > 0 || featuresList.length > 0) {
1030
+ output += '## ' + relativePath + '\n';
1031
+ if (featuresList.length > 0) {
1032
+ output += '### Implements Features\n';
1033
+ featuresList.forEach(f => output += '- `' + f + '`\n');
1034
+ }
1035
+ if (exportsList.length > 0) {
1036
+ output += '### Exports\n';
1037
+ exportsList.forEach(sig => output += '- `' + sig + '`\n');
1038
+ }
1039
+ if (importsList.length > 0) {
1040
+ output += '### Dependencies\n';
1041
+ importsList.forEach(imp => output += '- `' + imp + '`\n');
1042
+ }
1043
+ output += '\n';
1044
+ }
1045
+ }
1046
+ }
1047
+ }
1048
+
1049
+ srcDirs.forEach(dir => walk(path.join(process.cwd(), dir)));
1050
+ // Generate Visual Graph
1051
+ visualOutput += '## Harness Relationship Map\n\n```mermaid\nflowchart LR\n';
1052
+ visualOutput += ' manifest[".codex-plugin/plugin.json"] --> skills[".codex/skills/*"]\n';
1053
+ visualOutput += ' package["package.json"] --> cli["bin/genesis-harness.js"]\n';
1054
+ visualOutput += ' package --> verify["scripts/verify.sh"]\n';
1055
+ visualOutput += ' package --> evals["scripts/run-evals.sh"]\n';
1056
+ visualOutput += ' cli --> install["install / postinstall"]\n';
1057
+ visualOutput += ' cli --> hooks["setup-hooks"]\n';
1058
+ visualOutput += ' hooks --> docsgate["genesis-harness docs-gate"]\n';
1059
+ visualOutput += ' docsgate --> docsync["check-docs-sync.sh"]\n';
1060
+ visualOutput += ' docsgate --> specsync["check-spec-changelog.sh"]\n';
1061
+ visualOutput += ' skills --> contracts["contracts/"]\n';
1062
+ visualOutput += ' skills --> fixtures["fixtures/"]\n';
1063
+ visualOutput += ' skills --> tests["tests/ + playwright/"]\n';
1064
+ visualOutput += ' skills --> memory[".codebase/"]\n';
1065
+ visualOutput += ' verify --> skills\n';
1066
+ visualOutput += ' verify --> contracts\n';
1067
+ visualOutput += ' verify --> fixtures\n';
1068
+ visualOutput += ' verify --> memory\n';
1069
+ visualOutput += ' evals --> install\n';
1070
+ visualOutput += ' evals --> cli\n';
1071
+ visualOutput += ' evals --> unit["tests/unit/*.test.js"]\n';
1072
+ visualOutput += ' evals --> integration["tests/integration/*.test.js"]\n';
1073
+ visualOutput += ' evals --> pack["npm pack smoke"]\n';
1074
+ visualOutput += '```\n\n';
1075
+
1076
+ visualOutput += '## Skill Workflow Relationships\n\n```mermaid\nflowchart TD\n';
1077
+ visualOutput += ' harness["genesis-harness"] --> planning["genesis-planning"]\n';
1078
+ visualOutput += ' harness --> research["genesis-research-first"]\n';
1079
+ visualOutput += ' planning --> architecture["genesis-architecture"]\n';
1080
+ visualOutput += ' planning --> api["genesis-api-contract"]\n';
1081
+ visualOutput += ' planning --> design["genesis-design-spec"]\n';
1082
+ visualOutput += ' api --> apisync["genesis-api-sync"]\n';
1083
+ visualOutput += ' design --> ui["genesis-ui-ux-test"]\n';
1084
+ visualOutput += ' api --> specimpact["spec-impact-engine"]\n';
1085
+ visualOutput += ' specimpact --> specprop["genesis-spec-propagation"]\n';
1086
+ visualOutput += ' specprop --> docs["genesis-docs-automation"]\n';
1087
+ visualOutput += ' ui --> verifybefore["genesis-verification-before-completion"]\n';
1088
+ visualOutput += ' apisync --> verifybefore\n';
1089
+ visualOutput += ' docs --> verifybefore\n';
1090
+ visualOutput += ' verifybefore --> release["genesis-release"]\n';
1091
+ visualOutput += ' harness --> memorymap["genesis-codebase-map"]\n';
1092
+ visualOutput += ' harness --> observability["genesis-observability-automation"]\n';
1093
+ visualOutput += '```\n\n';
1094
+
1095
+ visualOutput += '## Code Dependency Hints\n\n```mermaid\nflowchart TD\n';
1096
+ if (depEdges.length > 0) {
1097
+ visualOutput += depEdges.join('\n') + '\n';
1098
+ } else {
1099
+ visualOutput += ' Root["No dependencies found"]\n';
1100
+ }
1101
+ visualOutput += '```\n\n';
1102
+
1103
+ // Parse Roadmap for features and roles
1104
+ const roadmapFile = path.join(process.cwd(), '.planning', 'ROADMAP.md');
1105
+ if (fs.existsSync(roadmapFile)) {
1106
+ visualOutput += '## .planning/ROADMAP.md Derived Feature Status\n\n```mermaid\ngraph TD\n';
1107
+ visualOutput += ' classDef completed fill:#d4edda,stroke:#28a745,stroke-width:2px;\n';
1108
+ visualOutput += ' classDef inprogress fill:#fff3cd,stroke:#ffc107,stroke-width:2px;\n';
1109
+ visualOutput += ' classDef pending fill:#e2e3e5,stroke:#6c757d,stroke-width:2px;\n';
1110
+
1111
+ const rmContent = fs.readFileSync(roadmapFile, 'utf8').split('\n');
1112
+ const roles = [];
1113
+ let currentRoleObj = { title: 'General', tasks: [] };
1114
+
1115
+ let taskIdCounter = 0;
1116
+ const allTasksMap = new Map();
1117
+
1118
+ rmContent.forEach(line => {
1119
+ if (line.match(/^#+\s+(.+)/)) {
1120
+ const title = line.match(/^#+\s+(.+)/)[1].trim();
1121
+ // If switching roles, push the current one if it has tasks
1122
+ if (currentRoleObj.tasks.length > 0) {
1123
+ roles.push(currentRoleObj);
1124
+ }
1125
+ currentRoleObj = { title: title, tasks: [] };
1126
+ } else if (line.match(/^-\s*\[([ xX~!\/])\]\s+(.+)/)) {
1127
+ const match = line.match(/^-\s*\[([ xX~!\/])\]\s+(.+)/);
1128
+ const statusChar = match[1].toLowerCase();
1129
+ let rawName = match[2].trim();
1130
+ let dependsOn = [];
1131
+ let mappedFiles = [];
1132
+
1133
+ const depMatch = rawName.match(/\(depends_on:\s*(.+?)\)/i);
1134
+ if (depMatch) {
1135
+ dependsOn = depMatch[1].split(',').map(s => s.trim());
1136
+ rawName = rawName.replace(/\(depends_on:\s*.+?\)/i, '').trim();
1137
+ }
1138
+
1139
+ const filesMatch = rawName.match(/\(files:\s*(.+?)\)/i);
1140
+ if (filesMatch) {
1141
+ mappedFiles = filesMatch[1].split(',').map(s => s.trim());
1142
+ rawName = rawName.replace(/\(files:\s*.+?\)/i, '').trim();
1143
+ }
1144
+
1145
+ const taskId = `Task${taskIdCounter++}`;
1146
+ allTasksMap.set(rawName.toLowerCase(), taskId);
1147
+
1148
+ currentRoleObj.tasks.push({
1149
+ id: taskId,
1150
+ statusChar: statusChar,
1151
+ name: rawName,
1152
+ dependsOn: dependsOn,
1153
+ mappedFiles: mappedFiles
1154
+ });
1155
+ }
1156
+ });
1157
+ if (currentRoleObj.tasks.length > 0) {
1158
+ roles.push(currentRoleObj);
1159
+ }
1160
+
1161
+ if (roles.length === 0) {
1162
+ visualOutput += ' Project["Project Roadmap"] --> NoTasks["No tasks found"]\n';
1163
+ } else {
1164
+ roles.forEach((r, idx) => {
1165
+ visualOutput += ` subgraph Role_${idx} ["${r.title}"]\n`;
1166
+ r.tasks.forEach(t => {
1167
+ let label = `Roadmap task ${t.id.replace('Task', '')}`;
1168
+ visualOutput += ` ${t.id}["${label}"]\n`;
1169
+ if (t.statusChar === 'x') {
1170
+ visualOutput += ` class ${t.id} completed;\n`;
1171
+ } else if (t.statusChar === '/' || t.statusChar === '~' || t.statusChar === '!') {
1172
+ visualOutput += ` class ${t.id} inprogress;\n`;
1173
+ } else {
1174
+ visualOutput += ` class ${t.id} pending;\n`;
1175
+ }
1176
+ });
1177
+ visualOutput += ` end\n`;
1178
+ });
1179
+
1180
+ // Draw dependencies
1181
+ roles.forEach(r => {
1182
+ r.tasks.forEach(t => {
1183
+ if (t.dependsOn.length > 0) {
1184
+ t.dependsOn.forEach(depName => {
1185
+ const depId = allTasksMap.get(depName.toLowerCase());
1186
+ if (depId) {
1187
+ visualOutput += ` ${depId} --> ${t.id}\n`;
1188
+ }
1189
+ });
1190
+ }
1191
+ });
1192
+ });
1193
+ }
1194
+ visualOutput += '```\n\n';
1195
+
1196
+ // Đưa Roadmap vào COMPRESSED_CONTEXT.md cho AI đọc (Dạng text thuần)
1197
+ output += '\n## Project Planning & Roadmap\n';
1198
+ output += rmContent.join('\n') + '\n';
1199
+ }
1200
+
1201
+ fs.writeFileSync(contextFile, output);
1202
+ fs.writeFileSync(visualFile, visualOutput);
1203
+ console.log('[genesis-harness] Context compressed and saved to ' + contextFile);
1204
+ console.log('[genesis-harness] Visual Graph saved to ' + visualFile);
1205
+ }
1206
+
1207
+ function setupHooks(rootPath = process.cwd()) {
1208
+ if (!rootPath) {
1209
+ console.log('[genesis-harness] Project root not detected, skipping hooks setup.');
1210
+ return;
1211
+ }
1212
+
1213
+ const hooksDir = path.join(rootPath, '.git', 'hooks');
1214
+ const preCommitFile = path.join(hooksDir, 'pre-commit');
1215
+
1216
+ if (!fs.existsSync(hooksDir)) {
1217
+ console.log('[genesis-harness] Not a git repository, skipping hooks setup.');
1218
+ return;
1219
+ }
1220
+
1221
+ const hookContent = `#!/bin/sh
1222
+ # genesis-harness auto-sync
1223
+ echo "[genesis-harness] Syncing compressed context before commit..."
1224
+ npx genesis-harness sync
1225
+ echo "[genesis-harness] Running docs drift gate..."
1226
+ npx genesis-harness docs-gate
1227
+ git add .codebase/COMPRESSED_CONTEXT.md .codebase/VISUAL_GRAPH.md 2>/dev/null || true
1228
+ `;
1229
+
1230
+ if (fs.existsSync(preCommitFile)) {
1231
+ const existingContent = fs.readFileSync(preCommitFile, 'utf8');
1232
+ if (existingContent !== hookContent) {
1233
+ const backupPath = preCommitFile + '.backup.' + Date.now();
1234
+ fs.renameSync(preCommitFile, backupPath);
1235
+ console.log('[genesis-harness] Existing pre-commit hook backed up to ' + backupPath);
1236
+ } else {
1237
+ console.log('[genesis-harness] Git hooks already up to date.');
1238
+ return;
1239
+ }
1240
+ }
1241
+
1242
+ fs.writeFileSync(preCommitFile, hookContent);
1243
+ fs.chmodSync(preCommitFile, '755');
1244
+ console.log('[genesis-harness] Git pre-commit hook installed successfully.');
1245
+ }
1246
+
1247
+ function runDocsGate() {
1248
+ const docsSyncScript = path.join(packageRoot, ".codex", "skills", "genesis-harness", "scripts", "check-docs-sync.sh");
1249
+ const specChangelogScript = path.join(packageRoot, ".codex", "skills", "genesis-harness", "scripts", "check-spec-changelog.sh");
1250
+ const bash = resolveBash();
1251
+
1252
+ if (!fs.existsSync(docsSyncScript)) fail(`missing docs sync gate at ${docsSyncScript}`);
1253
+
1254
+ const docsResult = spawnSync(bash, [docsSyncScript, process.cwd()], {
1255
+ stdio: "inherit",
1256
+ env: process.env
1257
+ });
1258
+ if (docsResult.status) process.exit(docsResult.status);
1259
+
1260
+ if (fs.existsSync(path.join(process.cwd(), ".planning", "SPEC_CHANGELOG.md")) && fs.existsSync(specChangelogScript)) {
1261
+ const specResult = spawnSync(bash, [specChangelogScript, process.cwd()], {
1262
+ stdio: "inherit",
1263
+ env: process.env
1264
+ });
1265
+ if (specResult.status) process.exit(specResult.status);
1266
+ }
1267
+ }
1268
+
1269
+ /**
1270
+ * runVerifyGate() — L09 Victory Blocker
1271
+ *
1272
+ * Runs ALL required verification gates in sequence.
1273
+ * Agent MUST call this before claiming any task is done.
1274
+ * Exits with non-zero if any gate fails — prevents "under-finish" hallucination.
1275
+ */
1276
+ function runVerifyGate() {
1277
+ const bash = resolveBash();
1278
+ const verifyScript = path.join(packageRoot, "scripts", "verify.sh");
1279
+ const evalsScript = path.join(packageRoot, "scripts", "run-evals.sh");
1280
+ const coldStartScript = path.join(packageRoot, "scripts", "cold-start-check.js");
1281
+
1282
+ console.log("\x1b[1m\x1b[36m══════════════════════════════════════════════════════\x1b[0m");
1283
+ console.log("\x1b[1m\x1b[36m GENESIS HARNESS — VERIFY-GATE (L09 Victory Blocker) \x1b[0m");
1284
+ console.log("\x1b[1m\x1b[36m══════════════════════════════════════════════════════\x1b[0m");
1285
+ console.log("\x1b[33mRunning all verification gates. Task is NOT done until all pass.\x1b[0m\n");
1286
+
1287
+ const gates = [
1288
+ {
1289
+ name: "1. Structural verify (verify.sh)",
1290
+ run: () => spawnSync(bash, [verifyScript], { stdio: "inherit", env: process.env }).status
1291
+ },
1292
+ {
1293
+ name: "2. Feature registry + observability (feature_registry.test.js)",
1294
+ run: () => spawnSync(process.execPath, [
1295
+ path.join(packageRoot, "tests", "unit", "feature_registry.test.js")
1296
+ ], { stdio: "inherit", env: process.env }).status
1297
+ },
1298
+ {
1299
+ name: "3. Cold-start check (cold-start-check.js)",
1300
+ run: () => fs.existsSync(coldStartScript)
1301
+ ? spawnSync(process.execPath, [coldStartScript], { stdio: "inherit", env: process.env }).status
1302
+ : 0
1303
+ },
1304
+ {
1305
+ name: "4. Unit tests (tests/unit/*.test.js)",
1306
+ run: () => {
1307
+ const unitDir = path.join(packageRoot, "tests", "unit");
1308
+ if (!fs.existsSync(unitDir)) return 0;
1309
+ for (const f of fs.readdirSync(unitDir).filter(f => f.endsWith(".test.js"))) {
1310
+ const result = spawnSync(process.execPath, [path.join(unitDir, f)], {
1311
+ stdio: "inherit", env: process.env
1312
+ });
1313
+ if (result.status) return result.status;
1314
+ }
1315
+ return 0;
1316
+ }
1317
+ }
1318
+ ];
1319
+
1320
+ let allPassed = true;
1321
+ for (const gate of gates) {
1322
+ process.stdout.write(`\n\x1b[33m▶ ${gate.name}\x1b[0m\n`);
1323
+ const code = gate.run();
1324
+ if (code !== 0) {
1325
+ console.log(`\x1b[31m✗ FAILED (exit ${code})\x1b[0m`);
1326
+ allPassed = false;
1327
+ break; // Stop on first failure
1328
+ }
1329
+ console.log(`\x1b[32m✓ PASSED\x1b[0m`);
1330
+ }
1331
+
1332
+ console.log("\n\x1b[1m\x1b[36m══════════════════════════════════════════════════════\x1b[0m");
1333
+ if (allPassed) {
1334
+ console.log("\x1b[1m\x1b[32m✓ ALL GATES PASSED — Task may now be declared DONE.\x1b[0m");
1335
+ console.log("\x1b[32mUpdate .codebase/CURRENT_STATE.md and RECOVERY_POINTS.md.\x1b[0m");
1336
+ } else {
1337
+ console.log("\x1b[1m\x1b[31m✗ VERIFICATION FAILED — Do NOT declare this task done.\x1b[0m");
1338
+ console.log("\x1b[31mFix the failing gate, then re-run: genesis-harness verify-gate\x1b[0m");
1339
+ process.exit(1);
1340
+ }
1341
+ console.log("\x1b[1m\x1b[36m══════════════════════════════════════════════════════\x1b[0m\n");
1342
+ }
1343
+
1344
+ function runColdStart() {
1345
+ const coldStartScript = path.join(packageRoot, "scripts", "cold-start-check.js");
1346
+ if (fs.existsSync(coldStartScript)) {
1347
+ const result = spawnSync(process.execPath, [coldStartScript], { stdio: "inherit", env: process.env });
1348
+ process.exit(result.status);
1349
+ } else {
1350
+ console.error("Cold start script not found.");
1351
+ process.exit(1);
1352
+ }
1353
+ }
1354
+
1355
+ function healTest(testCommand) {
1356
+ if (!testCommand) {
1357
+ console.error('[genesis-harness] Missing test command. Usage: genesis-harness heal "npm test"');
1358
+ process.exit(1);
1359
+ }
1360
+
1361
+ const { execSync } = require('child_process');
1362
+ try {
1363
+ console.log(`[genesis-harness] Running test: ${testCommand}`);
1364
+ execSync(testCommand, { stdio: 'inherit' });
1365
+ console.log('[genesis-harness] Tests passed! No healing required.');
1366
+ } catch (error) {
1367
+ const errorLog = error.stdout ? error.stdout.toString() : error.message;
1368
+ const planningDir = path.join(process.cwd(), '.planning');
1369
+ if (!fs.existsSync(planningDir)) {
1370
+ fs.mkdirSync(planningDir, { recursive: true });
1371
+ }
1372
+ const logFile = path.join(planningDir, 'LAST_ERROR.log');
1373
+ fs.writeFileSync(logFile, errorLog);
1374
+
1375
+ console.log('\\n\\x1b[31m\\x1b[1m[AGENT_DIRECTIVE] TESTS FAILED. READ .planning/LAST_ERROR.log AND IMMEDIATELY EXECUTE genesis-debug-guide TO FIX THE CODE. DO NOT STOP UNTIL TESTS PASS.\\x1b[0m\\n');
1376
+ process.exit(1);
1377
+ }
1378
+ }
1379
+
1380
+ function mcpSetupInteractive() {
1381
+ const options = [
1382
+ { name: "@modelcontextprotocol/server-puppeteer", desc: "Browser UI Testing", selected: true },
1383
+ { name: "@modelcontextprotocol/server-fetch", desc: "URL Markdown Reader", selected: true },
1384
+ { name: "@modelcontextprotocol/server-github", desc: "Repo & PR management", selected: false },
1385
+ { name: "@modelcontextprotocol/server-memory", desc: "Knowledge Graph Memory", selected: true },
1386
+ { name: "@modelcontextprotocol/server-sqlite", desc: "Vector Memory DB", selected: false }
1387
+ ];
1388
+
1389
+ let selectedIndex = 0;
1390
+
1391
+ const renderMenu = () => {
1392
+ console.clear();
1393
+ console.log("\x1b[1m\x1b[36m======================================================================\x1b[0m");
1394
+ console.log("\x1b[1m\x1b[36m GENESIS HARNESS - MCP INSTALLER \x1b[0m");
1395
+ console.log("\x1b[1m\x1b[36m======================================================================\x1b[0m\n");
1396
+ console.log(" \x1b[1mSelect which MCP Servers you want to install globally.\x1b[0m");
1397
+ console.log(" Use \x1b[33mUp/Down Arrow\x1b[0m to navigate.");
1398
+ console.log(" Use \x1b[33mSpace\x1b[0m to toggle selection.");
1399
+ console.log(" Press \x1b[32mEnter\x1b[0m to confirm and install.");
1400
+ console.log(" Press \x1b[90mEsc or Ctrl+C\x1b[0m to cancel.\n");
1401
+
1402
+ options.forEach((opt, idx) => {
1403
+ const checkbox = opt.selected ? "\x1b[32m[x]\x1b[0m" : "[ ]";
1404
+ const cursor = idx === selectedIndex ? "\x1b[1m\x1b[36m➔\x1b[0m " : " ";
1405
+ const name = idx === selectedIndex ? `\x1b[1m${opt.name}\x1b[0m` : opt.name;
1406
+ console.log(` ${cursor} ${checkbox} ${name.padEnd(50)} \x1b[90m(${opt.desc})\x1b[0m`);
1407
+ });
1408
+ console.log("\n\x1b[1m\x1b[36m======================================================================\x1b[0m");
1409
+ };
1410
+
1411
+ process.stdin.setRawMode(true);
1412
+ process.stdin.resume();
1413
+ process.stdin.setEncoding("utf8");
1414
+
1415
+ const cleanExit = () => {
1416
+ process.stdin.setRawMode(false);
1417
+ process.stdin.pause();
1418
+ console.clear();
1419
+ console.log("\n\x1b[33m[-] MCP Setup Cancelled.\x1b[0m\n");
1420
+ process.exit(0);
1421
+ };
1422
+
1423
+ const executeInstall = () => {
1424
+ process.stdin.setRawMode(false);
1425
+ process.stdin.pause();
1426
+ console.clear();
1427
+ const toInstall = options.filter(o => o.selected).map(o => o.name);
1428
+
1429
+ if (toInstall.length === 0) {
1430
+ console.log("\n\x1b[33m[-] No MCP servers selected. Exiting.\x1b[0m\n");
1431
+ process.exit(0);
1432
+ }
1433
+
1434
+ console.log(`\n\x1b[1m\x1b[32m[+] Installing selected MCP servers globally...\x1b[0m\n`);
1435
+ const args = ["install", "-g", ...toInstall];
1436
+ const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
1437
+
1438
+ const result = spawnSync(npmCmd, args, { stdio: "inherit", env: process.env });
1439
+ if (result.status === 0) {
1440
+ console.log(`\n\x1b[1m\x1b[32m✓ Successfully installed MCP servers.\x1b[0m`);
1441
+ console.log(`You can now configure your Agent Client to use them. See mcp.example.json.\n`);
1442
+ } else {
1443
+ console.error(`\n\x1b[1m\x1b[31m[-] Installation failed with status ${result.status}\x1b[0m\n`);
1444
+ }
1445
+ process.exit(result.status || 0);
1446
+ };
1447
+
1448
+ renderMenu();
1449
+
1450
+ process.stdin.on("data", (key) => {
1451
+ if (key === "\u0003" || key === "\u001b") { // Ctrl+C or Esc
1452
+ cleanExit();
1453
+ } else if (key === "\u001b[A") { // Up arrow
1454
+ selectedIndex = (selectedIndex - 1 + options.length) % options.length;
1455
+ renderMenu();
1456
+ } else if (key === "\u001b[B") { // Down arrow
1457
+ selectedIndex = (selectedIndex + 1) % options.length;
1458
+ renderMenu();
1459
+ } else if (key === " ") { // Space
1460
+ options[selectedIndex].selected = !options[selectedIndex].selected;
1461
+ renderMenu();
1462
+ } else if (key === "\r") { // Enter
1463
+ executeInstall();
1464
+ }
1465
+ });
1466
+ }
1467
+
767
1468
  const command = process.argv[2] || "help";
768
1469
  const args = process.argv.slice(3);
769
1470
 
770
1471
  switch (command) {
771
1472
  case "install":
772
1473
  copySkills({ target: parseTarget(args, "both") });
1474
+ seedLeanCtxPolicy(process.cwd());
1475
+ setupHooks();
773
1476
  break;
774
1477
  case "postinstall":
775
1478
  if (process.env.GENESIS_HARNESS_SKIP_POSTINSTALL === "1") {
776
1479
  process.exit(0);
777
1480
  }
778
1481
  copySkills({ quiet: true, target: "both" });
1482
+ const postinstallRoot = resolvePostinstallProjectRoot();
1483
+ seedLeanCtxPolicy(postinstallRoot, { quiet: true });
1484
+ setupHooks(postinstallRoot);
779
1485
  break;
780
1486
  case "verify":
781
1487
  verifySkill(parseTarget(args, "both"));
@@ -796,6 +1502,9 @@ switch (command) {
796
1502
  case "docs":
797
1503
  showDocsStatus();
798
1504
  break;
1505
+ case "docs-gate":
1506
+ runDocsGate();
1507
+ break;
799
1508
  case "remember":
800
1509
  rememberFact(args[0], args[1]);
801
1510
  break;
@@ -808,9 +1517,30 @@ switch (command) {
808
1517
  case "prime":
809
1518
  primeContext();
810
1519
  break;
1520
+ case "leanctx":
1521
+ showLeanCtx();
1522
+ break;
811
1523
  case "view-mockup":
812
1524
  viewMockupsInteractive(args[0]);
813
1525
  break;
1526
+ case "mcp":
1527
+ mcpSetupInteractive();
1528
+ break;
1529
+ case "sync":
1530
+ syncContext();
1531
+ break;
1532
+ case "setup-hooks":
1533
+ setupHooks();
1534
+ break;
1535
+ case "verify-gate":
1536
+ runVerifyGate();
1537
+ break;
1538
+ case "cold-start":
1539
+ runColdStart();
1540
+ break;
1541
+ case "heal":
1542
+ healTest(args.join(" "));
1543
+ break;
814
1544
  case "help":
815
1545
  case "--help":
816
1546
  case "-h":