Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ Tous les changements notables de BASE sont documentés ici. Le format suit l'esp

BASE suit le [Semantic Versioning](https://semver.org/lang/fr/): la surface publique stable (format des ressources, commandes CLI, outils MCP, schémas de projection, contrat des points d'extension) ne casse pas sans incrément majeur. Détail: [Versions et stabilité](docs/reference/versions-et-stabilite.md).

## [Unreleased]

### Corrigé
- **base init**: sur un workspace existant, les artefacts d'outils manquants (ex. `CLAUDE.md`) n'étaient pas proposés. Le healing ne couvrait que `detection.type === "root"`; étendu à `"workspace"` pour respecter FR-INIT-004. (Fixes #12)
- **base-docs-site**: le frontmatter YAML s'affichait en clair dans le corps des pages sur Windows. `stripFrontmatter()` testait `startsWith("---\n")` sans tenir compte des fins de ligne CRLF (`\r\n`). Corrigé en acceptant les deux, et en splittant sur `/\r?\n/` pour éviter les `\r` parasites dans le contenu rendu. (Fixes #10)

## [1.0.0] - 2026-06-25

Première version publique de BASE: un cadre **local-first** et **ouvert** pour structurer la collaboration humain-IA. Le savoir métier vit dans des fichiers Markdown que vous possédez; un cœur **sans dépendance tierce** (Node 18 ou plus) médie les actions sensibles; et tout ce qui sort vers un outil tiers reste un **choix explicite**. La promesse tient en une ligne: l'IA travaille à partir de ce que vous avez structuré plutôt que de suppositions, et vous gardez la main sur ce qu'elle voit, ce qu'il faut vérifier et où elle s'arrête.
Expand Down
4 changes: 2 additions & 2 deletions packages/base-docs-site/src/lib/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ function escapeAttribute(value: string): string {
}

function stripFrontmatter(content: string): string {
if (!content.startsWith("---\n")) return content;
const lines = content.split("\n");
if (!content.startsWith("---\n") && !content.startsWith("---\r\n")) return content;
const lines = content.split(/\r?\n/);
const end = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
return end === -1 ? content : lines.slice(end + 1).join("\n");
}
Expand Down
27 changes: 27 additions & 0 deletions tests/base-cli-init.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,33 @@ describe("base init (CLI)", () => {
assert.match(await fs.readFile(path.join(tmpDir, ".ai", "base.mjs"), "utf8"), /BASE launcher/);
});

it("an existing workspace missing its tool artifacts gets exactly the missing ones healed", async () => {
// Set up a minimal workspace with two roots but no CLAUDE.md / AGENTS.md.
for (const client of ["client-a", "client-b"]) {
await fs.mkdir(path.join(tmpDir, client, ".ai", "agents", "x"), { recursive: true });
await fs.writeFile(
path.join(tmpDir, client, ".ai", "agents", "x", "AGENT.md"),
"---\nid: x\ntype: agent\ndescription: X.\n---\n# X\n",
);
}
await fs.writeFile(
path.join(tmpDir, "base.workspace.json"),
JSON.stringify({ schema_version: "base.workspace.v1", roots: [{ id: "client-a", dir: "client-a" }, { id: "client-b", dir: "client-b" }] }),
);
// CLAUDE.md is absent — doctor would flag missing_tool_artifacts.
await assert.rejects(() => fs.access(path.join(tmpDir, "CLAUDE.md")));

const dry = await run("init", "--json");
const parsed = JSON.parse(dry.stdout.slice(dry.stdout.indexOf("{")));
assert.equal(parsed.detection.type, "workspace");
const planned = parsed.plan.map((e) => e.path);
assert.ok(planned.includes("CLAUDE.md"), "workspace healing proposes missing CLAUDE.md");
assert.equal(parsed.applied, false);

await run("init", "--yes");
assert.match(await fs.readFile(path.join(tmpDir, "CLAUDE.md"), "utf8"), /point d'entrée pour Claude Code/);
});

it("a collection of roots gets a workspace file (--json exposes the plan)", async () => {
for (const client of ["client-a", "client-b"]) {
await fs.mkdir(path.join(tmpDir, client, ".ai", "agents", "x"), { recursive: true });
Expand Down
2 changes: 1 addition & 1 deletion tools/base.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ async function runInit(args) {
const { applyInitPlan, buildInitPlan, detectPerimeter } = await import("./core/perimeter.mjs");
const detection = await detectPerimeter(rootDir);
let plan;
if (detection.type === "root") {
if (detection.type === "root" || detection.type === "workspace") {
const missing = [];
for (const artifact of await buildArtifacts(rootDir)) {
if (await fs.access(path.join(rootDir, artifact.path)).then(() => true, () => false)) continue;
Expand Down