+1
-1
| { | ||
| "name": "gatecraft", | ||
| "version": "1.0.0", | ||
| "version": "1.0.1", | ||
| "description": "The AI Engineering Operating System — install a complete engineering loop, quality gates, and 26 specialist roles into any repository. Hidden, gitignored, zero dependencies.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+39
-0
@@ -29,2 +29,41 @@ # CHANGELOG.md — History of the Gatecraft | ||
| ## [1.0.1] — 2026-08-05 | ||
| Two fixes to `--share` mode, the option that lets a team commit project knowledge | ||
| while keeping the framework kernel out of git. Both were found by installing | ||
| gatecraft into gatecraft. | ||
| **What you must do.** If you installed with `--share` on 1.0.0, run | ||
| `npx gatecraft@latest doctor` and check what it now says. If your `.gitignore` | ||
| already excluded `.ai/` before you installed, your project memory was never | ||
| actually being committed — the install said it was. Remove that rule and commit | ||
| `.ai/PROJECT_CONTEXT.md`, `.ai/DECISIONS.md`, and `.ai/memory/` to start sharing | ||
| them for real. If you followed the earlier `git rm -r --cached .ai` advice from | ||
| `doctor`, check whether that commit removed shared files your teammates needed. | ||
| ### Fixed | ||
| - `gatecraft doctor` treated a `--share` install as if it were hidden. It reported | ||
| every shared project file as accidentally committed and advised | ||
| `git rm -r --cached .ai` — a command that untracks precisely the files `--share` | ||
| exists to share, deleting them for every teammate on the next commit. The advice | ||
| was the defect. `doctor` now distinguishes project-owned files from | ||
| framework-owned ones and reports only genuinely leaked framework files, naming | ||
| them individually rather than by wildcard. | ||
| - `gatecraft init --share` reported `project memory shared` whether or not the | ||
| share worked. Git will not re-include a file inside an excluded directory, so a | ||
| pre-existing `.ai/` or `/.ai/` rule in `.gitignore` makes every negation the | ||
| installer writes inert. The install would claim success while committing nothing, | ||
| and a team could discover months later that no context had ever been shared. | ||
| `init` now detects the conflicting rule, reports its line number and text, and | ||
| states plainly that project memory is not shared. | ||
| ### Changed | ||
| - Share-mode tests now assert what `git` would actually commit rather than that the | ||
| `.gitignore` contains a negation. The previous test passed against both bugs. | ||
| --- | ||
| ## [1.0.0] — 2026-08-04 | ||
@@ -31,0 +70,0 @@ |
+81
-0
@@ -367,1 +367,82 @@ # DECISIONS.md — Architecture Decision Records | ||
| whether the documents drifted, and which sections were never read.)* | ||
| --- | ||
| ## ADR-0002: Share mode commits project knowledge, hides the framework kernel | ||
| **Date:** 2026-08-05 | ||
| **Status:** Accepted | ||
| **Deciders:** CTO, System Architect | ||
| **Tags:** git, collaboration, installation | ||
| ### Context and Problem | ||
| A developer installs gatecraft with `--yes`, filling in PROJECT_CONTEXT.md and recording | ||
| decisions in `.ai/DECISIONS.md` and `.ai/memory/`. A teammate clones the repository | ||
| and gets AGENTS.md pointing at `.ai/` — which is not there, because the default `--hidden` | ||
| mode gitignores the entire directory. Running `init` gives them a fresh empty framework, | ||
| not the one carrying the first developer's work. | ||
| The framework ships 16,338 lines across 35 files. Of those, 13,477 lines (13 files) are | ||
| framework-owned — SYSTEM.md, KNOWLEDGE.md, PROMPTS.md, the shipped kernel that does | ||
| not change per project. The other 2,861 lines (22 files) are project-owned — context, | ||
| decisions, memory, and working directories that *do* accumulate project-specific facts. | ||
| A team needs the project half shared and the framework half local. Committing all of | ||
| `.ai/` bloats every diff with 13k lines of stable prose; hiding all of it loses the | ||
| decisions and memory the first developer invested. | ||
| ### Decision | ||
| Add `--share` mode alongside the existing `--hidden` default. Share mode writes a | ||
| selective `.gitignore` block: | ||
| ```gitignore | ||
| .ai/* | ||
| !.ai/PROJECT_CONTEXT.md | ||
| !.ai/DECISIONS.md | ||
| !.ai/memory/ | ||
| !.ai/architecture/ | ||
| !.ai/.gatecraft-manifest.json | ||
| ``` | ||
| The framework-owned files stay out of git; the project-owned files and the manifest are | ||
| committed. A teammate cloning the repo gets the shared context and can run | ||
| `gatecraft doctor --fix` to restore the 13 missing framework files without overwriting | ||
| the 13 project files that travelled. | ||
| ### Consequences | ||
| **Positive:** | ||
| - Context and decisions propagate to the team on clone, not lost to each developer's local `.ai/`. | ||
| - Diffs stay clean — the 13k-line kernel is never committed. | ||
| - Version skew is detectable: the manifest records the framework version, so `status` | ||
| can warn a teammate if their local CLI is ahead or behind. | ||
| **Negative:** | ||
| - A pre-existing `.ai/` or `/.ai/` rule in `.gitignore` silently defeats every negation | ||
| (git cannot re-include a file inside an excluded directory). `init --share` now checks | ||
| for this conflict and warns loudly rather than falsely claiming success. | ||
| - The manifest is now committed, which exposes the framework version and installation | ||
| timestamp to the repository. Not sensitive, but visible. | ||
| ### Alternatives Considered | ||
| 1. **Commit nothing (status quo before this ADR).** Simple, but loses all project | ||
| knowledge on clone. | ||
| 2. **Commit everything.** Also simple, but 13k lines in every diff is untenable. | ||
| 3. **Add `gatecraft sync` to push/pull shared content to a backend.** Adds a dependency | ||
| and a new failure mode; rejected for v1. | ||
| 4. **Treat `.ai/` like `.vscode/` — personal notes only.** Makes AGENTS.md a lie (it | ||
| claims the framework is shared), and forces every developer to re-fill context | ||
| independently. | ||
| ### Revisit Triggers | ||
| - A team reports that shared memory drifted from the codebase and became stale faster | ||
| than local memory would have. | ||
| - Version skew (different CLI versions across a team) causes a manifest conflict that | ||
| blocks a pull request. | ||
| - Six months: measure whether filled-in context actually reduces the "agent guessed | ||
| wrong about the stack" error class. | ||
| # AI Engineering Operating System (Gatecraft) | ||
| Version 1.0.0 — see [VERSION.md](VERSION.md) and [CHANGELOG.md](CHANGELOG.md). | ||
| Version 1.0.1 — see [VERSION.md](VERSION.md) and [CHANGELOG.md](CHANGELOG.md). | ||
@@ -5,0 +5,0 @@ The Gatecraft is a technology-agnostic operating system for AI coding agents and the |
| # VERSION.md — Versioning and Compatibility | ||
| **Current version: 1.0.0** | ||
| **Current version: 1.0.1** | ||
@@ -150,2 +150,3 @@ This file governs the version of the AI Engineering Operating System itself — the | ||
| | 1.0.0 | 15 | 12 | Twelve-stage loop; ten scoring dimensions; 90/100 threshold; Security veto | Initial release | | ||
| | 1.0.1 | 15 | 12 | Unchanged | Installer fixes only. No document, directory, or kernel change; no override can be affected. | | ||
@@ -152,0 +153,0 @@ Record each release here. The columns are the things an override can depend on, so a |
@@ -130,9 +130,35 @@ 'use strict'; | ||
| // 5. Accidentally committed. | ||
| // | ||
| // In share mode, tracked project files are the entire point — context, | ||
| // decisions, and memory are committed so a teammate gets them on clone. Only | ||
| // the framework half is meant to stay out of git, so this warns on that half | ||
| // alone. Warning on everything would be worse than noise here: the suggested | ||
| // `git rm -r --cached .ai` would untrack exactly the files the mode exists to | ||
| // share, and the next `git commit` would delete them for everyone else. | ||
| const staged = checkGitStaged(root); | ||
| if (shouldHide && staged && staged.length) { | ||
| add( | ||
| 'warn', | ||
| `${staged.length} file(s) under .ai/ are tracked by git despite being installed hidden`, | ||
| 'git rm -r --cached .ai # then commit' | ||
| ); | ||
| const offenders = | ||
| m.mode === 'share' | ||
| ? staged.filter((f) => { | ||
| const rel = f.replace(/^\.ai\//, ''); | ||
| // The manifest is shared deliberately: it is what tells a teammate's | ||
| // CLI that an install already exists here. It is not PROJECT_OWNED | ||
| // (that list governs what upgrade refuses to overwrite, and the | ||
| // manifest is our bookkeeping, not the user's document), so it needs | ||
| // naming separately rather than reclassifying and changing upgrade. | ||
| if (rel === '.gatecraft-manifest.json') return false; | ||
| return !payload.isProjectOwned(rel); | ||
| }) | ||
| : staged; | ||
| if (offenders.length) { | ||
| add( | ||
| 'warn', | ||
| m.mode === 'share' | ||
| ? `${offenders.length} framework file(s) under .ai/ are tracked by git — only project files are meant to be shared` | ||
| : `${offenders.length} file(s) under .ai/ are tracked by git despite being installed hidden`, | ||
| m.mode === 'share' | ||
| ? `git rm --cached ${offenders.slice(0, 3).join(' ')} # then commit` | ||
| : 'git rm -r --cached .ai # then commit' | ||
| ); | ||
| } | ||
| } | ||
@@ -139,0 +165,0 @@ |
+15
-1
@@ -186,3 +186,17 @@ 'use strict'; | ||
| m.gitignore.mode = mode; | ||
| if (mode === 'share') ui.ok('.gitignore — framework hidden, project memory shared'); | ||
| if (mode === 'share') { | ||
| // Only claim the share worked if git will honour it. An existing rule | ||
| // that excludes the whole directory makes every negation we just wrote | ||
| // inert, and reporting success anyway is how a team discovers months | ||
| // later that none of their context was ever committed. | ||
| const conflict = gitignore.conflictingRule(root); | ||
| if (conflict) { | ||
| ui.warn('.gitignore — framework hidden, but project memory is NOT shared'); | ||
| ui.note(`line ${conflict.line} (${conflict.text}) excludes all of .ai/`); | ||
| ui.note('git cannot re-include a file inside an excluded directory'); | ||
| ui.note(`remove that line to share project memory as --share intends`); | ||
| } else { | ||
| ui.ok('.gitignore — framework hidden, project memory shared'); | ||
| } | ||
| } | ||
| else ui.ok(`.gitignore — ${r.created ? 'created, ' : ''}.ai/ hidden from git`); | ||
@@ -189,0 +203,0 @@ } else { |
+38
-1
@@ -83,2 +83,39 @@ 'use strict'; | ||
| module.exports = { BEGIN, END, ensure, remove, isIgnored, findBlock }; | ||
| /** | ||
| * Find a rule *outside* our block that excludes the whole `.ai/` directory. | ||
| * | ||
| * This matters only for share mode, and it matters absolutely. Git will not | ||
| * re-include a file inside an excluded directory: once `.ai/` itself is | ||
| * excluded, `!.ai/DECISIONS.md` is dead text. So a user who already had `.ai/` | ||
| * in their .gitignore gets a `--share` install whose every negation is inert, | ||
| * shares nothing, and says nothing about it — the failure this project is | ||
| * supposed to prevent, arriving silently. gatecraft's own repository has such a | ||
| * line, which is how this was found. | ||
| * | ||
| * Returns { line, text } for reporting, or null when there is no conflict. | ||
| */ | ||
| function conflictingRule(root) { | ||
| const p = paths(root).gitignore; | ||
| if (!fsx.exists(p)) return null; | ||
| const text = fsx.read(p); | ||
| const found = findBlock(text); | ||
| return ( | ||
| fsx | ||
| .lines(text) | ||
| .map((raw, i) => ({ raw, i })) | ||
| .filter(({ raw, i }) => { | ||
| if (!found) return true; | ||
| // Offsets are cheaper to compare than re-parsing: skip our own lines. | ||
| const at = fsx.lines(text).slice(0, i).join('\n').length; | ||
| return at < found.start || at >= found.end; | ||
| }) | ||
| .map(({ raw, i }) => ({ t: raw.trim(), line: i + 1, raw })) | ||
| // A directory exclusion, not a single-file one. `.ai/**` and `.ai/*` also | ||
| // exclude the directory contents wholesale, so they belong here too. | ||
| .filter(({ t }) => ['.ai', '.ai/', '/.ai', '/.ai/', '.ai/*', '.ai/**', '/.ai/*', '/.ai/**'].includes(t)) | ||
| .map(({ line, raw }) => ({ line, text: raw.trim() }))[0] || null | ||
| ); | ||
| } | ||
| module.exports = { BEGIN, END, ensure, remove, isIgnored, findBlock, conflictingRule }; |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
953116
1%2412
3.21%