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,50 @@
1
+ import type { SyncCmdDeps } from '../../../shared/common/sync/sync-deps.type.js';
2
+ import { SyncSkillsResult } from './sync-skills.types.js';
3
+ import type { SyncSkillsFileEntry, SyncSkillsOptions } from './sync-skills.types.js';
4
+ /**
5
+ * @purpose Recursively scan sourceDir for skill directories and return a map of skillName → {relativePath → Buffer}.
6
+ * @param sourceDir Source directory (ai/skills/).
7
+ * @param [skillNames] Optional filter: only scan these skill names.
8
+ * @throws If a requested skillName does not exist in sourceDir.
9
+ * @returns Map of skill names to their file contents.
10
+ */
11
+ export declare function scanSkills(sourceDir: string, skillNames?: string[]): Map<string, Map<string, Buffer>>;
12
+ /**
13
+ * @purpose Compare a single source file with its target counterpart and produce a file entry.
14
+ * @param skillName Skill directory name.
15
+ * @param relativePath File path relative to skill root.
16
+ * @param sourceData Source file contents.
17
+ * @param targetData Target file contents (undefined if absent).
18
+ * @param targetSkillDir Absolute path to target skill directory.
19
+ * @param dryRun When true, skips actual file writes.
20
+ * @param writeFile Injectable file writer.
21
+ * @param mkdir Injectable directory creator.
22
+ * @returns SyncSkillsFileEntry with comparison result.
23
+ */
24
+ export declare function syncFile(skillName: string, relativePath: string, sourceData: Buffer, targetData: Buffer | undefined, targetSkillDir: string, dryRun: boolean, writeFile: (path: string, data: Buffer) => void, mkdir: (path: string, opts?: {
25
+ recursive: boolean;
26
+ }) => void): SyncSkillsFileEntry;
27
+ /**
28
+ * @purpose Recursively collect file paths in an orphan skill directory for deletion reporting.
29
+ * @param dir Orphan skill directory path.
30
+ * @param deps Injectable filesystem dependencies.
31
+ * @returns Array of relative file paths inside the orphan directory.
32
+ */
33
+ export declare function collectOrphanFiles(dir: string, deps: SyncCmdDeps): string[];
34
+ /**
35
+ * @purpose Delete a single orphan skill from the target directory.
36
+ * @param skillName Orphan skill name.
37
+ * @param targetDir Absolute path to the target skills directory.
38
+ * @param dryRun When true, only produces preview entries.
39
+ * @param deps Injectable filesystem dependencies.
40
+ * @returns Array of SyncSkillsFileEntry for the deleted (or deleteFailed) skill.
41
+ */
42
+ export declare function deleteOrphan(skillName: string, targetDir: string, dryRun: boolean, deps: SyncCmdDeps): SyncSkillsFileEntry[];
43
+ /**
44
+ * @purpose Main entry point: compare source skills with target, handle orphan deletion.
45
+ * @param deps Injectable filesystem dependencies (SyncCmdDeps).
46
+ * @param opts Sync-skills options.
47
+ * @throws On invalid sourceDir, unwritable target, or write failures.
48
+ * @returns SyncSkillsResult with entries and computed summaries.
49
+ */
50
+ export declare function collectAndCompareSkills(deps: SyncCmdDeps, opts: SyncSkillsOptions): SyncSkillsResult;
@@ -0,0 +1,13 @@
1
+ import type { SyncSkillsFileEntry } from './sync-skills.types.js';
2
+ /** @purpose Formatting options for the sync-skills formatter. */
3
+ export type SyncSkillsFormatOptions = {
4
+ /** @purpose Preview mode — shows would-be actions without writing. */
5
+ dryRun?: boolean;
6
+ };
7
+ /**
8
+ * @purpose Format sync-skills entries into stdout lines grouped by skill with markers.
9
+ * @param entries List of sync-skills file entries.
10
+ * @param [opts] Formatting options — dryRun enables preview labels.
11
+ * @returns Array of formatted lines; the last line is the summary.
12
+ */
13
+ export declare function format(entries: SyncSkillsFileEntry[], opts?: SyncSkillsFormatOptions): string[];
@@ -0,0 +1,14 @@
1
+ import type { SyncCmdDeps } from '../../../shared/common/sync/sync-deps.type.js';
2
+ export type { SyncCmdDeps } from '../../../shared/common/sync/sync-deps.type.js';
3
+ /**
4
+ * @purpose CLI entry point for the sync-skills command.
5
+ * *
6
+ * Parses CLI arguments, resolves the package source directory,
7
+ * compares source with target, and outputs the result.
8
+ * *
9
+ * @param rawArgs Raw CLI arguments (typically process.argv).
10
+ * @param [deps] Optional injectable dependencies for testing.
11
+ * @throws {Error} On package not found or invalid subdirectory.
12
+ * @returns Exit code (0 success, 1 error).
13
+ */
14
+ export declare function run(rawArgs: string[], deps?: SyncCmdDeps): number;
@@ -0,0 +1,56 @@
1
+ /** @purpose Discriminated status of a synced skill file. */
2
+ export type SyncSkillsFileStatus = 'added' | 'updated' | 'deleted' | 'unchanged' | 'deleteFailed';
3
+ /** @purpose Options for the sync-skills command. */
4
+ export type SyncSkillsOptions = {
5
+ /** @purpose Absolute path to ai/skills/ in the npm package. */
6
+ sourceDir: string;
7
+ /** @purpose Absolute path to <cwd>/.claude/skills/. */
8
+ targetDir: string;
9
+ /** @purpose Optional filter: skill directory names. */
10
+ skillNames?: string[];
11
+ /** @purpose Preview without writing. */
12
+ dryRun?: boolean;
13
+ };
14
+ /** @purpose A single file entry in the sync-skills result. */
15
+ export type SyncSkillsFileEntry = {
16
+ /** @purpose Skill directory name (e.g., sdd-execute). */
17
+ skillName: string;
18
+ /** @purpose Path relative to the skill root (e.g., SKILL.md, scripts/verify.sh). */
19
+ relativePath: string;
20
+ /** @purpose Sync status. */
21
+ status: SyncSkillsFileStatus;
22
+ /** @purpose Size of the source file in bytes. */
23
+ sourceSize?: number;
24
+ /** @purpose Size of the target file in bytes (updated/unchanged only). */
25
+ targetSize?: number;
26
+ /** @purpose OS error code for deleteFailed entries. */
27
+ errorCode?: string;
28
+ };
29
+ /** @purpose Aggregated result of a sync-skills operation with computed summaries. */
30
+ export declare class SyncSkillsResult {
31
+ /** @purpose List of all synced file entries, sorted lexicographically by skillName then relativePath. */
32
+ readonly entries: SyncSkillsFileEntry[];
33
+ /**
34
+ * @purpose Construct a SyncSkillsResult from a list of entries.
35
+ * @param entries File entries.
36
+ */
37
+ constructor(entries: SyncSkillsFileEntry[]);
38
+ /** @purpose Files with status "added". | @returns Array of added entries. */
39
+ get added(): SyncSkillsFileEntry[];
40
+ /** @purpose Files with status "updated". | @returns Array of updated entries. */
41
+ get updated(): SyncSkillsFileEntry[];
42
+ /** @purpose Files with status "deleted". | @returns Array of deleted entries. */
43
+ get deleted(): SyncSkillsFileEntry[];
44
+ /** @purpose Files with status "unchanged". | @returns Array of unchanged entries. */
45
+ get unchanged(): SyncSkillsFileEntry[];
46
+ /** @purpose Files with status "deleteFailed". | @returns Array of delete-failed entries. */
47
+ get deleteFailed(): SyncSkillsFileEntry[];
48
+ /** @purpose Human-readable summary of sync counts. | @returns Summary string. */
49
+ get summary(): string;
50
+ /** @purpose Dry-run summary message. | @returns Dry-run message. */
51
+ get dryRunSummary(): string;
52
+ }
53
+ /** @purpose Error code: source directory not found. */
54
+ export declare const ERR_SKILLS_SOURCE_NOT_FOUND = "ERR_SKILLS_SOURCE_NOT_FOUND";
55
+ /** @purpose Error code: skill name not found in source. */
56
+ export declare const ERR_SKILLS_SKILL_NOT_FOUND = "ERR_SKILLS_SKILL_NOT_FOUND";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @purpose Print CLI help for the vcs-reply command.
3
+ */
4
+ export declare function printHelp(): void;
package/dist/gennady.js CHANGED
@@ -1,48 +1,103 @@
1
1
  #!/usr/bin/env node
2
- import { c as e } from "./chunks/shared-Bjy30TeM.js";
2
+ import { c as i } from "./chunks/shared-DL_1LG0K.js";
3
3
  import "node:fs";
4
4
  import "node:path";
5
5
  import "node:url";
6
- const i = "0.7.0", t = /* @__PURE__ */ new Set(["help", "--help", "-h"]), a = process.argv[2];
7
- (!a || t.has(a)) && (await import("./chunks/help.cmd-B_G7EWzF.js"), process.exit(0));
8
- e({ name: "gennady", version: i });
9
- switch (a) {
6
+ const s = "0.8.0", r = /* @__PURE__ */ new Set(["help", "--help", "-h"]), t = process.argv[2];
7
+ (!t || r.has(t)) && (await import("./chunks/help.cmd-BseRtoBF.js"), process.exit(0));
8
+ i({ name: "gennady", version: s });
9
+ const o = process.argv.slice(3);
10
+ if (o.some((a) => r.has(a))) {
11
+ let a = !1;
12
+ switch (t) {
13
+ case "cat":
14
+ await import("./chunks/help-CjkR6SB9.js").then((e) => e.printHelp()), a = !0;
15
+ break;
16
+ case "review":
17
+ await import("./chunks/help-B2ETH0j6.js").then((e) => e.printHelp()), a = !0;
18
+ break;
19
+ case "vcs-reply":
20
+ await import("./chunks/help-CSPhOoI9.js").then((e) => e.printHelp()), a = !0;
21
+ break;
22
+ case "review-verify":
23
+ await import("./chunks/help-BvgYOSyQ.js").then((e) => e.printHelp()), a = !0;
24
+ break;
25
+ case "review-issues":
26
+ await import("./chunks/help-_Y_TN4DZ.js").then((e) => e.printHelp()), a = !0;
27
+ break;
28
+ case "resolve-conflicts":
29
+ await import("./chunks/help-CsuLpZn-.js").then((e) => e.printHelp()), a = !0;
30
+ break;
31
+ case "commit":
32
+ await import("./chunks/help-5qCaicff.js").then((e) => e.printHelp()), a = !0;
33
+ break;
34
+ case "remote-console":
35
+ await import("./chunks/help-BCDURaCQ.js").then((e) => e.printHelp()), a = !0;
36
+ break;
37
+ case "lint":
38
+ await import("./chunks/help-DL_QBMwy.js").then((e) => e.printHelp()), a = !0;
39
+ break;
40
+ case "alt-opinion":
41
+ await import("./chunks/help-CaBNA6Qr.js").then((e) => e.printHelp()), a = !0;
42
+ break;
43
+ case "agent-mon":
44
+ await import("./chunks/help-Coi1l5u9.js").then((e) => e.printHelp()), a = !0;
45
+ break;
46
+ case "sync":
47
+ await import("./chunks/help-CV1vpwrh.js").then((e) => e.printHelp()), a = !0;
48
+ break;
49
+ case "orient":
50
+ await import("./chunks/help-DqBZ2xcp.js").then((e) => e.printHelp()), a = !0;
51
+ break;
52
+ }
53
+ a || console.error(`No help available for "${t}".`), process.exit(0);
54
+ }
55
+ switch (t) {
10
56
  case "cat":
11
- await import("./chunks/index-zBnnuvLA.js");
57
+ await import("./chunks/index-BU1AXJGA.js");
12
58
  break;
13
59
  case "review":
14
- await import("./chunks/index-CaahtXiM.js");
60
+ await import("./chunks/index-BghL1-Nk.js");
15
61
  break;
16
62
  case "vcs-reply":
17
- await import("./chunks/index-g1LXlp77.js");
63
+ await import("./chunks/index-k0nToACp.js");
18
64
  break;
19
65
  case "review-verify":
20
- await import("./chunks/index-DU6jD7SS.js");
66
+ await import("./chunks/index-OtehG7uM.js");
21
67
  break;
22
68
  case "review-issues":
23
- await import("./chunks/index-BeL1Zcbg.js");
69
+ await import("./chunks/index-BlqEQEGy.js");
24
70
  break;
25
71
  case "resolve-conflicts":
26
- await import("./chunks/index-UbHoePfr.js");
72
+ await import("./chunks/index-CkczJ__m.js");
27
73
  break;
28
74
  case "commit":
29
- await import("./chunks/index-D1qsi0Uc.js");
75
+ await import("./chunks/index-CDM74qdR.js");
30
76
  break;
31
77
  case "remote-console":
32
- await import("./chunks/index-D9ceRUyB.js");
78
+ await import("./chunks/index-CGsIRefC.js");
33
79
  break;
34
80
  case "lint":
35
- await import("./chunks/index-KxSZKmZn.js");
81
+ await import("./chunks/index-Du1xR-qO.js");
36
82
  break;
37
83
  case "alt-opinion":
38
- await import("./chunks/index-CXuhZzS3.js");
84
+ await import("./chunks/index-BXWDYM_9.js");
39
85
  break;
40
86
  case "agent-mon":
41
- await import("./chunks/index-CVR66voe.js");
87
+ await import("./chunks/index-DoaxOhQ8.js");
42
88
  break;
43
89
  case "sync":
44
- await import("./chunks/index-DJpVmyp2.js");
90
+ await import("./chunks/index-ZM_FonZj.js");
91
+ break;
92
+ case "sync-skills":
93
+ await import("./chunks/index-DEzym4Ae.js");
94
+ break;
95
+ case "orient":
96
+ await import("./chunks/index-BgKyH0dB.js");
97
+ break;
98
+ case "agents-rules":
99
+ await import("./chunks/index-CtJKSOL0.js");
45
100
  break;
46
101
  default:
47
- await import("./chunks/help.cmd-B_G7EWzF.js"), process.exit(0);
102
+ await import("./chunks/help.cmd-BseRtoBF.js"), process.exit(0);
48
103
  }
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { u, r as m } from "./chunks/shared-Bjy30TeM.js";
2
- import { G as y, i as d, j as A, k as R, m as f } from "./chunks/shared-Bjy30TeM.js";
3
- import { R as C, h as O, r as T, i as L, c as $ } from "./chunks/services-CaLOhuLV.js";
1
+ import { u, m } from "./chunks/shared-DL_1LG0K.js";
2
+ import { G as y, o as d, q as A, t as f, v as R } from "./chunks/shared-DL_1LG0K.js";
3
+ import { R as C, f as O, r as T, g as L, c as $ } from "./chunks/services-IgZptnTi.js";
4
4
  class h {
5
5
  /**
6
6
  * @purpose Create an instance with default settings (Ollama local).
@@ -175,10 +175,10 @@ export {
175
175
  O as connectRemoteConsoleClient,
176
176
  d as getProgrammingLanguage,
177
177
  A as getSysLang,
178
- R as parseGitDiff,
178
+ f as parseGitDiff,
179
179
  T as remoteConsoleClient,
180
180
  L as serializeRemoteConsoleArg,
181
181
  $ as startRemoteConsoleServer,
182
- f as unguard,
182
+ R as unguard,
183
183
  u as unguardOrThrow
184
184
  };
@@ -1,4 +1,4 @@
1
- import Parser from 'tree-sitter';
1
+ import type { default as Parser } from 'tree-sitter';
2
2
  import type { SyntaxNode } from 'tree-sitter';
3
3
  import type { DbcAstAdapter, DbcExportedEntity, DbcMember, DbcParamInfo, DbcParseResult, DbcSignatureInfo } from '../../dbc-ast-adapter.types.js';
4
4
  /** @purpose In-memory representation of JSDoc contract info before attaching to entities. */
@@ -26,9 +26,10 @@ export declare class DbcTsAstAdapter implements DbcAstAdapter {
26
26
  parseFile(filePath: string, content?: string): Promise<DbcParseResult>;
27
27
  /**
28
28
  * @purpose Lazy-initializes the tree-sitter Parser with the TypeScript grammar.
29
+ * Uses dynamic import so tree-sitter is only loaded when the adapter is actually used.
29
30
  * @returns Configured Parser instance.
30
31
  */
31
- protected _initializeParser(): Parser;
32
+ protected _initializeParser(): Promise<Parser>;
32
33
  /**
33
34
  * @purpose Walks the root AST node and extracts all exported entities,
34
35
  * skipping re-exports.
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @purpose One replacement rule: regex pattern → replacement string.
3
+ * @invariant `from` must have the global flag `g`.
4
+ */
5
+ export interface PathNormalizationRule {
6
+ /** @purpose Regex pattern to match. Must carry the `g` flag. */
7
+ from: RegExp;
8
+ /** @purpose Replacement string. */
9
+ to: string;
10
+ }
11
+ /**
12
+ * @purpose Applies replacement rules to file content sequentially.
13
+ * Rules are applied in array order — from specific to general to avoid conflicts.
14
+ * @param content Raw file content to normalize.
15
+ * @param rules Ordered list of replacement rules.
16
+ * @returns Normalized content with all dev paths replaced by production equivalents.
17
+ */
18
+ export declare function normalize(content: string, rules: readonly PathNormalizationRule[]): string;
19
+ /** @purpose Path rules for `sync` (ai/directives/): directive and CLI paths only. */
20
+ export declare const SYNC_PATH_RULES: readonly PathNormalizationRule[];
21
+ /** @purpose Path rules for `sync-skills` (ai/skills/): all rules, including skill path replacement. */
22
+ export declare const SYNC_SKILLS_PATH_RULES: readonly PathNormalizationRule[];
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @purpose Locate a subdirectory inside the installed gennady npm package.
3
+ * @param projectRoot Project root directory (contains node_modules/).
4
+ * @param subdir Subdirectory path inside the gennady package (e.g., 'ai/directives').
5
+ * @returns Absolute path or null if the package or subdirectory is not found.
6
+ */
7
+ export declare function resolvePackageDir(projectRoot: string, subdir: string): string | null;
8
+ /**
9
+ * @purpose Byte-level comparison of two buffers.
10
+ * @param a First buffer.
11
+ * @param b Second buffer.
12
+ * @returns false when buffers are byte-identical, true otherwise. Degrades to true on non-Buffer inputs.
13
+ */
14
+ export declare function compareBytes(a: Buffer | undefined | null, b: Buffer | undefined | null): boolean;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @purpose Injectable dependencies for sync CLI commands.
3
+ * @invariant All fields are optional to allow partial mocking in tests.
4
+ * @invariant unlink and rmdir are no-op by default in the sync command; sync-skills provides real implementations.
5
+ */
6
+ export type SyncCmdDeps = {
7
+ /**
8
+ * @purpose Read file from disk.
9
+ * @param path File path.
10
+ * @returns File contents as Buffer.
11
+ */
12
+ readFile?: (path: string) => Buffer;
13
+ /**
14
+ * @purpose Write file to disk.
15
+ * @param path File path.
16
+ * @param data File contents.
17
+ */
18
+ writeFile?: (path: string, data: Buffer) => void;
19
+ /**
20
+ * @purpose Create directory.
21
+ * @param path Directory path.
22
+ * @param [opts] Options.
23
+ */
24
+ mkdir?: (path: string, opts?: {
25
+ recursive: boolean;
26
+ }) => void;
27
+ /**
28
+ * @purpose Get file stats.
29
+ * @param path File path.
30
+ * @returns Stats object with isDirectory and isFile.
31
+ */
32
+ stat?: (path: string) => {
33
+ isDirectory(): boolean;
34
+ isFile(): boolean;
35
+ };
36
+ /**
37
+ * @purpose List directory contents.
38
+ * @param path Directory path.
39
+ * @returns File names.
40
+ */
41
+ readdir?: (path: string) => string[];
42
+ /**
43
+ * @purpose Resolve gennady package subdirectory path.
44
+ * @param projectRoot Project root directory.
45
+ * @param subdir Subdirectory inside the gennady package.
46
+ * @returns Absolute path or null if not found.
47
+ */
48
+ resolvePackageDir?: (projectRoot: string, subdir: string) => string | null;
49
+ /** @purpose Standard output stream. */
50
+ stdout?: NodeJS.WriteStream;
51
+ /** @purpose Standard error stream. */
52
+ stderr?: NodeJS.WriteStream;
53
+ /**
54
+ * @purpose Delete a file (no-op in sync, used by sync-skills).
55
+ * @param path File path.
56
+ */
57
+ unlink?: (path: string) => void;
58
+ /**
59
+ * @purpose Delete a directory recursively (no-op in sync, used by sync-skills).
60
+ * @param path Directory path.
61
+ * @param [options] Options.
62
+ */
63
+ rmdir?: (path: string, options?: {
64
+ recursive: boolean;
65
+ }) => void;
66
+ };
@@ -0,0 +1,19 @@
1
+ /** @purpose Entry shape for the shared formatter: status discriminator and relative path. */
2
+ export type SyncFormatEntry = {
3
+ /** @purpose Sync status discriminator. */
4
+ status: 'added' | 'updated' | 'deleted' | 'unchanged';
5
+ /** @purpose File path relative to the sync root. */
6
+ relativePath: string;
7
+ };
8
+ /** @purpose Options for formatSyncOutput: dryRun toggles preview labels. */
9
+ export type SyncFormatOptions = {
10
+ /** @purpose When true, outputs preview labels instead of writing. */
11
+ dryRun?: boolean;
12
+ };
13
+ /**
14
+ * @purpose Format a list of sync entries into stdout lines with markers and a summary.
15
+ * @param entries List of entries with status and relative path.
16
+ * @param [opts] Formatting options — dryRun enables preview labels.
17
+ * @returns Array of formatted lines; the last line is the summary.
18
+ */
19
+ export declare function formatSyncOutput(entries: SyncFormatEntry[], opts?: SyncFormatOptions): string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gennady",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "author": "Konstantin Lebedev <ibnrubaxa@gmail.com>",
5
5
  "description": "Gennady — General Extensible Neural Network Adaptive Data Yntelligence",
6
6
  "keywords": [
@@ -39,10 +39,11 @@
39
39
  "build:types": "tsc -p tsconfig.types.json && node --import tsx scripts/normalize-dts-imports.ts",
40
40
  "build:publish": "npm run build && npm run build:types && node --import tsx scripts/prepare-publish-artifacts.ts",
41
41
  "test": "node --import tsx --test --experimental-test-module-mocks",
42
+ "test:e2e": "GENNADY_E2E=1 node --import tsx --test --experimental-test-module-mocks cli/__tests__/e2e/*.test.ts",
42
43
  "test:watch": "tsx --test-loader 'cli/**/*.test.ts' 'shared/**/*.test.ts' 'services/**/*.test.ts'",
43
44
  "dev": "tsx cli/gennady.ts",
44
45
  "build": "vite build",
45
- "prepublishOnly": "npm run lint && npm run build:publish",
46
+ "prepublishOnly": "npm run lint && npm run test:e2e && npm run build:publish",
46
47
  "postpublish": "node --import tsx scripts/cleanup-publish-artifacts.ts",
47
48
  "publish-next": "node --import tsx scripts/publish-next.ts",
48
49
  "format": "prettier --write .",
@@ -64,8 +65,6 @@
64
65
  "prettier": "^3.4.2",
65
66
  "qrcode": "^1.5.4",
66
67
  "release-it": "20.0.1",
67
- "tree-sitter": "^0.22.4",
68
- "tree-sitter-typescript": "^0.23.2",
69
68
  "tsx": "^4.19.1",
70
69
  "typescript": "^5.7.2",
71
70
  "vite": "^6.0.0",
@@ -73,6 +72,8 @@
73
72
  },
74
73
  "dependencies": {
75
74
  "ink": "^7.0.3",
76
- "react": "^19.2.6"
75
+ "react": "^19.2.6",
76
+ "tree-sitter": "^0.22.4",
77
+ "tree-sitter-typescript": "^0.23.2"
77
78
  }
78
79
  }
@@ -1,176 +0,0 @@
1
- import { existsSync as E, readdirSync as D, statSync as m, mkdirSync as F, writeFileSync as P, readFileSync as C } from "node:fs";
2
- import { join as u, sep as L } from "node:path";
3
- import { fileURLToPath as v } from "node:url";
4
- import { l as O } from "./services-CaLOhuLV.js";
5
- import { p as x } from "./shared-Bjy30TeM.js";
6
- class I {
7
- /** @purpose List of all synced file entries. */
8
- entries;
9
- /**
10
- * @purpose Construct a SyncResult from a list of entries.
11
- * @param entries File entries.
12
- */
13
- constructor(t) {
14
- this.entries = t;
15
- }
16
- /** @purpose Files with status "added". | @returns Array of added entries. */
17
- get added() {
18
- return this.entries.filter((t) => t.status === "added");
19
- }
20
- /** @purpose Files with status "updated". | @returns Array of updated entries. */
21
- get updated() {
22
- return this.entries.filter((t) => t.status === "updated");
23
- }
24
- /** @purpose Files with status "unchanged". | @returns Array of unchanged entries. */
25
- get unchanged() {
26
- return this.entries.filter((t) => t.status === "unchanged");
27
- }
28
- /** @purpose Human-readable summary of added/updated/unchanged counts. | @returns Summary string. */
29
- get summary() {
30
- const t = this.added.length, n = this.updated.length, c = this.unchanged.length;
31
- return `Synced: ${t} added, ${n} updated, ${c} skipped (unchanged)`;
32
- }
33
- /** @purpose Dry-run summary message. | @returns Dry-run message. */
34
- get dryRunSummary() {
35
- return "Dry-run: no files written.";
36
- }
37
- }
38
- const w = "ERR_SYNC_SUBDIR_NOT_FOUND", T = "ERR_SYNC_SOURCE_NOT_FOUND", S = "ai/directives", N = /* @__PURE__ */ new Set([
39
- "architecture",
40
- "dbc-audit.directive.xml",
41
- "dev-review.directive.xml",
42
- "semantic-change-extractor.directive.xml"
43
- ]);
44
- function B(e) {
45
- const t = u(e, "node_modules", "gennady", S);
46
- if (E(t)) return t;
47
- try {
48
- const n = import.meta.resolve("gennady"), s = v(n).replace(/[/\\]dist[/\\].*$/, ""), r = u(s, S);
49
- if (E(r)) return r;
50
- } catch {
51
- }
52
- return null;
53
- }
54
- function b(e, t) {
55
- if (t && t.length > 0) {
56
- const c = D(e).filter(
57
- (r) => !N.has(r) && m(u(e, r)).isDirectory()
58
- );
59
- for (const r of t)
60
- if (!c.includes(r)) {
61
- const o = `ai/directives/${r}/ not found in package.
62
- Available: ${c.join(", ")}`, i = new Error(o);
63
- throw i.code = w, i;
64
- }
65
- const s = [];
66
- for (const r of t)
67
- y(u(e, r), r, s);
68
- return s.sort();
69
- }
70
- const n = [];
71
- return y(e, "", n), n.sort();
72
- }
73
- function y(e, t, n) {
74
- let c;
75
- try {
76
- c = D(e);
77
- } catch {
78
- return;
79
- }
80
- for (const s of c) {
81
- if (s.startsWith(".") || N.has(s)) continue;
82
- const r = u(e, s), o = t ? u(t, s) : s, i = m(r, { throwIfNoEntry: !1 });
83
- i && (i.isDirectory() ? y(r, o, n) : i.isFile() && n.push(o.split(L).join("/")));
84
- }
85
- }
86
- function W(e, t) {
87
- try {
88
- e.stat(t.sourceDir);
89
- } catch {
90
- const s = `Source directory not found: ${t.sourceDir}`, r = new Error(s);
91
- throw r.code = T, r;
92
- }
93
- const n = b(t.sourceDir, t.subdirs), c = [];
94
- for (const s of n) {
95
- const r = u(t.sourceDir, s), o = u(t.targetDir, s), i = e.readFile(r), l = i.length;
96
- let d = null;
97
- try {
98
- d = e.readFile(o);
99
- } catch {
100
- }
101
- let a;
102
- d === null ? a = "added" : Buffer.compare(i, d) === 0 ? a = "unchanged" : a = "updated", c.push({
103
- relativePath: s,
104
- status: a,
105
- sourceSize: l,
106
- targetSize: d?.length
107
- }), !t.dryRun && a !== "unchanged" && (e.mkdir(u(t.targetDir, s, ".."), { recursive: !0 }), e.writeFile(o, i));
108
- }
109
- return new I(c);
110
- }
111
- const K = "+", Y = "~", j = "=", G = "(unchanged)", H = "(would add)", M = "(would update)", z = "(unchanged, skip)";
112
- function V(e, t = {}) {
113
- const n = [];
114
- for (const o of e) {
115
- const { relativePath: i, status: l } = o;
116
- let d, a;
117
- l === "added" ? (d = K, a = t.dryRun ? H : "") : l === "updated" ? (d = Y, a = t.dryRun ? M : "") : (d = j, a = t.dryRun ? z : G);
118
- const g = i.padEnd(64);
119
- a ? n.push(` ${d} ${g} ${a}`) : n.push(` ${d} ${i}`);
120
- }
121
- const c = e.filter((o) => o.status === "added").length, s = e.filter((o) => o.status === "updated").length, r = e.filter((o) => o.status === "unchanged").length;
122
- return t.dryRun ? n.push("Dry-run: no files written.") : n.push(`Synced: ${c} added, ${s} updated, ${r} skipped (unchanged)`), n;
123
- }
124
- function X(e, t, n, c) {
125
- n.write(`Sync${t ? " (dry-run)" : ""}: ${c}
126
- `);
127
- const s = V(e, { dryRun: t });
128
- for (const r of s)
129
- n.write(r + `
130
- `);
131
- }
132
- function A(e, t) {
133
- const n = C, c = P, s = F, r = m, o = D, i = B, l = process.stdout, d = process.stderr, a = x(e, {
134
- dryRun: ["dry-run"]
135
- }), g = a.dryRun === !0 || a.dryRun === "true", _ = a._.filter(
136
- (f) => typeof f == "string" && f !== "sync"
137
- ), p = process.cwd(), R = i(p);
138
- if (!R)
139
- return d.write(`Error: gennady package not found. Install it locally: npm i -D gennady
140
- `), 1;
141
- const U = u(p, "ai", "directives"), $ = {
142
- sourceDir: R,
143
- targetDir: U,
144
- subdirs: _.length > 0 ? _ : void 0,
145
- dryRun: g
146
- };
147
- try {
148
- const h = W({
149
- readFile: n,
150
- writeFile: c,
151
- mkdir: s,
152
- stat: (k) => r(k),
153
- readdir: o,
154
- cwd: p
155
- }, $);
156
- return X(h.entries, g, l, p), 0;
157
- } catch (f) {
158
- const h = f;
159
- if (h.code === w)
160
- return d.write(`Error: ${h.message}
161
- `), 1;
162
- throw h;
163
- }
164
- }
165
- if (process.argv[1]) {
166
- const e = v(import.meta.url);
167
- if (e === process.argv[1] || e.endsWith(process.argv[1]))
168
- try {
169
- const t = A(process.argv);
170
- process.exit(t);
171
- } catch (t) {
172
- const n = new Error("[syncCmd] Self-execution failed", { cause: t });
173
- O.error("[syncCmd#run] [self-executing → failed]", { error: n }), process.exit(1);
174
- }
175
- }
176
- A(process.argv);