@polderlabs/bizar 10.7.0 → 10.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/agents/_shared/AGENT_BASELINE.md +266 -0
- package/.claude/agents/_shared/CLAUDE_TOOLS.md +412 -0
- package/.claude/agents/_shared/SKILLS.md +109 -0
- package/.claude/agents/brand-designer.md +55 -0
- package/.claude/agents/exec-assistant.md +34 -0
- package/.claude/agents/help-desk.md +44 -0
- package/.claude/agents/it-lead.md +53 -0
- package/.claude/agents/knowledge-manager.md +49 -0
- package/.claude/agents/office-coordinator.md +39 -0
- package/.claude/agents/office-greeter.md +53 -0
- package/.claude/agents/office-manager.md +287 -0
- package/.claude/agents/principal-engineer.md +58 -0
- package/.claude/agents/qa-reviewer.md +51 -0
- package/.claude/agents/research-analyst.md +53 -0
- package/.claude/agents/senior-engineer.md +55 -0
- package/.claude/agents/support-tech.md +87 -0
- package/.claude/agents/vp-engineering.md +54 -0
- package/.claude/commands/audit.md +48 -0
- package/.claude/commands/bizar.md +22 -0
- package/.claude/commands/cron.md +36 -0
- package/.claude/commands/explain.md +17 -0
- package/.claude/commands/goal.md +99 -0
- package/.claude/commands/init.md +15 -0
- package/.claude/commands/learn.md +46 -0
- package/.claude/commands/plan.md +35 -0
- package/.claude/commands/plow-through.md +50 -0
- package/.claude/commands/pr-review.md +49 -0
- package/.claude/commands/setup-provider.md +96 -0
- package/.claude/commands/spec.md +47 -0
- package/.claude/commands/sprint.md +43 -0
- package/.claude/commands/tailscale-serve.md +100 -0
- package/.claude/commands/team.md +132 -0
- package/.claude/commands/test.md +62 -0
- package/.claude/commands/validate.md +68 -0
- package/.claude/commands/visual-plan.md +24 -0
- package/.claude/hooks/README.md +108 -0
- package/.claude/hooks/__tests__/pretooluse-editwrite.test.mjs +146 -0
- package/.claude/hooks/__tests__/sessionend-recall.test.mjs +256 -0
- package/.claude/hooks/__tests__/sessionstart-prime.test.mjs +325 -0
- package/.claude/hooks/__tests__/thinking-route.test.mjs +319 -0
- package/.claude/hooks/auto-instinct.sh +81 -0
- package/.claude/hooks/learning-extract.mjs +92 -0
- package/.claude/hooks/post-merge-audit.sh +93 -0
- package/.claude/hooks/posttooluse-editwrite.mjs +91 -0
- package/.claude/hooks/pretooluse-bash.mjs +87 -0
- package/.claude/hooks/pretooluse-editwrite.mjs +117 -0
- package/.claude/hooks/sessionend-recall.mjs +384 -0
- package/.claude/hooks/sessionstart-prime.mjs +278 -0
- package/.claude/hooks/thinking-route.mjs +314 -0
- package/.claude/hooks/worker-suggest.mjs +110 -0
- package/.claude/settings.json +167 -0
- package/.claude/skills/9router/SKILL.md +80 -0
- package/.claude/skills/9router-chat/SKILL.md +73 -0
- package/.claude/skills/9router-embeddings/SKILL.md +69 -0
- package/.claude/skills/9router-image/SKILL.md +86 -0
- package/.claude/skills/9router-stt/SKILL.md +79 -0
- package/.claude/skills/9router-tts/SKILL.md +80 -0
- package/.claude/skills/9router-web-fetch/SKILL.md +99 -0
- package/.claude/skills/9router-web-search/SKILL.md +91 -0
- package/.claude/skills/agent-browser/SKILL.md +64 -0
- package/.claude/skills/bizar/README.md +9 -0
- package/.claude/skills/bizar/SKILL.md +447 -0
- package/.claude/skills/cpp-coding-standards/README.md +28 -0
- package/.claude/skills/cpp-coding-standards/SKILL.md +634 -0
- package/.claude/skills/cpp-coding-standards/references/concurrency.md +320 -0
- package/.claude/skills/cpp-coding-standards/references/error-handling.md +229 -0
- package/.claude/skills/cpp-coding-standards/references/memory-safety.md +216 -0
- package/.claude/skills/cpp-coding-standards/references/modern-idioms.md +282 -0
- package/.claude/skills/cpp-coding-standards/references/review-checklist.md +96 -0
- package/.claude/skills/cpp-testing/README.md +28 -0
- package/.claude/skills/cpp-testing/SKILL.md +304 -0
- package/.claude/skills/cpp-testing/references/coverage.md +370 -0
- package/.claude/skills/cpp-testing/references/framework-compare.md +175 -0
- package/.claude/skills/cpp-testing/references/host-test-for-embedded.md +499 -0
- package/.claude/skills/cpp-testing/references/mocking.md +364 -0
- package/.claude/skills/cpp-testing/references/tdd-workflow.md +308 -0
- package/.claude/skills/cubesandbox/SKILL.md +148 -0
- package/.claude/skills/de-sloppify/SKILL.md +38 -0
- package/.claude/skills/de-sloppify/cleanup.mjs +253 -0
- package/.claude/skills/de-sloppify/cleanup.test.mjs +189 -0
- package/.claude/skills/embedded-esp-idf/README.md +41 -0
- package/.claude/skills/embedded-esp-idf/SKILL.md +439 -0
- package/.claude/skills/embedded-esp-idf/references/freertos-patterns.md +214 -0
- package/.claude/skills/embedded-esp-idf/references/host-tests.md +164 -0
- package/.claude/skills/embedded-esp-idf/references/idf-py-commands.md +157 -0
- package/.claude/skills/embedded-esp-idf/references/kconfig.md +159 -0
- package/.claude/skills/embedded-esp-idf/references/logging-discipline.md +118 -0
- package/.claude/skills/embedded-esp-idf/references/memory-and-iram.md +137 -0
- package/.claude/skills/embedded-esp-idf/references/nvs.md +121 -0
- package/.claude/skills/embedded-esp-idf/references/packed-structs.md +192 -0
- package/.claude/skills/embedded-esp-idf/scripts/idf_env.sh +47 -0
- package/.claude/skills/embedded-esp-idf/scripts/size_check.sh +77 -0
- package/.claude/skills/glyph/SKILL.md +163 -0
- package/.claude/skills/harness-engineering/SKILL.md +142 -0
- package/.claude/skills/lightrag/SKILL.md +81 -0
- package/.claude/skills/memory-protocol/SKILL.md +105 -0
- package/.claude/skills/obsidian/SKILL.md +306 -0
- package/.claude/skills/read-the-damn-docs/SKILL.md +113 -0
- package/.claude/skills/self-improvement/SKILL.md +64 -0
- package/.claude/skills/skillopt/SKILL.md +129 -0
- package/.claude/skills/thinking-archetypes/SKILL.md +90 -0
- package/.claude/skills/thinking-bayesian/SKILL.md +267 -0
- package/.claude/skills/thinking-bounded-rationality/SKILL.md +406 -0
- package/.claude/skills/thinking-circle-of-competence/SKILL.md +216 -0
- package/.claude/skills/thinking-cynefin/SKILL.md +70 -0
- package/.claude/skills/thinking-debiasing/SKILL.md +192 -0
- package/.claude/skills/thinking-dual-process/SKILL.md +282 -0
- package/.claude/skills/thinking-effectuation/SKILL.md +366 -0
- package/.claude/skills/thinking-feedback-loops/SKILL.md +464 -0
- package/.claude/skills/thinking-fermi-estimation/SKILL.md +263 -0
- package/.claude/skills/thinking-first-principles/SKILL.md +167 -0
- package/.claude/skills/thinking-five-whys-plus/SKILL.md +139 -0
- package/.claude/skills/thinking-inversion/SKILL.md +195 -0
- package/.claude/skills/thinking-jobs-to-be-done/SKILL.md +363 -0
- package/.claude/skills/thinking-kepner-tregoe/SKILL.md +154 -0
- package/.claude/skills/thinking-leverage-points/SKILL.md +390 -0
- package/.claude/skills/thinking-lindy-effect/SKILL.md +331 -0
- package/.claude/skills/thinking-map-territory/SKILL.md +111 -0
- package/.claude/skills/thinking-margin-of-safety/SKILL.md +330 -0
- package/.claude/skills/thinking-model-combination/SKILL.md +406 -0
- package/.claude/skills/thinking-model-router/SKILL.md +360 -0
- package/.claude/skills/thinking-model-selection/SKILL.md +341 -0
- package/.claude/skills/thinking-occams-razor/SKILL.md +129 -0
- package/.claude/skills/thinking-ooda/SKILL.md +127 -0
- package/.claude/skills/thinking-opportunity-cost/SKILL.md +360 -0
- package/.claude/skills/thinking-pre-mortem/SKILL.md +170 -0
- package/.claude/skills/thinking-probabilistic/SKILL.md +324 -0
- package/.claude/skills/thinking-red-team/SKILL.md +142 -0
- package/.claude/skills/thinking-regret-minimization/SKILL.md +335 -0
- package/.claude/skills/thinking-reversibility/SKILL.md +326 -0
- package/.claude/skills/thinking-scientific-method/SKILL.md +162 -0
- package/.claude/skills/thinking-second-order/SKILL.md +184 -0
- package/.claude/skills/thinking-socratic/SKILL.md +198 -0
- package/.claude/skills/thinking-steel-manning/SKILL.md +332 -0
- package/.claude/skills/thinking-systems/SKILL.md +238 -0
- package/.claude/skills/thinking-theory-of-constraints/SKILL.md +338 -0
- package/.claude/skills/thinking-thought-experiment/SKILL.md +354 -0
- package/.claude/skills/thinking-triz/SKILL.md +171 -0
- package/.claude/skills/thinking-via-negativa/SKILL.md +358 -0
- package/bizar-dash/skills/publishing/SKILL.md +2 -1
- package/cli/install/postinstall.mjs +54 -28
- package/cli/install/postinstall.test.mjs +98 -0
- package/cli/provision.mjs +29 -0
- package/install.sh +7 -0
- package/package.json +7 -2
- package/scripts/git-hooks/__tests__/commit-msg.test.mjs +61 -0
- package/scripts/git-hooks/commit-msg +38 -0
- package/scripts/install-hooks.sh +9 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli/install/postinstall.test.mjs
|
|
3
|
+
*
|
|
4
|
+
* Verifies the postinstall bootstrap resolves source paths via the package
|
|
5
|
+
* root, not via BIZAR_HOME (regression test for the v10.7.0 ENOENT crash:
|
|
6
|
+
* `~/.config/config/agents/mike.md → ~/.claude/agents/mike.md`).
|
|
7
|
+
*/
|
|
8
|
+
import { test } from 'node:test';
|
|
9
|
+
import assert from 'node:assert/strict';
|
|
10
|
+
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readdirSync, copyFileSync, readFileSync } from 'node:fs';
|
|
11
|
+
import { join, dirname, resolve } from 'node:path';
|
|
12
|
+
import { tmpdir } from 'node:os';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const REPO_ROOT = resolve(__dirname, '..', '..');
|
|
17
|
+
|
|
18
|
+
// Mirror the postinstall's PACKAGE_ROOT computation.
|
|
19
|
+
const PACKAGE_ROOT_FROM_HERE = join(__dirname, '..', '..');
|
|
20
|
+
const SRC_AGENTS_DIR = join(PACKAGE_ROOT_FROM_HERE, '.claude', 'agents');
|
|
21
|
+
const SRC_SETTINGS_FILE = join(PACKAGE_ROOT_FROM_HERE, '.claude', 'settings.json');
|
|
22
|
+
|
|
23
|
+
test('PACKAGE_ROOT resolves to the repo root (not BIZAR_HOME)', () => {
|
|
24
|
+
// The bug: source paths used PATHS.bizarHome + '/../config/agents/' which
|
|
25
|
+
// climbed one level up from BIZAR_HOME — a runtime state dir. The fix
|
|
26
|
+
// resolves via import.meta.url to the actual package root.
|
|
27
|
+
assert.ok(SRC_AGENTS_DIR.endsWith('/.claude/agents'), `got ${SRC_AGENTS_DIR}`);
|
|
28
|
+
assert.ok(SRC_SETTINGS_FILE.endsWith('/.claude/settings.json'), `got ${SRC_SETTINGS_FILE}`);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('.claude/agents/ directory exists in the repo (canonical agent source)', () => {
|
|
32
|
+
assert.ok(existsSync(SRC_AGENTS_DIR), `${SRC_AGENTS_DIR} must exist`);
|
|
33
|
+
const files = readdirSync(SRC_AGENTS_DIR).filter(f => f.endsWith('.md'));
|
|
34
|
+
assert.ok(files.length >= 14, `expected >=14 agents, got ${files.length}`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('every shipped agent file resolves to a real file under .claude/agents/', () => {
|
|
38
|
+
const files = readdirSync(SRC_AGENTS_DIR).filter(f => f.endsWith('.md'));
|
|
39
|
+
for (const file of files) {
|
|
40
|
+
assert.ok(existsSync(join(SRC_AGENTS_DIR, file)), `${file} must exist on disk`);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('.claude/settings.json exists in the repo (settings template)', () => {
|
|
45
|
+
assert.ok(existsSync(SRC_SETTINGS_FILE), `${SRC_SETTINGS_FILE} must exist`);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('copy semantics: existing files in destination are not overwritten', () => {
|
|
49
|
+
// Build a fake source tree + destination tree.
|
|
50
|
+
const work = mkdtempSync(join(tmpdir(), 'bizar-postinstall-'));
|
|
51
|
+
try {
|
|
52
|
+
const srcDir = join(work, 'src');
|
|
53
|
+
const dstDir = join(work, 'dst');
|
|
54
|
+
mkdirSync(srcDir, { recursive: true });
|
|
55
|
+
mkdirSync(dstDir, { recursive: true });
|
|
56
|
+
writeFileSync(join(srcDir, 'a.md'), '# shipped');
|
|
57
|
+
writeFileSync(join(dstDir, 'a.md'), '# user-edited');
|
|
58
|
+
|
|
59
|
+
// Mirror postinstall's loop: skip when dst exists.
|
|
60
|
+
const files = ['a.md'];
|
|
61
|
+
for (const f of files) {
|
|
62
|
+
const dst = join(dstDir, f);
|
|
63
|
+
if (!existsSync(dst)) {
|
|
64
|
+
copyFileSync(join(srcDir, f), dst);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const content = readFileSync(join(dstDir, 'a.md'), 'utf8');
|
|
68
|
+
assert.match(content, /user-edited/, 'existing user file must be preserved');
|
|
69
|
+
} finally {
|
|
70
|
+
rmSync(work, { recursive: true, force: true });
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('copy semantics: missing files are copied from source', () => {
|
|
75
|
+
const work = mkdtempSync(join(tmpdir(), 'bizar-postinstall-'));
|
|
76
|
+
try {
|
|
77
|
+
const srcDir = join(work, 'src');
|
|
78
|
+
const dstDir = join(work, 'dst');
|
|
79
|
+
mkdirSync(srcDir, { recursive: true });
|
|
80
|
+
mkdirSync(dstDir, { recursive: true });
|
|
81
|
+
writeFileSync(join(srcDir, 'b.md'), '# shipped');
|
|
82
|
+
|
|
83
|
+
const files = ['b.md'];
|
|
84
|
+
for (const f of files) {
|
|
85
|
+
const dst = join(dstDir, f);
|
|
86
|
+
if (!existsSync(dst)) copyFileSync(join(srcDir, f), dst);
|
|
87
|
+
}
|
|
88
|
+
assert.match(readFileSync(join(dstDir, 'b.md'), 'utf8'), /shipped/);
|
|
89
|
+
} finally {
|
|
90
|
+
rmSync(work, { recursive: true, force: true });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('regression: source path does NOT include "config/agents/" (legacy deleted in F-107)', () => {
|
|
95
|
+
// F-107 deleted config/agents/. Postinstall must not reference it.
|
|
96
|
+
assert.ok(!SRC_AGENTS_DIR.includes('/config/agents/'),
|
|
97
|
+
`BUG: SRC_AGENTS_DIR must not contain /config/agents/ (legacy tree), got ${SRC_AGENTS_DIR}`);
|
|
98
|
+
});
|
package/cli/provision.mjs
CHANGED
|
@@ -490,6 +490,34 @@ export async function syncHookFiles({ dryRun = false } = {}) {
|
|
|
490
490
|
return { ok: true, message: 'hook scripts installed', copied: readdirSync(dest).length, skipped: 0 };
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
+
// ─── git hooks ──────────────────────────────────────────────────────────────
|
|
494
|
+
//
|
|
495
|
+
// Install the repo-local git hooks (.git/hooks/commit-msg, pre-commit, pre-push)
|
|
496
|
+
// from scripts/git-hooks/. Runs only when invoked from inside a git repo, so
|
|
497
|
+
// the npm-install path (which doesn't have a .git/) is a no-op.
|
|
498
|
+
export function installGitHooks({ dryRun = false } = {}) {
|
|
499
|
+
if (!existsSync(REPO_ROOT) || !existsSync(join(REPO_ROOT, '.git'))) {
|
|
500
|
+
return { ok: true, message: 'no git repo — skipping git hook install', installed: [] };
|
|
501
|
+
}
|
|
502
|
+
if (dryRun) return { ok: true, message: '[dry-run] would install git hooks from scripts/git-hooks/', installed: [] };
|
|
503
|
+
|
|
504
|
+
const src = join(REPO_ROOT, 'scripts', 'git-hooks');
|
|
505
|
+
const dest = join(REPO_ROOT, '.git', 'hooks');
|
|
506
|
+
if (!existsSync(src)) return { ok: true, message: `no git hooks source at ${src}`, installed: [] };
|
|
507
|
+
ensureDir(dest);
|
|
508
|
+
const installed = [];
|
|
509
|
+
for (const name of readdirSync(src)) {
|
|
510
|
+
const fp = join(src, name);
|
|
511
|
+
const st = statSync(fp);
|
|
512
|
+
if (!st.isFile()) continue;
|
|
513
|
+
const dst = join(dest, name);
|
|
514
|
+
copyFileSync(fp, dst);
|
|
515
|
+
try { chmodSync(dst, 0o755); } catch { /* ignore */ }
|
|
516
|
+
installed.push(name);
|
|
517
|
+
}
|
|
518
|
+
return { ok: true, message: `${installed.length} git hook(s) installed`, installed };
|
|
519
|
+
}
|
|
520
|
+
|
|
493
521
|
// ─── settings.json ──────────────────────────────────────────────────────────
|
|
494
522
|
|
|
495
523
|
export function writeClaudeSettings({ dryRun = false, force = false } = {}) {
|
|
@@ -697,6 +725,7 @@ export async function runProvision(opts = {}) {
|
|
|
697
725
|
await runStep('Syncing commands', () => syncCommandFiles({ dryRun, force }));
|
|
698
726
|
await runStep('Syncing rules', () => syncRulesFiles({ dryRun, force }));
|
|
699
727
|
await runStep('Syncing hooks', () => syncHookFiles({ dryRun, force }));
|
|
728
|
+
await runStep('Installing git hooks', () => installGitHooks({ dryRun }));
|
|
700
729
|
await runStep('Building SDK', () => buildSdk({ dryRun }));
|
|
701
730
|
await runStep('Building plugin', () => buildPlugin({ dryRun }));
|
|
702
731
|
await runStep('Building dashboard', () => buildDash({ dryRun }));
|
package/install.sh
CHANGED
|
@@ -32,6 +32,13 @@ EOF
|
|
|
32
32
|
Darwin) macos;;
|
|
33
33
|
*) err "Unsupported OS — use install.ps1 on Windows"; exit 1;;
|
|
34
34
|
esac
|
|
35
|
+
# Auto-install the repo-local git hooks (pre-commit, pre-push, commit-msg).
|
|
36
|
+
# The commit-msg hook strips Claude/agent co-author trailers from commits
|
|
37
|
+
# so the human author identity is the only one on every Bizar commit.
|
|
38
|
+
if [ "${DRY:-0}" -eq 0 ] && [ -f "$(dirname "$0")/scripts/install-hooks.sh" ]; then
|
|
39
|
+
note "Installing git hooks (commit-msg + pre-commit + pre-push)..."
|
|
40
|
+
bash "$(dirname "$0")/scripts/install-hooks.sh" || warn "git hook install failed — run ./scripts/install-hooks.sh manually"
|
|
41
|
+
fi
|
|
35
42
|
local a=(--mode=install)
|
|
36
43
|
[ "$ni" -eq 1 ] && a+=(--yes)
|
|
37
44
|
[ "${DRY:-0}" -eq 1 ] && a+=(--dry-run)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polderlabs/bizar",
|
|
3
|
-
"version": "10.7.
|
|
3
|
+
"version": "10.7.2",
|
|
4
4
|
"description": "90s-office multi-agent system for Claude Code — 14 agents across 4 cost tiers with cost-aware routing, plans, and a configurable agent harness. Ships as a single npm package with the dashboard server, Claude Code MCP server, and typed SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
"templates/",
|
|
15
15
|
"scripts/",
|
|
16
16
|
"plugins/",
|
|
17
|
-
"install.sh"
|
|
17
|
+
"install.sh",
|
|
18
|
+
".claude/agents/",
|
|
19
|
+
".claude/skills/",
|
|
20
|
+
".claude/commands/",
|
|
21
|
+
".claude/hooks/",
|
|
22
|
+
".claude/settings.json"
|
|
18
23
|
],
|
|
19
24
|
"scripts": {
|
|
20
25
|
"typecheck": "tsc --noEmit",
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/git-hooks/__tests__/commit-msg.test.mjs
|
|
3
|
+
*
|
|
4
|
+
* Tests the commit-msg hook that strips Claude/agent co-author trailers.
|
|
5
|
+
* Runs the bash hook against synthetic commit messages and asserts the
|
|
6
|
+
* post-rewrite contents.
|
|
7
|
+
*/
|
|
8
|
+
import { test } from 'node:test';
|
|
9
|
+
import assert from 'node:assert/strict';
|
|
10
|
+
import { execFileSync } from 'node:child_process';
|
|
11
|
+
import { mkdtempSync, writeFileSync, readFileSync } from 'node:fs';
|
|
12
|
+
import { tmpdir } from 'node:os';
|
|
13
|
+
import { join, dirname, resolve } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const HOOK = resolve(__dirname, '..', 'commit-msg');
|
|
18
|
+
|
|
19
|
+
function runHook(input) {
|
|
20
|
+
const dir = mkdtempSync(join(tmpdir(), 'bizar-hook-'));
|
|
21
|
+
const msgFile = join(dir, 'msg');
|
|
22
|
+
writeFileSync(msgFile, input);
|
|
23
|
+
execFileSync('bash', [HOOK, msgFile], { stdio: 'pipe' });
|
|
24
|
+
return readFileSync(msgFile, 'utf8');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
test('strips Claude Co-Authored-By trailer', () => {
|
|
28
|
+
const out = runHook('feat: test\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n');
|
|
29
|
+
assert.doesNotMatch(out, /Claude <noreply@anthropic\.com>/);
|
|
30
|
+
assert.match(out, /feat: test/);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('strips lowercase co-authored-by trailer (case-insensitive header)', () => {
|
|
34
|
+
const out = runHook('feat: test\n\nco-authored-by: claude <noreply@anthropic.com>\n');
|
|
35
|
+
assert.doesNotMatch(out, /claude <noreply@anthropic\.com>/);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('strips agent-themed polderlabs.dev trailers', () => {
|
|
39
|
+
const out = runHook('feat: test\n\nCo-authored-by: Odin <noreply@polderlabs.dev>\nCo-authored-by: Thor <noreply@polderlabs.dev>\n');
|
|
40
|
+
assert.doesNotMatch(out, /Odin <noreply@polderlabs\.dev>/);
|
|
41
|
+
assert.doesNotMatch(out, /Thor <noreply@polderlabs\.dev>/);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test('preserves human co-author trailers', () => {
|
|
45
|
+
const out = runHook('feat: test\n\nCo-Authored-By: Real Human <real@example.com>\n');
|
|
46
|
+
assert.match(out, /Real Human <real@example\.com>/);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('preserves embedded Claude mentions in commit body', () => {
|
|
50
|
+
const out = runHook('feat: test\n\nDiscussed Claude <noreply@anthropic.com> inline.\n\nSigned-off-by: Berk <berk@berkderooij.nl>\n');
|
|
51
|
+
assert.match(out, /Discussed Claude <noreply@anthropic\.com> inline\./);
|
|
52
|
+
assert.match(out, /Signed-off-by: Berk <berk@berkderooij\.nl>/);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('collapses blank lines left by trailer removal', () => {
|
|
56
|
+
const input = 'feat: x\n\nbody line\n\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n';
|
|
57
|
+
const out = runHook(input);
|
|
58
|
+
// No triple-newlines, no trailing Claude line
|
|
59
|
+
assert.doesNotMatch(out, /\n\n\n/);
|
|
60
|
+
assert.doesNotMatch(out, /Claude/);
|
|
61
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Strip Claude/agent co-author trailers that don't belong on Bizar commits.
|
|
3
|
+
#
|
|
4
|
+
# Why: Claude Code's CLI appends "Co-Authored-By: Claude <noreply@anthropic.com>"
|
|
5
|
+
# by default; older Bizar release docs also templated Norse/office-agent
|
|
6
|
+
# personas as co-authors. This repo's commits should only carry the human
|
|
7
|
+
# author identity. Any matching trailers get removed before the commit lands.
|
|
8
|
+
#
|
|
9
|
+
# To bypass in an emergency: git commit --no-verify
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
|
|
12
|
+
msg_file="$1"
|
|
13
|
+
tmp="$(mktemp)"
|
|
14
|
+
trap 'rm -f "$tmp"' EXIT
|
|
15
|
+
|
|
16
|
+
# Remove the two known offender trailers (case-insensitive on the header).
|
|
17
|
+
# Also strip any Co-authored-by: line whose email ends in @anthropic.com
|
|
18
|
+
# or @polderlabs.dev — defense-in-depth against future variations.
|
|
19
|
+
sed -E \
|
|
20
|
+
-e '/^[Cc]o-[Aa]uthored-[Bb]y:[[:space:]]*Claude <noreply@anthropic\.com>[[:space:]]*$/d' \
|
|
21
|
+
-e '/^[Cc]o-[Aa]uthored-[Bb]y:[[:space:]]*[^<]*<(noreply@anthropic\.com|noreply@polderlabs\.dev)>$/d' \
|
|
22
|
+
"$msg_file" > "$tmp"
|
|
23
|
+
|
|
24
|
+
# Collapse runs of blank lines that may result from trailer removal.
|
|
25
|
+
awk 'BEGIN { blank=0; seen=0 } {
|
|
26
|
+
if ($0 == "") { blank++ }
|
|
27
|
+
else {
|
|
28
|
+
if (blank > 0 && seen) print "";
|
|
29
|
+
blank=0; print; seen=1
|
|
30
|
+
}
|
|
31
|
+
}' "$tmp" > "${tmp}.2"
|
|
32
|
+
mv "${tmp}.2" "$tmp"
|
|
33
|
+
|
|
34
|
+
if ! cmp -s "$msg_file" "$tmp"; then
|
|
35
|
+
echo "commit-msg: stripped Claude/agent co-author trailers" >&2
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
mv "$tmp" "$msg_file"
|
package/scripts/install-hooks.sh
CHANGED
|
@@ -18,6 +18,9 @@ chmod +x "$GIT_HOOKS_DIR/pre-commit"
|
|
|
18
18
|
cp "$HOOKS_DIR/pre-push" "$GIT_HOOKS_DIR/pre-push"
|
|
19
19
|
chmod +x "$GIT_HOOKS_DIR/pre-push"
|
|
20
20
|
|
|
21
|
+
cp "$HOOKS_DIR/commit-msg" "$GIT_HOOKS_DIR/commit-msg"
|
|
22
|
+
chmod +x "$GIT_HOOKS_DIR/commit-msg"
|
|
23
|
+
|
|
21
24
|
echo "✓ Installed pre-commit hook"
|
|
22
25
|
echo " The hook scans staged changes for likely secrets (Bearer tokens, API keys, etc.)"
|
|
23
26
|
echo " It also blocks commits touching .bizar/ or config/cline.json (per-machine state)"
|
|
@@ -27,3 +30,9 @@ echo "✓ Installed pre-push hook"
|
|
|
27
30
|
echo " Blocks pushes whose commit range includes .bizar/ or config/cline.json changes"
|
|
28
31
|
echo " Defense-in-depth: catches bypasses of pre-commit via --no-verify"
|
|
29
32
|
echo " To bypass in an emergency: git push --no-verify"
|
|
33
|
+
echo ""
|
|
34
|
+
echo "✓ Installed commit-msg hook"
|
|
35
|
+
echo " Strips Co-Authored-By: Claude <noreply@anthropic.com> (and agent-themed"
|
|
36
|
+
echo " noreply@polderlabs.dev trailers) from commit messages — this repo's"
|
|
37
|
+
echo " commits should only carry the human author identity."
|
|
38
|
+
echo " To bypass in an emergency: git commit --no-verify"
|