<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DevOps · Loosely Decoupled</title>
    <link>https://looselydecoupled.com/topics/devops/</link>
    <description>Delivery and operations practice, judged by what survives an incident.</description>
    <language>en-us</language>
    
    <copyright>© 2026 Emmanuel Tsouris</copyright>
    <lastBuildDate>Sat, 17 Oct 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://looselydecoupled.com/topics/devops/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Why Doesn&#39;t My Claude Code Plugin Load in a Cloud Session?</title>
      <link>https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/</link>
      <guid isPermaLink="true">https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/</guid>
      <pubDate>Sat, 17 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Emmanuel Tsouris</dc:creator>
      <category>Automation</category>
      <category>DevOps</category>
      
      <description>The install worked in my test cloud session only because it had the plugin&#39;s private repository attached. Sessions without it failed silently.</description>
      <content:encoded><![CDATA[<div class="answer" data-answer="true">
  <p class="answer__label" data-pagefind-ignore>TL;DR</p>
  <p>Claude Code cloud sessions don&rsquo;t install the plugins a repository&rsquo;s <code>.claude/settings.json</code> turns on, and their GitHub proxy only serves the repositories attached to the session. So a plugin from a private marketplace doesn&rsquo;t load there. Its skill still can. Zip the skill folder, upload it under <strong>Customize</strong> &gt; <strong>Skills</strong> on claude.ai, and turn it on. In a new cloud session with nothing attached, it showed up as <code>anthropic-skills:&lt;skill&gt;</code>. The trade-offs: you upload again for every release, and the skill loads in every session on your account. A missing skill doesn&rsquo;t raise an error. Claude still answers, but without the skill&rsquo;s process, so ask the session to list its skills.</p>

</div>

<p>I was working on a project spread across several private repositories, and I created a skill I wanted to share between them as a Claude Code plugin. My plan was a private GitHub repository that&rsquo;s both the plugin and its marketplace, so the skill stays in sync on my laptop and in cloud sessions. Most of my work happens in Claude Code on the web, because I like that it keeps the work off my laptop, runs in isolation, and is there on my phone. I tested the install commands by hand in a cloud session, then put them in the environment&rsquo;s setup script, and the test worked. In the next real session the skill never loaded, and I got no error, not even a small one. Here&rsquo;s how I tracked down the cause, what I tried that didn&rsquo;t work, and the two workarounds that did.</p>
<h2 id="skill-doesnt-load-in-a-cloud-session-and-theres-no-error">Skill doesn&rsquo;t load in a cloud session, and there&rsquo;s no error<a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#skill-doesnt-load-in-a-cloud-session-and-theres-no-error"
     aria-label="Link to section: Skill doesn’t load in a cloud session, and there’s no error" data-pagefind-ignore></a>
</h2>
<p>The session starts normally. You paste the notes the skill is built for, and Claude gives a reasonable, generic answer instead of following the skill.</p>
<p>That&rsquo;s what makes it easy to miss. When a skill doesn&rsquo;t load, Claude still answers, and nothing hints that a skill should have run. The only clue I had was that the answer was good but didn&rsquo;t follow the skill&rsquo;s steps.</p>
<p>My test had passed only because of how that session was set up. The session I tested in had the skills repository attached as one of its sources. The real sessions had only the repository I was working in. I also never confirmed that the saved setup script still had the install commands in it. Its log only showed output from another installer.</p>
<p>A second session found the cause. I messaged the failing session from the one where I built the plugin and asked it to inspect its own environment: the plugin list, the contents of <code>~/.claude</code>, and the settings files. It reported that no marketplace had ever been added.</p>
<h2 id="cloud-session-cant-reach-a-private-repository-that-isnt-attached">Cloud session can&rsquo;t reach a private repository that isn&rsquo;t attached<a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#cloud-session-cant-reach-a-private-repository-that-isnt-attached"
     aria-label="Link to section: Cloud session can’t reach a private repository that isn’t attached" data-pagefind-ignore></a>
</h2>
<p>A cloud session reaches GitHub through a proxy, and the <a href="https://code.claude.com/docs/en/cloud-environments#github-proxy" rel="external noopener" target="_blank">cloud environments documentation<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> says it serves GitHub API requests only for the repositories attached to the session. Git behaved the same way for me. In a fresh session with only the consuming repository attached, I tried to reach the skills repository:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">git</span> <span class="console__token">ls-remote</span> <span class="console__token">https://github.com/&lt;owner&gt;/&lt;skills-repo&gt;.git</span></span></code></pre>
    </div>
  </div><div class="code-block code-block--wrap console__output">
    <p class="console__output-label">Output</p>
    <div class="code-block__scroll"><pre><code><span class="line"><span class="console__token">fatal:</span> <span class="console__token">could</span> <span class="console__token">not</span> <span class="console__token">read</span> <span class="console__token">Username</span> <span class="console__token">for</span> <span class="console__token">&#39;https://github.com&#39;:</span> <span class="console__token">terminal</span> <span class="console__token">prompts</span> <span class="console__token">disabled</span></span></code></pre>
    </div>
  </div></div><p>No credentials were supplied for it, so git asked for a username and gave up, even though the Claude GitHub App had access to that repository on my account. I didn&rsquo;t run the same command against the attached repository to compare. The same session hit the limit again, this time with nothing to do with plugins: <code>npm ci</code> couldn&rsquo;t fetch the project&rsquo;s private SDK, which lives in another private repository that wasn&rsquo;t attached.</p>
<p>Here&rsquo;s what that means for each way of installing the plugin:</p>
<ul>
<li><strong>Repository settings:</strong> The <a href="https://code.claude.com/docs/en/cloud-environments#what-carries-over-from-your-setup" rel="external noopener" target="_blank">documentation<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> lists plugins and marketplaces declared in a repo&rsquo;s <code>.claude/settings.json</code> as not available in cloud sessions. I never put the plugin there, so this is what the docs say, not something I tested.</li>
<li><strong>Setup-script install:</strong> I never confirmed my install commands ran, and the log was empty. If the script can only reach attached repositories, like the session, it had nothing to fetch. I haven&rsquo;t tested that.</li>
<li><strong>Plugin installed mid-session:</strong> In one session, installing from an attached copy left the plugin enabled but not loaded, and the cloud UI has <code>/reload-skills</code> but no <code>/reload-plugins</code>. In an earlier session the same install did make the skill appear. I don&rsquo;t know what was different.</li>
</ul>
<h2 id="plugin-added-on-claudeai-never-reaches-the-session">Plugin added on claude.ai never reaches the session<a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#plugin-added-on-claudeai-never-reaches-the-session"
     aria-label="Link to section: Plugin added on claude.ai never reaches the session" data-pagefind-ignore></a>
</h2>
<p>Your claude.ai account has a plugins page too. Under <strong>Customize</strong> &gt; <strong>Plugins</strong> &gt; <strong>Add</strong> &gt; <strong>Add marketplace</strong> &gt; <strong>Add from a repository</strong>, claude.ai takes a private GitHub repository once the Claude GitHub App can read it. The <a href="https://claude.com/docs/plugins/overview" rel="external noopener" target="_blank">plugins overview<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> says a plugin you add there downloads to Claude Code as a synced plugin, which the <a href="https://code.claude.com/docs/en/plugins/loading#synced-plugins" rel="external noopener" target="_blank">plugin loading reference<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> names <code>&lt;name&gt;@synced</code>. That reference says synced plugins load in Cowork and in signed-in terminal sessions. It doesn&rsquo;t mention cloud sessions, although mine did get Anthropic&rsquo;s synced plugins.</p>
<figure><img src="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/add-marketplace_hu_cc27f6804a5ca6de.png" alt="The Add marketplace dialog on claude.ai, with a URL field that takes a GitHub owner/repo or a Git repository URL." width="704" height="264" loading="lazy" decoding="async"><figcaption>Customize, Plugins, Add, Add marketplace, Add from a repository.</figcaption>
</figure>
<p>The marketplace synced and the plugin showed as added, with no error. In the ten minutes I watched, it never showed up anywhere:</p>
<ul>
<li><strong>Cloud session, started five minutes after the add:</strong> The synced manifest under <code>~/.claude/plugins/synced/</code> listed 10 plugins, all from Anthropic&rsquo;s <code>knowledge-work-plugins</code> marketplace. Mine wasn&rsquo;t there.</li>
<li><strong>Terminal on my Mac, started ten minutes after the add:</strong> It printed <code>Plugins changed. Run /reload-plugins to activate.</code>, and the rewritten manifest listed 4 plugins from the same Anthropic marketplace. Mine wasn&rsquo;t there either.</li>
<li><strong><code>claude plugin marketplace list</code>:</strong> Its <code>From claude.ai:</code> section listed only <strong>My Uploads</strong> and the Anthropic Directory.</li>
</ul>
<p>One caveat: my Mac already had the same plugin installed from the command line, which may have affected that result. The cloud session had no local copy, so its result is clean. <strong>Add</strong> &gt; <strong>Upload plugin</strong> is a second way to put a plugin on your account. I didn&rsquo;t try it.</p>
<p>An open issue, <a href="https://github.com/anthropics/claude-code/issues/92031" rel="external noopener" target="_blank">anthropics/claude-code#92031<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a>, describes the same thing from another account: plugins installed on claude.ai never reach cloud sessions, while skills enabled on the account do. That&rsquo;s the route in the next section.</p>
<h2 id="upload-the-skill-to-your-claudeai-account">Upload the skill to your claude.ai account<a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#upload-the-skill-to-your-claudeai-account"
     aria-label="Link to section: Upload the skill to your claude.ai account" data-pagefind-ignore></a>
</h2>
<p>A skill doesn&rsquo;t need to be a plugin to reach a cloud session. The <a href="https://code.claude.com/docs/en/skills#skills-in-cowork-and-cloud-sessions" rel="external noopener" target="_blank">skills documentation<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> says cloud sessions load the skills enabled for your claude.ai account, synced at session start.</p>
<ol>
<li>
<p>On your machine, zip the skill folder, with its <code>SKILL.md</code> inside the folder. To build the zip from a release tag in the skills repository, run:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">git</span> <span class="console__token">archive</span> <span class="console__token">--format=zip</span> <span class="console__token">--prefix=&lt;skill&gt;/</span> <span class="console__token">-o</span> <span class="console__token">&lt;skill&gt;.zip</span> <span class="console__token">\</span>
</span><span class="line">    <span class="console__token">&lt;tag&gt;:plugins/&lt;plugin&gt;/skills/&lt;skill&gt;</span></span></code></pre>
    </div>
  </div></div></li>
<li>
<p>On claude.ai, go to <strong>Customize</strong> &gt; <strong>Skills</strong> &gt; <strong>Add</strong> &gt; <strong>Upload skill</strong>, upload the zip, and turn the skill on.</p>
</li>
<li>
<p>Start a new cloud session, and ask Claude to list its skills.</p>
</li>
</ol>
<figure><img src="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/upload-skill_hu_769c3e4dfaeb44d3.png" alt="The Upload a skill page on claude.ai. It takes a .md, .zip or .skill file, and a security scan runs on upload." width="1030" height="480" loading="lazy" decoding="async"><figcaption>Customize, Skills, Add, Upload skill.</figcaption>
</figure>
<p>On upload, claude.ai previews the skill&rsquo;s name and description and runs a security scan. In a new cloud session with only the consuming repository attached, the skill showed up as <code>anthropic-skills:&lt;skill&gt;</code>. I checked that in one session. My test of Claude actually using the skill, in the section on confirming it loaded, ran on the copied version from the next section, which has the same files.</p>
<p>There are two costs. claude.ai numbers the skill&rsquo;s versions v1, v2 and so on, not by your git tags, and each release means another upload. It&rsquo;s an account skill, so it loads in every session on the account, not only the repositories that use it. That includes terminal sessions: on my Mac it appeared beside the same skill from the installed plugin, so the session offered it twice. Uninstall the local plugin, or turn the account skill off, to keep one.</p>
<h2 id="copy-the-skill-into-the-cloud-session">Copy the skill into the cloud session<a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#copy-the-skill-into-the-cloud-session"
     aria-label="Link to section: Copy the skill into the cloud session" data-pagefind-ignore></a>
</h2>
<p>If a session needs a specific release, copy the skill in from the skills repository instead. The <a href="https://code.claude.com/docs/en/skills" rel="external noopener" target="_blank">skills documentation<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> says Claude Code watches <code>~/.claude/skills/</code> and picks up a skill added there during the session, without a restart.</p>
<ol>
<li>
<p>Start the cloud session with two repositories attached: the one you&rsquo;re working in and the skills repository.</p>
</li>
<li>
<p>Ask Claude where the skills repository was cloned. It was <code>/home/user/&lt;skills-repo&gt;</code> in my sessions.</p>
</li>
<li>
<p>Ask Claude to copy the skill folder into your user skills directory, changing nothing else:</p>
<div class="console"><div class="code-block code-block--wrap console__command" data-copyable data-copy-label="command">
    <div class="code-block__head"><span class="code-block__lang">Command</span></div>
    <div class="code-block__scroll"><pre data-copy-source><code><span class="line"><span class="prompt">$ </span><span class="console__token">mkdir</span> <span class="console__token">-p</span> <span class="console__token">~/.claude/skills</span>
</span><span class="line"><span class="prompt">$ </span><span class="console__token">cp</span> <span class="console__token">-R</span> <span class="console__token">/home/user/&lt;skills-repo&gt;/plugins/&lt;plugin&gt;/skills/&lt;skill&gt;</span> <span class="console__token">\</span>
</span><span class="line">    <span class="console__token">~/.claude/skills/&lt;skill&gt;</span></span></code></pre>
    </div>
  </div></div></li>
<li>
<p>Ask Claude to list its skills. If <code>~/.claude/skills/</code> didn&rsquo;t exist when the session started, run <code>/reload-skills</code> first, because Claude Code wasn&rsquo;t watching it.</p>
</li>
</ol>
<figure><img src="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/reload-skills_hu_873f416ef627492b.png" alt="The prompt box of a cloud session with /reload-skills typed and its description: pick up skills added or changed on disk during this session." width="845" height="140" loading="lazy" decoding="async">
</figure>
<p>The skill loads as plain <code>&lt;skill&gt;</code>, with no prefix. To copy a specific release, run <code>git -C /home/user/&lt;skills-repo&gt; fetch --tags</code> and <code>git -C /home/user/&lt;skills-repo&gt; checkout &lt;tag&gt;</code> before the copy. I haven&rsquo;t tested that step. The copy doesn&rsquo;t carry over to other sessions.</p>
<p>If every session in a repository needs the skill, commit a copy under that repository&rsquo;s <code>.claude/skills/</code> instead. The documentation lists the repository&rsquo;s own skills as available in cloud sessions. The cost is a copy in every repository, which is what the plugin was meant to avoid. I built it once, checked it against the release tag, and didn&rsquo;t merge it.</p>
<p>Both routes bring over the plugin&rsquo;s skills only. Hooks, agents and MCP servers in the plugin don&rsquo;t load this way, and I haven&rsquo;t found a way to load them in a cloud session without <code>/reload-plugins</code>.</p>
<h2 id="confirm-the-skill-loaded">Confirm the skill loaded<a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#confirm-the-skill-loaded"
     aria-label="Link to section: Confirm the skill loaded" data-pagefind-ignore></a>
</h2>
<p>There are two checks. The session&rsquo;s skill list shows the skill loaded. Asking a question the skill is built for, without naming the skill, shows that Claude actually uses it. My first test was a request to look over a project and say where it could be better. The session announced the skill&rsquo;s first step and worked through its process. If the answer is generic, ask which skill it used.</p>
<p>For the skill list, ask Claude to list its skills, and look for <code>&lt;skill&gt;</code> or <code>anthropic-skills:&lt;skill&gt;</code>.</p>
<p>A plugin that shows as enabled in <code>claude plugin list</code> isn&rsquo;t proof. In one of my sessions it was enabled and never loaded. A folder in <code>~/.claude/skills/</code> isn&rsquo;t proof either, until the session lists it.</p>
<h2 id="do-this-with-a-coding-agent">Do this with a coding agent<a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#do-this-with-a-coding-agent"
     aria-label="Link to section: Do this with a coding agent" data-pagefind-ignore></a>
</h2>
<p>What found my cause was an agent inspecting its own environment. Paste this into the coding agent in the cloud session you want to check. Run it once in a session where the skill works and once where it doesn&rsquo;t, then compare the two reports. Replace the placeholders first.</p>
<div class="code-block code-block--wrap" data-copyable data-copy-label="Text">
  <div class="code-block__head"><span class="code-block__lang">Text</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">Inspect your own environment, read-only, and report whether a skill is available and where it came from. Change nothing: do not install, enable, disable, add, remove, copy, or edit anything, and do not print any token, key, or password you come across.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Skill name: &lt;skill-name&gt;
</span></span><span class="line"><span class="cl">Plugin name: &lt;plugin-name&gt;
</span></span><span class="line"><span class="cl">Skills repository: &lt;owner&gt;/&lt;skills-repo&gt;
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">1. Print the Claude Code version.
</span></span><span class="line"><span class="cl">2. List the installed plugins with their version, scope, and status. Note which ones are &lt;name&gt;@synced.
</span></span><span class="line"><span class="cl">3. List the plugin marketplaces that have been added.
</span></span><span class="line"><span class="cl">4. List ~/.claude/skills/ and ~/.claude/plugins/, including plugins/synced/.
</span></span><span class="line"><span class="cl">5. List the repositories attached to this session, and say whether the skills repository is one of them.
</span></span><span class="line"><span class="cl">6. Run git ls-remote https://github.com/&lt;owner&gt;/&lt;skills-repo&gt;.git and quote any error verbatim.
</span></span><span class="line"><span class="cl">7. Report whether this repository&#39;s .claude/settings.json has enabledPlugins or extraKnownMarketplaces entries.
</span></span><span class="line"><span class="cl">8. Report whether a setup script log exists for this session and whether it contains output from a plugin install.
</span></span><span class="line"><span class="cl">9. List the skills available to this session, and say whether &lt;skill-name&gt; is among them and under what name.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Then answer in this form:
</span></span><span class="line"><span class="cl">- Skill &lt;skill-name&gt; available: yes or no, and under what name
</span></span><span class="line"><span class="cl">- Plugin &lt;plugin-name&gt; installed: yes or no, and whether it is loaded or only enabled
</span></span><span class="line"><span class="cl">- Synced plugins: the names, or none
</span></span><span class="line"><span class="cl">- Skills repository attached: yes or no
</span></span><span class="line"><span class="cl">- Skills repository reachable with git: yes, or the error
</span></span><span class="line"><span class="cl">- Plugin settings in the repository: the entries, or none
</span></span><span class="line"><span class="cl">- Setup script install output: found, not found, or no log
</span></span><span class="line"><span class="cl">- Anything you could not check, and why</span></span></code></pre></div></div>
  </div>
</div><h2 id="common-failures">Common failures<a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#common-failures"
     aria-label="Link to section: Common failures" data-pagefind-ignore></a>
</h2>
<h3 id="git-fails-with-terminal-prompts-disabled">Git fails with <code>terminal prompts disabled</code><a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#git-fails-with-terminal-prompts-disabled"
     aria-label="Link to section: Git fails with terminal prompts disabled" data-pagefind-ignore></a>
</h3>
<div class="console console--error"><div class="code-block code-block--wrap console__output">
    <p class="console__output-label">Error</p>
    <div class="code-block__scroll"><pre><code><span class="line"><span class="console__token">fatal:</span> <span class="console__token">could</span> <span class="console__token">not</span> <span class="console__token">read</span> <span class="console__token">Username</span> <span class="console__token">for</span> <span class="console__token">&#39;https://github.com&#39;:</span> <span class="console__token">terminal</span> <span class="console__token">prompts</span> <span class="console__token">disabled</span></span></code></pre>
    </div>
  </div></div><p>In a Claude Code cloud session, git prints this for a private repository that isn&rsquo;t attached to the session, because the GitHub proxy supplies no credentials for it. A mistyped repository name probably produces the same prompt. Check the name, then attach the repository or start a session with it attached. The documentation describes the proxy&rsquo;s answer to a GitHub API request for an unattached repository as a 403 whose message starts <code>GitHub access to</code> and contains <code>is not enabled for this session</code>.</p>
<h3 id="setup-script-install-leaves-no-trace">Setup-script install leaves no trace<a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#setup-script-install-leaves-no-trace"
     aria-label="Link to section: Setup-script install leaves no trace" data-pagefind-ignore></a>
</h3>
<p>First check that the saved setup script still contains the install commands. Mine was never checked. If it does, a private skills repository that isn&rsquo;t attached is a likely cause. The <a href="https://code.claude.com/docs/en/cloud-environments#environment-caching" rel="external noopener" target="_blank">documentation<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> also says later sessions can start from a cached snapshot and skip the setup script, so a missing log line doesn&rsquo;t prove the script failed. Check the session itself with <code>claude plugin list</code>.</p>
<h3 id="enabledplugins-in-claudesettingsjson-does-nothing-in-a-cloud-session"><code>enabledPlugins</code> in <code>.claude/settings.json</code> does nothing in a cloud session<a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#enabledplugins-in-claudesettingsjson-does-nothing-in-a-cloud-session"
     aria-label="Link to section: enabledPlugins in .claude/settings.json does nothing in a cloud session" data-pagefind-ignore></a>
</h3>
<p>A cloud session doesn&rsquo;t install the plugins a repository turns on under <code>enabledPlugins</code>. The <a href="https://code.claude.com/docs/en/plugins/loading#plugins-shared-through-a-repository" rel="external noopener" target="_blank">plugin loading reference<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> says it doesn&rsquo;t add the marketplaces under <code>extraKnownMarketplaces</code> either, because that needs the workspace trust dialog, which a cloud session never shows. Those keys still work for local sessions. The open issue <a href="https://github.com/anthropics/claude-code/issues/78119" rel="external noopener" target="_blank">anthropics/claude-code#78119<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> tracks this, and a comment there reports that plugins declared in organization-managed settings don&rsquo;t install in cloud sessions either. I can&rsquo;t test that on an individual account.</p>
<h3 id="plugin-is-enabled-but-the-skill-isnt-listed">Plugin is enabled but the skill isn&rsquo;t listed<a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#plugin-is-enabled-but-the-skill-isnt-listed"
     aria-label="Link to section: Plugin is enabled but the skill isn’t listed" data-pagefind-ignore></a>
</h3>
<p>A plugin installed after the session starts loads only when Claude Code restarts or runs <code>/reload-plugins</code>, and the cloud UI has no <code>/reload-plugins</code>. Upload the skill to your claude.ai account, or copy it into <code>~/.claude/skills/</code>.</p>
<h3 id="install-worked-in-a-test-session-and-fails-in-the-real-one">Install worked in a test session and fails in the real one<a class="heading-anchor" href="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/#install-worked-in-a-test-session-and-fails-in-the-real-one"
     aria-label="Link to section: Install worked in a test session and fails in the real one" data-pagefind-ignore></a>
</h3>
<p>What&rsquo;s attached to a session decides what it can reach. Test in a session set up like the one that will use the skill, with the same repositories attached and nothing more.</p>
<p><strong>Still open:</strong> a way to load a whole private plugin, with its hooks, agents and MCP servers, into a cloud session without attaching its repository. Its skills have a route. I haven&rsquo;t found one for the rest. If you&rsquo;ve found a way that works, I&rsquo;d like to hear about it. Reply to any newsletter issue.</p>
<figure><img src="https://looselydecoupled.com/claude-code-plugin-not-loading-cloud-session/cover_hu_22d45f9a47861538.png" alt="A four-panel comic. Panel one: an engineer at a laptop says “Run the skill.” to a robot. Panel two: the robot instantly holds out a neat stack of paper. It says “Done!” Panel three: the engineer, reading the pages with a worried frown, says “That’s not the skill.” while the robot beams. Panel four: the robot, now wearing a small fake moustache, hands on hips, says “No. It’s my impression of it.”" width="1276" height="530" loading="lazy" decoding="async"><figcaption>I got no error, not even a small one.</figcaption>
</figure>
<div class="tested" id="tested">
  <p class="label label--seam">Tested with</p><dl class="tested__rows">
    <div class="tested__row"><dt>Claude Code</dt> <dd>2.1.302</dd></div>
    <div class="tested__row"><dt>Account</dt> <dd>individual claude.ai plan</dd></div>
    <div class="tested__row"><dt>Cloud session</dt> <dd>claude.ai/code</dd></div>
    <div class="tested__row"><dt>Terminal</dt> <dd>macOS, signed in with a claude.ai account</dd></div>
    <div class="tested__row"><dt>Date</dt> <dd>October 2026</dd></div>
  </dl>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>How VBScript&#39;s Friction Produced PowerShell&#39;s Object Pipeline</title>
      <link>https://looselydecoupled.com/vbscript-primitive-glue-retrospective/</link>
      <guid isPermaLink="true">https://looselydecoupled.com/vbscript-primitive-glue-retrospective/</guid>
      <pubDate>Fri, 26 Dec 2025 00:00:00 +0000</pubDate>
      <dc:creator>Emmanuel Tsouris</dc:creator>
      <category>VBScript</category>
      <category>PowerShell</category>
      <category>History</category>
      <category>Windows Server</category>
      <category>DevOps</category>
      
      <description>The friction that moved Windows automation from VBScript&#39;s text to PowerShell&#39;s objects also moved cloud tooling from scripts to declarative templates.</description>
      <content:encoded><![CDATA[<p>If you started in the cloud era, infrastructure as code is JSON, HCL and YAML:
declare a state, and the provider makes it so. In the Windows environments I
managed in the early 2000s, state wasn&rsquo;t declared. It was coerced, one
script at a time, and the language was VBScript.</p>
<p>My claim: VBScript&rsquo;s friction is what produced PowerShell&rsquo;s object pipeline,
and the same friction is what moved cloud tooling from imperative scripts to
declarative templates. The history is worth a few minutes because it predicts
what the next tool will fix.</p>
<h2 id="on-error-resume-next"><code>On Error Resume Next</code><a class="heading-anchor" href="https://looselydecoupled.com/vbscript-primitive-glue-retrospective/#on-error-resume-next"
     aria-label="Link to section: On Error Resume Next" data-pagefind-ignore></a>
</h2>
<p>In 2003, mapping a printer by security group or reading the BIOS version of
500 servers had no command. It had Notepad, and a <code>.vbs</code> file run by Windows
Script Host.</p>
<p>VBScript had no safety rails. The line that opened most production scripts
was:</p>
<div class="code-block code-block--wrap" data-copyable data-copy-label="VBScript">
  <div class="code-block__head"><span class="code-block__lang">VBScript</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vb" data-lang="vb"><span class="line"><span class="cl"><span class="k">On</span> <span class="k">Error</span> <span class="k">Resume</span> <span class="k">Next</span></span></span></code></pre></div></div>
  </div>
</div><p>It tells the interpreter to ignore any failing line and carry on. Distributed
systems talk about failing fast; a VBScript failed silently and left the
system in whatever state the last successful line produced. Nobody wrote it
out of carelessness. Without it, one missing printer stopped that user&rsquo;s logon
script at that line, and every mapping after it was skipped. Structured error
handling in VBScript was awkward enough that swallowing errors was the
pragmatic choice.</p>
<h2 id="wmi-as-the-engine-vbscript-as-the-crank">WMI as the engine, VBScript as the crank<a class="heading-anchor" href="https://looselydecoupled.com/vbscript-primitive-glue-retrospective/#wmi-as-the-engine-vbscript-as-the-crank"
     aria-label="Link to section: WMI as the engine, VBScript as the crank" data-pagefind-ignore></a>
</h2>
<p>The lasting legacy of that era isn&rsquo;t VBScript&rsquo;s syntax. It&rsquo;s that VBScript
was how a generation of administrators met WMI, Windows Management
Instrumentation, Microsoft&rsquo;s implementation of the DMTF&rsquo;s WBEM standard. WMI
exposed the operating system as a queryable database: disks, services and
processes as rows, with a SQL-like language to select them.</p>
<p>The database was good. The interface to it was this:</p>
<div class="code-block" data-copyable data-copy-label="VBScript">
  <div class="code-block__head"><span class="code-block__lang">VBScript</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vb" data-lang="vb"><span class="line"><span class="cl"><span class="c">&#39; Free space per logical disk
</span></span></span><span class="line"><span class="cl"><span class="n">strComputer</span> <span class="o">=</span> <span class="s">&#34;.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">Set</span> <span class="n">objWMIService</span> <span class="o">=</span> <span class="n">GetObject</span><span class="p">(</span><span class="s">&#34;winmgmts:\\&#34;</span> <span class="o">&amp;</span> <span class="n">strComputer</span> <span class="o">&amp;</span> <span class="s">&#34;\root\cimv2&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">Set</span> <span class="n">colItems</span> <span class="o">=</span> <span class="n">objWMIService</span><span class="p">.</span><span class="n">ExecQuery</span><span class="p">(</span><span class="s">&#34;Select * from Win32_LogicalDisk&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">For</span> <span class="k">Each</span> <span class="n">objItem</span> <span class="ow">in</span> <span class="n">colItems</span>
</span></span><span class="line"><span class="cl">    <span class="n">Wscript</span><span class="p">.</span><span class="n">Echo</span> <span class="s">&#34;Disk: &#34;</span> <span class="o">&amp;</span> <span class="n">objItem</span><span class="p">.</span><span class="n">Name</span> <span class="o">&amp;</span> <span class="s">&#34; Free Space: &#34;</span> <span class="o">&amp;</span> <span class="n">objItem</span><span class="p">.</span><span class="n">FreeSpace</span>
</span></span><span class="line"><span class="cl"><span class="k">Next</span></span></span></code></pre></div></div>
  </div>
</div><p>Seven lines to read one property, and every line is ceremony: a COM moniker,
a query string, a loop and string concatenation to print. The verbosity forced
administrators to learn loops, variable types and COM objects, which is the
one thing VBScript did for the profession. It was a high barrier, and it
separated the people who clicked from the people who scripted.</p>
<h2 id="from-parsing-text-to-passing-objects">From parsing text to passing objects<a class="heading-anchor" href="https://looselydecoupled.com/vbscript-primitive-glue-retrospective/#from-parsing-text-to-passing-objects"
     aria-label="Link to section: From parsing text to passing objects" data-pagefind-ignore></a>
</h2>
<p>Every VBScript that read another program&rsquo;s output parsed text. Jeffrey
Snover&rsquo;s Monad Manifesto (2002), the design document behind PowerShell 1.0
(2006), names that practice as the problem. Text output is a contract nobody
declared, so a script is coupled to the exact formatting of whatever it
reads. When &ldquo;Free Space&rdquo; became &ldquo;FreeSpace&rdquo;, the script broke.</p>
<p>PowerShell separated the data from its presentation. The same query, in the
modern cmdlet:</p>
<div class="code-block code-block--wrap" data-copyable data-copy-label="PowerShell">
  <div class="code-block__head"><span class="code-block__lang">PowerShell</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="c"># Objects, not text</span>
</span></span><span class="line"><span class="cl"><span class="nb">Get-CimInstance</span> <span class="n">Win32_LogicalDisk</span> <span class="p">|</span> <span class="nb">Select-Object</span> <span class="n">DeviceID</span><span class="p">,</span> <span class="n">FreeSpace</span></span></span></code></pre></div></div>
  </div>
</div><p>Side by side, the two snippets show a change of model, not of syntax.
VBScript was imperative: you told the computer how to connect, how to iterate
and how to print. PowerShell is declarative at the call site: you ask for the
object and the pipeline handles the rest. Formatting happens once, at the end,
by a formatter that knows the object&rsquo;s type.</p>
<h2 id="why-this-matters-for-cloud-work">Why this matters for cloud work<a class="heading-anchor" href="https://looselydecoupled.com/vbscript-primitive-glue-retrospective/#why-this-matters-for-cloud-work"
     aria-label="Link to section: Why this matters for cloud work" data-pagefind-ignore></a>
</h2>
<p>Cloud tooling made the same move, a decade later. The first generation of AWS
automation was imperative scripts around the CLI: create an instance, wait,
attach a volume, tag it. CloudFormation (2011) and Terraform (2014) replaced
that with a declared end state and an engine that computes the steps.</p>
<p>VBScript was the imperative-scripts phase of Windows administration. It was
hard to debug and didn&rsquo;t scale past the person who wrote it. It was also
necessary: it&rsquo;s how administration got from the GUI to anything automated at
all, and the pain of it is what made the next model&rsquo;s advantages legible.</p>
<h2 id="what-would-change-my-mind">What would change my mind<a class="heading-anchor" href="https://looselydecoupled.com/vbscript-primitive-glue-retrospective/#what-would-change-my-mind"
     aria-label="Link to section: What would change my mind" data-pagefind-ignore></a>
</h2>
<p>The argument rests on one causal claim: that text-parsing friction, not some
other pressure, is what produced the object pipeline. The Monad Manifesto
supports it in Snover&rsquo;s own words, which is the best evidence available short
of the design meetings. The account could be a retrospective tidying of
decisions made for other reasons, such as .NET adoption inside Microsoft. If
it is, the lineage still holds but the lesson doesn&rsquo;t.</p>
<p>I still keep a folder of old <code>.vbs</code> scripts. I do not run them. They are a
record of what the tooling cost before someone decided the cost was the
problem. The next time a CloudFormation template takes an afternoon to debug,
the alternative was <code>On Error Resume Next</code>.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
