@miniidealab/openlogos 0.7.3 → 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.
- package/codex-plugin-template/plugin.json +10 -0
- package/codex-plugin-template/session-start.sh +125 -0
- package/dist/commands/change.d.ts.map +1 -1
- package/dist/commands/change.js +21 -3
- package/dist/commands/change.js.map +1 -1
- package/dist/commands/init.d.ts +9 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +173 -34
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/module.d.ts +5 -0
- package/dist/commands/module.d.ts.map +1 -0
- package/dist/commands/module.js +233 -0
- package/dist/commands/module.js.map +1 -0
- package/dist/commands/next.d.ts +10 -0
- package/dist/commands/next.d.ts.map +1 -0
- package/dist/commands/next.js +125 -0
- package/dist/commands/next.js.map +1 -0
- package/dist/commands/status.d.ts +12 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +116 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +13 -1
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/verify.js +2 -2
- package/dist/commands/verify.js.map +1 -1
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +68 -8
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/sync-resource-index.js +5 -5
- package/dist/lib/sync-resource-index.js.map +1 -1
- package/package.json +8 -4
- package/skills/architecture-designer/SKILL.en.md +1 -1
- package/skills/architecture-designer/SKILL.md +1 -1
- package/skills/prd-writer/SKILL.en.md +1 -1
- package/skills/prd-writer/SKILL.md +1 -1
- package/skills/product-designer/SKILL.en.md +1 -1
- package/skills/product-designer/SKILL.md +1 -1
- package/skills/scenario-architect/SKILL.en.md +2 -2
- package/skills/scenario-architect/SKILL.md +5 -5
- package/skills/test-writer/SKILL.en.md +1 -1
- package/skills/test-writer/SKILL.md +2 -2
- package/spec/change-management.md +5 -0
- package/spec/cli-json-output.md +20 -1
- package/spec/codex-plugin.md +138 -0
- package/spec/directory-convention.md +4 -2
- package/spec/logos-project.md +41 -5
- package/spec/module-naming-convention.md +74 -0
- 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,
|
|
1
|
+
{"version":3,"file":"change.d.ts","sourceRoot":"","sources":["../../src/commands/change.ts"],"names":[],"mappings":"AAIA,wBAAgB,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,QA0EnC"}
|
package/dist/commands/change.js
CHANGED
|
@@ -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
|
|
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":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"change.js","sourceRoot":"","sources":["../../src/commands/change.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/commands/init.js
CHANGED
|
@@ -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 '
|
|
114
|
+
return 'codex';
|
|
114
115
|
if (answer === '4')
|
|
115
|
-
return '
|
|
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
|
|
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 += `-
|
|
486
|
+
section += `- \`${basePath}/${name}/SKILL.md\` — ${desc}\n`;
|
|
397
487
|
}
|
|
398
488
|
return section;
|
|
399
489
|
}
|
|
400
|
-
function skillPath(name) {
|
|
401
|
-
return
|
|
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/`);
|