@khanhcan148/mk 0.1.30 → 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanhcan148/mk",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "description": "CLI to install and manage MyClaudeKit (.claude/) in your projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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
  // ---------------------------------------------------------------------------
@@ -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
+ }