@khanhcan148/mk 0.1.29 → 0.1.31
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/package.json
CHANGED
|
@@ -285,9 +285,9 @@ function buildToml(projectRoot = PACKAGE_ROOT) {
|
|
|
285
285
|
const hookBlocks = buildHookBlocks(projectRoot);
|
|
286
286
|
|
|
287
287
|
const parts = [
|
|
288
|
-
'# codex >=0.
|
|
288
|
+
'# codex >=0.130.0 <0.140.0',
|
|
289
289
|
'',
|
|
290
|
-
emitTable('features', {
|
|
290
|
+
emitTable('features', { hooks: true }),
|
|
291
291
|
'',
|
|
292
292
|
emitHookGroups('hooks.PreToolUse', hookBlocks['hooks.PreToolUse'].items),
|
|
293
293
|
emitHookGroups('hooks.PostToolUse', hookBlocks['hooks.PostToolUse'].items),
|
|
@@ -36,6 +36,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
36
36
|
import { KIT_INTERNAL_SKILLS, COPY_FILTER_PATTERNS } from '../src/lib/constants.js';
|
|
37
37
|
import { loadModelMap } from '../src/lib/runtime-codex.js';
|
|
38
38
|
import { rewriteKitPaths } from '../src/lib/codex-rewrite.js';
|
|
39
|
+
import { rewriteAskUserQuestionForCodex } from '../src/lib/codex-askuser-rewrite.js';
|
|
39
40
|
|
|
40
41
|
// ---------------------------------------------------------------------------
|
|
41
42
|
// Constants
|
|
@@ -147,113 +148,6 @@ function transformFrontmatter(content, map) {
|
|
|
147
148
|
return `---\n${normalizedFm}---\n${body}`;
|
|
148
149
|
}
|
|
149
150
|
|
|
150
|
-
// ---------------------------------------------------------------------------
|
|
151
|
-
// AskUserQuestion semantic rewrite for Codex
|
|
152
|
-
// ---------------------------------------------------------------------------
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* The gate section injected into every converted .md file that contains
|
|
156
|
-
* AskUserQuestion calls. Placed after the closing `---` frontmatter delimiter
|
|
157
|
-
* for SKILL.md files, or at the start of the file for reference files.
|
|
158
|
-
*/
|
|
159
|
-
const CODEX_GATE_SECTION = `## Codex Interaction Gates
|
|
160
|
-
This skill was converted from Claude Code. Treat every \`AskUserQuestion(...)\` block below as a blocking Codex prompt gate:
|
|
161
|
-
- Render the question, header, and options in normal chat.
|
|
162
|
-
- Stop immediately after presenting the prompt.
|
|
163
|
-
- Wait for the user's next reply before continuing.
|
|
164
|
-
- Do not proceed to later phases until the answer is available.
|
|
165
|
-
- If running in a non-interactive or sub-agent context, use the fallback stated near the prompt.
|
|
166
|
-
`;
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Rewrite AskUserQuestion prose phrases outside fenced blocks.
|
|
170
|
-
* Works line-by-line to track fence state, leaving content inside
|
|
171
|
-
* triple-backtick fences byte-for-byte unchanged.
|
|
172
|
-
*
|
|
173
|
-
* Rewrites applied outside fences:
|
|
174
|
-
* "Use AskUserQuestion to" → "Use a Codex prompt gate to"
|
|
175
|
-
* "If AskUserQuestion unavailable" → "If running non-interactively (Codex sub-agent context)"
|
|
176
|
-
*
|
|
177
|
-
* @param {string} content Raw Markdown content
|
|
178
|
-
* @returns {string} Rewritten content
|
|
179
|
-
*/
|
|
180
|
-
function rewritePromptGatePhrases(content) {
|
|
181
|
-
const lines = content.split('\n');
|
|
182
|
-
let inFence = false;
|
|
183
|
-
const result = [];
|
|
184
|
-
for (const line of lines) {
|
|
185
|
-
// Detect fence boundaries: a line starting with ``` toggles fence state.
|
|
186
|
-
if (/^```/.test(line)) {
|
|
187
|
-
inFence = !inFence;
|
|
188
|
-
result.push(line);
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
if (inFence) {
|
|
192
|
-
result.push(line);
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
// Apply prose rewrites only outside fences.
|
|
196
|
-
let rewritten = line
|
|
197
|
-
.replace(/Use AskUserQuestion to/g, 'Use a Codex prompt gate to')
|
|
198
|
-
.replace(/If AskUserQuestion unavailable/g, 'If running non-interactively (Codex sub-agent context)');
|
|
199
|
-
result.push(rewritten);
|
|
200
|
-
}
|
|
201
|
-
return result.join('\n');
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Inject the `## Codex Interaction Gates` section exactly once.
|
|
206
|
-
*
|
|
207
|
-
* Injection location:
|
|
208
|
-
* - Files with YAML frontmatter (starting with `---`): injected immediately
|
|
209
|
-
* after the closing `---\n` delimiter.
|
|
210
|
-
* - Files without frontmatter (reference files): injected at the start.
|
|
211
|
-
*
|
|
212
|
-
* Idempotent: if the section is already present, returns content unchanged.
|
|
213
|
-
*
|
|
214
|
-
* @param {string} content Raw Markdown content
|
|
215
|
-
* @returns {string} Content with gate section injected (exactly once)
|
|
216
|
-
*/
|
|
217
|
-
function injectCodexGateSection(content) {
|
|
218
|
-
// Idempotency guard. Flat includes is safe here: '## Codex Interaction Gates' is only ever
|
|
219
|
-
// emitted by this function, never present in source .claude/skills/ files, so a fenced-block
|
|
220
|
-
// false-positive is impossible in practice.
|
|
221
|
-
if (content.includes('## Codex Interaction Gates')) {
|
|
222
|
-
return content;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Has frontmatter: inject after closing `---`.
|
|
226
|
-
if (/^---\r?\n/.test(content)) {
|
|
227
|
-
// Find the position right after the closing `---` + newline.
|
|
228
|
-
const closingRe = /^---\r?\n([\s\S]*?)\r?\n---\r?\n/;
|
|
229
|
-
const m = content.match(closingRe);
|
|
230
|
-
if (m) {
|
|
231
|
-
const insertAt = m[0].length;
|
|
232
|
-
return content.slice(0, insertAt) + CODEX_GATE_SECTION + '\n' + content.slice(insertAt);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// No frontmatter: inject at start.
|
|
237
|
-
return CODEX_GATE_SECTION + '\n' + content;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Full AskUserQuestion rewrite pipeline for a single Markdown file:
|
|
242
|
-
* 1. If the content contains no `AskUserQuestion`, return unchanged.
|
|
243
|
-
* 2. Inject the `## Codex Interaction Gates` section (idempotent).
|
|
244
|
-
* 3. Rewrite prose phrases outside fenced blocks.
|
|
245
|
-
*
|
|
246
|
-
* @param {string} content Raw Markdown content
|
|
247
|
-
* @returns {string} Transformed content
|
|
248
|
-
*/
|
|
249
|
-
function rewriteAskUserQuestionForCodex(content) {
|
|
250
|
-
if (!content.includes('AskUserQuestion')) {
|
|
251
|
-
return content;
|
|
252
|
-
}
|
|
253
|
-
const withGate = injectCodexGateSection(content);
|
|
254
|
-
return rewritePromptGatePhrases(withGate);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
151
|
// ---------------------------------------------------------------------------
|
|
258
152
|
// Post-copy rewrite walk
|
|
259
153
|
// ---------------------------------------------------------------------------
|
package/src/commands/codex.js
CHANGED
|
@@ -23,6 +23,22 @@ import chalk from 'chalk';
|
|
|
23
23
|
import { TOOL_DIR_NAME, COPY_FILTER_PATTERNS } from '../lib/constants.js';
|
|
24
24
|
import { convertHooksToCodex } from '../../scripts/convert-hooks-to-codex.js';
|
|
25
25
|
import { rewriteKitPaths } from '../lib/codex-rewrite.js';
|
|
26
|
+
import { rewriteAskUserQuestionForCodex } from '../lib/codex-askuser-rewrite.js';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Compose the two rewriters used for `.codex/workflows/` Markdown:
|
|
30
|
+
* 1. rewriteKitPaths — swaps `.claude/<subdir>/` references to `.codex/<subdir>/`.
|
|
31
|
+
* 2. rewriteAskUserQuestionForCodex — injects `## Codex Interaction Gates` and
|
|
32
|
+
* rewrites prose AskUserQuestion phrases for the Codex runtime.
|
|
33
|
+
*
|
|
34
|
+
* Skills are gate-injected by the skills converter; workflows historically were
|
|
35
|
+
* only path-rewritten, leaving `AskUserQuestion(` blocks in `.codex/workflows/`
|
|
36
|
+
* ungated. Codex follows workflow file references directly, so they need the
|
|
37
|
+
* same treatment.
|
|
38
|
+
*/
|
|
39
|
+
function rewriteWorkflowForCodex(text) {
|
|
40
|
+
return rewriteAskUserQuestionForCodex(rewriteKitPaths(text));
|
|
41
|
+
}
|
|
26
42
|
|
|
27
43
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
28
44
|
const PACKAGE_ROOT = resolve(__dirname, '..', '..');
|
|
@@ -331,7 +347,7 @@ export async function runCodexConversion(options = {}) {
|
|
|
331
347
|
console.log(chalk.cyan(`Mirroring ${workflowsDir}`));
|
|
332
348
|
console.log(chalk.cyan(` → ${workflowsOut}`));
|
|
333
349
|
}
|
|
334
|
-
mirrorDirWithRewrite(workflowsDir, workflowsOut);
|
|
350
|
+
mirrorDirWithRewrite(workflowsDir, workflowsOut, { rewriter: rewriteWorkflowForCodex });
|
|
335
351
|
}
|
|
336
352
|
|
|
337
353
|
const hooksResult = await convertHooksToCodex({ projectRoot, outputDir: codexRoot, verbose });
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codex-askuser-rewrite.js — Shared AskUserQuestion → Codex prose-gate rewriter.
|
|
3
|
+
*
|
|
4
|
+
* Used by both the skills converter (`scripts/convert-skills-to-codex.js`) and
|
|
5
|
+
* the workflows mirror (`src/commands/codex.js`) so that every converted `.md`
|
|
6
|
+
* file containing `AskUserQuestion(` gets the same gate-injection + prose-rewrite
|
|
7
|
+
* treatment.
|
|
8
|
+
*
|
|
9
|
+
* Properties guaranteed:
|
|
10
|
+
* - Idempotent: running the pipeline twice on the same content yields a single
|
|
11
|
+
* `## Codex Interaction Gates` section and no doubled prose tags.
|
|
12
|
+
* - Fence-aware: prose rewrites apply only outside triple-backtick fences;
|
|
13
|
+
* fenced AskUserQuestion schemas survive byte-for-byte.
|
|
14
|
+
* - Suffix-absorbing: when the source contains the literal trailing
|
|
15
|
+
* ` (sub-agent context)` after `If AskUserQuestion unavailable`, the rewrite
|
|
16
|
+
* absorbs it so the output is `If running non-interactively (Codex sub-agent context)`
|
|
17
|
+
* rather than the doubled form `... (Codex sub-agent context) (sub-agent context)`.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The gate section injected into every converted .md file that contains
|
|
22
|
+
* AskUserQuestion calls. Placed after the closing `---` frontmatter delimiter
|
|
23
|
+
* for SKILL.md files, or at the start of the file for reference / workflow files.
|
|
24
|
+
*/
|
|
25
|
+
export const CODEX_GATE_SECTION = `## Codex Interaction Gates
|
|
26
|
+
This skill was converted from Claude Code. Treat every \`AskUserQuestion(...)\` block below as a blocking Codex prompt gate:
|
|
27
|
+
- Render the question, header, and options in normal chat.
|
|
28
|
+
- Stop immediately after presenting the prompt.
|
|
29
|
+
- Wait for the user's next reply before continuing.
|
|
30
|
+
- Do not proceed to later phases until the answer is available.
|
|
31
|
+
- If running in a non-interactive or sub-agent context, use the fallback stated near the prompt.
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Rewrite AskUserQuestion prose phrases outside fenced blocks.
|
|
36
|
+
*
|
|
37
|
+
* Rewrites applied outside fences:
|
|
38
|
+
* "Use AskUserQuestion to" → "Use a Codex prompt gate to"
|
|
39
|
+
* "If AskUserQuestion unavailable (sub-agent context)" → "If running non-interactively (Codex sub-agent context)"
|
|
40
|
+
* "If AskUserQuestion unavailable" (no trailing tag) → "If running non-interactively (Codex sub-agent context)"
|
|
41
|
+
*
|
|
42
|
+
* The optional ` (sub-agent context)` suffix is consumed by the same replace so
|
|
43
|
+
* the rewrite does not produce doubled tags when the source carries the
|
|
44
|
+
* Claude-side canonical form.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} content Raw Markdown content
|
|
47
|
+
* @returns {string} Rewritten content
|
|
48
|
+
*/
|
|
49
|
+
export function rewritePromptGatePhrases(content) {
|
|
50
|
+
const lines = content.split('\n');
|
|
51
|
+
let inFence = false;
|
|
52
|
+
const result = [];
|
|
53
|
+
for (const line of lines) {
|
|
54
|
+
// Detect fence boundaries: a line starting with ``` toggles fence state.
|
|
55
|
+
if (/^```/.test(line)) {
|
|
56
|
+
inFence = !inFence;
|
|
57
|
+
result.push(line);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (inFence) {
|
|
61
|
+
result.push(line);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Apply prose rewrites only outside fences.
|
|
65
|
+
// The (?:\s*\(sub-agent context\))? optional group absorbs the canonical
|
|
66
|
+
// Claude-side trailing tag so the rewrite does not double it.
|
|
67
|
+
const rewritten = line
|
|
68
|
+
.replace(/Use AskUserQuestion to/g, 'Use a Codex prompt gate to')
|
|
69
|
+
.replace(
|
|
70
|
+
/If AskUserQuestion unavailable(?:\s*\(sub-agent context\))?/g,
|
|
71
|
+
'If running non-interactively (Codex sub-agent context)',
|
|
72
|
+
);
|
|
73
|
+
result.push(rewritten);
|
|
74
|
+
}
|
|
75
|
+
return result.join('\n');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Inject the `## Codex Interaction Gates` section exactly once.
|
|
80
|
+
*
|
|
81
|
+
* Injection location:
|
|
82
|
+
* - Files with YAML frontmatter (starting with `---`): injected immediately
|
|
83
|
+
* after the closing `---\n` delimiter.
|
|
84
|
+
* - Files without frontmatter (reference / workflow files): injected at the start.
|
|
85
|
+
*
|
|
86
|
+
* Idempotent: if the section is already present, returns content unchanged.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} content Raw Markdown content
|
|
89
|
+
* @returns {string} Content with gate section injected (exactly once)
|
|
90
|
+
*/
|
|
91
|
+
export function injectCodexGateSection(content) {
|
|
92
|
+
// Idempotency guard. Flat includes is safe here: '## Codex Interaction Gates' is only ever
|
|
93
|
+
// emitted by this function, never present in source .claude/ files, so a fenced-block
|
|
94
|
+
// false-positive is impossible in practice.
|
|
95
|
+
if (content.includes('## Codex Interaction Gates')) {
|
|
96
|
+
return content;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Has frontmatter: inject after closing `---`.
|
|
100
|
+
if (/^---\r?\n/.test(content)) {
|
|
101
|
+
const closingRe = /^---\r?\n([\s\S]*?)\r?\n---\r?\n/;
|
|
102
|
+
const m = content.match(closingRe);
|
|
103
|
+
if (m) {
|
|
104
|
+
const insertAt = m[0].length;
|
|
105
|
+
return content.slice(0, insertAt) + CODEX_GATE_SECTION + '\n' + content.slice(insertAt);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// No frontmatter: inject at start.
|
|
110
|
+
return CODEX_GATE_SECTION + '\n' + content;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Full AskUserQuestion rewrite pipeline for a single Markdown file:
|
|
115
|
+
* 1. If the content contains no `AskUserQuestion`, return unchanged.
|
|
116
|
+
* 2. Inject the `## Codex Interaction Gates` section (idempotent).
|
|
117
|
+
* 3. Rewrite prose phrases outside fenced blocks.
|
|
118
|
+
*
|
|
119
|
+
* @param {string} content Raw Markdown content
|
|
120
|
+
* @returns {string} Transformed content
|
|
121
|
+
*/
|
|
122
|
+
export function rewriteAskUserQuestionForCodex(content) {
|
|
123
|
+
if (!content.includes('AskUserQuestion')) {
|
|
124
|
+
return content;
|
|
125
|
+
}
|
|
126
|
+
const withGate = injectCodexGateSection(content);
|
|
127
|
+
return rewritePromptGatePhrases(withGate);
|
|
128
|
+
}
|