@warpgogol/forge 2.8.0 → 2.8.2

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 (271) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +23 -13
  3. package/README.uk.md +19 -12
  4. package/dist/os/session/handlers/validate.d.ts.map +1 -1
  5. package/dist/os/session/handlers/validate.js +23 -1
  6. package/dist/os/session/handlers/validate.js.map +1 -1
  7. package/dist/os/session/types.d.ts +31 -1
  8. package/dist/os/session/types.d.ts.map +1 -1
  9. package/dist/os/session/types.js +7 -0
  10. package/dist/os/session/types.js.map +1 -1
  11. package/os/adr/adr.module.ts +158 -0
  12. package/os/adr/frontmatter-io.ts +80 -0
  13. package/os/adr/handlers/archive.ts +244 -0
  14. package/os/adr/handlers/implement-stamp.ts +331 -0
  15. package/os/adr/handlers/list-create.ts +207 -0
  16. package/os/adr/handlers/validate.test.ts +232 -0
  17. package/os/adr/handlers/validate.ts +429 -0
  18. package/os/adr/index.ts +33 -0
  19. package/os/adr/types.ts +141 -0
  20. package/os/audit/audit.module.ts +48 -0
  21. package/os/audit/frontmatter-io.ts +84 -0
  22. package/os/audit/handlers/archive.ts +239 -0
  23. package/os/audit/index.ts +23 -0
  24. package/os/audit/types.ts +29 -0
  25. package/os/compass/compass.module.ts +186 -0
  26. package/os/compass/handlers/compass-audit-handler.ts +382 -0
  27. package/os/compass/handlers/compass-change-summary-handler.ts +272 -0
  28. package/os/compass/handlers/compass-inventory-handler.ts +294 -0
  29. package/os/compass/handlers/compass-inventory.ts +521 -0
  30. package/os/compass/handlers/git-revision.ts +137 -0
  31. package/os/compass/handlers/resolve-scan-root-workpiece.test.ts +65 -0
  32. package/os/compass/handlers/resolve-scan-root.ts +79 -0
  33. package/os/compass/index.ts +33 -0
  34. package/os/core/core.module.ts +948 -0
  35. package/os/core/handlers/assets-check.ts +122 -0
  36. package/os/core/handlers/assets-helpers.ts +174 -0
  37. package/os/core/handlers/assets-list.ts +99 -0
  38. package/os/core/handlers/build.ts +155 -0
  39. package/os/core/handlers/determinism-check.ts +354 -0
  40. package/os/core/handlers/dev.ts +127 -0
  41. package/os/core/handlers/invariant-engine.test.ts +681 -0
  42. package/os/core/handlers/knowledge-compact.ts +248 -0
  43. package/os/core/handlers/lifecycle-handlers.test.ts +524 -0
  44. package/os/core/handlers/note-frontmatter-validate.test.ts +93 -0
  45. package/os/core/handlers/note-link-validate.test.ts +117 -0
  46. package/os/core/handlers/note-orphan-detect.test.ts +94 -0
  47. package/os/core/handlers/package-health.test.ts +226 -0
  48. package/os/core/handlers/package-health.ts +231 -0
  49. package/os/core/handlers/pinned-check.ts +200 -0
  50. package/os/core/handlers/pinned-init.ts +333 -0
  51. package/os/core/handlers/pinned-types.ts +50 -0
  52. package/os/core/handlers/pinned-validate.ts +301 -0
  53. package/os/core/handlers/profile-resolve.ts +92 -0
  54. package/os/core/handlers/release-prepare.ts +294 -0
  55. package/os/core/handlers/release-publish.ts +211 -0
  56. package/os/core/handlers/validate.ts +262 -0
  57. package/os/core/handlers/workspace-deps.ts +55 -0
  58. package/os/core/index.ts +13 -0
  59. package/os/exploration/exploration.module.ts +90 -0
  60. package/os/exploration/frontmatter-io.ts +77 -0
  61. package/os/exploration/handlers/archive.ts +159 -0
  62. package/os/exploration/handlers/list.ts +77 -0
  63. package/os/exploration/handlers/show.ts +107 -0
  64. package/os/exploration/index.ts +25 -0
  65. package/os/exploration/types.ts +65 -0
  66. package/os/mission/handlers/archive.test.ts +391 -0
  67. package/os/mission/handlers/archive.ts +453 -0
  68. package/os/mission/index.ts +1 -0
  69. package/os/mission/mission.module.ts +48 -0
  70. package/os/mission/types.ts +41 -0
  71. package/os/naming/index.ts +14 -0
  72. package/os/naming/naming-convention.test.ts +163 -0
  73. package/os/naming/naming-convention.ts +378 -0
  74. package/os/naming/naming.module.ts +36 -0
  75. package/os/notes/index.ts +10 -0
  76. package/os/notes/notes.module.ts +109 -0
  77. package/os/plan/frontmatter-io.ts +82 -0
  78. package/os/plan/handlers/archive.ts +238 -0
  79. package/os/plan/index.ts +23 -0
  80. package/os/plan/plan.module.ts +48 -0
  81. package/os/plan/types.ts +29 -0
  82. package/os/program/discovery.ts +294 -0
  83. package/os/program/handlers/complete.ts +419 -0
  84. package/os/program/handlers/lease.ts +433 -0
  85. package/os/program/handlers/seal.ts +332 -0
  86. package/os/program/handlers/validate.ts +213 -0
  87. package/os/program/lease.ts +156 -0
  88. package/os/program/program.module.ts +212 -0
  89. package/os/program/schemas.ts +218 -0
  90. package/os/program/state.ts +264 -0
  91. package/os/rfc/acceptance.ts +344 -0
  92. package/os/rfc/decision-log.ts +310 -0
  93. package/os/rfc/dna-trace.ts +280 -0
  94. package/os/rfc/frontmatter-io.test.ts +132 -0
  95. package/os/rfc/frontmatter-io.ts +130 -0
  96. package/os/rfc/handlers/archive.ts +300 -0
  97. package/os/rfc/handlers/check.ts +238 -0
  98. package/os/rfc/handlers/implement-stamp.ts +515 -0
  99. package/os/rfc/handlers/index-graph.ts +237 -0
  100. package/os/rfc/handlers/lifecycle.test.ts +127 -0
  101. package/os/rfc/handlers/lifecycle.ts +249 -0
  102. package/os/rfc/handlers/list-create.ts +296 -0
  103. package/os/rfc/handlers/pipeline-status.ts +173 -0
  104. package/os/rfc/handlers/shared.ts +121 -0
  105. package/os/rfc/handlers/supersede-propose.ts +241 -0
  106. package/os/rfc/handlers/validate-rules.test.ts +891 -0
  107. package/os/rfc/handlers/validate-rules.ts +984 -0
  108. package/os/rfc/handlers/validate.ts +166 -0
  109. package/os/rfc/handlers.ts +19 -0
  110. package/os/rfc/index.ts +97 -0
  111. package/os/rfc/rfc.module.ts +416 -0
  112. package/os/rfc/types.ts +596 -0
  113. package/os/rfc/verification-evidence.ts +244 -0
  114. package/os/session/atif-parser.ts +166 -0
  115. package/os/session/frontmatter-io.ts +163 -0
  116. package/os/session/handlers/archive.ts +288 -0
  117. package/os/session/handlers/list.ts +174 -0
  118. package/os/session/handlers/save.ts +348 -0
  119. package/os/session/handlers/validate.ts +250 -0
  120. package/os/session/index.ts +50 -0
  121. package/os/session/session.module.ts +126 -0
  122. package/os/session/types.ts +203 -0
  123. package/os/spec/live-spec-list-show-validate.test.ts +257 -0
  124. package/os/spec/live-spec-list.ts +78 -0
  125. package/os/spec/live-spec-merge.test.ts +261 -0
  126. package/os/spec/live-spec-merge.ts +413 -0
  127. package/os/spec/live-spec-show.ts +117 -0
  128. package/os/spec/live-spec-types.ts +93 -0
  129. package/os/spec/live-spec-validate.ts +170 -0
  130. package/os/spec/spec-materialize.ts +390 -0
  131. package/os/spec/spec-schema.ts +174 -0
  132. package/os/spec/spec-status.ts +284 -0
  133. package/os/spec/spec-validate.ts +513 -0
  134. package/os/spec/spec.module.ts +135 -0
  135. package/os/werkstatt/handlers/lock.ts +168 -0
  136. package/os/werkstatt/handlers/schema.ts +47 -0
  137. package/os/werkstatt/handlers/werkstatt-lock-recover.ts +175 -0
  138. package/os/werkstatt/handlers/werkstatt-lock-status.ts +73 -0
  139. package/os/werkstatt/handlers/werkstatt-operation-validate.ts +104 -0
  140. package/os/werkstatt/index.ts +39 -0
  141. package/os/werkstatt/werkstatt.module.ts +62 -0
  142. package/os/workflow/handlers.ts +304 -0
  143. package/os/workflow/index.ts +28 -0
  144. package/os/workflow/types.ts +99 -0
  145. package/os/workflow/workflow.module.ts +54 -0
  146. package/package.json +3 -3
  147. package/profiles/godot-csharp.yaml +4 -4
  148. package/profiles/knowledge-typescript-turborepo.yaml +110 -0
  149. package/profiles/phaser-turborepo.yaml +5 -5
  150. package/skills/_shared/fo-session-summary.md +132 -16
  151. package/skills/fo/fo-add-tests/pbt-guide.md +13 -13
  152. package/skills/fo/fo-doc-audit/SKILL.md +6 -6
  153. package/skills/fo/fo-handoff/SKILL.md +5 -0
  154. package/skills/fo/fo-idea/SKILL.md +1 -1
  155. package/skills/fo/fo-review/SKILL.md +1 -1
  156. package/skills/fo/fo-session-retro/SKILL.md +8 -0
  157. package/skills/fo/fo-session-save/SKILL.md +8 -0
  158. package/src/cli-output.ts +59 -0
  159. package/src/config/__tests__/resolve-terminology.test.ts +45 -0
  160. package/src/config/forge-config.ts +524 -0
  161. package/src/forge-module.ts +39 -0
  162. package/src/index.ts +172 -0
  163. package/src/knowledge/__tests__/promote.test.ts +219 -0
  164. package/src/knowledge/__tests__/serialize.test.ts +192 -0
  165. package/src/knowledge/budgets.ts +202 -0
  166. package/src/knowledge/compact.ts +405 -0
  167. package/src/knowledge/index.ts +53 -0
  168. package/src/knowledge/parse.ts +227 -0
  169. package/src/knowledge/promote.ts +156 -0
  170. package/src/knowledge/schema.ts +112 -0
  171. package/src/knowledge/serialize.ts +72 -0
  172. package/src/migration-adapters/__tests__/ignored-files.test.ts +51 -0
  173. package/src/migration-adapters/__tests__/registry.test.ts +131 -0
  174. package/src/migration-adapters/__tests__/types.test.ts +86 -0
  175. package/src/migration-adapters/git-utils.ts +71 -0
  176. package/src/migration-adapters/ignored-files.ts +221 -0
  177. package/src/migration-adapters/index.ts +21 -0
  178. package/src/migration-adapters/node-typescript-pnpm/index.ts +189 -0
  179. package/src/migration-adapters/phaser-pnpm/index.ts +189 -0
  180. package/src/migration-adapters/registry.ts +61 -0
  181. package/src/migration-adapters/types.ts +68 -0
  182. package/src/onboarding/__tests__/workspace-discovery.test.ts +134 -0
  183. package/src/onboarding/agents-generate.ts +443 -0
  184. package/src/onboarding/create.ts +427 -0
  185. package/src/onboarding/doctor.ts +1297 -0
  186. package/src/onboarding/init.ts +337 -0
  187. package/src/onboarding/invariant-engine.ts +448 -0
  188. package/src/onboarding/memory-scaffold.ts +189 -0
  189. package/src/onboarding/nested-agents-generate.ts +93 -0
  190. package/src/onboarding/nested-agents-templates.ts +233 -0
  191. package/src/onboarding/profile-validate.ts +139 -0
  192. package/src/onboarding/scaffold-project.ts +299 -0
  193. package/src/onboarding/scaffold.ts +143 -0
  194. package/src/onboarding/upgrade.ts +558 -0
  195. package/src/onboarding/workspace-discovery.ts +167 -0
  196. package/src/profiles/__tests__/profile-schema.test.ts +228 -0
  197. package/src/profiles/__tests__/stack-profile.test.ts +190 -0
  198. package/src/profiles/__tests__/terminology-utils.test.ts +71 -0
  199. package/src/profiles/profile-schema.ts +350 -0
  200. package/src/profiles/stack-profile.ts +188 -0
  201. package/src/profiles/terminology-utils.ts +36 -0
  202. package/src/registry.ts +185 -0
  203. package/src/skill-schema.ts +37 -0
  204. package/src/tests/acceptance-criteria.test.ts +99 -0
  205. package/src/tests/adr-implement-stamp.test.ts +368 -0
  206. package/src/tests/agents-generate-domain.test.ts +317 -0
  207. package/src/tests/agents-generate.test.ts +458 -0
  208. package/src/tests/bindings-schema.test.ts +314 -0
  209. package/src/tests/budgets.test.ts +213 -0
  210. package/src/tests/cli-output.test.ts +196 -0
  211. package/src/tests/compact.test.ts +796 -0
  212. package/src/tests/create.test.ts +289 -0
  213. package/src/tests/doctor-autonomy.test.ts +98 -0
  214. package/src/tests/doctor-bindings.test.ts +286 -0
  215. package/src/tests/doctor-domain.test.ts +113 -0
  216. package/src/tests/exploration-handlers.test.ts +256 -0
  217. package/src/tests/fixtures/agents-generate-business-before.txt +288 -0
  218. package/src/tests/forge-config.test.ts +420 -0
  219. package/src/tests/fs-atomic.test.ts +96 -0
  220. package/src/tests/fs.test.ts +103 -0
  221. package/src/tests/generated-marker.test.ts +161 -0
  222. package/src/tests/hash.test.ts +37 -0
  223. package/src/tests/implement-stamp.test.ts +505 -0
  224. package/src/tests/init-bindings.test.ts +183 -0
  225. package/src/tests/knowledge-parse.test.ts +307 -0
  226. package/src/tests/knowledge-pbt.test.ts +164 -0
  227. package/src/tests/memory-scaffold.test.ts +192 -0
  228. package/src/tests/migration-adapters.test.ts +480 -0
  229. package/src/tests/package-files.test.ts +54 -0
  230. package/src/tests/pinned-check.test.ts +194 -0
  231. package/src/tests/pinned-init.test.ts +126 -0
  232. package/src/tests/profile-schema.test.ts +194 -0
  233. package/src/tests/profile-validate.test.ts +120 -0
  234. package/src/tests/program-lease.test.ts +245 -0
  235. package/src/tests/program-paths-extended.test.ts +117 -0
  236. package/src/tests/program-paths.test.ts +98 -0
  237. package/src/tests/program-property.test.ts +139 -0
  238. package/src/tests/program-schemas.test.ts +445 -0
  239. package/src/tests/program-spec-node.test.ts +250 -0
  240. package/src/tests/promote.test.ts +481 -0
  241. package/src/tests/registry.test.ts +73 -0
  242. package/src/tests/scaffold-project.test.ts +150 -0
  243. package/src/tests/session-handlers.test.ts +503 -0
  244. package/src/tests/session-pbt.test.ts +190 -0
  245. package/src/tests/skill-schema.test.ts +146 -0
  246. package/src/tests/skill-validate-knowledge.test.ts +26 -0
  247. package/src/tests/skill-validate.test.ts +254 -0
  248. package/src/tests/stack-profile.test.ts +221 -0
  249. package/src/tests/string-utils.test.ts +42 -0
  250. package/src/tests/upgrade.test.ts +428 -0
  251. package/src/tests/werkstatt-lock.test.ts +343 -0
  252. package/src/tests/workspace-discovery-domain.test.ts +112 -0
  253. package/src/tests/workspace-discovery.test.ts +135 -0
  254. package/src/types.ts +221 -0
  255. package/src/utils/__tests__/fs-idempotent.test.ts +73 -0
  256. package/src/utils/__tests__/fs-trash.test.ts +45 -0
  257. package/src/utils/fs-atomic.ts +93 -0
  258. package/src/utils/fs-idempotent.ts +41 -0
  259. package/src/utils/fs-trash-sync.ts +37 -0
  260. package/src/utils/fs-trash.ts +24 -0
  261. package/src/utils/fs.ts +74 -0
  262. package/src/utils/generated-marker.ts +166 -0
  263. package/src/utils/hash.ts +19 -0
  264. package/src/utils/index.ts +29 -0
  265. package/src/utils/string-utils.ts +19 -0
  266. package/src/validators/__tests__/note-orphan-detect.test.ts +94 -0
  267. package/src/validators/note-frontmatter-validate.ts +142 -0
  268. package/src/validators/note-link-validate.ts +145 -0
  269. package/src/validators/note-orphan-detect.ts +146 -0
  270. package/src/validators/port-validate.ts +97 -0
  271. package/src/validators/skill-validate.ts +831 -0
@@ -0,0 +1,146 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Detect orphan notes in a markdown note vault — notes with zero inbound wikilinks.</purpose>
4
+ <non-goals>
5
+ <item>Do not import from @warpgogol/* — this module is portable.</item>
6
+ <item>Do not implement domain-specific logic beyond orphan detection.</item>
7
+ </non-goals>
8
+ </MODULE_CONTRACT>
9
+ <CHANGE_SUMMARY>
10
+ <item>RFC-0808: initial note.orphan.detect command handler.</item>
11
+ </CHANGE_SUMMARY>
12
+ */
13
+
14
+ import fs from "node:fs";
15
+ import path from "node:path";
16
+ import { parse as parseYaml } from "yaml";
17
+ import type { ForgeCommandResult } from "../types.ts";
18
+
19
+ interface NoteOrphanDetectInput {
20
+ flags: Record<string, unknown>;
21
+ }
22
+
23
+ export interface OrphanReport {
24
+ file: string;
25
+ inboundLinks: 0;
26
+ severity: "warning";
27
+ }
28
+
29
+ export interface NoteOrphanDetectResult {
30
+ command: "note.orphan.detect";
31
+ orphans: OrphanReport[];
32
+ count: number;
33
+ }
34
+
35
+ function collectMarkdownFiles(dir: string, baseDir: string, results: string[]): void {
36
+ const SKIP_DIRS = new Set(["node_modules", ".git", ".turbo", "dist", ".cache", ".obsidian"]);
37
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
38
+ for (const entry of entries) {
39
+ if (entry.isDirectory() && SKIP_DIRS.has(entry.name)) continue;
40
+ const fullPath = path.join(dir, entry.name);
41
+ const relPath = path.relative(baseDir, fullPath).replace(/\\/g, "/");
42
+ if (entry.isDirectory()) {
43
+ collectMarkdownFiles(fullPath, baseDir, results);
44
+ } else if (entry.name.endsWith(".md")) {
45
+ results.push(relPath);
46
+ }
47
+ }
48
+ }
49
+
50
+ export function runNoteOrphanDetect(
51
+ input: NoteOrphanDetectInput,
52
+ context: { workspaceRoot?: string },
53
+ ): ForgeCommandResult<NoteOrphanDetectResult> {
54
+ const workspaceRoot = context.workspaceRoot ?? process.cwd();
55
+ const vaultDir = (input.flags["vault-dir"] as string) ?? "vault";
56
+ const scanDir = path.join(workspaceRoot, vaultDir);
57
+
58
+ if (!fs.existsSync(scanDir)) {
59
+ return {
60
+ data: {
61
+ command: "note.orphan.detect",
62
+ orphans: [],
63
+ count: 0,
64
+ },
65
+ exitCode: 0,
66
+ summary: "note.orphan.detect: vault directory not found — 0 orphans",
67
+ };
68
+ }
69
+
70
+ const allMdFiles: string[] = [];
71
+ collectMarkdownFiles(scanDir, workspaceRoot, allMdFiles);
72
+
73
+ const noteMap = new Map<string, string>();
74
+ for (const mdFile of allMdFiles) {
75
+ const basename = path.basename(mdFile, ".md");
76
+ noteMap.set(basename, mdFile);
77
+ const fullPath = path.join(workspaceRoot, mdFile);
78
+ try {
79
+ const content = fs.readFileSync(fullPath, "utf8");
80
+ const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
81
+ if (fmMatch) {
82
+ try {
83
+ const fm = parseYaml(fmMatch[1]) as Record<string, unknown>;
84
+ if (fm.aliases && Array.isArray(fm.aliases)) {
85
+ for (const alias of fm.aliases) {
86
+ if (typeof alias === "string") noteMap.set(alias, mdFile);
87
+ }
88
+ }
89
+ } catch {
90
+ // skip malformed frontmatter
91
+ }
92
+ }
93
+ } catch {
94
+ // skip unreadable files
95
+ }
96
+ }
97
+
98
+ const inboundLinks = new Map<string, number>();
99
+ for (const mdFile of allMdFiles) {
100
+ inboundLinks.set(mdFile, 0);
101
+ }
102
+
103
+ const linkRegex = /\[\[([^\]]+?)\]\]/g;
104
+ for (const file of allMdFiles) {
105
+ const fullPath = path.join(workspaceRoot, file);
106
+ try {
107
+ const content = fs.readFileSync(fullPath, "utf8");
108
+ let match: RegExpExecArray | null;
109
+ linkRegex.lastIndex = 0;
110
+ while ((match = linkRegex.exec(content)) !== null) {
111
+ const linkTarget = match[1].split("|")[0].split("#")[0].trim();
112
+ if (!linkTarget) continue;
113
+ const targetFile = noteMap.get(linkTarget);
114
+ if (targetFile && targetFile !== file) {
115
+ inboundLinks.set(targetFile, (inboundLinks.get(targetFile) ?? 0) + 1);
116
+ }
117
+ }
118
+ } catch {
119
+ // skip unreadable files
120
+ }
121
+ }
122
+
123
+ const orphans: OrphanReport[] = [];
124
+ for (const [file, count] of inboundLinks) {
125
+ if (count === 0) {
126
+ orphans.push({
127
+ file,
128
+ inboundLinks: 0,
129
+ severity: "warning",
130
+ });
131
+ }
132
+ }
133
+
134
+ return {
135
+ data: {
136
+ command: "note.orphan.detect",
137
+ orphans,
138
+ count: orphans.length,
139
+ },
140
+ exitCode: 0,
141
+ summary:
142
+ orphans.length > 0
143
+ ? `note.orphan.detect: ${orphans.length} orphan(s) found`
144
+ : "note.orphan.detect: OK — 0 orphans",
145
+ };
146
+ }
@@ -0,0 +1,97 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>forge.port.validate — validates that a ported skill or command complies with forge contracts (frontmatter, registry, no project-specific dependencies).</purpose>
4
+ <non-goals>
5
+ <item>Do not validate all skills — only the named skill or command.</item>
6
+ </non-goals>
7
+ </MODULE_CONTRACT>
8
+ <CHANGE_SUMMARY>
9
+ <item>RFC-0374: initial forge.port.validate handler.</item>
10
+ </CHANGE_SUMMARY>
11
+ */
12
+
13
+ import fs from "node:fs";
14
+ import path from "node:path";
15
+ import { FORGE_SKILLS } from "../registry.ts";
16
+
17
+ interface PortValidateResult {
18
+ command: string;
19
+ status: "pass" | "fail";
20
+ violations: string[];
21
+ }
22
+
23
+ // RFC-0556: removed @warpgogol/site-kernel-checks and @warpgogol/site-kernel-handoff
24
+ // from this list — os/compass and os/werkstatt no longer import them (handlers inlined).
25
+ // RFC-0776: @warpgogol/site-kernel replaced by @warpgogol/werkstatt/kernel.
26
+ // @warpgogol/werkstatt/kernel remains forbidden for src/ portability.
27
+ const FORBIDDEN_IMPORTS = [
28
+ "@warpgogol/werkstatt/kernel",
29
+ "@warpgogol/werkstatt-site/ui",
30
+ "@warpgogol/werkstatt-shared/share/page",
31
+ ];
32
+
33
+ export function runPortValidate(
34
+ input: { flags: { name?: string } },
35
+ context: unknown,
36
+ ): PortValidateResult {
37
+ const ctx = context as { workspaceRoot?: string };
38
+ const workspaceRoot = ctx?.workspaceRoot ?? process.cwd();
39
+ const forgeRoot = path.join(workspaceRoot, "packages", "forge");
40
+ const name = input.flags?.name;
41
+
42
+ if (!name) {
43
+ return {
44
+ command: "forge.port.validate",
45
+ status: "fail",
46
+ violations: ["--name flag is required"],
47
+ };
48
+ }
49
+
50
+ const violations: string[] = [];
51
+
52
+ // Check if skill exists in registry
53
+ const skillEntry = FORGE_SKILLS.find((s) => s.name === name);
54
+ if (!skillEntry) {
55
+ violations.push(`Skill "${name}" not found in forge registry`);
56
+ }
57
+
58
+ // Check for forbidden imports in TypeScript files under the skill/command directory
59
+ const skillDir = path.join(forgeRoot, "skills");
60
+ const searchDirs = [
61
+ path.join(skillDir, "fo", name),
62
+ path.join(skillDir, "shared", name),
63
+ path.join(skillDir, "meta", name),
64
+ path.join(forgeRoot, "os"),
65
+ ];
66
+
67
+ for (const dir of searchDirs) {
68
+ if (fs.existsSync(dir)) {
69
+ scanForForbiddenImports(dir, FORBIDDEN_IMPORTS, violations);
70
+ }
71
+ }
72
+
73
+ return {
74
+ command: "forge.port.validate",
75
+ status: violations.length === 0 ? "pass" : "fail",
76
+ violations,
77
+ };
78
+ }
79
+
80
+ function scanForForbiddenImports(dir: string, forbidden: string[], violations: string[]): void {
81
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
82
+ for (const entry of entries) {
83
+ const fullPath = path.join(dir, entry.name);
84
+ if (entry.isDirectory()) {
85
+ scanForForbiddenImports(fullPath, forbidden, violations);
86
+ } else if (entry.name.endsWith(".ts") || entry.name.endsWith(".mjs")) {
87
+ const content = fs.readFileSync(fullPath, "utf8");
88
+ for (const imp of forbidden) {
89
+ if (content.includes(imp)) {
90
+ violations.push(
91
+ `Forbidden import "${imp}" found in ${path.relative(process.cwd(), fullPath)}`,
92
+ );
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }