I created a plugin that lets you extract your full GUI and script hierarchy for easy ai assistance. Designed for fast AI integration (Claude, GPT, Gemini Playground version can handle the whole shebang), it maps properties, remotes, attributes, and more. Ideal for rapid debugging or documentation.
Includes a free web tool for visualizing your game structure and script connections. The web based tool is accessible whether or not you buy the plugin. If you have some existing export you want to try out. By means go ahead.
Demo videos below.
Price: $5 or use the free pastable command version ive shared at the bottom of this page
Link: https://create.roblox.com/store/asset/103991125687866/
Web Tool: https://www.scriptexporter.com
Includes demo video on landing page showing the script exporter plugin working in unison with the web based project mapper.
Built this for personal use, now public. Feedback welcome.
Some screenshots below
You can chose which parts to export. The collated file will be exported to a module script in ServerStorage. Plugin has options to pretty export or soft minify for the likes of claude where you are going to want to save your tokens. Additionally there is the option of mapping the attributes of your GUI which comes in handy when you are trying to use a chatbot to help you find out what is causing your mobile responsiveness to break!
Once pasted into the companion web based mapper. You will have a searchable, color coordinated UI that will help you copy groups of associated files.
One of the major headaches when dealing with ai assisted vibe coding is chabots tenancy to either create their own remote events/functions or for them to leave them out of the equation. Here we have visual mapper that helps you understand what files are connected. Additionally, clicking these links in the ui will again highlight the linked files so you can easily copy and paste the problem files for debugging.
Mapping your gui attributes visually can sometimes help, since the Studio interface for this is a massive pain.
Lastly on the UI we have a visual mind mapper that is color coordinated and again will link with clicks onto the files that are selected in the script explorer to help you visualize the structure of the code. I can see this being very useful for visual learners like myself when dealing with brand new projects.
Update
Due to a lot of the feedback focusing negatively on the ai aspect, ive decided to give a barebones pasteable version the plugin here in the thread. Thereby showcasing the web based tools utility as a learning, mapping and visualizing aid. I want to help the next generation of roblox builders as best I can so here is a pastable command script that you can then dump into the web based tool on the main website above to get the benefits of the paid plugin completely for free.
Open your command bar
Paste the command and press enter, you will get every user generated script in your project mapped.
You will now have a file module file created in ServerStorage called CombinedUserScripts_1xxxxx
You can then use the web based tool on the main website without paying a thing. Minus the GUI mapping.
FULL SCRIPT EXPORTER PASTABLE COMMAND SCRIPT MINUS GUI MAPPING, FILTERING AND BEAUTIFYING
local function exportScripts()
-- Configuration
local REMOVE_LARGE_COMMENTS = true -- Set to false to keep all comments
local MAX_COMMENT_LINES = 4 -- Comments longer than this will be removed if REMOVE_LARGE_COMMENTS is true
local INCLUDE_DISABLED_SCRIPTS = false -- Set to true to include disabled scripts, false to exclude them
local SOFT_MINIFY = false -- Set to true to enable soft minification of the output
local TYPICAL_MINIFY = true -- Set to true to enable typical minification of the output
local scripts = {}
local filterList = {} -- This is kept from your script, but you mentioned it's not needed for the basic.
-- For a truly basic version matching only the *output style* you might remove filterList logic.
local maxScriptLength = 190000 -- Buffer below 200000 limit
local remoteEvents = {}
-- Read the filter list from the StringValue object (Kept from your script)
local filterListObject = workspace:FindFirstChild("FileList")
if filterListObject and filterListObject:IsA("StringValue") then
for line in filterListObject.Value:gmatch("[^\r\n]+") do
filterList[line:lower()] = true
end
else
warn("Filter list 'Workspace>FileList' (StringValue) not found or not a StringValue. No filtering will be applied from it.")
end
-- Function to check if a script should be included
local function shouldInclude(scriptPath, instance)
local isDisabled = false
if instance:IsA("Script") or instance:IsA("LocalScript") then
isDisabled = not instance.Enabled
end
-- Check against filterList if it has entries
local isFilteredOut = false
if next(filterList) then -- Check if filterList is not empty
isFilteredOut = filterList[scriptPath:lower()]
end
return not isFilteredOut and
not scriptPath:lower():match("^coregui>") and
scriptPath ~= "Workspace>FileList" and -- Explicitly exclude the FileList StringValue itself
(INCLUDE_DISABLED_SCRIPTS or not isDisabled)
end
-- Function to get full path of an instance
local function getFullPath(instance)
local path = instance.Name
local current = instance.Parent
while current and current ~= game do
path = current.Name .. ">" .. path
current = current.Parent
end
return path
end
-- Function to remove large comments, stray closing brackets, and triple backticks (Your function)
local function removeLargeCommentsAndClean(source)
if not REMOVE_LARGE_COMMENTS then
return source:gsub("```", ""):gsub("%]=*%]", "")
end
local lines = {}
local originalLines = source:split("\n")
local inBlockComment = false
local blockCommentLevel = 0
local blockCommentStartLine = 0
for i = 1, #originalLines do
local line = originalLines[i]
line = line:gsub("```", "")
local blockStartMatch = line:match("^%s*%-%-%[(=*)%[")
if blockStartMatch then
if not inBlockComment then
inBlockComment = true
blockCommentLevel = #blockStartMatch
blockCommentStartLine = #lines + 1
table.insert(lines, line)
else
table.insert(lines, line)
end
elseif inBlockComment then
table.insert(lines, line)
local blockEndMatch = line:match("%]" .. string.rep("=", blockCommentLevel) .. "%]")
if blockEndMatch then
inBlockComment = false
local currentCommentLineCount = (#lines) - blockCommentStartLine + 1
if currentCommentLineCount > MAX_COMMENT_LINES then
for j = 1, currentCommentLineCount do
table.remove(lines, blockCommentStartLine)
end
end
blockCommentLevel = 0
end
else
line = line:gsub("%]=*%]", "")
table.insert(lines, line)
end
end
return table.concat(lines, "\n")
end
-- Function to find remote events (Your function)
local function findRemoteEvents(source, scriptPath)
if not source then
warn("Empty source for script: " .. scriptPath)
return
end
for line in source:gmatch("[^\r\n]+") do
if line:match("Instance%.new%(%s*[\"']RemoteEvent[\"']%s*%)") then
local eventName = line:match("local%s+(%w+)%s*=")
if eventName then
table.insert(remoteEvents, {name = eventName, path = scriptPath})
else
table.insert(remoteEvents, {name = "Unnamed RemoteEvent", path = scriptPath})
end
end
end
end
-- Function to soft minify content (Your function)
local function softMinify(content)
if not SOFT_MINIFY or TYPICAL_MINIFY then -- Typical takes precedence
return content
end
local lines = {}
for line in content:gmatch("[^\r\n]+") do
line = line:gsub("^%s+", ""):gsub("%s+$", "")
line = line:gsub("%-%-[^\n]*", "")
if not line:match("^%s*$") then
table.insert(lines, line)
end
end
local result = table.concat(lines, "\n")
result = result:gsub("\n+", "\n")
return result
end
-- Function to typically minify content (Your function)
local function typicalMinify(content)
if not TYPICAL_MINIFY then
return content
end
local result = content
result = result:gsub("%-%-%[%[.-%]%]", "")
result = result:gsub("%-%-[^\n]*\n", "\n")
result = result:gsub("%-%-[^\n]*$", "")
result = result:gsub("\n%s*\n", "\n")
result = result:gsub("^\n*", ""):gsub("\n*$", "")
result = result:gsub("%s+", " ")
result = result:gsub("^%s*(.-)%s*$", "%1")
result = result:gsub(";%s*}", "}")
result = result:gsub("%s*([%{%}%(%)=%[%];,])%s*", "%1")
result = result:gsub("([\n])%s+", "%1")
return result
end
-- Recursively find all scripts
local function findScripts(obj)
for _, child in ipairs(obj:GetChildren()) do
if child:IsA("LocalScript") or child:IsA("ModuleScript") or child:IsA("Script") then
local scriptPath = getFullPath(child)
if shouldInclude(scriptPath, child) then
local success, source = pcall(function() return child.Source end)
if success and source and source:match("%S") then
local cleanSource = removeLargeCommentsAndClean(source)
findRemoteEvents(cleanSource, scriptPath)
table.insert(scripts, {type = child.ClassName, name = scriptPath, source = cleanSource})
print("Including file:", child.ClassName .. ": " .. scriptPath)
else
if not success then warn("Could not read source for: " .. scriptPath .. " Error: " .. tostring(source))
else print("Skipping empty script:", child.ClassName .. ": " .. scriptPath) end
end
else
print("Excluding file:", child.ClassName .. ": " .. scriptPath)
end
end
if #child:GetChildren() > 0 then
findScripts(child)
end
end
end
print("--- Starting Script Export ---")
findScripts(game) -- Scan entire game
if #scripts == 0 then
print("--- No scripts found to export. ---")
return
end
-- Create multiple ModuleScripts for combined content
local scriptIndex = 1
local parentService = game:GetService("ServerStorage")
local currentScript = Instance.new("ModuleScript")
currentScript.Name = "CombinedUserScripts_1"
currentScript.Parent = parentService
local currentSource = ""
-- Add remote event mappings as comments at the top
local eventComments = "-- Remote Event Mappings (based on 'Instance.new(\"RemoteEvent\")' detection):\n"
if #remoteEvents > 0 then
for _, event in ipairs(remoteEvents) do
eventComments = eventComments .. string.format("-- %s: (found in %s)\n", event.name, event.path)
end
else
eventComments = eventComments .. "-- No RemoteEvents explicitly created with Instance.new(\"RemoteEvent\") found.\n"
end
currentSource = eventComments .. "\n"
for _, scriptItem in ipairs(scripts) do
local processedSource = scriptItem.source
if TYPICAL_MINIFY then
processedSource = typicalMinify(processedSource)
elseif SOFT_MINIFY then
processedSource = softMinify(processedSource)
end
-- THIS IS THE CHANGED LINE TO MATCH THE SYSTEM PROMPT'S EXPORT STYLE
local scriptContent = string.format("-- %s: %s\n%s\n\n",
scriptItem.type, scriptItem.name,
processedSource)
if #currentSource + #scriptContent > maxScriptLength and #currentSource > #eventComments + 5 then
currentScript.Source = currentSource
scriptIndex = scriptIndex + 1
currentScript = Instance.new("ModuleScript")
currentScript.Name = "CombinedUserScripts_" .. scriptIndex
currentScript.Parent = parentService
currentSource = eventComments .. "\n" -- Add event mappings to each new script
print("Creating new combined script: " .. currentScript.Name)
end
currentSource = currentSource .. scriptContent
end
if #currentSource > #eventComments + 5 then
currentScript.Source = currentSource
elseif currentScript.Parent then -- If it only has comments, destroy it
currentScript:Destroy()
scriptIndex = math.max(0, scriptIndex - 1)
print("Last combined script was empty or contained only headers; removed.")
end
if scriptIndex > 0 then
print("--- Export Complete: Created " .. scriptIndex .. " combined script(s) in ServerStorage, containing " .. #scripts .. " original scripts. ---")
elseif #scripts > 0 then
print("--- Scripts processed, but no combined script files were generated (likely due to all being empty after processing). ---")
else
print("--- No scripts were exported. ---")
end
end
-- Run the export function
exportScripts()
This effectively makes the paid plugin donation based. Exports multiple files if your project goes over 200000 lines (will split into various modules)