@khanhcan148/mk 0.1.32 → 0.1.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  Modular packages that extend Claude Code with specialized knowledge, workflows, and tool integrations.
8
8
 
9
- **Quick Navigation:** [Quick Reference](docs/quick-reference.md) | [Common Workflows](docs/common-workflows.md) | [Skill Index](docs/skill-index.md)
9
+ **Quick Navigation:** [Usage Guides](docs/guides/index.md) | [Quick Reference](docs/quick-reference.md) | [Common Workflows](docs/common-workflows.md) | [Skill Index](docs/skill-index.md)
10
10
 
11
11
 
12
12
  ## Quick Start
@@ -39,6 +39,9 @@ mk update Update kit files (preserves your edits by default); auto-sy
39
39
  mk update --force Update and overwrite user-modified files
40
40
  mk codex Mirror .claude/ to .codex/ for OpenAI Codex CLI (see docs/codex/COMMAND.md)
41
41
  mk codex --cwd DIR Convert a project at a different path
42
+ mk env List all MK_* environment variables grouped by scope
43
+ mk env <NAME> Show full details for a single variable (scope, type, default, current value)
44
+ mk env --format json Output all variables as JSON
42
45
  mk remove Remove all kit files tracked by manifest
43
46
  mk --version Show installed CLI version
44
47
  mk --help Show help
@@ -90,8 +93,8 @@ cp -r .claude ~/.claude/
90
93
 
91
94
  ```
92
95
  ├── .claude/
93
- │ ├── agents/ # 36 agents (5 primary + 31 utility: implementers, quality, docs, specialized, concerns, brainstorm critics)
94
- │ ├── skills/ # 62 skill packages (SKILL.md + scripts/references/assets)
96
+ │ ├── agents/ # 38 agents (5 primary + 33 utility: implementers, quality, docs, specialized, concerns, brainstorm critics)
97
+ │ ├── skills/ # 61 skill packages (SKILL.md + scripts/references/assets)
95
98
  │ │ ├── mk-*/ # 20 workflow commands (/mk-audit, /mk-brainstorm, /mk-log-analysis, /mk-overview, /mk-wiki, etc.)
96
99
  │ │ └── ... # Domain skills (frontend, backend, testing, browser automation, etc.)
97
100
  │ └── workflows/ # Development protocols
@@ -228,6 +231,7 @@ python .claude/skills/skill-creator/scripts/package_skill.py <path/to/skill-fold
228
231
 
229
232
  | Document | Description |
230
233
  |----------|-------------|
234
+ | [mk-* Usage Guides](docs/guides/index.md) | Human how-to guides for all 20 /mk-* commands: when to use, flags, phase flow, pitfalls |
231
235
  | [Skill Index](docs/skill-index.md) | Categorized list of all skills and workflows |
232
236
  | [mk-* Workflows & Agents](docs/mk-workflow-agents.md) | How each /mk-* command works and which agents it uses |
233
237
  | [`mk codex` Command Guide](docs/codex/COMMAND.md) | Convert .claude/ to .codex/ for OpenAI Codex CLI; auto-sync on mk update |
@@ -240,6 +244,7 @@ python .claude/skills/skill-creator/scripts/package_skill.py <path/to/skill-fold
240
244
  | [Product Domain Glossary](docs/product-domain-glossary.md) | Core domain concepts, terminology, and ecosystem definitions |
241
245
  | [Quick Reference](docs/quick-reference.md) | Common commands and quick lookup |
242
246
  | [Common Workflows](docs/common-workflows.md) | Practical workflow examples |
247
+ | [Environment Variables](.claude/env-vars.md) | All MK_* env vars with type, default, and description; run `mk env` for live values, or `mk env --markdown` to regenerate this table |
243
248
 
244
249
  ## Core Principles
245
250
 
package/bin/mk.js CHANGED
@@ -8,6 +8,7 @@ import { updateAction } from '../src/commands/update.js';
8
8
  import { removeAction } from '../src/commands/remove.js';
9
9
  import { loginAction, logoutAction, statusAction } from '../src/commands/auth.js';
10
10
  import { codexAction } from '../src/commands/codex.js';
11
+ import { envAction } from '../src/commands/env.js';
11
12
 
12
13
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
14
  const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
@@ -60,4 +61,11 @@ auth.command('status')
60
61
  .description('Show current authentication status')
61
62
  .action(() => statusAction());
62
63
 
64
+ program.command('env [name]')
65
+ .description('List or describe MK_* environment variables')
66
+ .option('--format <fmt>', 'Output format: text (default) or json')
67
+ .option('--markdown', 'Emit the full env-vars table as markdown (same as .claude/env-vars.md)')
68
+ .option('--all', 'Include internal/testing variables (hidden by default)')
69
+ .action((name, options) => envAction(name, options));
70
+
63
71
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanhcan148/mk",
3
- "version": "0.1.32",
3
+ "version": "0.1.34",
4
4
  "description": "CLI to install and manage MyClaudeKit (.claude/) in your projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -18,6 +18,7 @@
18
18
  "test": "node --test test/lib/*.test.js test/commands/*.test.js test/scripts/*.test.js test/integration/*.test.js test/characterization/*.characterization.test.js .claude/hooks/tests/*.test.cjs",
19
19
  "lint": "node --check src/**/*.js bin/**/*.js 2>/dev/null",
20
20
  "selftest": "python3 .claude/skills/mk-selftest/scripts/validate_kit.py",
21
+ "gen:env-docs": "node scripts/gen-env-docs.js",
21
22
  "codex:convert": "node bin/mk.js codex",
22
23
  "codex:convert-and-diff": "node scripts/codex-diff-check.js"
23
24
  },
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * gen-env-docs.js — Generate .claude/env-vars.md from the env registry.
4
+ *
5
+ * Usage: node scripts/gen-env-docs.js
6
+ * npm script: npm run gen:env-docs
7
+ *
8
+ * Output is deterministic: entries sorted by scope then name.
9
+ *
10
+ * Output lives under .claude/ because that is the only directory shipped to
11
+ * user projects by `mk init`/`mk update` (the GitHub tarball extractor filters
12
+ * to paths containing `.claude/`). Anything under `docs/` is repo-only and
13
+ * never reaches users — see src/lib/download.js TarExtractor.
14
+ *
15
+ * The markdown-building logic is exported as `renderMarkdown(entries)` so the
16
+ * CLI can serve `mk env --markdown` from the same source — no static-file drift.
17
+ */
18
+
19
+ import { writeFileSync } from 'node:fs';
20
+ import { fileURLToPath } from 'node:url';
21
+ import { dirname, resolve } from 'node:path';
22
+ import { ENTRIES } from '../src/lib/env-registry.js';
23
+
24
+ const __dirname = dirname(fileURLToPath(import.meta.url));
25
+ const KIT_ROOT = resolve(__dirname, '..');
26
+ const OUT_FILE = resolve(KIT_ROOT, '.claude', 'env-vars.md');
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // Scope labels (single source of truth for grouping)
30
+ // ---------------------------------------------------------------------------
31
+
32
+ export const SCOPE_LABELS = {
33
+ 'sql-guard': 'SQL Guard',
34
+ vault: 'Vault Knowledge',
35
+ cli: 'CLI',
36
+ auth: 'Auth',
37
+ team: 'Team Mode',
38
+ codex: 'Codex',
39
+ };
40
+
41
+ export function scopeLabel(scope) {
42
+ return SCOPE_LABELS[scope] ?? scope;
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // renderMarkdown — pure function. Used by both the file writer below AND
47
+ // by `mk env --markdown` (src/commands/env.js).
48
+ // ---------------------------------------------------------------------------
49
+
50
+ /**
51
+ * Render the env-vars registry as a markdown document.
52
+ * @param {ReadonlyArray<object>} entries — the env-registry ENTRIES array
53
+ * @returns {string} markdown content (newline-terminated)
54
+ */
55
+ export function renderMarkdown(entries) {
56
+ const sorted = [...entries].sort((a, b) => {
57
+ if (a.scope < b.scope) return -1;
58
+ if (a.scope > b.scope) return 1;
59
+ return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
60
+ });
61
+
62
+ // User-facing vars are grouped by scope; internal/testing vars (hook *_TEST
63
+ // bypasses, *_MAX_STDIN_BYTES caps) are collected into a single trailing
64
+ // section so the everyday reference stays uncluttered.
65
+ const userFacing = sorted.filter((e) => !e.internal);
66
+ const internal = sorted.filter((e) => e.internal);
67
+
68
+ const groups = new Map();
69
+ for (const entry of userFacing) {
70
+ if (!groups.has(entry.scope)) groups.set(entry.scope, []);
71
+ groups.get(entry.scope).push(entry);
72
+ }
73
+
74
+ const lines = [];
75
+ lines.push('# MK_* Environment Variables');
76
+ lines.push('');
77
+ lines.push(
78
+ '> Auto-generated by `npm run gen:env-docs` (or `mk env --markdown`). Do not edit manually.'
79
+ );
80
+ lines.push('');
81
+ lines.push(
82
+ 'All `MK_*` environment variables recognized by the kit. ' +
83
+ 'Run `mk env` for an interactive overview, or `mk env <NAME>` for details on a single variable.'
84
+ );
85
+ lines.push('');
86
+
87
+ const renderTable = (rows) => {
88
+ lines.push('| Name | Type | Default | Description |');
89
+ lines.push('|------|------|---------|-------------|');
90
+ for (const entry of rows) {
91
+ const def = entry.default !== undefined ? `\`${entry.default}\`` : '—';
92
+ const desc = entry.description.replace(/\|/g, '\\|');
93
+ lines.push(`| \`${entry.name}\` | ${entry.type} | ${def} | ${desc} |`);
94
+ }
95
+ lines.push('');
96
+ };
97
+
98
+ for (const [scope, scopeEntries] of groups) {
99
+ lines.push(`## ${scopeLabel(scope)}`);
100
+ lines.push('');
101
+ renderTable(scopeEntries);
102
+ }
103
+
104
+ if (internal.length > 0) {
105
+ lines.push('## Internal / testing');
106
+ lines.push('');
107
+ lines.push(
108
+ '> These are plumbing knobs a normal user never sets (hook test bypasses and ' +
109
+ 'defensive stdin caps). `mk env` hides them by default — use `mk env --all`.'
110
+ );
111
+ lines.push('');
112
+ renderTable(internal);
113
+ }
114
+
115
+ lines.push('---');
116
+ lines.push('');
117
+ lines.push(`_${entries.length} variables documented (${internal.length} internal). Run \`mk env\` for live values._`);
118
+ lines.push('');
119
+
120
+ return lines.join('\n');
121
+ }
122
+
123
+ // ---------------------------------------------------------------------------
124
+ // CLI entry point: write to disk
125
+ // ---------------------------------------------------------------------------
126
+
127
+ if (import.meta.url === `file://${process.argv[1]}`) {
128
+ const content = renderMarkdown(ENTRIES);
129
+ writeFileSync(OUT_FILE, content, 'utf8');
130
+ process.stdout.write(`Written: .claude/env-vars.md (${ENTRIES.length} entries)\n`);
131
+ }
@@ -0,0 +1,189 @@
1
+ import { ENTRIES, findByName } from '../lib/env-registry.js';
2
+ import { renderMarkdown } from '../../scripts/gen-env-docs.js';
3
+
4
+ // ---------------------------------------------------------------------------
5
+ // Scope display labels
6
+ // ---------------------------------------------------------------------------
7
+
8
+ const SCOPE_LABELS = {
9
+ 'sql-guard': 'SQL Guard',
10
+ vault: 'Vault Knowledge',
11
+ cli: 'CLI',
12
+ auth: 'Auth',
13
+ team: 'Team Mode',
14
+ codex: 'Codex',
15
+ };
16
+
17
+ function scopeLabel(scope) {
18
+ return SCOPE_LABELS[scope] ?? scope;
19
+ }
20
+
21
+ // ---------------------------------------------------------------------------
22
+ // Formatting helpers
23
+ // ---------------------------------------------------------------------------
24
+
25
+ /**
26
+ * Return the current runtime value of an env var, or '(unset)'.
27
+ * If the entry has { secret: true }, redact the value.
28
+ *
29
+ * @param {object} entry
30
+ * @returns {string}
31
+ */
32
+ function currentValue(entry) {
33
+ const raw = process.env[entry.name];
34
+ if (raw === undefined) return '(unset)';
35
+ if (entry.secret) return '(set — redacted)';
36
+ return raw;
37
+ }
38
+
39
+ /**
40
+ * Format a default value for display.
41
+ *
42
+ * @param {string|undefined} def
43
+ * @param {string} description
44
+ * @returns {string}
45
+ */
46
+ function displayDefault(def) {
47
+ if (def === undefined) return '(none)';
48
+ return def;
49
+ }
50
+
51
+ // Column widths for list mode
52
+ const COL_NAME = 32;
53
+ const COL_VALUE = 16;
54
+
55
+ function padRight(str, width) {
56
+ return str.length >= width ? str : str + ' '.repeat(width - str.length);
57
+ }
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // List mode — grouped by scope
61
+ // ---------------------------------------------------------------------------
62
+
63
+ function listMode(format, showInternal = false) {
64
+ // By default hide internal/testing vars (hook *_TEST bypasses, *_MAX_STDIN_BYTES
65
+ // caps) — they are plumbing a user never sets. `--all` shows them.
66
+ const visible = showInternal ? [...ENTRIES] : ENTRIES.filter((e) => !e.internal);
67
+ const hiddenCount = ENTRIES.length - visible.length;
68
+
69
+ // Group entries by scope, preserving insertion order of first occurrence
70
+ const groups = new Map();
71
+ for (const entry of visible) {
72
+ if (!groups.has(entry.scope)) groups.set(entry.scope, []);
73
+ groups.get(entry.scope).push(entry);
74
+ }
75
+
76
+ if (format === 'json') {
77
+ const enriched = visible.map((e) => ({ ...e, current: currentValue(e) }));
78
+ process.stdout.write(JSON.stringify({ entries: enriched }, null, 2) + '\n');
79
+ return;
80
+ }
81
+
82
+ // Text mode — sorted scopes for deterministic output
83
+ const sortedScopes = [...groups.keys()].sort();
84
+
85
+ for (const scope of sortedScopes) {
86
+ const entries = groups.get(scope);
87
+ process.stdout.write(`${scopeLabel(scope)}\n`);
88
+
89
+ for (const entry of entries) {
90
+ const namePad = padRight(entry.name, COL_NAME);
91
+ const val = currentValue(entry);
92
+ const valPad = padRight(val, COL_VALUE);
93
+ const firstLine = ` ${namePad} ${valPad} ${entry.description}\n`;
94
+ process.stdout.write(firstLine);
95
+
96
+ // Show default on second line if there is one
97
+ if (entry.default !== undefined) {
98
+ const indent = ' '.repeat(2 + COL_NAME + 1 + COL_VALUE + 1);
99
+ process.stdout.write(`${indent}Default: ${entry.default}\n`);
100
+ }
101
+ }
102
+ process.stdout.write('\n');
103
+ }
104
+
105
+ const scopeCount = sortedScopes.length;
106
+ process.stdout.write(`Run \`mk env <NAME>\` for full details on a single variable.\n`);
107
+ process.stdout.write(`${visible.length} variables across ${scopeCount} scope${scopeCount === 1 ? '' : 's'}.\n`);
108
+ if (hiddenCount > 0) {
109
+ process.stdout.write(`${hiddenCount} internal/testing variable${hiddenCount === 1 ? '' : 's'} hidden — run \`mk env --all\` to show.\n`);
110
+ }
111
+ }
112
+
113
+ // ---------------------------------------------------------------------------
114
+ // Detail mode — single entry
115
+ // ---------------------------------------------------------------------------
116
+
117
+ function detailMode(name) {
118
+ const entry = findByName(name);
119
+ if (!entry) {
120
+ process.stderr.write(`Unknown env var: ${name}\n`);
121
+ process.exitCode = 1;
122
+ return;
123
+ }
124
+
125
+ const val = currentValue(entry);
126
+ const def = entry.default !== undefined
127
+ ? entry.default
128
+ : '(none)';
129
+
130
+ process.stdout.write(`${entry.name}\n\n`);
131
+ process.stdout.write(` Scope: ${entry.scope}\n`);
132
+ process.stdout.write(` Type: ${entry.type}\n`);
133
+ process.stdout.write(` Applies to: ${entry.applies_to}\n`);
134
+ process.stdout.write(` Default: ${def}\n`);
135
+ process.stdout.write(` Current: ${val}\n`);
136
+ if (entry.since) {
137
+ process.stdout.write(` Since: ${entry.since}\n`);
138
+ }
139
+ if (entry.internal) {
140
+ process.stdout.write(` Internal: yes (testing/tuning plumbing — not a user setting)\n`);
141
+ }
142
+ process.stdout.write(`\n ${entry.description}\n`);
143
+ }
144
+
145
+ // ---------------------------------------------------------------------------
146
+ // Detail mode with JSON format
147
+ // ---------------------------------------------------------------------------
148
+
149
+ function detailModeJson(name) {
150
+ const entry = findByName(name);
151
+ if (!entry) {
152
+ process.stderr.write(`Unknown env var: ${name}\n`);
153
+ process.exitCode = 1;
154
+ return;
155
+ }
156
+ process.stdout.write(JSON.stringify({ entries: [{ ...entry, current: currentValue(entry) }] }, null, 2) + '\n');
157
+ }
158
+
159
+ // ---------------------------------------------------------------------------
160
+ // envAction — exported for bin/mk.js and tests
161
+ // ---------------------------------------------------------------------------
162
+
163
+ /**
164
+ * Handle 'mk env [name]'.
165
+ *
166
+ * @param {string|undefined} name - Optional env var name for detail mode
167
+ * @param {{ format?: string }} options
168
+ */
169
+ export function envAction(name, options = {}) {
170
+ const format = options.format ?? 'text';
171
+
172
+ // --markdown emits the same markdown that `npm run gen:env-docs` writes to disk.
173
+ // Useful for piping to a file in a user project: `mk env --markdown > ENV.md`.
174
+ // List mode only — ignores any name argument (markdown is always the full table).
175
+ if (options.markdown) {
176
+ process.stdout.write(renderMarkdown(ENTRIES));
177
+ return;
178
+ }
179
+
180
+ if (name) {
181
+ if (format === 'json') {
182
+ detailModeJson(name);
183
+ } else {
184
+ detailMode(name);
185
+ }
186
+ } else {
187
+ listMode(format, options.all === true);
188
+ }
189
+ }
@@ -80,9 +80,18 @@ export function redactSecrets(message, ctx = {}) {
80
80
  out = out.split(ctx.storedToken).join('[REDACTED-TOKEN]');
81
81
  }
82
82
 
83
- // S3: Redact any GitHub token-shaped substring (belt-and-braces)
83
+ // S3b: Redact any GitHub token-shaped substring (belt-and-braces)
84
84
  out = out.replace(/gh[pousr]_[A-Za-z0-9_]{30,}/g, '[REDACTED-TOKEN]');
85
85
 
86
+ // S5: Redact SQL Server / MSSQL Password= and ODBC Pwd= alias in connection strings
87
+ out = out.replace(/((?:Password|Pwd)\s*=\s*)([^;]+?)(\s*;|\s*$)/gi, '$1[REDACTED-PASSWORD]$3');
88
+
89
+ // S6: Redact PostgreSQL DSN user:pass@ credentials
90
+ out = out.replace(/(postgres(?:ql)?:\/\/)([^:@/]+):([^@/]+)@/gi, '$1[REDACTED-USER]:[REDACTED-PASSWORD]@');
91
+
92
+ // S7: Redact Azure Storage / CosmosDB AccountKey= in connection strings
93
+ out = out.replace(/(AccountKey\s*=\s*)([^;]+?)(\s*;|\s*$)/gi, '$1[REDACTED-KEY]$3');
94
+
86
95
  return out;
87
96
  }
88
97
 
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Central registry of all MK_* environment variables recognized by the kit.
3
+ *
4
+ * Each entry is frozen and the array itself is frozen.
5
+ * Consumers: `mk env` CLI subcommand, gen-env-docs.js, selftest drift detector.
6
+ *
7
+ * Deliberate omissions:
8
+ * - MK_VAULT_AUDIT_FLOOR and MK_VAULT_RELAXED are *forbidden* vars (validators_vault.py
9
+ * actively rejects any kit file that references them). Documenting them as supported
10
+ * configuration would be misleading.
11
+ * - MK_COMMAND_RE, MK_FM_SCHEMA_*, MK_INIT_RE, MK_PLAN_SKILL_MD, MK_SKILLS, MK_TIER are
12
+ * Python module-level constants in selftest scripts, not environment variables.
13
+ *
14
+ * If a var is secret-like (e.g. MK_*_TOKEN, MK_*_KEY, MK_*_SECRET) it should carry
15
+ * { secret: true } and `mk env` will redact its current value. None of the current
16
+ * vars are secret.
17
+ *
18
+ * { internal: true } marks a var as test-only or defensive-tuning plumbing that a
19
+ * normal user never sets (e.g. hook *_TEST bypasses, *_MAX_STDIN_BYTES caps). These
20
+ * stay in the registry so drift detection has a complete catalog, but `mk env` hides
21
+ * them by default (show with `mk env --all`) and the generated doc lists them in a
22
+ * separate "Internal / testing" section. They remain addressable via `mk env <NAME>`.
23
+ */
24
+
25
+ const _RAW = [
26
+ {
27
+ name: 'MK_CACHE_DIR',
28
+ scope: 'vault',
29
+ default: undefined,
30
+ description:
31
+ 'Override the cache directory used by vault_probe.py. ' +
32
+ 'Resolution order: MK_CACHE_DIR → $XDG_CACHE_HOME/mk → ~/.cache/mk → OS tempdir.',
33
+ type: 'path',
34
+ applies_to: 'skill',
35
+ since: '0.1.21',
36
+ },
37
+ {
38
+ name: 'MK_DAB_AUDIT_DIR',
39
+ scope: 'data-api-builder',
40
+ default: 'docs/dab-audit',
41
+ description:
42
+ 'Override the directory where dab_audit_writer.py appends the data-api-builder ' +
43
+ 'preflight audit log (dab-audit-YYYY-MM-DD.jsonl). Defaults to docs/dab-audit, ' +
44
+ 'relative to invocation cwd. Set to redirect logs to an artefact store in CI. ' +
45
+ 'Mirrors MK_SECURITY_AUDIT_DIR.',
46
+ type: 'path',
47
+ applies_to: 'skill',
48
+ since: '0.1.33',
49
+ },
50
+ {
51
+ name: 'MK_DAB_BASH_GUARD_MAX_STDIN_BYTES',
52
+ internal: true,
53
+ scope: 'data-api-builder',
54
+ default: '1048576',
55
+ description:
56
+ 'Maximum stdin bytes the dab-bash-guard hook (Gate 4) reads before failing open ' +
57
+ '(allowing the command to proceed). Default is 1 048 576 bytes (1 MB).',
58
+ type: 'integer',
59
+ applies_to: 'hook',
60
+ since: '0.1.34',
61
+ },
62
+ {
63
+ name: 'MK_DAB_BASH_GUARD_TEST',
64
+ internal: true,
65
+ scope: 'data-api-builder',
66
+ default: undefined,
67
+ description:
68
+ 'When set to "1", dab-bash-guard.cjs returns early at module load without executing the ' +
69
+ 'stdin pipeline. Used exclusively by hook unit tests; do not set in production.',
70
+ type: 'boolean',
71
+ applies_to: 'hook',
72
+ since: '0.1.34',
73
+ },
74
+ {
75
+ name: 'MK_DAB_BASH_OK',
76
+ scope: 'data-api-builder',
77
+ default: undefined,
78
+ description:
79
+ 'Opt-out override: when set to "1", allows Bash invocations of a DB CLI ' +
80
+ '(sqlcmd/psql/mysql/mssql-cli/mongosh/bcp/cqlsh) with query intent that the dab-bash-guard ' +
81
+ 'hook (Gate 4) otherwise blocks. Set only for deliberate DB administration; prefer the ' +
82
+ 'data-api-builder MCP tools.',
83
+ type: 'boolean',
84
+ applies_to: 'hook',
85
+ since: '0.1.34',
86
+ },
87
+ {
88
+ name: 'MK_DAB_MCP_GUARD_MAX_STDIN_BYTES',
89
+ internal: true,
90
+ scope: 'data-api-builder',
91
+ default: '1048576',
92
+ description:
93
+ 'Maximum stdin bytes the dab-mcp-guard hook (Gate 1) reads before failing open ' +
94
+ '(allowing the call to proceed). Default is 1 048 576 bytes (1 MB).',
95
+ type: 'integer',
96
+ applies_to: 'hook',
97
+ since: '0.1.34',
98
+ },
99
+ {
100
+ name: 'MK_DAB_MCP_GUARD_TEST',
101
+ internal: true,
102
+ scope: 'data-api-builder',
103
+ default: undefined,
104
+ description:
105
+ 'When set to "1", dab-mcp-guard.cjs skips main() at module load. Used exclusively by hook ' +
106
+ 'unit tests; do not set in production.',
107
+ type: 'boolean',
108
+ applies_to: 'hook',
109
+ since: '0.1.34',
110
+ },
111
+ {
112
+ name: 'MK_DAB_WRITES_OK',
113
+ scope: 'data-api-builder',
114
+ default: undefined,
115
+ description:
116
+ 'Opt-out override: when set to "1", allows the four write-capable data-api-builder MCP ' +
117
+ 'calls (create_record/update_record/delete_record/execute_entity) that the dab-mcp-guard ' +
118
+ 'hook (Gate 1) otherwise blocks. Set only for deliberate writes; DAB role config remains ' +
119
+ 'as defence-in-depth.',
120
+ type: 'boolean',
121
+ applies_to: 'hook',
122
+ since: '0.1.34',
123
+ },
124
+ {
125
+ name: 'MK_FORCE_NO_TOOL_DIR',
126
+ internal: true,
127
+ scope: 'codex',
128
+ default: undefined,
129
+ description:
130
+ 'When set to "1", convert-agents-to-codex.js aborts with exit 1 (simulates a missing ' +
131
+ 'TOOL_DIR_NAME constant). Used exclusively in integration tests; do not set in production.',
132
+ type: 'boolean',
133
+ applies_to: 'cli',
134
+ since: '0.1.22',
135
+ },
136
+ {
137
+ name: 'MK_INTROSPECT_MAX_NOTES',
138
+ scope: 'vault',
139
+ default: undefined,
140
+ description:
141
+ 'Maximum number of vault notes vault_introspect.py will enumerate before stopping. ' +
142
+ 'Prevents runaway scanning on large vaults. Must be a positive integer.',
143
+ type: 'integer',
144
+ applies_to: 'skill',
145
+ since: '0.1.21',
146
+ },
147
+ {
148
+ name: 'MK_OAUTH_SCOPE',
149
+ scope: 'auth',
150
+ default: 'repo',
151
+ description:
152
+ 'GitHub OAuth scope requested during the device flow. Defaults to "repo" (required to ' +
153
+ 'access the private kit repository). Override only for testing or alternative scopes.',
154
+ type: 'string',
155
+ applies_to: 'cli',
156
+ since: '0.1.0',
157
+ },
158
+ {
159
+ name: 'MK_SQL_GUARD_ALLOWLIST',
160
+ scope: 'sql-guard',
161
+ default: '.claude/sql-guard.allowlist',
162
+ description:
163
+ 'Path to the SQL guard allowlist file (kit-root-relative or absolute). ' +
164
+ 'Used by sql-allowlist.cjs to load the list of approved SQL patterns.',
165
+ type: 'path',
166
+ applies_to: 'hook',
167
+ since: '0.1.26',
168
+ },
169
+ {
170
+ name: 'MK_SQL_GUARD_MAX_STDIN_BYTES',
171
+ internal: true,
172
+ scope: 'sql-guard',
173
+ default: '1048576',
174
+ description:
175
+ 'Maximum stdin bytes the sql-guard hook will read before failing open (allowing the ' +
176
+ 'command to proceed). Default is 1 048 576 bytes (1 MB). Increase for large queries.',
177
+ type: 'integer',
178
+ applies_to: 'hook',
179
+ since: '0.1.26',
180
+ },
181
+ {
182
+ name: 'MK_SQL_GUARD_TEST',
183
+ internal: true,
184
+ scope: 'sql-guard',
185
+ default: undefined,
186
+ description:
187
+ 'When set to "1", sql-guard.cjs returns early at module load without executing the ' +
188
+ 'stdin pipeline. Used exclusively by hook unit tests; do not set in production.',
189
+ type: 'boolean',
190
+ applies_to: 'hook',
191
+ since: '0.1.26',
192
+ },
193
+ {
194
+ name: 'MK_TEAM_MAX_ITERATIONS',
195
+ scope: 'team',
196
+ default: '8',
197
+ description:
198
+ 'Maximum agent-team loop iterations enforced by loop-detector.js before the loop is ' +
199
+ 'terminated. Applies to --team mode in mk-implement and mk-review.',
200
+ type: 'integer',
201
+ applies_to: 'skill',
202
+ since: '0.1.15',
203
+ },
204
+ {
205
+ name: 'MK_VAULT_CACHE_REBUILD',
206
+ scope: 'vault',
207
+ default: undefined,
208
+ description:
209
+ 'When set to "1", vault_retrieval_plan.py ignores any cached retrieval plan and ' +
210
+ 'rebuilds it from scratch. Useful after vault restructuring.',
211
+ type: 'boolean',
212
+ applies_to: 'skill',
213
+ since: '0.1.21',
214
+ },
215
+ {
216
+ name: 'MK_VAULT_ENABLED',
217
+ scope: 'vault',
218
+ default: undefined,
219
+ description:
220
+ 'Privacy-by-default opt-in gate. Set to "1" to enable Obsidian vault consultation ' +
221
+ 'across mk-* skills and agents. Any other value (or unset) means vault access is disabled.',
222
+ type: 'boolean',
223
+ applies_to: 'skill',
224
+ since: '0.1.21',
225
+ },
226
+ {
227
+ name: 'MK_VERBOSE',
228
+ scope: 'cli',
229
+ default: undefined,
230
+ description:
231
+ 'When set to "1", enables verbose output in mk update and the Codex converters ' +
232
+ '(per-file change enumeration, routine warnings, long-path warnings). ' +
233
+ 'Equivalent to passing --verbose on mk update.',
234
+ type: 'boolean',
235
+ applies_to: 'cli',
236
+ since: '0.1.25',
237
+ },
238
+ ];
239
+
240
+ /**
241
+ * Frozen array of all known MK_* environment variables.
242
+ * @type {ReadonlyArray<{name:string,scope:string,default:string|undefined,description:string,type:string,applies_to:string,since?:string}>}
243
+ */
244
+ export const ENTRIES = Object.freeze(_RAW.map(Object.freeze));
245
+
246
+ /**
247
+ * Look up a registry entry by exact variable name.
248
+ *
249
+ * @param {string|undefined} name
250
+ * @returns {{ name:string, scope:string, default:string|undefined, description:string, type:string, applies_to:string, since?:string } | undefined}
251
+ */
252
+ export function findByName(name) {
253
+ if (typeof name !== 'string') return undefined;
254
+ return ENTRIES.find((e) => e.name === name);
255
+ }