hoi4-agent-tools 3.4.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/README.md +26 -18
- package/dist/hoi4_agent_tools/core/condition-evaluator.js +48 -2
- package/dist/hoi4_agent_tools/core/condition-schema.js +18 -0
- package/dist/hoi4_agent_tools/core/configuration.js +10 -0
- package/dist/hoi4_agent_tools/core/domain-scan-patterns.js +9 -1
- package/dist/hoi4_agent_tools/core/impact-semantic.js +11 -4
- 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/core/scanner.js +1 -1
- package/dist/hoi4_agent_tools/core/scenario-model.js +6 -0
- package/dist/hoi4_agent_tools/core/scenario-state.js +3 -0
- package/dist/hoi4_agent_tools/core/workspace.js +13 -1
- package/dist/hoi4_agent_tools/event/graph.js +8 -1
- package/dist/hoi4_agent_tools/event/operations.js +16 -10
- package/dist/hoi4_agent_tools/event/service.js +1 -0
- package/dist/hoi4_agent_tools/event/source-analysis.js +1 -1
- package/dist/hoi4_agent_tools/focus/render.js +10 -2
- package/dist/hoi4_agent_tools/gui/layout.js +58 -11
- package/dist/hoi4_agent_tools/gui/scenario.js +3 -1
- package/dist/hoi4_agent_tools/gui/source-graph.js +4 -1
- package/dist/hoi4_agent_tools/gui/studio.js +32 -10
- package/dist/hoi4_agent_tools/gui/validators.js +3 -0
- package/dist/hoi4_agent_tools/map/model.js +121 -12
- package/dist/hoi4_agent_tools/map/operations.js +80 -7
- package/dist/hoi4_agent_tools/map/service.js +1 -0
- package/dist/hoi4_agent_tools/map/validation.js +38 -1
- package/dist/hoi4_agent_tools/mcp/server/create-modern-operations.js +18 -1
- package/dist/hoi4_agent_tools/mcp/server/create.js +2 -0
- package/dist/hoi4_agent_tools/mcp/server/instructions.js +1 -1
- package/dist/hoi4_agent_tools/mcp/tools/reference.js +60 -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/service.js +2 -2
- package/dist/hoi4_agent_tools/probability/source-analysis.js +16 -0
- package/dist/hoi4_agent_tools/probability/value-evaluator.js +2 -1
- package/dist/hoi4_agent_tools/reference/lines.js +52 -0
- package/dist/hoi4_agent_tools/reference/operations.js +41 -0
- package/dist/hoi4_agent_tools/reference/service.js +427 -0
- package/dist/hoi4_agent_tools/reference/source-lookup.js +90 -0
- package/dist/hoi4_agent_tools/schemas/analysis.js +2 -0
- package/dist/hoi4_agent_tools/schemas/gui-requests.js +1 -1
- package/dist/hoi4_agent_tools/schemas/probability-requests.js +1 -1
- package/dist/hoi4_agent_tools/schemas/probability.js +26 -2
- package/dist/hoi4_agent_tools/schemas/reference.js +160 -0
- package/dist/hoi4_agent_tools/technology/operations.js +29 -1
- package/dist/hoi4_agent_tools/technology/render.js +11 -5
- package/dist/hoi4_agent_tools/technology/service.js +1 -0
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/README.md +3 -0
- package/docs/events.md +1 -1
- package/docs/gui.md +7 -0
- package/docs/jobs.md +1 -0
- package/docs/map.md +8 -0
- package/docs/mechanics.md +2 -1
- package/docs/probability.md +7 -0
- package/docs/reference.md +32 -0
- package/docs/research/reference-benchmark.md +28 -0
- package/docs/setup.md +6 -0
- package/docs/technology.md +5 -1
- package/examples/clients/codex-config.toml +2 -0
- package/package.json +4 -2
- package/schemas/configuration.schema.json +72 -58
- 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 +43 -26
- 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 +102 -75
- 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 +49 -32
- 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 +160 -131
- package/schemas/probability-evaluate-input.schema.json +161 -132
- package/schemas/probability-inspect-input.schema.json +20 -11
- package/schemas/probability-render-input.schema.json +1 -1
- package/schemas/probability-scenario-set.schema.json +110 -90
- package/schemas/probability-sequence-input.schema.json +154 -134
- package/schemas/probability-simulate-input.schema.json +161 -132
- package/schemas/probability-sweep-input.schema.json +163 -134
- package/schemas/reference-context-input.schema.json +42 -0
- package/schemas/reference-read-input.schema.json +41 -0
- package/schemas/reference-search-input.schema.json +45 -0
- package/schemas/scenario-suite.schema.json +92 -75
- package/schemas/scenario-test-input.schema.json +103 -86
- package/schemas/shared-scenario.schema.json +39 -22
- package/schemas/source-lookup-input.schema.json +61 -0
- package/server.json +2 -2
- package/docs/images/readme/chaos-meter-ingame.png +0 -0
- package/docs/images/readme/chaos-meter-mcp.png +0 -0
- package/docs/images/readme/communist-insurgency-ingame.png +0 -0
- package/docs/images/readme/communist-insurgency-mcp.png +0 -0
- package/docs/images/readme/event-chain-mcp.png +0 -0
- package/docs/images/readme/focus-tree-mcp.png +0 -0
- package/docs/images/readme/map-mcp.png +0 -0
- package/docs/images/readme/probability-mcp.png +0 -0
- package/docs/images/readme/settings-advanced-mcp.png +0 -0
- package/docs/images/readme/settings-events-mcp.png +0 -0
- package/docs/images/readme/technology-tree-mcp.png +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import { workspaceIdSchema } from './common.js';
|
|
3
|
+
export const referenceSourceSchema = z.enum(['game_doc', 'wiki', 'script_doc']);
|
|
4
|
+
export const referenceSurfaceSchema = z.enum([
|
|
5
|
+
'general',
|
|
6
|
+
'event',
|
|
7
|
+
'decision',
|
|
8
|
+
'idea',
|
|
9
|
+
'focus',
|
|
10
|
+
'technology',
|
|
11
|
+
'gui',
|
|
12
|
+
'map',
|
|
13
|
+
'localisation',
|
|
14
|
+
'ai',
|
|
15
|
+
]);
|
|
16
|
+
export const referenceSearchRequestSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
workspaceId: workspaceIdSchema,
|
|
19
|
+
query: z.string().trim().min(2).max(160),
|
|
20
|
+
sources: z.array(referenceSourceSchema).min(1).max(3).optional(),
|
|
21
|
+
limit: z.number().int().min(1).max(12).default(6),
|
|
22
|
+
})
|
|
23
|
+
.strict();
|
|
24
|
+
export const referenceReadRequestSchema = z
|
|
25
|
+
.object({
|
|
26
|
+
workspaceId: workspaceIdSchema,
|
|
27
|
+
id: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
28
|
+
revision: z.string().regex(/^[a-f0-9]{64}$/u),
|
|
29
|
+
startLine: z.number().int().min(1).optional(),
|
|
30
|
+
startColumn: z.number().int().min(1).optional(),
|
|
31
|
+
maxLines: z.number().int().min(1).max(80).default(40),
|
|
32
|
+
})
|
|
33
|
+
.strict();
|
|
34
|
+
export const referenceContextRequestSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
workspaceId: workspaceIdSchema,
|
|
37
|
+
surface: referenceSurfaceSchema,
|
|
38
|
+
question: z.string().trim().min(2).max(160).optional(),
|
|
39
|
+
limit: z.number().int().min(1).max(24).default(16),
|
|
40
|
+
})
|
|
41
|
+
.strict();
|
|
42
|
+
export const sourceLookupRequestSchema = z
|
|
43
|
+
.object({
|
|
44
|
+
workspaceId: workspaceIdSchema,
|
|
45
|
+
symbol: z.string().trim().min(1).max(256),
|
|
46
|
+
kind: z
|
|
47
|
+
.string()
|
|
48
|
+
.regex(/^[a-z][a-z0-9_]{0,63}$/u)
|
|
49
|
+
.optional(),
|
|
50
|
+
includeReferences: z.boolean().default(true),
|
|
51
|
+
maxDefinitions: z.number().int().min(1).max(3).default(3),
|
|
52
|
+
maxReferences: z.number().int().min(0).max(20).default(10),
|
|
53
|
+
maxLines: z.number().int().min(1).max(80).default(30),
|
|
54
|
+
fromLine: z.number().int().min(1).optional(),
|
|
55
|
+
fromColumn: z.number().int().min(1).optional(),
|
|
56
|
+
expectedRevision: z
|
|
57
|
+
.string()
|
|
58
|
+
.regex(/^[a-f0-9]{64}$/u)
|
|
59
|
+
.optional(),
|
|
60
|
+
})
|
|
61
|
+
.strict();
|
|
62
|
+
const hash = z.string().regex(/^[a-f0-9]{64}$/u);
|
|
63
|
+
const line = z.number().int().min(1);
|
|
64
|
+
const count = z.number().int().min(0);
|
|
65
|
+
const coverage = z
|
|
66
|
+
.object({
|
|
67
|
+
files: count,
|
|
68
|
+
sections: count,
|
|
69
|
+
unavailable: z.boolean(),
|
|
70
|
+
})
|
|
71
|
+
.strict();
|
|
72
|
+
const coverageBySource = z
|
|
73
|
+
.object({ game_doc: coverage, wiki: coverage, script_doc: coverage })
|
|
74
|
+
.strict();
|
|
75
|
+
export const referenceSectionSchema = z
|
|
76
|
+
.object({
|
|
77
|
+
id: hash,
|
|
78
|
+
revision: hash,
|
|
79
|
+
source: referenceSourceSchema,
|
|
80
|
+
title: z.string().max(512),
|
|
81
|
+
heading: z.string().max(512),
|
|
82
|
+
path: z.string().max(4096),
|
|
83
|
+
startLine: line,
|
|
84
|
+
endLine: line,
|
|
85
|
+
excerpt: z.string().max(300),
|
|
86
|
+
authority: z.string().max(80),
|
|
87
|
+
})
|
|
88
|
+
.strict();
|
|
89
|
+
export const referenceSearchDataSchema = z
|
|
90
|
+
.object({
|
|
91
|
+
results: z.array(referenceSectionSchema.extend({ score: z.number() })).max(12),
|
|
92
|
+
total: count,
|
|
93
|
+
coverage: coverageBySource,
|
|
94
|
+
skipped: count,
|
|
95
|
+
})
|
|
96
|
+
.strict();
|
|
97
|
+
export const referenceReadDataSchema = referenceSectionSchema
|
|
98
|
+
.extend({
|
|
99
|
+
sectionStartLine: line,
|
|
100
|
+
sectionEndLine: line,
|
|
101
|
+
startColumn: line,
|
|
102
|
+
text: z.string().max(8000),
|
|
103
|
+
nextLine: line.nullable(),
|
|
104
|
+
nextColumn: line.nullable(),
|
|
105
|
+
})
|
|
106
|
+
.strict();
|
|
107
|
+
export const referenceContextDataSchema = z
|
|
108
|
+
.object({
|
|
109
|
+
surface: referenceSurfaceSchema,
|
|
110
|
+
sections: z.array(referenceSectionSchema).max(24),
|
|
111
|
+
omitted: count,
|
|
112
|
+
missing: z.array(z.string().max(256)).max(32),
|
|
113
|
+
coverage: coverageBySource,
|
|
114
|
+
skipped: count,
|
|
115
|
+
})
|
|
116
|
+
.strict();
|
|
117
|
+
export const sourceLookupDataSchema = z
|
|
118
|
+
.object({
|
|
119
|
+
revision: hash,
|
|
120
|
+
complete: z.boolean(),
|
|
121
|
+
skippedSourceCount: count,
|
|
122
|
+
definitionCount: count,
|
|
123
|
+
referencesIncluded: z.boolean(),
|
|
124
|
+
referenceCount: count,
|
|
125
|
+
referencesTruncated: z.boolean(),
|
|
126
|
+
referencesComplete: z.boolean(),
|
|
127
|
+
unresolvedReferenceCount: count,
|
|
128
|
+
definitions: z
|
|
129
|
+
.array(z
|
|
130
|
+
.object({
|
|
131
|
+
kind: z.string().max(64),
|
|
132
|
+
id: z.string().max(1024),
|
|
133
|
+
path: z.string().max(4096),
|
|
134
|
+
rootKind: z.string().max(32),
|
|
135
|
+
loadOrder: count,
|
|
136
|
+
overridden: z.boolean(),
|
|
137
|
+
sourceShadowed: z.boolean(),
|
|
138
|
+
startLine: line.nullable(),
|
|
139
|
+
endLine: line.nullable(),
|
|
140
|
+
fromLine: line.nullable(),
|
|
141
|
+
toLine: line.nullable(),
|
|
142
|
+
nextLine: line.nullable(),
|
|
143
|
+
nextColumn: line.nullable(),
|
|
144
|
+
text: z.string().max(2000),
|
|
145
|
+
})
|
|
146
|
+
.strict())
|
|
147
|
+
.max(3),
|
|
148
|
+
references: z
|
|
149
|
+
.array(z
|
|
150
|
+
.object({
|
|
151
|
+
kind: z.string().max(64),
|
|
152
|
+
from: z.string().max(1024),
|
|
153
|
+
toKind: z.string().max(64),
|
|
154
|
+
path: z.string().max(4096),
|
|
155
|
+
line: line.nullable(),
|
|
156
|
+
})
|
|
157
|
+
.strict())
|
|
158
|
+
.max(20),
|
|
159
|
+
})
|
|
160
|
+
.strict();
|
|
@@ -58,6 +58,34 @@ export function technologyValidation(graph) {
|
|
|
58
58
|
],
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
|
+
export function technologyRenderValidation(graph, render) {
|
|
62
|
+
const omitted = render.omittedNodeCount;
|
|
63
|
+
const unresolvedSprites = render.unresolvedIconSprites.length;
|
|
64
|
+
const sourcePlacementPassed = render.view !== 'folder' || render.sourceAccurate;
|
|
65
|
+
const analysisBoundaryPassed = graph.complete || (graph.analysisMode === 'focused' && graph.skippedSourceCount === 0);
|
|
66
|
+
const renderPassed = sourcePlacementPassed && omitted === 0 && unresolvedSprites === 0;
|
|
67
|
+
return {
|
|
68
|
+
passed: renderPassed && analysisBoundaryPassed,
|
|
69
|
+
checks: [
|
|
70
|
+
{
|
|
71
|
+
id: 'technology-render',
|
|
72
|
+
passed: renderPassed,
|
|
73
|
+
message: renderPassed
|
|
74
|
+
? 'The requested technology view rendered with complete node and sprite coverage'
|
|
75
|
+
: `${sourcePlacementPassed ? 0 : 1} source-placement failure(s), ${omitted} omitted node(s), and ${unresolvedSprites} unresolved sprite(s) affect the requested technology view`,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'technology-analysis-boundary',
|
|
79
|
+
passed: analysisBoundaryPassed,
|
|
80
|
+
message: graph.complete
|
|
81
|
+
? 'The supporting technology analysis is complete'
|
|
82
|
+
: analysisBoundaryPassed
|
|
83
|
+
? 'Whole-workspace helper projections were deferred; the requested render retains direct source evidence'
|
|
84
|
+
: `${graph.skippedSourceCount} technology source(s) were skipped; linked evidence records the partial analysis boundary`,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
};
|
|
88
|
+
}
|
|
61
89
|
export async function inspectTechnologies(viewer, input) {
|
|
62
90
|
const output = await viewer.analyze(input);
|
|
63
91
|
const result = emptyServiceResult(input.workspaceId, {
|
|
@@ -93,7 +121,7 @@ export async function renderTechnologies(viewer, input) {
|
|
|
93
121
|
result.code = output.graph.complete ? 'TECH_RENDERED' : 'TECH_RENDERED_PARTIAL';
|
|
94
122
|
setInlineFilesScanned(result, output.graph.filesScanned);
|
|
95
123
|
result.artifacts = output.artifacts.map(publicArtifactLink);
|
|
96
|
-
result.validation =
|
|
124
|
+
result.validation = technologyRenderValidation(output.graph, output.render);
|
|
97
125
|
return result;
|
|
98
126
|
}
|
|
99
127
|
export async function compareTechnologies(viewer, input) {
|
|
@@ -72,9 +72,10 @@ function technologyNode(graph, technologyId, placement) {
|
|
|
72
72
|
subTechnologies: technology.subTechnologies.map((id, index) => {
|
|
73
73
|
const definition = graph.technologies.find((candidate) => candidate.id === id);
|
|
74
74
|
const slot = itemLayout?.subTechnologySlots?.find((candidate) => candidate.index === index);
|
|
75
|
+
const iconSprite = slot?.sprite ?? definition?.icon.sprite;
|
|
75
76
|
return {
|
|
76
77
|
id,
|
|
77
|
-
...(
|
|
78
|
+
...(iconSprite === undefined ? {} : { iconSprite }),
|
|
78
79
|
placementStatus: slot === undefined ? 'missing_source_slot' : 'source_slot',
|
|
79
80
|
...(slot === undefined ? {} : { slot }),
|
|
80
81
|
};
|
|
@@ -743,10 +744,15 @@ function svgFor(titleText, positioned, edges, sourceAccurate, iconDataUris, spri
|
|
|
743
744
|
? undefined
|
|
744
745
|
: (iconDataUris[preferredSprite] ?? iconDataUris[node.itemBackgroundSprite]);
|
|
745
746
|
const icon = node.iconSprite === undefined ? undefined : iconDataUris[node.iconSprite];
|
|
746
|
-
const
|
|
747
|
+
const nativeIconSize = node.layoutSize === 'large' && node.iconSprite !== undefined
|
|
748
|
+
? spriteDimensions[node.iconSprite]
|
|
749
|
+
: undefined;
|
|
750
|
+
const fallbackIconSize = node.layoutSize === 'small' ? 62 : 70;
|
|
751
|
+
const iconWidth = nativeIconSize?.width ?? fallbackIconSize;
|
|
752
|
+
const iconHeight = nativeIconSize?.height ?? fallbackIconSize;
|
|
747
753
|
const center = node.iconPosition ?? { x: node.width / 2, y: node.height / 2, centered: true };
|
|
748
|
-
const iconX = node.x + center.x - (center.centered ?
|
|
749
|
-
const iconY = node.y + center.y - (center.centered ?
|
|
754
|
+
const iconX = node.x + center.x - (center.centered ? iconWidth / 2 : 0);
|
|
755
|
+
const iconY = node.y + center.y - (center.centered ? iconHeight / 2 : 0);
|
|
750
756
|
const name = node.namePosition ?? { x: 3, y: -3, maxWidth: node.width - 6 };
|
|
751
757
|
const labelWidth = Math.min(name.maxWidth ?? node.width - 6, node.width - name.x - 3);
|
|
752
758
|
let fittedLabel = node.label;
|
|
@@ -766,7 +772,7 @@ function svgFor(titleText, positioned, edges, sourceAccurate, iconDataUris, spri
|
|
|
766
772
|
: `<image data-item-background-sprite="${escapeXml(preferredSprite)}" href="${escapeXml(itemSkin)}" x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" preserveAspectRatio="none"/>`;
|
|
767
773
|
const iconSvg = icon === undefined
|
|
768
774
|
? ''
|
|
769
|
-
: `<image href="${escapeXml(icon)}" x="${iconX}" y="${iconY}" width="${
|
|
775
|
+
: `<image data-tech-icon-sprite="${escapeXml(node.iconSprite)}" href="${escapeXml(icon)}" x="${iconX}" y="${iconY}" width="${iconWidth}" height="${iconHeight}" preserveAspectRatio="xMidYMid meet"/>`;
|
|
770
776
|
const designTeamSprite = node.designTeamIcon?.sprite;
|
|
771
777
|
const designTeamImage = designTeamSprite === undefined ? undefined : iconDataUris[designTeamSprite];
|
|
772
778
|
const designTeamSize = designTeamSprite === undefined ? undefined : spriteDimensions[designTeamSprite];
|
|
@@ -487,6 +487,7 @@ export class TechnologyTreeViewer {
|
|
|
487
487
|
});
|
|
488
488
|
const guiSprites = [
|
|
489
489
|
...preliminary.itemLayouts.flatMap(({ designTeamIcon }) => designTeamIcon === undefined ? [] : [designTeamIcon.sprite]),
|
|
490
|
+
...preliminary.itemLayouts.flatMap(({ subTechnologySlots }) => (subTechnologySlots ?? []).flatMap(({ sprite }) => (sprite === undefined ? [] : [sprite]))),
|
|
490
491
|
...preliminary.itemLayouts.flatMap(({ backgroundSprite }) => backgroundSprite === undefined
|
|
491
492
|
? []
|
|
492
493
|
: [
|
|
@@ -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.5.0';
|
|
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/README.md
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
# Documentation
|
|
2
2
|
|
|
3
3
|
- [Setup](setup.md): install the server, configure HOI4 paths, and connect an MCP client.
|
|
4
|
+
- [Local references](reference.md): retrieve cited offline wiki and installed documentation sections and exact source definitions.
|
|
4
5
|
- [Event chains](events.md): inspect, trace, lint, render, and compare event chains.
|
|
5
6
|
- [Cross-system impact and decisions](analysis.md): trace source consumers and evaluate decision or mission scenarios.
|
|
6
7
|
- [Mechanic tests and scenario suites](mechanics.md): interpret declared effect steps, check package links, and run resumable cases.
|
|
7
8
|
- [Technology trees](technology.md): inspect, trace, lint, render, and compare technologies and doctrines.
|
|
9
|
+
- [Visual examples and comparison limits](visual-comparisons.md): real game captures beside MCP renders, with measured findings and limits.
|
|
10
|
+
- [Interface examples and comparison limits](https://github.com/klimPaskov/hoi4-agent-tools/blob/main/docs/gui-comparisons.md): native-size vanilla and Chaos Redux windows, populated templates, scenarios, and provenance.
|
|
8
11
|
- [Bounded helper expansion](helper-expansion.md): resume source-linked event and technology helper paths with exact coverage boundaries.
|
|
9
12
|
- [Persistent jobs](jobs.md): run, inspect, cancel, and recover long operations.
|
|
10
13
|
- [AI and MTTH analysis](probability.md): evaluate weighted logic, timing, uncertainty, sequences, and patches.
|
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
|
|
package/docs/gui.md
CHANGED
|
@@ -33,6 +33,12 @@ Inspection checks missing assets and localisation, invalid sizes, overlap, clipp
|
|
|
33
33
|
|
|
34
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.
|
|
35
35
|
|
|
36
|
+
GUI scenarios accept `date` in `YYYY.M.D` or `YYYY-MM-DD` form and `controls` as a map from state IDs to controller tags, for example `{"date":"1944.10.1","country":{"tag":"GER"},"controls":{"87":"GER","88":"GER"}}`. Date and controller facts reach the shared condition evaluator, including `controls_state` and `is_controlled_by` in declared state scopes. An absent state-controller fact remains unresolved. These fields are explicit scenario assumptions; they do not prove a live campaign's state or engine execution.
|
|
37
|
+
|
|
38
|
+
A requested root window uses its source `show_position` when present. This is the settled visible endpoint, so native windows whose ordinary `position` is offscreen can be inspected as open panels. The static render does not claim to reproduce the engine's transition animation.
|
|
39
|
+
|
|
40
|
+
Explicit native list rows resolve their entry template in the owning GUI file before considering a shared template, so reused names in unrelated vanilla windows do not select the wrong layout. One-column native grids with a one-pixel slot height stack entries by their rendered heights. Scenario `.text` overrides can name installed localisation keys even when those keys do not appear literally in the selected GUI source. For dimensions normally supplied by engine code, provide `values["element.width"]` or `values["element.height"]` in native UI pixels and record the source or declared-state basis for those values.
|
|
41
|
+
|
|
36
42
|
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.
|
|
37
43
|
|
|
38
44
|
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.
|
|
@@ -51,6 +57,7 @@ The scene records the selected shader source, hash and entry point.
|
|
|
51
57
|
Additional shader operations, including animated end-cap sampling, remain explicit unsupported findings rather than accepted pixel-parity evidence.
|
|
52
58
|
|
|
53
59
|
Container scrollbars use referenced or inline `extendedScrollbarType` templates, including their backgrounds, inline track, thumb and end-button artwork.
|
|
60
|
+
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.
|
|
54
61
|
Each attachment has independent instance IDs and source provenance even when multiple containers reuse one template.
|
|
55
62
|
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`.
|
|
56
63
|
A nested container contributes its declared bounds rather than the overflowing artwork of its descendants.
|
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
|
@@ -82,3 +82,11 @@ Set `collision` to `reject` to require an unused destination. Set `renameLocalis
|
|
|
82
82
|
`hoi4.map_rewrite` also moves and merges states, moves provinces between strategic regions, merges or removes provinces, changes province definitions and types, adds or removes normal and special adjacency, and updates supply, railway, building, unit, weather, and entity-locator records. Send one ordered operation list when several connected changes belong together.
|
|
83
83
|
|
|
84
84
|
Every rewrite returns changed-area, changed-ID, semantic, affected-file, diagnostic, and rendered comparison evidence. A failed operation does not leave a partial multi-file result.
|
|
85
|
+
|
|
86
|
+
## Dated state history
|
|
87
|
+
|
|
88
|
+
Map inspection reads active `common/bookmarks/*.txt` sources and uses the earliest valid bookmark date as the state-history basis. Supported owner, controller, core, claim, victory-point, and building commands in earlier dated blocks contribute to the inspected state and its rendered owner or controller layer. If no bookmark date is available, dated state fields retain their direct values and inspection reports `MAP_STATE_DATED_HISTORY_UNRESOLVED`.
|
|
89
|
+
|
|
90
|
+
Map rewrites preserve dated source blocks byte-for-byte. An update to a field overridden by an applicable dated block returns `MAP_DATED_STATE_HISTORY_CONFLICT`. Province membership changes also block when a dated victory point or building entry would need remapping. Splitting or merging a source state with dated history returns `MAP_DATED_STATE_DISTRIBUTION_UNSUPPORTED`, because copying or deleting that history would change its later meaning. A direct edit with only later dated changes may proceed; those later commands remain and can supersede the direct value at later bookmarks. Edit the dated source deliberately when that later behavior is the intended change.
|
|
91
|
+
|
|
92
|
+
`floating_harbor` placement rows differ from fixed `naval_base_spawn` ports. Their XYZ coordinates normally identify a sea placement, while the final numeric field names a land target. The map catalog does not count floating-harbor placements as starting naval-base locators. A missing or non-land target is an error. An off-sea position or an unusual state/coastal association is a review warning because those patterns also occur in installed vanilla map data; the warning retains the exact source line.
|
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
|
@@ -18,6 +18,9 @@ All seven tools are read-only. Proposed source is parsed in memory and never wri
|
|
|
18
18
|
|
|
19
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
20
|
|
|
21
|
+
Source request paths are exact workspace-relative files, such as `common/decisions/example.txt`.
|
|
22
|
+
The `mod:`, `game:`, and `dependency-N:` labels in returned source citations identify origins; they are not part of a probability request's `source.path`.
|
|
23
|
+
|
|
21
24
|
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.
|
|
22
25
|
|
|
23
26
|
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.
|
|
@@ -94,6 +97,8 @@ Focus, technology, and doctrine probabilities require a complete candidate pool.
|
|
|
94
97
|
|
|
95
98
|
Decision and mission adapters intentionally return scores and ranks without inventing normalized probabilities. Event-option and `random_list` adapters normalize only their complete local pools. Direct random remains an independent percentage. MTTH horizon chance uses the versioned game timing model and returns a bound when an inactive-to-active polling phase is unknown.
|
|
96
99
|
|
|
100
|
+
`candidateOverrides` declares candidate eligibility for a scenario. It does not force weight-modifier conditions to be true or false; those conditions still use the scenario's actual declared variables, flags, scopes, and controller facts. An overridden eligibility claim is an explicit scenario assumption, not evidence that the engine makes that candidate available.
|
|
101
|
+
|
|
97
102
|
Named acceptance bands and configurable diagnostic thresholds let a test suite state intended probability, timing, starvation, dominance, prevalence, and sensitivity limits. Evaluate, sweep, and simulation requests can name the metrics of interest; that set is retained in result metadata while the authoritative result keeps the eligibility and trace context required to explain them. Sweeps enumerate declared alternatives exactly, add trigger breakpoints and their adjacent values for continuous ranges, and report local elasticities, pairwise interactions, rank reversals, cliffs, and missed target bands. Sweep expansion is bounded and rejected before it can silently truncate the requested analysis.
|
|
98
103
|
|
|
99
104
|
## Results
|
|
@@ -119,6 +124,8 @@ Deterministic simulation uses constant-memory Latin hypercube sampling by defaul
|
|
|
119
124
|
|
|
120
125
|
Pass `inlineClausewitz` or `virtualPatch` in a source selector to analyze text without writing it. `hoi4.probability_compare` evaluates the before and after source under the same scenarios and attributes each changed rank, score, probability, or uncertainty to its modifier trace.
|
|
121
126
|
|
|
127
|
+
For a frozen file stored outside its gameplay folder, set the source selector's `path` to its logical gameplay path, `snapshotPath` to the preserved workspace-relative file, and `expectedSourceHash` to the SHA-256 of that file's exact bytes. For example, a decision copy in `docs/evidence/decisions-before.txt` can be analyzed as `common/decisions/policy.txt`. The server verifies the frozen bytes and retains their physical source provenance while using the logical path for domain recognition. It never replaces the current file. Both paths must identify exact files inside authorized roots; traversal and wildcard selectors are rejected. Shared helpers and constants still come from the current configured workspace, so one frozen file is not a complete historical workspace snapshot.
|
|
128
|
+
|
|
122
129
|
## Declared sequences
|
|
123
130
|
|
|
124
131
|
Sequence analysis accepts a `customPoolManifest`. The manifest is the complete model boundary: candidates, selection mode, cadence, state, recovery, caps, cooldowns, removals, resets, timer changes, and terminal states. Small finite systems use exact state distributions, bounded systems expose omitted beam mass, and large systems use deterministic seeded Monte Carlo. Results include per-candidate and per-category next-choice probability, expected selections, ever-selected probability, starvation, and expected first-selection day. The analyzer never executes event effects or infers wider campaign state.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Local references and source lookup
|
|
2
|
+
|
|
3
|
+
The reference tools read only local, authorized sources. They do not fetch the Paradox Wiki or start Hearts of Iron IV. The installed game's documentation has the highest authority for current syntax; the offline wiki supplies broader modding guidance. A result identifies its source, path, exact lines, and SHA-256 revision so an agent can inspect only the relevant section.
|
|
4
|
+
|
|
5
|
+
The server discovers these directories for a workspace:
|
|
6
|
+
|
|
7
|
+
| Source | Default location | Optional workspace registration |
|
|
8
|
+
| ------------------------------ | --------------------------------------------------------------------------- | ------------------------------------------------------ |
|
|
9
|
+
| Installed game documentation | `gameRoot/documentation/**/*.md` and `gameRoot/common/**/*documentation.md` | Configure `gameRoot`. |
|
|
10
|
+
| Offline wiki | `modRoot/paradox_wiki/**/*.md` | Set absolute `wikiRoot`. |
|
|
11
|
+
| Generated script documentation | `modRoot/script_docs/**/*.md` and `**/*.log` | Set absolute `scriptDocsRoot` to a user-supplied dump. |
|
|
12
|
+
|
|
13
|
+
Only Markdown headings and recognizable standalone names in generated `.log` files form sections. A dump that yields one section should be treated as insufficiently parsed; inspect its source format before relying on it. Source bytes are hashed on each request, while unchanged section indexes are reused. Missing roots are reported as unavailable. The public package contains none of these local source files.
|
|
14
|
+
|
|
15
|
+
## Tools
|
|
16
|
+
|
|
17
|
+
- `hoi4.reference_context`: request a surface such as `event`, `focus`, `technology`, `gui`, or `map`. It returns only the relevant offline wiki and installed documentation citations for that surface. Supply `question` to put the five best matching sections first. `missing` names a selected source absent from the configured roots. Citations are pointers and excerpts; read the pertinent sections before changing code.
|
|
18
|
+
- `hoi4.reference_search`: search page names, headings, exact script tokens, and matching body lines. The default limit is six results and the maximum is twelve. Filter `sources` to `game_doc`, `wiki`, or `script_doc` when appropriate.
|
|
19
|
+
- `hoi4.reference_read`: provide a search/context result's `id` and `revision`. Read up to 80 lines and 8,000 UTF-8 bytes. Continue with the returned `nextLine`, `nextColumn` as `startColumn`, and the same revision. Long source lines continue without losing characters. A changed source returns `REFERENCE_REVISION_STALE`.
|
|
20
|
+
- `hoi4.source_lookup`: find exact definitions and indexed usages in the installed game and configured mod source. The response includes load order, override status, up to three source blocks bounded to 2,000 bytes each, a scan revision, and explicit reference counts and truncation. Follow `nextLine` and `nextColumn` as `fromLine` and `fromColumn`, with `expectedRevision`, to read more of the active definition.
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{ "workspaceId": "current", "surface": "event", "question": "save_event_target_as" }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Call `hoi4.reference_context` with that input, then search `save_event_target_as` and pass the result's `id` and `revision` to `hoi4.reference_read`. Use `hoi4.source_lookup` for an exact event, effect, or other indexed symbol. Run `hoi4.event_inspect` and the relevant render/compare/scenario tools for the event itself; references do not validate a mod implementation.
|
|
29
|
+
|
|
30
|
+
The local index admits at most 512 files per source kind, 2 MB per file, 32 MB in total, and 20,000 sections. Search and read responses are bounded by the MCP result budget. `skipped` and per-source coverage expose incomplete ingestion instead of implying that an omitted source was checked. Registered external reference roots are operator configuration and remain private to the authorized workspace.
|
|
31
|
+
|
|
32
|
+
See [the measured Chaos Redux reference retrieval](research/reference-benchmark.md) for actual source coverage and response sizes.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Chaos Redux reference retrieval
|
|
2
|
+
|
|
3
|
+
Measured on 2026-09-26 against the local Chaos Redux `paradox_wiki/` snapshot and the installed Hearts of Iron IV documentation. The fixture script requested context bundles for six work surfaces, then searched and read the installed `save_event_target_as` effect documentation. These are local-source measurements, not a claim about response accuracy for every future task.
|
|
4
|
+
|
|
5
|
+
| Surface | Citations | Missing required sources | Context JSON bytes | Elapsed milliseconds |
|
|
6
|
+
| ---------- | --------: | -----------------------: | -----------------: | -------------------: |
|
|
7
|
+
| Event | 10 | 0 | 5,536 | 218 |
|
|
8
|
+
| Decision | 7 | 0 | 3,988 | 115 |
|
|
9
|
+
| Focus | 8 | 0 | 4,537 | 128 |
|
|
10
|
+
| Technology | 6 | 0 | 3,525 | 137 |
|
|
11
|
+
| GUI | 6 | 0 | 3,678 | 109 |
|
|
12
|
+
| Map | 7 | 0 | 4,224 | 113 |
|
|
13
|
+
|
|
14
|
+
The eleven full core wiki files alone total 1,167,186 bytes in this snapshot. The task-specific context responses above used 3,525–5,536 bytes, approximately 99.5–99.7% fewer bytes than loading those eleven whole pages. This measures retrieved bytes, not tokenizer-specific tokens or complete knowledge of every page. Follow-up section reads add context as needed. Each bundle selected the relevant wiki topics and installed documentation for its work surface; no selected source was missing in these six runs.
|
|
15
|
+
|
|
16
|
+
The `save_event_target_as` search returned the installed `effects_documentation.md` section at line 6,496 first. A 12-line read returned the command name and supported scopes from that file. The exact source revision and path were returned with the citation. The server did not invoke the game or access the wiki website.
|
|
17
|
+
|
|
18
|
+
## Natural-language discovery
|
|
19
|
+
|
|
20
|
+
Nine queries were chosen before ranking changes to cover focus OR prerequisites, technology icon sizing, event-target scope documentation, mission timeouts, scripted GUI click effects, dated state history, localisation BOM, AI focus weights, and event option weights. Each query has a manually checked answer-bearing file and section in the local sources. The original exact-phrase ranking found the expected page in the top five for 2 of 9 queries. Term-aware ranking found the expected page and section in the top five for all 9. Five answer-bearing sections ranked first; all nine ranked fourth or better. This is a small task-specific retrieval set, not a general precision guarantee.
|
|
21
|
+
|
|
22
|
+
The nine ranked result sets occupied 2,776–4,216 JSON bytes each. The dedicated run took 126–234 milliseconds per query, including source hash checks; earlier concurrent runs took up to about three seconds. `scripts/evaluate-reference.ts` reruns the page, section, context coverage, response-size, and latency checks against a supplied mod and game root. It exits with a failure when an expected answer-bearing section or required context source is missing.
|
|
23
|
+
|
|
24
|
+
Revision-bound section IDs stayed stable across unrelated retrieval calls in the fixture test, and a fresh reference-service instance reopened the same cited text without the previous cache. A changed source invalidated the old revision. This supports returning to an exact citation without keeping full pages in conversation context. It does not measure a model's memory or guarantee that every future question can be answered from one retrieved section.
|
|
25
|
+
|
|
26
|
+
The retrieval benchmark used the local service directly. A separate real-workspace stdio run identified the built server as 3.5.0 and completed documentation context in 306 ms, a cited section read in 162 ms, and a first exact source lookup in 58,851 ms. That lookup builds the broader game/mod symbol inventory; it should not be confused with the much narrower documentation lookup. MCP transport fixtures cover both protocol adapters, workspace authorization, read-scope denial, and source usage discovery. Individual task adequacy and actual engine behavior remain separate checks.
|
|
27
|
+
|
|
28
|
+
The four additional public tool definitions add approximately 6.5 KB to the one-time MCP tool discovery response. The complete list is about 72 KB in the discovery fixture and is tested against a 72 KiB ceiling. The context reduction above applies to reference retrieval after discovery.
|
package/docs/setup.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Setup
|
|
2
2
|
|
|
3
|
+
For Codex, the supplied registration allows 120 seconds for startup and 600 seconds per tool call.
|
|
4
|
+
Large source scans and GUI renders can take several minutes; keep selectors and scenario matrices bounded and use negotiated task support when available.
|
|
5
|
+
`tool_timeout_sec` is the per-server tool deadline described in the [official configuration reference](https://developers.openai.com/ja-JP/docs/config-file/config-reference).
|
|
6
|
+
|
|
3
7
|
HOI4 Agent Tools requires Node.js 22.19 or later in the Node 22 line, or Node.js 24.
|
|
4
8
|
|
|
5
9
|
## Install
|
|
@@ -33,6 +37,8 @@ hoi4-agent-tools-setup --init --mod-root /projects/hoi4-mods --mod-root /worksho
|
|
|
33
37
|
|
|
34
38
|
The corresponding config fields are `modRoots`, `gameRoot`, and `workspaceStorageRoot`.
|
|
35
39
|
|
|
40
|
+
For a configured workspace, `wikiRoot` can select an absolute path to an offline Paradox Wiki snapshot and `scriptDocsRoot` can select an absolute path to a user-supplied generated documentation dump. Without these fields, the reference tools look for `paradox_wiki/` and `script_docs/` inside the mod. See [Local references](reference.md).
|
|
41
|
+
|
|
36
42
|
Linked directory entries are not followed. If a mod folder is a symlink or junction, configure the real parent directory as another `--mod-root`.
|
|
37
43
|
|
|
38
44
|
## MCP client registration
|
package/docs/technology.md
CHANGED
|
@@ -56,10 +56,14 @@ It preserves separate conditional call sites and identifies source, cycle, and d
|
|
|
56
56
|
|
|
57
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`.
|
|
58
58
|
|
|
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
|
|
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
60
|
|
|
61
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
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.
|
|
66
|
+
|
|
63
67
|
Example folder render:
|
|
64
68
|
|
|
65
69
|
```json
|
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.5.0",
|
|
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",
|
|
@@ -49,11 +49,12 @@
|
|
|
49
49
|
"docs/focus.md",
|
|
50
50
|
"docs/gui.md",
|
|
51
51
|
"docs/map.md",
|
|
52
|
+
"docs/reference.md",
|
|
53
|
+
"docs/research/reference-benchmark.md",
|
|
52
54
|
"docs/http.md",
|
|
53
55
|
"docs/development.md",
|
|
54
56
|
"docs/jobs.md",
|
|
55
57
|
"docs/helper-expansion.md",
|
|
56
|
-
"docs/images/readme/",
|
|
57
58
|
"examples/clients/",
|
|
58
59
|
"examples/probability/",
|
|
59
60
|
"schemas/",
|
|
@@ -83,6 +84,7 @@
|
|
|
83
84
|
"test:coverage": "vitest run --coverage",
|
|
84
85
|
"benchmark": "node --expose-gc --import tsx scripts/benchmark.ts",
|
|
85
86
|
"docs:examples": "tsx scripts/generate-readme-examples.ts",
|
|
87
|
+
"docs:comparison-examples": "tsx scripts/generate-real-visual-comparisons.ts",
|
|
86
88
|
"fixtures:generate": "npm run fixtures:generate:focus && npm run fixtures:generate:gui && npm run fixtures:generate:map && npm run fixtures:generate:event && npm run fixtures:generate:technology && npm run fixtures:generate:probability",
|
|
87
89
|
"fixtures:generate:focus": "tsx scripts/generate-fixtures.ts",
|
|
88
90
|
"fixtures:generate:gui": "tsx scripts/generate-gui-fixture.ts",
|