@miniidealab/openlogos 0.7.2 → 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 (52) hide show
  1. package/codex-plugin-template/plugin.json +10 -0
  2. package/codex-plugin-template/session-start.sh +125 -0
  3. package/dist/commands/change.d.ts.map +1 -1
  4. package/dist/commands/change.js +21 -3
  5. package/dist/commands/change.js.map +1 -1
  6. package/dist/commands/init.d.ts +9 -1
  7. package/dist/commands/init.d.ts.map +1 -1
  8. package/dist/commands/init.js +173 -34
  9. package/dist/commands/init.js.map +1 -1
  10. package/dist/commands/module.d.ts +5 -0
  11. package/dist/commands/module.d.ts.map +1 -0
  12. package/dist/commands/module.js +233 -0
  13. package/dist/commands/module.js.map +1 -0
  14. package/dist/commands/next.d.ts +10 -0
  15. package/dist/commands/next.d.ts.map +1 -0
  16. package/dist/commands/next.js +125 -0
  17. package/dist/commands/next.js.map +1 -0
  18. package/dist/commands/status.d.ts +12 -0
  19. package/dist/commands/status.d.ts.map +1 -1
  20. package/dist/commands/status.js +116 -1
  21. package/dist/commands/status.js.map +1 -1
  22. package/dist/commands/sync.d.ts.map +1 -1
  23. package/dist/commands/sync.js +13 -1
  24. package/dist/commands/sync.js.map +1 -1
  25. package/dist/commands/verify.js +2 -2
  26. package/dist/commands/verify.js.map +1 -1
  27. package/dist/i18n.d.ts.map +1 -1
  28. package/dist/i18n.js +68 -8
  29. package/dist/i18n.js.map +1 -1
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +30 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/lib/sync-resource-index.js +5 -5
  34. package/dist/lib/sync-resource-index.js.map +1 -1
  35. package/package.json +8 -4
  36. package/skills/architecture-designer/SKILL.en.md +1 -1
  37. package/skills/architecture-designer/SKILL.md +1 -1
  38. package/skills/prd-writer/SKILL.en.md +1 -1
  39. package/skills/prd-writer/SKILL.md +1 -1
  40. package/skills/product-designer/SKILL.en.md +1 -1
  41. package/skills/product-designer/SKILL.md +1 -1
  42. package/skills/scenario-architect/SKILL.en.md +2 -2
  43. package/skills/scenario-architect/SKILL.md +11 -5
  44. package/skills/test-writer/SKILL.en.md +1 -1
  45. package/skills/test-writer/SKILL.md +2 -2
  46. package/spec/change-management.md +5 -0
  47. package/spec/cli-json-output.md +20 -1
  48. package/spec/codex-plugin.md +138 -0
  49. package/spec/directory-convention.md +4 -2
  50. package/spec/logos-project.md +41 -5
  51. package/spec/module-naming-convention.md +74 -0
  52. package/spec/workflow.md +1 -1
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "openlogos",
3
+ "version": "1.0.0",
4
+ "description": "OpenLogos methodology — Why→What→How structured AI-driven development",
5
+ "interface": {
6
+ "display_name": "OpenLogos",
7
+ "short_description": "Structured development: PRD → Design → Scenarios → API → Code",
8
+ "default_prompt": ["What should I do next in the OpenLogos workflow?"]
9
+ }
10
+ }
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env bash
2
+ # OpenLogos Codex Plugin — SessionStart hook
3
+ # Injects current phase and next-step context into the Codex session.
4
+ # Falls back silently (returns {}) if the CLI is unavailable or project is not initialized.
5
+ #
6
+ # Output format: {"systemMessage": "...", "hookSpecificOutput": {"hookEventName": "SessionStart", "additionalContext": "..."}}
7
+ # Codex uses systemMessage; additionalContext is included for forward compatibility.
8
+
9
+ set -euo pipefail
10
+
11
+ if ! command -v openlogos &>/dev/null; then
12
+ echo "{}"
13
+ exit 0
14
+ fi
15
+
16
+ if [ ! -f "logos/logos.config.json" ]; then
17
+ echo "{}"
18
+ exit 0
19
+ fi
20
+
21
+ STATUS=$(openlogos status --format json 2>/dev/null) || { echo "{}"; exit 0; }
22
+
23
+ # Parse JSON using python3 (preferred) or node
24
+ _py_parse() {
25
+ python3 -c "import sys,json; d=json.load(sys.stdin); $1" 2>/dev/null || echo "$2"
26
+ }
27
+ _node_parse() {
28
+ node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const r=JSON.parse(d);$1}catch(e){process.stdout.write('$2')}})" 2>/dev/null || echo "$2"
29
+ }
30
+
31
+ if command -v python3 &>/dev/null; then
32
+ LOCALE=$(python3 -c "import json; d=json.load(open('logos/logos.config.json')); print(d.get('locale','en'))" 2>/dev/null || echo "en")
33
+ LIFECYCLE=$(python3 -c "import json; d=json.load(open('logos/logos.config.json')); print(d.get('lifecycle','initial'))" 2>/dev/null || echo "initial")
34
+ PROJECT_NAME=$(python3 -c "import json; d=json.load(open('logos/logos.config.json')); print(d.get('name',''))" 2>/dev/null || echo "")
35
+ CURRENT_PHASE=$(echo "$STATUS" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('current_phase') or 'all-done')" 2>/dev/null || echo "unknown")
36
+ SUGGESTION=$(echo "$STATUS" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('suggestion','Run openlogos status for next steps'))" 2>/dev/null || echo "")
37
+ ALL_DONE=$(echo "$STATUS" | python3 -c "import sys,json; d=json.load(sys.stdin); print('true' if d.get('data',{}).get('all_done') else 'false')" 2>/dev/null || echo "false")
38
+ elif command -v node &>/dev/null; then
39
+ LOCALE=$(node -e "const d=JSON.parse(require('fs').readFileSync('logos/logos.config.json','utf-8')); console.log(d.locale||'en')" 2>/dev/null || echo "en")
40
+ LIFECYCLE=$(node -e "const d=JSON.parse(require('fs').readFileSync('logos/logos.config.json','utf-8')); console.log(d.lifecycle||'initial')" 2>/dev/null || echo "initial")
41
+ PROJECT_NAME=$(node -e "const d=JSON.parse(require('fs').readFileSync('logos/logos.config.json','utf-8')); console.log(d.name||'')" 2>/dev/null || echo "")
42
+ CURRENT_PHASE=$(echo "$STATUS" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const r=JSON.parse(d);console.log((r.data||{}).current_phase||'all-done')}catch(e){console.log('unknown')}})" 2>/dev/null || echo "unknown")
43
+ SUGGESTION=$(echo "$STATUS" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const r=JSON.parse(d);console.log((r.data||{}).suggestion||'')}catch(e){console.log('')}})" 2>/dev/null || echo "")
44
+ ALL_DONE=$(echo "$STATUS" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{const r=JSON.parse(d);console.log((r.data||{}).all_done?'true':'false')}catch(e){console.log('false')}})" 2>/dev/null || echo "false")
45
+ else
46
+ echo "{}"
47
+ exit 0
48
+ fi
49
+
50
+ # Language policy
51
+ if [ "$LOCALE" = "zh" ]; then
52
+ LANG_POLICY="Language Policy: ALL output MUST be in Chinese (中文)."
53
+ else
54
+ LANG_POLICY="Language Policy: ALL output MUST be in English."
55
+ fi
56
+
57
+ # Change management status
58
+ GUARD_FILE="logos/.openlogos-guard"
59
+ CHANGE_MGMT=""
60
+ GUARD_STATUS=""
61
+
62
+ if [ "$LIFECYCLE" = "active" ] && [ -f "$GUARD_FILE" ]; then
63
+ if command -v python3 &>/dev/null; then
64
+ ACTIVE_CHANGE=$(python3 -c "import json; d=json.load(open('$GUARD_FILE')); print(d.get('activeChange',''))" 2>/dev/null || echo "")
65
+ elif command -v node &>/dev/null; then
66
+ ACTIVE_CHANGE=$(node -e "const d=JSON.parse(require('fs').readFileSync('$GUARD_FILE','utf-8')); console.log(d.activeChange||'')" 2>/dev/null || echo "")
67
+ else
68
+ ACTIVE_CHANGE=""
69
+ fi
70
+
71
+ if [ -n "$ACTIVE_CHANGE" ]; then
72
+ GUARD_STATUS="🔓 Active change: $ACTIVE_CHANGE"
73
+ CHANGE_MGMT="Change Management: ACTIVE — guard file detected. Active change proposal: '$ACTIVE_CHANGE'. Only modify files within the scope of logos/changes/$ACTIVE_CHANGE/proposal.md. When done, run openlogos merge $ACTIVE_CHANGE then openlogos archive $ACTIVE_CHANGE."
74
+ else
75
+ GUARD_STATUS="⛔ NO active change proposal"
76
+ CHANGE_MGMT="Change Management: ACTIVE — ⛔ NO guard file found. Before modifying ANY source code, you MUST first run openlogos change <slug> to create a change proposal."
77
+ fi
78
+ elif [ "$LIFECYCLE" = "active" ]; then
79
+ GUARD_STATUS="⛔ NO active change proposal"
80
+ CHANGE_MGMT="Change Management: ACTIVE — ⛔ NO guard file found. Before modifying ANY source code, you MUST first run openlogos change <slug> to create a change proposal."
81
+ else
82
+ CHANGE_MGMT="Change Management: Initial development — follow Phase progression, no change proposals needed."
83
+ fi
84
+
85
+ # Build system message
86
+ if [ "$ALL_DONE" = "true" ]; then
87
+ STATUS_LINE="📊 OpenLogos: All phases complete"
88
+ elif [ -n "$GUARD_STATUS" ]; then
89
+ STATUS_LINE="📊 OpenLogos: $CURRENT_PHASE | $GUARD_STATUS"
90
+ else
91
+ STATUS_LINE="📊 OpenLogos: $CURRENT_PHASE"
92
+ fi
93
+
94
+ # Build additional context
95
+ CONTEXT="=== OpenLogos Project Context ===
96
+ Project: $PROJECT_NAME
97
+ Locale: $LOCALE
98
+ Lifecycle: $LIFECYCLE
99
+ Current Phase: $CURRENT_PHASE
100
+ Suggested Next: $SUGGESTION
101
+ $LANG_POLICY
102
+ $CHANGE_MGMT
103
+ === End OpenLogos Context ==="
104
+
105
+ # JSON-escape context and status line
106
+ if command -v python3 &>/dev/null; then
107
+ CONTEXT_ESCAPED=$(printf '%s' "$CONTEXT" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))" 2>/dev/null || echo '""')
108
+ SYS_MSG_ESCAPED=$(printf '%s' "$STATUS_LINE" | python3 -c "import sys,json; s=sys.stdin.read(); print(json.dumps(s)[1:-1])" 2>/dev/null || echo "$STATUS_LINE")
109
+ elif command -v node &>/dev/null; then
110
+ CONTEXT_ESCAPED=$(printf '%s' "$CONTEXT" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>console.log(JSON.stringify(d)))" 2>/dev/null || echo '""')
111
+ SYS_MSG_ESCAPED=$(printf '%s' "$STATUS_LINE" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const s=JSON.stringify(d);process.stdout.write(s.slice(1,-1))})" 2>/dev/null || echo "$STATUS_LINE")
112
+ else
113
+ CONTEXT_ESCAPED='""'
114
+ SYS_MSG_ESCAPED="$STATUS_LINE"
115
+ fi
116
+
117
+ cat <<ENDJSON
118
+ {
119
+ "systemMessage": "$SYS_MSG_ESCAPED",
120
+ "hookSpecificOutput": {
121
+ "hookEventName": "SessionStart",
122
+ "additionalContext": $CONTEXT_ESCAPED
123
+ }
124
+ }
125
+ ENDJSON
@@ -1 +1 @@
1
- {"version":3,"file":"change.d.ts","sourceRoot":"","sources":["../../src/commands/change.ts"],"names":[],"mappings":"AAIA,wBAAgB,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,QAwDnC"}
1
+ {"version":3,"file":"change.d.ts","sourceRoot":"","sources":["../../src/commands/change.ts"],"names":[],"mappings":"AAIA,wBAAgB,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,QA0EnC"}
@@ -1,4 +1,4 @@
1
- import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
1
+ import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { readLocale, t, proposalTemplate, tasksTemplate } from '../i18n.js';
4
4
  export function change(slug) {
@@ -16,11 +16,30 @@ export function change(slug) {
16
16
  process.exit(1);
17
17
  }
18
18
  const changePath = join(root, 'logos', 'changes', slug);
19
+ const locale = readLocale(root);
19
20
  if (existsSync(changePath)) {
20
21
  console.error(`Error: Change proposal '${slug}' already exists.`);
21
22
  process.exit(1);
22
23
  }
23
- const locale = readLocale(root);
24
+ const guardPath = join(root, 'logos', '.openlogos-guard');
25
+ if (existsSync(guardPath)) {
26
+ try {
27
+ const guard = JSON.parse(readFileSync(guardPath, 'utf-8'));
28
+ const activeChange = typeof guard.activeChange === 'string' ? guard.activeChange : null;
29
+ const activeChangePath = activeChange ? join(root, 'logos', 'changes', activeChange) : null;
30
+ const archivedChangePath = activeChange ? join(root, 'logos', 'changes', 'archive', activeChange) : null;
31
+ if (activeChange && activeChangePath && archivedChangePath && existsSync(activeChangePath) && !existsSync(archivedChangePath)) {
32
+ console.error(t(locale, 'change.guardConflict', { activeChange }));
33
+ console.error(t(locale, 'change.guardConflictHint', { activeChange }));
34
+ process.exit(1);
35
+ }
36
+ }
37
+ catch {
38
+ console.error(t(locale, 'change.guardInvalid'));
39
+ console.error(t(locale, 'change.guardInvalidHint'));
40
+ process.exit(1);
41
+ }
42
+ }
24
43
  console.log(`\n${t(locale, 'change.creating', { slug })}\n`);
25
44
  const deltaDirs = ['deltas/prd', 'deltas/api', 'deltas/database', 'deltas/scenario'];
26
45
  mkdirSync(changePath, { recursive: true });
@@ -32,7 +51,6 @@ export function change(slug) {
32
51
  writeFileSync(join(changePath, 'tasks.md'), tasksTemplate(locale));
33
52
  console.log(` ✓ logos/changes/${slug}/tasks.md`);
34
53
  console.log(` ✓ logos/changes/${slug}/deltas/`);
35
- const guardPath = join(root, 'logos', '.openlogos-guard');
36
54
  const guard = JSON.stringify({
37
55
  activeChange: slug,
38
56
  createdAt: new Date().toISOString(),
@@ -1 +1 @@
1
- {"version":3,"file":"change.js","sourceRoot":"","sources":["../../src/commands/change.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"change.js","sourceRoot":"","sources":["../../src/commands/change.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { type Locale } from '../i18n.js';
2
- export type AiTool = 'claude-code' | 'opencode' | 'cursor' | 'other' | 'all';
2
+ export type AiTool = 'claude-code' | 'opencode' | 'codex' | 'cursor' | 'other' | 'all';
3
3
  type NameSource = 'argument' | 'package.json' | 'Cargo.toml' | 'pyproject.toml' | 'directory';
4
4
  interface NameResult {
5
5
  name: string;
@@ -11,6 +11,14 @@ export declare const SKILL_NAMES: readonly ["project-init", "prd-writer", "produ
11
11
  export declare function findSkillsSource(): string | null;
12
12
  export declare function findSpecSource(): string | null;
13
13
  export declare function findOpenCodePluginTemplateSource(): string | null;
14
+ export declare function findCodexPluginTemplateSource(): string | null;
15
+ export declare function deployCodexPlugin(root: string, locale?: Locale): {
16
+ target: string;
17
+ config: {
18
+ created: boolean;
19
+ updated: boolean;
20
+ };
21
+ } | null;
14
22
  export declare function deployOpenCodePlugin(root: string, locale?: Locale): {
15
23
  target: string;
16
24
  config: {
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAiD,MAAM,YAAY,CAAC;AAExF,MAAM,MAAM,MAAM,GAAG,aAAa,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;AAE7E,KAAK,UAAU,GAAG,UAAU,GAAG,cAAc,GAAG,YAAY,GAAG,gBAAgB,GAAG,WAAW,CAAC;AAE9F,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA+B9D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAI1D;AAsFD,eAAO,MAAM,WAAW,uPAcd,CAAC;AAkBX,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAahD;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAW9C;AAED,wBAAgB,gCAAgC,IAAI,MAAM,GAAG,IAAI,CAahE;AAuDD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,MAAa,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA6BzK;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAalE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,GAAE,SAAqB,GAAG,MAAM,CAkE1F;AAYD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAa,EACrB,SAAS,GAAE,SAAqB,EAChC,YAAY,CAAC,EAAE,MAAM,GACpB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAkC1C;AA0ID,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE7C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAiB,GAAG,MAAM,CA4DjG;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAYvE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,EAAE,SAAS,GAAE,SAAqB,GAAG,MAAM,CAoJtI;AAED,wBAAsB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,iBA4FvF"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAAiD,MAAM,YAAY,CAAC;AAExF,MAAM,MAAM,MAAM,GAAG,aAAa,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;AAEvF,KAAK,UAAU,GAAG,UAAU,GAAG,cAAc,GAAG,YAAY,GAAG,gBAAgB,GAAG,WAAW,CAAC;AAE9F,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CA+B9D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAI1D;AAwFD,eAAO,MAAM,WAAW,uPAcd,CAAC;AAkBX,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAahD;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAW9C;AAED,wBAAgB,gCAAgC,IAAI,MAAM,GAAG,IAAI,CAahE;AAED,wBAAgB,6BAA6B,IAAI,MAAM,GAAG,IAAI,CAa7D;AAqCD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,MAAa,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,CAyBhJ;AAuDD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,MAAa,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA6BzK;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAalE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,GAAE,SAAqB,GAAG,MAAM,CAkE1F;AAYD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAa,EACrB,SAAS,GAAE,SAAqB,EAChC,YAAY,CAAC,EAAE,MAAM,GACpB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAgD1C;AA+KD,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE7C,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAiB,GAAG,MAAM,CA4DjG;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAqBvE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,EAAE,SAAS,GAAE,SAAqB,GAAG,MAAM,CAoJtI;AAED,wBAAsB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,iBAwGvF"}
@@ -1,4 +1,4 @@
1
- import { mkdirSync, writeFileSync, readFileSync, existsSync, copyFileSync, readdirSync } from 'node:fs';
1
+ import { mkdirSync, writeFileSync, readFileSync, existsSync, copyFileSync, readdirSync, chmodSync } from 'node:fs';
2
2
  import { join, basename, dirname } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { createInterface } from 'node:readline';
@@ -84,7 +84,7 @@ async function chooseLocale() {
84
84
  if (!isTTY()) {
85
85
  console.error('Error: --locale is required in non-interactive mode.');
86
86
  console.error('');
87
- console.error('Usage: openlogos init --locale <en|zh> [--ai-tool <claude-code|opencode|cursor|other>] [name]');
87
+ console.error('Usage: openlogos init --locale <en|zh> [--ai-tool <claude-code|opencode|codex|cursor|other|all>] [name]');
88
88
  console.error('');
89
89
  console.error('Ask the user to choose a language first:');
90
90
  console.error(' --locale en English');
@@ -103,6 +103,7 @@ async function chooseAiTool(locale) {
103
103
  console.log(`\n${t(locale, 'init.aiToolHeader')}`);
104
104
  console.log(t(locale, 'init.aiToolClaudeCode'));
105
105
  console.log(t(locale, 'init.aiToolOpenCode'));
106
+ console.log(t(locale, 'init.aiToolCodex'));
106
107
  console.log(t(locale, 'init.aiToolCursor'));
107
108
  console.log(t(locale, 'init.aiToolOther'));
108
109
  console.log(t(locale, 'init.aiToolAll') + '\n');
@@ -110,10 +111,12 @@ async function chooseAiTool(locale) {
110
111
  if (answer === '2')
111
112
  return 'opencode';
112
113
  if (answer === '3')
113
- return 'cursor';
114
+ return 'codex';
114
115
  if (answer === '4')
115
- return 'other';
116
+ return 'cursor';
116
117
  if (answer === '5')
118
+ return 'other';
119
+ if (answer === '6')
117
120
  return 'all';
118
121
  return 'claude-code';
119
122
  }
@@ -184,6 +187,71 @@ export function findOpenCodePluginTemplateSource() {
184
187
  return devTemplate;
185
188
  return null;
186
189
  }
190
+ export function findCodexPluginTemplateSource() {
191
+ const currentFile = fileURLToPath(import.meta.url);
192
+ const currentDir = dirname(currentFile);
193
+ // npm package layout: <pkg>/dist/commands/init.js → <pkg>/codex-plugin-template/
194
+ const packageTemplate = join(currentDir, '..', '..', 'codex-plugin-template');
195
+ if (existsSync(packageTemplate))
196
+ return packageTemplate;
197
+ // dev layout: cli/src/commands/init.ts → <repo>/plugin-codex/
198
+ const devTemplate = join(currentDir, '..', '..', '..', 'plugin-codex');
199
+ if (existsSync(devTemplate))
200
+ return devTemplate;
201
+ return null;
202
+ }
203
+ function mergeCodexConfig(root) {
204
+ const configDir = join(root, '.codex');
205
+ const configPath = join(configDir, 'config.toml');
206
+ const pluginBlock = `\n[plugins.openlogos]\nenabled = true\n`;
207
+ const hookBlock = `\n[[hooks.SessionStart]]\n[[hooks.SessionStart.hooks]]\ntype = "command"\ncommand = ".codex-plugin/hooks/session-start.sh"\ntimeout = 5\nasync = false\nstatusMessage = "Loading OpenLogos phase context..."\n`;
208
+ mkdirSync(configDir, { recursive: true });
209
+ if (!existsSync(configPath)) {
210
+ writeFileSync(configPath, pluginBlock + hookBlock);
211
+ return { created: true, updated: true };
212
+ }
213
+ const existing = readFileSync(configPath, 'utf-8');
214
+ let content = existing;
215
+ let changed = false;
216
+ const hasPluginBlock = content.includes('[plugins.openlogos]');
217
+ const hasOpenLogosHook = content.includes('command = ".codex-plugin/hooks/session-start.sh"');
218
+ if (!hasPluginBlock) {
219
+ content += pluginBlock;
220
+ changed = true;
221
+ }
222
+ if (!hasOpenLogosHook) {
223
+ content += hookBlock;
224
+ changed = true;
225
+ }
226
+ if (changed) {
227
+ writeFileSync(configPath, content);
228
+ }
229
+ return { created: false, updated: changed };
230
+ }
231
+ export function deployCodexPlugin(root, locale = 'en') {
232
+ const source = findCodexPluginTemplateSource();
233
+ if (!source || !existsSync(source))
234
+ return null;
235
+ const pluginDir = join(root, '.codex-plugin');
236
+ const hooksDir = join(pluginDir, 'hooks');
237
+ mkdirSync(hooksDir, { recursive: true });
238
+ const pluginJsonSrc = join(source, 'plugin.json');
239
+ const hookSrc = join(source, 'session-start.sh');
240
+ if (!existsSync(pluginJsonSrc) || !existsSync(hookSrc))
241
+ return null;
242
+ copyFileSync(pluginJsonSrc, join(pluginDir, 'plugin.json'));
243
+ const hookDest = join(hooksDir, 'session-start.sh');
244
+ copyFileSync(hookSrc, hookDest);
245
+ try {
246
+ chmodSync(hookDest, 0o755);
247
+ }
248
+ catch { /* ignore on platforms that don't support chmod */ }
249
+ const configResult = mergeCodexConfig(root);
250
+ const targetLabel = locale === 'zh'
251
+ ? '.codex-plugin/ + .codex/config.toml'
252
+ : '.codex-plugin/ + .codex/config.toml';
253
+ return { target: targetLabel, config: configResult };
254
+ }
187
255
  function mergeOpenCodeConfig(root) {
188
256
  const configPath = join(root, 'opencode.json');
189
257
  const defaultConfig = {
@@ -366,6 +434,19 @@ export function deploySkills(root, aiTool, locale = 'en', lifecycle = 'initial',
366
434
  writeFileSync(join(targetDir, 'openlogos-policy.mdc'), generatePolicyMdc(locale, lifecycle));
367
435
  return { target: '.cursor/rules/', count };
368
436
  }
437
+ if (aiTool === 'codex') {
438
+ const targetDir = join(root, '.agents', 'skills');
439
+ for (const name of SKILL_NAMES) {
440
+ const skillDir = join(targetDir, name);
441
+ mkdirSync(skillDir, { recursive: true });
442
+ const srcPath = resolveSkillFile(source, name, locale);
443
+ if (srcPath) {
444
+ copyFileSync(srcPath, join(skillDir, 'SKILL.md'));
445
+ count++;
446
+ }
447
+ }
448
+ return { target: '.agents/skills/', count };
449
+ }
369
450
  const targetDir = join(root, 'logos', 'skills');
370
451
  for (const name of SKILL_NAMES) {
371
452
  const skillDir = join(targetDir, name);
@@ -387,18 +468,27 @@ function shouldIncludeActiveSkills(aiTool, target) {
387
468
  return target === 'claude';
388
469
  if (aiTool === 'opencode')
389
470
  return target === 'agents';
471
+ if (aiTool === 'codex')
472
+ return target === 'agents';
390
473
  return true;
391
474
  }
392
- function generateActiveSkillsSection(locale) {
475
+ function skillBasePath(aiTool, target) {
476
+ if (aiTool === 'codex' && target === 'agents') {
477
+ return '.agents/skills';
478
+ }
479
+ return 'logos/skills';
480
+ }
481
+ function generateActiveSkillsSection(locale, aiTool, target) {
393
482
  let section = '';
483
+ const basePath = skillBasePath(aiTool, target);
394
484
  for (const name of SKILL_NAMES) {
395
485
  const desc = SKILL_DESCRIPTIONS[name]?.[locale] ?? SKILL_DESCRIPTIONS[name]?.en ?? name;
396
- section += `- \`skills/${name}/\` — ${desc}\n`;
486
+ section += `- \`${basePath}/${name}/SKILL.md\` — ${desc}\n`;
397
487
  }
398
488
  return section;
399
489
  }
400
- function skillPath(name) {
401
- return `logos/skills/${name}/SKILL.md`;
490
+ function skillPath(name, aiTool, target) {
491
+ return `${skillBasePath(aiTool, target)}/${name}/SKILL.md`;
402
492
  }
403
493
  function generatePhaseDetectionPlain(locale) {
404
494
  if (locale === 'zh') {
@@ -411,7 +501,14 @@ function generatePhaseDetectionPlain(locale) {
411
501
  - API 存在但 \`logos/resources/test/\` 为空 → 建议 Phase 3 Step 3a(test-writer)
412
502
  - 测试用例存在但 \`logos/resources/scenario/\` 为空 → 建议 Phase 3 Step 3b(test-orchestrator,仅 API 项目)
413
503
  - 编排测试存在但 \`logos/resources/implementation/\` 为空 → 建议 Phase 3 Step 4(code-implementor)
414
- - 代码已生成但 \`logos/resources/verify/\` 为空 → 建议 Phase 3 Step 5(运行测试后 \`openlogos verify\`)`;
504
+ - 代码已生成但 \`logos/resources/verify/\` 为空 → 建议 Phase 3 Step 5(运行测试后 \`openlogos verify\`)
505
+
506
+ 文件命名规范(模块前缀):
507
+ - 所有设计文档遵循 \`<module>-<序号>-<类型>.md\` 格式,初始项目默认使用 \`core-\` 前缀
508
+ - 场景实现文件:\`<module>-SXX-<slug>.md\`(如 \`core-S01-cli-init.md\`)
509
+ - 测试用例文件:\`<module>-SXX-test-cases.md\`(如 \`core-S01-test-cases.md\`)
510
+ - 场景编号全局唯一,由 \`logos-project.yaml\` 的 \`scenario_counter.next_id\` 维护,严禁不同模块从 S01 重新开始
511
+ - 多模块状态:\`openlogos status\` 聚合展示所有模块(in-progress 置顶);\`openlogos next\` 单模块直接建议,多模块并列列出,无 in-progress 时提示 \`module add\``;
415
512
  }
416
513
  return `Phase detection logic:
417
514
  - \`logos/resources/prd/1-product-requirements/\` is empty → suggest Phase 1 (prd-writer)
@@ -422,31 +519,52 @@ function generatePhaseDetectionPlain(locale) {
422
519
  - API exists but \`logos/resources/test/\` is empty → suggest Phase 3 Step 3a (test-writer)
423
520
  - test cases exist but \`logos/resources/scenario/\` is empty → suggest Phase 3 Step 3b (test-orchestrator, API projects only)
424
521
  - orchestration tests exist but \`logos/resources/implementation/\` is empty → suggest Phase 3 Step 4 (code-implementor)
425
- - code generated but \`logos/resources/verify/\` is empty → suggest Phase 3 Step 5 (run tests then \`openlogos verify\`)`;
522
+ - code generated but \`logos/resources/verify/\` is empty → suggest Phase 3 Step 5 (run tests then \`openlogos verify\`)
523
+
524
+ File naming convention (module prefix):
525
+ - All design documents follow \`<module>-<number>-<type>.md\` format; default module is \`core-\` prefix
526
+ - Scenario implementation files: \`<module>-SXX-<slug>.md\` (e.g. \`core-S01-cli-init.md\`)
527
+ - Test case files: \`<module>-SXX-test-cases.md\` (e.g. \`core-S01-test-cases.md\`)
528
+ - Scenario numbers are globally unique, maintained by \`scenario_counter.next_id\` in \`logos-project.yaml\`; never restart from S01 for a new module
529
+ - Multi-module status: \`openlogos status\` shows all modules (in-progress first); \`openlogos next\` gives direct suggestion for single module, lists all for multiple, prompts \`module add\` when none in-progress`;
426
530
  }
427
- function generatePhaseDetectionWithSkills(locale) {
531
+ function generatePhaseDetectionWithSkills(locale, aiTool, target) {
428
532
  if (locale === 'zh') {
429
533
  return `Phase 检测逻辑(检测到对应阶段时,**必须先读取** Skill 文件并按其步骤执行):
430
- - \`logos/resources/prd/1-product-requirements/\` 为空 → Phase 1 → **读取 \`${skillPath('prd-writer')}\` 并按其步骤执行**
431
- - 需求存在但 \`2-product-design/\` 为空 → Phase 2 → **读取 \`${skillPath('product-designer')}\` 并按其步骤执行**
432
- - 设计存在但 \`3-technical-plan/1-architecture/\` 为空 → Phase 3 Step 0 → **读取 \`${skillPath('architecture-designer')}\` 并按其步骤执行**
433
- - 架构存在但 \`3-technical-plan/2-scenario-implementation/\` 为空 → Phase 3 Step 1 → **读取 \`${skillPath('scenario-architect')}\` 并按其步骤执行**
434
- - 场景存在但 \`logos/resources/api/\` 为空 → Phase 3 Step 2 → **读取 \`${skillPath('api-designer')}\` 和 \`${skillPath('db-designer')}\` 并按其步骤执行**
435
- - API 存在但 \`logos/resources/test/\` 为空 → Phase 3 Step 3a → **读取 \`${skillPath('test-writer')}\` 并按其步骤执行**
436
- - 测试用例存在但 \`logos/resources/scenario/\` 为空 → Phase 3 Step 3b → **读取 \`${skillPath('test-orchestrator')}\` 并按其步骤执行**(仅 API 项目)
437
- - 编排测试存在但 \`logos/resources/implementation/\` 为空 → Phase 3 Step 4 → **读取 \`${skillPath('code-implementor')}\` 并按其步骤执行**(完成后可用 \`${skillPath('code-reviewer')}\` 进行代码审查)
438
- - 代码已生成但 \`logos/resources/verify/\` 为空 → Phase 3 Step 5(运行测试后 \`openlogos verify\`)`;
534
+ - \`logos/resources/prd/1-product-requirements/\` 为空 → Phase 1 → **读取 \`${skillPath('prd-writer', aiTool, target)}\` 并按其步骤执行**
535
+ - 需求存在但 \`2-product-design/\` 为空 → Phase 2 → **读取 \`${skillPath('product-designer', aiTool, target)}\` 并按其步骤执行**
536
+ - 设计存在但 \`3-technical-plan/1-architecture/\` 为空 → Phase 3 Step 0 → **读取 \`${skillPath('architecture-designer', aiTool, target)}\` 并按其步骤执行**
537
+ - 架构存在但 \`3-technical-plan/2-scenario-implementation/\` 为空 → Phase 3 Step 1 → **读取 \`${skillPath('scenario-architect', aiTool, target)}\` 并按其步骤执行**
538
+ - 场景存在但 \`logos/resources/api/\` 为空 → Phase 3 Step 2 → **读取 \`${skillPath('api-designer', aiTool, target)}\` 和 \`${skillPath('db-designer', aiTool, target)}\` 并按其步骤执行**
539
+ - API 存在但 \`logos/resources/test/\` 为空 → Phase 3 Step 3a → **读取 \`${skillPath('test-writer', aiTool, target)}\` 并按其步骤执行**
540
+ - 测试用例存在但 \`logos/resources/scenario/\` 为空 → Phase 3 Step 3b → **读取 \`${skillPath('test-orchestrator', aiTool, target)}\` 并按其步骤执行**(仅 API 项目)
541
+ - 编排测试存在但 \`logos/resources/implementation/\` 为空 → Phase 3 Step 4 → **读取 \`${skillPath('code-implementor', aiTool, target)}\` 并按其步骤执行**(完成后可用 \`${skillPath('code-reviewer', aiTool, target)}\` 进行代码审查)
542
+ - 代码已生成但 \`logos/resources/verify/\` 为空 → Phase 3 Step 5(运行测试后 \`openlogos verify\`)
543
+
544
+ 文件命名规范(模块前缀):
545
+ - 所有设计文档遵循 \`<module>-<序号>-<类型>.md\` 格式,初始项目默认使用 \`core-\` 前缀
546
+ - 场景实现文件:\`<module>-SXX-<slug>.md\`(如 \`core-S01-cli-init.md\`)
547
+ - 测试用例文件:\`<module>-SXX-test-cases.md\`(如 \`core-S01-test-cases.md\`)
548
+ - 场景编号全局唯一,由 \`logos-project.yaml\` 的 \`scenario_counter.next_id\` 维护,严禁不同模块从 S01 重新开始
549
+ - 多模块状态:\`openlogos status\` 聚合展示所有模块(in-progress 置顶);\`openlogos next\` 单模块直接建议,多模块并列列出,无 in-progress 时提示 \`module add\``;
439
550
  }
440
551
  return `Phase detection logic (**when a phase is detected, you MUST read the corresponding Skill file and follow its steps**):
441
- - \`logos/resources/prd/1-product-requirements/\` is empty → Phase 1 → **read \`${skillPath('prd-writer')}\` and follow its steps**
442
- - requirements exist but \`2-product-design/\` is empty → Phase 2 → **read \`${skillPath('product-designer')}\` and follow its steps**
443
- - design exists but \`3-technical-plan/1-architecture/\` is empty → Phase 3 Step 0 → **read \`${skillPath('architecture-designer')}\` and follow its steps**
444
- - architecture exists but \`3-technical-plan/2-scenario-implementation/\` is empty → Phase 3 Step 1 → **read \`${skillPath('scenario-architect')}\` and follow its steps**
445
- - scenarios exist but \`logos/resources/api/\` is empty → Phase 3 Step 2 → **read \`${skillPath('api-designer')}\` and \`${skillPath('db-designer')}\` and follow their steps**
446
- - API exists but \`logos/resources/test/\` is empty → Phase 3 Step 3a → **read \`${skillPath('test-writer')}\` and follow its steps**
447
- - test cases exist but \`logos/resources/scenario/\` is empty → Phase 3 Step 3b → **read \`${skillPath('test-orchestrator')}\` and follow its steps** (API projects only)
448
- - orchestration tests exist but \`logos/resources/implementation/\` is empty → Phase 3 Step 4 → **read \`${skillPath('code-implementor')}\` and follow its steps** (after completion, use \`${skillPath('code-reviewer')}\` for code review)
449
- - code generated but \`logos/resources/verify/\` is empty → Phase 3 Step 5 (run tests then \`openlogos verify\`)`;
552
+ - \`logos/resources/prd/1-product-requirements/\` is empty → Phase 1 → **read \`${skillPath('prd-writer', aiTool, target)}\` and follow its steps**
553
+ - requirements exist but \`2-product-design/\` is empty → Phase 2 → **read \`${skillPath('product-designer', aiTool, target)}\` and follow its steps**
554
+ - design exists but \`3-technical-plan/1-architecture/\` is empty → Phase 3 Step 0 → **read \`${skillPath('architecture-designer', aiTool, target)}\` and follow its steps**
555
+ - architecture exists but \`3-technical-plan/2-scenario-implementation/\` is empty → Phase 3 Step 1 → **read \`${skillPath('scenario-architect', aiTool, target)}\` and follow its steps**
556
+ - scenarios exist but \`logos/resources/api/\` is empty → Phase 3 Step 2 → **read \`${skillPath('api-designer', aiTool, target)}\` and \`${skillPath('db-designer', aiTool, target)}\` and follow their steps**
557
+ - API exists but \`logos/resources/test/\` is empty → Phase 3 Step 3a → **read \`${skillPath('test-writer', aiTool, target)}\` and follow its steps**
558
+ - test cases exist but \`logos/resources/scenario/\` is empty → Phase 3 Step 3b → **read \`${skillPath('test-orchestrator', aiTool, target)}\` and follow its steps** (API projects only)
559
+ - orchestration tests exist but \`logos/resources/implementation/\` is empty → Phase 3 Step 4 → **read \`${skillPath('code-implementor', aiTool, target)}\` and follow its steps** (after completion, use \`${skillPath('code-reviewer', aiTool, target)}\` for code review)
560
+ - code generated but \`logos/resources/verify/\` is empty → Phase 3 Step 5 (run tests then \`openlogos verify\`)
561
+
562
+ File naming convention (module prefix):
563
+ - All design documents follow \`<module>-<number>-<type>.md\` format; default module is \`core-\` prefix
564
+ - Scenario implementation files: \`<module>-SXX-<slug>.md\` (e.g. \`core-S01-cli-init.md\`)
565
+ - Test case files: \`<module>-SXX-test-cases.md\` (e.g. \`core-S01-test-cases.md\`)
566
+ - Scenario numbers are globally unique, maintained by \`scenario_counter.next_id\` in \`logos-project.yaml\`; never restart from S01 for a new module
567
+ - Multi-module status: \`openlogos status\` shows all modules (in-progress first); \`openlogos next\` gives direct suggestion for single module, lists all for multiple, prompts \`module add\` when none in-progress`;
450
568
  }
451
569
  function generateStep4ExecutionRules(locale) {
452
570
  if (locale === 'zh') {
@@ -510,7 +628,7 @@ const DIRECTORIES = [
510
628
  ];
511
629
  export function createLogosConfig(name, locale, aiTool = 'cursor') {
512
630
  const aiToolValue = aiTool === 'all'
513
- ? ['claude-code', 'opencode', 'cursor']
631
+ ? ['claude-code', 'opencode', 'codex', 'cursor']
514
632
  : aiTool;
515
633
  return JSON.stringify({
516
634
  name,
@@ -577,6 +695,15 @@ export function createLogosProject(name, locale) {
577
695
 
578
696
  tech_stack: {}
579
697
 
698
+ scenario_counter:
699
+ next_id: 1
700
+
701
+ modules:
702
+ - id: core
703
+ name: ${locale === 'zh' ? '核心功能' : 'Core'}
704
+ status: in-progress
705
+ loop_phase: requirements
706
+
580
707
  resource_index: []
581
708
 
582
709
  ${conventionsForYaml(locale)}
@@ -626,7 +753,7 @@ When the user's request is vague or they ask "what should I do next":
626
753
  3. Provide a ready-to-use prompt the user can directly say
627
754
  4. Never start generating documents without confirming key information
628
755
 
629
- ${includeSkills ? generatePhaseDetectionWithSkills(locale) : generatePhaseDetectionPlain(locale)}
756
+ ${includeSkills ? generatePhaseDetectionWithSkills(locale, aiTool, target) : generatePhaseDetectionPlain(locale)}
630
757
 
631
758
  ${generateStep4ExecutionRules(locale)}
632
759
 
@@ -639,7 +766,7 @@ ${generateDocumentPostEditVerify(locale)}
639
766
  content += `
640
767
  ## Active Skills
641
768
  ${skillAutoLoadInstr}
642
- ${generateActiveSkillsSection(locale)}`;
769
+ ${generateActiveSkillsSection(locale, aiTool, target)}`;
643
770
  }
644
771
  const changeMgmt = lifecycle === 'active'
645
772
  ? (locale === 'zh'
@@ -731,7 +858,7 @@ export async function init(name, options) {
731
858
  process.exit(1);
732
859
  }
733
860
  const locale = options?.locale === 'zh' ? 'zh' : options?.locale === 'en' ? 'en' : await chooseLocale();
734
- const aiTool = options?.aiTool === 'claude-code' ? 'claude-code' : options?.aiTool === 'opencode' ? 'opencode' : options?.aiTool === 'cursor' ? 'cursor' : options?.aiTool === 'other' ? 'other' : options?.aiTool === 'all' ? 'all' : await chooseAiTool(locale);
861
+ const aiTool = options?.aiTool === 'claude-code' ? 'claude-code' : options?.aiTool === 'opencode' ? 'opencode' : options?.aiTool === 'codex' ? 'codex' : options?.aiTool === 'cursor' ? 'cursor' : options?.aiTool === 'other' ? 'other' : options?.aiTool === 'all' ? 'all' : await chooseAiTool(locale);
735
862
  const { name: projectName, source: nameSource } = await resolveProjectName(locale, root, name);
736
863
  const sourceLabel = {
737
864
  'argument': '',
@@ -755,7 +882,7 @@ export async function init(name, options) {
755
882
  console.log(` ✓ AGENTS.md`);
756
883
  writeFileSync(join(root, 'CLAUDE.md'), createAgentsMd(locale, aiTool, 'claude', 'initial'));
757
884
  console.log(` ✓ CLAUDE.md`);
758
- const deployTools = aiTool === 'all' ? ['claude-code', 'opencode', 'cursor'] : [aiTool];
885
+ const deployTools = aiTool === 'all' ? ['claude-code', 'opencode', 'codex', 'cursor'] : [aiTool];
759
886
  for (const tool of deployTools) {
760
887
  const deployResult = deploySkills(root, tool, locale, 'initial');
761
888
  if (deployResult && deployResult.count > 0) {
@@ -777,6 +904,18 @@ export async function init(name, options) {
777
904
  }
778
905
  }
779
906
  }
907
+ if (aiTool === 'codex' || aiTool === 'all') {
908
+ const codexResult = deployCodexPlugin(root, locale);
909
+ if (codexResult) {
910
+ console.log(` ✓ ${t(locale, 'init.codexPluginDeployed', { target: codexResult.target })}`);
911
+ if (codexResult.config.created) {
912
+ console.log(` ✓ ${t(locale, 'init.codexConfigCreated')}`);
913
+ }
914
+ else if (codexResult.config.updated) {
915
+ console.log(` ✓ ${t(locale, 'init.codexConfigUpdated')}`);
916
+ }
917
+ }
918
+ }
780
919
  const specResult = deploySpecs(root);
781
920
  if (specResult && specResult.count > 0) {
782
921
  console.log(` ✓ ${specResult.count} specs deployed to logos/spec/`);