hoi4-agent-tools 3.3.0 → 3.4.1
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/CHANGELOG.md +22 -0
- package/dist/hoi4_agent_tools/core/condition-evaluator.js +20 -8
- package/dist/hoi4_agent_tools/core/helper-expansion.js +3 -0
- package/dist/hoi4_agent_tools/core/index.js +1 -0
- package/dist/hoi4_agent_tools/core/job-worker-host.js +19 -1
- package/dist/hoi4_agent_tools/core/operation-result.js +4 -1
- package/dist/hoi4_agent_tools/event/comparison-selection.js +78 -0
- package/dist/hoi4_agent_tools/event/helper-expansion.js +48 -0
- package/dist/hoi4_agent_tools/event/operations.js +32 -10
- package/dist/hoi4_agent_tools/event/service.js +21 -2
- package/dist/hoi4_agent_tools/focus/compact.js +241 -27
- package/dist/hoi4_agent_tools/focus/layout.js +119 -8
- package/dist/hoi4_agent_tools/focus/render.js +62 -0
- package/dist/hoi4_agent_tools/focus/scenario.js +99 -0
- package/dist/hoi4_agent_tools/focus/tool-operations.js +30 -4
- package/dist/hoi4_agent_tools/gui/assets.js +54 -4
- package/dist/hoi4_agent_tools/gui/layout.js +935 -104
- package/dist/hoi4_agent_tools/gui/renderer.js +47 -17
- package/dist/hoi4_agent_tools/gui/shader.js +515 -0
- package/dist/hoi4_agent_tools/gui/source-graph.js +166 -6
- package/dist/hoi4_agent_tools/gui/studio.js +245 -19
- package/dist/hoi4_agent_tools/gui/tool-operations.js +9 -0
- package/dist/hoi4_agent_tools/gui/validators.js +41 -12
- package/dist/hoi4_agent_tools/map/operations.js +70 -1
- package/dist/hoi4_agent_tools/map/render.js +70 -1
- package/dist/hoi4_agent_tools/map/service.js +30 -6
- package/dist/hoi4_agent_tools/map/tool-operations.js +67 -1
- package/dist/hoi4_agent_tools/mcp/tools/event.js +10 -1
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +14 -4
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +5 -1
- package/dist/hoi4_agent_tools/mcp/tools/map.js +12 -2
- package/dist/hoi4_agent_tools/mcp/tools/probability.js +5 -0
- package/dist/hoi4_agent_tools/mechanic/interpreter.js +91 -30
- package/dist/hoi4_agent_tools/mechanic/source.js +19 -4
- package/dist/hoi4_agent_tools/probability/evaluation.js +3 -0
- package/dist/hoi4_agent_tools/probability/operations.js +8 -0
- package/dist/hoi4_agent_tools/probability/service.js +33 -12
- package/dist/hoi4_agent_tools/probability/source-analysis.js +1 -0
- package/dist/hoi4_agent_tools/schemas/event.js +8 -0
- package/dist/hoi4_agent_tools/schemas/focus-requests.js +40 -1
- package/dist/hoi4_agent_tools/schemas/gui-requests.js +8 -0
- package/dist/hoi4_agent_tools/schemas/map-requests.js +22 -1
- package/dist/hoi4_agent_tools/schemas/technology.js +7 -0
- package/dist/hoi4_agent_tools/technology/graph.js +25 -10
- package/dist/hoi4_agent_tools/technology/operations.js +29 -1
- package/dist/hoi4_agent_tools/technology/render.js +287 -31
- package/dist/hoi4_agent_tools/technology/service.js +56 -8
- package/dist/hoi4_agent_tools/technology/source-analysis.js +166 -3
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/events.md +4 -2
- package/docs/focus.md +10 -0
- package/docs/gui.md +50 -3
- package/docs/jobs.md +1 -0
- package/docs/map.md +8 -0
- package/docs/mechanics.md +2 -1
- package/docs/probability.md +3 -1
- package/docs/technology.md +11 -2
- package/package.json +1 -1
- package/schemas/configuration.schema.json +1 -1
- package/schemas/continuous-focus-palette.schema.json +1 -1
- package/schemas/custom-weighted-pool.schema.json +1 -1
- package/schemas/decision-inspect-input.schema.json +1 -1
- package/schemas/event-feature-manifest.schema.json +1 -1
- package/schemas/focus-plan.schema.json +1 -1
- package/schemas/focus-planning-sidecar.schema.json +1 -1
- package/schemas/gui-animation-source.schema.json +1 -1
- package/schemas/gui-helper.schema.json +1 -1
- package/schemas/gui-scenario.schema.json +1 -1
- package/schemas/helper-expansion-request.schema.json +1 -1
- package/schemas/helper-expansion-summary.schema.json +1 -1
- package/schemas/impact-inspect-input.schema.json +1 -1
- package/schemas/map-operation.schema.json +1 -1
- package/schemas/mechanic-test-input.schema.json +1 -1
- package/schemas/operation-result.schema.json +1 -1
- package/schemas/package-check-input.schema.json +1 -1
- package/schemas/package-manifest.schema.json +1 -1
- package/schemas/probability-analysis-result.schema.json +1 -1
- package/schemas/probability-compare-input.schema.json +1 -1
- package/schemas/probability-evaluate-input.schema.json +1 -1
- package/schemas/probability-inspect-input.schema.json +1 -1
- package/schemas/probability-render-input.schema.json +1 -1
- package/schemas/probability-scenario-set.schema.json +1 -1
- package/schemas/probability-sequence-input.schema.json +1 -1
- package/schemas/probability-simulate-input.schema.json +1 -1
- package/schemas/probability-sweep-input.schema.json +1 -1
- package/schemas/scenario-suite.schema.json +1 -1
- package/schemas/scenario-test-input.schema.json +1 -1
- package/schemas/shared-scenario.schema.json +1 -1
- package/server.json +2 -2
|
@@ -115,6 +115,12 @@ function resolvedNumber(value, constants) {
|
|
|
115
115
|
}
|
|
116
116
|
return undefined;
|
|
117
117
|
}
|
|
118
|
+
function resolvedNumericScalar(value, constants) {
|
|
119
|
+
if (!value?.startsWith('@'))
|
|
120
|
+
return value;
|
|
121
|
+
const resolved = resolvedNumber(value, constants);
|
|
122
|
+
return resolved === undefined ? value : String(resolved);
|
|
123
|
+
}
|
|
118
124
|
function aiMetadata(block) {
|
|
119
125
|
const ai = childBlocks(block, 'ai_will_do')[0];
|
|
120
126
|
const weights = childBlocks(block, 'ai_research_weights')[0];
|
|
@@ -183,7 +189,7 @@ function addDynamicOrStatic(values, ownerId, document, staticAction, output, kin
|
|
|
183
189
|
}
|
|
184
190
|
function parseTechnologyDefinitions(file, document, fragment) {
|
|
185
191
|
for (const wrapper of childBlocks(document.root, 'technologies')) {
|
|
186
|
-
const constants = constantsIn(wrapper);
|
|
192
|
+
const constants = new Map([...constantsIn(document.root), ...constantsIn(wrapper)]);
|
|
187
193
|
for (const definition of assignments(wrapper)) {
|
|
188
194
|
if (definition.key.value.startsWith('@') || definition.value.type !== 'block')
|
|
189
195
|
continue;
|
|
@@ -378,10 +384,12 @@ function parseTechnologyDefinitions(file, document, fragment) {
|
|
|
378
384
|
rawSource: document.text.slice(definition.start, definition.end),
|
|
379
385
|
...(firstValue(block, 'start_year') === undefined
|
|
380
386
|
? {}
|
|
381
|
-
: { startYear: firstValue(block, 'start_year') }),
|
|
387
|
+
: { startYear: resolvedNumericScalar(firstValue(block, 'start_year'), constants) }),
|
|
382
388
|
...(firstValue(block, 'research_cost') === undefined
|
|
383
389
|
? {}
|
|
384
|
-
: {
|
|
390
|
+
: {
|
|
391
|
+
researchCost: resolvedNumericScalar(firstValue(block, 'research_cost'), constants),
|
|
392
|
+
}),
|
|
385
393
|
...(firstValue(block, 'doctrine_name') === undefined
|
|
386
394
|
? {}
|
|
387
395
|
: { doctrineName: firstValue(block, 'doctrine_name') }),
|
|
@@ -612,6 +620,58 @@ function parseTechnologyGridboxes(file, document, fragment, signal) {
|
|
|
612
620
|
const absoluteX = current.offsetX + (localX ?? 0);
|
|
613
621
|
const absoluteY = current.offsetY + (localY ?? 0);
|
|
614
622
|
const folderId = technologyFolderContainer(current.namedContainers);
|
|
623
|
+
const backgroundSprite = firstValue(block, 'spriteType');
|
|
624
|
+
if (elementType === 'containerwindowtype' && name?.value.endsWith('_folder') === true) {
|
|
625
|
+
const panel = childBlocks(block, 'background')[0];
|
|
626
|
+
const panelSprite = panel === undefined ? undefined : firstValue(panel, 'quadTextureSprite');
|
|
627
|
+
if (panelSprite !== undefined)
|
|
628
|
+
(fragment.backgrounds ??= []).push({
|
|
629
|
+
id: deterministicId('tech-panel', {
|
|
630
|
+
name: name.value,
|
|
631
|
+
path: file.displayPath,
|
|
632
|
+
offset: assignment.start,
|
|
633
|
+
}),
|
|
634
|
+
name: 'folder_panel',
|
|
635
|
+
folderId: name.value,
|
|
636
|
+
sprite: panelSprite,
|
|
637
|
+
position: { x: 0, y: 0 },
|
|
638
|
+
size: {},
|
|
639
|
+
location: nodeLocation(document, assignment, name.value),
|
|
640
|
+
sourcePath: file.displayPath,
|
|
641
|
+
loadOrder: file.loadOrder,
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
if (elementType === 'icontype' &&
|
|
645
|
+
name !== undefined &&
|
|
646
|
+
folderId !== undefined &&
|
|
647
|
+
backgroundSprite !== undefined &&
|
|
648
|
+
(name.value.endsWith('_techtree_bg') || backgroundSprite.endsWith('_techtree_bg')) &&
|
|
649
|
+
localX !== undefined &&
|
|
650
|
+
localY !== undefined) {
|
|
651
|
+
const parentSize = childBlocks(current.block, 'size')[0];
|
|
652
|
+
const size = childBlocks(block, 'size')[0] ?? parentSize;
|
|
653
|
+
const width = numericLiteral(size === undefined ? undefined : firstValue(size, 'width'));
|
|
654
|
+
const height = numericLiteral(size === undefined ? undefined : firstValue(size, 'height'));
|
|
655
|
+
const background = {
|
|
656
|
+
id: deterministicId('tech-background', {
|
|
657
|
+
name: name.value,
|
|
658
|
+
path: file.displayPath,
|
|
659
|
+
offset: assignment.start,
|
|
660
|
+
}),
|
|
661
|
+
name: name.value,
|
|
662
|
+
folderId,
|
|
663
|
+
sprite: backgroundSprite,
|
|
664
|
+
position: { x: absoluteX, y: absoluteY },
|
|
665
|
+
size: {
|
|
666
|
+
...(width === undefined ? {} : { width }),
|
|
667
|
+
...(height === undefined ? {} : { height }),
|
|
668
|
+
},
|
|
669
|
+
location: nodeLocation(document, assignment, name.value),
|
|
670
|
+
sourcePath: file.displayPath,
|
|
671
|
+
loadOrder: file.loadOrder,
|
|
672
|
+
};
|
|
673
|
+
(fragment.backgrounds ??= []).push(background);
|
|
674
|
+
}
|
|
615
675
|
if (elementType === 'gridboxtype' && name?.value.endsWith('_tree') === true) {
|
|
616
676
|
const slotSize = childBlocks(block, 'slotsize')[0];
|
|
617
677
|
const width = numericLiteral(slotSize === undefined ? undefined : firstValue(slotSize, 'width'));
|
|
@@ -659,6 +719,64 @@ function parseTechnologyGridboxes(file, document, fragment, signal) {
|
|
|
659
719
|
const size = childBlocks(block, 'size')[0];
|
|
660
720
|
const width = numericLiteral(size === undefined ? undefined : firstValue(size, 'width'));
|
|
661
721
|
const height = numericLiteral(size === undefined ? undefined : firstValue(size, 'height'));
|
|
722
|
+
const subTechnologySlots = assignments(block)
|
|
723
|
+
.flatMap((child) => {
|
|
724
|
+
if (child.value.type !== 'block' ||
|
|
725
|
+
child.key.value.toLowerCase() !== 'containerwindowtype')
|
|
726
|
+
return [];
|
|
727
|
+
const slotName = firstValue(child.value, 'name');
|
|
728
|
+
const match = /^sub_technology_slot_(\d+)$/u.exec(slotName ?? '');
|
|
729
|
+
if (match === null)
|
|
730
|
+
return [];
|
|
731
|
+
const slotPosition = childBlocks(child.value, 'position')[0];
|
|
732
|
+
const slotSize = childBlocks(child.value, 'size')[0];
|
|
733
|
+
const x = numericLiteral(slotPosition === undefined ? undefined : firstValue(slotPosition, 'x'));
|
|
734
|
+
const y = numericLiteral(slotPosition === undefined ? undefined : firstValue(slotPosition, 'y'));
|
|
735
|
+
const slotWidth = numericLiteral(slotSize === undefined ? undefined : firstValue(slotSize, 'width'));
|
|
736
|
+
const slotHeight = numericLiteral(slotSize === undefined ? undefined : firstValue(slotSize, 'height'));
|
|
737
|
+
if (x === undefined ||
|
|
738
|
+
y === undefined ||
|
|
739
|
+
slotWidth === undefined ||
|
|
740
|
+
slotHeight === undefined)
|
|
741
|
+
return [];
|
|
742
|
+
const picture = childBlocks(child.value, 'iconType').find((icon) => firstValue(icon, 'name') === 'picture');
|
|
743
|
+
const sprite = picture === undefined ? undefined : firstValue(picture, 'spriteType');
|
|
744
|
+
return [
|
|
745
|
+
{
|
|
746
|
+
index: Number(match[1]),
|
|
747
|
+
position: { x, y },
|
|
748
|
+
size: { width: slotWidth, height: slotHeight },
|
|
749
|
+
...(sprite === undefined ? {} : { sprite }),
|
|
750
|
+
},
|
|
751
|
+
];
|
|
752
|
+
})
|
|
753
|
+
.sort((left, right) => left.index - right.index);
|
|
754
|
+
const itemBackground = childBlocks(block, 'background')[0];
|
|
755
|
+
const backgroundSprite = itemBackground === undefined
|
|
756
|
+
? undefined
|
|
757
|
+
: firstValue(itemBackground, 'quadTextureSprite');
|
|
758
|
+
const icon = childBlocks(block, 'iconType').find((candidate) => firstValue(candidate, 'name') === 'Icon');
|
|
759
|
+
const iconPosition = icon === undefined ? undefined : childBlocks(icon, 'position')[0];
|
|
760
|
+
const iconX = iconPosition === undefined ? undefined : numericLiteral(firstValue(iconPosition, 'x'));
|
|
761
|
+
const iconY = iconPosition === undefined ? undefined : numericLiteral(firstValue(iconPosition, 'y'));
|
|
762
|
+
const designTeam = childBlocks(block, 'iconType').find((candidate) => firstValue(candidate, 'name') === 'can_assign_design_team_icon');
|
|
763
|
+
const designTeamPosition = designTeam === undefined ? undefined : childBlocks(designTeam, 'position')[0];
|
|
764
|
+
const designTeamX = designTeamPosition === undefined
|
|
765
|
+
? undefined
|
|
766
|
+
: numericLiteral(firstValue(designTeamPosition, 'x'));
|
|
767
|
+
const designTeamY = designTeamPosition === undefined
|
|
768
|
+
? undefined
|
|
769
|
+
: numericLiteral(firstValue(designTeamPosition, 'y'));
|
|
770
|
+
const designTeamSprite = designTeam === undefined ? undefined : firstValue(designTeam, 'spriteType');
|
|
771
|
+
const nameBox = childBlocks(block, 'instantTextBoxType').find((candidate) => firstValue(candidate, 'name') === 'name');
|
|
772
|
+
const nameBoxPosition = nameBox === undefined ? undefined : childBlocks(nameBox, 'position')[0];
|
|
773
|
+
const nameX = nameBoxPosition === undefined
|
|
774
|
+
? undefined
|
|
775
|
+
: numericLiteral(firstValue(nameBoxPosition, 'x'));
|
|
776
|
+
const nameY = nameBoxPosition === undefined
|
|
777
|
+
? undefined
|
|
778
|
+
: numericLiteral(firstValue(nameBoxPosition, 'y'));
|
|
779
|
+
const nameMaxWidth = nameBox === undefined ? undefined : numericLiteral(firstValue(nameBox, 'maxWidth'));
|
|
662
780
|
const layout = {
|
|
663
781
|
id: deterministicId('tech-item-layout', {
|
|
664
782
|
name: name.value,
|
|
@@ -688,6 +806,31 @@ function parseTechnologyGridboxes(file, document, fragment, signal) {
|
|
|
688
806
|
? {}
|
|
689
807
|
: { heightExpression: firstValue(size, 'height') ?? '<missing>' }),
|
|
690
808
|
},
|
|
809
|
+
...(backgroundSprite === undefined ? {} : { backgroundSprite }),
|
|
810
|
+
...(iconX === undefined || iconY === undefined
|
|
811
|
+
? {}
|
|
812
|
+
: {
|
|
813
|
+
iconPosition: {
|
|
814
|
+
x: iconX,
|
|
815
|
+
y: iconY,
|
|
816
|
+
centered: booleanValue(firstValue(icon, 'centerposition')) === true,
|
|
817
|
+
},
|
|
818
|
+
}),
|
|
819
|
+
...(designTeamX === undefined ||
|
|
820
|
+
designTeamY === undefined ||
|
|
821
|
+
designTeamSprite === undefined
|
|
822
|
+
? {}
|
|
823
|
+
: { designTeamIcon: { sprite: designTeamSprite, x: designTeamX, y: designTeamY } }),
|
|
824
|
+
...(nameX === undefined || nameY === undefined
|
|
825
|
+
? {}
|
|
826
|
+
: {
|
|
827
|
+
namePosition: {
|
|
828
|
+
x: nameX,
|
|
829
|
+
y: nameY,
|
|
830
|
+
...(nameMaxWidth === undefined ? {} : { maxWidth: nameMaxWidth }),
|
|
831
|
+
},
|
|
832
|
+
}),
|
|
833
|
+
...(subTechnologySlots.length === 0 ? {} : { subTechnologySlots }),
|
|
691
834
|
location: nodeLocation(document, assignment, name.value),
|
|
692
835
|
sourcePath: file.displayPath,
|
|
693
836
|
loadOrder: file.loadOrder,
|
|
@@ -714,6 +857,26 @@ function parseTechnologyGridboxes(file, document, fragment, signal) {
|
|
|
714
857
|
folderId,
|
|
715
858
|
year,
|
|
716
859
|
position: { x: absoluteX, y: absoluteY },
|
|
860
|
+
style: {
|
|
861
|
+
...(firstValue(block, 'font') === undefined
|
|
862
|
+
? {}
|
|
863
|
+
: { fontName: firstValue(block, 'font') }),
|
|
864
|
+
...(firstValue(block, 'textColor') === undefined
|
|
865
|
+
? {}
|
|
866
|
+
: { colour: firstValue(block, 'textColor') }),
|
|
867
|
+
...(numericLiteral(firstValue(block, 'maxWidth')) === undefined
|
|
868
|
+
? {}
|
|
869
|
+
: { maxWidth: numericLiteral(firstValue(block, 'maxWidth')) }),
|
|
870
|
+
...(numericLiteral(firstValue(block, 'maxHeight')) === undefined
|
|
871
|
+
? {}
|
|
872
|
+
: { maxHeight: numericLiteral(firstValue(block, 'maxHeight')) }),
|
|
873
|
+
...(firstValue(block, 'format') === undefined
|
|
874
|
+
? {}
|
|
875
|
+
: { format: firstValue(block, 'format') }),
|
|
876
|
+
...(firstValue(block, 'Orientation') === undefined
|
|
877
|
+
? {}
|
|
878
|
+
: { orientation: firstValue(block, 'Orientation') }),
|
|
879
|
+
},
|
|
717
880
|
location: nodeLocation(document, assignment, name.value),
|
|
718
881
|
sourcePath: file.displayPath,
|
|
719
882
|
loadOrder: file.loadOrder,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
export const PACKAGE_NAME = 'hoi4-agent-tools';
|
|
3
|
-
export const PACKAGE_VERSION = '3.
|
|
3
|
+
export const PACKAGE_VERSION = '3.4.1';
|
|
4
4
|
export const MCP_PROTOCOL_VERSION = LATEST_PROTOCOL_VERSION;
|
|
5
5
|
export const CONFIG_VERSION = 1;
|
|
6
6
|
export const TRANSACTION_VERSION = 1;
|
package/docs/events.md
CHANGED
|
@@ -19,7 +19,7 @@ Use the three event tools directly from the target mod.
|
|
|
19
19
|
|
|
20
20
|
Use narrow identifiers, direction, and depth limits when the task concerns one chain.
|
|
21
21
|
Broad scans build the structural event graph without expanding every scripted helper into duplicate paths.
|
|
22
|
-
Focused trace, path, and selector-based inventory requests may defer workspace-wide helper projections and lifecycle passes
|
|
22
|
+
Focused trace, path, and selector-based inventory requests may defer workspace-wide helper projections and lifecycle passes. When every event source was scanned and no blocking diagnostics remain, validation accepts the requested direct-source evidence while the linked artifacts continue to record that deferred boundary.
|
|
23
23
|
Focused trace and path calls collapse only the selected bounded route.
|
|
24
24
|
For a large helper closure, use [bounded helper expansion](helper-expansion.md) to receive resumable pages without first materializing every projection.
|
|
25
25
|
|
|
@@ -80,7 +80,9 @@ Example `hoi4.event_render` arguments:
|
|
|
80
80
|
|
|
81
81
|
## Compare
|
|
82
82
|
|
|
83
|
-
`hoi4.event_compare` compares whole workspace event graphs
|
|
83
|
+
`hoi4.event_compare` compares whole workspace event graphs. `before` and `after` each accept exactly one graph reference: `{ "revision": "<sha256>" }` or `{ "artifactUri": "hoi4-agent://..." }`. If no reference or overlay is supplied, the tool compares the previous cached revision with a fresh scan of the current source and reports an error when no prior revision is available. Comparisons refresh current source by default so normal agent file edits are detected; set `refresh: false` only when intentionally comparing cached graphs.
|
|
84
|
+
|
|
85
|
+
Set `selector` to an event, namespace, file, source location, node, or manifest to view the downstream chain in both revisions. `maxChainNodes` bounds each traversal, defaults to 1,000, and reports `selectionTruncated` when the chain exceeds the boundary. The artifact retains the full comparison and graph hashes, and its `selection` reports selected, omitted, and unattributed changes explicitly. Diagnostics without a chain owner remain unattributed. The rendered view uses the selected changed nodes.
|
|
84
86
|
|
|
85
87
|
Alternatively, compare the current graph with `proposedSources`, an in-memory list of `{ relativePath, source, expectedSourceHash? }` overlays. Set `source` to a string to add or replace source, or to `null` to delete an existing mod source. `after` and `proposedSources` are mutually exclusive. Proposed sources are analyzed without being written. The result reports added, removed, and changed definitions, edges, options, state operations, diagnostics, and route reachability.
|
|
86
88
|
|
package/docs/focus.md
CHANGED
|
@@ -34,6 +34,10 @@ The rewrite measures canvas bounds, same-row spacing, the same curved connector
|
|
|
34
34
|
|
|
35
35
|
Use the default `layoutMode: "authored"` with a complete plan when deliberately designing coordinates or changing gameplay. Set `layoutMode: "compact"` with a complete plan when creating a large automatically arranged tree. Inspect and render calls remain useful before or after either rewrite mode, but a separate plan is not required for compact cleanup.
|
|
36
36
|
|
|
37
|
+
For a bounded layout repair, set `compactFocusIds` to the focus IDs whose positions may change and `pinnedFocusIds` to anchors that must keep their current coordinates. The compact planner preserves the positions and coordinates of every focus outside the selection and leaves gameplay fields untouched. It evaluates candidate layouts against the current tree and reports a constraint error if the selected repair cannot preserve those anchors without worsening connector or spacing defects.
|
|
38
|
+
|
|
39
|
+
Pass `symmetryGroups` with a `centerFocusId` and one or more `{ "leftFocusId": "...", "rightFocusId": "..." }` pairs to `hoi4.focus_rewrite` in compact mode when named branches should align at equal distance and row around a center focus. A pair may contain a pinned or unselected focus; incompatible fixed positions produce a conflict with the specific pair and offsets.
|
|
40
|
+
|
|
37
41
|
A cleanup should not change gameplay relationships to improve the picture. One `prerequisite` block containing several focus IDs is an OR group; several prerequisite blocks are AND requirements.
|
|
38
42
|
|
|
39
43
|
## Layout metadata and unsupported script
|
|
@@ -46,6 +50,12 @@ If malformed or unsupported script makes a requested change ambiguous, the rewri
|
|
|
46
50
|
|
|
47
51
|
Inspection and rendering can report missing references, invalid prerequisite structure, duplicate or overlapping positions, insufficient spacing, long or crossing connectors, branch asymmetry, route conflicts, missing localisation or sprites, weak terminal branches, repeated rewards, and missing AI metadata. National-tree inspection also returns the exact `continuous_focus_position` coordinates and linked palette IDs in its compact result, so an agent can identify and move the continuous-focus area without opening the full artifact. Renders are offline review artifacts, not game screenshots.
|
|
48
52
|
|
|
53
|
+
For a named tree, pass `scenario: { "completedFocusIds": ["my_route_root"] }` to `hoi4.focus_inspect`. The linked inspection artifact records each focus's prerequisite groups, route locks, exclusive choices, and unresolved runtime fields against that completed set. The inline tree summary counts completed, blocked, unresolved, and structural candidate focuses, plus contradictory completed choices. A structural candidate still requires runtime trigger evaluation before an agent can call it playable.
|
|
54
|
+
|
|
55
|
+
Pass up to 16 `cropFocusIds` to `hoi4.focus_raster` for source-linked problem crops around specific focuses. Each crop uses the same rendered pixels as the full tree; a JSON manifest records the focus ID, crop bounds, image hash, and diagnostics. An unknown focus ID is reported explicitly.
|
|
56
|
+
|
|
57
|
+
The layout JSON reports horizontal, vertical, and Manhattan grid spans for every prerequisite connector in `layout.connectorMeasurements`.
|
|
58
|
+
|
|
49
59
|
`hoi4.focus_render` is the normal structural view and writes complete HTML, SVG, JSON, and source-map artifacts without decoding every icon or creating a PNG. `hoi4.focus_raster` adds decoded icons and the high-fidelity PNG. National trees with 200 or more focuses raster at half scale by default; pass `reviewScale` from `0.25` through `1` when a different PNG size is needed.
|
|
50
60
|
|
|
51
61
|
The public workflow is regression-tested with 1,024-focus creation, compact rewrites, deterministic repeat runs, inspection, vector rendering, and raster rendering. A separate 1,024-icon test decodes a distinct texture for every focus. Large source diffs use the same deterministic review-artifact path instead of blocking at the quadratic matrix size.
|
package/docs/gui.md
CHANGED
|
@@ -29,18 +29,51 @@ Rewrite once, then inspect and render the result.
|
|
|
29
29
|
5. Fix the existing mod-owned file with targeted patches, a complete supported source replacement, or a structured helper replacement.
|
|
30
30
|
6. Rewrite once, then inspect and render the result.
|
|
31
31
|
|
|
32
|
-
Inspection checks missing assets and localisation, invalid sizes, overlap, clipping, overflow, button-label centering, content crossing a background boundary, conflicting click regions, invisible blockers, broken parents or contexts, list-row cuts, state conflicts, trigger/effect gaps, and resolution drift. `hoi4.gui_render` returns a scripted-scenario gallery and JSON delta report alongside the generic state and resolution galleries.
|
|
32
|
+
Inspection checks missing assets and localisation, invalid sizes, overlap, clipping, overflow, button-label centering, content crossing a background boundary, conflicting click regions, invisible blockers, broken parents or contexts, list-row cuts, state conflicts, trigger/effect gaps, and resolution drift. `hoi4.gui_render` returns a scripted-scenario gallery and JSON delta report alongside the generic state and resolution galleries. The scenario matrix records shown and hidden branches, enabled and disabled branches, reasons for hidden or disabled controls, measured label boxes and text overflow, and rendered background boxes with sprite provenance. Use its branch coverage list to identify scenario conditions that still need evidence.
|
|
33
|
+
|
|
34
|
+
For source comparisons, pass `sourceBaseline: { "relativePath": "interface/example.gui", "source": "..." }` to `hoi4.gui_render`. The tool renders that mod-owned `.gui` text and the current source with the same requested scenario, and returns source-baseline and source-comparison PNGs plus a JSON report with source revisions and pixel deltas. `comparisonScenario` remains the separate comparison between two scenarios on the current source.
|
|
33
35
|
|
|
34
36
|
The renderer resolves sprites and fonts through the complete mod, dependency, and installed-game load order. Mod windows therefore render ordinary vanilla sprites, tiled vanilla panels, progress bars, masked shields, button frames, country flags, and localisation icons such as `£command_power` without copying game assets into the mod. Case differences in GFX and font identifiers do not prevent a valid asset from resolving.
|
|
35
37
|
|
|
36
38
|
Bitmap text uses the language-appropriate HOI4 font definition and its real glyph atlas. Header, serif, compact, typewriter, black, inverted, and other declared faces keep their own glyph shapes, native metrics, face colour, border colour, language override, and font-specific `textcolors` palette. The renderer reads BMFont channel roles, separates face and outline coverage, and recognizes both vanilla black-on-white masks and conventional white-on-transparent custom masks. HOI4 localisation colour runs such as `§Y` and `§R` therefore colour the glyph face while retaining its sharp antialiasing and declared border instead of flattening the glyph into a blurred monochrome mask or switching to the tool font. The default language uses the base font rather than an unrelated language override, and the renderer recognizes installed-game atlases whose filename omits a page suffix declared by the matching `.fnt` file.
|
|
37
39
|
|
|
38
|
-
`uiScale` is the in-game UI scale, not an image zoom. Use `1` for
|
|
40
|
+
`uiScale` is the in-game UI scale, not an image zoom. Use `1` for native pixel geometry at any screen resolution. Bitmap-font glyphs come directly from the real atlas without a sharpening, threshold, opacity-gain, or reconstructed-edge pass. Native-size coverage is unchanged. Enlarged coverage uses restrained Lanczos2 interpolation to avoid blocky pixel replication without running a sharpening filter. Downscaling uses nearest-neighbour sampling. Bitmap placement is snapped to the output pixel grid, and bitmap colours use alpha masks, so native, fractional-scale, local, and VPS renders remain deterministic.
|
|
39
41
|
|
|
40
42
|
Layout follows Clausewitz orientation and element-origin anchors, including `CENTER_LEFT`, `CENTER_RIGHT`, `CENTER_UP`, `CENTER_DOWN`, `origo`, `centerposition`, inherited container coordinate origins, local scale, and both `%` and `%%` relative dimensions. Text boxes use native font metrics together with `maxWidth`, `maxHeight`, alignment, wrapping, and `fixedsize` clipping. Cornered tile sprites use fixed nine-slice borders and optional center tiling; progress bars composite their filled and background textures; masked shields composite their background and mask textures.
|
|
41
43
|
|
|
44
|
+
A single-percent dimension is a proportional extent; a double-percent dimension names the far edge in the parent and subtracts the element's resolved anchor position.
|
|
45
|
+
For example, a centered root at y=423 with `height=85%%` in a 1440-pixel viewport ends at y=1224 and has height 801, rather than height 1224.
|
|
46
|
+
GUI element type matching is case-insensitive while source spelling and bytes are preserved.
|
|
47
|
+
A zero horizontal or vertical corner border produces the corresponding three-slice composition rather than stretching the entire texture.
|
|
48
|
+
Colour-only progress sprites use their declared size, `color` and `colortwo` without requiring a texture.
|
|
49
|
+
The supported source-backed progress shader selects a colour or texture on each side of its progress threshold; semi-transparent primary pixels do not blend over a second complete texture layer.
|
|
50
|
+
The scene records the selected shader source, hash and entry point.
|
|
51
|
+
Additional shader operations, including animated end-cap sampling, remain explicit unsupported findings rather than accepted pixel-parity evidence.
|
|
52
|
+
|
|
53
|
+
Container scrollbars use referenced or inline `extendedScrollbarType` templates, including their backgrounds, inline track, thumb and end-button artwork.
|
|
54
|
+
The template's slider and end-button roles are engine-owned compound controls, so scripted click-effect checks apply to authored source buttons and explicitly referenced role names rather than requiring effects for the native scrollbar internals.
|
|
55
|
+
Each attachment has independent instance IDs and source provenance even when multiple containers reuse one template.
|
|
56
|
+
The renderer measures direct content bounds and populated grid/list rows, retaining declared list bounds as a minimum, clamps scroll offsets, and applies `autohide_scrollbars`.
|
|
57
|
+
A nested container contributes its declared bounds rather than the overflowing artwork of its descendants.
|
|
58
|
+
Margins inset the scrolling clip without adding layout padding; attached scrollbars anchor to the outer window, and children wholly inside a fixed border remain stationary.
|
|
59
|
+
Clipping is resolved after scroll positioning, so off-screen content can scroll into view and nested scrolling retains the correct ancestor boundaries.
|
|
60
|
+
Visible orthogonal scrollbars reserve their anchored gutters to avoid overlapping end controls.
|
|
61
|
+
Native corner-space behavior still needs matching in-game reference qualification; synthetic non-overlap checks alone do not establish engine parity.
|
|
62
|
+
|
|
63
|
+
Supply `values["container.scrollX"]` and `values["container.scrollY"]` in native UI pixels; these keys also work inside list rows.
|
|
64
|
+
The existing `scrollOffsets` map remains a vertical-offset shorthand, including names with row-instance suffixes.
|
|
65
|
+
`minimum-value` and `maximum-value` scenarios sample content endpoints.
|
|
66
|
+
The scene element's `scroll` record exposes its viewport, measured content dimensions, applied offsets and maximum offsets in output pixels after UI scaling.
|
|
67
|
+
Missing, wrong-axis, duplicate or privately scoped scrollbar references and unclipped/background-less containers produce explicit findings rather than invented templates.
|
|
68
|
+
|
|
42
69
|
Scripted-GUI composition follows `parent_scripted_gui` chains even when the linked child window is a top-level sibling in the `.gui` file. Literal `always = yes` and `always = no` visibility and click-enabled blocks are applied automatically. A scenario's `values` object supplies concrete runtime values in one place: variable names resolve numeric text, scripted-localisation names resolve dynamic labels, a progress-bar element name sets its fill value, and element keys ending in `.image`, `.frame`, `.x`, `.y`, `.visible`, or `.enabled` update that visual property. Scripted-GUI property expressions such as `image = "[GetMeterSprite]"` or `x = meter_fill_width` consume matching scenario values automatically. Dynamic text is parsed after scenario and scripted-localisation substitution, so a runtime string containing `£command_power` or `£[GetResourceIcon]` renders the resolved inline HOI4 text icon instead of literal marker text. Dynamic lists instantiate their declared `entry_container` or `country_scope_entry_container`; each scenario row can select the country template with `countryScope`, choose an explicit `entryContainer`, provide `countryTag` and `ideology` for `GetFlag`, and supply row-scoped text, image, frame, position, visibility, and enabled values. Masked country sprites use the actual mask and overlay dimensions, so custom flag sizes are discovered from GFX assets rather than assumed by the renderer.
|
|
43
70
|
|
|
71
|
+
Native lists can also use an explicit `entryContainer` without fabricated scripted-GUI wiring.
|
|
72
|
+
Each expanded row instance includes its owning list identity, so two lists using the same template cannot overwrite one another's clipping or geometry.
|
|
73
|
+
For native country-flag controls, bind `element_name.countryTag` and `element_name.ideology` in scenario or row values.
|
|
74
|
+
Only valid bounded tag tokens enter asset discovery; an invalid tag is reported, not converted into a different country's identifier or a filesystem path.
|
|
75
|
+
An unrelated global country value does not turn every native icon into a flag.
|
|
76
|
+
|
|
44
77
|
`hoi4.gui_inspect` and `hoi4.gui_render` generate a seeded exploratory preview by default. The generator follows the complete `parent_scripted_gui` surface, discovers numeric and text tokens, fills meters within their declared ranges, and populates dynamic lists with coherent rows. Scripted-localisation text and sprite branches use the same structured condition evaluator as probability analysis, including compound conditions, inclusive comparisons, constants, declared scopes, and scripted triggers. An unknown earlier condition leaves the token unresolved instead of guessing a branch. The scenario's `conditionResults` records checked choices and missing inputs. Explicit values remain authoritative; generated inputs are not verified campaign state. Normal controls and animation frames remain stable unless variation is requested. Every explicit `relatedScenarios` entry is rendered beside the generated view.
|
|
45
78
|
|
|
46
79
|
Declare scoped facts with `scopes`, for example `"scopes": { "FROM": { "id": "target", "actor": "FRA", "state": { "support": 10 } } }`. Existing flat values such as `FROM.support` remain usable. Seeded pane and active/idle selection is exploratory layout sampling, not proof that every visibility branch is reachable.
|
|
@@ -104,10 +137,24 @@ GUI rewrites keep complete validation evidence in a linked JSON resource and ret
|
|
|
104
137
|
|
|
105
138
|
Large inspections, renders, and rewrites send periodic MCP progress heartbeats while a service stage is still running. Clients that request progress can reset their idle request timer from these notifications; there is no 180-second GUI operation limit in the server, and cancellation still follows the request signal.
|
|
106
139
|
|
|
140
|
+
## Source-backed button shading
|
|
141
|
+
|
|
142
|
+
The renderer reads referenced shader source, including legacy `.lua` references whose implementation is a `.shader` file.
|
|
143
|
+
For supported native sprite transforms and straight-alpha blending, a bounded interpreter derives affine RGBA colour operations from the selected normal, hover, pressed or disabled pixel program.
|
|
144
|
+
It supports scalar/vector arithmetic, swizzles, constant interpolation, dot products and clock-dependent constant expressions without executing shader code or arbitrary functions.
|
|
145
|
+
The scene records source identity, effect and entry point; unresolved features, nonlinear sampled-colour operations, additional texture samples and unsupported geometry or blend states remain fidelity findings.
|
|
146
|
+
Supported primary samplers retain point or linear filtering for magnification and minification and use clamped, non-mipmapped texture sampling.
|
|
147
|
+
|
|
148
|
+
Button shading is independent of explicit atlas frames and checkbox checked state.
|
|
149
|
+
Use `values["element.checked"]` for a checkbox's Boolean state, `values["element.pressed"]` for a held button, and `values["element.stateTimeSeconds"]` for elapsed time in the current button state.
|
|
150
|
+
These keys also work in list rows.
|
|
151
|
+
Without a per-element state clock, `animationTimeSeconds` supplies elapsed time; `0` represents entry into the state, not a settled hover.
|
|
152
|
+
Source or scenario `enabled = false` selects disabled appearance and removes the control from click regions.
|
|
153
|
+
|
|
107
154
|
## Scenario fidelity
|
|
108
155
|
|
|
109
156
|
The renderer does not run the game engine. Each render includes a fidelity report that separates fields it models from fields it approximates, ignores, cannot resolve, or does not support. Treat that report as part of the review.
|
|
110
157
|
|
|
111
158
|
The generated primary view replaces discovered numeric and text runtime tokens with plausible values. Set `generatedScenarios.preservePlaceholder` to `true`, or disable generation, when the same run should also show unresolved numeric values such as `[?variable|format]` as `[X]` and unresolved text-returning scripted or scoped localisation such as `[GetStatusText]`, `[FROM.GetName]`, and `[?leader_scope.GetName]` as `[dynamic_loc]`. Put `variable` or `GetStatusText` in the supplied scenario's `values` object when an exact value matters; explicit values always take precedence over generated ones and can drive the related meter or state-dependent controls. Supported HOI4 `§` localisation colour controls use the active font's palette and `§!` restores the face colour.
|
|
112
159
|
|
|
113
|
-
A rewrite stops if malformed or unsupported GUI script makes the requested change ambiguous. Frame-sheet animation, nine-slice tiling, progress composition, masking, and the primary and secondary textures referenced by vanilla GFX definitions render offline. Font selection follows mod/game load order and preview language, accepts Paradox hex and numeric color declarations, and uses the supplied HOI4 bitmap or outline font assets without requiring host font installation.
|
|
160
|
+
A rewrite stops if malformed or unsupported GUI script makes the requested change ambiguous. Frame-sheet animation, nine-slice tiling, progress composition, masking, and the primary and secondary textures referenced by vanilla GFX definitions render offline. Font selection follows mod/game load order and preview language, accepts Paradox hex and numeric color declarations, and uses the supplied HOI4 bitmap or outline font assets without requiring host font installation. Shader operations outside the supported colour subset, hardcoded controls, and dynamic values that are not supplied by a scenario remain fidelity-report entries; an unexecuted shader never hides its resolved base texture.
|
package/docs/jobs.md
CHANGED
|
@@ -57,6 +57,7 @@ Event and technology helper expansion also saves an authenticated intermediate f
|
|
|
57
57
|
A replacement validates the saved paths, adjacency cursor, graph identity, and checkpoint bytes before continuing.
|
|
58
58
|
Source changes before intermediate-frontier recovery cause a fresh analysis of the current revision; old and new graph evidence are never combined.
|
|
59
59
|
An active host allows at most two replacement attempts after proven worker death, and never retries the same unchanged frontier twice.
|
|
60
|
+
If another bounded host has already claimed an interrupted job, the initiating host follows that live replacement until it publishes a terminal outcome; it does not expose the replacement's intermediate `running` record as the synchronous result.
|
|
60
61
|
Checkpoint recovery never overrides cancellation or live/unverifiable ownership, and never replays a rewrite.
|
|
61
62
|
Event and technology scan artifacts retain revision-addressed graphs for later comparisons, and bounded responses state their requested depth, node, edge, render, helper-expansion, or candidate coverage.
|
|
62
63
|
Their opt-in `helper_expansion` mode also returns [bounded continuation pages](helper-expansion.md) through both ordinary calls and persistent jobs.
|
package/docs/map.md
CHANGED
|
@@ -10,8 +10,14 @@ The linked JSON catalog contains every active province definition, state, and st
|
|
|
10
10
|
|
|
11
11
|
Use `query` for a compact name or ID result list and `coordinates` for exact pixel or map-coordinate lookup. Use `provinceIds`, `stateIds`, and `regionIds` for focused records. Up to 32 selected provinces can also produce exact geometry as maximal `[y, startX, endXExclusive]` row runs.
|
|
12
12
|
|
|
13
|
+
Set `lookupOnly: true` for coordinate, query, and selected-ID lookups without full validation, the complete catalog artifact, overview rendering, or province geometry export. The lookup artifact contains only the requested matches and source revision. Use full inspection for allocation previews or province row runs.
|
|
14
|
+
|
|
13
15
|
`hoi4.map_render` creates the same complete catalog and navigator with a chosen base layer and overlays. Base layers include province, state, strategic region, terrain, continent, owner, controller, cores, claims, and coast. Overlays include coastlines, ports, victory points, resources, state and province buildings, supply nodes, railways, adjacencies, and building, unit, and weather positions.
|
|
14
16
|
|
|
17
|
+
Pass `tile: { x, y, width, height }` to `hoi4.map_render` for a reusable bounded PNG, JSON, and HTML view of one top-left bitmap area. The tile preserves the selected base layer and overlays, and its JSON records raster dimensions and province IDs in the area. Identical source revisions and render settings produce identical artifact hashes.
|
|
18
|
+
|
|
19
|
+
Pass `area: { provinceIds, stateIds, regionIds, padding }` to render the exact bounding area of those map entities with optional pixel padding. The result reports the resolved tile coordinates. An unknown ID or an area larger than 2,048 pixels in either direction is explicit, so a narrower selection can be requested.
|
|
20
|
+
|
|
15
21
|
## Create states
|
|
16
22
|
|
|
17
23
|
The compact `create_state` form needs selected provinces and a display name:
|
|
@@ -67,6 +73,8 @@ Use `renumber_map_entity` for `province`, `state`, or `strategic-region` IDs:
|
|
|
67
73
|
|
|
68
74
|
If the destination exists, the default behavior swaps the two IDs. Province swaps update definitions, state and region membership, victory points, province buildings, adjacency, supply nodes, railways, unit positions, building sea references, and standard province and victory-point localisation keys. State swaps update state records, standard state localisation keys, and building-position state references. Strategic-region swaps update region records, standard localisation keys, and weather-position references. Province and strategic-region operations preserve their required contiguous ID sets.
|
|
69
75
|
|
|
76
|
+
Renumbering also inspects active event, focus, decision, scripted helper, on-action, idea, character, and country-history source for typed scalar map fields. It rewrites only parsed values in `province`, `province_id`, `start_province`, `target_province`, `state`, `state_id`, `start_state`, `target_state`, `owns_state`, `controls_state`, `transfer_state`, `transfer_state_to`, `strategic_region`, and `strategic_region_id`. Other numbers, comments, and identifiers remain untouched. A matching reference in a read-only game or dependency source blocks the operation with its path and field. A script that cannot be safely parsed also blocks the operation.
|
|
77
|
+
|
|
70
78
|
Set `collision` to `reject` to require an unused destination. Set `renameLocalisation` to `false` only when custom localisation-key handling is intentional.
|
|
71
79
|
|
|
72
80
|
## Other map changes
|
package/docs/mechanics.md
CHANGED
|
@@ -41,7 +41,8 @@ An `advance_days` step only expires modeled timed flags; it never runs on action
|
|
|
41
41
|
|
|
42
42
|
The linked trace records applied, skipped, and unresolved source operations with before and after values.
|
|
43
43
|
The `single_payment` assertion checks the named balance's final change and the number and size of its traced deductions, so a compensating grant cannot hide a second charge.
|
|
44
|
-
Supported operations include documented variable arithmetic, flags, event targets, arrays, conditionals, finite declared scope iteration, scripted helpers, safe meta substitutions, and selected balance effects.
|
|
44
|
+
Supported operations include documented persistent and unscoped temporary-variable arithmetic, exact `has_variable` presence checks, flags, event targets, arrays, conditionals, finite declared scope iteration, scripted helpers, safe meta substitutions, and selected balance effects.
|
|
45
|
+
Active scripted-effect documents are parsed once per source file and their helper blocks share that bounded source model.
|
|
45
46
|
Decision engine political-power costs are applied once before `complete_effect`; custom cost triggers check affordability and their payment remains in `complete_effect`.
|
|
46
47
|
Unresolved eligibility, unsupported effects, dynamic names that cannot be resolved, missing finite scope catalogs, and bounded-work limits remain unresolved rather than guessed.
|
|
47
48
|
An effect with an unknown write set conservatively makes state assertions unresolved.
|
package/docs/probability.md
CHANGED
|
@@ -16,7 +16,9 @@ Use them for event timing and option weights, decision and mission scores, focus
|
|
|
16
16
|
|
|
17
17
|
All seven tools are read-only. Proposed source is parsed in memory and never written. When an installed game root is configured, evaluation fails closed unless `launcher-settings.json` identifies the supported HOI4 build and checksum. Results otherwise state that they target the adapter version without claiming local-game verification.
|
|
18
18
|
|
|
19
|
-
Pass a source without an adapter when the surface type is not yet known. Inspection returns every compatible adapter, candidate counts, and example candidate IDs. If a requested adapter, identifier, or candidate pool does not match the source, inspection returns the same discovery result with an explanation and suggested adapter instead of `PROBABILITY_SURFACE_EMPTY`.
|
|
19
|
+
Pass a source without an adapter when the surface type is not yet known. Inspection returns every compatible adapter, candidate counts, and example candidate IDs; its discovery artifact includes every candidate ID and source location. If a requested adapter, identifier, or candidate pool does not match the source, inspection returns the same discovery result with an explanation and suggested adapter instead of `PROBABILITY_SURFACE_EMPTY`.
|
|
20
|
+
|
|
21
|
+
For a selected source, `hoi4.probability_inspect` includes missing source-condition fields in compact `requiredInputPaths`, adapter-wide completeness flags in `adapterRequiredInputPaths`, and the full candidate catalog, source provenance, and per-candidate requirements in its JSON resource. Unsupported source constructs remain unresolved evidence rather than being presented as scenario inputs. Evaluation reports eligible, excluded, and unresolved candidate counts inline; the resource preserves each candidate's exact eligibility and unresolved reasons for every scenario.
|
|
20
22
|
|
|
21
23
|
Evaluation, sweep, simulation, and source comparison also accept an omitted adapter. When a source selector identifies one unambiguous weighted surface, the analyzer selects that source-backed adapter automatically. If a caller supplies a mismatched adapter or `custom_weighted_pool` for a source-backed request, the analyzer corrects it when the source has exactly one match. `customPoolManifest`, `beforeManifest`, and `afterManifest` use the custom-pool adapter automatically; `PROBABILITY_SURFACE_EMPTY` is reserved for sources with no weighted surface, while genuinely multi-adapter sources ask the caller to narrow by identifier or line.
|
|
22
24
|
|
package/docs/technology.md
CHANGED
|
@@ -47,15 +47,22 @@ Impact mode takes a subject with `kind` set to `technology`, `category`, `folder
|
|
|
47
47
|
Findings are separated into confirmed errors, probable defects, design warnings, and unresolved analysis. Roots, grant-only technologies, early dates, zero AI weights, repeated effects, and routing nodes are evaluated in context instead of being treated as automatic defects. The viewer keeps its authoritative graph in memory while the linked scan artifact remains a bounded summary; use its revision with the focused inspect modes or a render resource to inspect the relevant records.
|
|
48
48
|
|
|
49
49
|
Ordinary technology inspection uses the materialized graph and its explicit helper-depth and projection limits.
|
|
50
|
+
Workspaces with a game root, more than 1,000 scanned files, or more than 4,096 helper calls retain direct references and expose deferred helper expansion as incomplete coverage; `helper_expansion` pages provide bounded paths for those workspaces.
|
|
50
51
|
A truncated helper expansion reports incomplete coverage.
|
|
51
52
|
For a large helper closure, [bounded helper expansion](helper-expansion.md) uses the structural call inventory and streams source-linked paths without first materializing the full projection.
|
|
52
53
|
It preserves separate conditional call sites and identifies source, cycle, and depth boundaries without simulating trigger outcomes.
|
|
53
54
|
|
|
54
55
|
## Render
|
|
55
56
|
|
|
56
|
-
`hoi4.tech_render` returns authoritative JSON with deterministic SVG and PNG resources; set `includeHtml` when a bundled static report is useful. Folder renders read the actual `techtree_<folder>_item` and `techtree_<folder>_small_item` geometry, choose the small item for technologies that do not unlock equipment or explicitly force it, and
|
|
57
|
+
`hoi4.tech_render` returns authoritative JSON with deterministic SVG and PNG resources; set `includeHtml` when a bundled static report is useful. Folder renders read the actual `techtree_<folder>_item` and `techtree_<folder>_small_item` geometry, choose the small item for technologies that do not unlock equipment or explicitly force it, and place the source GUI's year labels. Available views are `summary`, `folder`, `dependencies`, `technology`, `doctrine`, `exclusive`, `memberships`, `bonuses`, `grants`, `unlocks`, `metadata`, `assets`, and `unresolved`.
|
|
57
58
|
|
|
58
|
-
Folder renders use
|
|
59
|
+
Folder renders use folder assignments, gridbox geometry, technology coordinates, and item dimensions found in source. File-local `@` constants in technology coordinates are resolved before placement. A technology with multiple prerequisite roots can use its own declared gridbox. The item template supplies its unavailable, available, or researched skin, icon center, name position, and design-team icon position when the corresponding sprites resolve. `sub_technology_slot_<index>` geometry and its source `picture` sprite position each declared subtechnology icon; the technology-id icon is a fallback when the slot does not declare art, and a missing slot stays visible as unresolved placement metadata. The folder's tiled panel sprite fills the canvas, and source `*_techtree_bg` sprites render behind technology nodes at their declared GUI positions; when the GUI omits a sprite size, the decoded dimensions supply it. Year labels retain their source font, box dimensions, alignment, orientation, and colour declarations in the graph. Sprite declarations and texture files resolve through the active load order across the mod, configured dependencies, and the detected or configured game installation. The authoritative JSON and SVG expose `layoutSize`, rendered sprite coverage, and unresolved sprite names without embedding image data in JSON. `sourceAccurate` describes source pixel placement only; it is not a percentage of visual agreement with a game screenshot. The game may change the visibility of design-team icons and status corners at runtime. Dependency and other semantic views are labelled as generated analysis layouts.
|
|
60
|
+
|
|
61
|
+
A `small` 72-pixel item can still display its source `GFX_*_medium` artwork at full card size; the layout name does not select smaller icon art.
|
|
62
|
+
|
|
63
|
+
Render validation is scoped to the requested view: folder source placement, omitted nodes, and unresolved sprites determine whether that render passes. Graph-wide semantic diagnostics remain available through technology inspection and do not falsely invalidate an otherwise complete source-backed folder render. Deferred whole-workspace helper projection remains an explicit analysis boundary and does not by itself invalidate a complete direct-source render.
|
|
64
|
+
|
|
65
|
+
Subtechnology cards use the sprite declared by their source GUI slot before any technology-id fallback. Slot-declared textures are included in the bounded asset scan even when no ordinary technology icon references the same file.
|
|
59
66
|
|
|
60
67
|
Example folder render:
|
|
61
68
|
|
|
@@ -68,6 +75,8 @@ Example folder render:
|
|
|
68
75
|
}
|
|
69
76
|
```
|
|
70
77
|
|
|
78
|
+
Pass `scenario: { "year": 1936, "researchedTechnologyIds": ["infantry_weapons"] }` to `hoi4.tech_render` to display known researched technologies, exclusive conflicts, and missing source prerequisites. Folder renders select the corresponding item skin when its sprite is available. The JSON lists the missing prerequisite IDs and years ahead of the scenario date for each displayed technology. A `structural_candidate` status means the supplied research set has no known structural blocker; country conditions and other dynamic requirements remain unresolved. Unknown researched IDs are rejected.
|
|
79
|
+
|
|
71
80
|
Large dependency requests return a bounded overview plus focused folder resources and a coverage manifest. The graph itself is not truncated: use the manifest and linked `hoi4-agent://` resources to inspect the relevant folder or branch without loading the entire system into the prompt.
|
|
72
81
|
|
|
73
82
|
## Compare
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hoi4-agent-tools",
|
|
3
3
|
"mcpName": "io.github.klimPaskov/hoi4-agent-tools",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.4.1",
|
|
5
5
|
"description": "Analyze HOI4 events, decisions, impact, tech, AI, and MTTH; build focus trees, GUIs, and maps.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "Apache-2.0",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/configuration.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/continuous-focus-palette.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/custom-weighted-pool.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/decision-inspect-input.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/event-feature-manifest.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/focus-plan.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/focus-planning-sidecar.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/gui-animation-source.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/gui-helper.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/gui-scenario.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/helper-expansion-request.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/helper-expansion-summary.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.
|
|
2
|
+
"$id": "https://github.com/klimPaskov/hoi4-agent-tools/blob/v3.4.1/schemas/impact-inspect-input.schema.json",
|
|
3
3
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
4
|
"type": "object",
|
|
5
5
|
"properties": {
|