gennady 0.7.0 → 0.8.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 (154) hide show
  1. package/ai/directives/coding/result-conventions.xml +25 -7
  2. package/ai/directives/sdd/audit.directive.xml +1 -1
  3. package/ai/directives/sdd/critic-protocol.xml +78 -0
  4. package/ai/directives/sdd/critic.directive.xml +158 -0
  5. package/ai/directives/sdd/phase-execution-protocol.xml +11 -9
  6. package/ai/skills/alt-opinion/SKILL.md +79 -0
  7. package/ai/skills/alt-opinion/opinion.prompt.md +11 -0
  8. package/ai/skills/alt-opinion/synth.prompt.md +8 -0
  9. package/ai/skills/sdd-audit/SKILL.md +13 -0
  10. package/ai/skills/sdd-check/SKILL.md +139 -0
  11. package/ai/skills/sdd-continue/SKILL.md +13 -0
  12. package/ai/skills/sdd-critic/SKILL.md +12 -0
  13. package/ai/skills/sdd-discover/SKILL.md +13 -0
  14. package/ai/skills/sdd-execute/SKILL.md +205 -0
  15. package/ai/skills/sdd-execute/scripts/README.md +84 -0
  16. package/ai/skills/sdd-execute/scripts/check-blockers.sh +158 -0
  17. package/ai/skills/sdd-execute/scripts/classify-scripts.js +111 -0
  18. package/ai/skills/sdd-execute/scripts/classify-scripts.ts +133 -0
  19. package/ai/skills/sdd-execute/scripts/extract-section.sh +219 -0
  20. package/ai/skills/sdd-execute/scripts/lint-artifacts.sh +143 -0
  21. package/ai/skills/sdd-execute/scripts/scan.sh +385 -0
  22. package/ai/skills/sdd-execute/scripts/sdd +114 -0
  23. package/ai/skills/sdd-execute/scripts/verify.sh +139 -0
  24. package/ai/skills/sdd-execute-batch/SKILL.md +213 -0
  25. package/ai/skills/sdd-fix/SKILL.md +13 -0
  26. package/ai/skills/sdd-infra/SKILL.md +13 -0
  27. package/ai/skills/sdd-module-decomposition/SKILL.md +13 -0
  28. package/ai/skills/sdd-scaffold/SKILL.md +13 -0
  29. package/ai/skills/sdd-setup/SKILL.md +13 -0
  30. package/dist/ai/directives/coding/result-conventions.xml +25 -7
  31. package/dist/ai/directives/sdd/audit.directive.xml +1 -1
  32. package/dist/ai/directives/sdd/critic-protocol.xml +78 -0
  33. package/dist/ai/directives/sdd/critic.directive.xml +158 -0
  34. package/dist/ai/directives/sdd/phase-execution-protocol.xml +11 -9
  35. package/dist/ai/skills/alt-opinion/SKILL.md +79 -0
  36. package/dist/ai/skills/alt-opinion/opinion.prompt.md +11 -0
  37. package/dist/ai/skills/alt-opinion/synth.prompt.md +8 -0
  38. package/dist/ai/skills/sdd-audit/SKILL.md +13 -0
  39. package/dist/ai/skills/sdd-check/SKILL.md +139 -0
  40. package/dist/ai/skills/sdd-continue/SKILL.md +13 -0
  41. package/dist/ai/skills/sdd-critic/SKILL.md +12 -0
  42. package/dist/ai/skills/sdd-discover/SKILL.md +13 -0
  43. package/dist/ai/skills/sdd-execute/SKILL.md +205 -0
  44. package/dist/ai/skills/sdd-execute/scripts/README.md +84 -0
  45. package/dist/ai/skills/sdd-execute/scripts/check-blockers.sh +158 -0
  46. package/dist/ai/skills/sdd-execute/scripts/classify-scripts.js +111 -0
  47. package/dist/ai/skills/sdd-execute/scripts/classify-scripts.ts +133 -0
  48. package/dist/ai/skills/sdd-execute/scripts/extract-section.sh +219 -0
  49. package/dist/ai/skills/sdd-execute/scripts/lint-artifacts.sh +143 -0
  50. package/dist/ai/skills/sdd-execute/scripts/scan.sh +385 -0
  51. package/dist/ai/skills/sdd-execute/scripts/sdd +114 -0
  52. package/dist/ai/skills/sdd-execute/scripts/verify.sh +139 -0
  53. package/dist/ai/skills/sdd-execute-batch/SKILL.md +213 -0
  54. package/dist/ai/skills/sdd-fix/SKILL.md +13 -0
  55. package/dist/ai/skills/sdd-infra/SKILL.md +13 -0
  56. package/dist/ai/skills/sdd-module-decomposition/SKILL.md +13 -0
  57. package/dist/ai/skills/sdd-scaffold/SKILL.md +13 -0
  58. package/dist/ai/skills/sdd-setup/SKILL.md +13 -0
  59. package/dist/chunks/_commonjsHelpers-DaMA6jEr.js +8 -0
  60. package/dist/chunks/{devtools-B-7ugZhF.js → devtools-CPZ0IUNM.js} +1 -1
  61. package/dist/chunks/help-5qCaicff.js +6 -0
  62. package/dist/chunks/help-B2ETH0j6.js +6 -0
  63. package/dist/chunks/help-BCDURaCQ.js +6 -0
  64. package/dist/chunks/help-BvgYOSyQ.js +6 -0
  65. package/dist/chunks/help-CSPhOoI9.js +8 -0
  66. package/dist/chunks/help-CV1vpwrh.js +6 -0
  67. package/dist/chunks/help-CaBNA6Qr.js +12 -0
  68. package/dist/chunks/help-CjkR6SB9.js +6 -0
  69. package/dist/chunks/help-Coi1l5u9.js +8 -0
  70. package/dist/chunks/help-CsuLpZn-.js +8 -0
  71. package/dist/chunks/help-DL_QBMwy.js +8 -0
  72. package/dist/chunks/help-DqBZ2xcp.js +6 -0
  73. package/dist/chunks/help-_Y_TN4DZ.js +6 -0
  74. package/dist/chunks/{help.cmd-B_G7EWzF.js → help.cmd-BseRtoBF.js} +10 -2
  75. package/dist/chunks/{index-zBnnuvLA.js → index-BU1AXJGA.js} +12 -7
  76. package/dist/chunks/{index-CXuhZzS3.js → index-BXWDYM_9.js} +1 -1
  77. package/dist/chunks/index-BgKyH0dB.js +658 -0
  78. package/dist/chunks/{index-CaahtXiM.js → index-BghL1-Nk.js} +2 -2
  79. package/dist/chunks/{index-BeL1Zcbg.js → index-BlqEQEGy.js} +1 -1
  80. package/dist/chunks/{index-D1qsi0Uc.js → index-CDM74qdR.js} +3 -3
  81. package/dist/chunks/{index-D9ceRUyB.js → index-CGsIRefC.js} +2 -2
  82. package/dist/chunks/{index-CiEM-8nJ.js → index-CIvkaAQ3.js} +2 -2
  83. package/dist/chunks/{index-UbHoePfr.js → index-CkczJ__m.js} +10 -10
  84. package/dist/chunks/index-CtJKSOL0.js +21 -0
  85. package/dist/chunks/index-DEzym4Ae.js +369 -0
  86. package/dist/chunks/{index-CVR66voe.js → index-DoaxOhQ8.js} +401 -393
  87. package/dist/chunks/index-Du1xR-qO.js +752 -0
  88. package/dist/chunks/{index-DU6jD7SS.js → index-OtehG7uM.js} +1 -1
  89. package/dist/chunks/index-ZM_FonZj.js +156 -0
  90. package/dist/chunks/{index-g1LXlp77.js → index-k0nToACp.js} +2 -2
  91. package/dist/chunks/{run-review-command.logic-DpkRoEi8.js → run-review-command.logic-BeYQin-I.js} +9 -9
  92. package/dist/chunks/services-IgZptnTi.js +2732 -0
  93. package/dist/chunks/shared-DL_1LG0K.js +753 -0
  94. package/dist/cli/cmd/agent-mon/help.d.ts +4 -0
  95. package/dist/cli/cmd/agents-rules/agents-rules.cmd.d.ts +8 -0
  96. package/dist/cli/cmd/agents-rules/index.d.ts +1 -0
  97. package/dist/cli/cmd/alt-opinion/help.d.ts +4 -0
  98. package/dist/cli/cmd/cat/help.d.ts +4 -0
  99. package/dist/cli/cmd/commit/help.d.ts +4 -0
  100. package/dist/cli/cmd/lint/checks/anchor-class-body.check.d.ts +11 -0
  101. package/dist/cli/cmd/lint/checks/invariant-count.check.d.ts +11 -0
  102. package/dist/cli/cmd/lint/checks/utils/glob-match.d.ts +15 -0
  103. package/dist/cli/cmd/lint/checks/utils/strip-strings-comments.d.ts +7 -0
  104. package/dist/cli/cmd/lint/help.d.ts +4 -0
  105. package/dist/cli/cmd/lint/lint.cmd.d.ts +1 -1
  106. package/dist/cli/cmd/lint/lint.types.d.ts +6 -0
  107. package/dist/cli/cmd/orient/core/build-index.d.ts +8 -0
  108. package/dist/cli/cmd/orient/core/damerau-levenshtein.d.ts +26 -0
  109. package/dist/cli/cmd/orient/core/extract-header.d.ts +8 -0
  110. package/dist/cli/cmd/orient/core/hints.d.ts +9 -0
  111. package/dist/cli/cmd/orient/core/query-consumer.d.ts +10 -0
  112. package/dist/cli/cmd/orient/core/query-entity.d.ts +11 -0
  113. package/dist/cli/cmd/orient/core/query-graph.d.ts +18 -0
  114. package/dist/cli/cmd/orient/core/query-keyword.d.ts +10 -0
  115. package/dist/cli/cmd/orient/core/query-spec.d.ts +15 -0
  116. package/dist/cli/cmd/orient/core/query-task.d.ts +8 -0
  117. package/dist/cli/cmd/orient/core/scan-files.d.ts +8 -0
  118. package/dist/cli/cmd/orient/help.d.ts +5 -0
  119. package/dist/cli/cmd/orient/index.d.ts +1 -0
  120. package/dist/cli/cmd/orient/orient.cmd.d.ts +7 -0
  121. package/dist/cli/cmd/orient/orient.types.d.ts +136 -0
  122. package/dist/cli/cmd/orient/render/render-detail.d.ts +10 -0
  123. package/dist/cli/cmd/orient/render/render-file-list.d.ts +15 -0
  124. package/dist/cli/cmd/orient/render/render-graph.d.ts +13 -0
  125. package/dist/cli/cmd/orient/render/render-search.d.ts +8 -0
  126. package/dist/cli/cmd/orient/render/render-specs.d.ts +13 -0
  127. package/dist/cli/cmd/orient/render/render-tree.d.ts +13 -0
  128. package/dist/cli/cmd/remote-console/help.d.ts +4 -0
  129. package/dist/cli/cmd/resolve-conflicts/help.d.ts +4 -0
  130. package/dist/cli/cmd/review/help.d.ts +4 -0
  131. package/dist/cli/cmd/review-issues/help.d.ts +4 -0
  132. package/dist/cli/cmd/review-verify/help.d.ts +4 -0
  133. package/dist/cli/cmd/sync/help.d.ts +4 -0
  134. package/dist/cli/cmd/sync/sync-core.d.ts +3 -2
  135. package/dist/cli/cmd/sync/sync-formatter.d.ts +8 -9
  136. package/dist/cli/cmd/sync/sync.cmd.d.ts +2 -21
  137. package/dist/cli/cmd/sync-skills/index.d.ts +1 -0
  138. package/dist/cli/cmd/sync-skills/sync-skills-core.d.ts +50 -0
  139. package/dist/cli/cmd/sync-skills/sync-skills-formatter.d.ts +13 -0
  140. package/dist/cli/cmd/sync-skills/sync-skills.cmd.d.ts +14 -0
  141. package/dist/cli/cmd/sync-skills/sync-skills.types.d.ts +56 -0
  142. package/dist/cli/cmd/vcs-reply/help.d.ts +4 -0
  143. package/dist/gennady.js +73 -18
  144. package/dist/index.js +5 -5
  145. package/dist/services/dbc/linter/implementations/ts/dbc-ts-ast-adapter.d.ts +3 -2
  146. package/dist/shared/common/sync/path-normalizer.d.ts +22 -0
  147. package/dist/shared/common/sync/sync-core.shared.d.ts +14 -0
  148. package/dist/shared/common/sync/sync-deps.type.d.ts +66 -0
  149. package/dist/shared/common/sync/sync-formatter.shared.d.ts +19 -0
  150. package/package.json +6 -5
  151. package/dist/chunks/index-DJpVmyp2.js +0 -176
  152. package/dist/chunks/index-KxSZKmZn.js +0 -540
  153. package/dist/chunks/services-CaLOhuLV.js +0 -2889
  154. package/dist/chunks/shared-Bjy30TeM.js +0 -665
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env -S node --import tsx
2
+ // @file: Heuristic npm script classifier — reads package.json, outputs discovered commands as JSON.
3
+ // @consumers: verify.sh
4
+ // @tasks: TSK-55
5
+ //
6
+ // Usage:
7
+ // classify-scripts.ts [--json] [project-root]
8
+ //
9
+ // Output:
10
+ // --json (default): JSON array [{name, body, classes}]
11
+ // no flag: human-readable summary
12
+
13
+ import { readFileSync } from 'node:fs';
14
+ import { resolve } from 'node:path';
15
+
16
+ type ScriptClass = 'typecheck' | 'gennady' | 'lint' | 'test' | 'format' | 'umbrella' | 'unknown';
17
+
18
+ interface ScriptEntry {
19
+ name: string;
20
+ body: string;
21
+ classes: ScriptClass[];
22
+ }
23
+
24
+ interface ClassifiedProject {
25
+ scripts: ScriptEntry[];
26
+ selected: Partial<Record<ScriptClass, string>>;
27
+ }
28
+
29
+ function hasCategory(body: string, category: string): boolean {
30
+ const patterns: Record<string, RegExp> = {
31
+ tsc: /\b(tsc|tsgo)\b/,
32
+ lint: /\b(eslint|mail-core-lint\b|biome check\b|standard\b)\b/,
33
+ test: /\b(jest|vitest|playwright test|mocha\b|--test\b)\b/,
34
+ format: /\b(prettier|biome format)\b/,
35
+ };
36
+ return patterns[category]?.test(body) ?? false;
37
+ }
38
+
39
+ function classify(name: string, body: string): ScriptClass[] {
40
+ const isUmbrellaName = /^(check|ci-check|check:all|verify)$/.test(name);
41
+ if (isUmbrellaName && body.length > 0) return ['umbrella'];
42
+
43
+ const tscCount = hasCategory(body, 'tsc') ? 1 : 0;
44
+ const lintCount = hasCategory(body, 'lint') ? 1 : 0;
45
+ const testCount = hasCategory(body, 'test') ? 1 : 0;
46
+ const hasGennady = /\bgennady\b/.test(body) || /lint:contracts/.test(body);
47
+ const hasChain = body.includes('&&');
48
+ const hasParallel = /[&]/.test(body) && /;\s*wait/.test(body);
49
+
50
+ if (tscCount + lintCount + testCount >= 2 && (hasChain || hasParallel)) return ['umbrella'];
51
+ if (hasGennady && hasChain) return ['umbrella'];
52
+
53
+ const classes: ScriptClass[] = [];
54
+ if (hasGennady) classes.push('gennady');
55
+
56
+ const typecheckName = /^(type-?check|typecheck|typecheck:|tsc)$/.test(name);
57
+ const typecheckBody = /\b(tsc|tsgo)\b/.test(body) && /--noEmit/.test(body);
58
+ if (
59
+ (typecheckName || typecheckBody || name === 'lint:ts') &&
60
+ !name.startsWith('build:') &&
61
+ !name.startsWith('prepublish')
62
+ )
63
+ classes.push('typecheck');
64
+
65
+ const lintName = /^(lint|lint:all|lint-check|eslint|mc:eslint|stylist:lint|stylelint)$/.test(
66
+ name
67
+ );
68
+ const lintBody = hasCategory(body, 'lint');
69
+ if (
70
+ (lintName || lintBody) &&
71
+ !name.startsWith('lint:fix') &&
72
+ !name.startsWith('lint:contracts') &&
73
+ !name.startsWith('lint:ts') &&
74
+ !hasGennady
75
+ )
76
+ classes.push('lint');
77
+
78
+ const testName = /^(test|test:|mc:test|mc:jest|jest)$/.test(name);
79
+ const testBody = hasCategory(body, 'test') || /\bnode\b.*--test\b/.test(body);
80
+ if (testName || (testBody && !name.startsWith('build:') && !name.startsWith('prepublish')))
81
+ classes.push('test');
82
+
83
+ const formatName = /^format(:?check)?$/.test(name);
84
+ const formatBody = hasCategory(body, 'format');
85
+ if (formatName || formatBody) classes.push('format');
86
+
87
+ return classes.length > 0 ? classes : ['unknown'];
88
+ }
89
+
90
+ function selectBest(entries: ScriptEntry[], cls: ScriptClass): string | undefined {
91
+ const candidates = entries.filter((e) => e.classes.includes(cls));
92
+ if (candidates.length === 0) return undefined;
93
+
94
+ // Prefer shorter, more specific names
95
+ const priority: Record<string, number> = {
96
+ typecheck: { 'type-check': 10, typecheck: 10, tsc: 5 },
97
+ gennady: { 'lint:contracts': 10 },
98
+ lint: { lint: 10, eslint: 7, 'lint:all': 5, 'lint-check': 5, 'mc:eslint': 3 },
99
+ test: { test: 10, 'test:unit': 7, 'mc:test': 5, 'mc:jest': 3, jest: 3 },
100
+ format: { 'format:check': 10, format: 5 },
101
+ };
102
+
103
+ const rank = (name: string) => priority[cls]?.[name] ?? 1;
104
+ candidates.sort((a, b) => rank(b.name) - rank(a.name));
105
+ return candidates[0]?.name;
106
+ }
107
+
108
+ // --- Main ---
109
+ const root = process.argv.slice(2).find((a) => !a.startsWith('-')) || process.cwd();
110
+ const pkgPath = resolve(root, 'package.json');
111
+
112
+ let pkg: { scripts?: Record<string, string> };
113
+ try {
114
+ pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
115
+ } catch {
116
+ console.error(`[classify-scripts] package.json not found at ${pkgPath}`);
117
+ process.exit(1);
118
+ }
119
+
120
+ const entries: ScriptEntry[] = Object.entries(pkg.scripts ?? {}).map(([name, body]) => ({
121
+ name,
122
+ body,
123
+ classes: classify(name, body),
124
+ }));
125
+
126
+ const selected: Partial<Record<ScriptClass, string>> = {};
127
+ for (const cls of ['typecheck', 'gennady', 'lint', 'test', 'format'] as ScriptClass[]) {
128
+ const best = selectBest(entries, cls);
129
+ if (best) selected[cls] = best;
130
+ }
131
+
132
+ const result: ClassifiedProject = { scripts: entries, selected };
133
+ console.log(JSON.stringify(result));
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/env bash
2
+ # @file: Extract a named section between <!--SECTION:<NAME>--> / <!--/SECTION:<NAME>--> markers from a markdown file.
3
+ # @consumers: sdd-execute, sdd-execute-batch (L2 orchestrator prompt-building); phase agents (rules extraction)
4
+ # @contract: AX_BASH_NO_SILENT_EMPTY — never produces empty stdout. On miss → emits actionable instruction for the orchestrator + exit ≠ 0.
5
+ #
6
+ # Anchor grammar (canonical, AX_TICKET_ANCHOR_FORMAT):
7
+ # Open marker: <!--SECTION:<NAME>-->
8
+ # Close marker: <!--/SECTION:<NAME>-->
9
+ # <NAME>: ^[A-Z][A-Z0-9_]*$ (uppercase, starts with letter, alnum + underscore only)
10
+ # No quotes, no spaces, no attributes — atomic identifiers only.
11
+ #
12
+ # Canonical names: META, PHASES_OVERVIEW, PHASE_P1, PHASE_P2, PHASE_P1_FIX, BDD, VERIFICATION, TEST_COVERAGE, EXECUTION_LOG.
13
+ #
14
+ # Usage:
15
+ # extract-section.sh <file> <NAME>
16
+ #
17
+ # Examples:
18
+ # extract-section.sh ticket.md META
19
+ # extract-section.sh ticket.md PHASE_P1
20
+ # extract-section.sh ticket.md EXECUTION_LOG
21
+ #
22
+ # Exit codes:
23
+ # 0 — section found, content emitted to stdout
24
+ # 1 — file not found / unreadable
25
+ # 2 — anchor markers not present in file
26
+ # 3 — START found but END missing, or vice versa (corrupted markers), or duplicate occurrences
27
+ # 4 — bad invocation (missing args, invalid NAME)
28
+
29
+ set -uo pipefail
30
+
31
+ PROG="extract-section"
32
+ NAME_REGEX='^[A-Z][A-Z0-9_]*$'
33
+
34
+ if [[ $# -ne 2 ]]; then
35
+ cat <<EOF
36
+ [$PROG] BAD_INVOCATION
37
+ expected: $PROG <file> <NAME>
38
+ got: $PROG $*
39
+
40
+ Required action (ORCHESTRATOR):
41
+ Pass exactly 2 arguments — file path and SECTION name.
42
+ NAME must match: $NAME_REGEX
43
+ Examples of valid names: META, PHASES_OVERVIEW, PHASE_P1, BDD, EXECUTION_LOG.
44
+ EOF
45
+ exit 4
46
+ fi
47
+
48
+ file="$1"
49
+ name="$2"
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # NAME validation
53
+ # ---------------------------------------------------------------------------
54
+
55
+ if [[ ! "$name" =~ $NAME_REGEX ]]; then
56
+ cat <<EOF
57
+ [$PROG] INVALID_NAME
58
+ name: $name
59
+ rule: $NAME_REGEX
60
+
61
+ Diagnosis: section name does not match canonical anchor grammar.
62
+
63
+ Required action (ORCHESTRATOR):
64
+ 1. Use uppercase letters, digits, and underscores only.
65
+ 2. Start with a letter (not a digit or underscore).
66
+ 3. No spaces, no quotes, no attributes inside the name.
67
+
68
+ Canonical names: META, PHASES_OVERVIEW, PHASE_P1, PHASE_P2, PHASE_P1_FIX,
69
+ BDD, VERIFICATION, TEST_COVERAGE, EXECUTION_LOG.
70
+
71
+ If you need attributes (kind, rules, etc.) — put them inside the section content,
72
+ NOT inside the anchor name. The anchor is an identifier only.
73
+ EOF
74
+ exit 4
75
+ fi
76
+
77
+ # ---------------------------------------------------------------------------
78
+ # File existence + readability
79
+ # ---------------------------------------------------------------------------
80
+
81
+ if [[ ! -f "$file" ]]; then
82
+ cat <<EOF
83
+ [$PROG] FILE_NOT_FOUND
84
+ file: $file
85
+
86
+ Required action (ORCHESTRATOR):
87
+ 1. Verify the file path argument (typos, wrong scope, wrong task-id).
88
+ 2. Check whether the ticket/spec exists at the expected location.
89
+ 3. If task ID resolves to a different path — fix the resolution layer, not this script.
90
+
91
+ DO NOT proceed to dispatch phase agent until the file is located.
92
+ EOF
93
+ exit 1
94
+ fi
95
+
96
+ if [[ ! -r "$file" ]]; then
97
+ cat <<EOF
98
+ [$PROG] FILE_NOT_READABLE
99
+ file: $file
100
+ perms: $(stat -f '%Sp' "$file" 2>/dev/null || stat -c '%A' "$file" 2>/dev/null || echo 'unknown')
101
+
102
+ Required action (ORCHESTRATOR):
103
+ Verify file permissions; chmod if needed.
104
+ EOF
105
+ exit 1
106
+ fi
107
+
108
+ # ---------------------------------------------------------------------------
109
+ # Marker scan — pre-flight before extraction
110
+ # ---------------------------------------------------------------------------
111
+
112
+ start_marker="<!--SECTION:${name}-->"
113
+ end_marker="<!--/SECTION:${name}-->"
114
+
115
+ start_count=$(grep -cF "$start_marker" "$file" || true)
116
+ end_count=$(grep -cF "$end_marker" "$file" || true)
117
+
118
+ if [[ "$start_count" -eq 0 && "$end_count" -eq 0 ]]; then
119
+ cat <<EOF
120
+ [$PROG] ANCHOR_NOT_FOUND
121
+ section: $name
122
+ file: $file
123
+ searched markers:
124
+ $start_marker
125
+ $end_marker
126
+
127
+ Diagnosis: file does not contain anchor markers for this section.
128
+
129
+ Possible causes:
130
+ 1. Ticket/spec scaffolded before anchor convention introduced (pre-2026-05-21).
131
+ 2. Anchor was deleted or corrupted during manual edit.
132
+ 3. Wrong section name — verify against canonical names
133
+ (META, PHASES_OVERVIEW, PHASE_P<N>, BDD, VERIFICATION, TEST_COVERAGE, EXECUTION_LOG).
134
+ 4. Section uses non-canonical name — review file structure.
135
+
136
+ Required action (ORCHESTRATOR, not phase agent):
137
+ 1. Read the file directly via Read tool.
138
+ 2. Verify the section exists by markdown header (e.g. ## 1. Meta, ### P1 — impl).
139
+ 3. If section exists in header form but lacks anchors → retrofit anchors per
140
+ AX_TICKET_ANCHOR_FORMAT in ~/Developer/gennady/ai/directives/sdd/scaffold.directive.xml.
141
+ 4. If section does not exist at all → escalate to operator: ticket needs
142
+ re-scaffolding or section content authoring.
143
+
144
+ DO NOT proceed to dispatch a phase agent until anchors are in place.
145
+ EOF
146
+ exit 2
147
+ fi
148
+
149
+ if [[ "$start_count" -ne "$end_count" ]]; then
150
+ cat <<EOF
151
+ [$PROG] ANCHOR_UNBALANCED
152
+ section: $name
153
+ file: $file
154
+ $start_marker count: $start_count
155
+ $end_marker count: $end_count
156
+
157
+ Diagnosis: markers are corrupted — counts of open and close markers do not match.
158
+
159
+ Required action (ORCHESTRATOR):
160
+ 1. Read the file.
161
+ 2. Locate the imbalanced marker via:
162
+ grep -nF '$start_marker' "$file"
163
+ grep -nF '$end_marker' "$file"
164
+ 3. Restore the missing one OR remove the duplicate one.
165
+ 4. Retry this script.
166
+
167
+ DO NOT proceed to dispatch a phase agent until markers are balanced.
168
+ EOF
169
+ exit 3
170
+ fi
171
+
172
+ if [[ "$start_count" -gt 1 ]]; then
173
+ cat <<EOF
174
+ [$PROG] ANCHOR_DUPLICATED
175
+ section: $name
176
+ file: $file
177
+ occurrences: $start_count
178
+
179
+ Diagnosis: the same section appears more than once in this file — ambiguous extraction.
180
+
181
+ Required action (ORCHESTRATOR):
182
+ 1. Read the file and identify duplicate sections.
183
+ 2. Either merge them or rename one to be unique (e.g. PHASE_P1 vs PHASE_P1_FIX).
184
+ 3. Retry this script.
185
+ EOF
186
+ exit 3
187
+ fi
188
+
189
+ # ---------------------------------------------------------------------------
190
+ # Extraction — markers verified present and balanced; emit content between them.
191
+ # Exclude the marker lines themselves from output.
192
+ # ---------------------------------------------------------------------------
193
+
194
+ content=$(awk -v start="$start_marker" -v end="$end_marker" '
195
+ $0 == start { in_block = 1; next }
196
+ $0 == end { in_block = 0; next }
197
+ in_block { print }
198
+ ' "$file")
199
+
200
+ if [[ -z "$content" ]]; then
201
+ cat <<EOF
202
+ [$PROG] ANCHOR_EMPTY
203
+ section: $name
204
+ file: $file
205
+
206
+ Diagnosis: markers are present and balanced, but the section between them is empty.
207
+
208
+ Required action (ORCHESTRATOR):
209
+ 1. Read the file.
210
+ 2. Determine whether the section is intentionally empty (rare) or accidentally
211
+ stripped during refactor.
212
+ 3. If accidental → re-author content.
213
+ 4. If intentional → use a different section reference; this one carries no payload.
214
+ EOF
215
+ exit 2
216
+ fi
217
+
218
+ printf '%s\n' "$content"
219
+ exit 0
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env bash
2
+ # @file: Run gennady lint on phase artifacts; parse output for success/failure (gennady exit-code unreliable).
3
+ # @consumers: phase agents (STEP_5_VERIFY); audit subagents (STEP_1 pre-pass cross-check)
4
+ # @contract: AX_BASH_NO_SILENT_EMPTY — never produces empty stdout. On miss → actionable instruction.
5
+ #
6
+ # Why this wrapper exists:
7
+ # - gennady CLI lives outside the project (canonical: ~/Developer/gennady/cli/gennady.ts).
8
+ # - gennady requires `node --experimental-strip-types` (Node 22+) — the bare `node` invocation is non-obvious.
9
+ # - gennady returns exit code 0 even when lint reports errors (the failure signal is the literal token
10
+ # "[linting → failed]" in stdout). We must parse output, not trust exit code.
11
+ # - Success signal is the literal token "[linting → clean] no errors".
12
+ #
13
+ # Usage:
14
+ # lint-artifacts.sh <file1> [<file2> ...]
15
+ #
16
+ # Exit codes:
17
+ # 0 — all files clean (gennady output contains "[linting → clean]" and NOT "[linting → failed]")
18
+ # 1 — gennady CLI not found / unable to invoke
19
+ # 2 — lint reported errors (one or more violations); full output emitted
20
+ # 3 — ambiguous output (neither "clean" nor "failed" token present); investigate gennady CLI changes
21
+ # 4 — bad invocation (no args)
22
+
23
+ set -uo pipefail
24
+
25
+ PROG="lint-artifacts"
26
+ GENNADY_CLI=~/Developer/gennady/cli/gennady.ts
27
+
28
+ if [[ $# -lt 1 ]]; then
29
+ cat <<EOF
30
+ [$PROG] BAD_INVOCATION
31
+ expected: $PROG <file1> [<file2> ...]
32
+ got: $PROG (no args)
33
+
34
+ Required action (ORCHESTRATOR / phase agent):
35
+ Pass at least one TypeScript file path to lint.
36
+ Typically: phase's Target Files list.
37
+ EOF
38
+ exit 4
39
+ fi
40
+
41
+ if [[ ! -f "$GENNADY_CLI" ]]; then
42
+ cat <<EOF
43
+ [$PROG] GENNADY_CLI_NOT_FOUND
44
+ expected at: $GENNADY_CLI
45
+
46
+ Diagnosis: the gennady AST DbC linter is unreachable from this environment.
47
+
48
+ Required action (ORCHESTRATOR):
49
+ 1. Verify the gennady project is checked out at ~/Developer/gennady.
50
+ 2. If gennady moved → update GENNADY_CLI variable in this script.
51
+ 3. If on a CI/sandbox without gennady → this is a HARD blocker; phase cannot verify.
52
+ Report to operator: cannot complete phase without DBC contract verification.
53
+
54
+ DO NOT proceed to EMIT_HANDOFF without gennady lint passing.
55
+ EOF
56
+ exit 1
57
+ fi
58
+
59
+ # ---------------------------------------------------------------------------
60
+ # Run gennady lint and capture output
61
+ # ---------------------------------------------------------------------------
62
+
63
+ # Use a temp file to capture full output; we need both grep on it and to emit on failure.
64
+ tmp_out=$(mktemp -t lint-artifacts.XXXXXX)
65
+ trap 'rm -f "$tmp_out"' EXIT
66
+
67
+ # Run gennady. We intentionally ignore its exit code (unreliable per contract above).
68
+ npx tsx "$GENNADY_CLI" lint "$@" > "$tmp_out" 2>&1 || true
69
+
70
+ has_clean=$(grep -c '\[linting → clean\]' "$tmp_out" 2>/dev/null || echo 0)
71
+ has_failed=$(grep -c '\[linting → failed\]' "$tmp_out" 2>/dev/null || echo 0)
72
+
73
+ # Convert to single-line numbers (grep -c with || echo 0 may produce odd output on some shells)
74
+ has_clean=${has_clean//[^0-9]/}
75
+ has_failed=${has_failed//[^0-9]/}
76
+ has_clean=${has_clean:-0}
77
+ has_failed=${has_failed:-0}
78
+
79
+ # ---------------------------------------------------------------------------
80
+ # Branch on tokens
81
+ # ---------------------------------------------------------------------------
82
+
83
+ if [[ "$has_failed" -gt 0 ]]; then
84
+ # Extract just the error lines (those with file:line: error: format) + final summary
85
+ cat <<EOF
86
+ [$PROG] LINT_FAILED
87
+ files lint'd: $#
88
+ gennady output token: [linting → failed]
89
+
90
+ Full lint output (gennady DBC AST findings):
91
+ EOF
92
+ # Print error lines and the failure summary line
93
+ grep -E '\[linting → failed\]|: error: |^---|^References:|^ ' "$tmp_out" | head -200
94
+ echo
95
+ cat <<EOF
96
+ ---
97
+
98
+ Required action (PHASE AGENT, before EMIT_HANDOFF):
99
+ 1. Read each error: format is <file>:<line>:<col>: error: <ERR_CODE>: <message>
100
+ 2. Common categories:
101
+ - ERR_DBC_LINT_PARAM_MISSING — add @param <name> <coherent-role-description>
102
+ - ERR_DBC_LINT_RETURNS_MISSING — add @returns <business-purpose-of-value>
103
+ - ERR_DBC_ORDER — reorder JSDoc tags per AX_BASE_CONTRACT_SHAPE:
104
+ @purpose → @consumers → @implements → @invariant → @pre →
105
+ @param → @throws → @returns → @post → @sideEffect
106
+ 3. Fix all violations in the target files.
107
+ 4. Re-run this script. Repeat until clean.
108
+ 5. Do NOT EMIT_HANDOFF with lint failures present — that is fabricated DONE.
109
+
110
+ References:
111
+ /Users/k.lebedev/Developer/vkt/ai/directives/coding/typescript-rules.xml
112
+ — AX_TAG_USAGE_MATRIX, AX_BASE_CONTRACT_SHAPE, AX_FLAT_JSDOC_FOR_PROPERTIES
113
+ EOF
114
+ exit 2
115
+ fi
116
+
117
+ if [[ "$has_clean" -gt 0 ]]; then
118
+ # Clean. Emit concise PASS line to keep parent context light.
119
+ file_count=$#
120
+ echo "[$PROG] LINT_PASS"
121
+ echo " files lint'd: $file_count"
122
+ echo " gennady output token: [linting → clean]"
123
+ exit 0
124
+ fi
125
+
126
+ # Neither token found — gennady output format may have changed.
127
+ cat <<EOF
128
+ [$PROG] AMBIGUOUS_OUTPUT
129
+ files lint'd: $#
130
+ gennady output token: (neither "linting → clean" nor "linting → failed" found)
131
+
132
+ Diagnosis: gennady CLI output format may have changed (or there is an unhandled edge case).
133
+
134
+ Captured output:
135
+ $(cat "$tmp_out" | head -50)
136
+
137
+ Required action (ORCHESTRATOR):
138
+ 1. Re-read gennady source: ~/Developer/gennady/cli/gennady.ts
139
+ 2. Check command output tokens in the LintCommand implementation.
140
+ 3. Update this script's parsing to match new tokens.
141
+ 4. Until resolved → treat as a HARD blocker; DO NOT assume PASS.
142
+ EOF
143
+ exit 3