@ulpi/cli 0.1.5 → 0.1.7

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 (109) hide show
  1. package/README.md +143 -214
  2. package/dist/{auth-PN7TMQHV-2W4ICG64.js → auth-FWM7MM4Q-VZC3U2XZ.js} +1 -1
  3. package/dist/{auth-BFFBUJUC.js → auth-HDK7ECJL.js} +2 -1
  4. package/dist/{chunk-RJIRWQJD.js → chunk-3BCW6ABU.js} +402 -142
  5. package/dist/{chunk-L3PWNHSA.js → chunk-3WB5CXH4.js} +180 -5
  6. package/dist/{chunk-K4OVPFY2.js → chunk-4UCJIAOU.js} +2 -2
  7. package/dist/chunk-4XTHZVDS.js +109 -0
  8. package/dist/chunk-4ZPOZULQ.js +6522 -0
  9. package/dist/{chunk-SIAQVRKG.js → chunk-5MI5GIXM.js} +48 -2
  10. package/dist/{chunk-KLEASXUR.js → chunk-6ZL6NXMV.js} +1 -1
  11. package/dist/{chunk-AV5RB3N2.js → chunk-76D3BYJD.js} +48 -0
  12. package/dist/{chunk-DOIKS6C5.js → chunk-AWOSRA5F.js} +1 -1
  13. package/dist/{chunk-UCMT5OKP.js → chunk-BFEKZZHM.js} +274 -57
  14. package/dist/chunk-C7CLUQI6.js +1286 -0
  15. package/dist/{chunk-ELTGWMDE.js → chunk-E3B5NROU.js} +7 -7
  16. package/dist/chunk-EJ7TW77N.js +1418 -0
  17. package/dist/{chunk-P2RESJRN.js → chunk-EWLYVXQ4.js} +2 -2
  18. package/dist/{chunk-6OURRFP7.js → chunk-IV6MWETF.js} +383 -168
  19. package/dist/chunk-IZPJHSPX.js +1478 -0
  20. package/dist/chunk-JLHNLM3C.js +228 -0
  21. package/dist/chunk-PO4NUZUU.js +147 -0
  22. package/dist/chunk-S6ANCSYO.js +1271 -0
  23. package/dist/chunk-SEU7WWNQ.js +1251 -0
  24. package/dist/chunk-SNQ7NAIS.js +453 -0
  25. package/dist/{ulpi-RMMCUAGP-EWYUE7RU.js → chunk-TSLDGT5O.js} +73 -35
  26. package/dist/{chunk-EIWYSP3A.js → chunk-UXHCHOWQ.js} +83 -62
  27. package/dist/chunk-WED4LM5N.js +322 -0
  28. package/dist/chunk-WVOZE25N.js +6757 -0
  29. package/dist/{chunk-5SCG7UYM.js → chunk-XKF4DPUM.js} +7 -7
  30. package/dist/{chunk-74WVVWJ4.js → chunk-YOKL7RB5.js} +184 -15
  31. package/dist/chunk-Z53CAR7G.js +298 -0
  32. package/dist/{ci-JQ56YIKC.js → ci-COZRTPGQ.js} +124 -26
  33. package/dist/cloud-2F3NLVHN.js +274 -0
  34. package/dist/{codemap-HMYBXJL2.js → codemap-XNGMAF3F.js} +37 -37
  35. package/dist/codex-MB5YTMRT.js +132 -0
  36. package/dist/{config-YYWEN7U2.js → config-OOELBYTH.js} +1 -1
  37. package/dist/dist-2BJYR5EI.js +59 -0
  38. package/dist/dist-3EIQTZHT.js +1380 -0
  39. package/dist/{dist-WAMAQVPK.js → dist-4U5L2X2C.js} +2 -2
  40. package/dist/{dist-4XTJ6HLM.js → dist-54KAMNLO.js} +16 -15
  41. package/dist/dist-6M4MZWZW.js +58 -0
  42. package/dist/dist-6X576SU2.js +27 -0
  43. package/dist/dist-7QOEYLFX.js +103 -0
  44. package/dist/dist-AYBGHEDY.js +2541 -0
  45. package/dist/dist-EK45QNEM.js +45 -0
  46. package/dist/{dist-U7ZIJMZD.js → dist-FKFEJRPX.js} +16 -15
  47. package/dist/dist-GTEJUBBT.js +66 -0
  48. package/dist/dist-HA74OKJZ.js +40 -0
  49. package/dist/{dist-XG2GG5SD.js → dist-HU5RZAON.js} +14 -2
  50. package/dist/dist-IYE3OBRB.js +374 -0
  51. package/dist/{dist-7WLLPWWB.js → dist-JLU26AB6.js} +12 -9
  52. package/dist/{dist-6G7JC2RA.js → dist-KUCI6JFE.js} +49 -9
  53. package/dist/dist-NUEMFZFL.js +33 -0
  54. package/dist/{dist-GWGTAHNM.js → dist-NUXMDXZ3.js} +31 -3
  55. package/dist/{dist-5R4RYNQO.js → dist-YCNWHSLN.js} +15 -5
  56. package/dist/{dist-6MFVWIFF.js → dist-YFFG2ZD6.js} +9 -16
  57. package/dist/dist-ZG4OKCSR.js +15 -0
  58. package/dist/doctor-FKYSIHER.js +345 -0
  59. package/dist/{export-import-4A5MWLIA.js → export-import-JFQH4KSJ.js} +1 -1
  60. package/dist/{history-RNUWO4JZ.js → history-UMGQNQQ7.js} +7 -7
  61. package/dist/{hooks-installer-K2JXEBNN.js → hooks-installer-YEYTYA6Q.js} +2 -2
  62. package/dist/index.js +398 -622
  63. package/dist/{init-NQWFZPKO.js → init-TJYW5ROZ.js} +78 -12
  64. package/dist/job-HIDMAFW2.js +376 -0
  65. package/dist/jobs.memory-PLMMSFHB-VBECCTHN.js +33 -0
  66. package/dist/kiro-VMUHDFGK.js +153 -0
  67. package/dist/{launchd-OYXUAVW6.js → launchd-U3MSWBRH.js} +9 -17
  68. package/dist/mcp-PDUD7SGP.js +249 -0
  69. package/dist/mcp-installer-PQU3XOGO.js +259 -0
  70. package/dist/mcp-setup-OA7IB3H3.js +263 -0
  71. package/dist/{memory-D6ZFFCI2.js → memory-ZNAEAK3B.js} +17 -17
  72. package/dist/{ollama-3XCUZMZT-FYKHW4TZ.js → ollama-3XCUZMZT-4JMH6B7P.js} +1 -1
  73. package/dist/{openai-E7G2YAHU-IG33BFYF.js → openai-E7G2YAHU-T3HMBPH7.js} +2 -2
  74. package/dist/portal-JYWVHXDU.js +210 -0
  75. package/dist/prd-Q4J5NVAR.js +408 -0
  76. package/dist/repos-WWZXNN3P.js +271 -0
  77. package/dist/review-integration-RQE4KMAV.js +14 -0
  78. package/dist/{rules-3OFGWHP4.js → rules-Y4VSOY5Y.js} +3 -3
  79. package/dist/run-VPNXEIBY.js +687 -0
  80. package/dist/server-COL4AXKU-P7S7NNF6.js +11 -0
  81. package/dist/server-U7PQ6FTS-MG4MJPTS.js +20 -0
  82. package/dist/{skills-GY2CTPWN.js → skills-QEYU2N27.js} +4 -2
  83. package/dist/start-IJKY5RVT.js +303 -0
  84. package/dist/{status-SE43TIFJ.js → status-BHQYYGAL.js} +2 -2
  85. package/dist/{templates-O2XDKB5R.js → templates-CBRUJ66V.js} +6 -5
  86. package/dist/tui-DP7736EX.js +61 -0
  87. package/dist/ulpi-5EN6JCAS-LFE3WSL4.js +10 -0
  88. package/dist/{uninstall-KWGSGZTI.js → uninstall-BX6FOV77.js} +3 -3
  89. package/dist/{update-QYZA4D23.js → update-AQKTHFVQ.js} +3 -3
  90. package/dist/{version-checker-MVB74DEX.js → version-checker-5L5PUOEX.js} +2 -2
  91. package/package.json +13 -4
  92. package/dist/chunk-26LLDX2T.js +0 -553
  93. package/dist/chunk-DDRLI6JU.js +0 -331
  94. package/dist/chunk-IFATANHR.js +0 -453
  95. package/dist/chunk-JWUUVXIV.js +0 -13694
  96. package/dist/chunk-LD52XG3X.js +0 -4273
  97. package/dist/chunk-MIAQVCFW.js +0 -39
  98. package/dist/chunk-YYZOFYS6.js +0 -415
  99. package/dist/dist-XD4YI27T.js +0 -26
  100. package/dist/mcp-installer-TOYDP77X.js +0 -124
  101. package/dist/projects-COUJP4ZC.js +0 -271
  102. package/dist/review-KMGP2S25.js +0 -152
  103. package/dist/server-USLHY6GH-F4JSXCWA.js +0 -18
  104. package/dist/server-X5P6WH2M-ULZF5WHZ.js +0 -11
  105. package/dist/ui-4SM2SUI6.js +0 -167
  106. package/dist/ui.html +0 -698
  107. /package/dist/skills/{ulpi-generate-guardian → ulpi-generate-guards}/SKILL.md +0 -0
  108. /package/dist/skills/{ulpi-generate-guardian → ulpi-generate-guards}/references/framework-rules.md +0 -0
  109. /package/dist/skills/{ulpi-generate-guardian → ulpi-generate-guards}/references/language-rules.md +0 -0
@@ -2,14 +2,15 @@ import {
2
2
  ULPI_GLOBAL_DIR,
3
3
  projectGuardsFile,
4
4
  projectGuardsFileAlt
5
- } from "./chunk-DDRLI6JU.js";
5
+ } from "./chunk-C7CLUQI6.js";
6
6
 
7
- // ../../packages/projects-engine/dist/index.js
7
+ // ../../packages/repos-engine/dist/index.js
8
8
  import * as fs from "fs";
9
9
  import * as path from "path";
10
10
  import * as crypto from "crypto";
11
11
  var DEFAULT_BASE_DIR = ULPI_GLOBAL_DIR;
12
- var REGISTRY_FILE = "projects.json";
12
+ var REGISTRY_FILE = "repos.json";
13
+ var LEGACY_REGISTRY_FILE = "projects.json";
13
14
  function registryPath(baseDir) {
14
15
  return path.join(baseDir, REGISTRY_FILE);
15
16
  }
@@ -18,7 +19,7 @@ function atomicWrite(filePath, data) {
18
19
  fs.writeFileSync(tmpPath, data, "utf-8");
19
20
  fs.renameSync(tmpPath, filePath);
20
21
  }
21
- function generateProjectId(projectPath) {
22
+ function generateRepoId(projectPath) {
22
23
  const normalized = path.resolve(projectPath);
23
24
  const hash = crypto.createHash("sha256").update(normalized).digest("hex");
24
25
  return hash.slice(0, 12);
@@ -26,11 +27,31 @@ function generateProjectId(projectPath) {
26
27
  function createEmptyRegistry() {
27
28
  return {
28
29
  version: 1,
29
- projects: {},
30
- defaultProject: void 0
30
+ repos: {},
31
+ defaultRepo: void 0
31
32
  };
32
33
  }
33
- function loadProjectRegistry(baseDir = DEFAULT_BASE_DIR) {
34
+ function migrateIfNeeded(baseDir) {
35
+ const newPath = registryPath(baseDir);
36
+ if (fs.existsSync(newPath)) return;
37
+ const legacyPath = path.join(baseDir, LEGACY_REGISTRY_FILE);
38
+ if (!fs.existsSync(legacyPath)) return;
39
+ try {
40
+ const raw = fs.readFileSync(legacyPath, "utf-8");
41
+ const parsed = JSON.parse(raw);
42
+ const migrated = {
43
+ version: 1,
44
+ repos: parsed.projects ?? parsed.repos ?? {},
45
+ defaultRepo: parsed.defaultProject ?? parsed.defaultRepo
46
+ };
47
+ fs.mkdirSync(path.dirname(newPath), { recursive: true });
48
+ atomicWrite(newPath, JSON.stringify(migrated, null, 2));
49
+ fs.unlinkSync(legacyPath);
50
+ } catch {
51
+ }
52
+ }
53
+ function loadRepoRegistry(baseDir = DEFAULT_BASE_DIR) {
54
+ migrateIfNeeded(baseDir);
34
55
  const filePath = registryPath(baseDir);
35
56
  if (!fs.existsSync(filePath)) {
36
57
  return createEmptyRegistry();
@@ -38,7 +59,7 @@ function loadProjectRegistry(baseDir = DEFAULT_BASE_DIR) {
38
59
  try {
39
60
  const raw = fs.readFileSync(filePath, "utf-8");
40
61
  const parsed = JSON.parse(raw);
41
- if (parsed && typeof parsed === "object" && parsed.version === 1 && typeof parsed.projects === "object") {
62
+ if (parsed && typeof parsed === "object" && parsed.version === 1 && typeof parsed.repos === "object") {
42
63
  return parsed;
43
64
  }
44
65
  return createEmptyRegistry();
@@ -46,17 +67,17 @@ function loadProjectRegistry(baseDir = DEFAULT_BASE_DIR) {
46
67
  return createEmptyRegistry();
47
68
  }
48
69
  }
49
- function saveProjectRegistry(registry, baseDir = DEFAULT_BASE_DIR) {
70
+ function saveRepoRegistry(registry, baseDir = DEFAULT_BASE_DIR) {
50
71
  const filePath = registryPath(baseDir);
51
72
  const dir = path.dirname(filePath);
52
73
  fs.mkdirSync(dir, { recursive: true });
53
74
  atomicWrite(filePath, JSON.stringify(registry, null, 2));
54
75
  }
55
- function registerProject(projectDir, options = {}, baseDir = DEFAULT_BASE_DIR) {
76
+ function registerRepo(projectDir, options = {}, baseDir = DEFAULT_BASE_DIR) {
56
77
  const absPath = path.resolve(projectDir);
57
- const id = generateProjectId(absPath);
58
- const registry = loadProjectRegistry(baseDir);
59
- const existing = registry.projects[id];
78
+ const id = generateRepoId(absPath);
79
+ const registry = loadRepoRegistry(baseDir);
80
+ const existing = registry.repos[id];
60
81
  let configStatus = "none";
61
82
  const rulesPath = projectGuardsFile(absPath);
62
83
  const rulesYamlPath = projectGuardsFileAlt(absPath);
@@ -88,80 +109,80 @@ function registerProject(projectDir, options = {}, baseDir = DEFAULT_BASE_DIR) {
88
109
  hooksInstalled: options.hooksInstalled ?? hooksInstalled,
89
110
  stack: options.stack ?? existing?.stack
90
111
  };
91
- registry.projects[id] = entry;
92
- saveProjectRegistry(registry, baseDir);
112
+ registry.repos[id] = entry;
113
+ saveRepoRegistry(registry, baseDir);
93
114
  return entry;
94
115
  }
95
- function unregisterProject(idOrPath, baseDir = DEFAULT_BASE_DIR) {
96
- const registry = loadProjectRegistry(baseDir);
97
- if (registry.projects[idOrPath]) {
98
- delete registry.projects[idOrPath];
99
- if (registry.defaultProject === idOrPath) {
100
- registry.defaultProject = void 0;
116
+ function unregisterRepo(idOrPath, baseDir = DEFAULT_BASE_DIR) {
117
+ const registry = loadRepoRegistry(baseDir);
118
+ if (registry.repos[idOrPath]) {
119
+ delete registry.repos[idOrPath];
120
+ if (registry.defaultRepo === idOrPath) {
121
+ registry.defaultRepo = void 0;
101
122
  }
102
- saveProjectRegistry(registry, baseDir);
123
+ saveRepoRegistry(registry, baseDir);
103
124
  return true;
104
125
  }
105
126
  const absPath = path.resolve(idOrPath);
106
- const id = generateProjectId(absPath);
107
- if (registry.projects[id]) {
108
- delete registry.projects[id];
109
- if (registry.defaultProject === id) {
110
- registry.defaultProject = void 0;
127
+ const id = generateRepoId(absPath);
128
+ if (registry.repos[id]) {
129
+ delete registry.repos[id];
130
+ if (registry.defaultRepo === id) {
131
+ registry.defaultRepo = void 0;
111
132
  }
112
- saveProjectRegistry(registry, baseDir);
133
+ saveRepoRegistry(registry, baseDir);
113
134
  return true;
114
135
  }
115
136
  return false;
116
137
  }
117
- function getProject(idOrPath, baseDir = DEFAULT_BASE_DIR) {
118
- const registry = loadProjectRegistry(baseDir);
119
- if (registry.projects[idOrPath]) {
120
- return registry.projects[idOrPath];
138
+ function getRepo(idOrPath, baseDir = DEFAULT_BASE_DIR) {
139
+ const registry = loadRepoRegistry(baseDir);
140
+ if (registry.repos[idOrPath]) {
141
+ return registry.repos[idOrPath];
121
142
  }
122
143
  const absPath = path.resolve(idOrPath);
123
- const id = generateProjectId(absPath);
124
- return registry.projects[id] ?? null;
144
+ const id = generateRepoId(absPath);
145
+ return registry.repos[id] ?? null;
125
146
  }
126
- function listProjects(baseDir = DEFAULT_BASE_DIR) {
127
- const registry = loadProjectRegistry(baseDir);
128
- return Object.values(registry.projects).sort((a, b) => {
147
+ function listRepos(baseDir = DEFAULT_BASE_DIR) {
148
+ const registry = loadRepoRegistry(baseDir);
149
+ return Object.values(registry.repos).sort((a, b) => {
129
150
  return new Date(b.lastAccessed).getTime() - new Date(a.lastAccessed).getTime();
130
151
  });
131
152
  }
132
- function setDefaultProject(idOrPath, baseDir = DEFAULT_BASE_DIR) {
133
- const registry = loadProjectRegistry(baseDir);
153
+ function setDefaultRepo(idOrPath, baseDir = DEFAULT_BASE_DIR) {
154
+ const registry = loadRepoRegistry(baseDir);
134
155
  if (!idOrPath) {
135
- registry.defaultProject = void 0;
136
- saveProjectRegistry(registry, baseDir);
156
+ registry.defaultRepo = void 0;
157
+ saveRepoRegistry(registry, baseDir);
137
158
  return true;
138
159
  }
139
160
  let id = idOrPath;
140
- if (!registry.projects[id]) {
161
+ if (!registry.repos[id]) {
141
162
  const absPath = path.resolve(idOrPath);
142
- id = generateProjectId(absPath);
163
+ id = generateRepoId(absPath);
143
164
  }
144
- if (!registry.projects[id]) {
165
+ if (!registry.repos[id]) {
145
166
  return false;
146
167
  }
147
- registry.defaultProject = id;
148
- saveProjectRegistry(registry, baseDir);
168
+ registry.defaultRepo = id;
169
+ saveRepoRegistry(registry, baseDir);
149
170
  return true;
150
171
  }
151
- function getDefaultProject(baseDir = DEFAULT_BASE_DIR) {
152
- const registry = loadProjectRegistry(baseDir);
153
- if (!registry.defaultProject) {
172
+ function getDefaultRepo(baseDir = DEFAULT_BASE_DIR) {
173
+ const registry = loadRepoRegistry(baseDir);
174
+ if (!registry.defaultRepo) {
154
175
  return null;
155
176
  }
156
- return registry.projects[registry.defaultProject] ?? null;
177
+ return registry.repos[registry.defaultRepo] ?? null;
157
178
  }
158
- function scanForProjects(rootDir, maxDepth = 3) {
159
- const projects = [];
179
+ function scanForRepos(rootDir, maxDepth = 3) {
180
+ const repos = [];
160
181
  function scan(dir, depth) {
161
182
  if (depth > maxDepth) return;
162
183
  const ulpiPath = path.join(dir, ".ulpi");
163
184
  if (fs.existsSync(path.join(ulpiPath, "guards.yml")) || fs.existsSync(path.join(ulpiPath, "guards.yaml"))) {
164
- projects.push(dir);
185
+ repos.push(dir);
165
186
  return;
166
187
  }
167
188
  try {
@@ -182,16 +203,16 @@ function scanForProjects(rootDir, maxDepth = 3) {
182
203
  }
183
204
  }
184
205
  scan(path.resolve(rootDir), 0);
185
- return projects;
206
+ return repos;
186
207
  }
187
208
 
188
209
  export {
189
- loadProjectRegistry,
190
- registerProject,
191
- unregisterProject,
192
- getProject,
193
- listProjects,
194
- setDefaultProject,
195
- getDefaultProject,
196
- scanForProjects
210
+ loadRepoRegistry,
211
+ registerRepo,
212
+ unregisterRepo,
213
+ getRepo,
214
+ listRepos,
215
+ setDefaultRepo,
216
+ getDefaultRepo,
217
+ scanForRepos
197
218
  };
@@ -0,0 +1,322 @@
1
+ import {
2
+ installKiroSkill
3
+ } from "./chunk-IZPJHSPX.js";
4
+ import {
5
+ BUNDLED_SKILLS
6
+ } from "./chunk-4ZPOZULQ.js";
7
+ import {
8
+ getBinaryPath
9
+ } from "./chunk-C7CLUQI6.js";
10
+
11
+ // ../../packages/agent-installer/dist/index.js
12
+ import * as fs from "fs";
13
+ import * as path from "path";
14
+ import * as os from "os";
15
+ import * as fs2 from "fs";
16
+ import * as path2 from "path";
17
+ function buildMcpServers() {
18
+ const binary = getBinaryPath();
19
+ return {
20
+ "ulpi-codemap": {
21
+ command: binary,
22
+ args: ["codemap", "serve"],
23
+ transportType: "stdio"
24
+ },
25
+ "ulpi-memory": {
26
+ command: binary,
27
+ args: ["memory", "serve"],
28
+ transportType: "stdio"
29
+ }
30
+ };
31
+ }
32
+ function kiroAgentsDir() {
33
+ return path.join(os.homedir(), ".kiro", "agents");
34
+ }
35
+ function kiroAgentPath(name) {
36
+ return path.join(kiroAgentsDir(), `${name}.json`);
37
+ }
38
+ function kiroSkillsDir(projectDir) {
39
+ return path.join(projectDir, ".kiro", "skills");
40
+ }
41
+ function buildHooks() {
42
+ const binary = getBinaryPath();
43
+ return {
44
+ agentSpawn: [{ command: `${binary} session-start` }],
45
+ preToolUse: [{ matcher: "*", command: `${binary} pre-tool` }],
46
+ postToolUse: [{ matcher: "*", command: `${binary} post-tool` }],
47
+ stop: [{ command: `${binary} stop` }]
48
+ };
49
+ }
50
+ function generateAgentConfig() {
51
+ return {
52
+ name: "ulpi-governance",
53
+ description: "ULPI-governed coding agent. Enforces rules from .ulpi/guards.yml, tracks sessions, captures memory, and provides CodeMap semantic search.",
54
+ tools: ["read", "write", "shell"],
55
+ allowedTools: ["read"],
56
+ hooks: buildHooks(),
57
+ resources: [
58
+ "skill://.kiro/skills/*/SKILL.md",
59
+ "file://.ulpi/guards.yml"
60
+ ],
61
+ mcpServers: buildMcpServers()
62
+ };
63
+ }
64
+ function installKiroHooks(mode) {
65
+ if (mode === "agent") {
66
+ const agentConfig = generateAgentConfig();
67
+ const agentPath = kiroAgentPath("ulpi-governance");
68
+ const dir2 = path.dirname(agentPath);
69
+ fs.mkdirSync(dir2, { recursive: true });
70
+ fs.writeFileSync(agentPath, JSON.stringify(agentConfig, null, 2) + "\n", "utf-8");
71
+ return { file: agentPath, agent: "ulpi-governance" };
72
+ }
73
+ const defaultPath = kiroAgentPath("kiro_default");
74
+ let config = {};
75
+ if (fs.existsSync(defaultPath)) {
76
+ try {
77
+ config = JSON.parse(fs.readFileSync(defaultPath, "utf-8"));
78
+ } catch {
79
+ }
80
+ }
81
+ config.hooks = buildHooks();
82
+ config.mcpServers = { ...config.mcpServers ?? {}, ...buildMcpServers() };
83
+ const dir = path.dirname(defaultPath);
84
+ fs.mkdirSync(dir, { recursive: true });
85
+ fs.writeFileSync(defaultPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
86
+ return { file: defaultPath, agent: "kiro_default" };
87
+ }
88
+ function installKiroSkills(projectDir) {
89
+ let count = 0;
90
+ for (const skillInfo of BUNDLED_SKILLS) {
91
+ const skill = {
92
+ name: skillInfo.id,
93
+ path: skillInfo.filename,
94
+ source: "bundled",
95
+ description: skillInfo.description
96
+ };
97
+ const dirName = `ulpi-${skillInfo.id}`;
98
+ const skillDir = path.join(kiroSkillsDir(projectDir), dirName);
99
+ const skillPath = path.join(skillDir, "SKILL.md");
100
+ if (fs.existsSync(skillPath)) continue;
101
+ installKiroSkill(skill, projectDir, "repo");
102
+ count++;
103
+ }
104
+ return count;
105
+ }
106
+ function uninstallKiro(projectDir) {
107
+ let agentRemoved = false;
108
+ let skillsRemoved = 0;
109
+ let agentsRemoved = 0;
110
+ const agentPath = kiroAgentPath("ulpi-governance");
111
+ if (fs.existsSync(agentPath)) {
112
+ fs.unlinkSync(agentPath);
113
+ agentRemoved = true;
114
+ }
115
+ const skillsDir = kiroSkillsDir(projectDir);
116
+ if (fs.existsSync(skillsDir)) {
117
+ const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
118
+ for (const entry of entries) {
119
+ if (entry.isDirectory() && entry.name.startsWith("ulpi-")) {
120
+ fs.rmSync(path.join(skillsDir, entry.name), { recursive: true, force: true });
121
+ skillsRemoved++;
122
+ }
123
+ }
124
+ }
125
+ const claudeAgentsDir = path.join(projectDir, ".claude", "agents");
126
+ const kiroAgentsBase = path.join(projectDir, ".kiro", "agents");
127
+ if (fs.existsSync(claudeAgentsDir) && fs.existsSync(kiroAgentsBase)) {
128
+ const claudeEntries = fs.readdirSync(claudeAgentsDir);
129
+ for (const file of claudeEntries) {
130
+ if (!file.endsWith(".md")) continue;
131
+ const agentName = file.replace(/\.md$/, "");
132
+ const kiroJson = path.join(kiroAgentsBase, `${agentName}.json`);
133
+ const kiroPromptDir = path.join(kiroAgentsBase, agentName);
134
+ if (fs.existsSync(kiroJson)) {
135
+ fs.unlinkSync(kiroJson);
136
+ agentsRemoved++;
137
+ }
138
+ if (fs.existsSync(kiroPromptDir)) {
139
+ fs.rmSync(kiroPromptDir, { recursive: true, force: true });
140
+ }
141
+ }
142
+ }
143
+ return { agentRemoved, skillsRemoved, agentsRemoved };
144
+ }
145
+ function getKiroStatus(projectDir) {
146
+ const agentPath = kiroAgentPath("ulpi-governance");
147
+ const agentInstalled = fs.existsSync(agentPath);
148
+ let skillCount = 0;
149
+ let convertedSkillCount = 0;
150
+ const skillsDir = kiroSkillsDir(projectDir);
151
+ if (fs.existsSync(skillsDir)) {
152
+ const entries = fs.readdirSync(skillsDir, { withFileTypes: true });
153
+ for (const e of entries) {
154
+ if (!e.isDirectory()) continue;
155
+ if (e.name.startsWith("ulpi-")) {
156
+ skillCount++;
157
+ } else {
158
+ convertedSkillCount++;
159
+ }
160
+ }
161
+ }
162
+ let convertedAgentCount = 0;
163
+ const claudeAgentsDir = path.join(projectDir, ".claude", "agents");
164
+ const kiroAgentsBase = path.join(projectDir, ".kiro", "agents");
165
+ if (fs.existsSync(claudeAgentsDir) && fs.existsSync(kiroAgentsBase)) {
166
+ const claudeEntries = fs.readdirSync(claudeAgentsDir);
167
+ for (const file of claudeEntries) {
168
+ if (!file.endsWith(".md")) continue;
169
+ const agentName = file.replace(/\.md$/, "");
170
+ if (fs.existsSync(path.join(kiroAgentsBase, `${agentName}.json`))) {
171
+ convertedAgentCount++;
172
+ }
173
+ }
174
+ }
175
+ const mcpPath = path.join(projectDir, ".kiro", "settings", "mcp.json");
176
+ let mcpConfigured = false;
177
+ if (fs.existsSync(mcpPath)) {
178
+ try {
179
+ const mcpConfig = JSON.parse(fs.readFileSync(mcpPath, "utf-8"));
180
+ const servers = mcpConfig.mcpServers;
181
+ mcpConfigured = !!(servers?.["ulpi-codemap"] || servers?.["ulpi-memory"]);
182
+ } catch {
183
+ }
184
+ }
185
+ return { agentInstalled, agentPath, skillCount, convertedSkillCount, convertedAgentCount, mcpConfigured };
186
+ }
187
+ function installCodexMcpServers(projectDir) {
188
+ const configPath = path2.join(projectDir, ".codex", "config.toml");
189
+ let content = "";
190
+ if (fs2.existsSync(configPath)) {
191
+ content = fs2.readFileSync(configPath, "utf-8");
192
+ }
193
+ const servers = buildMcpServers();
194
+ let added = 0;
195
+ for (const [name, entry] of Object.entries(servers)) {
196
+ const actualHeader = `[mcp_servers.${name}]`;
197
+ if (content.includes(actualHeader)) continue;
198
+ const argsToml = entry.args.map((a) => `"${a}"`).join(", ");
199
+ content += `
200
+ ${actualHeader}
201
+ command = "${entry.command}"
202
+ args = [${argsToml}]
203
+ `;
204
+ added++;
205
+ }
206
+ if (added > 0) {
207
+ const dir = path2.dirname(configPath);
208
+ fs2.mkdirSync(dir, { recursive: true });
209
+ fs2.writeFileSync(configPath, content, "utf-8");
210
+ }
211
+ return added;
212
+ }
213
+ function uninstallCodex(projectDir) {
214
+ let agentsRemoved = 0;
215
+ let skillsRemoved = 0;
216
+ let mcpRemoved = false;
217
+ const claudeAgentsDir = path2.join(projectDir, ".claude", "agents");
218
+ const codexAgentsBase = path2.join(projectDir, ".codex", "agents");
219
+ if (fs2.existsSync(claudeAgentsDir) && fs2.existsSync(codexAgentsBase)) {
220
+ const claudeEntries = fs2.readdirSync(claudeAgentsDir);
221
+ for (const file of claudeEntries) {
222
+ if (!file.endsWith(".md")) continue;
223
+ const agentName = file.replace(/\.md$/, "");
224
+ const codexToml = path2.join(codexAgentsBase, `${agentName}.toml`);
225
+ const codexInstructionsDir = path2.join(codexAgentsBase, agentName);
226
+ if (fs2.existsSync(codexToml)) {
227
+ fs2.unlinkSync(codexToml);
228
+ agentsRemoved++;
229
+ }
230
+ if (fs2.existsSync(codexInstructionsDir)) {
231
+ fs2.rmSync(codexInstructionsDir, { recursive: true, force: true });
232
+ }
233
+ }
234
+ }
235
+ const claudeSkillsDir = path2.join(projectDir, ".claude", "skills");
236
+ const codexSkillsDir = path2.join(projectDir, ".codex", "skills");
237
+ if (fs2.existsSync(claudeSkillsDir) && fs2.existsSync(codexSkillsDir)) {
238
+ const claudeEntries = fs2.readdirSync(claudeSkillsDir, { withFileTypes: true });
239
+ for (const entry of claudeEntries) {
240
+ if (!entry.isDirectory()) continue;
241
+ const codexSkillDir = path2.join(codexSkillsDir, entry.name);
242
+ if (fs2.existsSync(codexSkillDir)) {
243
+ fs2.rmSync(codexSkillDir, { recursive: true, force: true });
244
+ skillsRemoved++;
245
+ }
246
+ }
247
+ }
248
+ const configPath = path2.join(projectDir, ".codex", "config.toml");
249
+ if (fs2.existsSync(configPath)) {
250
+ let content = fs2.readFileSync(configPath, "utf-8");
251
+ const originalLen = content.length;
252
+ for (const name of ["ulpi-codemap", "ulpi-memory"]) {
253
+ const sectionRegex = new RegExp(
254
+ `\\n?\\[mcp_servers\\.${name.replace(/-/g, "\\-")}\\]\\n(?:[^\\[]*?)(?=\\n\\[|$)`,
255
+ "s"
256
+ );
257
+ content = content.replace(sectionRegex, "");
258
+ }
259
+ if (fs2.existsSync(claudeAgentsDir)) {
260
+ const claudeEntries = fs2.readdirSync(claudeAgentsDir);
261
+ for (const file of claudeEntries) {
262
+ if (!file.endsWith(".md")) continue;
263
+ const agentName = file.replace(/\.md$/, "");
264
+ const sectionRegex = new RegExp(
265
+ `\\n?\\[agents\\.${agentName}\\]\\n(?:[^\\[]*?)(?=\\n\\[|$)`,
266
+ "s"
267
+ );
268
+ content = content.replace(sectionRegex, "");
269
+ }
270
+ }
271
+ if (content.length !== originalLen) {
272
+ mcpRemoved = true;
273
+ fs2.writeFileSync(configPath, content.trim() + "\n", "utf-8");
274
+ }
275
+ }
276
+ return { agentsRemoved, skillsRemoved, mcpRemoved };
277
+ }
278
+ function getCodexStatus(projectDir) {
279
+ let convertedAgentCount = 0;
280
+ let convertedSkillCount = 0;
281
+ const claudeAgentsDir = path2.join(projectDir, ".claude", "agents");
282
+ const codexAgentsBase = path2.join(projectDir, ".codex", "agents");
283
+ if (fs2.existsSync(claudeAgentsDir) && fs2.existsSync(codexAgentsBase)) {
284
+ const claudeEntries = fs2.readdirSync(claudeAgentsDir);
285
+ for (const file of claudeEntries) {
286
+ if (!file.endsWith(".md")) continue;
287
+ const agentName = file.replace(/\.md$/, "");
288
+ if (fs2.existsSync(path2.join(codexAgentsBase, `${agentName}.toml`))) {
289
+ convertedAgentCount++;
290
+ }
291
+ }
292
+ }
293
+ const claudeSkillsDir = path2.join(projectDir, ".claude", "skills");
294
+ const codexSkillsDir = path2.join(projectDir, ".codex", "skills");
295
+ if (fs2.existsSync(claudeSkillsDir) && fs2.existsSync(codexSkillsDir)) {
296
+ const claudeEntries = fs2.readdirSync(claudeSkillsDir, { withFileTypes: true });
297
+ for (const entry of claudeEntries) {
298
+ if (!entry.isDirectory()) continue;
299
+ if (fs2.existsSync(path2.join(codexSkillsDir, entry.name, "SKILL.md"))) {
300
+ convertedSkillCount++;
301
+ }
302
+ }
303
+ }
304
+ const configPath = path2.join(projectDir, ".codex", "config.toml");
305
+ let mcpConfigured = false;
306
+ if (fs2.existsSync(configPath)) {
307
+ const content = fs2.readFileSync(configPath, "utf-8");
308
+ mcpConfigured = content.includes("[mcp_servers.ulpi-codemap]") || content.includes("[mcp_servers.ulpi-memory]");
309
+ }
310
+ return { convertedAgentCount, convertedSkillCount, mcpConfigured };
311
+ }
312
+
313
+ export {
314
+ buildMcpServers,
315
+ installKiroHooks,
316
+ installKiroSkills,
317
+ uninstallKiro,
318
+ getKiroStatus,
319
+ installCodexMcpServers,
320
+ uninstallCodex,
321
+ getCodexStatus
322
+ };