triples-agentic 2.4.3 → 2.4.4

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.
package/README.md CHANGED
@@ -112,7 +112,7 @@ npx triples-agentic all # all platforms
112
112
  # Global install
113
113
  npx triples-agentic claude --global # → ~/.claude/skills/
114
114
  npx triples-agentic cursor --global # → ~/.cursor/rules/
115
- npx triples-agentic codex --global # → ~/.codex/AGENTS.md
115
+ npx triples-agentic codex --global # → ~/.codex/skills/
116
116
  npx triples-agentic windsurf --global # → ~/.codeium/windsurf/rules/
117
117
 
118
118
  # Install into a specific directory
@@ -141,7 +141,7 @@ triples-agentic claude # direct install for Claude Code
141
141
  | **Claude Code** | `.claude/skills/*.md` | `.claude/settings.json` (PreToolUse hook) |
142
142
  | **Cursor AI** | `.cursor/rules/*.mdc` | `.cursor/rules/triples-safety.mdc` (always-applied rule) |
143
143
  | **GitHub Copilot** | `.github/instructions/*.instructions.md` | `.github/instructions/triples-safety.instructions.md` |
144
- | **OpenAI Codex** | `AGENTS.md` (project) / `~/.codex/AGENTS.md` (global) | `.codex/config.toml` / `~/.codex/config.toml` (PreToolUse hook) |
144
+ | **OpenAI Codex** | `.codex/skills/<skill>/SKILL.md` (project) / `~/.codex/skills/<skill>/SKILL.md` (global) | `.codex/config.toml` / `~/.codex/config.toml` (PreToolUse hook) |
145
145
  | **Windsurf** | `.windsurfrules` | `.windsurf/hooks.json` (pre_run_command hook) |
146
146
 
147
147
  Global install paths:
@@ -150,7 +150,7 @@ Global install paths:
150
150
  |---|---|---|
151
151
  | Claude Code | `~/.claude/skills/` | `~/.claude/settings.json` |
152
152
  | Cursor AI | `~/.cursor/rules/` | `~/.cursor/rules/triples-safety.mdc` |
153
- | OpenAI Codex | `~/.codex/AGENTS.md` | `~/.codex/config.toml` |
153
+ | OpenAI Codex | `~/.codex/skills/` | `~/.codex/config.toml` |
154
154
  | Windsurf | `~/.codeium/windsurf/rules/` | `~/.codeium/windsurf/hooks.json` |
155
155
 
156
156
  ---
@@ -214,6 +214,15 @@ SeoYeon walks you through the complete workflow and delegates to each agent.
214
214
  /seoyeon status Check current run state
215
215
  ```
216
216
 
217
+ ### In Codex
218
+ Use `/skills` to browse installed TripleS skills, or mention them directly in your prompt:
219
+
220
+ ```text
221
+ Use $seoyeon to orchestrate this feature from PRD through QA.
222
+ Use $jiwoo-prd to draft the PRD for this feature.
223
+ Use $kaede-backend to implement the backend task from the breakdown.
224
+ ```
225
+
217
226
  ### With other coding assistants
218
227
  Ask for the agent by name — e.g., "Act as JiWoo and create a PRD for [description]"
219
228
 
@@ -280,7 +289,9 @@ triples-agentic/
280
289
  │ │ ├── task-breakdown.md # → workspace/TASK_BREAKDOWN.md
281
290
  │ │ └── test-case.md # → workspace/TEST_CASES.md
282
291
  │ └── bin/
283
- └── setup.js # Platform installer CLI
292
+ ├── setup.js # Platform installer CLI entrypoint
293
+ │ └── setup/
294
+ │ └── codex.js # Codex-specific installer
284
295
  ├── docs/
285
296
  │ └── workflow.md # Full workflow diagram + agent roster
286
297
  └── CHANGELOG.md
@@ -292,9 +303,9 @@ your-project/
292
303
  │ └── settings.json # Claude Code PreToolUse safety hook
293
304
  ├── .cursor/rules/ # Cursor AI agent rules + safety rule
294
305
  ├── .github/instructions/ # Copilot agent instructions + safety instruction
306
+ ├── .codex/skills/ # Codex skill bundles (project install)
295
307
  ├── .codex/config.toml # Codex PreToolUse safety hook
296
308
  ├── .windsurf/hooks.json # Windsurf pre_run_command safety hook
297
- ├── AGENTS.md # Codex agent context
298
309
  └── .windsurfrules # Windsurf agent rules
299
310
  ```
300
311
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triples-agentic",
3
- "version": "2.4.3",
3
+ "version": "2.4.4",
4
4
  "description": "TripleS software engineering agent orchestrator — skill plugin for Claude Code, Cursor, Copilot, Codex, and Windsurf",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,230 @@
1
+ import { readFileSync, existsSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ const CODEX_AGENT_SKILL_METADATA = {
5
+ seoyeon: {
6
+ description: 'Coordinate the full TripleS delivery pipeline across PRD, RFC, task breakdown, development, test cases, and QA. Use when the user wants end-to-end orchestration, status, routing, or delivery summaries.',
7
+ shortDescription: 'Orchestrate the full TripleS delivery flow',
8
+ defaultPrompt: 'Use $seoyeon to orchestrate this feature from PRD through QA.',
9
+ },
10
+ 'jiwoo-prd': {
11
+ description: 'Create, review, and refine implementation-ready product requirements documents for TripleS workflows. Use when the user needs a PRD with explicit quality gates and product clarifications.',
12
+ shortDescription: 'Create and review implementation-ready PRDs',
13
+ defaultPrompt: 'Use $jiwoo-prd to draft or revise the PRD for this feature.',
14
+ },
15
+ 'yooyeon-rfc': {
16
+ description: 'Create, review, and refine implementation-ready technical RFCs that trace back to an approved PRD. Use when the user needs architecture decisions, API contracts, risks, or rollout plans documented.',
17
+ shortDescription: 'Create and review implementation-ready RFCs',
18
+ defaultPrompt: 'Use $yooyeon-rfc to turn this PRD into a technical RFC.',
19
+ },
20
+ 'nakyoung-tasks': {
21
+ description: 'Break approved PRDs and RFCs into estimated, dependency-aware engineering tasks. Use when the user needs a delivery plan, story points, task sequencing, or platform assignments.',
22
+ shortDescription: 'Break work into ready-to-build tasks',
23
+ defaultPrompt: 'Use $nakyoung-tasks to create the task breakdown for this project.',
24
+ },
25
+ 'yubin-frontend': {
26
+ description: 'Implement frontend web tasks from the approved TripleS task breakdown with strong accessibility, performance, and testing practices. Use when the user needs web UI implementation work.',
27
+ shortDescription: 'Implement web frontend tasks from breakdown',
28
+ defaultPrompt: 'Use $yubin-frontend to implement the assigned web frontend task.',
29
+ },
30
+ 'kaede-backend': {
31
+ description: 'Implement backend APIs, services, and data access layers from the approved TripleS task breakdown. Use when the user needs backend implementation with strong reliability, security, and testing practices.',
32
+ shortDescription: 'Implement backend APIs and services',
33
+ defaultPrompt: 'Use $kaede-backend to implement the assigned backend task.',
34
+ },
35
+ 'yeonji-android': {
36
+ description: 'Implement Android tasks in Kotlin and Jetpack Compose from the approved TripleS task breakdown. Use when the user needs Android-native feature work.',
37
+ shortDescription: 'Implement Android features in Kotlin',
38
+ defaultPrompt: 'Use $yeonji-android to implement the assigned Android task.',
39
+ },
40
+ 'sohyun-ios': {
41
+ description: 'Implement iOS tasks in Swift and SwiftUI from the approved TripleS task breakdown. Use when the user needs iOS-native feature work with Apple platform conventions.',
42
+ shortDescription: 'Implement iOS features in SwiftUI',
43
+ defaultPrompt: 'Use $sohyun-ios to implement the assigned iOS task.',
44
+ },
45
+ 'kotone-flutter': {
46
+ description: 'Implement Flutter tasks in Dart with Riverpod from the approved TripleS task breakdown. Use when the user needs cross-platform mobile feature work.',
47
+ shortDescription: 'Implement Flutter features with Riverpod',
48
+ defaultPrompt: 'Use $kotone-flutter to implement the assigned Flutter task.',
49
+ },
50
+ 'lynn-testcase': {
51
+ description: 'Create, review, and refine implementation-ready QA test suites from approved PRDs and RFCs. Use when the user needs structured test cases, coverage gaps, or automation candidates documented.',
52
+ shortDescription: 'Create and review QA test suites',
53
+ defaultPrompt: 'Use $lynn-testcase to create the test case suite for this project.',
54
+ },
55
+ 'shion-qa': {
56
+ description: 'Execute approved test suites, file defects, and produce a go/no-go QA recommendation. Use when the user needs test execution, bug reports, or release-readiness assessment.',
57
+ shortDescription: 'Execute tests and issue go/no-go QA reports',
58
+ defaultPrompt: 'Use $shion-qa to execute QA and produce a release recommendation.',
59
+ },
60
+ };
61
+
62
+ function codexSkillMetadata(agent) {
63
+ return CODEX_AGENT_SKILL_METADATA[agent.slug] || {
64
+ description: `Use when the user wants help from the TripleS ${agent.displayName} workflow.`,
65
+ shortDescription: `Use the ${agent.displayName} TripleS workflow`,
66
+ defaultPrompt: `Use $${agent.slug} for this TripleS workflow.`,
67
+ };
68
+ }
69
+
70
+ function codexSkillFileContent(agent, helpers) {
71
+ const { stripAgentMetadataComments, yamlEscape } = helpers;
72
+ const meta = codexSkillMetadata(agent);
73
+ const references = [];
74
+
75
+ if (agent.knowledgePaths.length) {
76
+ references.push('- Bundled knowledge references live under `references/knowledge/`.');
77
+ }
78
+
79
+ if (agent.templatePaths.length) {
80
+ references.push('- Bundled templates live under `references/templates/`.');
81
+ }
82
+
83
+ if (references.length) {
84
+ references.push('- When the original TripleS instructions mention `knowledge/...` or `templates/...`, resolve them to those bundled copies inside this skill directory.');
85
+ }
86
+
87
+ const preface = references.length
88
+ ? ['## Bundled References', '', ...references, ''].join('\n')
89
+ : '';
90
+
91
+ return [
92
+ '---',
93
+ `name: "${yamlEscape(agent.slug)}"`,
94
+ `description: "${yamlEscape(meta.description)}"`,
95
+ '---',
96
+ '',
97
+ `# ${agent.displayName}`,
98
+ '',
99
+ preface,
100
+ stripAgentMetadataComments(agent.content),
101
+ '',
102
+ ].join('\n');
103
+ }
104
+
105
+ function codexSkillUiYaml(agent, helpers) {
106
+ const { yamlEscape } = helpers;
107
+ const meta = codexSkillMetadata(agent);
108
+ return [
109
+ 'interface:',
110
+ ` display_name: "${yamlEscape(agent.displayName)}"`,
111
+ ` short_description: "${yamlEscape(meta.shortDescription)}"`,
112
+ ` default_prompt: "${yamlEscape(meta.defaultPrompt)}"`,
113
+ '',
114
+ 'policy:',
115
+ ' allow_implicit_invocation: true',
116
+ '',
117
+ ].join('\n');
118
+ }
119
+
120
+ function writeCodexSkillBundle(skillsRoot, agent, ctx) {
121
+ const { ROOT, KNOWLEDGE_DIR, writeFile } = ctx;
122
+ const skillDir = join(skillsRoot, agent.slug);
123
+ writeFile(join(skillDir, 'SKILL.md'), codexSkillFileContent(agent, ctx));
124
+ writeFile(join(skillDir, 'agents', 'openai.yaml'), codexSkillUiYaml(agent, ctx));
125
+
126
+ for (const relPath of agent.knowledgePaths) {
127
+ const sourcePath = join(KNOWLEDGE_DIR, relPath);
128
+ if (existsSync(sourcePath)) {
129
+ writeFile(join(skillDir, 'references', 'knowledge', relPath), readFileSync(sourcePath, 'utf-8'));
130
+ }
131
+ }
132
+
133
+ for (const relPath of agent.templatePaths) {
134
+ const sourcePath = join(ROOT, 'templates', relPath);
135
+ if (existsSync(sourcePath)) {
136
+ writeFile(join(skillDir, 'references', 'templates', relPath), readFileSync(sourcePath, 'utf-8'));
137
+ }
138
+ }
139
+ }
140
+
141
+ function maybeRewriteLegacyCodexAgentsDoc(targetPath, skillsRoot, ctx) {
142
+ const { display, writeFile } = ctx;
143
+ if (!existsSync(targetPath)) return;
144
+
145
+ let existing = '';
146
+ try { existing = readFileSync(targetPath, 'utf-8'); } catch { return; }
147
+ if (!existing.includes('TripleS Agent Orchestrator')) return;
148
+
149
+ const compatDoc = [
150
+ '# TripleS Agentic for Codex',
151
+ '',
152
+ 'TripleS workflows are installed as Codex skills, not as a generated agent catalog in this file.',
153
+ '',
154
+ `- Skills root: \`${display(skillsRoot)}\``,
155
+ '- In Codex, use `/skills` to browse them or mention a skill directly such as `$seoyeon` or `$jiwoo-prd`.',
156
+ '- The actual workflow instructions now live in each skill bundle under `SKILL.md`.',
157
+ '',
158
+ ].join('\n');
159
+
160
+ writeFile(targetPath, compatDoc);
161
+ }
162
+
163
+ function installCodexSettings(base, ctx) {
164
+ const { GLOBAL_PATHS, isGlobal, projectDir, loadCodexHooks, tomlEscape, writeFile } = ctx;
165
+ const hookEntries = loadCodexHooks().filter(entry => entry.event === 'PreToolUse');
166
+ if (hookEntries.length === 0) return;
167
+
168
+ const configPath = isGlobal && !base
169
+ ? join(GLOBAL_PATHS.codex, 'config.toml')
170
+ : join(base || projectDir, '.codex', 'config.toml');
171
+ let existing = existsSync(configPath) ? readFileSync(configPath, 'utf-8') : '';
172
+
173
+ existing = existing.replace(/\n?# triples-agentic hooks[\s\S]*?# end triples-agentic hooks\n?/g, '');
174
+
175
+ let block = '\n# triples-agentic hooks\n';
176
+ for (const entry of hookEntries) {
177
+ block += '\n[[hooks.PreToolUse]]\n';
178
+ if (entry.matcher) block += `matcher = "${tomlEscape(entry.matcher)}"\n`;
179
+ for (const hook of (entry.hooks || [])) {
180
+ block += '\n[[hooks.PreToolUse.hooks]]\n';
181
+ block += 'type = "command"\n';
182
+ block += `command = "${tomlEscape(hook.command)}"\n`;
183
+ if (hook.statusMessage) block += `statusMessage = "${tomlEscape(hook.statusMessage)}"\n`;
184
+ }
185
+ }
186
+ block += '\n# end triples-agentic hooks\n';
187
+
188
+ writeFile(configPath, existing.trimEnd() + block);
189
+ }
190
+
191
+ export function installCodex(base, ctx) {
192
+ const { GLOBAL_PATHS, isGlobal, projectDir, allAgents, display } = ctx;
193
+ const codexRoot = isGlobal && !base
194
+ ? GLOBAL_PATHS.codex
195
+ : join(base || projectDir, '.codex');
196
+ const skillsRoot = join(codexRoot, 'skills');
197
+ console.log(`\nInstalling OpenAI Codex skills → ${display(skillsRoot)}`);
198
+
199
+ for (const agent of allAgents()) {
200
+ writeCodexSkillBundle(skillsRoot, agent, ctx);
201
+ }
202
+
203
+ const legacyAgentsPath = isGlobal && !base
204
+ ? join(GLOBAL_PATHS.codex, 'AGENTS.md')
205
+ : join(base || projectDir, 'AGENTS.md');
206
+ maybeRewriteLegacyCodexAgentsDoc(legacyAgentsPath, skillsRoot, ctx);
207
+ installCodexSettings(base, ctx);
208
+ }
209
+
210
+ export function detectCodexInstallations(ctx) {
211
+ const { GLOBAL_PATHS, projectDir } = ctx;
212
+ const hasMarker = (file) => {
213
+ try { return readFileSync(file, 'utf-8').includes('TripleS Agent Orchestrator'); } catch { return false; }
214
+ };
215
+
216
+ const found = [];
217
+ const codexGlobal = join(GLOBAL_PATHS.codex, 'AGENTS.md');
218
+ const codexGlobalSkills = join(GLOBAL_PATHS.codex, 'skills', 'seoyeon', 'SKILL.md');
219
+ if (existsSync(codexGlobalSkills) || (existsSync(codexGlobal) && hasMarker(codexGlobal))) {
220
+ found.push({ platform: 'codex', isGlobal: true });
221
+ }
222
+
223
+ const codexProject = join(projectDir, 'AGENTS.md');
224
+ const codexProjectSkills = join(projectDir, '.codex', 'skills', 'seoyeon', 'SKILL.md');
225
+ if (existsSync(codexProjectSkills) || (existsSync(codexProject) && hasMarker(codexProject))) {
226
+ found.push({ platform: 'codex', isGlobal: false });
227
+ }
228
+
229
+ return found;
230
+ }
package/src/bin/setup.js CHANGED
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * TripleS Agentic — Skill Plugin Setup
4
4
  *
5
- * Installs all TripleS skill files (11 agents + 40 knowledge skills)
5
+ * Installs all TripleS skill files (11 agents + 41 knowledge references)
6
6
  * into your coding assistant's config directory.
7
7
  *
8
8
  * Usage:
@@ -89,16 +89,51 @@ function parseComment(content, key) {
89
89
  return m ? m[1].trim() : '';
90
90
  }
91
91
 
92
+ function parseCommentList(content, key) {
93
+ const raw = parseComment(content, key);
94
+ if (!raw) return [];
95
+ return raw.split(',').map(item => item.trim()).filter(Boolean);
96
+ }
97
+
98
+ function parseHeading(content) {
99
+ const m = content.match(/^#\s+(.+)$/m);
100
+ return m ? m[1].trim() : '';
101
+ }
102
+
103
+ function stripAgentMetadataComments(content) {
104
+ return content.replace(/^<!-- .*? -->\r?\n/gm, '').trimStart();
105
+ }
106
+
107
+ function yamlEscape(value) {
108
+ return String(value).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
109
+ }
110
+
111
+ function shortHeading(heading, fallback) {
112
+ if (!heading) return fallback;
113
+ return heading.split('—')[0].trim();
114
+ }
115
+
92
116
  // ─── Source collectors ────────────────────────────────────────────────────────
93
117
 
94
118
  function allAgents() {
95
119
  return readdirSync(AGENTS_DIR)
96
- .filter(f => f.endsWith('.md'))
120
+ .filter(f => f.endsWith('.md') && f !== 'README.md')
97
121
  .map(f => {
98
122
  const name = f.replace('.md', '');
99
123
  const content = readFileSync(join(AGENTS_DIR, f), 'utf-8');
124
+ const slug = parseComment(content, 'triples-agent') || name;
100
125
  const persona = parseComment(content, 'persona') || 'software engineering agent';
101
- return { name, content, persona };
126
+ const heading = parseHeading(content);
127
+ return {
128
+ name,
129
+ slug,
130
+ content,
131
+ persona,
132
+ heading,
133
+ displayName: shortHeading(heading, name),
134
+ knowledgePaths: parseCommentList(content, 'knowledge'),
135
+ templatePaths: parseCommentList(content, 'templates'),
136
+ };
102
137
  })
103
138
  .sort((a, b) => a.name.localeCompare(b.name));
104
139
  }
@@ -187,6 +222,30 @@ function tomlEscape(str) {
187
222
  return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
188
223
  }
189
224
 
225
+ function createInstallerContext() {
226
+ return {
227
+ ROOT,
228
+ HOME,
229
+ AGENTS_DIR,
230
+ KNOWLEDGE_DIR,
231
+ KNOWLEDGE_GROUPS,
232
+ GLOBAL_PATHS,
233
+ projectDir,
234
+ isGlobal,
235
+ allAgents,
236
+ loadCodexHooks,
237
+ stripAgentMetadataComments,
238
+ yamlEscape,
239
+ tomlEscape,
240
+ display,
241
+ writeFile,
242
+ };
243
+ }
244
+
245
+ async function loadCodexInstaller() {
246
+ return import('./setup/codex.js');
247
+ }
248
+
190
249
  // ─── Platform installers ──────────────────────────────────────────────────────
191
250
 
192
251
  /** Write (or merge) PreToolUse hooks from platforms.claude into .claude/settings.json */
@@ -215,35 +274,6 @@ function installClaudeSettings(claudeDir) {
215
274
  writeFile(settingsPath, JSON.stringify(settings, null, 2));
216
275
  }
217
276
 
218
- /** Write (or append) PreToolUse hooks from platforms.codex into .codex/config.toml */
219
- function installCodexSettings(base) {
220
- const hookEntries = loadCodexHooks().filter(e => e.event === 'PreToolUse');
221
- if (hookEntries.length === 0) return;
222
-
223
- const configPath = isGlobal && !base
224
- ? join(GLOBAL_PATHS.codex, 'config.toml')
225
- : join(base || projectDir, '.codex', 'config.toml');
226
- let existing = existsSync(configPath) ? readFileSync(configPath, 'utf-8') : '';
227
-
228
- // Remove previous triples-agentic block (idempotent reinstall)
229
- existing = existing.replace(/\n?# triples-agentic hooks[\s\S]*?# end triples-agentic hooks\n?/g, '');
230
-
231
- let block = '\n# triples-agentic hooks\n';
232
- for (const entry of hookEntries) {
233
- block += `\n[[hooks.PreToolUse]]\n`;
234
- if (entry.matcher) block += `matcher = "${tomlEscape(entry.matcher)}"\n`;
235
- for (const hook of (entry.hooks || [])) {
236
- block += `\n[[hooks.PreToolUse.hooks]]\n`;
237
- block += `type = "command"\n`;
238
- block += `command = "${tomlEscape(hook.command)}"\n`;
239
- if (hook.statusMessage) block += `statusMessage = "${tomlEscape(hook.statusMessage)}"\n`;
240
- }
241
- }
242
- block += '\n# end triples-agentic hooks\n';
243
-
244
- writeFile(configPath, existing.trimEnd() + block);
245
- }
246
-
247
277
  /** Write (or merge) Windsurf hooks from platforms.windsurf into .windsurf/hooks.json */
248
278
  function installWindsurfSettings(base) {
249
279
  const byEvent = loadWindsurfHooks();
@@ -335,32 +365,9 @@ function installCopilot(base) {
335
365
  }
336
366
  }
337
367
 
338
- function installCodex(base) {
339
- const dest = isGlobal && !base
340
- ? join(GLOBAL_PATHS.codex, 'AGENTS.md')
341
- : join(base || projectDir, 'AGENTS.md');
342
- console.log(`\nInstalling OpenAI Codex → ${display(dest)}`);
343
-
344
- const safetyBody = loadSafetyRulesBody();
345
- const lines = ['# TripleS Agent Orchestrator\n\n', ...(safetyBody ? [safetyBody + '\n\n'] : []), '## Agents\n\n'];
346
- for (const { name, content } of allAgents()) {
347
- lines.push(`---\n\n${content}\n\n`);
348
- }
349
-
350
- lines.push('## Knowledge Skills\n\n');
351
- for (const group of KNOWLEDGE_GROUPS) {
352
- lines.push(`### ${group.charAt(0).toUpperCase() + group.slice(1)}\n\n`);
353
- for (const { name, content } of allKnowledgeSkills().filter(s => s.group === group)) {
354
- const body = stripFrontmatter(content);
355
- lines.push(`#### ${name}\n\n${body}\n\n`);
356
- }
357
- }
358
-
359
- writeFileSync(dest, lines.join(''), 'utf-8');
360
- console.log(` ✓ ${display(dest)}`);
361
-
362
- // Hooks — enforced at harness level via .codex/config.toml
363
- installCodexSettings(base);
368
+ async function installCodex(base) {
369
+ const { installCodex: runCodexInstall } = await loadCodexInstaller();
370
+ runCodexInstall(base, createInstallerContext());
364
371
  }
365
372
 
366
373
  function installWindsurf(base) {
@@ -443,7 +450,7 @@ const KNOWLEDGE_SUMMARY = {
443
450
  * Detect existing TripleS installations by checking sentinel files.
444
451
  * Returns an array of { platform, isGlobal } objects.
445
452
  */
446
- function detectInstallations() {
453
+ async function detectInstallations() {
447
454
  const found = [];
448
455
 
449
456
  const hasFile = (...parts) => existsSync(join(...parts));
@@ -468,12 +475,8 @@ function detectInstallations() {
468
475
  found.push({ platform: 'copilot', isGlobal: false });
469
476
 
470
477
  // Codex
471
- const codexGlobal = join(GLOBAL_PATHS.codex, 'AGENTS.md');
472
- if (hasFile(codexGlobal) && hasMarker(codexGlobal))
473
- found.push({ platform: 'codex', isGlobal: true });
474
- const codexProject = join(projectDir, 'AGENTS.md');
475
- if (hasFile(codexProject) && hasMarker(codexProject))
476
- found.push({ platform: 'codex', isGlobal: false });
478
+ const { detectCodexInstallations } = await loadCodexInstaller();
479
+ found.push(...detectCodexInstallations(createInstallerContext()));
477
480
 
478
481
  // Windsurf
479
482
  const windsurfGlobal = join(GLOBAL_PATHS.windsurf, '.windsurfrules');
@@ -487,7 +490,7 @@ function detectInstallations() {
487
490
  }
488
491
 
489
492
  async function runUpdate() {
490
- const installations = detectInstallations();
493
+ const installations = await detectInstallations();
491
494
 
492
495
  if (installations.length === 0) {
493
496
  console.log('\n⚠ No existing TripleS Agentic installations detected.\n');
@@ -509,32 +512,33 @@ async function runUpdate() {
509
512
  const savedIsGlobal = isGlobal;
510
513
  for (const { platform, isGlobal: g } of installations) {
511
514
  isGlobal = g;
512
- INSTALLERS[platform](g ? null : projectDir);
515
+ await INSTALLERS[platform](g ? null : projectDir);
513
516
  }
514
517
  isGlobal = savedIsGlobal;
515
518
 
516
519
  const totalKnowledge = Object.values(KNOWLEDGE_SUMMARY).reduce((n, g) => n + g.length, 0);
517
- console.log(`\n✅ Updated ${installations.length} installation(s) — ${AGENT_COMMANDS.length} agents, ${totalKnowledge} knowledge skills\n`);
520
+ console.log(`\n✅ Updated ${installations.length} installation(s) — ${AGENT_COMMANDS.length} agents, ${totalKnowledge} knowledge references\n`);
518
521
  }
519
522
 
520
523
  // ─── Success banner ───────────────────────────────────────────────────────────
521
524
 
522
525
  function printSuccessBanner() {
523
526
  const totalKnowledge = Object.values(KNOWLEDGE_SUMMARY).reduce((n, g) => n + g.length, 0);
527
+ const knowledgeGroups = Object.keys(KNOWLEDGE_SUMMARY).length;
524
528
  console.log('\n✅ TripleS Agentic installed successfully!\n');
525
529
 
526
- console.log('── Agents (invoke as slash commands) ──────────────────────────');
530
+ console.log('── Agents / Skills ───────────────────────────────────────────');
527
531
  for (const [cmd, desc] of AGENT_COMMANDS) {
528
- console.log(` /${cmd.padEnd(18)} ${desc}`);
532
+ console.log(` ${cmd.padEnd(18)} ${desc}`);
529
533
  }
530
534
 
531
- console.log(`\n── Knowledge Skills (${totalKnowledge} skills across 4 groups) ──────────────────`);
535
+ console.log(`\n── Knowledge Library (${totalKnowledge} bundled references across ${knowledgeGroups} groups) ───────`);
532
536
  for (const [group, skills] of Object.entries(KNOWLEDGE_SUMMARY)) {
533
537
  const label = group.includes('/') ? group : group; // full path for sub-groups
534
538
  console.log(` ${label.padEnd(16)} ${skills.map(s => `/${s}`).join(' ')}`);
535
539
  }
536
540
 
537
- console.log('\nStart the full pipeline with /seoyeon');
541
+ console.log('\nUse `/seoyeon` in Claude Code, or `/skills` / `$seoyeon` in Codex.');
538
542
  console.log('To update later run: npx triples-agentic update\n');
539
543
  }
540
544
 
@@ -592,7 +596,7 @@ async function runWizard() {
592
596
 
593
597
  console.log('');
594
598
  for (const p of selectedPlatforms) {
595
- INSTALLERS[p](useGlobal && GLOBAL_PATHS[p] ? null : projectDir);
599
+ await INSTALLERS[p](useGlobal && GLOBAL_PATHS[p] ? null : projectDir);
596
600
  }
597
601
 
598
602
  printSuccessBanner();
@@ -618,7 +622,7 @@ async function main() {
618
622
 
619
623
  if (platformArg === 'all') {
620
624
  for (const installer of Object.values(INSTALLERS)) {
621
- installer(isGlobal ? null : projectDir);
625
+ await installer(isGlobal ? null : projectDir);
622
626
  }
623
627
  printSuccessBanner();
624
628
  return;
@@ -630,7 +634,7 @@ async function main() {
630
634
  process.exit(1);
631
635
  }
632
636
 
633
- INSTALLERS[platformArg](isGlobal ? null : projectDir);
637
+ await INSTALLERS[platformArg](isGlobal ? null : projectDir);
634
638
  printSuccessBanner();
635
639
  }
636
640