@event4u/agent-config 17.2.1 → 17.4.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/skills/install-agent-config/SKILL.md +9 -9
- package/CHANGELOG.md +167 -193
- package/CREDITS.md +2 -2
- package/README.md +1 -1
- package/dist/agent-src/skills/code-intelligence/SKILL.md +16 -12
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/check_memory.ts +1 -1
- package/dist/cli/commands/recordTriggerEval.js +1 -1
- package/dist/cli/registry.js +2 -2
- package/dist/cli-delegate/{chunk-KR5ZIFPO.js → chunk-5AYJGUA3.js} +1 -3
- package/dist/cli-delegate/chunk-DIFZBP4G.js +1336 -0
- package/dist/cli-delegate/chunk-E7ACTJTJ.js +6792 -0
- package/dist/cli-delegate/{chunk-VYZGJLCA.js → chunk-RKNT4LIK.js} +22 -1286
- package/dist/cli-delegate/{chunk-BZJ4Y3CI.js → chunk-WY5ZZPGL.js} +2 -2
- package/dist/cli-delegate/{chunk-CMZEKSNN.js → chunk-X6LZ7W5A.js} +73 -10
- package/dist/cli-delegate/cmd_conformance.js +190 -192
- package/dist/cli-delegate/cmd_doctor.js +5 -5
- package/dist/cli-delegate/cmd_doctor_neighbours.js +3 -3
- package/dist/cli-delegate/cmd_explain.js +1 -1
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +2 -2
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_route_audit.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_settings_set.js +8 -4
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_update.js +24 -13
- package/dist/cli-delegate/cmd_upgrade.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/cli-delegate/dist-25AZAHOP.js +3240 -0
- package/dist/cli-delegate/dist-P7MFPMF3.js +411 -0
- package/dist/cli-delegate/dist-XAS5ZLQB.js +6 -0
- package/dist/cli-delegate/{upgrade_unattended-VVL6JLSX.js → upgrade_unattended-QCWOKTUN.js} +178 -163
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +3 -3
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/code_graph_refresh.chunk.cjs +443 -0
- package/dist/hooks/code_graph_refresh.chunk.cjs.sha256 +1 -0
- package/dist/hooks/dispatch.js +1001 -938
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/hooks/egress_engine.chunk.cjs +57 -7
- package/dist/hooks/egress_engine.chunk.cjs.sha256 +1 -1
- package/dist/hooks/update_notice.chunk.cjs +914 -0
- package/dist/hooks/update_notice.chunk.cjs.sha256 +1 -0
- package/dist/install/install.mjs +6974 -6037
- package/dist/mcp/mcp-cloudflare-catalogue.json +1 -0
- package/dist/mcp/registry-manifest.json +3 -2
- package/dist/mcp/server.json +13 -4
- package/dist/mcp/server.mjs +71202 -63282
- package/dist/scripts/_lib/egress_rules.js +8 -2
- package/dist/scripts/condense.js +18 -15
- package/dist/server/routes/discovery.js +1 -1
- package/dist/server/routes/install.js +1 -1
- package/dist/server/routes/ping.js +1 -1
- package/dist/server/routes/schema.js +4 -4
- package/dist/server/routes/settings.js +2 -6
- package/dist/server/routes/wizard.js +6 -6
- package/dist/server/schemas/settings.js +42 -42
- package/dist/shared/settingsSurface.js +1 -1
- package/dist/shared/userMd/schema.js +1 -1
- package/dist/shared/zodJsonSchema.js +47 -0
- package/dist/ui/assets/index-B0bcIgi0.js +77 -0
- package/dist/ui/index.html +1 -1
- package/package.json +6 -3
- package/src/agent-src/templates/scripts/check_memory.ts +1 -1
- package/src/config/ci-local-parity.yml +11 -0
- package/src/config/continuity-surface.json +9 -2
- package/src/config/evaluator-budgets.json +9 -8
- package/src/config/gate-violation-baselines.json +3 -3
- package/src/config/gitignore-block.txt +6 -5
- package/src/config/hook-bundle-budget.json +46 -6
- package/src/config/pack-size-budget.json +12 -1
- package/src/config/packed-binary-manifest.json +18 -0
- package/src/config/sigstore/npm_keys.json +50 -0
- package/src/config/sigstore/trust-material.json +9 -0
- package/src/config/sigstore/trusted_root.json +126 -0
- package/src/config/skill-body-budget.json +7 -1
- package/src/scripts/_cli/cmd_settings_set.ts +3 -2
- package/src/scripts/_cli/cmd_update.ts +48 -20
- package/src/scripts/_cli/cmd_upgrade.ts +7 -1
- package/src/scripts/_cli/upgrade_unattended.ts +90 -76
- package/src/scripts/_dispatch.bash +4 -3
- package/src/scripts/_lib/confusables.ts +0 -19
- package/src/scripts/_lib/egress_normalize.ts +53 -23
- package/src/scripts/_lib/egress_rules.ts +8 -2
- package/src/scripts/_lib/egress_text.ts +5 -3
- package/src/scripts/_lib/egress_token.ts +7 -5
- package/src/scripts/_lib/gate_collector_experimental.ts +633 -0
- package/src/scripts/_lib/git_common_dir.ts +10 -0
- package/src/scripts/_lib/graph_feeder_record.ts +3 -2
- package/src/scripts/_lib/install_reach_checks.ts +32 -2
- package/src/scripts/_lib/lineage_backfill.ts +136 -0
- package/src/scripts/_lib/lineage_ledger.ts +211 -0
- package/src/scripts/_lib/lineage_map.ts +55 -0
- package/src/scripts/_lib/lineage_query.ts +140 -0
- package/src/scripts/_lib/lineage_seed.ts +116 -0
- package/src/scripts/_lib/lineage_watch.ts +249 -0
- package/src/scripts/_lib/link_crypto.ts +8 -4
- package/src/scripts/_lib/listing_manifests.ts +85 -0
- package/src/scripts/_lib/neighbour_census.ts +1 -0
- package/src/scripts/_lib/production_reach.ts +3 -3
- package/src/scripts/_lib/project_registry.ts +227 -0
- package/src/scripts/_lib/provenance_verify.ts +14 -242
- package/src/scripts/_lib/release_settings_surface.ts +2 -6
- package/src/scripts/_lib/sigstore_provenance.ts +177 -0
- package/src/scripts/_lib/source_id.ts +48 -0
- package/src/scripts/_lib/source_shape.ts +4 -0
- package/src/scripts/_lib/unattended_gates.ts +6 -1
- package/src/scripts/adoption_status.ts +231 -27
- package/src/scripts/advisories.ts +26 -10
- package/src/scripts/ai_council/council_egress.ts +5 -6
- package/src/scripts/bench_graph_feeder_latency.ts +10 -3
- package/src/scripts/build_mcp_registry_manifest.ts +23 -12
- package/src/scripts/check_dependency_floors.ts +14 -0
- package/src/scripts/check_egress_mutations.ts +1 -0
- package/src/scripts/check_release_manifest_content.ts +17 -2
- package/src/scripts/check_release_pr_shape.ts +5 -0
- package/src/scripts/code_graph/build.ts +145 -45
- package/src/scripts/code_graph/cli.ts +382 -35
- package/src/scripts/code_graph/conventions_default.ts +247 -0
- package/src/scripts/code_graph/detect.ts +5 -5
- package/src/scripts/code_graph/estate_cli.ts +135 -0
- package/src/scripts/code_graph/estate_index.ts +394 -0
- package/src/scripts/code_graph/estate_manifests.ts +223 -0
- package/src/scripts/code_graph/estate_map.ts +141 -0
- package/src/scripts/code_graph/extract.ts +8 -2
- package/src/scripts/code_graph/extract_more.ts +241 -2
- package/src/scripts/code_graph/graph_budget.ts +158 -0
- package/src/scripts/code_graph/graph_home.ts +80 -0
- package/src/scripts/code_graph/lineage_cli.ts +315 -0
- package/src/scripts/code_graph/loader.ts +10 -2
- package/src/scripts/code_graph/map.ts +195 -53
- package/src/scripts/code_graph/map_model.ts +113 -20
- package/src/scripts/code_graph/map_verify.ts +80 -5
- package/src/scripts/code_graph/map_viewer/viewer.css +18 -0
- package/src/scripts/code_graph/map_viewer/viewer.js +146 -15
- package/src/scripts/code_graph/passes.ts +173 -0
- package/src/scripts/code_graph/project_ref.ts +25 -0
- package/src/scripts/code_graph/projects_cli.ts +254 -0
- package/src/scripts/code_graph/providers/angular.ts +55 -0
- package/src/scripts/code_graph/providers/aspnetcore.ts +182 -0
- package/src/scripts/code_graph/providers/catalog.ts +299 -0
- package/src/scripts/code_graph/providers/django.ts +206 -0
- package/src/scripts/code_graph/providers/express.ts +25 -0
- package/src/scripts/code_graph/providers/fastapi.ts +49 -0
- package/src/scripts/code_graph/providers/flask.ts +99 -0
- package/src/scripts/code_graph/providers/go_http.ts +121 -0
- package/src/scripts/code_graph/providers/index.ts +103 -0
- package/src/scripts/code_graph/{laravel_overlay.ts → providers/laravel.ts} +113 -7
- package/src/scripts/code_graph/providers/nestjs.ts +34 -0
- package/src/scripts/code_graph/providers/nextjs.ts +91 -0
- package/src/scripts/code_graph/providers/nuxt.ts +55 -0
- package/src/scripts/code_graph/providers/rails.ts +204 -0
- package/src/scripts/code_graph/providers/react-router.ts +58 -0
- package/src/scripts/code_graph/providers/shared/detect.ts +78 -0
- package/src/scripts/code_graph/providers/shared/extractors.ts +249 -0
- package/src/scripts/code_graph/providers/shared/js.ts +252 -0
- package/src/scripts/code_graph/providers/shared/kit.ts +210 -0
- package/src/scripts/code_graph/providers/shared/php.ts +64 -0
- package/src/scripts/code_graph/providers/shared/python.ts +223 -0
- package/src/scripts/code_graph/providers/spring.ts +175 -0
- package/src/scripts/code_graph/providers/sqlalchemy.ts +62 -0
- package/src/scripts/code_graph/providers/sveltekit.ts +59 -0
- package/src/scripts/code_graph/providers/symfony.ts +142 -0
- package/src/scripts/code_graph/providers/typeorm.ts +58 -0
- package/src/scripts/code_graph/providers/types.ts +56 -0
- package/src/scripts/code_graph/providers/vue-router.ts +21 -0
- package/src/scripts/code_graph/providers/wordpress.ts +128 -0
- package/src/scripts/code_graph/query.ts +9 -9
- package/src/scripts/code_graph/refresh_cli.ts +81 -0
- package/src/scripts/code_graph/refresh_lock.ts +197 -0
- package/src/scripts/code_graph/repo_identity.ts +169 -0
- package/src/scripts/code_graph/resolution_tiers.ts +19 -7
- package/src/scripts/code_graph/scip_reader.ts +2 -2
- package/src/scripts/code_graph/sqlite_store.ts +31 -28
- package/src/scripts/code_graph/types.ts +79 -41
- package/src/scripts/code_graph/validate.ts +6 -8
- package/src/scripts/condense.ts +15 -15
- package/src/scripts/generate_settings_reference.ts +2 -6
- package/src/scripts/hook_chunks.mjs +11 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +43 -23
- package/src/scripts/hooks/code_graph_refresh_hook.ts +163 -0
- package/src/scripts/hooks/dispatch_hook.ts +6 -7
- package/src/scripts/hooks/host_semantics.ts +72 -0
- package/src/scripts/hooks/lazy_concern.ts +8 -0
- package/src/scripts/hooks/user_notice.ts +105 -0
- package/src/scripts/install-hooks.sh +54 -33
- package/src/scripts/install.ts +2 -6
- package/src/scripts/lint_harvest_provenance.ts +137 -11
- package/src/scripts/lint_hook_manifest.ts +2 -0
- package/src/scripts/lint_marketplace.ts +33 -0
- package/src/scripts/lint_mcp_registry_manifest.ts +36 -0
- package/src/scripts/mcp_server/consumer_tool_catalog.json +70 -15
- package/src/scripts/mcp_server/graph_tools.ts +165 -41
- package/src/scripts/network_sources.ts +24 -6
- package/src/scripts/release.ts +2 -5
- package/src/scripts/release_drill.ts +4 -1
- package/src/scripts/release_env.ts +17 -0
- package/src/scripts/release_mix_taxonomy.json +3 -0
- package/src/scripts/schemas/provenance-mechanism.schema.json +46 -0
- package/src/scripts/update_notice_hook.ts +75 -7
- package/src/server/schemas/settings.ts +42 -42
- package/src/shared/settingsSurface.ts +1 -1
- package/src/shared/userMd/schema.ts +1 -1
- package/src/shared/zodJsonSchema.ts +41 -0
- package/src/vendor/grammars/README.md +8 -6
- package/src/vendor/grammars/tree-sitter-c_sharp.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-ruby.wasm +0 -0
- package/dist/cli-delegate/chunk-XIAPY44N.js +0 -4587
- package/dist/ui/assets/index-w70Ado09.js +0 -23
- /package/dist/cli-delegate/{chunk-FKYZOWVD.js → chunk-QBAFP67O.js} +0 -0
- /package/dist/cli-delegate/{chunk-BMGUGSY6.js → chunk-TQG6UMPC.js} +0 -0
|
@@ -53,12 +53,18 @@ export const ASSIGNMENT_KEYWORDS = {
|
|
|
53
53
|
token: 'token',
|
|
54
54
|
bearer: 'bearer',
|
|
55
55
|
};
|
|
56
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Source of a keyword-assignment shape: keyword group, operator, then the consumer's value.
|
|
58
|
+
* Not an assignment: `===` (strict equality), `==` followed by whitespace (a spaced
|
|
59
|
+
* comparison) and `=>` (an arrow) — read as one, `token === 'x'` yielded the value `==` and
|
|
60
|
+
* the council bundle refused every later `==`. A shell `name==value` stays an assignment,
|
|
61
|
+
* and its second `=` belongs to the operator, so the value read is `value`, never a lone `=`.
|
|
62
|
+
*/
|
|
57
63
|
export function assignmentSource(shape) {
|
|
58
64
|
const alt = shape.keywords.map((k) => ASSIGNMENT_KEYWORDS[k]).join('|');
|
|
59
65
|
const group = shape.capture === true ? `(${alt})` : `(?:${alt})`;
|
|
60
66
|
const gap = shape.gap ?? '\\s*';
|
|
61
|
-
const head = `${shape.before ?? ''}${group}${shape.after ?? ''}${gap}
|
|
67
|
+
const head = `${shape.before ?? ''}${group}${shape.after ?? ''}${gap}(?::|=(?!==|=\\s|>)=?)`;
|
|
62
68
|
return shape.value === undefined ? head : `${head}${gap}${shape.value}`;
|
|
63
69
|
}
|
|
64
70
|
export function assignmentPattern(shape, flags) {
|
package/dist/scripts/condense.js
CHANGED
|
@@ -1930,19 +1930,19 @@ export const PLUGIN_POINTER_SLUG = 'install-agent-config';
|
|
|
1930
1930
|
export const PLUGIN_POINTER_SKILL_BODY = `---
|
|
1931
1931
|
name: install-agent-config
|
|
1932
1932
|
description: >-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
Use when the user asks to install, set up, or upgrade agent-config, or
|
|
1933
|
+
Governance rules, skills and commands for AI coding agents in Claude Code, Cursor, GitHub Copilot and Gemini CLI —
|
|
1934
|
+
install or upgrade the full event4u agent-config suite. No plugin that ships this pointer is the full suite (the Claude
|
|
1935
|
+
Code one adds hooks, the Copilot one a read-only MCP server); ALL content installs via \`npx -y @event4u/agent-config init\`.
|
|
1936
|
+
Use when the user asks to install, set up, or upgrade agent-config, wants coding-agent rules or skills, or
|
|
1937
1937
|
wonders why this plugin contains no other skills.
|
|
1938
1938
|
---
|
|
1939
1939
|
|
|
1940
1940
|
# Install agent-config
|
|
1941
1941
|
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1942
|
+
The plugin that delivered this skill is a **discovery entry**, not the suite. The Claude Code plugin adds hooks that
|
|
1943
|
+
run only alongside a global npm install of the package (never a project-local one) — on a plugin-only install no hook
|
|
1944
|
+
runs, so the \`fail_closed\` guards (\`block-no-verify\`, \`block-kernel-rule-writes\`, \`block-plumbing-writes\`) are
|
|
1945
|
+
**off**; the session start says so once. The Copilot plugin adds a read-only MCP server and no hooks.
|
|
1946
1946
|
|
|
1947
1947
|
## Install (canonical, one command)
|
|
1948
1948
|
|
|
@@ -1952,7 +1952,7 @@ npx -y @event4u/agent-config init
|
|
|
1952
1952
|
|
|
1953
1953
|
The installer writes the full content projection, registers the dispatcher
|
|
1954
1954
|
hooks as a managed block in \`~/.claude/settings.json\` (byte-identical to
|
|
1955
|
-
|
|
1955
|
+
the Claude Code plugin's hooks, so nothing double-fires), and keeps everything
|
|
1956
1956
|
upgradeable via \`agent-config upgrade\`.
|
|
1957
1957
|
|
|
1958
1958
|
## Already installed both?
|
|
@@ -1969,11 +1969,14 @@ export function generate_plugin_command_skills() {
|
|
|
1969
1969
|
const pointer_dir = path.join(MODULE_STATE.PLUGIN_SKILLS_DIR, PLUGIN_POINTER_SLUG);
|
|
1970
1970
|
_mkdirp(pointer_dir);
|
|
1971
1971
|
const pointer_file = path.join(pointer_dir, 'SKILL.md');
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
fs.
|
|
1972
|
+
for (const file of [pointer_file, path.join(MODULE_STATE.PROJECT_ROOT, '.github', 'plugin', 'skills', PLUGIN_POINTER_SLUG, 'SKILL.md')]) {
|
|
1973
|
+
if (_isSymlink(file)) {
|
|
1974
|
+
fs.unlinkSync(file);
|
|
1975
|
+
}
|
|
1976
|
+
if (!_isFile(file) || fs.readFileSync(file, 'utf-8') !== PLUGIN_POINTER_SKILL_BODY) {
|
|
1977
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
1978
|
+
fs.writeFileSync(file, PLUGIN_POINTER_SKILL_BODY);
|
|
1979
|
+
}
|
|
1977
1980
|
}
|
|
1978
1981
|
// 2. Prune every other skill dir so the tree converges to shim shape
|
|
1979
1982
|
// from any prior state. Removable = every entry is a symlink (the
|
|
@@ -2001,7 +2004,7 @@ export function generate_plugin_command_skills() {
|
|
|
2001
2004
|
removed_dirs += 1;
|
|
2002
2005
|
}
|
|
2003
2006
|
}
|
|
2004
|
-
let msg = ' ✅ Plugin skills: bootstrap shim (1 pointer skill) in .claude-plugin/skills/';
|
|
2007
|
+
let msg = ' ✅ Plugin skills: bootstrap shim (1 pointer skill) in .claude-plugin/skills/ and .github/plugin/skills/';
|
|
2005
2008
|
if (removed_dirs) {
|
|
2006
2009
|
msg += ` (${removed_dirs} content entries pruned)`;
|
|
2007
2010
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* (PR working tree, dev install) rather than a runtime bug. See ADR-013
|
|
13
13
|
* and the R3 roadmap for the broader contract.
|
|
14
14
|
*/
|
|
15
|
-
import
|
|
15
|
+
import * as z from 'zod';
|
|
16
16
|
import { loadManifest, ManifestNotFoundError, ManifestParseError, } from '../../cli/discovery/loadManifest.js';
|
|
17
17
|
export const DiscoverySliceSchema = z.enum(['workspaces', 'packs', 'artefacts', 'unassigned']);
|
|
18
18
|
function pickSlice(manifest, slice) {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* road-to-single-install-source-of-truth § Phase 3 — this module now only
|
|
13
13
|
* serves the read-only detect / plan-preview / recovery / legacy-v3 routes.
|
|
14
14
|
*/
|
|
15
|
-
import
|
|
15
|
+
import * as z from 'zod';
|
|
16
16
|
import { computeConflicts } from '../../install/conflict.js';
|
|
17
17
|
import { cpSync, existsSync, mkdirSync } from 'node:fs';
|
|
18
18
|
import { detectLegacyV3, detectPackageRoot, detectProjectShape, detectScope, detectToolPresence, } from '../../install/detect.js';
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* are connected to the right process. The zod schema is exported so
|
|
7
7
|
* tests assert against the same source of truth as the handler.
|
|
8
8
|
*/
|
|
9
|
-
import
|
|
9
|
+
import * as z from 'zod';
|
|
10
10
|
import { readFileSync } from 'node:fs';
|
|
11
11
|
import { PACKAGE_JSON } from '../../cli/paths.js';
|
|
12
12
|
import { CAPABILITIES } from '../../shared/capabilities.js';
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* The SPA bundle does not ship Zod (council security-engineer mandate:
|
|
5
5
|
* client and server cannot drift). Instead it fetches JSON-Schema once at
|
|
6
|
-
* mount and feeds it to the form renderer. `
|
|
6
|
+
* mount and feeds it to the form renderer. `toJsonSchema7` does the
|
|
7
7
|
* conversion in-process; the response is cached for the lifetime of the
|
|
8
8
|
* server process because the schemas are compile-time constants.
|
|
9
9
|
*
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* generatedAt: ISO-8601 string,
|
|
15
15
|
* }
|
|
16
16
|
*/
|
|
17
|
-
import {
|
|
17
|
+
import { toJsonSchema7 } from '../../shared/zodJsonSchema.js';
|
|
18
18
|
import { settingsSchema } from '../schemas/settings.js';
|
|
19
19
|
import { userIdentitySchema } from '../../shared/userMd/schema.js';
|
|
20
20
|
let cached = null;
|
|
@@ -22,8 +22,8 @@ function build() {
|
|
|
22
22
|
if (cached !== null)
|
|
23
23
|
return cached;
|
|
24
24
|
cached = {
|
|
25
|
-
settings:
|
|
26
|
-
userIdentity:
|
|
25
|
+
settings: toJsonSchema7(settingsSchema, 'Settings'),
|
|
26
|
+
userIdentity: toJsonSchema7(userIdentitySchema, 'UserIdentity'),
|
|
27
27
|
generatedAt: new Date().toISOString(),
|
|
28
28
|
};
|
|
29
29
|
return cached;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { isDeepStrictEqual } from 'node:util';
|
|
4
|
-
import {
|
|
4
|
+
import { toJsonSchema7 } from '../../shared/zodJsonSchema.js';
|
|
5
5
|
import { settingsSchema } from '../schemas/settings.js';
|
|
6
6
|
import { parseYaml, mergeIntoTemplate, diffValues, deepMerge, renderSparseSettings, substituteTemplatePlaceholders, TEMPLATE_PLACEHOLDER_DEFAULTS } from '../io/yamlIO.js';
|
|
7
7
|
import { sparseSettingsValues } from './wizard.js';
|
|
@@ -102,11 +102,7 @@ const SHARED_WRITE_MESSAGE = 'This write lands through an agent-switch shared sy
|
|
|
102
102
|
// added there must get its default here, or `settingsSchema.safeParse` on
|
|
103
103
|
// the merged defaults will reject the first save.
|
|
104
104
|
// Computed once — Zod → JSON Schema conversion is pure and the schema is static.
|
|
105
|
-
const SETTINGS_JSON_SCHEMA =
|
|
106
|
-
name: 'AgentSettings',
|
|
107
|
-
$refStrategy: 'none',
|
|
108
|
-
target: 'jsonSchema7',
|
|
109
|
-
});
|
|
105
|
+
const SETTINGS_JSON_SCHEMA = toJsonSchema7(settingsSchema, 'AgentSettings');
|
|
110
106
|
const SETTINGS_RELATIVE = join('settings', '.agent-settings.yml');
|
|
111
107
|
/** Pre-typed-subdir flat-root location. Read as fallback for migration. */
|
|
112
108
|
const LEGACY_FLAT_RELATIVE = '.agent-settings.yml';
|
|
@@ -4,7 +4,7 @@ import { join, dirname } from 'node:path';
|
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
import { spawn } from 'node:child_process';
|
|
6
6
|
import { randomBytes } from 'node:crypto';
|
|
7
|
-
import
|
|
7
|
+
import * as z from 'zod';
|
|
8
8
|
import { settingsSchema } from '../schemas/settings.js';
|
|
9
9
|
import { userIdentitySchema } from '../../shared/userMd/schema.js';
|
|
10
10
|
import { composeUserIdentity } from '../../shared/userMd/utils.js';
|
|
@@ -101,7 +101,7 @@ async function readInstalledPacks(writeRoot, legacyReadRoot, userGlobalReadRoot)
|
|
|
101
101
|
const wizardStateSchema = z.object({
|
|
102
102
|
step: z.number().int().min(0),
|
|
103
103
|
totalSteps: z.number().int().min(1).optional(),
|
|
104
|
-
partial: z.record(z.unknown()).default({}),
|
|
104
|
+
partial: z.record(z.string(), z.unknown()).default({}),
|
|
105
105
|
startedAt: z.string().nullable().default(null),
|
|
106
106
|
});
|
|
107
107
|
// road-to-global-only-install § Phase 1.5 — WizardApplyPayload shape.
|
|
@@ -112,14 +112,14 @@ const wizardStateSchema = z.object({
|
|
|
112
112
|
const installerV1PayloadSchema = z.object({
|
|
113
113
|
schema_version: z.literal('installer-v1'),
|
|
114
114
|
ai_tools: z.array(z.string().min(1)).min(1),
|
|
115
|
-
configs: z.record(z.record(z.unknown())).default({}),
|
|
115
|
+
configs: z.record(z.string(), z.record(z.string(), z.unknown())).default({}),
|
|
116
116
|
dry_run: z.boolean().optional(),
|
|
117
117
|
});
|
|
118
118
|
const wizardV2PayloadSchema = z.object({
|
|
119
119
|
schema_version: z.literal('wizard-v2'),
|
|
120
120
|
tools: z.array(z.string().min(1)).min(1),
|
|
121
121
|
packs: z.array(z.string().min(1)).default([]),
|
|
122
|
-
settings: z.record(z.unknown()).default({}),
|
|
122
|
+
settings: z.record(z.string(), z.unknown()).default({}),
|
|
123
123
|
scope_to_project_only: z.boolean().optional(),
|
|
124
124
|
dry_run: z.boolean().optional(),
|
|
125
125
|
});
|
|
@@ -501,12 +501,12 @@ const aiCouncilPayloadSchema = z.object({
|
|
|
501
501
|
defaultMode: z.enum(['manual', 'api', 'cli', 'auto']).optional(),
|
|
502
502
|
minRounds: z.number().int().min(1).optional(),
|
|
503
503
|
maxTotalUsd: z.number().min(0).optional(),
|
|
504
|
-
members: z.record(z.object({
|
|
504
|
+
members: z.record(z.string(), z.object({
|
|
505
505
|
enabled: z.boolean().optional(),
|
|
506
506
|
participateLowImpact: z.boolean().optional(),
|
|
507
507
|
})).optional(),
|
|
508
508
|
// keys ∈ {trivial, low_impact, medium_impact} — others ignored.
|
|
509
|
-
decision: z.record(z.enum(['agent', 'council', 'user'])).optional(),
|
|
509
|
+
decision: z.record(z.string(), z.enum(['agent', 'council', 'user'])).optional(),
|
|
510
510
|
/**
|
|
511
511
|
* Declared so the operator can flip the one BILLING-CLASS decision in the
|
|
512
512
|
* fallback design without hand-editing — NOT to stop the GUI stripping it:
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* Render strategy: depth ≤ 2. The form renderer (`SchemaForm`, Phase 2)
|
|
11
11
|
* recurses one level into nested objects; deeper trees fail render-time.
|
|
12
12
|
*/
|
|
13
|
-
import
|
|
13
|
+
import * as z from 'zod';
|
|
14
14
|
const ruleLoadingTier = z.enum(['minimal', 'balanced', 'full', 'custom']);
|
|
15
15
|
const disciplineProfile = z.enum(['auto', 'off', 'essential', 'full']);
|
|
16
16
|
const enforcementMode = z.enum(['advisory', 'hard-stop']);
|
|
@@ -35,11 +35,11 @@ export const settingsSchema = z.object({
|
|
|
35
35
|
agent_config_version: z.string().default('').describe('Pin the package to an exact semver (e.g. "1.4.2") so all teammates load the same skill / rule set. Leave empty to track whatever is installed locally — useful for the maintainers of this package, risky for production projects.'),
|
|
36
36
|
profile: z.object({
|
|
37
37
|
id: profileId.default('developer').describe('Which experience you run — the audience identity that selects your default skill / command surface, README entry-path, and persona pre-selection (ADR-010, docs/contracts/profile-system.md). Six seed profiles: developer · content_creator · founder · agency · finance · ops. This is the first wizard question. In 6.0.0-A it records the choice only; pack-scoped surfacing (projection-time filtering, ADR-040) activates in 6.0.0-B behind a staged, opt-in rollout. Switch later with `agent-config use --profile=<id>`.'),
|
|
38
|
-
}).
|
|
38
|
+
}).prefault({ id: 'developer' }),
|
|
39
39
|
projection: z.object({
|
|
40
40
|
mode: projectionMode.default('legacy-all').describe('Whether the per-tool projector writes EVERY artefact into the host-tool trees (.claude/ .cursor/ .windsurf/) or only the active profile + packs\' artefacts (ADR-040, docs/contracts/capability-packs.md). legacy-all = (default, non-breaking) project the full surface exactly as 5.x did. scoped = project only the active profile\'s packs unioned with the runtime.active_packs overlay, expanded over the requires graph — opt in with `agent-config use --profile=<id>`. A failed scoped projection restores the full tree.'),
|
|
41
41
|
rule_packs: z.union([z.literal('auto'), z.array(z.string())]).default([]).describe('Scoping axis for the RULE layer beyond the derived workspace set, per pack ids (src/config/discovery/packs.yml). When set, a non-kernel rule also needs a packs frontmatter intersection to ship — e.g. deselecting frontend-design drops ui-audit-gate + design-fidelity. Opt-in, and an empty list is inactive; the workspace axis beside it is derived rather than configured (ADR-293 step 4.6). The literal "auto" derives the id list from the active-pack set (the same set the skill/command prune uses), so a domain safety floor stops shipping into installs that do not have the pack it guards; an explicit list stays supported and wins over the derivation.'),
|
|
42
|
-
}).
|
|
42
|
+
}).prefault({ mode: 'legacy-all' }),
|
|
43
43
|
rule_loading_tier: ruleLoadingTier.default('balanced').describe('Master switch for which rule tiers load and how cautiously the agent spends tokens. minimal = only the 9 kernel rules (cheapest, fewest guardrails). balanced = kernel + tier-1 (recommended default). full = kernel + tier-1 + tier-2 (most guardrails, highest token cost). custom = roll your own in agents/overrides/. LEGACY: superseded by discipline_profile — when that key is set it wins (mapping: minimal→off, balanced→essential, full→full).'),
|
|
44
44
|
discipline_profile: disciplineProfile.optional().describe('The ONE runtime knob for the discipline-rule tier (successor of rule_loading_tier; council 2026-07-07). off = kernel only (~1x tokens). essential = kernel + the measured lift-carrying rules (~3.3x, keeps the weak-host discipline lift). full = everything (~11.7x, EXPERIMENTAL — residual lift over essential not established). auto = resolve per session against the evidence-gated NULL-lift disable-list in src/config/host-capabilities.yml (measured-null host → off, otherwise → essential). Optional and opt-in until the P1/P2 evidence gates pass (agents/roadmaps/road-to-discipline-profile-tiering.md); absent = legacy rule_loading_tier applies.'),
|
|
45
45
|
cost: z.object({
|
|
@@ -51,13 +51,13 @@ export const settingsSchema = z.object({
|
|
|
51
51
|
cheap: z.number().min(0).nullable().default(null).describe('USD ceiling for the cheap model tier. Budget-aware delegation was ARCHIVED 2026-08-16 (docs/contracts/budget-routing.md), so no code routes on this today; the cap is still summed and reported by `budget.mjs tier`. null = no separate tier cap; global ceilings still apply.'),
|
|
52
52
|
medium: z.number().min(0).nullable().default(null).describe('USD ceiling for the medium model tier. null = no separate tier cap.'),
|
|
53
53
|
strong: z.number().min(0).nullable().default(null).describe('USD ceiling for the strong model tier. null = no separate tier cap. The never-block-to-save-money relation this used to describe went with the archived budget-routing layer (docs/contracts/budget-routing.md); nothing routes between tiers today.'),
|
|
54
|
-
}).
|
|
54
|
+
}).prefault({ cheap: null, medium: null, strong: null }),
|
|
55
55
|
}),
|
|
56
56
|
enforcement: enforcementMode.default('advisory').describe('What happens when a budget hits 100%. advisory = show a banner, keep working (default — never blocks an active task). hard-stop = refuse further model calls until the budget resets or you raise the ceiling.'),
|
|
57
57
|
}),
|
|
58
58
|
model: z.object({
|
|
59
59
|
auto_switch: modelAutoSwitch.default('suggest').describe('Per-skill model auto-switch (ADR-035). Skills declare a vendor-neutral model_tier (lite/medium/high); the generator maps it to a native Claude model (high→opus, medium→sonnet, lite→haiku). suggest (default) = never emit a native Claude model: key; the model-recommendation rule names the tier as a one-question suggestion on every surface — your explicit /model choice is never silently overridden. auto = render a native Claude model: into lite/medium/high-tier skills so Claude Code switches automatically for that turn (reverts next prompt), and suggest on surfaces without a native override. off = inert, no native key and no suggestion.'),
|
|
60
|
-
}).
|
|
60
|
+
}).prefault({ auto_switch: 'suggest' }),
|
|
61
61
|
personal: z.object({
|
|
62
62
|
ide: z.string().default('').describe('CLI binary your IDE registers (code, code-insiders, phpstorm, cursor, windsurf, idea, subl, …). Used by the file-editor skill to open edited files. Leave empty to disable IDE integration.'),
|
|
63
63
|
open_edited_files: z.boolean().default(false).describe('After the agent edits a file, run `<ide> <path>` to surface it in your editor immediately. Off by default to avoid window-stealing during long agent runs.'),
|
|
@@ -76,7 +76,7 @@ export const settingsSchema = z.object({
|
|
|
76
76
|
commit_format: gitCommitFormat.default('ticket-scope').describe('Where a ticket id goes in a commit subject. ticket-scope (default) = the ticket is the Conventional-Commits scope, `feat(DEV-1234): add export filter` — the behaviour every install had before this key existed. ticket-conventional = the ticket leads the subject and the scope names the system area, `DEV-1234 feat(exporter): add export filter`; a ticket is then never a scope. Without a ticket both shapes are plain Conventional Commits. A commit-linting config in the repository (commitlint, a commit-msg hook) outranks this key.'),
|
|
77
77
|
branch_pattern: z.string().default('{type}/{slug}').describe('Shape of a branch name the agent creates. Placeholders: {type} (feat, fix, …), {ticket} (e.g. DEV-1234), {slug} (short kebab-case description). Default {type}/{slug}. {slug} is required, and characters outside the placeholders are limited to A-Z a-z 0-9 . _ / - so a rendered name is safe in a command; agent-config git:convention show reports anything else as invalid. A team that names branches after the ticket sets {ticket}-{slug}. A placeholder with no value is dropped together with the separator that follows it, or the one before it when it is last, so no name starts or ends with a separator ({type}/{ticket}-{slug} without a ticket gives feat/slug). Reading a ticket back out of an existing branch never depends on this pattern.'),
|
|
78
78
|
update_strategy: gitUpdateStrategy.default('merge').describe('How a feature branch is brought up to date with its base. merge (default) = merge the base into the branch, as before this key existed. rebase = rebase the branch onto origin/<base> and publish with --force-with-lease, never a merge of the base into the branch. Rebase rewrites history, so the agent still asks for it unless the git-history-discipline rule already authorises the rewrite (the user asked this turn, an unrevoked standing instruction, or a pull --rebase the user started); this key decides which operation is proposed, never whether one is authorised.'),
|
|
79
|
-
}).
|
|
79
|
+
}).prefault({}),
|
|
80
80
|
github: z.object({
|
|
81
81
|
pr_reply_method: replyMethod.default('create_review_comment').describe('How the agent replies to PR review comments. create_review_comment = post a new review comment (works on every GitHub plan). replies_endpoint = thread the reply under the original comment (needs the newer REST endpoint). auto = detect at runtime, prefer threaded replies when available.'),
|
|
82
82
|
}),
|
|
@@ -106,8 +106,8 @@ export const settingsSchema = z.object({
|
|
|
106
106
|
request_size_caps: z.object({
|
|
107
107
|
bash_max_output_length: z.number().int().positive().max(150_000).nullable().default(null).describe('Character cap on Bash tool output the host places inline into a model request. null (default) = this package writes nothing and the host default of 30000 applies. A number is written into the host env block. Values above 150000 are capped by the host itself, so the schema refuses them here rather than writing a value the host silently changes. Bounds request SIZE, never request COUNT: lowering it suppresses no telemetry, no update check and no download.'),
|
|
108
108
|
max_mcp_output_tokens: z.number().int().positive().nullable().default(null).describe('Token cap on a single MCP tool result the host places into a model request. null (default) = nothing written, host default 25000 applies. Same size-not-count caveat: it does not govern MCP connections, timeouts, or whether a tool runs.'),
|
|
109
|
-
}).
|
|
110
|
-
}).
|
|
109
|
+
}).prefault({}).describe('Request-size caps this package may write into the host env block. Both null by default. No traffic or auto-updater variable is expressible here, by construction — see the block comment.'),
|
|
110
|
+
}).prefault({}).describe('What this package may write into the host environment. ABSENT resolves to the all-null shape, which is the same thing as off — a settings file predating this section must not fail to parse, and "no section" and "both caps null" both mean write nothing.'),
|
|
111
111
|
augment: z.object({
|
|
112
112
|
rules_use_symlinks: z.boolean().default(false).describe('When true, .augment/rules/*.md are symlinks into dist/agent-src/rules/ — edits flow back to source on save. When false (default), they are copies — safer on Windows and shared volumes, but rule edits in .augment/ are lost on the next `task sync`.'),
|
|
113
113
|
}),
|
|
@@ -138,24 +138,24 @@ export const settingsSchema = z.object({
|
|
|
138
138
|
// outcome than an uninformative table cell.
|
|
139
139
|
max_cost_per_run_usd: z.number().min(0).nullable().default(null).describe('Per-run USD ceiling for a CLASS-1 roadmap blocker executed via `agent-config gates --execute`. A class-1 entry whose **Budget:** field states a larger figure renders its consent line instead of running. null (default) = no per-run ceiling, per ADR-279: a spend bound applies only where one was set, and the absence of a bound is not a refusal. Bounds the size of an authorised spend; never supplies the authorisation — `--confirm` is still required on every class-1 run, set or unset.'),
|
|
140
140
|
max_cost_per_rolling_7d_usd: z.number().min(0).nullable().default(null).describe('Rolling 7-day USD ceiling for class-1 gate execution, summed from the append-only receipt ledger at agents/runtime/state/gate-budget-ledger.jsonl. A run whose estimate would cross it renders instead of running. null (default) = no rolling ceiling, per ADR-279. A per-run cap alone bounds one mistake, not a week of them, which is why option (a) of b-gate-budget-preauth carries two numbers — but each cap now bounds on its own, so setting one and leaving the other null is supported.'),
|
|
141
|
-
}).
|
|
141
|
+
}).prefault({ max_cost_per_run_usd: null, max_cost_per_rolling_7d_usd: null }),
|
|
142
142
|
}),
|
|
143
143
|
planning: z.object({
|
|
144
144
|
closure_pass: z.boolean().default(true).describe('Gate C — plan-closure pass. true (default) = a plan-artifact ask (/roadmap:create, roadmap-writing, /feature:plan, /feature:roadmap, /roadmap:materialize, /implement-ticket, /jira-ticket, /analyze:inbox, /analyze:roadmap-repos) ends in a closure pass (/challenge-me closure): every foreseeable decision is closed at the lowest rung that owns it and written into the roadmap\'s "## Decisions" table, so a long run never meets a question planning could have closed. false = inert, plan asks author directly. An explicit user bypass always wins for that turn and is counted as a bypass rather than as an absent closure.'),
|
|
145
145
|
risk_review: z.boolean().default(true).describe('Gate R1 — plan-risk review. true (default) = every ready (non-draft) plan must carry a schema-valid "## Risk Register" section (ranked risks, mitigation + anchor per row, freshness marker, exact honest-null grammar), enforced by lint_plan_risk_register at pre-push + CI. false = escape hatch, the validator skips.'),
|
|
146
146
|
completion_review: z.boolean().default(true).describe('Gate R2 — completion review at 100% roadmap completion / pre-PR. true (default) = a findings-before-fixes review by a fresh reviewer context must exist for the current diff hash (or an exact honest-null / skip declaration) before fix commits and PR creation, enforced by check_completion_review at pre-push + CI (CI authoritative; a crashed validator warns and allows). false = escape hatch, the validator skips.'),
|
|
147
147
|
})
|
|
148
|
-
// `.
|
|
148
|
+
// `.prefault({})` is load-bearing, not cosmetic: without it the SECTION is
|
|
149
149
|
// required, so any settings payload that omits `planning` fails with
|
|
150
150
|
// `ZodError: planning Required` — which contradicts this gate family's own
|
|
151
151
|
// contract ("missing key = true" for all three) and reds every server
|
|
152
152
|
// test whose fixture predates the section. With the default, an absent
|
|
153
153
|
// section materialises as the three `true` leaves the contract promises.
|
|
154
|
-
.
|
|
154
|
+
.prefault({}),
|
|
155
155
|
execution: z.object({
|
|
156
156
|
fix_loop_max: z.number().int().min(1).default(10).describe('Consecutive failed fix attempts against one validation target before the escalation ladder reaches its terminal rung. Reaching the bound triggers a strategy change, never a question. Overridable globally, per project and per prompt.'),
|
|
157
157
|
escalation: z.array(z.string()).default(['independent', 'council', 'team', 'owner_owned_check']).describe('The escalation rungs, in order, walked when the fix-loop bound is reached: independent (another session or a provider-diverse reviewer), council, team, then owner_owned_check — which asks whether the residue is owner-owned, not whether to ask the owner now.'),
|
|
158
|
-
}).
|
|
158
|
+
}).prefault({}),
|
|
159
159
|
quality: z.object({
|
|
160
160
|
local_auto_run: z.boolean().default(false).describe('Run quality tools (linters, type-checks, formatters) and the local test suite autonomously after edits, IN CHAT. Off by default — in a chat turn a human is present and remote CI is the authoritative gate, so a local full-pipeline run duplicates it at wall-clock cost. The agent only runs a quality tool on an explicit ask, a concrete CI failure, or the new-gate carve-out. A mission is governed by local_auto_run_in_mission instead.'),
|
|
161
161
|
local_auto_run_in_mission: z.boolean().default(true).describe('Run quality tools autonomously inside a mission — an autonomous roadmap run with a claimed contract. On by default: nobody is waiting, the next action belongs to the agent itself, and a red found twenty steps later costs more than the run that would have caught it at step one. Inert outside a mission, and a mission never lowers local_auto_run: true.'),
|
|
@@ -170,37 +170,37 @@ export const settingsSchema = z.object({
|
|
|
170
170
|
color: z.number().nullable().default(null).describe('Colour distance below which a value may be reconciled, in OKLab ΔEOK. Ships null, and null means "preserve and report" rather than "reconcile freely". No number is set because none is supported: zero of seven design-to-code benchmarks in the evidence set score token conformance, and a number in a config file — even flagged unmeasured — shapes behavior and acquires the authority of a default. Not an RGB channel delta: RGB distance is not perceptually uniform.'),
|
|
171
171
|
length: z.number().nullable().default(null).describe('Length distance below which a value may be reconciled, in absolute CSS pixels. Ships null, same reasoning as color. GitHub Primer\'s ±1px spacing widening is recorded as an externally observed candidate in src/scripts/_lib/design_tolerance.ts and is deliberately not the shipped value; max(1px, 2%) was rejected for growing more permissive at large dimensions with no evidence that this is wanted.'),
|
|
172
172
|
})
|
|
173
|
-
.
|
|
173
|
+
.prefault({ color: null, length: null }),
|
|
174
174
|
})
|
|
175
|
-
.
|
|
176
|
-
}).
|
|
175
|
+
.prefault({ enabled: false, tolerance: { color: null, length: null } }),
|
|
176
|
+
}).prefault({
|
|
177
177
|
fidelity_mode: 'strict',
|
|
178
178
|
approximation: { enabled: false, tolerance: { color: null, length: null } },
|
|
179
179
|
}),
|
|
180
180
|
code_graph: z.object({
|
|
181
181
|
consumer_index_paths: z.array(z.string()).default([]).describe("Extra project-relative paths where THIS project keeps a code-graph index another tool wrote. The built-in list is deliberately vendor-neutral (graph.json, code-graph.json, .code-graph/graph.json) and names no tool, so a tool that writes its index elsewhere is named here rather than waited for. A graph found this way LOADS and answers query and explain with every edge tagged read-from-elsewhere; the gate verbs (impact, untested, dead) refuse a graph made only of such edges and say so, because an empty answer from a gate must not read as 'nothing found' when it means 'nothing trusted'. Absolute paths and paths escaping the project root are ignored."),
|
|
182
|
-
}).
|
|
182
|
+
}).prefault({}),
|
|
183
183
|
screenshots: z.object({
|
|
184
184
|
identity_allowlist: z.array(z.string()).default([]).describe("Consumed by the doc-screenshot-hygiene rule and screenshot-hygiene skill. Public identity tokens SAFE to show unredacted in a documentation screenshot — the maintainer's own public handles plus well-known fake-data tokens. Not a general fake-data dictionary and not identity-resolution: everything not listed is treated as sensitive by default, and a public handle co-located with a real name does not whitelist the real name. Default [] = nothing auto-allowed."),
|
|
185
185
|
forbid_terminal_capture: z.boolean().default(true).describe('Consumed by the doc-screenshot-hygiene rule. true (default) = terminal/CLI/IDE screenshots are forbidden (highest leak vector: absolute local paths, env tokens); use text code blocks with text redaction instead. false = allowed, still subject to the data-bearing human gate.'),
|
|
186
186
|
data_bearing_gate: z.enum(['on', 'off']).default('on').describe('Consumed by the doc-screenshot-hygiene rule. on (default) = a data-bearing screenshot embed is gated behind this-turn human confirmation; uncertain/unresolved regions redact-or-refuse, never ship-and-hope; illustrative/no-data screenshots may embed with a stated justification. off = no data-bearing gate (the anonymization taxonomy still applies).'),
|
|
187
|
-
}).
|
|
187
|
+
}).prefault({ identity_allowlist: [], forbid_terminal_capture: true, data_bearing_gate: 'on' }),
|
|
188
188
|
verbosity: z.object({
|
|
189
189
|
offer_council_in_delivery: z.boolean().default(false).describe('Offer "run AI Council on this?" inside delivery commands (/feature-plan, /review-changes, /roadmap-create). Council commands themselves are unaffected.'),
|
|
190
|
-
}).
|
|
190
|
+
}).prefault({ offer_council_in_delivery: false }),
|
|
191
191
|
code_style: z.object({
|
|
192
192
|
docblocks: z.enum(['minimal', 'full']).default('minimal').describe('Consumed by the code-comment-discipline rule. minimal (default) = no signature-mirroring docblocks; docblocks only for machine-relevant precision (generics, array shapes) or genuine why-context. full = the exported public surface of a library package may carry one-line summary docblocks; the redundancy ban still holds.'),
|
|
193
|
-
}).
|
|
193
|
+
}).prefault({ docblocks: 'minimal' }),
|
|
194
194
|
reasoning: z.object({
|
|
195
195
|
enabled: z.boolean().default(true).describe('Master switch for the Reasoning Discipline Protocol (RDP). false = the whole layer is inert (zero overhead).'),
|
|
196
|
-
}).
|
|
196
|
+
}).prefault({}),
|
|
197
197
|
subagents: z.object({
|
|
198
198
|
quota_arbitrage: z.boolean().default(true).describe('Prefer a separate quota-pool model for delegable sub-tasks where the host manifest reports one. Optional bonus only — identical behaviour (minus the quota win) where unsupported. Never load-bearing.'),
|
|
199
199
|
model_map: z.object({
|
|
200
200
|
lite: z.string().default('').describe('Model alias for lite-tier sub-tasks. Empty = the tier runtime default.'),
|
|
201
201
|
medium: z.string().default('').describe('Model alias for medium-tier sub-tasks. Empty = the tier runtime default.'),
|
|
202
202
|
high: z.string().default('').describe('Model alias for high-tier sub-tasks. Empty = the tier runtime default.'),
|
|
203
|
-
}).
|
|
203
|
+
}).prefault({}).describe('Per-tier model map for downshift routing. Each empty value uses the tier runtime default (no vendor model baked in).'),
|
|
204
204
|
implementer_model: z.string().default('').describe('Override the model the orchestrator dispatches to subagents that write code (e.g. claude-sonnet-4, gpt-5). Empty (default) = inherit the session\'s primary model — cheapest and usually right.'),
|
|
205
205
|
judge_model: z.string().default('').describe('Override the model used for review / judge subagents that critique implementer output. Empty (default) = one tier above the implementer model — picks up nuance the implementer missed.'),
|
|
206
206
|
model_ceiling: z.string().default('').describe('Session-wide model CEILING for subagents (spend cap). Empty (default) = no ceiling. When set, suite-owned CLI spawn wrappers export CLAUDE_CODE_SUBAGENT_MODEL to the sessions they launch. Class C: a human sets it; the agent never writes or infers one.'),
|
|
@@ -213,7 +213,7 @@ export const settingsSchema = z.object({
|
|
|
213
213
|
// `worktrees.cleanup` governs REMOVAL, a separate question (ADR-295).
|
|
214
214
|
worktrees: z.object({
|
|
215
215
|
cleanup: z.enum(['off', 'notify', 'auto']).default('notify').describe('What the agent may do about a worktree whose work is provably on the trunk (merged by merge commit, rebase or squash), that is clean, holds no ignored file a rebuild would not restore, and has no live session. notify (default, and what an absent key resolves to) says once a day how many exist and removes nothing; auto removes them right after /pr:merge and once a day on the first session start, without force, keeping local branches; off says nothing. The daily pass reads merged pull requests through your gh login (branch name and head commit only).'),
|
|
216
|
-
}).
|
|
216
|
+
}).prefault({ cleanup: 'notify' }),
|
|
217
217
|
ai_team: z.object({
|
|
218
218
|
model: z.string().default('auto').describe("Model handed to the codex CLI. 'auto' (default) = pass no --model flag so the CLI's own default applies — tracks the subscription's current strongest model instead of pinning a stale ID. Any other value passes through verbatim as `--model <value>`."),
|
|
219
219
|
allow_delegate: z.boolean().default(false).describe('Second opt-in for the only wrapper that delegates write access (/team:delegate). false (default) = delegate refuses even when /team itself is available. Availability (codex CLI installed + authenticated) is necessary but not sufficient — this key must ALSO be true before delegation is reachable.'),
|
|
@@ -221,12 +221,12 @@ export const settingsSchema = z.object({
|
|
|
221
221
|
review_gate: z.object({
|
|
222
222
|
managed: z.boolean().default(false).describe("Managed governance of the codex plugin's Stop-hook Review Gate (road-to-team-mode Phase 4). false (default) = byte-identical pre-Phase-4 behavior: no counting, no circuit breaker. true = count consecutive BLOCK verdicts per session and trip the circuit breaker at max_consecutive_blocks."),
|
|
223
223
|
max_consecutive_blocks: z.number().int().min(1).default(3).describe('Circuit-breaker bound: after this many CONSECUTIVE BLOCK verdicts in one session, a visible notice is injected exactly once and the managed layer stops re-blocking — the user decides, never an infinite Claude↔Codex loop. An ALLOW verdict resets the counter. Positive integer.'),
|
|
224
|
-
}).
|
|
225
|
-
}).
|
|
224
|
+
}).prefault({ managed: false, max_consecutive_blocks: 3 }),
|
|
225
|
+
}).prefault({ model: 'auto', allow_delegate: false, max_calls_per_day: 50, review_gate: { managed: false, max_consecutive_blocks: 3 } }),
|
|
226
226
|
emergency: z.object({
|
|
227
227
|
orchestration_halt: z.boolean().default(false).describe('The one audited incident switch over the always-on orchestration stack (subagents, council, team). NOT an activation gate: false (default) = the stack runs normally. true = halted; arming requires no ceremony. Disarming (returning to false) requires orchestration_halt_justification to be a non-empty string. Both transitions emit one telemetry line.'),
|
|
228
228
|
orchestration_halt_justification: z.string().default('').describe('Required non-empty before orchestration_halt may return to false. Ignored while arming the halt.'),
|
|
229
|
-
}).
|
|
229
|
+
}).prefault({ orchestration_halt: false, orchestration_halt_justification: '' }),
|
|
230
230
|
onboarding: z.object({
|
|
231
231
|
onboarded: z.boolean().default(false).describe('Set to true once the developer has completed `agent-config setup`. The onboarding-gate rule blocks the first turn of every chat until this is true. Toggle back to false to re-trigger the wizard.'),
|
|
232
232
|
}),
|
|
@@ -246,7 +246,7 @@ export const settingsSchema = z.object({
|
|
|
246
246
|
}),
|
|
247
247
|
retro: z.object({
|
|
248
248
|
enabled: z.boolean().default(true).describe('Automatic retro after merged work (ADR-292, docs/contracts/retro-v1.md). true (default, owner directive 2026-10-12) = a merged PR and the first session of a new local day enqueue finished work; a deterministic triage picks what is worth one learning, and nothing behavior-changing is applied without a person. The one network read is the daily probe asking GitHub, through your own gh login, whether a pushed PR merged (repository and PR number only, three-second budget). false = no automatic path runs — no merge enqueue, no forge-merge marker, no daily probe and no network read; an explicit /retro still runs on request. Class C: an agent cannot set it; it cascades from the user-global file.'),
|
|
249
|
-
}).
|
|
249
|
+
}).prefault({}),
|
|
250
250
|
memory: z.object({
|
|
251
251
|
redact_patterns: z.array(z.string()).default([]).describe('Regex patterns (one per line) that scrub matches from chat-history transcripts and memory before they hit disk. Use for secrets, customer names, internal URLs. Patterns are anchored and case-insensitive.'),
|
|
252
252
|
session_index: z.enum(['on', 'off']).default('off').describe('Opt-in compact memory index at session start (road-to-memory-retrieval-economy P5). on = inject a compact id + title + ~tokens index of curated entries (hard cap 30 rows, bodies never included) through the hot-context hook; the agent fetches full entries via memory_get on demand. off (default) = no injection — the ship-criterion (measured hit-rate gain) is unproven, so off unless proven.'),
|
|
@@ -256,44 +256,44 @@ export const settingsSchema = z.object({
|
|
|
256
256
|
global_sharing: z.object({
|
|
257
257
|
enabled: z.boolean().default(true).describe('Master switch for the file-first global knowledge-card store (ADR-100; default-ON per ADR-119, the validated bounded-downside flip superseding ADR-103 — write-time redaction incl. hidden-unicode hardening, narrowest tier default, pre-registered demotion trigger). User-global setting — keep in ~/.event4u/agent-config/agent-settings.yml. false fully no-ops the layer (single-key revert); project-local cards (v1) are unaffected.'),
|
|
258
258
|
allowed_tiers: z.array(z.string()).default(['public']).describe('Origin tiers auto-eligible to cross a project boundary. proprietary is manual-only regardless (the gate hard-codes it), so an in-house schema never auto-shares.'),
|
|
259
|
-
}).
|
|
260
|
-
}).
|
|
259
|
+
}).prefault({}),
|
|
260
|
+
}).prefault({}),
|
|
261
261
|
egress: z.object({
|
|
262
262
|
placeholders: z.object({
|
|
263
263
|
mode: z.enum(['off', 'observe', 'protect', 'strict']).default('off').describe('Dispatcher egress stage (docs/contracts/egress-placeholders-v1.md). off (default) = nothing. observe = detect and count listed values in prompts and tool bodies, types and counts only, and change nothing a hook concern sees. protect = also give every recording and advisory concern a copy with each value replaced by a placeholder token, so local stores keep tokens; guards still see the original. strict = as protect until the host rewrite exists. No mode yet changes what reaches the model; the rule table holds credential rows and the validated identifier rows. Project layers only.'),
|
|
264
|
-
}).
|
|
265
|
-
}).
|
|
264
|
+
}).prefault({}),
|
|
265
|
+
}).prefault({}),
|
|
266
266
|
hooks: z.object({
|
|
267
267
|
concern_budget: z.object({
|
|
268
268
|
tier1_concerns: z.array(z.string()).default([]).describe('Concern IDs (one per line) that are allowed to block the run on failure rather than warn. Reserved for high-confidence guards — leave empty unless you maintain custom hooks.'),
|
|
269
269
|
}),
|
|
270
270
|
verify_before_complete: z.object({
|
|
271
271
|
touched_file_quality: z.enum(['off', 'shadow', 'warn']).default('off').describe("Stop-slot pass that runs the project's OWN quality commands — the ones resolve_toolchain() already reports — over the files this turn edited. Default off. `shadow` records a per-run result and emits nothing; `warn` adds ONE advisory line of at most 200 bytes. Never blocks and never counts as verification: a command recorded here never reaches _lib/verification_command.ts, so a clean eslint run cannot let a turn claim it verified anything. A writing command runs only in its check form (pint --test) or not at all, and a command with no per-file form is skipped rather than run project-wide at every stop."),
|
|
272
|
-
}).
|
|
272
|
+
}).prefault({}),
|
|
273
273
|
injection_scan: z.object({
|
|
274
274
|
enabled: z.boolean().default(false).describe('PostToolUse prompt-injection scanner (road-to-security-pillar.md P3.2). Default off. When on, scans tool output (file reads, web fetches, MCP responses) for injection signatures and WARNS in context (never blocks). Runtime backstop on top of the always-on untrusted-input-defense rule; detection is probabilistic.'),
|
|
275
|
-
}).
|
|
275
|
+
}).prefault({}),
|
|
276
276
|
runtime_journal: z.object({
|
|
277
277
|
enabled: z.boolean().default(false).describe('Runtime event journal (road-to-runtime-event-journal Phase 1). Default off. When on, one durable, episode-keyed record per dispatched hook event is appended to <git-common-dir>/agent-journal/journal.sqlite — a Class-A write that opens, writes and exits. It emits nothing to the model, never warns and never blocks: a locked database, a missing node:sqlite, a malformed envelope or a full disk all degrade to silence. Off rather than on because it CREATES a storage surface (a SQLite database under your .git/) rather than appending to a directory that already exists. The record type has no field able to hold free-form content — ids, an event name, a bounded capability identifier, repo-relative locators and two 12-hex digests; no prompt, no file body, no absolute path. Retention: a 30-day TTL anchored on episode close, plus explicit, time-bounded, human-only holds that themselves expire.'),
|
|
278
|
-
}).
|
|
278
|
+
}).prefault({}),
|
|
279
279
|
rtk_wrap: z.object({
|
|
280
280
|
enabled: z.boolean().default(false).describe('PreToolUse RTK-wrap nudge (token-saving Phase 3). Default off. When on AND the binary on PATH is verified as Rust Token Killer (a live two-stage identity probe — not a self-reported flag, and never a colliding same-name binary), warns (never blocks) "re-run wrapped with rtk" before a single verbose CLI command (git/npm/cargo/docker/…) — upstream reports 60–90% output-token savings (their estimate). Skips completeness-critical / piped / compound commands and git diff. No-op when rtk is absent, unverified, or a different tool. Its own activation charge is one advisory line under the 1,024 B default concern cap in src/config/hook-token-budget.json; the per-fire distribution is UNMEASURED — stated rather than estimated, because the saving above is a third-party output-token claim and not a standing-payload reduction this package measured.'),
|
|
281
|
-
}).
|
|
281
|
+
}).prefault({}),
|
|
282
282
|
design_slop: z.object({
|
|
283
283
|
enabled: z.boolean().default(false).describe('PreToolUse anti-slop nudge (road-to-anti-slop-detector Phase 3). Default off. When on, runs the lint_design_slop registry against about-to-be-written UI content and WARNS (never blocks) on P0/P1 aesthetic tells (side-stripe, gradient-text, magic z-index, …). Flags are rebuttable via DESIGN.md / design-slop-disable. Anti-loop: a file::rule signature surfaced 3x goes silent. Host-limited convenience layer; the universal gate is the lint_design_slop linter/CI.'),
|
|
284
|
-
}).
|
|
284
|
+
}).prefault({}),
|
|
285
285
|
design_pass: z.object({
|
|
286
286
|
enabled: z.boolean().default(false).describe('PostToolUse + stop design pass (road-to-frontend-power E1.1/E1.2/E1.3). Default off. One concern on two slots: on post_tool_use a write to a UI surface delivers the design findings as context and never blocks; on stop the same pass runs over every UI file touched this session, deduped against what the post pass surfaced, and a P0 objective floor (contrast, font size, heading skip, focus) blocks with a continuation. P1-P3 never block. post_tool_use rather than pre_tool_use for two measured reasons: pre_tool_use is declared by three hosts and honoured by one while post_tool_use is declared by six, and _lib/ui_surface.ts is a path predicate, so a pre-write gate cannot fire on the first write of a new surface. A pass that could not fully run reports verification: degraded with a reason rather than passing silently. Does not replace design_slop: two design keys is the honest state until the tiering experiment has a number.'),
|
|
287
|
-
}).
|
|
287
|
+
}).prefault({}),
|
|
288
288
|
ui_route_nudge: z.object({
|
|
289
289
|
enabled: z.boolean().default(false).describe('PreToolUse UI-route nudge (road-to-frontend-skill-application Phase 4). Default off. When on, a Write/Edit to a UI surface with no design consultation latched this session WARNS (never blocks) naming the route — run existing-ui-audit, then the fe-design loop. A read or search touching fe-design / existing-ui-audit / design-review / design-intelligence latches consultation and silences it for the session. Anti-loop: at most 2 nudges per session. It does not read the rules: the UI-surface decision comes from _lib/ui_surface.ts and no code parses rule frontmatter, so this runs parallel to the two UI rules rather than consuming their triggers, and a test keeps the sets from drifting. It is a nudge, so their enforced_by: none stays accurate.'),
|
|
290
|
-
}).
|
|
290
|
+
}).prefault({}),
|
|
291
291
|
code_graph: z.object({
|
|
292
292
|
enabled: z.boolean().default(false).describe('PreToolUse code-graph nudge (ADR-124 Phase 4). Default off. When on AND a native code-graph cache or a consumer-shipped graph.json/SCIP index is present, warns once per session (never blocks) as the agent is about to Grep/Glob or Read a source file — query the graph first for who-calls/where-used/impact questions (or rebuild if stale, build if absent). Source G’s strict block-first-read mode is deliberately un-ported.'),
|
|
293
|
-
}).
|
|
293
|
+
}).prefault({}),
|
|
294
294
|
suggestion_capture: z.object({
|
|
295
295
|
enabled: z.boolean().default(false).describe('Suggestion-block capture (road-to-suggestion-block-capture Phase 2). Default off. Two slots: `stop` reads `last_assistant_message` — a payload field, so no transcript file is read — and latches that the assistant turn carried a numbered-options block; `user_prompt_submit` CONSUMES that latch and classifies the answering turn as option_n / as_is / other / stale_block. Consume-once is the correctness guard: the latch is deleted on read, so a bare "1" three turns later meets no latch and classifies `other`, and a latch past its TTL or unparseable is `stale_block` rather than a guess. Writes COUNTS ONLY to agents/runtime/state/audit/suggestion-capture.jsonl — the record type has no field able to hold a prompt, an option label or a command name, and a test asserts the written key set against src/config/suggestion-capture.json. Off by default because this is an INSTRUMENT rather than a feature: it exists so a capture rate can be measured on a maintainer workspace over a fixed soak window, and there is nothing in it for a consumer to gain.'),
|
|
296
|
-
}).
|
|
296
|
+
}).prefault({}),
|
|
297
297
|
// `turn_end_gate` is deliberately ABSENT. The stop-slot turn-end gate is
|
|
298
298
|
// always armed (2026-08-12) and has no settings surface: whether it
|
|
299
299
|
// fires is decided by each detector's own trigger conditions, not by a
|
|
@@ -315,13 +315,13 @@ export const settingsSchema = z.object({
|
|
|
315
315
|
mode: z.enum(['off', 'notify']).default('notify').describe('Whether the first session start of a day prints the advisory findings that need action today. notify (default) reads the local cache only — a session start never touches the network; off prints nothing, and agent-config audit:deps show still works.'),
|
|
316
316
|
egress: z.enum(['none', 'query']).default('query').describe('What leaves the machine when agent-config audit:deps refresh runs. query (default) sends names and versions of public packages to api.osv.dev and reads endoflife.date and the PHP and Node release feeds; a private package name is never sent, and a native npm or composer audit, which sends the lockfile package list to the registry that tool is configured for, runs only for a lockfile with no private package. none sends nothing, and findings read unknown rather than clear.'),
|
|
317
317
|
kev: z.enum(['off', 'on']).default('on').describe('Whether a refresh reads the CISA known-exploited catalog. on (default) downloads the whole catalog and matches it locally, so nothing about the project is sent, and a finding in it is raised one tier in a runtime scope; off leaves tiers to the other sources. Runs only where egress is query.'),
|
|
318
|
-
}).
|
|
318
|
+
}).prefault({ mode: 'notify', egress: 'query', kev: 'on' }),
|
|
319
319
|
update: z.object({
|
|
320
320
|
mode: z.enum(['off', 'notify', 'ask', 'auto']).default('notify').describe('What the agent may do about a newer release. notify tells you and installs nothing (the default, and what an absent key resolves to); ask behaves as notify until the ring-0 exit ships a prompt; auto installs a staged release at a session start when every gate passes and no other session is live; off says nothing at all. Read from the user-global layer only — a repository cannot set how your global install updates itself.'),
|
|
321
|
-
}).
|
|
321
|
+
}).prefault({ mode: 'notify' }),
|
|
322
322
|
legal_review_prep: z.object({
|
|
323
323
|
acknowledged: z.boolean().default(false).describe('I understand the legal-review-prep pack provides templates and general information ONLY — it is NOT legal advice, creates no attorney-client relationship, and never replaces a licensed lawyer. Individual cases require an attorney. The pack stays inactive until this is checked.'),
|
|
324
324
|
require_council: z.boolean().default(true).describe('Gate legal work-product behind a multi-model AI-council / deep-research pass (defense-in-depth: documented multi-stage review + audit trail; fail-closed when no council is configured). Leave on for the safest posture. Turning it off lets single-model legal output through — not recommended for a high-risk pack.'),
|
|
325
|
-
}).
|
|
325
|
+
}).prefault({ acknowledged: false, require_council: true }),
|
|
326
326
|
});
|
|
327
327
|
//# sourceMappingURL=settings.js.map
|
|
@@ -23,7 +23,7 @@ function unwrapRef(root) {
|
|
|
23
23
|
return root;
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
-
* Flatten a `
|
|
26
|
+
* Flatten a `toJsonSchema7` output into dotted leaf entries. Objects with
|
|
27
27
|
* `properties` recurse; everything else (scalars, enums, arrays, free-form
|
|
28
28
|
* maps) is a leaf. Deterministic key order (sorted) so snapshots diff
|
|
29
29
|
* cleanly in git and across runs.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* wizard finish) and the Vite-bundled UI (form validation) consume
|
|
11
11
|
* the same Zod schema. ESLint forbids Node-only imports in this tree.
|
|
12
12
|
*/
|
|
13
|
-
import
|
|
13
|
+
import * as z from 'zod';
|
|
14
14
|
/** Hard cap on the optional `notes` block. Keeps the file loadable into every reply. */
|
|
15
15
|
export const USER_IDENTITY_NOTES_MAX_CHARS = 8_000;
|
|
16
16
|
/** Hard cap on the `voice_sample` block — one to three sentences in practice. */
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A Zod schema as a draft-07 JSON Schema, in the envelope the settings surface,
|
|
3
|
+
* the generated settings reference and the UI form renderer read:
|
|
4
|
+
* `{ $ref: '#/definitions/<name>', definitions: { <name>: … }, $schema }`.
|
|
5
|
+
*
|
|
6
|
+
* Zod 4 converts natively (`z.toJSONSchema`); this replaces `zod-to-json-schema`,
|
|
7
|
+
* which only understands Zod 3. `io: 'input'` keeps a defaulted field optional,
|
|
8
|
+
* as the old converter did, because a settings file may omit it.
|
|
9
|
+
*/
|
|
10
|
+
import * as z from 'zod';
|
|
11
|
+
/**
|
|
12
|
+
* The places the native output differs from what consumers were built on: an
|
|
13
|
+
* object without `additionalProperties` (the old converter closed every
|
|
14
|
+
* object), an `enum` without a `type` (it named the primitive type), and an
|
|
15
|
+
* integer carrying the safe-integer bounds Zod 4 adds to `.int()`, which says
|
|
16
|
+
* nothing a reader of a settings schema can use.
|
|
17
|
+
*/
|
|
18
|
+
function normalise(node) {
|
|
19
|
+
if (Array.isArray(node)) {
|
|
20
|
+
for (const n of node)
|
|
21
|
+
normalise(n);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (typeof node !== 'object' || node === null)
|
|
25
|
+
return;
|
|
26
|
+
const o = node;
|
|
27
|
+
if (o['properties'] !== undefined && o['additionalProperties'] === undefined)
|
|
28
|
+
o['additionalProperties'] = false;
|
|
29
|
+
if (Array.isArray(o['enum']) && o['type'] === undefined) {
|
|
30
|
+
const kinds = new Set(o['enum'].map((v) => typeof v));
|
|
31
|
+
if (kinds.size === 1 && (kinds.has('string') || kinds.has('number') || kinds.has('boolean')))
|
|
32
|
+
o['type'] = [...kinds][0];
|
|
33
|
+
}
|
|
34
|
+
if (o['maximum'] === Number.MAX_SAFE_INTEGER)
|
|
35
|
+
delete o['maximum'];
|
|
36
|
+
if (o['minimum'] === Number.MIN_SAFE_INTEGER)
|
|
37
|
+
delete o['minimum'];
|
|
38
|
+
for (const v of Object.values(o))
|
|
39
|
+
normalise(v);
|
|
40
|
+
}
|
|
41
|
+
export function toJsonSchema7(schema, name) {
|
|
42
|
+
const body = z.toJSONSchema(schema, { target: 'draft-7', io: 'input', unrepresentable: 'any' });
|
|
43
|
+
normalise(body);
|
|
44
|
+
const { $schema: _drop, ...definition } = body;
|
|
45
|
+
return { $ref: `#/definitions/${name}`, definitions: { [name]: definition }, $schema: 'http://json-schema.org/draft-07/schema#' };
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=zodJsonSchema.js.map
|