@maccesar/aiskills 1.16.0 → 1.17.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.
@@ -16,6 +16,11 @@ import {
16
16
  } from '../config.js';
17
17
  import { hasHook } from '../hooks.js';
18
18
  import { readLastCheck } from '../cache.js';
19
+ import {
20
+ isClaudePluginEnabled,
21
+ hasClaudePluginCache,
22
+ pluginProvidesSkill,
23
+ } from '../claude-plugin.js';
19
24
 
20
25
  const CHECK = chalk.green('✓');
21
26
  const CROSS = chalk.red('✗');
@@ -79,8 +84,18 @@ export async function doctorCommand() {
79
84
  for (const platform of platforms) {
80
85
  const missing = [];
81
86
  const broken = [];
87
+ const servedByPlugin = [];
82
88
 
83
89
  for (const skill of SKILLS) {
90
+ // A skill the marketplace plugin provides is *supposed* to have no mirror
91
+ // here — the CLI removes it on purpose to avoid a duplicate entry. Counting
92
+ // it as missing turns a healthy marketplace install into a wall of errors
93
+ // telling the user to run a command that will correctly do nothing.
94
+ if (platform.name === 'claude' && pluginProvidesSkill(skill, homeDir)) {
95
+ servedByPlugin.push(skill);
96
+ continue;
97
+ }
98
+
84
99
  const linkPath = join(platform.skillsDir, skill);
85
100
  try {
86
101
  const stat = lstatSync(linkPath);
@@ -96,15 +111,26 @@ export async function doctorCommand() {
96
111
  }
97
112
  }
98
113
 
99
- const linkedCount = SKILLS.length - missing.length - broken.length;
114
+ const expected = SKILLS.length - servedByPlugin.length;
115
+ const linkedCount = expected - missing.length - broken.length;
116
+ const pluginNote =
117
+ servedByPlugin.length > 0
118
+ ? chalk.dim(` (+${servedByPlugin.length} served by the marketplace plugin)`)
119
+ : '';
100
120
 
101
121
  if (missing.length === 0 && broken.length === 0) {
102
- console.log(` ${CHECK} ${platform.displayName}: ${SKILLS.length}/${SKILLS.length} skills linked`);
122
+ const summary =
123
+ expected === 0
124
+ ? `all ${servedByPlugin.length} skills served by the marketplace plugin`
125
+ : `${linkedCount}/${expected} skills linked${pluginNote}`;
126
+ console.log(` ${CHECK} ${platform.displayName}: ${summary}`);
103
127
  } else {
104
128
  const problems = [];
105
129
  if (missing.length > 0) problems.push(`missing: ${missing.join(', ')}`);
106
130
  if (broken.length > 0) problems.push(`broken: ${broken.join(', ')}`);
107
- console.log(` ${CROSS} ${platform.displayName}: ${linkedCount}/${SKILLS.length} skills linked (${problems.join('; ')})`);
131
+ console.log(
132
+ ` ${CROSS} ${platform.displayName}: ${linkedCount}/${expected} skills linked${pluginNote} (${problems.join('; ')})`
133
+ );
108
134
  issues += missing.length + broken.length;
109
135
 
110
136
  // Collect symlink issues for detailed report
@@ -117,6 +143,27 @@ export async function doctorCommand() {
117
143
  }
118
144
  }
119
145
 
146
+ // Marketplace plugin
147
+ //
148
+ // Worth its own section because the two channels look identical from the
149
+ // outside and produce opposite expectations: with the plugin enabled, absent
150
+ // mirrors are correct; without it, absent mirrors mean no skills at all.
151
+ console.log('');
152
+ console.log(' Marketplace plugin:');
153
+ const pluginEnabled = isClaudePluginEnabled(homeDir);
154
+ const pluginCached = hasClaudePluginCache(homeDir);
155
+
156
+ if (pluginEnabled) {
157
+ console.log(` ${CHECK} Enabled — Claude Code is served by the plugin, mirrors intentionally absent`);
158
+ } else if (pluginCached) {
159
+ console.log(` ${WARN} Not enabled, but a cache directory remains from a previous install`);
160
+ console.log(` ${chalk.dim('Harmless on this version. On aiskills < 1.16.1 it made install skip')}`);
161
+ console.log(` ${chalk.dim('every symlink, leaving Claude Code with no skills. Remove it with:')}`);
162
+ console.log(` ${chalk.cyan('rm -rf ~/.claude/plugins/cache/maccesar-aiskills')}`);
163
+ } else {
164
+ console.log(` ${CHECK} Not installed — skills reach Claude Code through npm mirrors`);
165
+ }
166
+
120
167
  // Symlink issues detail
121
168
  if (symlinkIssues.length > 0) {
122
169
  console.log('');
@@ -79,10 +79,12 @@ export async function skillsCommand(options) {
79
79
  chalk.dim('(agentskills.io standard)')
80
80
  );
81
81
  console.log(
82
- ' ' + chalk.dim('Read directly by Gemini, Codex, Cursor, Cline, Amp, GitHub Copilot, +more.')
82
+ ' ' + chalk.green(''),
83
+ chalk.dim('Gemini, Codex, Cursor, Cline, Amp, GitHub Copilot +more read it directly —')
83
84
  );
85
+ console.log(' ' + chalk.dim('nothing else to configure for them.'));
84
86
  console.log(
85
- ' ' + chalk.dim('Below: extra symlink mirrors for Claude Code.')
87
+ ' ' + chalk.dim('Claude Code needs symlink mirrors, created below.')
86
88
  );
87
89
  console.log('');
88
90
 
@@ -112,10 +114,19 @@ export async function skillsCommand(options) {
112
114
  process.exit(1);
113
115
  }
114
116
 
115
- // Show detected platforms
117
+ // Show detected platforms.
118
+ //
119
+ // Only assistants that need aiskills-managed mirrors appear here, so a bare
120
+ // "Claude Code detected" reads as if the other assistants were not found at
121
+ // all. They were never looked for: Gemini, Codex and the rest read
122
+ // ~/.agents/skills/ directly and are already served by the install itself.
116
123
  if (detectedPlatforms.length > 0) {
117
124
  for (const platform of detectedPlatforms) {
118
- console.log(chalk.green('✓'), `${platform.displayName} detected`);
125
+ console.log(
126
+ chalk.green('✓'),
127
+ `${platform.displayName} detected`,
128
+ chalk.dim('— needs mirrors, linked below')
129
+ );
119
130
  }
120
131
  console.log('');
121
132
  } else if (isLocal) {
@@ -269,6 +280,10 @@ export async function skillsCommand(options) {
269
280
  spinner.succeed(
270
281
  `${commandsResult.installed.length} slash command${commandsResult.installed.length !== 1 ? 's' : ''} installed`
271
282
  );
283
+ } else if (commandsResult.skipped.length > 0) {
284
+ spinner.info(
285
+ `${commandsResult.skipped.length} slash command${commandsResult.skipped.length !== 1 ? 's' : ''} already provided by the marketplace plugin`
286
+ );
272
287
  } else {
273
288
  spinner.info('No slash commands to install');
274
289
  }
@@ -67,6 +67,10 @@ async function performUpdate(baseDir, repoDir, spinner) {
67
67
  spinner.succeed(
68
68
  `${commandsResult.installed.length} slash command${commandsResult.installed.length !== 1 ? 's' : ''} synced`
69
69
  );
70
+ } else if (commandsResult.skipped.length > 0) {
71
+ spinner.info(
72
+ `${commandsResult.skipped.length} slash command${commandsResult.skipped.length !== 1 ? 's' : ''} already provided by the marketplace plugin`
73
+ );
70
74
  } else {
71
75
  spinner.info('No slash commands to sync');
72
76
  }
package/lib/config.js CHANGED
@@ -64,6 +64,17 @@ export const CLAUDE_PLUGIN_NAME = 'aiskills';
64
64
  export const getClaudePluginSkillsPath = (baseDir = os.homedir()) =>
65
65
  path.join(baseDir, '.claude', 'plugins', 'cache', CLAUDE_PLUGIN_MARKETPLACE, CLAUDE_PLUGIN_NAME);
66
66
 
67
+ // The key Claude Code writes under "enabledPlugins" when the plugin is installed.
68
+ export const CLAUDE_PLUGIN_KEY = `${CLAUDE_PLUGIN_NAME}@${CLAUDE_PLUGIN_MARKETPLACE}`;
69
+
70
+ // Where Claude Code records which plugins are enabled. Both files are consulted
71
+ // because the local variant overrides the shared one, and either may carry the
72
+ // entry depending on how the plugin was installed.
73
+ export const getClaudeSettingsPaths = (baseDir = os.homedir()) => [
74
+ path.join(baseDir, '.claude', 'settings.json'),
75
+ path.join(baseDir, '.claude', 'settings.local.json'),
76
+ ];
77
+
67
78
  // AI platform detection
68
79
  //
69
80
  // Only platforms that need aiskills-managed symlinks appear here.
package/lib/installer.js CHANGED
@@ -135,6 +135,7 @@ export async function installCommands(repoDir, baseDir = os.homedir()) {
135
135
  installed: [],
136
136
  failed: [],
137
137
  removed: [],
138
+ skipped: [],
138
139
  };
139
140
 
140
141
  const legacyLocal = removeCommands(baseDir, { legacyOnly: true });
@@ -147,7 +148,24 @@ export async function installCommands(repoDir, baseDir = os.homedir()) {
147
148
  results.failed.push(...legacyGlobal.failed);
148
149
  }
149
150
 
151
+ const { pluginProvidesCommand } = await import('./claude-plugin.js');
152
+ const commandsDir = getClaudeCommandsDir(baseDir);
153
+
150
154
  for (const cmd of COMMANDS) {
155
+ // Same rule the symlink step applies to skills: when the marketplace plugin
156
+ // already provides the command, installing our own copy makes it show up
157
+ // twice in the autocomplete. Clean up any copy left from before the plugin
158
+ // was installed.
159
+ if (pluginProvidesCommand(cmd, baseDir)) {
160
+ const stalePath = join(commandsDir, `${cmd}.md`);
161
+ if (existsSync(stalePath)) {
162
+ await remove(stalePath);
163
+ results.removed.push(cmd);
164
+ }
165
+ results.skipped.push(cmd);
166
+ continue;
167
+ }
168
+
151
169
  if (await installCommand(repoDir, cmd, baseDir)) {
152
170
  results.installed.push(cmd);
153
171
  } else {
package/lib/symlink.js CHANGED
@@ -75,21 +75,16 @@ async function removePath(path) {
75
75
  * and an additional symlink at ~/.claude/skills/<skill> produces a duplicate
76
76
  * entry in the slash-command autocomplete. This helper lets the symlink step
77
77
  * skip Claude when the plugin already covers it.
78
+ *
79
+ * Requires the plugin to be *enabled*, not merely cached — see `claude-plugin.js`
80
+ * for why the distinction matters.
78
81
  * @param {string} skillName - Skill name (e.g. 'stitch-showcase')
79
82
  * @param {string} baseDir - Optional base directory (defaults to homedir via config)
80
83
  * @returns {Promise<boolean>} True if the plugin provides this skill
81
84
  */
82
85
  export async function isClaudePluginSkillInstalled(skillName, baseDir) {
83
- const { readdir } = await import('fs/promises');
84
- const { getClaudePluginSkillsPath } = await import('./config.js');
85
- const pluginBase = getClaudePluginSkillsPath(baseDir);
86
- if (!existsSync(pluginBase)) return false;
87
- try {
88
- const versions = await readdir(pluginBase);
89
- return versions.some((v) => existsSync(join(pluginBase, v, 'skills', skillName)));
90
- } catch {
91
- return false;
92
- }
86
+ const { pluginProvidesSkill } = await import('./claude-plugin.js');
87
+ return pluginProvidesSkill(skillName, baseDir);
93
88
  }
94
89
 
95
90
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maccesar/aiskills",
3
- "version": "1.16.0",
3
+ "version": "1.17.0",
4
4
  "description": "AI coding assistant skills for Claude Code, Gemini CLI, and Codex CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -53,7 +53,8 @@
53
53
  "files": [
54
54
  "bin/",
55
55
  "lib/",
56
- "skills/"
56
+ "skills/",
57
+ "commands/"
57
58
  ],
58
59
  "publishConfig": {
59
60
  "access": "public"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: humaniza
3
- description: 'Úsalo cuando humanices textos en español (especialmente es-MX) editando emails, documentación, marketing, soporte o textos técnicos, eliminando patrones típicos de IA y devolviendo una versión natural y clara. Triggers: "humanizar", "hacerlo más natural", "quitar tono IA", "hacerlo sonar humano".'
3
+ description: 'Editor de estilo para textos en español (especialmente es-MX): quita los tics de escritura de IA y devuelve prosa natural, concreta y directa, sin cambiar el contenido. Úsalo siempre que alguien quiera revisar, pulir o reescribir un texto en español —emails, documentación, marketing, soporte, posts, textos técnicos aunque nunca diga "humanizar": "esto suena a ChatGPT", "quítale lo robótico", "hazlo más natural", "que no parezca IA", "sonó muy acartonado", "límale el tono", "revísame este correo antes de mandarlo". También cuando el texto mismo trae las señales: rayas por todas partes, "no es X, sino Y", "cabe destacar", listas de tres, abridores como "la verdad es que". No es para: traducir, corregir solo ortografía o gramática, escribir un texto desde cero, ni editar textos en inglés.'
4
4
  allowed-tools: Read, Write, Edit, Grep, Glob, Bash, AskUserQuestion
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: refactoring-ui
3
- description: 'Use when the user asks for UI/UX design advice, design reviews, visual hierarchy improvements, color system help, typography guidance, spacing decisions, depth/shadow usage, image handling, or finishing touches on any interface. Inspired by "Refactoring UI" by Adam Wathan & Steve Schoger. Triggers: "how do I make this look better?", color palettes, type scales, spacing systems, visual hierarchy, design reviews, shadows/depth, image handling, empty states, borders.'
3
+ description: 'Design advice grounded in the principles of "Refactoring UI" by Adam Wathan & Steve Schoger. Use this whenever someone is trying to make an interface look better, or asks about visual hierarchy, color palettes and greys, type scales, spacing systems, shadows and depth, images, empty states, borders, dark mode, motion and hover states, or the layout of a modal, form or table — including when they never say "design" and just paste a component asking why it looks off, or say it "feels cramped", "looks generic", "needs polish", "how do I make this look better?". Applies to any stack: Tailwind, plain CSS, React, Blade, mobile. Not for: writing the copy itself, logo or brand identity work, user research, or debugging CSS that renders wrong.'
4
4
  ---
5
5
 
6
6
  # Refactoring UI Skill
@@ -17,13 +17,13 @@ SKILL.md is not enough.**
17
17
 
18
18
  | Task involves | Required reading |
19
19
  |---|---|
20
- | Color systems, typography, spacing scales defining the system | [references/01-foundations.md](references/01-foundations.md) |
21
- | Layout, white space, visual hierarchy, page-level structure | [references/02-page-mechanics.md](references/02-page-mechanics.md) |
22
- | Color usage, HSL, greys, contrast, shadows, depth, images | [references/03-visual-treatment.md](references/03-visual-treatment.md) |
23
- | Empty states, borders, accents, finishing touches | [references/04-polish.md](references/04-polish.md) |
24
- | Motion, microinteractions, transitions, hover/press states, loading | [references/05-motion.md](references/05-motion.md) |
25
- | Dark mode, multi-theme color tokens, theme toggle, contrast strategy | [references/06-dark-mode.md](references/06-dark-mode.md) |
26
- | Modals, forms, tables — component-specific layout and behavior patterns | [references/07-component-patterns.md](references/07-component-patterns.md) |
20
+ | Project mindset: feature-first work, scope discipline, defining systems, picking a voice | [references/01-foundations.md](references/01-foundations.md) |
21
+ | Visual hierarchy, layout, white space, spacing scales, typography | [references/02-page-mechanics.md](references/02-page-mechanics.md) |
22
+ | Color systems (HSL, shades, greys, contrast), depth and shadows, image handling | [references/03-visual-treatment.md](references/03-visual-treatment.md) |
23
+ | Empty states, borders, accents, decorative defaults, finishing touches | [references/04-polish.md](references/04-polish.md) |
24
+ | Motion, microinteractions, transitions, hover/press states, loading, `prefers-reduced-motion` — *complementary, not from the book* | [references/05-motion.md](references/05-motion.md) |
25
+ | Dark mode, multi-theme color tokens, theme toggle, contrast strategy — *complementary, extrapolates the book's HSL principles* | [references/06-dark-mode.md](references/06-dark-mode.md) |
26
+ | Modals (focus, layout), forms (labels, validation), tables (density, alignment) *complementary, extends the book's principles* | [references/07-component-patterns.md](references/07-component-patterns.md) |
27
27
 
28
28
  ### Step 2 — Output contract
29
29
 
@@ -41,38 +41,17 @@ prepend `FROM_MEMORY (unverified):` to that claim. Do not hide it.
41
41
 
42
42
  ### Banned behaviors
43
43
 
44
- - Inventing ratios, scale values, contrast numbers, or rules not in the references
45
- - ❌ Reproducing the book's prose, illustrations, or examples verbatim — paraphrase only
46
- - ❌ Mixing in advice from unrelated design systems (Material, HIG, Tailwind defaults) as if it were *Refactoring UI* doctrine
47
- - ❌ Marking the answer complete without listing which reference files you read
44
+ These four are where advice like this usually goes wrong, so they're worth naming:
48
45
 
49
- ## When to use
50
-
51
- - User asks "how do I make this look better?"
52
- - User asks about color palettes, type scales, or spacing systems
53
- - User asks about visual hierarchy or emphasis
54
- - User is designing a UI component, page, or layout
55
- - User wants a design review or critique
56
- - User asks about shadows, depth, or layering
57
- - User asks about handling images in UI
58
- - User asks about empty states, borders, or decorative elements
46
+ - Inventing ratios, scale values, contrast numbers, or rules not in the references. A made-up number is indistinguishable from a real one once it's in someone's stylesheet.
47
+ - Reproducing the book's prose, illustrations, or examples verbatim. The references paraphrase on purpose — the original material belongs to its authors and is worth buying.
48
+ - Presenting advice from unrelated design systems (Material, HIG, Tailwind defaults) as *Refactoring UI* doctrine. Those systems are fine; attributing them here makes the citation meaningless.
49
+ - Marking the answer complete without listing which reference files you read. The list is what lets the reader tell a grounded answer from a plausible one.
59
50
 
60
51
  ## Source
61
52
 
62
53
  Inspired by 'Refactoring UI' by Adam Wathan & Steve Schoger — refactoringui.com
63
54
 
64
- ## Reference Files
65
-
66
- | File | Topics |
67
- | ----------------------------------- | --------------------------------------------------------------------------------------- |
68
- | `references/01-foundations.md` | Project mindset: feature-first work, scope discipline, defining systems, picking a voice |
69
- | `references/02-page-mechanics.md` | Visual hierarchy, layout, white space, spacing scales, typography |
70
- | `references/03-visual-treatment.md` | Color systems (HSL, shades, greys, contrast), depth and shadows, image handling |
71
- | `references/04-polish.md` | Finishing touches: borders, accents, empty states, decorative defaults, design intuition |
72
- | `references/05-motion.md` | Motion system (durations, easings), hover/press states, loading patterns, prefers-reduced-motion — **complementary** (not from RUI) |
73
- | `references/06-dark-mode.md` | Dark mode color tokens, text contrast, shadow handling, images, theme toggle — **complementary** (extrapolates RUI's HSL principles) |
74
- | `references/07-component-patterns.md` | Modals (focus, layout), forms (labels, validation), tables (density, alignment) — **complementary** (extends RUI principles to specific components) |
75
-
76
55
  ## Anti-Patterns to Watch For
77
56
 
78
57
  - Designing layouts/navs/shells before designing real features [source: references/01-foundations.md]
@@ -84,6 +63,25 @@ Inspired by 'Refactoring UI' by Adam Wathan & Steve Schoger — refactoringui.co
84
63
  - Designing with placeholder images instead of real content [source: references/03-visual-treatment.md]
85
64
  - Shrinking a logo down to use as a favicon [source: references/03-visual-treatment.md]
86
65
  - Using preprocessor `lighten()` / `darken()` to derive shades [source: references/03-visual-treatment.md]
66
+ - Leaving an empty state as a bare "No data" rectangle — it's often the first thing a new user sees [source: references/04-polish.md]
67
+ - Rendering tabs, filters or pagination that have nothing to operate on (reads as broken) [source: references/04-polish.md]
68
+ - Reaching for a border when a shadow, a second background color, or more space separates better [source: references/04-polish.md]
69
+ - Shipping browser-default bullets, checkboxes and radios [source: references/04-polish.md]
70
+ - Animating layout properties (`width`, `top`, `margin`) instead of `transform` / `opacity` [source: references/05-motion.md]
71
+ - Inventing a duration per component instead of picking from a fixed motion scale [source: references/05-motion.md]
72
+ - Scaling a whole element up on hover (1.05×), or animating font size [source: references/05-motion.md]
73
+ - Showing a spinner before ~150ms of waiting [source: references/05-motion.md]
74
+ - Ignoring `prefers-reduced-motion` [source: references/05-motion.md]
75
+ - Pure `#000` for the canvas, or pure white for text [source: references/06-dark-mode.md]
76
+ - Producing dark mode with `filter: invert(1)` [source: references/06-dark-mode.md]
77
+ - Reusing the light-mode brand color in dark mode without rechecking contrast [source: references/06-dark-mode.md]
78
+ - Applying the theme after first paint (flash of the wrong theme on every load) [source: references/06-dark-mode.md]
79
+ - Keeping every light-mode shadow unchanged on a dark canvas, where it's invisible [source: references/06-dark-mode.md]
80
+ - Using a placeholder as the only label [source: references/07-component-patterns.md]
81
+ - Validating a field on every keystroke instead of on blur [source: references/07-component-patterns.md]
82
+ - A modal with no focus trap, or centered vertically instead of anchored near the top [source: references/07-component-patterns.md]
83
+ - Left-aligning numeric columns, which hides magnitude [source: references/07-component-patterns.md]
84
+ - A translucent sticky table header, with rows showing through it [source: references/07-component-patterns.md]
87
85
 
88
86
  ## Attribution
89
87
 
@@ -93,53 +93,25 @@ repositories. Very often it's also **one working session**: someone adds an
93
93
  endpoint on the API side and, without switching context, wires the app that
94
94
  consumes it. The work is a single thought; only the folders are separate.
95
95
 
96
- **Install from inside each repo, separately.** Open the backend, install; open the
97
- app, install. It's a one-time act per repo and it's worth doing from the right
98
- place: sitting inside the project means its own context file loads, its own MCP
99
- servers connect, and its stack-specific skills detect themselves. Installing a
96
+ **Install from inside each repo, separately**, and let each keep its own
97
+ `status.md` two repos have two branches, two deploy states and two histories, so
98
+ one shared file goes stale on whichever side isn't being edited. Installing a
100
99
  repo's notes from its sibling means describing a project you're looking at from
101
- outside and the result reads like it, because the detail that makes `context.md`
102
- useful is exactly what you don't see from across the fence.
103
-
104
- Each keeps its own `status.md` — two repos have two branches, two deploy states
105
- and two histories, and one shared file would go stale on whichever side isn't
106
- being edited.
107
-
108
- Put the sibling's **path** in the header, not just its name, so whoever reads it
109
- next can actually go there:
110
-
111
- ```markdown
112
- **Sibling:** `../../Apps/EM Industrial` (Titanium client) — waiting on
113
- `/work-orders/{id}/progress`, not built here yet.
114
- ```
115
-
116
- **Once both are installed, updating them from one session is fine** — that's the
117
- day-to-day case, and it's different from installing. The files already exist and
118
- carry the project's own vocabulary; you're appending what changed, not inventing
119
- a description of a repo you can't see.
100
+ outside, and the result reads like it.
120
101
 
121
102
  **When a session touched both, close both.** This is the part that gets skipped,
122
103
  and it's where the two halves drift into separate realities: the mobile notes say
123
104
  "waiting on the API" for three weeks while the backend's notes never mention that
124
- anything is waiting. If you added the endpoint and consumed it in the same
125
- session, both `status.md` files changed write both before finishing.
126
-
127
- Even then, write the sibling's status from what you did to it, not from what you
128
- assume about it. "Added the client call for `/work-orders/{id}/progress`" is
129
- something you know. "The app is now feature-complete for E5" is something the app
130
- would have to tell you.
131
-
132
- If the sibling isn't reachable from where you're working, say so in the handoff
133
- rather than guessing at its state. "Endpoint added here; the app side needs its
134
- status updated, I couldn't reach that repo" is honest and actionable. A confident
135
- claim about a repo you didn't open is neither.
105
+ anything is waiting. Write each side from what you did to it, not from what you
106
+ assume about it and if you couldn't reach the sibling, say that in the handoff
107
+ instead of guessing at its state.
136
108
 
137
109
  A **monorepo** is the opposite case and takes the opposite answer — one
138
110
  `docs/project/` at the root, packages as headings — because one repo has one branch,
139
111
  one deploy and one history to describe. And `status.md` being rewritten whole every
140
112
  session makes it **conflict-prone the moment a second person or branch touches it**.
141
- Both cases are in `references/file-layout.md`; neither comes up on a project with one
142
- person and one branch, which is most of them.
113
+ All three cases, with the sibling header format, are in `references/file-layout.md`;
114
+ none comes up on a project with one person and one branch, which is most of them.
143
115
 
144
116
  ### Why status.md is not loaded at startup
145
117
 
@@ -3,6 +3,19 @@
3
3
  Read this when installing the convention in a project or migrating one that keeps
4
4
  its notes somewhere else.
5
5
 
6
+ <!-- TOC-START -->
7
+ ## Contents
8
+
9
+ - [Layout](#layout)
10
+ - [The pointer block](#the-pointer-block)
11
+ - [Two variants](#two-variants)
12
+ - [Repos that come in pairs](#repos-that-come-in-pairs)
13
+ - [Templates](#templates)
14
+ - [Migrating an existing project](#migrating-an-existing-project)
15
+ - [Sizing](#sizing)
16
+
17
+ <!-- TOC-END -->
18
+
6
19
  ## Layout
7
20
 
8
21
  ```
@@ -87,6 +100,41 @@ Resolve conflicts by **keeping both sides and merging by section** — two peopl
87
100
  nobody gets back. If the friction is constant rather than occasional, the team wants
88
101
  a heading per workstream inside the file. Still one file.
89
102
 
103
+ ## Repos that come in pairs
104
+
105
+ A web backend and the mobile client that consumes it. The mechanics of installing
106
+ and closing both are in the skill; what follows is the detail that didn't need to
107
+ load every session.
108
+
109
+ **Install from inside each repo, separately.** Open the backend, install; open the
110
+ app, install. It's a one-time act per repo and it's worth doing from the right
111
+ place: sitting inside the project means its own context file loads, its own MCP
112
+ servers connect, and its stack-specific skills detect themselves. Installing a
113
+ repo's notes from its sibling means describing a project you're looking at from
114
+ outside — and the result reads like it, because the detail that makes `context.md`
115
+ useful is exactly what you don't see from across the fence.
116
+
117
+ Put the sibling's **path** in the header of `status.md`, not just its name, so
118
+ whoever reads it next can actually go there:
119
+
120
+ ```markdown
121
+ **Sibling:** `../../Apps/EM Industrial` (Titanium client) — waiting on
122
+ `/work-orders/{id}/progress`, not built here yet.
123
+ ```
124
+
125
+ **Once both are installed, updating them from one session is fine** — that's the
126
+ day-to-day case, and it's different from installing. The files already exist and
127
+ carry the project's own vocabulary; you're appending what changed, not inventing a
128
+ description of a repo you can't see.
129
+
130
+ Write the sibling's status from what you did to it, not from what you assume about
131
+ it. "Added the client call for `/work-orders/{id}/progress`" is something you know.
132
+ "The app is now feature-complete for E5" is something the app would have to tell
133
+ you. And if the sibling isn't reachable from where you're working, say so in the
134
+ handoff rather than guessing: "endpoint added here; the app side needs its status
135
+ updated, I couldn't reach that repo" is honest and actionable. A confident claim
136
+ about a repo you didn't open is neither.
137
+
90
138
  ## Templates
91
139
 
92
140
  ### `docs/project/status.md`
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: stitch-showcase
3
- description: 'Use when the user has Google Stitch design exports and wants to do anything with them build a gallery, organize screens, generate a navigable showcase, process zip files, or browse designs. Triggers: "organiza mis diseños de Stitch", "arma el muestrario", "organize my Stitch designs", "build the showcase", "tengo los zips de Stitch", "mis exports de Stitch", or any mention of Stitch exports, screen.png + code.html pairs, or design zip files. Also: "optimiza el showcase", "mejora las descripciones", "enrich the showcase" to improve an existing showcase.'
3
+ description: 'Turns Google Stitch design exports (zips holding `code.html` + `screen.png`) into a navigable showcase gallery, viewer, component catalog in about three seconds, and enriches it on demand. Use this for anything involving those exports: "organiza mis diseños de Stitch", "arma el muestrario", "organize my Stitch designs", "build the showcase", "tengo los zips de Stitch", "mis exports de Stitch", or a bare path to a folder of design zips. Also for maintaining one that already exists: "optimiza el showcase", "mejora las descripciones", "agrega estas pantallas nuevas", "el cliente pidió otra pantalla", "estandariza los navbars", "make all the footers the same". Not for: Figma or Sketch exports, loose screenshots, redesigning the screens themselves, or building the real app from them.'
4
4
  ---
5
5
 
6
6
  # stitch-showcase
7
7
 
8
8
  Converts Google Stitch exports (zips with `code.html` + `screen.png`) into a navigable showcase with `index.html` + `viewer.html` + `catalog.html`.
9
9
 
10
- **Architecture**: Python script generates all HTML from pre-built templates in ~3 seconds. AI enrichment (descriptions, sections, hero text) is **optional and on-demand** — only when the user asks to optimize. The AI NEVER writes index.html or viewer.html from scratch.
10
+ **Architecture**: a Python script generates all HTML from pre-built templates in ~3 seconds. AI enrichment (descriptions, sections, hero text) is **optional and on-demand** — only when the user asks to optimize.
11
11
 
12
12
  ## Prerequisites
13
13
 
@@ -39,7 +39,11 @@ digraph showcase {
39
39
  }
40
40
  ```
41
41
 
42
- **CRITICAL**: Always run `build_showcase.py` WITHOUT `--context` to generate HTMLs from templates. The `--context` flag is ONLY for debugging/inspecting the data JSON. NEVER have the AI write index.html or viewer.html manually — the templates handle layout, grid, viewer, theme, tabs, search, and all interactive features.
42
+ Two things about this that are easy to get wrong and expensive to undo:
43
+
44
+ Run `build_showcase.py` **without** `--context`, since that's the invocation that writes the HTML. `--context` only dumps the data JSON for inspection, so a run with it leaves you with no showcase and no error saying why.
45
+
46
+ Don't write `index.html` or `viewer.html` by hand. The templates already carry the layout, grid, viewer, theme, tabs, search and every interactive behavior, and every build regenerates both files — so hand-written HTML costs a lot to produce and disappears on the next rebuild.
43
47
 
44
48
  ## Mode 1: Build (default — instant)
45
49