Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This repo builds the Grill CLI and project setup tooling. The generated map-proj
- Local helpers in `config/ProjectConfigModels.kt` should stay thin: typealiases plus immutable copy helpers for shared records.
- `YamlHelper.dumpProjectConfig` intentionally serializes a pruned YAML map instead of the shared records directly. This preserves the old user-facing `wurst.build` behavior by omitting null/default nested fields.
- `wbschema.json` should stay lenient and aligned with the shared config parser, especially for `scriptMode`, `wc3Patch`, and nullable legacy fields.
- Compiler-owned agent references belong in `~/.wurst/wurst-compiler/agent-docs/`. Generated project notes should prefer those version-matched local files when present and retain an online fallback until compiler releases ship them.

## WC3 Patch And Core JASS

Expand Down
13 changes: 12 additions & 1 deletion src/main/kotlin/file/SetupApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ object SetupApp {

private data class WurstProcessResult(val exitCode: Int, val output: List<String>)

internal const val AGENTS_TEMPLATE_VERSION = "2026-08-05"
internal const val AGENTS_TEMPLATE_VERSION = "2026-08-08"
Comment thread
Frotty marked this conversation as resolved.
private const val AGENTS_TEMPLATE_MARKER_PREFIX = "<!-- WURST_AGENTS_TEMPLATE_VERSION:"
private const val AGENTS_TEMPLATE_MARKER = "<!-- WURST_AGENTS_TEMPLATE_VERSION: $AGENTS_TEMPLATE_VERSION -->"
private const val AGENTS_TEMPLATE_SOURCE_HINT = "WurstScript Warcraft III map project notes"
Expand Down Expand Up @@ -936,6 +936,17 @@ object SetupApp {
if (markerLine == AGENTS_TEMPLATE_MARKER) {
return null
}
if (markerLine != null) {
val markerVersion = markerLine
.removePrefix(AGENTS_TEMPLATE_MARKER_PREFIX)
.removeSuffix("-->")
.trim()
// Template versions are ISO dates, so lexical ordering is chronological. A newer
// downloaded template is valid even when this older Grill binary cannot recognize it.
if (markerVersion > AGENTS_TEMPLATE_VERSION) {
return null
}
}
if (markerLine != null) {
return "AGENTS.md was generated from an older WurstSetup template ($markerLine). Consider refreshing it from templates/AGENTS.md and re-applying project-local notes."
}
Expand Down
27 changes: 26 additions & 1 deletion src/main/kotlin/global/InstallationManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import net.NetStatus
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.nio.file.StandardCopyOption
import java.util.jar.JarFile
import java.util.regex.Pattern


Expand All @@ -19,6 +21,7 @@ object InstallationManager {
private val log = KotlinLogging.logger {}
private const val FOLDER_PATH = ".wurst"
private const val COMPILER_FILE_NAME = "wurstscript.jar"
private const val LANGUAGE_AGENT_DOC_ENTRY = "agent-docs/WURST_LANGUAGE.md"
private const val GRILL_JAR_NAME = "grill.jar"
private const val LEGACY_GRILL_JAR_NAME = "WurstSetup.jar"

Expand Down Expand Up @@ -49,6 +52,7 @@ object InstallationManager {
log.info("verifyInstallation: detectedCompilerJar=$detectedCompilerJar exists=${detectedCompilerJar?.let { Files.exists(it) }}")
if (detectedCompilerJar != null) {
log.info("Found installation at $detectedCompilerJar")
ensureCompilerAgentDocs(detectedCompilerJar)
status = InstallationStatus.INSTALLED_UNKNOWN
try {
if (!Files.isWritable(detectedCompilerJar)) {
Expand Down Expand Up @@ -94,9 +98,11 @@ object InstallationManager {
log.info("\t📦 Extracting..")
ZipArchiveExtractor.extractArchive(it, installDir)
Files.delete(it)
if (detectCompilerJar() == null) {
val compilerJar = detectCompilerJar()
if (compilerJar == null) {
log.error("❌ Compiler not found after extraction.")
} else {
ensureCompilerAgentDocs(compilerJar)
if (isFreshInstall) { wurstConfig = WurstConfigData() }
ensureGrillJarInstalled()
setLaunchersExecutable()
Expand Down Expand Up @@ -193,6 +199,25 @@ object InstallationManager {
}
}

private fun ensureCompilerAgentDocs(compilerJar: Path) {
try {
JarFile(compilerJar.toFile()).use { jar ->
val entry = jar.getJarEntry(LANGUAGE_AGENT_DOC_ENTRY) ?: return

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove stale language docs when the compiler omits them

When a compiler that contains agent-docs/WURST_LANGUAGE.md is replaced or downgraded to a compiler without that entry, this early return leaves the previously extracted file in place. Generated project notes then find the local file and treat it as compiler-matched instead of using the online fallback, potentially giving agents semantics from the wrong compiler version; delete the extracted file when the active JAR lacks the entry.

AGENTS.md reference: AGENTS.md:L18-L18

Useful? React with 👍 / 👎.

val docsDir = compilerDir.resolve("agent-docs")
Files.createDirectories(docsDir)
jar.getInputStream(entry).use { input ->
Files.copy(
input,
docsDir.resolve("WURST_LANGUAGE.md"),
StandardCopyOption.REPLACE_EXISTING
)
}
}
} catch (e: Exception) {
log.warn("Could not extract compiler agent docs: ${e.message}")
}
}

private fun resolveOwnJar(): Path? {
return try {
val url = InstallationManager::class.java.protectionDomain.codeSource.location
Expand Down
37 changes: 37 additions & 0 deletions src/test/kotlin/AgentsTemplateTests.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import file.SetupApp
import org.testng.Assert
import org.testng.annotations.Test
import java.nio.file.Files
import java.nio.file.Paths

class AgentsTemplateTests {
private val templatePath = Paths.get("templates", "AGENTS.md")

@Test
fun testTemplateStaysTokenLean() {
val content = Files.readString(templatePath)
val wordCount = Regex("""\S+""").findAll(content).count()

Assert.assertTrue(wordCount <= 900, "AGENTS template grew to $wordCount words (limit: 900)")
Assert.assertTrue(content.length <= 7000, "AGENTS template grew to ${content.length} characters (limit: 7000)")
}

@Test
fun testLanguageDocsPreferCompilerMatchedLocalReference() {
val content = Files.readString(templatePath)
val localReference = "~/.wurst/wurst-compiler/agent-docs/WURST_LANGUAGE.md"
val onlineFallback = "https://wurstlang.org/manual.html"
val localIndex = content.indexOf(localReference)
val onlineIndex = content.indexOf(onlineFallback)

Assert.assertTrue(localIndex >= 0, "Missing compiler-matched local language reference")
Assert.assertTrue(onlineIndex > localIndex, "Online manual must remain a fallback after the local reference")
}

@Test
fun testNewerTemplateDoesNotLookStaleToOlderGrill() {
val newerMarked = "<!-- WURST_AGENTS_TEMPLATE_VERSION: 2099-01-01 -->\n# AGENTS.md\n"

Assert.assertNull(SetupApp.agentsTemplateWarning(newerMarked))
}
}
Loading
Loading