ima2-gen 2.0.15 → 2.0.16

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.
Files changed (125) hide show
  1. package/README.md +30 -0
  2. package/bin/commands/skill.js +336 -17
  3. package/bin/ima2.js +22 -2
  4. package/bin/lib/doctor-checks.js +6 -4
  5. package/config.js +1 -0
  6. package/docs/CLI.md +25 -3
  7. package/docs/PROMPT_STUDIO.ko.md +29 -0
  8. package/docs/PROMPT_STUDIO.md +31 -0
  9. package/docs/migration/runtime-test-inventory.md +7 -1
  10. package/lib/agentImageVideoGen.js +8 -0
  11. package/lib/agentQueueStore.js +36 -10
  12. package/lib/agentQueueWorker.js +79 -7
  13. package/lib/agentRuntime.js +2 -0
  14. package/lib/assetLifecycle.js +40 -8
  15. package/lib/canvasVersionStore.js +86 -0
  16. package/lib/cardNewsGenerator.js +37 -3
  17. package/lib/cardNewsJobStore.js +10 -0
  18. package/lib/cardNewsManifestStore.js +6 -12
  19. package/lib/cardNewsPath.js +25 -0
  20. package/lib/db.js +2 -0
  21. package/lib/generatePipeline.js +474 -0
  22. package/lib/generationInputValidation.js +32 -0
  23. package/lib/grokImageCore.js +18 -1
  24. package/lib/historyIndex.js +11 -4
  25. package/lib/historyList.js +3 -0
  26. package/lib/imageMetadata.js +3 -0
  27. package/lib/inflight.js +30 -3
  28. package/lib/multimodePipeline.js +462 -0
  29. package/lib/nodeGeneration.js +440 -0
  30. package/lib/nodeValidation.js +23 -0
  31. package/lib/oauthProxy/generators.js +3 -246
  32. package/lib/oauthProxy/multimodeGenerators.js +251 -0
  33. package/lib/routeHelpers.js +9 -2
  34. package/package.json +2 -2
  35. package/routes/agent.js +4 -4
  36. package/routes/canvasVersions.js +21 -1
  37. package/routes/cardNews.js +2 -1
  38. package/routes/edit.js +25 -9
  39. package/routes/events.js +5 -1
  40. package/routes/generate.js +2 -481
  41. package/routes/keys.js +31 -39
  42. package/routes/multimode.js +2 -502
  43. package/routes/nodes.js +3 -448
  44. package/routes/video.js +12 -3
  45. package/routes/videoExtended.js +19 -4
  46. package/server.js +53 -1
  47. package/skills/ima2/SKILL.md +204 -0
  48. package/skills/ima2-front/SKILL.md +611 -0
  49. package/skills/ima2-front/references/a11y-patterns.md +134 -0
  50. package/skills/ima2-front/references/aesthetics.md +327 -0
  51. package/skills/ima2-front/references/anti-slop.md +241 -0
  52. package/skills/ima2-front/references/asset-requirements.md +431 -0
  53. package/skills/ima2-front/references/brand-asset-sourcing.md +122 -0
  54. package/skills/ima2-front/references/color-system.md +132 -0
  55. package/skills/ima2-front/references/consistency-locks.md +20 -0
  56. package/skills/ima2-front/references/crud-ui.md +53 -0
  57. package/skills/ima2-front/references/dropdown-layer.md +110 -0
  58. package/skills/ima2-front/references/i18n-global.md +104 -0
  59. package/skills/ima2-front/references/iterative-design.md +90 -0
  60. package/skills/ima2-front/references/korea-2026.md +174 -0
  61. package/skills/ima2-front/references/layout-discipline.md +161 -0
  62. package/skills/ima2-front/references/liquid-glass.md +181 -0
  63. package/skills/ima2-front/references/logo-sections.md +194 -0
  64. package/skills/ima2-front/references/mobile-ux.md +144 -0
  65. package/skills/ima2-front/references/performance-budget.md +106 -0
  66. package/skills/ima2-front/references/preflight-full.md +58 -0
  67. package/skills/ima2-front/references/product-density.md +53 -0
  68. package/skills/ima2-front/references/prototype-variants.md +54 -0
  69. package/skills/ima2-front/references/reference-capture.md +120 -0
  70. package/skills/ima2-front/references/responsive-viewport.md +139 -0
  71. package/skills/ima2-front/references/seo-baseline.md +123 -0
  72. package/skills/ima2-front/references/soft-3d-asset-gates.md +70 -0
  73. package/skills/ima2-front/references/stacks/astro.md +204 -0
  74. package/skills/ima2-front/references/stacks/mobile-native.md +281 -0
  75. package/skills/ima2-front/references/stacks/nextjs.md +219 -0
  76. package/skills/ima2-front/references/stacks/react.md +264 -0
  77. package/skills/ima2-front/references/stacks/svelte.md +235 -0
  78. package/skills/ima2-front/references/stacks/vanilla.md +226 -0
  79. package/skills/ima2-front/references/theme-switching.md +82 -0
  80. package/skills/ima2-front/references/top-bar.md +126 -0
  81. package/skills/ima2-front/references/typography-wrapping.md +270 -0
  82. package/skills/ima2-front/references/ux-writing-ko.md +62 -0
  83. package/skills/ima2-front/references/visual-verification.md +102 -0
  84. package/skills/ima2-uiux/SKILL.md +477 -0
  85. package/skills/ima2-uiux/references/anti-slop-judgment.md +39 -0
  86. package/skills/ima2-uiux/references/color-system.md +80 -0
  87. package/skills/ima2-uiux/references/design-isms.md +194 -0
  88. package/skills/ima2-uiux/references/design-read-example.md +33 -0
  89. package/skills/ima2-uiux/references/design-system-bootstrap.md +133 -0
  90. package/skills/ima2-uiux/references/favicon-logo.md +355 -0
  91. package/skills/ima2-uiux/references/form-patterns.md +118 -0
  92. package/skills/ima2-uiux/references/intent-discovery-ladder.md +86 -0
  93. package/skills/ima2-uiux/references/korean-design-vocabulary.md +52 -0
  94. package/skills/ima2-uiux/references/layout-macrostructures.md +54 -0
  95. package/skills/ima2-uiux/references/logo-trust-sections.md +96 -0
  96. package/skills/ima2-uiux/references/mobile-native-ux.md +207 -0
  97. package/skills/ima2-uiux/references/product-personalities.md +118 -0
  98. package/skills/ima2-uiux/references/responsive-nav.md +66 -0
  99. package/skills/ima2-uiux/references/typography-line-breaks.md +193 -0
  100. package/skills/ima2-uiux/references/ux-preflight.md +65 -0
  101. package/skills/ima2-uiux/references/ux-states.md +197 -0
  102. package/skills/ima2-uiux/references/visual-hierarchy.md +115 -0
  103. package/ui/dist/.vite/manifest.json +13 -13
  104. package/ui/dist/assets/AgentWorkspace-CtYt4SF5.js +3 -0
  105. package/ui/dist/assets/{CardNewsWorkspace-Cdxn1ohU.js → CardNewsWorkspace-DN5cVqG5.js} +1 -1
  106. package/ui/dist/assets/GenerationRequestLogPanel-DnmUQdKR.js +1 -0
  107. package/ui/dist/assets/{NodeCanvas-C67sLboA.js → NodeCanvas-CxpfIBI0.js} +1 -1
  108. package/ui/dist/assets/PromptBuilderPanel-C2R3ObtR.js +2 -0
  109. package/ui/dist/assets/{PromptImportDialog-DeQXsEEv.js → PromptImportDialog-BIxz_yEx.js} +2 -2
  110. package/ui/dist/assets/PromptImportDiscoverySection-BQTlPMME.js +1 -0
  111. package/ui/dist/assets/PromptImportFolderSection-Ds_Th9O3.js +1 -0
  112. package/ui/dist/assets/{PromptLibraryPanel-Db3xEPM_.js → PromptLibraryPanel-Rs4l45pV.js} +2 -2
  113. package/ui/dist/assets/{SettingsWorkspace-BDk_1W-V.js → SettingsWorkspace-p1AZ6uT9.js} +1 -1
  114. package/ui/dist/assets/index-0-_vgFGs.js +4 -0
  115. package/ui/dist/assets/index-Dm3pFxV4.js +23 -0
  116. package/ui/dist/assets/index-J8yDF3Ch.css +1 -0
  117. package/ui/dist/index.html +2 -2
  118. package/ui/dist/assets/AgentWorkspace-DAg_7zpY.js +0 -3
  119. package/ui/dist/assets/GenerationRequestLogPanel-C_qvEUi2.js +0 -1
  120. package/ui/dist/assets/PromptBuilderPanel-DjvVdAie.js +0 -2
  121. package/ui/dist/assets/PromptImportDiscoverySection-CrwyUKYI.js +0 -1
  122. package/ui/dist/assets/PromptImportFolderSection-CToGMqYE.js +0 -1
  123. package/ui/dist/assets/index-2oG6in1i.css +0 -1
  124. package/ui/dist/assets/index-CoxMOXAU.js +0 -23
  125. package/ui/dist/assets/index-p8egnGHk.js +0 -4
package/README.md CHANGED
@@ -97,6 +97,36 @@ Ctrl+C now performs a clean shutdown — closing the database, stopping child pr
97
97
  - **Mobile shell**: use the app bar, compose sheet, and compact settings toggle on smaller screens.
98
98
  - **Observable jobs**: active and recent jobs are tracked with safe logs and request IDs.
99
99
 
100
+ ### Agent Skills
101
+
102
+ ima2-gen ships three packaged skills for AI coding agents. These are Markdown
103
+ instruction files that agents load to get structured workflows for image/video
104
+ generation, frontend asset production, and design direction discovery.
105
+
106
+ | Skill | Command | What It Covers |
107
+ |-------|---------|----------------|
108
+ | **Core** | `ima2 skill` | CLI reference, prompting protocol, provider routing, Korean text, video workflows |
109
+ | **Frontend** | `ima2 skill front` | Asset pipeline (parallel gen, variant selection, provider routing), motion/video for web, responsive, a11y, anti-slop, 30+ reference files |
110
+ | **UI/UX Design** | `ima2 skill uiux` | Image-first design direction discovery, UX states, design-isms, product personalities, DESIGN.md workflow, 18 reference files |
111
+
112
+ ```bash
113
+ ima2 skill ls # list available skills
114
+ ima2 skill front # print the frontend skill
115
+ ima2 skill uiux # print the design skill
116
+ ima2 skill front path # print file path (for agents)
117
+ ima2 skill front --json # JSON wrapper (for agents)
118
+ ima2 skill front refs # list reference modules (35 files)
119
+ ima2 skill front ref motion # load one reference module
120
+ ima2 skill install --dir <path> # install skills to agent's skill dir
121
+ ima2 skill install --tmp # install to temp dir (fallback)
122
+ ```
123
+
124
+ The Frontend and UI/UX skills are production-grade design engineering guides
125
+ adapted for the ima2 workflow. They cover typography, color systems, layout
126
+ discipline, Korean UX patterns, motion choreography, and visual verification,
127
+ with every asset generation step mapped to `ima2 gen`, `ima2 video`, and
128
+ `ima2 multimode` commands.
129
+
100
130
  ### SSE Multiplexing
101
131
 
102
132
  The web UI uses a single `GET /api/events` Server-Sent Events connection for all generation progress. Multimode, node, and video requests are submitted as async POST (`202 { requestId }`) and progress events are multiplexed through a shared event bus. This eliminates the browser 6-connection limit that previously caused gallery hangs during concurrent generation. CLI clients that do not send `async: true` still receive per-request SSE streams for backward compatibility.
@@ -1,24 +1,80 @@
1
- import { existsSync, readFileSync } from "fs";
2
- import { dirname, join, relative } from "path";
1
+ import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, cpSync } from "fs";
2
+ import { dirname, join, relative, basename, extname, resolve } from "path";
3
+ import { tmpdir } from "os";
3
4
  import { fileURLToPath } from "url";
4
5
  import { parseArgs } from "../lib/args.js";
5
6
  import { die, json, out } from "../lib/output.js";
6
7
  const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
7
- const SKILL_PATH = join(ROOT, "skills", "ima2", "SKILL.md");
8
+ const SKILLS_DIR = join(ROOT, "skills");
8
9
  const PACKAGE_PATH = join(ROOT, "package.json");
10
+ const KNOWN_SKILLS = {
11
+ ima2: {
12
+ dir: "ima2",
13
+ description: "Core CLI reference, prompting protocol, provider routing",
14
+ },
15
+ front: {
16
+ dir: "ima2-front",
17
+ description: "Frontend implementation: assets, motion, responsive, a11y, anti-slop",
18
+ },
19
+ uiux: {
20
+ dir: "ima2-uiux",
21
+ description: "Design direction discovery, UX judgment, image-first ism workflow",
22
+ },
23
+ };
9
24
  const HELP = `
10
- ima2 skill [path] [--json]
25
+ ima2 skill [<name>] [path|refs|ref <name>|install] [--json]
11
26
 
12
- Print the packaged ima2 Markdown skill for agents.
27
+ Print packaged ima2 Markdown skills and reference modules for agents.
28
+ Each skill ships a SKILL.md + a references/ directory. Reference modules
29
+ are loaded by agents via relative paths from the SKILL.md.
13
30
 
14
31
  Commands:
15
- ima2 skill Print skills/ima2/SKILL.md
16
- ima2 skill path Print resolved skill path
17
- ima2 skill --json Print a JSON wrapper around the Markdown skill
32
+ ima2 skill Print skills/ima2/SKILL.md (core)
33
+ ima2 skill front Print skills/ima2-front/SKILL.md (frontend)
34
+ ima2 skill uiux Print skills/ima2-uiux/SKILL.md (design)
35
+ ima2 skill ls List all available skills
36
+ ima2 skill <name> path Print skill file path
37
+
38
+ Install to agent skill directory:
39
+ ima2 skill install --dir <path> Copy all skills to <path>/
40
+ ima2 skill install front --dir <path> Copy only the frontend skill
41
+ ima2 skill install uiux --dir <path> Copy only the design skill
42
+ ima2 skill install --tmp Copy to $TMPDIR/ima2-skills/ (ephemeral)
43
+ ima2 skill install front --tmp Copy one skill to temp dir
44
+
45
+ Each skill is a directory (e.g. <path>/ima2-front/) with SKILL.md +
46
+ references/. The agent determines its own skill path and passes --dir.
47
+ Use --tmp when no persistent path is available.
48
+
49
+ Reference modules (ad-hoc, without install):
50
+ ima2 skill <name> refs List reference modules for a skill
51
+ ima2 skill <name> ref <name> Print one reference module
52
+
53
+ Examples:
54
+ ima2 skill install --dir ~/.codex/skills Install all skills
55
+ ima2 skill install front --dir ~/.codex/skills Install frontend only
56
+ ima2 skill install --tmp Install to temp dir
57
+ ima2 skill front refs List frontend references
58
+ ima2 skill front ref motion Print references/motion.md
59
+ ima2 skill uiux ref design-isms Print references/design-isms.md
60
+
61
+ Agent integration:
62
+ The agent is responsible for resolving its own skill directory path.
63
+ Pass that path to --dir. After install, the agent reads SKILL.md and
64
+ follows references/ paths natively — no stdout piping needed.
65
+
66
+ Options:
67
+ --json Print JSON wrapper
68
+ --dir <path> Target directory for install
69
+ --tmp Use $TMPDIR/ima2-skills/ as target
70
+ -h, --help Show help
18
71
  `;
19
72
  const FLAGS = {
20
73
  json: { type: "boolean" },
21
74
  help: { short: "h", type: "boolean" },
75
+ "with-refs": { type: "boolean" },
76
+ dir: { type: "string" },
77
+ tmp: { type: "boolean" },
22
78
  };
23
79
  function readPackageVersion() {
24
80
  try {
@@ -29,30 +85,293 @@ function readPackageVersion() {
29
85
  return "?";
30
86
  }
31
87
  }
32
- function readSkill() {
33
- if (!existsSync(SKILL_PATH)) {
34
- die(5, `packaged skill not found: ${SKILL_PATH}`);
88
+ function resolveSkillPath(name) {
89
+ const entry = KNOWN_SKILLS[name];
90
+ if (!entry)
91
+ die(2, `unknown skill: "${name}". Available: ${Object.keys(KNOWN_SKILLS).join(", ")}`);
92
+ return join(SKILLS_DIR, entry.dir, "SKILL.md");
93
+ }
94
+ function readSkill(path) {
95
+ if (!existsSync(path)) {
96
+ die(5, `packaged skill not found: ${path}`);
97
+ }
98
+ return readFileSync(path, "utf-8");
99
+ }
100
+ function discoverRefs(skillDir) {
101
+ const refsDir = join(skillDir, "references");
102
+ if (!existsSync(refsDir))
103
+ return [];
104
+ const entries = [];
105
+ function walk(dir, prefix) {
106
+ for (const item of readdirSync(dir)) {
107
+ const full = join(dir, item);
108
+ const st = statSync(full);
109
+ if (st.isDirectory()) {
110
+ walk(full, prefix ? `${prefix}/${item}` : item);
111
+ }
112
+ else if (extname(item) === ".md") {
113
+ const nameNoExt = basename(item, ".md");
114
+ const qualName = prefix ? `${prefix}/${nameNoExt}` : nameNoExt;
115
+ const relFile = prefix ? `${prefix}/${item}` : item;
116
+ entries.push({
117
+ name: qualName,
118
+ file: relFile,
119
+ relPath: `references/${relFile}`,
120
+ absPath: full,
121
+ lines: readFileSync(full, "utf-8").split("\n").length,
122
+ });
123
+ }
124
+ }
35
125
  }
36
- return readFileSync(SKILL_PATH, "utf-8");
126
+ walk(refsDir, "");
127
+ return entries.sort((a, b) => a.name.localeCompare(b.name));
128
+ }
129
+ function resolveRef(skillDir, refName) {
130
+ const refs = discoverRefs(skillDir);
131
+ const exact = refs.find((r) => r.name === refName);
132
+ if (exact)
133
+ return exact;
134
+ const byBase = refs.filter((r) => {
135
+ const parts = r.name.split("/");
136
+ return parts[parts.length - 1] === refName;
137
+ });
138
+ if (byBase.length === 1)
139
+ return byBase[0];
140
+ const partial = refs.filter((r) => r.name.includes(refName));
141
+ if (partial.length === 1)
142
+ return partial[0];
143
+ return null;
144
+ }
145
+ // ── Install helpers ──
146
+ function copySkillDir(srcDir, destDir) {
147
+ mkdirSync(destDir, { recursive: true });
148
+ cpSync(srcDir, destDir, { recursive: true });
149
+ // Count files copied
150
+ let count = 0;
151
+ function countFiles(dir) {
152
+ for (const item of readdirSync(dir)) {
153
+ const full = join(dir, item);
154
+ if (statSync(full).isDirectory())
155
+ countFiles(full);
156
+ else
157
+ count++;
158
+ }
159
+ }
160
+ countFiles(destDir);
161
+ return { files: count };
162
+ }
163
+ function resolveInstallDir(args) {
164
+ if (args.dir)
165
+ return resolve(String(args.dir));
166
+ if (args.tmp)
167
+ return join(tmpdir(), "ima2-skills");
168
+ return null;
37
169
  }
170
+ function installSkills(targetBase, only, asJson) {
171
+ const results = [];
172
+ for (const [name, info] of Object.entries(KNOWN_SKILLS)) {
173
+ if (only && !only.includes(name))
174
+ continue;
175
+ const srcDir = join(SKILLS_DIR, info.dir);
176
+ if (!existsSync(join(srcDir, "SKILL.md")))
177
+ continue;
178
+ const destDir = join(targetBase, info.dir);
179
+ const { files } = copySkillDir(srcDir, destDir);
180
+ results.push({ name, dir: info.dir, dest: destDir, files });
181
+ }
182
+ if (results.length === 0 && only) {
183
+ die(2, `unknown skill: "${only.join(", ")}". Available: ${Object.keys(KNOWN_SKILLS).join(", ")}`);
184
+ }
185
+ if (asJson) {
186
+ json({
187
+ installed: results.map((r) => ({
188
+ name: r.name,
189
+ dir: r.dir,
190
+ path: r.dest,
191
+ files: r.files,
192
+ })),
193
+ target: targetBase,
194
+ });
195
+ }
196
+ else {
197
+ out(`\n Installed ${results.length} ima2 skill${results.length === 1 ? "" : "s"} to ${targetBase}\n`);
198
+ for (const r of results) {
199
+ out(` ✓ ${r.dir.padEnd(12)} ${r.files} files → ${r.dest}`);
200
+ }
201
+ out(`\n Agents can now read SKILL.md + references/ via filesystem paths.\n`);
202
+ }
203
+ }
204
+ // ── Main command ──
38
205
  export default async function skillCmd(argv) {
39
206
  const args = parseArgs(argv, { flags: FLAGS });
40
207
  if (args.help) {
41
208
  out(HELP);
42
209
  return;
43
210
  }
44
- if (args.positional[0] === "path") {
45
- out(SKILL_PATH);
211
+ const positional = args.positional;
212
+ // ima2 skill install — copy skills to filesystem
213
+ if (positional[0] === "install") {
214
+ const targetBase = resolveInstallDir(args);
215
+ if (!targetBase) {
216
+ die(2, "specify --dir <path> or --tmp. The agent determines its own skill directory path.\n\n Examples:\n ima2 skill install --dir ~/.codex/skills\n ima2 skill install front --dir ./skills\n ima2 skill install --tmp");
217
+ }
218
+ // Collect optional skill names after "install"
219
+ const installNames = positional.slice(1).filter((p) => KNOWN_SKILLS[p]);
220
+ installSkills(targetBase, installNames.length > 0 ? installNames : null, !!args.json);
221
+ return;
222
+ }
223
+ // ima2 skill ls — list available skills
224
+ if (positional[0] === "ls" || positional[0] === "list") {
225
+ if (args.json) {
226
+ const skills = Object.entries(KNOWN_SKILLS).map(([name, info]) => {
227
+ const skillPath = join(SKILLS_DIR, info.dir, "SKILL.md");
228
+ return {
229
+ name,
230
+ dir: info.dir,
231
+ description: info.description,
232
+ path: relative(ROOT, skillPath),
233
+ installed: existsSync(skillPath),
234
+ };
235
+ });
236
+ json({ skills });
237
+ }
238
+ else {
239
+ out("\n Available ima2 skills:\n");
240
+ for (const [name, info] of Object.entries(KNOWN_SKILLS)) {
241
+ const skillPath = join(SKILLS_DIR, info.dir, "SKILL.md");
242
+ const status = existsSync(skillPath) ? "✓" : "✗";
243
+ out(` ${status} ${name.padEnd(8)} ${info.description}`);
244
+ }
245
+ out(`\n Usage: ima2 skill <name> Print a skill`);
246
+ out(` ima2 skill <name> path Print its file path`);
247
+ out(` ima2 skill <name> refs List reference modules`);
248
+ out(` ima2 skill install [<name>] --dir <path>\n`);
249
+ }
250
+ return;
251
+ }
252
+ // Determine which skill to read
253
+ let skillName = "ima2";
254
+ let wantPath = false;
255
+ let wantRefs = false;
256
+ let wantRef = null;
257
+ if (positional.length > 0 && positional[0] !== "path" && positional[0] !== "refs" && positional[0] !== "ref" && KNOWN_SKILLS[positional[0]]) {
258
+ skillName = positional[0];
259
+ if (positional[1] === "path")
260
+ wantPath = true;
261
+ else if (positional[1] === "refs")
262
+ wantRefs = true;
263
+ else if (positional[1] === "ref" && positional[2])
264
+ wantRef = positional.slice(2).join("/");
265
+ }
266
+ else if (positional[0] === "path") {
267
+ wantPath = true;
268
+ }
269
+ else if (positional[0] === "refs") {
270
+ wantRefs = true;
271
+ }
272
+ else if (positional[0] === "ref" && positional[1]) {
273
+ wantRef = positional.slice(1).join("/");
274
+ }
275
+ const targetPath = resolveSkillPath(skillName);
276
+ if (wantPath) {
277
+ out(targetPath);
278
+ return;
279
+ }
280
+ const skillDir = dirname(targetPath);
281
+ // ima2 skill <name> refs — list reference modules
282
+ if (wantRefs) {
283
+ const refs = discoverRefs(skillDir);
284
+ if (refs.length === 0) {
285
+ if (args.json) {
286
+ json({ skill: skillName, refs: [] });
287
+ }
288
+ else {
289
+ out(`\n No reference modules for skill "${skillName}".\n`);
290
+ }
291
+ return;
292
+ }
293
+ if (args.json) {
294
+ json({
295
+ skill: skillName,
296
+ refs: refs.map((r) => ({
297
+ name: r.name,
298
+ file: r.relPath,
299
+ lines: r.lines,
300
+ })),
301
+ });
302
+ }
303
+ else {
304
+ out(`\n Reference modules for "${skillName}" (${refs.length} files):\n`);
305
+ for (const r of refs) {
306
+ out(` ${r.name.padEnd(30)} ${String(r.lines).padStart(4)} lines`);
307
+ }
308
+ out(`\n Load one: ima2 skill ${skillName} ref <name>`);
309
+ out(` Install: ima2 skill install ${skillName} --dir <path>\n`);
310
+ }
311
+ return;
312
+ }
313
+ // ima2 skill <name> ref <refname> — print one reference module
314
+ if (wantRef) {
315
+ const ref = resolveRef(skillDir, wantRef);
316
+ if (!ref) {
317
+ const refs = discoverRefs(skillDir);
318
+ const available = refs.map((r) => r.name).join(", ");
319
+ die(2, `reference "${wantRef}" not found in skill "${skillName}". Available: ${available}`);
320
+ }
321
+ const content = readFileSync(ref.absPath, "utf-8");
322
+ if (args.json) {
323
+ json({
324
+ skill: skillName,
325
+ ref: ref.name,
326
+ file: ref.relPath,
327
+ lines: ref.lines,
328
+ content,
329
+ });
330
+ }
331
+ else {
332
+ out(content.replace(/\n$/, ""));
333
+ }
334
+ return;
335
+ }
336
+ // --with-refs: bundle SKILL.md + all reference modules (hidden, discouraged)
337
+ if (args["with-refs"]) {
338
+ const content = readSkill(targetPath);
339
+ // Inline bundle — kept for backward compat but not recommended
340
+ const refs = discoverRefs(skillDir);
341
+ let bundled = content.replace(/\n$/, "");
342
+ if (refs.length > 0) {
343
+ bundled += "\n\n---\n\n# Bundled Reference Modules\n";
344
+ for (const ref of refs) {
345
+ const rc = readFileSync(ref.absPath, "utf-8").replace(/\n$/, "");
346
+ bundled += `\n## [ref: ${ref.name}] (${ref.relPath})\n\n${rc}\n`;
347
+ }
348
+ }
349
+ process.stderr.write(" hint: prefer 'ima2 skill install --dir <path>' — agents read references natively from disk.\n");
350
+ if (args.json) {
351
+ json({
352
+ name: skillName === "ima2" ? "ima2" : `ima2-${skillName}`,
353
+ format: "markdown-skill-bundled",
354
+ formatVersion: "1",
355
+ packageVersion: readPackageVersion(),
356
+ path: relative(ROOT, targetPath),
357
+ refCount: discoverRefs(skillDir).length,
358
+ source: "package",
359
+ content: bundled,
360
+ });
361
+ }
362
+ else {
363
+ out(bundled.replace(/\n$/, ""));
364
+ }
46
365
  return;
47
366
  }
48
- const content = readSkill();
367
+ const content = readSkill(targetPath);
49
368
  if (args.json) {
50
369
  json({
51
- name: "ima2",
370
+ name: skillName === "ima2" ? "ima2" : `ima2-${skillName}`,
52
371
  format: "markdown-skill",
53
372
  formatVersion: "1",
54
373
  packageVersion: readPackageVersion(),
55
- path: relative(ROOT, SKILL_PATH),
374
+ path: relative(ROOT, targetPath),
56
375
  source: "package",
57
376
  content,
58
377
  });
package/bin/ima2.js CHANGED
@@ -315,9 +315,21 @@ function showHelp() {
315
315
  config <sub> Config get/set/ls/path/rm (ima2 config --help)
316
316
  defaults <sub> Inspect/change model defaults (ima2 defaults --help)
317
317
  capabilities Agent capability metadata (ima2 capabilities --help)
318
- skill Print packaged agent skill (ima2 skill --help)
319
318
  ping Ping running server / check health
320
319
 
320
+ Agent skills (SKILL.md + references/):
321
+ skill ls List packaged skills (ima2, front, uiux)
322
+ skill [front|uiux] Print a skill's SKILL.md
323
+ skill [front|uiux] refs List reference modules
324
+ skill [front|uiux] ref <name> Print one reference module
325
+ skill install --dir <path> Install all skills to a directory
326
+ skill install front --dir <path> Install one skill only
327
+ skill install --tmp Install to temp dir (ephemeral)
328
+
329
+ Skills ship as directories (SKILL.md + references/). The agent resolves
330
+ its own skill path and passes it via --dir. After install, the agent
331
+ reads SKILL.md and follows references/ natively from disk.
332
+
321
333
  Options:
322
334
  -v, --version Show version
323
335
  -h, --help Show help
@@ -341,7 +353,15 @@ function showHelp() {
341
353
  ima2 gen "merge" --ref a.png --ref b.png -q high -o out.png
342
354
  ima2 video "a cat playing piano" --duration 10
343
355
  ima2 ls -n 10 Last 10 generations
344
- ima2 skill Print agent usage skill
356
+ ima2 skill Print core agent skill
357
+ ima2 skill ls List all skills (core, front, uiux)
358
+ ima2 skill front Print frontend implementation skill
359
+ ima2 skill uiux Print design direction skill
360
+ ima2 skill front refs List frontend reference modules
361
+ ima2 skill front ref motion Load one reference module
362
+ ima2 skill install --dir <path> Install all skills
363
+ ima2 skill install front --dir <path> Install frontend skill only
364
+ ima2 skill install --tmp Install to temp dir
345
365
  ima2 capabilities --json Inspect supported models/options
346
366
  ima2 defaults --json Inspect running server defaults
347
367
  ima2 ping Health check
@@ -63,10 +63,12 @@ export async function buildHardeningDoctorLines({ root, configFile, fileConfig,
63
63
  kind: "info",
64
64
  text: `Card News: ${runtimeConfig.features.cardNews ? "enabled" : "disabled"}`,
65
65
  });
66
- const skillPath = join(root, "skills", "ima2", "SKILL.md");
67
- lines.push(existsSync(skillPath)
68
- ? { kind: "pass", text: `packaged skill found: ${skillPath}` }
69
- : { kind: "fail", text: `packaged skill missing: ${skillPath}` });
66
+ for (const skillDir of ["ima2", "ima2-front", "ima2-uiux"]) {
67
+ const skillPath = join(root, "skills", skillDir, "SKILL.md");
68
+ lines.push(existsSync(skillPath)
69
+ ? { kind: "pass", text: `packaged skill found: ${skillPath}` }
70
+ : { kind: "fail", text: `packaged skill missing: ${skillPath}` });
71
+ }
70
72
  lines.push(probeBetterSqlite(root));
71
73
  const perm = configPermissionLine(configFile, fileConfig);
72
74
  if (perm)
package/config.js CHANGED
@@ -73,6 +73,7 @@ export const config = {
73
73
  // Accept both IMA2_PORT and legacy PORT.
74
74
  port: pickInt(firstDefined(env.IMA2_PORT, env.PORT), fileCfg.server?.port, 3333),
75
75
  host: pickStr(env.IMA2_HOST, fileCfg.server?.host, "127.0.0.1"),
76
+ lanToken: env.IMA2_LAN_TOKEN || "",
76
77
  bodyLimit: pickStr(env.IMA2_BODY_LIMIT, fileCfg.server?.bodyLimit, "50mb"),
77
78
  },
78
79
  limits: {
package/docs/CLI.md CHANGED
@@ -34,9 +34,24 @@ Agents should start from the packaged skill and capability commands instead of g
34
34
 
35
35
  | Command | Description |
36
36
  |---|---|
37
- | `ima2 skill` | Print the packaged Markdown skill from `skills/ima2/SKILL.md` |
38
- | `ima2 skill --json` | Print a JSON wrapper around the Markdown skill content |
39
- | `ima2 skill path` | Print the installed skill file path |
37
+ | `ima2 skill` | Print the core Markdown skill (`skills/ima2/SKILL.md`) |
38
+ | `ima2 skill front` | Print the frontend implementation skill (`skills/ima2-front/SKILL.md`) |
39
+ | `ima2 skill uiux` | Print the design direction skill (`skills/ima2-uiux/SKILL.md`) |
40
+ | `ima2 skill ls` | List all available packaged skills |
41
+ | `ima2 skill --json` | Print a JSON wrapper around the core skill content |
42
+ | `ima2 skill front --json` | Print a JSON wrapper around the frontend skill |
43
+ | `ima2 skill uiux --json` | Print a JSON wrapper around the design skill |
44
+ | `ima2 skill path` | Print the core skill file path |
45
+ | `ima2 skill front path` | Print the frontend skill file path |
46
+ | `ima2 skill uiux path` | Print the design skill file path |
47
+ | `ima2 skill front refs` | List reference modules for the frontend skill (names + line counts) |
48
+ | `ima2 skill uiux refs` | List reference modules for the design skill |
49
+ | `ima2 skill front ref <name>` | Print one reference module by name (e.g. `motion`, `stacks/react`) |
50
+ | `ima2 skill uiux ref <name>` | Print one reference module by name (e.g. `design-isms`) |
51
+ | `ima2 skill install --dir <path>` | Install all skills to agent's skill directory |
52
+ | `ima2 skill install --tmp` | Install to `$TMPDIR/ima2-skills/` (ephemeral fallback) |
53
+ | `ima2 skill front refs --json` | JSON list of reference modules |
54
+ | `ima2 skill front ref motion --json` | JSON wrapper around one reference module |
40
55
  | `ima2 capabilities --json` | Print supported commands, model/quality/reasoning values, and advisory limits |
41
56
  | `ima2 defaults --json` | Print the running server's effective model/reasoning defaults, falling back to local config when no server is reachable |
42
57
  | `ima2 defaults --local --json` | Print local effective defaults without contacting the server |
@@ -404,6 +419,13 @@ ima2 storage status --json
404
419
 
405
420
  # Config
406
421
  ima2 skill --json
422
+ ima2 skill ls
423
+ ima2 skill front --json
424
+ ima2 skill uiux path
425
+ ima2 skill front refs
426
+ ima2 skill front ref motion
427
+ ima2 skill install --dir ~/.codex/skills
428
+ ima2 skill install --tmp
407
429
  ima2 capabilities --json
408
430
  ima2 defaults set model gpt-5.5
409
431
  ima2 defaults set reasoning high
@@ -109,3 +109,32 @@ v1.1.13 Prompt Studio 문제를 고치면서 아래 동작을 계약으로 고
109
109
 
110
110
  ChatGPT 쿠키, OAuth token 파일, API key, raw upstream response, private prompt
111
111
  history, generated base64 data는 공유하지 마세요.
112
+
113
+ ## 캔버스 노트와 i2i 편집
114
+
115
+ 캔버스에서 메모(노트)·화살표·박스를 그리고 저장하면, 노트는 **픽셀이 아니라
116
+ 텍스트 지시**로 모델에 전달됩니다.
117
+
118
+ - 캔버스 버전을 저장하면 주석이 구워진 이미지는 UI 표시/기록용으로만 남고,
119
+ 모델 참조에는 항상 **깨끗한 원본**이 들어갑니다. 현재 이미지를 참조로
120
+ 추가하는 동작도 캔버스 버전이면 원본으로 대체됩니다.
121
+ - 노트 내용은 "캔버스 노트" 칩으로 작성창에 붙습니다. 다음 생성 요청이 이
122
+ 지시문(위치 + 내용 + 마크업 제거 규칙)을 함께 보냅니다. 칩은 언제든 지울
123
+ 수 있습니다.
124
+ - 박스를 그린 뒤 마스크 편집을 실행하면 깨끗한 원본 + 박스 마스크 + 노트
125
+ 지시문으로 편집 API를 호출합니다.
126
+
127
+ 그래도 결과물에 노트/마크업이 남으면:
128
+
129
+ - 노트 문구를 "무엇을 없앨지"가 아니라 **그 자리에 무엇이 보여야 하는지**로
130
+ 적어 보세요. 예: "포스트잇 제거" 대신 "포스트잇이 있던 자리에 연속된 나무
131
+ 책상 표면이 보이게".
132
+ - 같은 요청으로 여러 후보를 생성해 가장 깨끗한 결과를 고르세요.
133
+ - 남은 흔적만 다시 박스로 지정해 한 번 더 편집하세요.
134
+
135
+ ### 구워진 노트 되돌리기
136
+
137
+ 노트가 구워진 캔버스 버전에서는 **구워진 노트 되돌리기**를 사용할 수 있습니다.
138
+ 이후 픽셀 편집이 없으면 깨끗한 원본을 복원하고 저장 당시 노트를 편집 가능한
139
+ 초안으로 되살립니다. 배경 정리 등 픽셀 편집이 뒤에 추가됐다면, 되돌릴 때 해당
140
+ 편집도 사라지므로 먼저 확인합니다.
@@ -115,3 +115,34 @@ When opening an issue, include:
115
115
 
116
116
  Do not share ChatGPT cookies, OAuth token files, API keys, raw upstream
117
117
  responses, private prompt history, or generated base64 data.
118
+
119
+ ## Canvas Notes And I2I Editing
120
+
121
+ When you draw memos (notes), arrows, or boxes on the canvas and save, the
122
+ notes reach the model as **text instructions, not pixels**.
123
+
124
+ - Saving a canvas version keeps the burned-in annotated image for UI display
125
+ and history only; model references always use the **clean source**. Adding
126
+ the current image as a reference also swaps a canvas version for its
127
+ original.
128
+ - Note contents attach to the composer as a removable "Canvas notes" chip, so
129
+ the next generation carries the instructions (position + content + a
130
+ markup-removal rule).
131
+ - Drawing boxes and running the mask edit calls the edit API with the clean
132
+ source, a box mask, and the note instructions.
133
+
134
+ If markup still shows up in a result:
135
+
136
+ - Phrase notes as **what should be visible in that spot**, not only what to
137
+ remove. For example, "show the continuous wooden desk surface where the
138
+ sticky note was" instead of "remove the sticky note".
139
+ - Generate several candidates from the same request and pick the cleanest.
140
+ - Box only the residual trace and run one more mask edit.
141
+
142
+ ### Reverting Baked Notes
143
+
144
+ Canvas versions that contain baked notes offer **Revert baked notes**. When no
145
+ later pixel edit was added, the clean source is restored and the saved notes
146
+ return as an editable draft. If background cleanup or another pixel edit was
147
+ added afterward, Prompt Studio asks for confirmation because reverting also
148
+ discards those pixel edits.
@@ -4,7 +4,7 @@ Generated by `npm run test:inventory` (script: `scripts/classify-tests.mjs`).
4
4
 
5
5
  _Tests considered "runtime-importing" if they import from `../lib/`, `../routes/`, `../bin/`, `../server`, or `../config`._
6
6
 
7
- Total: 207 (runtime: 73, contract: 134)
7
+ Total: 213 (runtime: 76, contract: 137)
8
8
 
9
9
  ## Runtime-importing tests
10
10
  - `tests/agent-mode-auto-planner-contract.test.ts`
@@ -15,11 +15,14 @@ Total: 207 (runtime: 73, contract: 134)
15
15
  - `tests/agent-mode-runtime-contract.test.ts`
16
16
  - `tests/agent-mode-slash-command-contract.test.ts`
17
17
  - `tests/agent-mode-ux-feedback-contract.test.ts`
18
+ - `tests/agent-queue-store-contract.test.ts`
18
19
  - `tests/agent-video-intent.test.ts`
19
20
  - `tests/agy-artifact-fallback.test.ts`
20
21
  - `tests/agy-cli.test.ts`
21
22
  - `tests/api-cache-policy.test.ts`
22
23
  - `tests/api-provider-parity.test.ts`
24
+ - `tests/backend-hardening.test.ts`
25
+ - `tests/backend-input-lan-hardening.test.ts`
23
26
  - `tests/billing-source.test.ts`
24
27
  - `tests/card-news-contract.test.ts`
25
28
  - `tests/card-news-template.test.ts`
@@ -92,6 +95,7 @@ Total: 207 (runtime: 73, contract: 134)
92
95
  - `tests/async-capacity-retry-behavior.test.ts`
93
96
  - `tests/async-capacity-retry-contract.test.js`
94
97
  - `tests/async-stream-subscribe-order.test.js`
98
+ - `tests/backend-hardening-contract.test.js`
95
99
  - `tests/background-cleanup-brush-rasterize.test.js`
96
100
  - `tests/background-cleanup-mask-compose.test.js`
97
101
  - `tests/bin.test.js`
@@ -99,6 +103,7 @@ Total: 207 (runtime: 73, contract: 134)
99
103
  - `tests/browser-attention-badge-contract.test.js`
100
104
  - `tests/canvas-alpha-controls-contract.test.js`
101
105
  - `tests/canvas-annotation-contract.test.js`
106
+ - `tests/canvas-annotation-revert-contract.test.js`
102
107
  - `tests/canvas-apply-merged-contract.test.js`
103
108
  - `tests/canvas-background-cleanup-2x2-contract.test.js`
104
109
  - `tests/canvas-background-cleanup-contract.test.js`
@@ -212,6 +217,7 @@ Total: 207 (runtime: 73, contract: 134)
212
217
  - `tests/toast-stack-contract.test.js`
213
218
  - `tests/ui-error-code-contract.test.js`
214
219
  - `tests/video-continuity-ui-contract.test.js`
220
+ - `tests/video-defaults-persistence-contract.test.js`
215
221
  - `tests/video-gallery-refresh-contract.test.ts`
216
222
  - `tests/video-ui-source-count.test.ts`
217
223
  - `tests/vite-dev-port-contract.test.js`