| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933 |
- <!doctype html>
- <html lang="en">
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="description" content="How immutable Git-based npm installation works for codex-chief-of-staff.">
- <title>Git-based npm installation · Codex Chief of Staff</title>
- <style>
- :root {
- color-scheme: light;
- --ink: #12202d;
- --muted: #5e6d79;
- --paper: #f5f1e8;
- --surface: #fffdf8;
- --surface-2: #e9f0ed;
- --navy: #123047;
- --teal: #157a76;
- --teal-dark: #0b5654;
- --coral: #e76f51;
- --gold: #dcae45;
- --line: #ccd5d1;
- --code: #101d27;
- --success: #177245;
- --warning: #8a5415;
- --shadow: 0 18px 50px rgba(18, 48, 71, .10);
- --radius: 18px;
- --mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
- --sans: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
- }
- * { box-sizing: border-box; }
- html { scroll-behavior: smooth; }
- body {
- margin: 0;
- color: var(--ink);
- background:
- radial-gradient(circle at 12% -8%, rgba(21, 122, 118, .16), transparent 30rem),
- radial-gradient(circle at 94% 4%, rgba(231, 111, 81, .12), transparent 24rem),
- var(--paper);
- font-family: var(--sans);
- line-height: 1.62;
- }
- a { color: var(--teal-dark); text-underline-offset: 3px; }
- code, pre { font-family: var(--mono); }
- code:not(pre code) {
- padding: .14em .38em;
- border: 1px solid rgba(18, 48, 71, .12);
- border-radius: 6px;
- background: rgba(255, 255, 255, .72);
- font-size: .9em;
- }
- .shell {
- width: min(1180px, calc(100% - 32px));
- margin: 0 auto;
- }
- .topbar {
- position: sticky;
- top: 0;
- z-index: 20;
- border-bottom: 1px solid rgba(255, 255, 255, .12);
- background: rgba(18, 48, 71, .94);
- backdrop-filter: blur(14px);
- }
- .topbar-inner {
- display: flex;
- min-height: 62px;
- align-items: center;
- justify-content: space-between;
- gap: 24px;
- }
- .brand {
- color: #fff;
- font-weight: 760;
- letter-spacing: -.02em;
- text-decoration: none;
- white-space: nowrap;
- }
- .brand span { color: #78d5ca; }
- nav {
- display: flex;
- gap: 18px;
- overflow-x: auto;
- scrollbar-width: none;
- }
- nav a {
- color: #dce7ec;
- font-size: .86rem;
- font-weight: 650;
- text-decoration: none;
- white-space: nowrap;
- }
- nav a:hover { color: #fff; }
- .hero {
- padding: 86px 0 56px;
- }
- .hero-grid {
- display: grid;
- grid-template-columns: 1.25fr .75fr;
- align-items: center;
- gap: 54px;
- }
- .eyebrow {
- display: inline-flex;
- align-items: center;
- gap: 9px;
- margin-bottom: 18px;
- color: var(--teal-dark);
- font-size: .77rem;
- font-weight: 800;
- letter-spacing: .13em;
- text-transform: uppercase;
- }
- .eyebrow::before {
- width: 24px;
- height: 3px;
- border-radius: 99px;
- background: var(--coral);
- content: "";
- }
- h1, h2, h3 { line-height: 1.12; letter-spacing: -.035em; }
- h1 {
- max-width: 760px;
- margin: 0 0 22px;
- color: var(--navy);
- font-size: clamp(2.65rem, 6vw, 5.65rem);
- font-weight: 790;
- }
- h1 em {
- color: var(--teal);
- font-style: normal;
- }
- .lede {
- max-width: 760px;
- margin: 0;
- color: #40515e;
- font-size: clamp(1.05rem, 1.6vw, 1.28rem);
- }
- .hero-card {
- position: relative;
- overflow: hidden;
- padding: 26px;
- border: 1px solid rgba(255, 255, 255, .18);
- border-radius: 24px;
- color: #eef7f5;
- background: var(--navy);
- box-shadow: var(--shadow);
- }
- .hero-card::after {
- position: absolute;
- right: -50px;
- bottom: -70px;
- width: 180px;
- height: 180px;
- border-radius: 50%;
- background: rgba(120, 213, 202, .11);
- content: "";
- }
- .status {
- display: inline-flex;
- align-items: center;
- gap: 8px;
- padding: 6px 10px;
- border: 1px solid rgba(120, 213, 202, .42);
- border-radius: 999px;
- color: #9de5dc;
- background: rgba(21, 122, 118, .22);
- font: 700 .76rem var(--mono);
- }
- .status::before {
- width: 7px;
- height: 7px;
- border-radius: 50%;
- background: #78d5ca;
- box-shadow: 0 0 0 5px rgba(120, 213, 202, .12);
- content: "";
- }
- .facts {
- display: grid;
- gap: 0;
- margin: 24px 0 0;
- }
- .fact {
- display: grid;
- grid-template-columns: 110px 1fr;
- gap: 12px;
- padding: 13px 0;
- border-top: 1px solid rgba(255, 255, 255, .12);
- }
- .fact dt { color: #98adb9; font-size: .79rem; }
- .fact dd { margin: 0; font: 650 .85rem var(--mono); }
- .summary-strip {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- margin-bottom: 78px;
- border: 1px solid var(--line);
- border-radius: var(--radius);
- background: rgba(255, 253, 248, .72);
- box-shadow: 0 10px 34px rgba(18, 48, 71, .06);
- }
- .summary-item { padding: 24px; }
- .summary-item + .summary-item { border-left: 1px solid var(--line); }
- .summary-item strong {
- display: block;
- margin-bottom: 4px;
- color: var(--navy);
- font-size: 1.02rem;
- }
- .summary-item span { color: var(--muted); font-size: .88rem; }
- main { padding-bottom: 84px; }
- section {
- padding: 42px 0;
- scroll-margin-top: 78px;
- }
- .section-head {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 36px;
- align-items: end;
- margin-bottom: 28px;
- }
- h2 {
- margin: 0;
- color: var(--navy);
- font-size: clamp(2rem, 4vw, 3.2rem);
- font-weight: 780;
- }
- .section-head p { margin: 0; color: var(--muted); }
- .card-grid {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 18px;
- }
- .card {
- min-width: 0;
- padding: 25px;
- border: 1px solid var(--line);
- border-radius: var(--radius);
- background: var(--surface);
- box-shadow: 0 10px 28px rgba(18, 48, 71, .055);
- }
- .card h3 { margin: 10px 0 10px; color: var(--navy); font-size: 1.25rem; }
- .card p { margin: 0; color: var(--muted); }
- .number {
- display: inline-grid;
- width: 32px;
- height: 32px;
- place-items: center;
- border-radius: 10px;
- color: #fff;
- background: var(--teal);
- font: 760 .82rem var(--mono);
- }
- .before-after {
- display: grid;
- grid-template-columns: 1fr 54px 1fr;
- gap: 18px;
- align-items: stretch;
- }
- .state {
- min-width: 0;
- padding: 28px;
- border-radius: var(--radius);
- border: 1px solid var(--line);
- background: var(--surface);
- }
- .state.bad { border-top: 5px solid var(--coral); }
- .state.good { border-top: 5px solid var(--teal); }
- .state h3 { margin: 0 0 14px; font-size: 1.35rem; }
- .state ul { margin: 0; padding-left: 20px; color: var(--muted); }
- .state li + li { margin-top: 8px; }
- .arrow {
- display: grid;
- place-items: center;
- color: var(--teal);
- font-size: 2rem;
- font-weight: 800;
- }
- .flow {
- overflow: hidden;
- padding: 30px;
- border: 1px solid var(--line);
- border-radius: 24px;
- background: var(--surface);
- box-shadow: var(--shadow);
- }
- .flow svg { display: block; width: 100%; height: auto; }
- .callout {
- display: grid;
- grid-template-columns: auto 1fr;
- gap: 18px;
- margin-top: 22px;
- padding: 22px;
- border: 1px solid rgba(220, 174, 69, .48);
- border-radius: 15px;
- background: #fff8df;
- }
- .callout-icon {
- display: grid;
- width: 38px;
- height: 38px;
- place-items: center;
- border-radius: 12px;
- color: #fff;
- background: var(--gold);
- font-weight: 850;
- }
- .callout strong { display: block; color: #674211; }
- .callout p { margin: 3px 0 0; color: #725526; }
- .steps { display: grid; gap: 22px; }
- .step {
- display: grid;
- grid-template-columns: 54px minmax(0, 1fr);
- gap: 20px;
- align-items: start;
- }
- .step-index {
- display: grid;
- width: 48px;
- height: 48px;
- place-items: center;
- border-radius: 15px;
- color: #fff;
- background: var(--navy);
- box-shadow: 0 8px 20px rgba(18, 48, 71, .16);
- font: 800 1rem var(--mono);
- }
- .step-body {
- min-width: 0;
- padding: 24px;
- border: 1px solid var(--line);
- border-radius: var(--radius);
- background: var(--surface);
- }
- .step-body h3 { margin: 0 0 8px; color: var(--navy); font-size: 1.35rem; }
- .step-body > p { margin: 0 0 15px; color: var(--muted); }
- .code-wrap {
- position: relative;
- overflow: hidden;
- border: 1px solid #294252;
- border-radius: 13px;
- background: var(--code);
- }
- pre {
- overflow-x: auto;
- margin: 0;
- padding: 20px 56px 20px 20px;
- color: #e8f1f4;
- font-size: .83rem;
- line-height: 1.65;
- tab-size: 2;
- }
- .copy {
- position: absolute;
- top: 10px;
- right: 10px;
- padding: 6px 9px;
- border: 1px solid rgba(255, 255, 255, .18);
- border-radius: 7px;
- color: #dce7ec;
- background: rgba(255, 255, 255, .08);
- cursor: pointer;
- font: 700 .7rem var(--sans);
- }
- .copy:hover { background: rgba(255, 255, 255, .15); }
- .table-wrap {
- overflow-x: auto;
- border: 1px solid var(--line);
- border-radius: var(--radius);
- background: var(--surface);
- }
- table { width: 100%; border-collapse: collapse; }
- th, td {
- padding: 16px 18px;
- border-bottom: 1px solid var(--line);
- text-align: left;
- vertical-align: top;
- }
- th {
- color: var(--navy);
- background: var(--surface-2);
- font-size: .77rem;
- letter-spacing: .06em;
- text-transform: uppercase;
- }
- tr:last-child td { border-bottom: 0; }
- td:first-child { color: var(--navy); font-weight: 720; }
- td { color: var(--muted); }
- .checklist {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 14px;
- margin: 0;
- padding: 0;
- list-style: none;
- }
- .checklist li {
- position: relative;
- padding: 18px 18px 18px 52px;
- border: 1px solid var(--line);
- border-radius: 14px;
- background: var(--surface);
- color: var(--muted);
- }
- .checklist li::before {
- position: absolute;
- top: 18px;
- left: 18px;
- display: grid;
- width: 23px;
- height: 23px;
- place-items: center;
- border-radius: 50%;
- color: #fff;
- background: var(--success);
- content: "✓";
- font-size: .75rem;
- font-weight: 850;
- }
- .timeline {
- position: relative;
- display: grid;
- gap: 0;
- margin-left: 11px;
- padding-left: 35px;
- border-left: 2px solid var(--line);
- }
- .event {
- position: relative;
- padding: 0 0 28px 0;
- }
- .event::before {
- position: absolute;
- top: 3px;
- left: -43px;
- width: 14px;
- height: 14px;
- border: 4px solid var(--paper);
- border-radius: 50%;
- background: var(--teal);
- box-shadow: 0 0 0 2px var(--teal);
- content: "";
- }
- .event:last-child { padding-bottom: 0; }
- .event h3 { margin: 0 0 5px; color: var(--navy); font-size: 1.15rem; }
- .event p { margin: 0; color: var(--muted); }
- .two-col {
- display: grid;
- grid-template-columns: 1fr 1fr;
- gap: 22px;
- }
- .two-col > *, .card-grid > * { min-width: 0; }
- .mini-title {
- margin: 0 0 13px;
- color: var(--navy);
- font-size: 1.2rem;
- }
- .footer {
- padding: 34px 0 46px;
- border-top: 1px solid var(--line);
- color: var(--muted);
- font-size: .85rem;
- }
- .footer-inner {
- display: flex;
- justify-content: space-between;
- gap: 20px;
- }
- @media (max-width: 900px) {
- .hero-grid, .section-head, .two-col { grid-template-columns: 1fr; }
- .summary-strip { grid-template-columns: repeat(2, 1fr); }
- .summary-item:nth-child(3) { border-left: 0; border-top: 1px solid var(--line); }
- .summary-item:nth-child(4) { border-top: 1px solid var(--line); }
- .card-grid { grid-template-columns: 1fr; }
- .before-after { grid-template-columns: 1fr; }
- .arrow { transform: rotate(90deg); }
- .hero { padding-top: 60px; }
- }
- @media (max-width: 620px) {
- .shell { width: min(100% - 22px, 1180px); }
- .topbar-inner { align-items: flex-start; flex-direction: column; gap: 7px; padding: 11px 0; }
- nav { width: 100%; padding-bottom: 2px; }
- .summary-strip, .checklist { grid-template-columns: 1fr; }
- .summary-item + .summary-item { border-top: 1px solid var(--line); border-left: 0; }
- .step { grid-template-columns: 1fr; gap: 10px; }
- .step-index { width: 40px; height: 40px; }
- pre { padding-right: 20px; font-size: .75rem; }
- .copy { display: none; }
- .hero-card, .flow, .step-body, .card, .state { padding: 20px; }
- .footer-inner { flex-direction: column; }
- }
- @media print {
- .topbar, .copy { display: none; }
- body { background: #fff; }
- .hero { padding-top: 35px; }
- .card, .flow, .step-body, .state, .table-wrap { box-shadow: none; break-inside: avoid; }
- section { break-inside: avoid; }
- a { color: inherit; text-decoration: none; }
- }
- </style>
- </head>
- <body>
- <header class="topbar">
- <div class="shell topbar-inner">
- <a class="brand" href="#top">codex-chief-of-staff <span>/ distribution</span></a>
- <nav aria-label="Document sections">
- <a href="#model">Model</a>
- <a href="#install">Install</a>
- <a href="#verification">Verification</a>
- <a href="#release">Release</a>
- <a href="#rollback">Rollback</a>
- <a href="#troubleshooting">Troubleshooting</a>
- </nav>
- </div>
- </header>
- <div id="top" class="shell">
- <header class="hero">
- <div class="hero-grid">
- <div>
- <div class="eyebrow">Operational guide · Node 24 / npm 11</div>
- <h1>How our <em>Git-based npm install</em> works</h1>
- <p class="lede">The Chief-of-Staff MCP is installed straight from an immutable Git revision. The repository already carries its compiled runtime, so npm only has to fetch, place, and link the package—never build it on the consumer machine.</p>
- </div>
- <aside class="hero-card" aria-label="Distribution contract summary">
- <span class="status">PREBUILT GIT PACKAGE</span>
- <dl class="facts">
- <div class="fact"><dt>Package</dt><dd>codex-chief-of-staff@0.1.1</dd></div>
- <div class="fact"><dt>Executable</dt><dd>dist/src/index.js</dd></div>
- <div class="fact"><dt>Canonical Git</dt><dd>git.bodicsek.host</dd></div>
- <div class="fact"><dt>Revision</dt><dd>vX.Y.Z or full SHA</dd></div>
- <div class="fact"><dt>Consumer build</dt><dd>none</dd></div>
- </dl>
- </aside>
- </div>
- </header>
- <div class="summary-strip" aria-label="Key principles">
- <div class="summary-item"><strong>Immutable</strong><span>Release tag or 40-character commit only.</span></div>
- <div class="summary-item"><strong>Precompiled</strong><span><code>dist/src</code> is committed in Git.</span></div>
- <div class="summary-item"><strong>Script-free</strong><span>Consumer install uses <code>--ignore-scripts</code>.</span></div>
- <div class="summary-item"><strong>PATH stable</strong><span>Normal global prefix; registration is unchanged.</span></div>
- </div>
- <main>
- <section id="model">
- <div class="section-head">
- <h2>The distribution model</h2>
- <p>A Git dependency behaves differently from a registry tarball when npm sees build lifecycle hooks. Our package deliberately avoids those hooks and treats Git as a transport for an already-built npm package.</p>
- </div>
- <div class="before-after">
- <article class="state bad">
- <h3>Why the original Git install failed</h3>
- <ul>
- <li><code>dist</code> was ignored, although the executable pointed to <code>dist/src/index.js</code>.</li>
- <li>Lifecycle preparation could run in npm's temporary Git clone.</li>
- <li>The clone did not reliably contain the TypeScript development toolchain.</li>
- <li>The resulting install could be missing its runtime or link back to deleted temporary storage.</li>
- </ul>
- </article>
- <div class="arrow" aria-hidden="true">→</div>
- <article class="state good">
- <h3>The selected design</h3>
- <ul>
- <li>Track only compiled <code>dist/src/**</code>; keep compiled tests and other output ignored.</li>
- <li>Use maintainer-only <code>compile</code> and <code>pack:verified</code> scripts.</li>
- <li>Define no <code>prepare</code>, <code>prepack</code>, <code>install</code>, or related Git build triggers.</li>
- <li>Install the immutable revision with lifecycle scripts disabled.</li>
- </ul>
- </article>
- </div>
- <div class="flow" style="margin-top: 24px">
- <svg viewBox="0 0 1080 270" role="img" aria-labelledby="flow-title flow-desc">
- <title id="flow-title">Git-based npm installation flow</title>
- <desc id="flow-desc">A release commit contains source, package metadata and prebuilt runtime. A tag selects it. npm clones it into temporary storage, copies a real package into the global prefix, and links the executable on PATH for Codex.</desc>
- <defs>
- <marker id="arrowhead" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto" markerUnits="strokeWidth">
- <path d="M0,0 L0,6 L9,3 z" fill="#157a76"></path>
- </marker>
- </defs>
- <g font-family="Inter, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif">
- <rect x="20" y="55" width="190" height="145" rx="18" fill="#e9f0ed" stroke="#b8c8c2"></rect>
- <text x="45" y="88" fill="#123047" font-size="17" font-weight="750">Git revision</text>
- <text x="45" y="117" fill="#5e6d79" font-size="13">package.json</text>
- <text x="45" y="141" fill="#5e6d79" font-size="13">README.md</text>
- <text x="45" y="165" fill="#157a76" font-size="13" font-weight="700">dist/src/**</text>
- <text x="45" y="189" fill="#5e6d79" font-size="13">tag or full SHA</text>
- <path d="M215 127 H318" fill="none" stroke="#157a76" stroke-width="3" marker-end="url(#arrowhead)"></path>
- <text x="228" y="112" fill="#5e6d79" font-size="12">npm fetches</text>
- <rect x="325" y="55" width="190" height="145" rx="18" fill="#fff8df" stroke="#e0c472"></rect>
- <text x="350" y="88" fill="#674211" font-size="17" font-weight="750">Temporary clone</text>
- <text x="350" y="119" fill="#725526" font-size="13">No build runs</text>
- <text x="350" y="145" fill="#725526" font-size="13">No TypeScript needed</text>
- <text x="350" y="171" fill="#8a5415" font-size="13" font-weight="700">--ignore-scripts</text>
- <path d="M520 127 H623" fill="none" stroke="#157a76" stroke-width="3" marker-end="url(#arrowhead)"></path>
- <text x="535" y="112" fill="#5e6d79" font-size="12">npm places</text>
- <rect x="630" y="55" width="190" height="145" rx="18" fill="#e8f4f2" stroke="#79b8af"></rect>
- <text x="655" y="88" fill="#123047" font-size="17" font-weight="750">Global prefix</text>
- <text x="655" y="118" fill="#5e6d79" font-size="13">Real package directory</text>
- <text x="655" y="144" fill="#5e6d79" font-size="13">Executable resolves inside</text>
- <text x="655" y="170" fill="#157a76" font-size="13" font-weight="700">normal global PATH</text>
- <path d="M825 127 H928" fill="none" stroke="#157a76" stroke-width="3" marker-end="url(#arrowhead)"></path>
- <text x="846" y="112" fill="#5e6d79" font-size="12">Codex starts</text>
- <rect x="935" y="55" width="125" height="145" rx="18" fill="#123047"></rect>
- <text x="957" y="90" fill="#ffffff" font-size="17" font-weight="750">MCP</text>
- <text x="957" y="119" fill="#c7d9e1" font-size="13">stdio server</text>
- <text x="957" y="145" fill="#78d5ca" font-size="13" font-weight="700">4 tools</text>
- <text x="957" y="171" fill="#c7d9e1" font-size="13">App Server</text>
- </g>
- </svg>
- </div>
- <div class="callout">
- <div class="callout-icon">!</div>
- <div><strong>The temporary Git clone is not the installed package.</strong><p>The verifier rejects symlinked package roots and confirms that both the package and executable resolve inside an isolated global prefix—not inside npm's cache or temporary clone.</p></div>
- </div>
- </section>
- <section id="install">
- <div class="section-head">
- <h2>Installation procedure</h2>
- <p>Run the verifier from the source checkout first. Only after it passes should the exact same immutable package spec be installed into the normal global prefix.</p>
- </div>
- <div class="steps">
- <article class="step">
- <div class="step-index">01</div>
- <div class="step-body">
- <h3>Preflight Git access and the release tag</h3>
- <p>This proves the host is reachable and the selected tag exists without putting credentials in the package spec.</p>
- <div class="code-wrap"><button class="copy" type="button">Copy</button><pre><code>git ls-remote --exit-code \
- https://git.bodicsek.host/bodicsek/codex-chief-of-staff.git \
- refs/tags/v0.1.1</code></pre></div>
- </div>
- </article>
- <article class="step">
- <div class="step-index">02</div>
- <div class="step-body">
- <h3>Verify the immutable Git package in isolation</h3>
- <p>The verifier creates a disposable global prefix, installs with lifecycle scripts disabled, probes the executable over MCP, and then removes the prefix.</p>
- <div class="code-wrap"><button class="copy" type="button">Copy</button><pre><code>npm run verify:git-package -- \
- 'git+https://git.bodicsek.host/bodicsek/codex-chief-of-staff.git#v0.1.1'</code></pre></div>
- </div>
- </article>
- <article class="step">
- <div class="step-index">03</div>
- <div class="step-body">
- <h3>Install into the normal global npm prefix</h3>
- <p>There is intentionally no <code>--prefix</code> here. The executable must land on the PATH inherited by Codex. The dedicated cache avoids the known ownership problem in the user npm cache.</p>
- <div class="code-wrap"><button class="copy" type="button">Copy</button><pre><code>npm install --global \
- --ignore-scripts \
- --cache /private/tmp/codex-chief-of-staff-npm-cache \
- 'git+https://git.bodicsek.host/bodicsek/codex-chief-of-staff.git#v0.1.1'</code></pre></div>
- </div>
- </article>
- <article class="step">
- <div class="step-index">04</div>
- <div class="step-body">
- <h3>Confirm version, PATH, and registration</h3>
- <p>The registration stays PATH-based, so upgrading the package does not require a configuration edit.</p>
- <div class="code-wrap"><button class="copy" type="button">Copy</button><pre><code>command -v codex-chief-of-staff
- npm list --global --depth=0 codex-chief-of-staff
- codex mcp get codex-chief-of-staff --json</code></pre></div>
- </div>
- </article>
- <article class="step">
- <div class="step-index">05</div>
- <div class="step-body">
- <h3>Restart Codex CLI</h3>
- <p>MCP processes are initialized by a Codex client. Exit and relaunch the CLI so the existing registration starts the newly installed executable, then inspect <code>/mcp</code> or invoke <code>overview</code>.</p>
- <div class="code-wrap"><button class="copy" type="button">Copy</button><pre><code>codex mcp get codex-chief-of-staff --json
- # Restart the Codex CLI, then call the overview MCP tool.</code></pre></div>
- </div>
- </article>
- </div>
- </section>
- <section id="verification">
- <div class="section-head">
- <h2>What the verifier proves</h2>
- <p><code>verify-git-package.mjs</code> tests the install as a consumer would experience it, while keeping the machine's current global package untouched.</p>
- </div>
- <ul class="checklist">
- <li>Exactly one Git package spec is supplied.</li>
- <li>The URL uses <code>git+https</code>, <code>git+ssh</code>, or <code>git+file</code>.</li>
- <li>The revision is a <code>vX.Y.Z</code> tag or full 40-character SHA.</li>
- <li>npm installs globally inside a newly created temporary prefix.</li>
- <li>Lifecycle scripts are disabled during the install.</li>
- <li>A dedicated cache is used outside the global package directory.</li>
- <li>The package root is a real directory, not a temporary symlink.</li>
- <li>The executable resolves inside the installed package.</li>
- <li>The package name and <code>bin</code> mapping match the contract.</li>
- <li>MCP initialization instructions contain “call overview first”.</li>
- <li>The exposed inventory is exactly the four observational tools.</li>
- <li>Raw npm failures are not echoed with credential-bearing arguments.</li>
- </ul>
- <div class="table-wrap" style="margin-top: 24px">
- <table>
- <thead><tr><th>Contract surface</th><th>Required value</th><th>Why it matters</th></tr></thead>
- <tbody>
- <tr><td>Node engine</td><td><code>>=24 <25</code></td><td>Keeps the supported runtime explicit and reproducible.</td></tr>
- <tr><td>Package files</td><td><code>dist/src</code>, <code>README.md</code></td><td>Ships runtime and user documentation, excluding TypeScript and tests.</td></tr>
- <tr><td>Executable</td><td><code>codex-chief-of-staff → dist/src/index.js</code></td><td>Allows npm to create the stable PATH command.</td></tr>
- <tr><td>Git lifecycle</td><td>None</td><td>Consumer installation never compiles in npm's temporary clone.</td></tr>
- <tr><td>MCP tools</td><td><code>overview</code>, <code>list_threads</code>, <code>inspect_thread</code>, <code>wait_for_change</code></td><td>Prevents accidental protocol-surface drift.</td></tr>
- <tr><td>App Server URL</td><td><code>ws://127.0.0.1:4500</code></td><td>Preserves the existing loopback registration and shared runtime.</td></tr>
- </tbody>
- </table>
- </div>
- </section>
- <section id="release">
- <div class="section-head">
- <h2>Maintainer release flow</h2>
- <p>The maintainer still builds and tests. The consumer does not. Release validation makes the committed runtime and immutable tag trustworthy.</p>
- </div>
- <div class="two-col">
- <div class="card">
- <h3 class="mini-title">Source-to-release sequence</h3>
- <div class="timeline">
- <div class="event"><h3>Compile</h3><p><code>npm run compile</code> produces <code>dist/src/**</code>.</p></div>
- <div class="event"><h3>Test and compare</h3><p>The full suite runs and <code>verify:runtime</code> fails if rebuilding changes the committed runtime.</p></div>
- <div class="event"><h3>Package locally</h3><p><code>pack:verified</code> runs gates, then calls <code>npm pack --ignore-scripts</code>.</p></div>
- <div class="event"><h3>Commit and tag</h3><p>The release commit is pushed to both remotes; an immutable version tag points to that commit.</p></div>
- <div class="event"><h3>Verify the actual remote</h3><p>The Git-package verifier runs against the canonical HTTPS tag before rollout.</p></div>
- </div>
- </div>
- <div class="card">
- <h3 class="mini-title">Release commands</h3>
- <div class="code-wrap"><button class="copy" type="button">Copy</button><pre><code>npm run typecheck
- npm run compile
- npm test
- npm run verify:runtime
- npm run pack:verified -- \
- --pack-destination /Users/dnabracz/Work/packages \
- --cache /private/tmp/codex-chief-of-staff-npm-cache
- npm run verify:package -- \
- /Users/dnabracz/Work/packages/codex-chief-of-staff-0.1.1.tgz
- npm run verify:git-package -- \
- 'git+https://git.bodicsek.host/bodicsek/codex-chief-of-staff.git#v0.1.1'</code></pre></div>
- </div>
- </div>
- </section>
- <section id="rollback">
- <div class="section-head">
- <h2>Upgrade, rollback, removal</h2>
- <p>All three operations replace or remove the global npm package. The MCP registration is independent and remains stable unless removal is intentional.</p>
- </div>
- <div class="card-grid">
- <article class="card">
- <span class="number">↑</span>
- <h3>Upgrade or restore v0.1.1</h3>
- <p>Install the immutable Git tag through the normal global prefix, then restart Codex CLI.</p>
- <div class="code-wrap" style="margin-top: 16px"><button class="copy" type="button">Copy</button><pre><code>npm install --global \
- --ignore-scripts \
- --cache /private/tmp/codex-chief-of-staff-npm-cache \
- 'git+https://git.bodicsek.host/bodicsek/codex-chief-of-staff.git#v0.1.1'</code></pre></div>
- </article>
- <article class="card">
- <span class="number">↶</span>
- <h3>Roll back to 0.1.0</h3>
- <p>Reinstall the previously verified local tarball, verify the version, then restart Codex CLI.</p>
- <div class="code-wrap" style="margin-top: 16px"><button class="copy" type="button">Copy</button><pre><code>npm install --global \
- --ignore-scripts \
- --cache /private/tmp/codex-chief-of-staff-npm-cache \
- /Users/dnabracz/Work/packages/codex-chief-of-staff-0.1.0.tgz</code></pre></div>
- </article>
- <article class="card">
- <span class="number">×</span>
- <h3>Remove</h3>
- <p>Remove the package alone, or also remove the Codex registration when decommissioning the MCP entirely.</p>
- <div class="code-wrap" style="margin-top: 16px"><button class="copy" type="button">Copy</button><pre><code>npm uninstall --global codex-chief-of-staff
- # Optional full decommission:
- codex mcp remove codex-chief-of-staff</code></pre></div>
- </article>
- </div>
- </section>
- <section id="troubleshooting">
- <div class="section-head">
- <h2>Troubleshooting map</h2>
- <p>Diagnose the first failing boundary: Git access, npm placement, PATH resolution, MCP initialization, or App Server connectivity.</p>
- </div>
- <div class="table-wrap">
- <table>
- <thead><tr><th>Symptom</th><th>Likely boundary</th><th>Action</th></tr></thead>
- <tbody>
- <tr><td><code>git ls-remote</code> fails</td><td>Git authentication, CA trust, or repository access</td><td>Fix host access first. Do not embed credentials in the Git package URL.</td></tr>
- <tr><td>Verifier rejects <code>#main</code></td><td>Immutable input validation</td><td>Use <code>#vX.Y.Z</code> or the full 40-character commit SHA.</td></tr>
- <tr><td>npm reports cache <code>EPERM</code></td><td>Root-owned user cache entries</td><td>Use <code>/private/tmp/codex-chief-of-staff-npm-cache</code>; do not change cache ownership automatically.</td></tr>
- <tr><td>Executable is missing</td><td>Package payload or <code>bin</code> mapping</td><td>Inspect <code>package.json</code>, confirm <code>dist/src/index.js</code> exists, and rerun the isolated verifier.</td></tr>
- <tr><td>Executable resolves under a temp path</td><td>Broken Git package placement</td><td>Reject the revision. A valid install resolves within the chosen global prefix.</td></tr>
- <tr><td><code>command -v</code> is empty</td><td>Global npm bin directory not on PATH</td><td>Inspect the normal npm prefix and the PATH inherited by the process launching Codex.</td></tr>
- <tr><td>Codex shows old behavior after upgrade</td><td>Existing MCP child process</td><td>Restart Codex CLI so it launches the replacement PATH executable.</td></tr>
- <tr><td>MCP tools list, but overview is unavailable</td><td>Shared App Server connectivity</td><td>Confirm <code>CODEX_APP_SERVER_URL=ws://127.0.0.1:4500</code> and that the listener uses the same <code>CODEX_HOME</code>.</td></tr>
- </tbody>
- </table>
- </div>
- </section>
- <section aria-labelledby="rules-title">
- <div class="card" style="border-left: 6px solid var(--coral)">
- <h2 id="rules-title" style="font-size: 2rem">Rules worth remembering</h2>
- <div class="card-grid" style="margin-top: 22px">
- <div><h3>Git is the transport</h3><p>The revision must already be a complete runnable npm package.</p></div>
- <div><h3>Verification precedes installation</h3><p>Test in an isolated prefix before changing the normal global package.</p></div>
- <div><h3>Registration is stable</h3><p>Keep the PATH command and App Server environment unchanged across upgrades.</p></div>
- </div>
- </div>
- </section>
- </main>
- </div>
- <footer class="footer">
- <div class="shell footer-inner">
- <span>Codex Chief of Staff · Git-based npm distribution guide</span>
- <span>Canonical example: <code>v0.1.1</code> · Node 24</span>
- </div>
- </footer>
- <script>
- document.querySelectorAll(".copy").forEach((button) => {
- button.addEventListener("click", async () => {
- const code = button.nextElementSibling?.textContent ?? "";
- try {
- await navigator.clipboard.writeText(code);
- const original = button.textContent;
- button.textContent = "Copied";
- window.setTimeout(() => { button.textContent = original; }, 1200);
- } catch {
- button.textContent = "Select text";
- }
- });
- });
- </script>
- </body>
- </html>
|