一键导入
stem-plugin-extraction
How to safely extract an inline STEM Lab tool from stem_lab_module.js into a standalone plugin file using window.StemLab.registerTool
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
How to safely extract an inline STEM Lab tool from stem_lab_module.js into a standalone plugin file using window.StemLab.registerTool
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Development patterns for the AlloFlow monolith (AlloFlowANTI.txt) - auditing, refactoring, feature addition, debugging, and safe editing of the ~29K-line single-file React application
Safe editing and deployment of the CDN-loaded JavaScript feature modules (stem_lab, word_sounds, behavior_lens, report_writer, and ~250 others)
Full production deployment pipeline for AlloFlow — push, build, stamp, deploy, verify
How to extract a React component from AlloFlowANTI.txt into a standalone CDN-loaded module — JSX transpilation, dependency injection, IIFE wrapping, and registration
Safe editing and validation of ui_strings.js — prevents JSON parse errors, verifies key coverage, handles nested section structure
| name | stem-plugin-extraction |
| description | How to safely extract an inline STEM Lab tool from stem_lab_module.js into a standalone plugin file using window.StemLab.registerTool |
Use this skill when extracting an inline tool definition from stem_lab_module.js into a standalone stem_tool_*.js plugin file that registers via window.StemLab.registerTool().
[!CAUTION] Extraction scripts that match on string patterns can accidentally consume adjacent IIFE closures. This caused a real production outage on 2026-03-24 when a missing
})()),collapsed 43,000 lines. Always runnode -csyntax verification after extraction.
STEM Lab tools come in two flavors:
stem_lab_module.js as conditional branches of the StemLabModal componentwindow.StemLab.registerTool('toolName', { render: fn }) and are loaded by the plugin fallback rendererThe plugin registry (_pluginFallback) provides a bridge context object (ctx) containing React, hooks, state, and utility functions.
Search for the tool's inline definition in stem_lab_module.js:
// The inline tool is typically guarded by a condition like:
stemLabTool === 'codingPlayground' && (() => {
// ... tool body (can be 500-2000 lines) ...
})(),
Note the start line (the condition) and end line (the })(), closure).
[!IMPORTANT] This is the #1 cause of post-extraction crashes. React hooks cannot be called conditionally, so they are often hoisted to the
StemLabModaltop level — outside the tool's inline body.
Search for hooks that reference your tool name:
# Find hooks that may be hoisted outside the tool body
python -c "
lines = open('stem_lab/stem_lab_module.js', encoding='utf-8').readlines()
for i, l in enumerate(lines):
if '_codingCanvas' in l or '_yourToolRef' in l:
print(f'{i}: {l.strip()}')"
Common hoisted patterns:
var _codingCanvasRef = React.useRef(null);React.useEffect(function() { ... codingPlayground ... });These hooks must be injected into the ctx bridge (see Step 5).
Create stem_lab/stem_tool_<name>.js with this structure:
window.StemLab.registerTool('toolName', {
icon: '🔬',
label: 'toolName',
desc: '',
color: 'slate',
category: 'creative', // or 'science', 'math', etc.
render: function(ctx) {
// ── Aliases — map ctx properties to original variable names ──
var React = ctx.React;
var h = React.createElement;
var labToolData = ctx.toolData;
var setLabToolData = ctx.setToolData;
var setStemLabTool = ctx.setStemLabTool;
var addToast = ctx.addToast;
var ArrowLeft = ctx.icons.ArrowLeft;
var awardStemXP = ctx.awardXP;
var stemCelebrate = ctx.celebrate;
var callGemini = ctx.callGemini;
// ... add any hoisted hooks ...
var _myToolRef = ctx._myToolRef;
// ── Tool body ──
return (function() {
// ... paste extracted inline body here ...
})();
}
});
Delete the inline tool body from stem_lab_module.js. Be extremely careful with closing brackets.
Verification after deletion:
node -c stem_lab/stem_lab_module.js
If this fails with SyntaxError: Unexpected token ')' or Unexpected end of input, you removed too many or too few closing brackets.
In stem_lab_module.js, find the _ctx object in _pluginFallback and add any hoisted hooks:
var _ctx = {
// ... existing properties ...
_myToolRef: typeof _myToolRef !== 'undefined' ? _myToolRef : null,
};
Add the tool to the _pluginOnlyTools object in _pluginFallback:
var _pluginOnlyTools = {
// ... existing tools ...
myToolName: true,
};
Add a <script> tag for the new plugin file in the <!-- STEM Lab Plugins --> section:
<script src="$CDN_BASE/stem_lab/stem_tool_<name>.js"></script>
# 1. Syntax check ALL modified files
node -c stem_lab/stem_lab_module.js
node -c stem_lab/stem_tool_<name>.js
# 2. Check for orphaned brackets
python -c "
src = open('stem_lab/stem_lab_module.js', encoding='utf-8').read()
print('Open parens:', src.count('('), 'Close:', src.count(')'))
print('Open braces:', src.count('{'), 'Close:', src.count('}'))
print('Open brackets:', src.count('['), 'Close:', src.count(']'))"
# 3. Follow /deploy workflow
node -c passes for monolith AND new plugin filectx_pluginOnlyToolsgit status --short shows clean working tree before deploystr.replace() can match the IIFE closure of the next tool if boundaries aren't precisectx.addToast, ArrowLeft) that are in scope in the monolith but not in the plugin. Always alias from ctx.