hoi4-agent-tools 3.5.1 → 3.7.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.
Files changed (72) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +137 -116
  3. package/dist/hoi4_agent_tools/core/engine.js +7 -7
  4. package/dist/hoi4_agent_tools/core/file-glob.js +164 -0
  5. package/dist/hoi4_agent_tools/core/index-segments.js +4 -0
  6. package/dist/hoi4_agent_tools/core/job-operation-registration.js +31 -0
  7. package/dist/hoi4_agent_tools/core/job-worker-host.js +27 -11
  8. package/dist/hoi4_agent_tools/core/job-worker.js +8 -16
  9. package/dist/hoi4_agent_tools/core/persistent-analysis-cache.js +37 -6
  10. package/dist/hoi4_agent_tools/core/scanner.js +133 -47
  11. package/dist/hoi4_agent_tools/core/source/cache.js +4 -0
  12. package/dist/hoi4_agent_tools/gui/layout.js +237 -11
  13. package/dist/hoi4_agent_tools/gui/renderer.js +36 -3
  14. package/dist/hoi4_agent_tools/gui/scenario.js +18 -0
  15. package/dist/hoi4_agent_tools/gui/studio.js +73 -21
  16. package/dist/hoi4_agent_tools/map/service.js +2 -2
  17. package/dist/hoi4_agent_tools/mcp/server/instructions.js +1 -1
  18. package/dist/hoi4_agent_tools/mcp/tools/reference.js +10 -1
  19. package/dist/hoi4_agent_tools/reference/lines.js +12 -0
  20. package/dist/hoi4_agent_tools/reference/operations.js +45 -2
  21. package/dist/hoi4_agent_tools/reference/script-validation.js +277 -0
  22. package/dist/hoi4_agent_tools/reference/service.js +139 -67
  23. package/dist/hoi4_agent_tools/reference/source-lookup.js +91 -15
  24. package/dist/hoi4_agent_tools/reference/source-navigation.js +130 -0
  25. package/dist/hoi4_agent_tools/schemas/reference.js +55 -0
  26. package/dist/hoi4_agent_tools/schemas/script-validation.js +69 -0
  27. package/dist/hoi4_agent_tools/version.js +1 -1
  28. package/docs/README.md +34 -18
  29. package/docs/examples.md +75 -0
  30. package/docs/gui.md +23 -0
  31. package/docs/reference.md +43 -2
  32. package/docs/research/reference-benchmark.md +41 -0
  33. package/docs/setup.md +50 -26
  34. package/docs/tools.md +65 -0
  35. package/npm-shrinkwrap.json +7088 -0
  36. package/package.json +10 -7
  37. package/schemas/configuration.schema.json +1 -1
  38. package/schemas/continuous-focus-palette.schema.json +1 -1
  39. package/schemas/custom-weighted-pool.schema.json +1 -1
  40. package/schemas/decision-inspect-input.schema.json +1 -1
  41. package/schemas/event-feature-manifest.schema.json +1 -1
  42. package/schemas/focus-plan.schema.json +1 -1
  43. package/schemas/focus-planning-sidecar.schema.json +1 -1
  44. package/schemas/gui-animation-source.schema.json +1 -1
  45. package/schemas/gui-helper.schema.json +1 -1
  46. package/schemas/gui-scenario.schema.json +139 -89
  47. package/schemas/helper-expansion-request.schema.json +1 -1
  48. package/schemas/helper-expansion-summary.schema.json +1 -1
  49. package/schemas/impact-inspect-input.schema.json +1 -1
  50. package/schemas/map-operation.schema.json +1 -1
  51. package/schemas/mechanic-test-input.schema.json +1 -1
  52. package/schemas/operation-result.schema.json +1 -1
  53. package/schemas/package-check-input.schema.json +1 -1
  54. package/schemas/package-manifest.schema.json +1 -1
  55. package/schemas/probability-analysis-result.schema.json +1 -1
  56. package/schemas/probability-compare-input.schema.json +1 -1
  57. package/schemas/probability-evaluate-input.schema.json +1 -1
  58. package/schemas/probability-inspect-input.schema.json +1 -1
  59. package/schemas/probability-render-input.schema.json +1 -1
  60. package/schemas/probability-scenario-set.schema.json +1 -1
  61. package/schemas/probability-sequence-input.schema.json +1 -1
  62. package/schemas/probability-simulate-input.schema.json +1 -1
  63. package/schemas/probability-sweep-input.schema.json +1 -1
  64. package/schemas/reference-context-input.schema.json +1 -1
  65. package/schemas/reference-read-input.schema.json +9 -1
  66. package/schemas/reference-search-input.schema.json +1 -1
  67. package/schemas/scenario-suite.schema.json +1 -1
  68. package/schemas/scenario-test-input.schema.json +1 -1
  69. package/schemas/script-validate-input.schema.json +92 -0
  70. package/schemas/shared-scenario.schema.json +1 -1
  71. package/schemas/source-lookup-input.schema.json +68 -2
  72. package/server.json +3 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 3.7.0 - 2026-10-04
6
+
7
+ - Publish an `npm-shrinkwrap.json` so every install reproduces the reviewed dependency closure, including all platforms' optional Sharp binaries (about 300 MB installed).
8
+ - Navigate exact nested source assignments and page through bounded child structure, including repeated keys and same-line blocks.
9
+ - Limit definition-only event/helper lookups to their source family and reuse semantic reference analysis for unchanged snapshots.
10
+ - Check snippet syntax, native command kinds, and declared scopes against cited installed or explicitly selected generated documentation, with unresolved coverage reported separately.
11
+ - Verify source files in bounded parallel batches, reject changing or replaced paths, and bound optional cache persistence without truncating analysis.
12
+ - Load isolated workers' domain modules after authenticating the job, preserving exact operation admission and recorded write policies.
13
+ - Filter reference scans by authority, reuse citation hints, decode supported reference encodings, and preserve byte-bounded continuations and explicit omission counts.
14
+ - Replace the unpatched globbing dependency chain and update vulnerable transitive pins, preserving relative-path, symlink, literal-filename and pattern-complexity checks.
15
+
16
+ ## 3.6.0 - 2026-09-27
17
+
18
+ - Preview immediate tooltip text from source localisation with declared position, width and padding, using the source tooltip sprite and font.
19
+ - Keep unknown GUI visibility and enablement separate from confirmed states, branch coverage and click regions.
20
+ - Identify inferred scrollbar gutter geometry as approximate.
21
+ - Provide a shorter setup guide, practical examples with real renders, and a separate tool reference; remove redundant planning and release-history documents.
22
+
5
23
  ## 3.5.1 - 2026-09-27
6
24
 
7
25
  - Preserve installed-game and offline-wiki citations in compact reference context bundles, select question-relevant sections within required sources, and report sources omitted by the result limit separately from missing files.
package/README.md CHANGED
@@ -1,137 +1,158 @@
1
1
  # HOI4 Agent Tools
2
2
 
3
- HOI4 Agent Tools is an MCP server for coding agents to understand Hearts of Iron IV event chains, decisions, cross-system dependencies, technology systems, AI weights, and MTTH timing and to inspect, create, or clean up focus trees, scripted GUIs, and maps. It produces source-linked structural and visual evidence that agents can use inside a larger modding workflow.
3
+ Give your coding agent one MCP server for Hearts of Iron IV modding.
4
+ It can find local documentation and vanilla examples, inspect a mod, render its trees and interfaces, compare source behavior, and make focused edits to focus trees, GUIs and maps.
5
+ The pictures below use real Chaos Redux or installed HOI4 files; click any image for the full-size version.
4
6
 
5
- ## What it does
7
+ ## Get started
6
8
 
7
- - Focus trees: inspect structure and references, render layouts, create trees, and reorganize existing branches.
8
- - Scripted GUIs: trace GUI, GFX, scripted-GUI, and localisation links; render value-driven variants, dynamic country flags, inline text icons, states, resolutions, hierarchy, and click regions; diagnose alignment, visibility, clipping, panel containment, and button-label centering; create or repair interface source.
9
- - Maps: navigate the complete rendered map by ID or localised name; inspect provinces, states, regions, adjacency, supply, railways, and positions; create states and provinces; change IDs; and repair connected map data.
10
- - Event chains: scan definitions and call sites, trace routes and state flow, lint references, render graphs, and compare revisions without editing event source.
11
- - Cross-system impact and decisions: trace definitions and consumers across source systems, evaluate declared actor and target scenarios, inspect costs and mission paths, and compare in-memory proposals.
12
- - Technology trees: reconstruct technology and doctrine paths, folder layouts, unlocks, bonuses, grants, metadata, assets, and structural changes.
13
- - AI and MTTH: evaluate weighted choices and timing across explicit scenarios, bind special scope chains, enumerate dynamic target pools, sweep uncertain inputs, simulate distributions, compare patches, and analyze declared stateful pools.
14
- - Local references: search and read cited offline wiki pages, installed game documentation, optional script documentation, and exact source symbols without loading whole manuals into the conversation.
9
+ Install [Node.js](https://nodejs.org/) 22.19+ in the Node 22 line, or Node.js 24, then run:
15
10
 
16
- ## Real results
11
+ ```bash
12
+ npm install --global hoi4-agent-tools
13
+ hoi4-agent-tools-setup --print-client-config
14
+ ```
17
15
 
18
- The examples use real installed vanilla and Chaos Redux source, supplied game captures, and explicit reproducible display scenarios. They demonstrate source-backed review, not a claim of 99.9% whole-image accuracy. Images, source revisions, hashes, and known differences are in [Visual comparisons](docs/visual-comparisons.md) and [Interface examples](docs/gui-comparisons.md).
16
+ Add the printed configuration to your MCP client and open your mod as its workspace.
17
+ Keep an installed copy of HOI4 available for vanilla examples, artwork and fonts.
18
+ The [setup guide](docs/setup.md) covers connecting your agent and choosing game or mod paths.
19
19
 
20
- | Options Video in game | Options Video from the MCP renderer |
21
- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
22
- | ![Options Video in game](docs/images/comparisons/gui/options-video-ingame.png) | ![Options Video rendered from installed source](docs/images/comparisons/gui/options-video-mcp.png) |
20
+ Ask your agent to find a focus tree and render it, locate the installed documentation for a script command, or preview a scripted GUI at different UI scales.
21
+ The examples below show the results.
23
22
 
24
- | Škoda Priority in game | Škoda Priority from the MCP renderer |
25
- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
26
- | ![Škoda Priority in game](docs/images/comparisons/gui/skoda-priority-ingame.png) | ![Škoda Priority rendered from installed source](docs/images/comparisons/gui/skoda-priority-mcp.png) |
23
+ ## Examples
27
24
 
28
- The interface gallery also includes Chaos Redux Settings, Event Log, and Chaos Meter, plus populated vanilla decision and occupation templates. Their manifests separate source-defined layout from declared native runtime fields.
25
+ [Focus trees](#focus-trees-and-alternative-prerequisites) · [Technology folders](#technology-folders-and-card-artwork) · [Interfaces](#scripted-interfaces) · [Maps and events](#maps-and-event-chains) · [Local rules and AI](#local-rules-and-ai-weights) · [All tools](#complete-tool-list)
29
26
 
30
- The other workbenches return complete source-linked layouts and catalogs rather than prose-only summaries.
27
+ ### Focus trees and alternative prerequisites
31
28
 
32
- For the supplied focus and technology game captures beside current-source MCP renders, see [Visual examples and comparison limits](https://github.com/klimPaskov/hoi4-agent-tools/blob/main/docs/visual-comparisons.md). These comparisons do not establish 99.9% whole-image accuracy.
29
+ | Tree or branch | Supplied in-game capture | MCP source preview |
30
+ | ---------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
31
+ | Fury | [![Fury in game](docs/images/comparisons/fury-ingame.png)](docs/images/comparisons/fury-ingame.png) | [![Fury focus tree](docs/images/comparisons/fury-mcp.png)](docs/images/comparisons/fury-mcp.png) |
32
+ | Holy Realm | [![Holy Realm in game](docs/images/comparisons/holy-realm-ingame.png)](docs/images/comparisons/holy-realm-ingame.png) | [![Holy Realm focus tree](docs/images/comparisons/holy-realm-mcp.png)](docs/images/comparisons/holy-realm-mcp.png) |
33
+ | Utopia Manifesto | [![Utopia in game](docs/images/comparisons/utopia-ingame.png)](docs/images/comparisons/utopia-ingame.png) | [![Utopia focus tree](docs/images/comparisons/utopia-mcp.png)](docs/images/comparisons/utopia-mcp.png) |
33
34
 
34
- | Fury focus tree from Chaos Redux | Infantry technology folder from Chaos Redux and vanilla |
35
- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
36
- | ![Fury focus tree rendered from Chaos Redux source](docs/images/comparisons/fury-mcp.png) | ![Infantry technology folder rendered from installed source](docs/images/comparisons/infantry-mcp.png) |
35
+ Dotted connectors in the MCP preview mark focuses where either preceding focus satisfies one prerequisite group.
36
+ The tree previews show route structure; the game's focus screen has different framing, labels and icons.
37
+ The [focus guide](docs/focus.md) explains inspection, rendering and edits, and the [comparison details](docs/visual-comparisons.md#focus-trees) retain source revisions.
38
+
39
+ ### Technology folders and card artwork
40
+
41
+ > Render a technology folder. Check card sizes, artwork, links and missing placements.
42
+
43
+ | Folder | Supplied in-game capture | MCP source preview |
44
+ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
45
+ | Infantry | [![Infantry in game](docs/images/comparisons/infantry-ingame.png)](docs/images/comparisons/infantry-ingame.png) | [![Infantry technology folder](docs/images/comparisons/infantry-mcp.png)](docs/images/comparisons/infantry-mcp.png) |
46
+ | Chemical warfare | [![Chemical folder in game](docs/images/comparisons/chemical-recent-ingame.png)](docs/images/comparisons/chemical-recent-ingame.png) | [![Chemical folder preview](docs/images/comparisons/chemical-historical-mcp.png)](docs/images/comparisons/chemical-historical-mcp.png) |
37
47
 
38
- | Map | Event options |
39
- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
40
- | ![Installed map rendered by the MCP](docs/images/comparisons/source/map-mcp.png) | ![Chaos Redux event chain rendered by the MCP](docs/images/comparisons/source/event-chain-mcp.png) |
48
+ The chemical comparison uses the source revision recorded with the screenshot and includes wide equipment cards.
49
+ The [technology comparison](docs/visual-comparisons.md#technology-folders) records its source revision, and the [technology guide](docs/technology.md) covers inspection and doctrine paths.
41
50
 
42
- These two views retain native output resolution. Their [source manifest](docs/images/comparisons/source/manifest.json) records the map revision and the exact Fury event used for the option view. The event renderer's omitted-node count includes the rest of the workspace graph outside that selection.
51
+ ### Scripted interfaces
43
52
 
44
- ## Use from a coding agent
53
+ > Preview an interface with its game assets and fonts. Check states, text clipping and clickable areas at 100% and 125% UI scale.
45
54
 
46
- Requires Node.js 22.19 or later in the Node 22 line, or Node.js 24.
55
+ | Interface | Supplied in-game capture | MCP source preview |
56
+ | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
57
+ | Video options | [![Options in game](docs/images/comparisons/gui/options-video-ingame.png)](docs/images/comparisons/gui/options-video-ingame.png) | [![Options preview](docs/images/comparisons/gui/options-video-mcp.png)](docs/images/comparisons/gui/options-video-mcp.png) |
58
+ | Škoda Priority | [![Škoda Priority in game](docs/images/comparisons/gui/skoda-priority-ingame.png)](docs/images/comparisons/gui/skoda-priority-ingame.png) | [![Škoda Priority preview](docs/images/comparisons/gui/skoda-priority-mcp.png)](docs/images/comparisons/gui/skoda-priority-mcp.png) |
59
+ | Chaos Redux Tag Manager | [![Tag Manager in game](docs/images/comparisons/gui/tag-manager-ingame.png)](docs/images/comparisons/gui/tag-manager-ingame.png) | [![Tag Manager preview](docs/images/comparisons/gui/tag-manager-mcp.png)](docs/images/comparisons/gui/tag-manager-mcp.png) |
47
60
 
48
- ```bash
49
- npm install --global hoi4-agent-tools@latest
50
- ```
61
+ The Tag Manager screenshot comes from an older layout.
62
+ The Options preview has no observed gamma value, and Škoda's game capture frames its background differently.
51
63
 
52
- ```toml
53
- [mcp_servers.hoi4_agent_tools]
54
- command = "hoi4-agent-tools.cmd"
55
- startup_timeout_sec = 120
56
- tool_timeout_sec = 600
57
- ```
64
+ | Event Log | Chaos Meter: status | Chaos Meter: deaths |
65
+ | -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
66
+ | [![Event Log preview](docs/images/comparisons/gui/event-log-mcp.png)](docs/images/comparisons/gui/event-log-mcp.png) | [![Chaos Meter status](docs/images/comparisons/gui/chaos-meter-status-mcp.png)](docs/images/comparisons/gui/chaos-meter-status-mcp.png) | [![Chaos Meter deaths](docs/images/comparisons/gui/chaos-meter-deaths-mcp.png)](docs/images/comparisons/gui/chaos-meter-deaths-mcp.png) |
58
67
 
59
- On non-Windows systems, use `hoi4-agent-tools` as the command. Agentic HOI4 repositories can include these steps in their agent template so the coding agent installs and registers the server itself; manual installation is not required in that workflow. Once connected, the server follows the active mod supplied by the client and the agent can call its HOI4 tools directly.
60
-
61
- ## Tools
62
-
63
- | Tool | Purpose |
64
- | --------------------------- | ----------------------------------------------------------------------------------------- |
65
- | `hoi4.focus_inspect` | Read focus trees, continuous-focus placement, and structural or reference problems. |
66
- | `hoi4.focus_render` | Produce fast HTML, SVG, JSON, and source-linked layout artifacts. |
67
- | `hoi4.focus_raster` | Produce a high-fidelity PNG review with decoded source icons. |
68
- | `hoi4.focus_rewrite` | Create or update a focus tree. |
69
- | `hoi4.gui_inspect` | Read a scripted GUI and its linked assets and logic. |
70
- | `hoi4.gui_render` | Render generated and explicit GUI scenarios, states, resolutions, and layout diagnostics. |
71
- | `hoi4.gui_rewrite` | Create or update a GUI source package. |
72
- | `hoi4.map_inspect` | Search, click, navigate, and inspect the complete rendered map and its linked data. |
73
- | `hoi4.map_render` | Render full-map layers, overlays, names, IDs, coordinates, and source-linked catalogs. |
74
- | `hoi4.map_rewrite` | Create or update states, provinces, IDs, networks, positions, and connected map data. |
75
- | `hoi4.reference_context` | Get compact, cited wiki and installed documentation pointers for a modding surface. |
76
- | `hoi4.reference_search` | Search bounded local documentation sections. |
77
- | `hoi4.reference_read` | Read one revision-bound section, with line continuation. |
78
- | `hoi4.source_lookup` | Find exact definitions, overrides, usages, and narrow source blocks. |
79
- | `hoi4.event_inspect` | Scan, trace, explain, lint, or assess event chains and their state flow. |
80
- | `hoi4.event_render` | Render source-linked event routes, options, timing, state, scope, and unresolved edges. |
81
- | `hoi4.event_compare` | Compare event-chain topology and diagnostics between revisions. |
82
- | `hoi4.impact_inspect` | Trace symbol and changed-file consumers across source systems and compare proposals. |
83
- | `hoi4.decision_inspect` | Inventory and evaluate decisions or missions under declared scenarios and source changes. |
84
- | `hoi4.mechanic_test` | Execute bounded source effects on a copied declared scenario and check assertions. |
85
- | `hoi4.package_check` | Check declarative package definitions, calls, registrations, assets, and case links. |
86
- | `hoi4.scenario_test` | Run named source and domain cases in resumable, revision-bound batches. |
87
- | `hoi4.job_inspect` | Inspect durable background work or retrieve its completed tool result. |
88
- | `hoi4.job_cancel` | Durably request cancellation of authorized background work. |
89
- | `hoi4.tech_inspect` | Scan, trace, explain, lint, and assess technology and doctrine systems. |
90
- | `hoi4.tech_render` | Render source layouts with real item sizes and year guides, plus dependencies and assets. |
91
- | `hoi4.tech_compare` | Compare technology graphs, placements, references, diagnostics, and source overlays. |
92
- | `hoi4.probability_inspect` | Locate weighted logic and discover compatible adapters, candidates, and required inputs. |
93
- | `hoi4.probability_evaluate` | Evaluate exact values, probabilities, timing, bounds, and unresolved inputs. |
94
- | `hoi4.probability_sweep` | Find sensitivity, breakpoints, cliffs, and rank reversals across declared ranges. |
95
- | `hoi4.probability_simulate` | Run deterministic sampled analysis with confidence and convergence data. |
96
- | `hoi4.probability_sequence` | Analyze declared recovery, caps, cooldowns, resets, timers, and terminal states. |
97
- | `hoi4.probability_compare` | Attribute AI-weight and MTTH changes between real or proposed source. |
98
- | `hoi4.probability_render` | Render cached rankings, matrices, timing, sensitivity, sequence, and comparisons. |
99
-
100
- Set `HOI4_AGENT_TOOLS_CHAOSX=1` on the server process to expose the optional `chaosx.focus_country_assets` and `chaosx.visual_revision` tools used by ChaosX workflows. They remain absent from the default public tool list.
101
-
102
- Large outputs are linked `hoi4-agent://` resources. For resources over 1 MiB, follow the `continuationUri` returned in `_meta` until it is `null`; clients may also request byte ranges with `?offset=<bytes>&length=<bytes>`.
103
-
104
- Long-running domain calls advertise optional MCP task support. A task survives client disconnects and can be retrieved after reconnect; clients without task support keep the ordinary synchronous result contract. Native background rewrites require a stable caller-supplied `requestKey` so retries resolve to one durable mutation receipt. See [Persistent jobs and MCP tasks](docs/jobs.md).
105
-
106
- ## Coexistence with agent workflows
107
-
108
- HOI4 Agent Tools provides HOI4 domain operations without replacing repository instructions such as `AGENTS.md`, skills, plans, or subagents. Its optional weighted-logic prompt helps an agent scope one probability analysis and then returns control to the normal workflow.
109
-
110
- Connecting and listing tools does not scan mod source. Compact tool schemas and linked resources keep large diagnostics, renders, and diffs out of the agent's working context until needed. Event, impact, decision, technology, and probability tools analyze source without editing it; only `hoi4.*_rewrite` calls edit mod source.
111
-
112
- ## Create or clean content
113
-
114
- Ask your agent in normal task language. A typical workflow is inspect, render, rewrite, then inspect the result. The agent can call a raster tool when a pixel review is useful without paying that cost during every structural operation.
115
-
116
- - Focus trees: "Create a complete national focus tree for this route specification," or "Compact this existing tree into a balanced, readable layout." Existing trees can use a plan-free compact reflow; new trees use a complete plan. See [Focus trees](docs/focus.md).
117
- - Scripted GUIs: "Create a scripted GUI for this mechanic," or "Render every value-driven version of this window and fix hidden controls, off-center button text, background alignment, clipping, and click-region conflicts." See [Scripted GUIs](docs/gui.md).
118
- - Maps: "Render the whole map and find this state by name," "Create a state from these provinces," "Create a province inside this exact rectangle," or "Swap these state IDs and update connected references." See [Maps](docs/map.md).
119
- - Event chains: "Trace every route from this event and explain where its flags and variables change," or "Compare the workspace event graph with its previous revision and render the affected routes." See [Event chains](docs/events.md).
120
- - Cross-system impact and decisions: "Find every direct and transitive consumer of this idea," or "Compare who can take this decision, what they pay, and its mission end paths for these actor and target scenarios." See [Cross-system impact and decisions](docs/analysis.md).
121
- - Mechanics and packages: "Test this transfer's conservation and payment," or "Check every declared package link and resume its named scenario suite." See [Mechanic tests and scenario suites](docs/mechanics.md).
122
- - Technology trees: "Explain everything this technology requires and unlocks," or "Compare this technology patch and render every affected folder and doctrine branch." See [Technology trees](docs/technology.md).
123
- - AI and MTTH: "Compare these focus weights across peace, defensive-war, and low-stability scenarios," or "Show when this MTTH event becomes likely and which unknown inputs control the result." See [AI and MTTH analysis](docs/probability.md).
124
- - References: "Find the installed effect signature and an exact vanilla example," or "Show the pertinent wiki and installed documentation for this GUI task." See [Local references](docs/reference.md).
125
-
126
- ## HTTP
127
-
128
- Use stdio for local MCP clients. For shared or remote deployments, see [HTTP](docs/http.md).
129
-
130
- ## Development
68
+ | Vanilla decisions | Vanilla occupation |
69
+ | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
70
+ | [![Decision window](docs/images/comparisons/native-ui/countrydecisionview-window.png)](docs/images/comparisons/native-ui/countrydecisionview-window.png) | [![Occupation window](docs/images/comparisons/native-ui/countryoccupationview-window.png)](docs/images/comparisons/native-ui/countryoccupationview-window.png) |
131
71
 
132
- ```bash
133
- npm ci
134
- npm run check
135
- ```
72
+ The Event Log, Chaos Meter, decision and occupation previews use declared states or positions for layout review.
73
+ They are source previews, not observations of a running campaign.
74
+ See the [interface gallery](docs/gui-comparisons.md) for full images and fidelity notes, and the [GUI guide](docs/gui.md) for scenario inputs.
75
+
76
+ ### Maps and event chains
77
+
78
+ > Find Brandenburg on the map, inspect its provinces and neighbours, and show the affected source files before a change.
79
+
80
+ > Trace an event's options and follow-up events. Point out unresolved calls and link each route to its source.
81
+
82
+ | Source map | Chaos Redux event chain |
83
+ | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
84
+ | [![Map rendered from installed source](docs/images/comparisons/source/map-mcp.png)](docs/images/comparisons/source/map-mcp.png) | [![Chaos Redux event options](docs/images/comparisons/source/event-chain-mcp.png)](docs/images/comparisons/source/event-chain-mcp.png) |
85
+
86
+ The [map guide](docs/map.md) covers connected province, state, supply and railway data.
87
+ The [event guide](docs/events.md) covers routes, scope, timing and unresolved references.
88
+
89
+ ### Local rules and AI weights
90
+
91
+ > Find the installed documentation for `save_event_target_as`, read the relevant section, and show a vanilla usage with file and line references.
92
+
93
+ > Compare this decision's `ai_will_do` score before and after a change in peace and war. List missing conditions separately from known results.
94
+
95
+ The [reference example](docs/research/reference-benchmark.md) found the installed effect documentation first and read its relevant 12-line section instead of loading whole wiki pages.
96
+ The [AI guide](docs/probability.md) covers supported weights, chance and timing models; a source score alone does not establish an AI click probability.
97
+ The [examples guide](docs/examples.md) has more prompt details.
98
+
99
+ ## Complete tool list
100
+
101
+ The default server exposes 35 tools.
102
+ Your agent can select them from ordinary language; use the live tool schema for call parameters.
136
103
 
137
- See [Development](docs/development.md). Apache-2.0 licensed. Hearts of Iron IV and Paradox Interactive are trademarks of their respective owners; this project is unaffiliated.
104
+ | Tool | Purpose |
105
+ | --------------------------- | -------------------------------------------------------------------------------------------- |
106
+ | `hoi4.focus_inspect` | Read focus trees, continuous-focus placement, and structural or reference problems. |
107
+ | `hoi4.focus_render` | Produce fast HTML, SVG, JSON, and source-linked layout artifacts. |
108
+ | `hoi4.focus_raster` | Produce a PNG preview with decoded source icons. |
109
+ | `hoi4.focus_rewrite` | Create or update a focus tree. |
110
+ | `hoi4.gui_inspect` | Read a scripted GUI and its linked assets and logic. |
111
+ | `hoi4.gui_render` | Render generated and explicit GUI scenarios, states, resolutions, and layout diagnostics. |
112
+ | `hoi4.gui_rewrite` | Create or update a GUI source package. |
113
+ | `hoi4.map_inspect` | Search, click, navigate, and inspect the complete rendered map and its linked data. |
114
+ | `hoi4.map_render` | Render full-map layers, overlays, names, IDs, coordinates, and source-linked catalogs. |
115
+ | `hoi4.map_rewrite` | Create or update states, provinces, IDs, networks, positions, and connected map data. |
116
+ | `hoi4.reference_context` | Get compact, cited wiki and installed documentation pointers for a modding surface. |
117
+ | `hoi4.reference_search` | Search bounded local documentation sections. |
118
+ | `hoi4.reference_read` | Read one revision-bound section, with line continuation. |
119
+ | `hoi4.source_lookup` | Find exact definitions, overrides, usages, and narrow source blocks. |
120
+ | `hoi4.script_validate` | Check snippet syntax, native command kinds, and declared scopes against local documentation. |
121
+ | `hoi4.event_inspect` | Scan, trace, explain, lint, or assess event chains and their state flow. |
122
+ | `hoi4.event_render` | Render source-linked event routes, options, timing, state, scope, and unresolved edges. |
123
+ | `hoi4.event_compare` | Compare event-chain topology and diagnostics between revisions. |
124
+ | `hoi4.impact_inspect` | Trace symbol and changed-file consumers across source systems and compare proposals. |
125
+ | `hoi4.decision_inspect` | Inventory and evaluate decisions or missions under declared scenarios and source changes. |
126
+ | `hoi4.mechanic_test` | Execute bounded source effects on a copied declared scenario and check assertions. |
127
+ | `hoi4.package_check` | Check declarative package definitions, calls, registrations, assets, and case links. |
128
+ | `hoi4.scenario_test` | Run named source and domain cases in resumable, revision-bound batches. |
129
+ | `hoi4.job_inspect` | Inspect durable background work or retrieve its completed tool result. |
130
+ | `hoi4.job_cancel` | Durably request cancellation of authorized background work. |
131
+ | `hoi4.tech_inspect` | Scan, trace, explain, lint, and assess technology and doctrine systems. |
132
+ | `hoi4.tech_render` | Render source layouts with real item sizes and year guides, plus dependencies and assets. |
133
+ | `hoi4.tech_compare` | Compare technology graphs, placements, references, diagnostics, and source overlays. |
134
+ | `hoi4.probability_inspect` | Locate weighted logic and discover compatible adapters, candidates, and required inputs. |
135
+ | `hoi4.probability_evaluate` | Evaluate supported weights, probability and timing models, and missing inputs. |
136
+ | `hoi4.probability_sweep` | Find sensitivity, breakpoints, cliffs, and rank reversals across declared ranges. |
137
+ | `hoi4.probability_simulate` | Run deterministic sampled analysis with confidence and convergence data. |
138
+ | `hoi4.probability_sequence` | Analyze declared recovery, caps, cooldowns, resets, timers, and terminal states. |
139
+ | `hoi4.probability_compare` | Attribute AI-weight and MTTH changes between real or proposed source. |
140
+ | `hoi4.probability_render` | Render cached rankings, matrices, timing, sensitivity, sequence, and comparisons. |
141
+
142
+ Set `HOI4_AGENT_TOOLS_CHAOSX=1` to expose two optional Chaos Redux workflow tools: `chaosx.focus_country_assets` and `chaosx.visual_revision`.
143
+ The [tool guide](docs/tools.md) explains results, linked artifacts and long operations.
144
+
145
+ ## How it fits your workflow
146
+
147
+ The server works through your coding agent and follows the mod workspace you provide.
148
+ It keeps large renders and reports in linked files, and retrieves relevant documentation sections instead of whole manuals.
149
+ Your repository instructions and review process still apply.
150
+
151
+ Inspection and rendering do not edit the mod.
152
+ The focus, GUI and map rewrite tools can change source files after checking the proposed edit.
153
+ The server does not launch or control the game; previews and source tests cannot prove live clicks, AI choices or engine timing.
154
+
155
+ [Documentation](docs/README.md) · [Tool reference](docs/tools.md) · [Contributing](CONTRIBUTING.md) · [Security](SECURITY.md)
156
+
157
+ Apache-2.0 licensed.
158
+ Hearts of Iron IV artwork belongs to its respective owners; this project is unaffiliated with Paradox Interactive.
@@ -182,13 +182,6 @@ export class CoreEngine {
182
182
  signal: controller.signal,
183
183
  });
184
184
  controller.signal.throwIfAborted();
185
- const persistentAnalysisCache = await this.persistentAnalysisCache;
186
- const analysisScope = {
187
- workspaceIdentity: workspace.workspaceIdentity,
188
- rootFingerprint: transactionRootFingerprint(workspace),
189
- principal: principal ?? null,
190
- };
191
- await persistentAnalysisCache?.hydrate(analysisScope, files, sourceDocuments, this.indexSegments, controller.signal);
192
185
  const revision = hashCanonical(files.map(({ absolutePath, displayPath, relativePath, rootKind, loadOrder, shadowedBy, sha256, }) => ({
193
186
  absolutePath,
194
187
  displayPath,
@@ -202,6 +195,13 @@ export class CoreEngine {
202
195
  const cached = this.#scanCache.get(cacheKey);
203
196
  if (cached !== undefined)
204
197
  return cached;
198
+ const persistentAnalysisCache = await this.persistentAnalysisCache;
199
+ const analysisScope = {
200
+ workspaceIdentity: workspace.workspaceIdentity,
201
+ rootFingerprint: transactionRootFingerprint(workspace),
202
+ principal: principal ?? null,
203
+ };
204
+ await persistentAnalysisCache?.hydrate(analysisScope, files, sourceDocuments, this.indexSegments, controller.signal);
205
205
  const index = await SymbolIndex.buildAsync(files, controller.signal, this.indexSegments);
206
206
  await persistentAnalysisCache?.persist(analysisScope, files, sourceDocuments, this.indexSegments, controller.signal);
207
207
  const snapshot = {
@@ -0,0 +1,164 @@
1
+ import path from 'node:path';
2
+ import { escape, globIterate, hasMagic, Ignore, unescape } from 'glob';
3
+ import { lstat, readdir } from 'node:fs/promises';
4
+ import { ServiceError } from './result.js';
5
+ function validatePatterns(patterns) {
6
+ if (patterns.length > 8192)
7
+ throw new ServiceError('SCAN_PATTERN_LIMIT', 'Too many source glob patterns');
8
+ let wildcardCount = 0;
9
+ for (const pattern of patterns) {
10
+ if (Buffer.byteLength(pattern, 'utf8') > 4096)
11
+ throw new ServiceError('SCAN_PATTERN_LIMIT', 'Source glob pattern exceeds its byte limit');
12
+ if (pattern.startsWith('/') ||
13
+ /^[A-Za-z]:/u.test(pattern) ||
14
+ pattern.split('/').includes('..') ||
15
+ pattern.includes('\0'))
16
+ throw new ServiceError('SCAN_PATTERN_ESCAPE', 'Source glob patterns must stay relative to the authorized root');
17
+ let depth = 0;
18
+ const groups = [];
19
+ let expansions = 1;
20
+ for (let index = 0; index < pattern.length; index++) {
21
+ const character = pattern[index];
22
+ if (character === '\\') {
23
+ index++;
24
+ continue;
25
+ }
26
+ if (character === '{') {
27
+ if (++depth > 8)
28
+ throw new ServiceError('SCAN_PATTERN_LIMIT', 'Source glob brace nesting exceeds its limit');
29
+ groups.push({ start: index + 1, choices: 1 });
30
+ }
31
+ if (character === ',' && groups.length > 0)
32
+ groups.at(-1).choices++;
33
+ if (character === '}') {
34
+ depth = Math.max(0, depth - 1);
35
+ const group = groups.pop();
36
+ if (group === undefined)
37
+ continue;
38
+ const range = /^(-?\d+|[A-Za-z])\.\.(-?\d+|[A-Za-z])(?:\.\.(-?\d+))?$/u.exec(pattern.slice(group.start, index));
39
+ if (range !== null) {
40
+ const first = /^-?\d+$/u.test(range[1]) ? Number(range[1]) : range[1].charCodeAt(0);
41
+ const last = /^-?\d+$/u.test(range[2]) ? Number(range[2]) : range[2].charCodeAt(0);
42
+ const step = Math.abs(Number(range[3] ?? 1));
43
+ group.choices = Math.floor(Math.abs(last - first) / step) + 1;
44
+ }
45
+ expansions *= group.choices;
46
+ if (!Number.isSafeInteger(expansions) || expansions > 128)
47
+ throw new ServiceError('SCAN_PATTERN_LIMIT', 'Source glob expansion exceeds its limit');
48
+ }
49
+ }
50
+ // Validate brace complexity before invoking the glob parser, which can expand braces.
51
+ if (hasMagic(pattern, { magicalBraces: true }) && ++wildcardCount > 512)
52
+ throw new ServiceError('SCAN_PATTERN_LIMIT', 'Too many wildcard source glob patterns');
53
+ }
54
+ }
55
+ /** Relative, cancellable enumeration without traversing symbolic-link directories. */
56
+ export async function* iterateFiles(patterns, options) {
57
+ validatePatterns(patterns);
58
+ validatePatterns(options.ignore ?? []);
59
+ options.signal?.throwIfAborted();
60
+ const nocase = options.caseSensitive === false;
61
+ const excluded = new Ignore([...(options.ignore ?? [])], { nocase });
62
+ const seen = new Set();
63
+ const allowedPatterns = [];
64
+ for (const pattern of new Set(patterns)) {
65
+ let current = options.cwd;
66
+ let allowed = true;
67
+ let literal = true;
68
+ let finalIsFile = false;
69
+ for (const part of pattern.split('/')) {
70
+ if (hasMagic(part, { magicalBraces: true })) {
71
+ literal = false;
72
+ break;
73
+ }
74
+ let name = unescape(part);
75
+ let candidate = path.resolve(current, name);
76
+ const relative = path.relative(options.cwd, candidate).replaceAll('\\', '/');
77
+ if (relative === '..' || relative.startsWith('../') || path.isAbsolute(relative))
78
+ throw new ServiceError('SCAN_PATTERN_ESCAPE', 'Literal glob prefix escaped its authorized root');
79
+ let metadata;
80
+ try {
81
+ metadata = await lstat(candidate);
82
+ }
83
+ catch (error) {
84
+ if (error.code !== 'ENOENT' &&
85
+ error.code !== 'ENOTDIR')
86
+ throw error;
87
+ if (!nocase) {
88
+ allowed = false;
89
+ break;
90
+ }
91
+ try {
92
+ name =
93
+ (await readdir(current)).find((entry) => entry.toLowerCase() === name.toLowerCase()) ??
94
+ name;
95
+ candidate = path.resolve(current, name);
96
+ metadata = await lstat(candidate);
97
+ }
98
+ catch {
99
+ allowed = false;
100
+ break;
101
+ }
102
+ }
103
+ if (metadata.isSymbolicLink()) {
104
+ allowed = false;
105
+ break;
106
+ }
107
+ finalIsFile = metadata.isFile();
108
+ current = candidate;
109
+ }
110
+ if (!allowed)
111
+ continue;
112
+ if (literal) {
113
+ if (!finalIsFile)
114
+ continue;
115
+ const relative = path.relative(options.cwd, current).replaceAll('\\', '/');
116
+ if (excluded.relative.some((rule) => rule.match(relative)) ||
117
+ excluded.absolute.some((rule) => rule.match(current)))
118
+ continue;
119
+ options.signal?.throwIfAborted();
120
+ if (!seen.has(relative)) {
121
+ seen.add(relative);
122
+ yield relative;
123
+ }
124
+ }
125
+ else
126
+ allowedPatterns.push(pattern);
127
+ }
128
+ if (allowedPatterns.length === 0)
129
+ return;
130
+ const matches = globIterate(allowedPatterns, {
131
+ cwd: options.cwd,
132
+ withFileTypes: true,
133
+ nodir: true,
134
+ dot: false,
135
+ follow: false,
136
+ nocase,
137
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
138
+ ignore: {
139
+ ignored: (entry) => entry.isSymbolicLink() || excluded.ignored(entry),
140
+ childrenIgnored: (entry) => entry.isSymbolicLink() || excluded.childrenIgnored(entry),
141
+ },
142
+ });
143
+ for await (const entry of matches) {
144
+ options.signal?.throwIfAborted();
145
+ if (!entry.isFile() || entry.isSymbolicLink())
146
+ continue;
147
+ const relative = path.relative(options.cwd, entry.fullpath()).replaceAll('\\', '/');
148
+ if (relative === '..' || relative.startsWith('../') || path.isAbsolute(relative))
149
+ throw new ServiceError('SCAN_PATTERN_ESCAPE', 'Glob result escaped its authorized root');
150
+ if (!seen.has(relative)) {
151
+ seen.add(relative);
152
+ yield relative;
153
+ }
154
+ }
155
+ }
156
+ export async function globFiles(patterns, options) {
157
+ const files = [];
158
+ for await (const file of iterateFiles(patterns, options))
159
+ files.push(file);
160
+ return files;
161
+ }
162
+ export function escapeFileGlob(relativePath) {
163
+ return escape(relativePath, { magicalBraces: true });
164
+ }
@@ -38,6 +38,10 @@ export class IndexSegmentCache {
38
38
  this.#entries.set(address, entry);
39
39
  return JSON.parse(entry.encoded);
40
40
  }
41
+ /** Availability check without decoding or cache-hit accounting. */
42
+ has(address) {
43
+ return this.#entries.has(address);
44
+ }
41
45
  /** Import one authenticated canonical entry without counting it as a process-local hit. */
42
46
  importEncoded(address, encoded) {
43
47
  const segment = JSON.parse(encoded);
@@ -0,0 +1,31 @@
1
+ import { JobOperations } from './job-executor.js';
2
+ import { ServiceError } from './result.js';
3
+ /** Load one trusted domain after the authenticated job identifies its operation. */
4
+ export async function registerWorkerOperations(engine, toolName) {
5
+ const operations = new JobOperations();
6
+ if (toolName.startsWith('hoi4.event_'))
7
+ (await import('../event/job-operations.js')).registerEventJobs(operations, engine);
8
+ else if (toolName.startsWith('hoi4.focus_'))
9
+ (await import('../focus/job-operations.js')).registerFocusJobs(operations, engine);
10
+ else if (toolName.startsWith('hoi4.gui_'))
11
+ (await import('../gui/job-operations.js')).registerGuiJobs(operations, engine);
12
+ else if (toolName.startsWith('hoi4.map_'))
13
+ (await import('../map/job-operations.js')).registerMapJobs(operations, engine);
14
+ else if (toolName.startsWith('hoi4.probability_'))
15
+ (await import('../probability/job-operations.js')).registerProbabilityJobs(operations, engine);
16
+ else if (toolName.startsWith('hoi4.tech_'))
17
+ (await import('../technology/job-operations.js')).registerTechnologyJobs(operations, engine);
18
+ else if ([
19
+ 'hoi4.impact_inspect',
20
+ 'hoi4.decision_inspect',
21
+ 'hoi4.mechanic_test',
22
+ 'hoi4.package_check',
23
+ 'hoi4.scenario_test',
24
+ ].includes(toolName))
25
+ (await import('./analysis-job-operations.js')).registerAnalysisJobs(operations, engine);
26
+ else
27
+ throw new ServiceError('JOB_OPERATION_UNAVAILABLE', 'No trusted worker domain matches this job');
28
+ // A matching prefix never authorizes an unknown suffix or a client-selected module.
29
+ operations.get(toolName);
30
+ return operations;
31
+ }
@@ -149,9 +149,11 @@ export class JobWorkerHost {
149
149
  stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
150
150
  windowsHide: true,
151
151
  });
152
+ let phase = 'waiting_for_ready';
153
+ let acknowledgmentTimer;
152
154
  const initialized = new Promise((resolve, reject) => {
153
155
  child.once('error', reject);
154
- child.once('exit', () => reject(new ServiceError('JOB_WORKER_STARTUP_EXIT', 'The worker exited before completing its readiness handshake')));
156
+ child.once('exit', (code, signal) => reject(new ServiceError('JOB_WORKER_STARTUP_EXIT', `The worker exited before completing its startup handshake (phase ${phase}, exit ${code ?? 'none'}, signal ${signal ?? 'none'})`)));
155
157
  child.once('message', (value) => {
156
158
  void (async () => {
157
159
  if (typeof value !== 'object' ||
@@ -162,7 +164,23 @@ export class JobWorkerHost {
162
164
  throw new ServiceError('JOB_WORKER_PROTOCOL', 'The worker did not provide its readiness handshake');
163
165
  await lease.handoffToProcess(child.pid);
164
166
  await new Promise((resolve, reject) => {
165
- child.once('disconnect', resolve);
167
+ phase = 'waiting_for_acceptance';
168
+ acknowledgmentTimer = setTimeout(() => reject(new ServiceError('JOB_WORKER_ACCEPT_TIMEOUT', 'The worker did not acknowledge receipt within its startup window')), 30_000);
169
+ child.once('message', (accepted) => {
170
+ if (typeof accepted !== 'object' ||
171
+ accepted === null ||
172
+ !('type' in accepted) ||
173
+ accepted.type !== 'accepted') {
174
+ reject(new ServiceError('JOB_WORKER_PROTOCOL', 'The worker did not acknowledge the dispatched job'));
175
+ return;
176
+ }
177
+ phase = 'accepted';
178
+ // Only close IPC after the child confirms receipt. The persistent job,
179
+ // rather than the launcher's lifetime, owns execution after this point.
180
+ if (child.connected)
181
+ child.disconnect();
182
+ resolve();
183
+ });
166
184
  child.send({
167
185
  configuration: {
168
186
  ...configuration,
@@ -178,14 +196,7 @@ export class JobWorkerHost {
178
196
  jobId: id,
179
197
  ...(principal === undefined ? {} : { principal }),
180
198
  }, (error) => {
181
- if (error === null) {
182
- // IPC is startup-only. Closing it after dispatch prevents launcher
183
- // lifetime from becoming an implicit cancellation channel.
184
- if (child.connected)
185
- child.disconnect();
186
- resolve();
187
- }
188
- else
199
+ if (error !== null)
189
200
  reject(error);
190
201
  });
191
202
  });
@@ -199,7 +210,12 @@ export class JobWorkerHost {
199
210
  });
200
211
  });
201
212
  });
202
- await initialized;
213
+ try {
214
+ await initialized;
215
+ }
216
+ finally {
217
+ clearTimeout(acknowledgmentTimer);
218
+ }
203
219
  for (;;) {
204
220
  const record = await this.jobs.get(workspaceId, id, principal);
205
221
  if (['completed', 'failed', 'cancelled'].includes(record.status))