mindforge-cc 1.0.0
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/.agent/CLAUDE.md +462 -0
- package/.agent/forge/help.md +7 -0
- package/.agent/forge/init-project.md +32 -0
- package/.agent/forge/plan-phase.md +30 -0
- package/.agent/mindforge/approve.md +18 -0
- package/.agent/mindforge/audit.md +30 -0
- package/.agent/mindforge/benchmark.md +33 -0
- package/.agent/mindforge/complete-milestone.md +18 -0
- package/.agent/mindforge/debug.md +126 -0
- package/.agent/mindforge/discuss-phase.md +138 -0
- package/.agent/mindforge/execute-phase.md +165 -0
- package/.agent/mindforge/health.md +21 -0
- package/.agent/mindforge/help.md +23 -0
- package/.agent/mindforge/init-org.md +131 -0
- package/.agent/mindforge/init-project.md +155 -0
- package/.agent/mindforge/install-skill.md +15 -0
- package/.agent/mindforge/map-codebase.md +298 -0
- package/.agent/mindforge/metrics.md +22 -0
- package/.agent/mindforge/migrate.md +40 -0
- package/.agent/mindforge/milestone.md +12 -0
- package/.agent/mindforge/next.md +105 -0
- package/.agent/mindforge/plan-phase.md +125 -0
- package/.agent/mindforge/plugins.md +40 -0
- package/.agent/mindforge/pr-review.md +41 -0
- package/.agent/mindforge/profile-team.md +23 -0
- package/.agent/mindforge/publish-skill.md +19 -0
- package/.agent/mindforge/quick.md +135 -0
- package/.agent/mindforge/release.md +10 -0
- package/.agent/mindforge/retrospective.md +26 -0
- package/.agent/mindforge/review.md +157 -0
- package/.agent/mindforge/security-scan.md +233 -0
- package/.agent/mindforge/ship.md +100 -0
- package/.agent/mindforge/skills.md +141 -0
- package/.agent/mindforge/status.md +104 -0
- package/.agent/mindforge/sync-confluence.md +11 -0
- package/.agent/mindforge/sync-jira.md +12 -0
- package/.agent/mindforge/tokens.md +8 -0
- package/.agent/mindforge/update.md +42 -0
- package/.agent/mindforge/verify-phase.md +62 -0
- package/.agent/mindforge/workspace.md +29 -0
- package/.claude/CLAUDE.md +462 -0
- package/.claude/commands/forge/help.md +7 -0
- package/.claude/commands/forge/init-project.md +32 -0
- package/.claude/commands/forge/plan-phase.md +30 -0
- package/.claude/commands/mindforge/approve.md +18 -0
- package/.claude/commands/mindforge/audit.md +30 -0
- package/.claude/commands/mindforge/benchmark.md +33 -0
- package/.claude/commands/mindforge/complete-milestone.md +18 -0
- package/.claude/commands/mindforge/debug.md +126 -0
- package/.claude/commands/mindforge/discuss-phase.md +138 -0
- package/.claude/commands/mindforge/execute-phase.md +165 -0
- package/.claude/commands/mindforge/health.md +21 -0
- package/.claude/commands/mindforge/help.md +23 -0
- package/.claude/commands/mindforge/init-org.md +131 -0
- package/.claude/commands/mindforge/init-project.md +155 -0
- package/.claude/commands/mindforge/install-skill.md +15 -0
- package/.claude/commands/mindforge/map-codebase.md +298 -0
- package/.claude/commands/mindforge/metrics.md +22 -0
- package/.claude/commands/mindforge/migrate.md +40 -0
- package/.claude/commands/mindforge/milestone.md +12 -0
- package/.claude/commands/mindforge/next.md +105 -0
- package/.claude/commands/mindforge/plan-phase.md +125 -0
- package/.claude/commands/mindforge/plugins.md +40 -0
- package/.claude/commands/mindforge/pr-review.md +41 -0
- package/.claude/commands/mindforge/profile-team.md +23 -0
- package/.claude/commands/mindforge/publish-skill.md +19 -0
- package/.claude/commands/mindforge/quick.md +135 -0
- package/.claude/commands/mindforge/release.md +10 -0
- package/.claude/commands/mindforge/retrospective.md +26 -0
- package/.claude/commands/mindforge/review.md +157 -0
- package/.claude/commands/mindforge/security-scan.md +233 -0
- package/.claude/commands/mindforge/ship.md +100 -0
- package/.claude/commands/mindforge/skills.md +141 -0
- package/.claude/commands/mindforge/status.md +104 -0
- package/.claude/commands/mindforge/sync-confluence.md +11 -0
- package/.claude/commands/mindforge/sync-jira.md +12 -0
- package/.claude/commands/mindforge/tokens.md +8 -0
- package/.claude/commands/mindforge/update.md +42 -0
- package/.claude/commands/mindforge/verify-phase.md +62 -0
- package/.claude/commands/mindforge/workspace.md +29 -0
- package/.forge/org/CONVENTIONS.md +0 -0
- package/.forge/org/ORG.md +0 -0
- package/.forge/org/SECURITY.md +0 -0
- package/.forge/org/TOOLS.md +0 -0
- package/.forge/personas/analyst.md +0 -0
- package/.forge/personas/architect.md +0 -0
- package/.forge/personas/debug-specialist.md +0 -0
- package/.forge/personas/developer.md +26 -0
- package/.forge/personas/qa-engineer.md +0 -0
- package/.forge/personas/release-manager.md +0 -0
- package/.forge/personas/security-reviewer.md +33 -0
- package/.forge/personas/tech-writer.md +0 -0
- package/.forge/skills/api-design/SKILL.md +0 -0
- package/.forge/skills/code-quality/SKILL.md +0 -0
- package/.forge/skills/documentation/SKILL.md +0 -0
- package/.forge/skills/security-review/SKILL.md +23 -0
- package/.forge/skills/testing-standards/SKILL.md +27 -0
- package/.github/workflows/mindforge-ci.yml +224 -0
- package/.gitlab-ci-mindforge.yml +18 -0
- package/.mindforge/MINDFORGE-SCHEMA.json +165 -0
- package/.mindforge/audit/AUDIT-SCHEMA.md +451 -0
- package/.mindforge/ci/ci-config-schema.md +21 -0
- package/.mindforge/ci/ci-mode.md +179 -0
- package/.mindforge/ci/github-actions-adapter.md +224 -0
- package/.mindforge/ci/gitlab-ci-adapter.md +31 -0
- package/.mindforge/ci/jenkins-adapter.md +44 -0
- package/.mindforge/distribution/registry-client.md +166 -0
- package/.mindforge/distribution/registry-schema.md +96 -0
- package/.mindforge/distribution/skill-publisher.md +44 -0
- package/.mindforge/distribution/skill-validator.md +74 -0
- package/.mindforge/engine/compaction-protocol.md +182 -0
- package/.mindforge/engine/context-injector.md +128 -0
- package/.mindforge/engine/dependency-parser.md +113 -0
- package/.mindforge/engine/skills/conflict-resolver.md +69 -0
- package/.mindforge/engine/skills/loader.md +184 -0
- package/.mindforge/engine/skills/registry.md +98 -0
- package/.mindforge/engine/skills/versioning.md +75 -0
- package/.mindforge/engine/verification-pipeline.md +111 -0
- package/.mindforge/engine/wave-executor.md +235 -0
- package/.mindforge/governance/GOVERNANCE-CONFIG.md +17 -0
- package/.mindforge/governance/approval-workflow.md +37 -0
- package/.mindforge/governance/change-classifier.md +63 -0
- package/.mindforge/governance/compliance-gates.md +31 -0
- package/.mindforge/integrations/confluence.md +27 -0
- package/.mindforge/integrations/connection-manager.md +163 -0
- package/.mindforge/integrations/github.md +25 -0
- package/.mindforge/integrations/gitlab.md +13 -0
- package/.mindforge/integrations/jira.md +102 -0
- package/.mindforge/integrations/slack.md +41 -0
- package/.mindforge/intelligence/antipattern-detector.md +75 -0
- package/.mindforge/intelligence/difficulty-scorer.md +55 -0
- package/.mindforge/intelligence/health-engine.md +208 -0
- package/.mindforge/intelligence/skill-gap-analyser.md +40 -0
- package/.mindforge/intelligence/smart-compaction.md +71 -0
- package/.mindforge/metrics/METRICS-SCHEMA.md +42 -0
- package/.mindforge/metrics/quality-tracker.md +32 -0
- package/.mindforge/monorepo/cross-package-planner.md +114 -0
- package/.mindforge/monorepo/dependency-graph-builder.md +32 -0
- package/.mindforge/monorepo/workspace-detector.md +129 -0
- package/.mindforge/org/CONVENTIONS.md +62 -0
- package/.mindforge/org/ORG.md +51 -0
- package/.mindforge/org/SECURITY.md +50 -0
- package/.mindforge/org/TOOLS.md +53 -0
- package/.mindforge/org/integrations/INTEGRATIONS-CONFIG.md +58 -0
- package/.mindforge/org/skills/MANIFEST.md +38 -0
- package/.mindforge/personas/analyst.md +52 -0
- package/.mindforge/personas/architect.md +75 -0
- package/.mindforge/personas/debug-specialist.md +52 -0
- package/.mindforge/personas/developer.md +85 -0
- package/.mindforge/personas/overrides/README.md +85 -0
- package/.mindforge/personas/qa-engineer.md +61 -0
- package/.mindforge/personas/release-manager.md +76 -0
- package/.mindforge/personas/security-reviewer.md +91 -0
- package/.mindforge/personas/tech-writer.md +51 -0
- package/.mindforge/plugins/PLUGINS-MANIFEST.md +23 -0
- package/.mindforge/plugins/plugin-loader.md +93 -0
- package/.mindforge/plugins/plugin-registry.md +44 -0
- package/.mindforge/plugins/plugin-schema.md +68 -0
- package/.mindforge/pr-review/ai-reviewer.md +266 -0
- package/.mindforge/pr-review/finding-formatter.md +46 -0
- package/.mindforge/pr-review/review-prompt-templates.md +44 -0
- package/.mindforge/production/compatibility-layer.md +39 -0
- package/.mindforge/production/migration-engine.md +52 -0
- package/.mindforge/production/production-checklist.md +165 -0
- package/.mindforge/production/token-optimiser.md +68 -0
- package/.mindforge/skills/accessibility/SKILL.md +106 -0
- package/.mindforge/skills/api-design/SKILL.md +98 -0
- package/.mindforge/skills/code-quality/SKILL.md +88 -0
- package/.mindforge/skills/data-privacy/SKILL.md +126 -0
- package/.mindforge/skills/database-patterns/SKILL.md +192 -0
- package/.mindforge/skills/documentation/SKILL.md +91 -0
- package/.mindforge/skills/incident-response/SKILL.md +180 -0
- package/.mindforge/skills/performance/SKILL.md +120 -0
- package/.mindforge/skills/security-review/SKILL.md +83 -0
- package/.mindforge/skills/testing-standards/SKILL.md +97 -0
- package/.mindforge/team/TEAM-PROFILE.md +42 -0
- package/.mindforge/team/multi-handoff.md +23 -0
- package/.mindforge/team/profiles/README.md +13 -0
- package/.mindforge/team/session-merger.md +18 -0
- package/.planning/ARCHITECTURE.md +0 -0
- package/.planning/AUDIT.jsonl +0 -0
- package/.planning/HANDOFF.json +28 -0
- package/.planning/PROJECT.md +33 -0
- package/.planning/RELEASE-CHECKLIST.md +68 -0
- package/.planning/REQUIREMENTS.md +0 -0
- package/.planning/ROADMAP.md +0 -0
- package/.planning/STATE.md +31 -0
- package/.planning/approvals/.gitkeep +1 -0
- package/.planning/archive/.gitkeep +1 -0
- package/.planning/audit-archive/.gitkeep +1 -0
- package/.planning/decisions/.gitkeep +0 -0
- package/.planning/decisions/ADR-001-handoff-tracking.md +41 -0
- package/.planning/decisions/ADR-002-markdown-commands.md +46 -0
- package/.planning/decisions/ADR-003-skills-trigger-model.md +37 -0
- package/.planning/decisions/ADR-004-wave-parallelism-model.md +45 -0
- package/.planning/decisions/ADR-005-append-only-audit-log.md +51 -0
- package/.planning/decisions/ADR-006-tiered-skills-system.md +22 -0
- package/.planning/decisions/ADR-007-trigger-keyword-model.md +22 -0
- package/.planning/decisions/ADR-008-just-in-time-skill-loading.md +29 -0
- package/.planning/decisions/ADR-009-enterprise-integration-retry-policy.md +8 -0
- package/.planning/decisions/ADR-010-governance-tier-escalation.md +8 -0
- package/.planning/decisions/ADR-011-multi-developer-handoff-contract.md +8 -0
- package/.planning/decisions/ADR-012-intelligence-feedback-loops.md +19 -0
- package/.planning/decisions/ADR-013-mindforge-md-constitution.md +16 -0
- package/.planning/decisions/ADR-014-metrics-as-signals-not-evaluation.md +15 -0
- package/.planning/decisions/ADR-015-npm-based-skill-registry.md +26 -0
- package/.planning/decisions/ADR-016-ci-exit-code-0-on-timeout.md +27 -0
- package/.planning/decisions/ADR-017-sdk-localhost-only.md +28 -0
- package/.planning/decisions/ADR-018-installer-self-install-detection.md +15 -0
- package/.planning/decisions/ADR-019-self-update-scope-preservation.md +14 -0
- package/.planning/decisions/ADR-020-v1.0.0-stable-interface-contract.md +23 -0
- package/.planning/jira-sync.json +9 -0
- package/.planning/milestones/.gitkeep +1 -0
- package/.planning/phases/day1/REVIEW-DAY1.md +50 -0
- package/.planning/phases/day1/SECURITY-REVIEW-DAY1.md +15 -0
- package/.planning/phases/day2/REVIEW-DAY2.md +521 -0
- package/.planning/phases/day3/REVIEW-DAY3.md +234 -0
- package/.planning/slack-threads.json +6 -0
- package/CHANGELOG.md +175 -0
- package/LICENSE +21 -0
- package/MINDFORGE.md +76 -0
- package/README.md +182 -0
- package/RELEASENOTES.md +41 -0
- package/SECURITY.md +4 -0
- package/bin/install.js +120 -0
- package/bin/installer-core.js +292 -0
- package/bin/migrations/0.1.0-to-0.5.0.js +37 -0
- package/bin/migrations/0.5.0-to-0.6.0.js +17 -0
- package/bin/migrations/0.6.0-to-1.0.0.js +100 -0
- package/bin/migrations/migrate.js +151 -0
- package/bin/migrations/schema-versions.js +64 -0
- package/bin/updater/changelog-fetcher.js +62 -0
- package/bin/updater/self-update.js +169 -0
- package/bin/updater/version-comparator.js +68 -0
- package/bin/validate-config.js +92 -0
- package/bin/wizard/config-generator.js +112 -0
- package/bin/wizard/environment-detector.js +76 -0
- package/bin/wizard/setup-wizard.js +237 -0
- package/docs/Context/Master-Context.md +701 -0
- package/docs/architecture/README.md +35 -0
- package/docs/architecture/decision-records-index.md +26 -0
- package/docs/ci-cd-integration.md +30 -0
- package/docs/ci-quickstart.md +78 -0
- package/docs/commands-reference.md +11 -0
- package/docs/contributing/CONTRIBUTING.md +38 -0
- package/docs/contributing/plugin-authoring.md +50 -0
- package/docs/contributing/skill-authoring.md +41 -0
- package/docs/enterprise-setup.md +25 -0
- package/docs/faq.md +38 -0
- package/docs/getting-started.md +36 -0
- package/docs/governance-guide.md +23 -0
- package/docs/mindforge-md-reference.md +53 -0
- package/docs/monorepo-guide.md +26 -0
- package/docs/persona-customisation.md +56 -0
- package/docs/quick-verify.md +33 -0
- package/docs/reference/audit-events.md +53 -0
- package/docs/reference/commands.md +82 -0
- package/docs/reference/config-reference.md +64 -0
- package/docs/reference/sdk-api.md +48 -0
- package/docs/reference/skills-api.md +57 -0
- package/docs/release-checklist-guide.md +37 -0
- package/docs/requirements.md +29 -0
- package/docs/sdk-reference.md +27 -0
- package/docs/security/SECURITY.md +42 -0
- package/docs/security/penetration-test-results.md +31 -0
- package/docs/security/threat-model.md +142 -0
- package/docs/skills-authoring-guide.md +119 -0
- package/docs/skills-publishing-guide.md +21 -0
- package/docs/team-setup-guide.md +21 -0
- package/docs/troubleshooting.md +119 -0
- package/docs/tutorial.md +195 -0
- package/docs/upgrade.md +44 -0
- package/docs/user-guide.md +131 -0
- package/docs/usp-features.md +214 -0
- package/eslint.config.mjs +31 -0
- package/examples/starter-project/.planning/AUDIT.jsonl +1 -0
- package/examples/starter-project/.planning/HANDOFF.json +23 -0
- package/examples/starter-project/.planning/PROJECT.md +27 -0
- package/examples/starter-project/.planning/STATE.md +10 -0
- package/examples/starter-project/MINDFORGE.md +40 -0
- package/examples/starter-project/README.md +14 -0
- package/implementation-roadmap/day-1-imp/DAY1-HARDEN.md +823 -0
- package/implementation-roadmap/day-1-imp/DAY1-IMPLEMENT.md +2459 -0
- package/implementation-roadmap/day-1-imp/DAY1-REVIEW.md +288 -0
- package/implementation-roadmap/day-2-imp/DAY2-HARDEN.md +954 -0
- package/implementation-roadmap/day-2-imp/DAY2-IMPLEMENT.md +2347 -0
- package/implementation-roadmap/day-2-imp/DAY2-REVIEW.md +422 -0
- package/implementation-roadmap/day-3-imp/DAY3-HARDEN.md +870 -0
- package/implementation-roadmap/day-3-imp/DAY3-IMPLEMENT.md +2798 -0
- package/implementation-roadmap/day-3-imp/DAY3-REVIEW.md +484 -0
- package/implementation-roadmap/day-4-imp/DAY4-HARDEN.md +1087 -0
- package/implementation-roadmap/day-4-imp/DAY4-IMPLEMENT.md +2874 -0
- package/implementation-roadmap/day-4-imp/DAY4-REVIEW.md +386 -0
- package/implementation-roadmap/day-5-imp/DAY5-HARDEN.md +1078 -0
- package/implementation-roadmap/day-5-imp/DAY5-IMPLEMENT.md +3151 -0
- package/implementation-roadmap/day-5-imp/DAY5-REVIEW.md +345 -0
- package/implementation-roadmap/day-6-imp/DAY6-COMPLETE.md +3919 -0
- package/implementation-roadmap/day-7-imp-prod/DAY7-PRODUCTION-FINAL.md +4513 -0
- package/package.json +31 -0
- package/sdk/README.md +69 -0
- package/sdk/eslint.config.mjs +34 -0
- package/sdk/package-lock.json +1507 -0
- package/sdk/package.json +30 -0
- package/sdk/src/client.ts +133 -0
- package/sdk/src/commands.ts +63 -0
- package/sdk/src/events.ts +166 -0
- package/sdk/src/index.ts +22 -0
- package/sdk/src/types.ts +87 -0
- package/sdk/tsconfig.json +13 -0
- package/tests/audit.test.js +206 -0
- package/tests/ci-mode.test.js +162 -0
- package/tests/compaction.test.js +161 -0
- package/tests/distribution.test.js +205 -0
- package/tests/e2e.test.js +618 -0
- package/tests/governance.test.js +130 -0
- package/tests/install.test.js +209 -0
- package/tests/integrations.test.js +128 -0
- package/tests/intelligence.test.js +117 -0
- package/tests/metrics.test.js +96 -0
- package/tests/migration.test.js +309 -0
- package/tests/production.test.js +416 -0
- package/tests/sdk.test.js +200 -0
- package/tests/skills-platform.test.js +403 -0
- package/tests/wave-engine.test.js +338 -0
|
@@ -0,0 +1,3919 @@
|
|
|
1
|
+
# MindForge — Day 6: Distribution, CI, SDK & Scale
|
|
2
|
+
# Branch: `feat/mindforge-distribution-platform`
|
|
3
|
+
# Prerequisite: `feat/mindforge-intelligence-layer` merged to `main`
|
|
4
|
+
# Version target: v0.6.0
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## BRANCH SETUP
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
git checkout main
|
|
12
|
+
git pull origin main
|
|
13
|
+
git checkout -b feat/mindforge-distribution-platform
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Verify all prior days are present and all 9 test suites pass:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
cat package.json | grep '"version"' # Must be "0.5.0"
|
|
20
|
+
node tests/install.test.js && \
|
|
21
|
+
node tests/wave-engine.test.js && \
|
|
22
|
+
node tests/audit.test.js && \
|
|
23
|
+
node tests/compaction.test.js && \
|
|
24
|
+
node tests/skills-platform.test.js && \
|
|
25
|
+
node tests/integrations.test.js && \
|
|
26
|
+
node tests/governance.test.js && \
|
|
27
|
+
node tests/intelligence.test.js && \
|
|
28
|
+
node tests/metrics.test.js
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## DAY 6 SCOPE
|
|
34
|
+
|
|
35
|
+
Day 6 builds the **Distribution, CI/CD, and Scale Layer** — making MindForge
|
|
36
|
+
a platform that organisations can publish to, integrate into pipelines, embed
|
|
37
|
+
in tools, and run at organisation-wide scale.
|
|
38
|
+
|
|
39
|
+
| Component | Description |
|
|
40
|
+
|---|---|
|
|
41
|
+
| Public Skills Registry | `npx mindforge-skills install/publish/search` — npm-based skill distribution |
|
|
42
|
+
| Monorepo & Multi-repo Support | Workspace-aware execution across packages/apps |
|
|
43
|
+
| MindForge CI Mode | Non-interactive execution in GitHub Actions / GitLab CI / Jenkins |
|
|
44
|
+
| AI-Generated PR Reviews | Claude API-powered code review with structured findings |
|
|
45
|
+
| `/mindforge:init-org` | Organisation-wide MindForge setup and standardisation |
|
|
46
|
+
| MindForge SDK | Programmatic TypeScript API for embedding in tools |
|
|
47
|
+
| MINDFORGE.md Schema Validation | JSON Schema validator for config file correctness |
|
|
48
|
+
| Real-time Progress Streaming | SSE-based progress events for tooling integration |
|
|
49
|
+
| Skill Quality Benchmarking | Automated skill effectiveness measurement |
|
|
50
|
+
| Day 6 test suites | `tests/distribution.test.js`, `tests/ci-mode.test.js`, `tests/sdk.test.js` |
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
# ═══════════════════════════════════════════════════════════════
|
|
55
|
+
# PART 1: IMPLEMENTATION PROMPT
|
|
56
|
+
# ═══════════════════════════════════════════════════════════════
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## TASK 1 — Scaffold Day 6 directory additions
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Distribution engine
|
|
64
|
+
mkdir -p .mindforge/distribution
|
|
65
|
+
touch .mindforge/distribution/registry-client.md
|
|
66
|
+
touch .mindforge/distribution/skill-publisher.md
|
|
67
|
+
touch .mindforge/distribution/skill-validator.md
|
|
68
|
+
touch .mindforge/distribution/registry-schema.md
|
|
69
|
+
|
|
70
|
+
# CI/CD engine
|
|
71
|
+
mkdir -p .mindforge/ci
|
|
72
|
+
touch .mindforge/ci/ci-mode.md
|
|
73
|
+
touch .mindforge/ci/github-actions-adapter.md
|
|
74
|
+
touch .mindforge/ci/gitlab-ci-adapter.md
|
|
75
|
+
touch .mindforge/ci/jenkins-adapter.md
|
|
76
|
+
touch .mindforge/ci/ci-config-schema.md
|
|
77
|
+
|
|
78
|
+
# Monorepo support
|
|
79
|
+
mkdir -p .mindforge/monorepo
|
|
80
|
+
touch .mindforge/monorepo/workspace-detector.md
|
|
81
|
+
touch .mindforge/monorepo/cross-package-planner.md
|
|
82
|
+
touch .mindforge/monorepo/dependency-graph-builder.md
|
|
83
|
+
|
|
84
|
+
# SDK
|
|
85
|
+
mkdir -p sdk/src
|
|
86
|
+
touch sdk/src/index.ts
|
|
87
|
+
touch sdk/src/types.ts
|
|
88
|
+
touch sdk/src/client.ts
|
|
89
|
+
touch sdk/src/commands.ts
|
|
90
|
+
touch sdk/src/events.ts
|
|
91
|
+
touch sdk/package.json
|
|
92
|
+
touch sdk/tsconfig.json
|
|
93
|
+
touch sdk/README.md
|
|
94
|
+
|
|
95
|
+
# AI PR Review engine
|
|
96
|
+
mkdir -p .mindforge/pr-review
|
|
97
|
+
touch .mindforge/pr-review/ai-reviewer.md
|
|
98
|
+
touch .mindforge/pr-review/review-prompt-templates.md
|
|
99
|
+
touch .mindforge/pr-review/finding-formatter.md
|
|
100
|
+
|
|
101
|
+
# MINDFORGE.md schema
|
|
102
|
+
touch .mindforge/MINDFORGE-SCHEMA.json
|
|
103
|
+
touch bin/validate-config.js
|
|
104
|
+
|
|
105
|
+
# New commands
|
|
106
|
+
touch .claude/commands/mindforge/init-org.md
|
|
107
|
+
touch .claude/commands/mindforge/publish-skill.md
|
|
108
|
+
touch .claude/commands/mindforge/install-skill.md
|
|
109
|
+
touch .claude/commands/mindforge/pr-review.md
|
|
110
|
+
touch .claude/commands/mindforge/workspace.md
|
|
111
|
+
touch .claude/commands/mindforge/benchmark.md
|
|
112
|
+
|
|
113
|
+
# Mirror to Antigravity
|
|
114
|
+
for cmd in init-org publish-skill install-skill pr-review workspace benchmark; do
|
|
115
|
+
cp .claude/commands/mindforge/${cmd}.md .agent/mindforge/${cmd}.md
|
|
116
|
+
done
|
|
117
|
+
|
|
118
|
+
# CI config templates
|
|
119
|
+
mkdir -p .github/workflows
|
|
120
|
+
touch .github/workflows/mindforge-ci.yml
|
|
121
|
+
touch .gitlab-ci-mindforge.yml
|
|
122
|
+
|
|
123
|
+
# Tests
|
|
124
|
+
touch tests/distribution.test.js
|
|
125
|
+
touch tests/ci-mode.test.js
|
|
126
|
+
touch tests/sdk.test.js
|
|
127
|
+
|
|
128
|
+
# Docs
|
|
129
|
+
touch docs/ci-cd-integration.md
|
|
130
|
+
touch docs/sdk-reference.md
|
|
131
|
+
touch docs/skills-publishing-guide.md
|
|
132
|
+
touch docs/monorepo-guide.md
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Commit:**
|
|
136
|
+
```bash
|
|
137
|
+
git add .
|
|
138
|
+
git commit -m "chore(day6): scaffold Day 6 distribution platform directory structure"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## TASK 2 — Write the Public Skills Registry
|
|
144
|
+
|
|
145
|
+
### `.mindforge/distribution/registry-schema.md`
|
|
146
|
+
|
|
147
|
+
```markdown
|
|
148
|
+
# MindForge Skills Registry — Schema & Protocol
|
|
149
|
+
|
|
150
|
+
## Registry concept
|
|
151
|
+
The public MindForge Skills Registry is an npm-based distribution system.
|
|
152
|
+
Skills are published as npm packages with the `mindforge-skill-` prefix.
|
|
153
|
+
The registry leverages the existing npm ecosystem for versioning, discovery,
|
|
154
|
+
and distribution.
|
|
155
|
+
|
|
156
|
+
## Package naming convention
|
|
157
|
+
```
|
|
158
|
+
mindforge-skill-[category]-[name]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Examples:
|
|
162
|
+
- `mindforge-skill-security-owasp` — OWASP security review skill
|
|
163
|
+
- `mindforge-skill-db-postgres-patterns` — PostgreSQL-specific patterns
|
|
164
|
+
- `mindforge-skill-frontend-react-a11y` — React accessibility patterns
|
|
165
|
+
- `mindforge-skill-testing-playwright` — Playwright E2E testing patterns
|
|
166
|
+
- `mindforge-skill-api-graphql` — GraphQL API design patterns
|
|
167
|
+
|
|
168
|
+
## Package structure
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
mindforge-skill-[category]-[name]/
|
|
172
|
+
├── SKILL.md ← The skill file (required)
|
|
173
|
+
├── package.json ← npm metadata
|
|
174
|
+
├── README.md ← Human documentation
|
|
175
|
+
├── CHANGELOG.md ← Version history
|
|
176
|
+
├── examples/ ← Optional usage examples
|
|
177
|
+
│ └── example-task.md
|
|
178
|
+
├── scripts/ ← Optional helper scripts
|
|
179
|
+
│ └── helper.sh
|
|
180
|
+
└── tests/
|
|
181
|
+
└── skill.test.js ← Skill validation tests
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## `package.json` for a skill package
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"name": "mindforge-skill-security-owasp",
|
|
189
|
+
"version": "1.2.0",
|
|
190
|
+
"description": "OWASP Top 10 security review skill for MindForge",
|
|
191
|
+
"keywords": [
|
|
192
|
+
"mindforge",
|
|
193
|
+
"mindforge-skill",
|
|
194
|
+
"security",
|
|
195
|
+
"owasp",
|
|
196
|
+
"agentic-framework"
|
|
197
|
+
],
|
|
198
|
+
"mindforge": {
|
|
199
|
+
"type": "skill",
|
|
200
|
+
"skill-name": "security-owasp",
|
|
201
|
+
"category": "security",
|
|
202
|
+
"min-mindforge-version": "0.5.0",
|
|
203
|
+
"triggers": ["OWASP", "security review", "injection", "auth", "XSS"],
|
|
204
|
+
"tier-recommendation": 1
|
|
205
|
+
},
|
|
206
|
+
"files": ["SKILL.md", "README.md", "examples/", "scripts/"],
|
|
207
|
+
"license": "MIT",
|
|
208
|
+
"homepage": "https://mindforge.dev/skills/security-owasp",
|
|
209
|
+
"repository": { "type": "git", "url": "https://github.com/mindforge-dev/skill-security-owasp" }
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Registry discovery
|
|
214
|
+
|
|
215
|
+
The MindForge registry is the standard npm registry with keyword filtering:
|
|
216
|
+
```bash
|
|
217
|
+
# Search for skills
|
|
218
|
+
npm search mindforge-skill [query]
|
|
219
|
+
|
|
220
|
+
# Example searches:
|
|
221
|
+
npm search mindforge-skill security # Find security skills
|
|
222
|
+
npm search mindforge-skill react # Find React-specific skills
|
|
223
|
+
npm search mindforge-skill testing # Find testing skills
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Registry quality standards
|
|
227
|
+
|
|
228
|
+
A skill package published to the MindForge registry must pass:
|
|
229
|
+
1. Schema validation: `npx mindforge-cc validate-skill ./SKILL.md`
|
|
230
|
+
2. Required metadata: package.json `mindforge` field fully populated
|
|
231
|
+
3. No malicious content: npm security audit passes
|
|
232
|
+
4. Version policy: follows semver with documented breaking changes
|
|
233
|
+
5. License: MIT, Apache-2.0, or BSD (GPL derivatives not accepted)
|
|
234
|
+
|
|
235
|
+
## Local registry (private skills)
|
|
236
|
+
|
|
237
|
+
Organisations with private skills can use:
|
|
238
|
+
- Private npm registry (Verdaccio, Artifactory, GitHub Packages)
|
|
239
|
+
- Configure in `.mindforge/org/integrations/INTEGRATIONS-CONFIG.md`:
|
|
240
|
+
```
|
|
241
|
+
MINDFORGE_SKILL_REGISTRY=https://npm.your-org.internal/
|
|
242
|
+
```
|
|
243
|
+
- Skills from private registry install with the same `npx mindforge-skills install` command
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
### `.mindforge/distribution/registry-client.md`
|
|
249
|
+
|
|
250
|
+
```markdown
|
|
251
|
+
# MindForge Skills Registry — Client Protocol
|
|
252
|
+
|
|
253
|
+
## Purpose
|
|
254
|
+
Define how MindForge discovers, downloads, validates, and installs skills
|
|
255
|
+
from the public or private npm-based registry.
|
|
256
|
+
|
|
257
|
+
## Installation flow
|
|
258
|
+
|
|
259
|
+
### Step 1 — Resolve package name
|
|
260
|
+
```bash
|
|
261
|
+
# From skill name to package name:
|
|
262
|
+
SKILL_NAME="security-owasp"
|
|
263
|
+
PACKAGE_NAME="mindforge-skill-${SKILL_NAME}"
|
|
264
|
+
|
|
265
|
+
# Or if user provides full package name:
|
|
266
|
+
PACKAGE_NAME="mindforge-skill-security-owasp"
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Step 2 — Check if already installed
|
|
270
|
+
```bash
|
|
271
|
+
# Check local MANIFEST.md
|
|
272
|
+
grep "| ${SKILL_NAME} |" .mindforge/org/skills/MANIFEST.md && echo "Already installed"
|
|
273
|
+
|
|
274
|
+
# Check if SKILL.md exists
|
|
275
|
+
[ -f ".mindforge/skills/${SKILL_NAME}/SKILL.md" ] && echo "Skill file exists"
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Step 3 — Fetch from registry
|
|
279
|
+
```bash
|
|
280
|
+
# Download to temp directory
|
|
281
|
+
TEMP_DIR=$(mktemp -d)
|
|
282
|
+
npm pack "${PACKAGE_NAME}@latest" --pack-destination "${TEMP_DIR}" --quiet
|
|
283
|
+
|
|
284
|
+
# Extract the tarball
|
|
285
|
+
cd "${TEMP_DIR}"
|
|
286
|
+
TARBALL=$(ls *.tgz | head -1)
|
|
287
|
+
tar -xzf "${TARBALL}" --strip-components=1 -C "${TEMP_DIR}"
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Step 4 — Validate the downloaded skill
|
|
291
|
+
Run the full skill validator (see `skill-validator.md`) against the downloaded SKILL.md.
|
|
292
|
+
If validation fails: abort installation. Never install a skill that fails validation.
|
|
293
|
+
|
|
294
|
+
### Step 5 — Injection guard check
|
|
295
|
+
Run the injection guard from Day 3 (`loader.md`) against the skill content.
|
|
296
|
+
If injection patterns detected: abort, write AUDIT entry, alert user.
|
|
297
|
+
|
|
298
|
+
### Step 6 — Install to correct tier location
|
|
299
|
+
```bash
|
|
300
|
+
# Determine target tier from user input or package.json tier-recommendation
|
|
301
|
+
TIER="${USER_SPECIFIED_TIER:-2}"
|
|
302
|
+
|
|
303
|
+
if [ "${TIER}" = "1" ]; then
|
|
304
|
+
TARGET_DIR=".mindforge/skills/${SKILL_NAME}"
|
|
305
|
+
elif [ "${TIER}" = "2" ]; then
|
|
306
|
+
TARGET_DIR=".mindforge/org/skills/${SKILL_NAME}"
|
|
307
|
+
else
|
|
308
|
+
TARGET_DIR=".mindforge/project-skills/${SKILL_NAME}"
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
mkdir -p "${TARGET_DIR}"
|
|
312
|
+
cp "${TEMP_DIR}/SKILL.md" "${TARGET_DIR}/SKILL.md"
|
|
313
|
+
[ -d "${TEMP_DIR}/examples" ] && cp -r "${TEMP_DIR}/examples" "${TARGET_DIR}/"
|
|
314
|
+
[ -d "${TEMP_DIR}/scripts" ] && cp -r "${TEMP_DIR}/scripts" "${TARGET_DIR}/"
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Step 7 — Register in MANIFEST.md
|
|
318
|
+
```bash
|
|
319
|
+
# Add entry to the correct tier section of MANIFEST.md
|
|
320
|
+
SKILL_VERSION=$(node -e "console.log(require('${TEMP_DIR}/package.json').version)")
|
|
321
|
+
|
|
322
|
+
# Insert into MANIFEST.md under the appropriate tier section
|
|
323
|
+
# Format: | name | version | stable | min-mf-version | path |
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Step 8 — Clean up and report
|
|
327
|
+
```bash
|
|
328
|
+
rm -rf "${TEMP_DIR}"
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Report to user:
|
|
332
|
+
```
|
|
333
|
+
✅ Skill installed: ${SKILL_NAME} v${SKILL_VERSION} (Tier ${TIER})
|
|
334
|
+
Triggers: [list from SKILL.md frontmatter]
|
|
335
|
+
Path: ${TARGET_DIR}/SKILL.md
|
|
336
|
+
|
|
337
|
+
Run /mindforge:skills validate to confirm installation.
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Step 9 — Write AUDIT entry
|
|
341
|
+
```json
|
|
342
|
+
{
|
|
343
|
+
"event": "skill_installed",
|
|
344
|
+
"skill_name": "security-owasp",
|
|
345
|
+
"skill_version": "1.2.0",
|
|
346
|
+
"package_name": "mindforge-skill-security-owasp",
|
|
347
|
+
"tier": 2,
|
|
348
|
+
"source": "npm-registry | private-registry",
|
|
349
|
+
"validation_passed": true
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Update protocol
|
|
354
|
+
|
|
355
|
+
### Check for updates
|
|
356
|
+
```bash
|
|
357
|
+
# Compare installed version against registry latest
|
|
358
|
+
INSTALLED=$(grep "| ${SKILL_NAME} |" MANIFEST.md | awk -F'|' '{print $3}' | tr -d ' ')
|
|
359
|
+
LATEST=$(npm info "${PACKAGE_NAME}" version 2>/dev/null)
|
|
360
|
+
|
|
361
|
+
if [ "${INSTALLED}" != "${LATEST}" ]; then
|
|
362
|
+
echo "Update available: ${SKILL_NAME} v${INSTALLED} → v${LATEST}"
|
|
363
|
+
fi
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Update a skill
|
|
367
|
+
```bash
|
|
368
|
+
# Run install flow for latest version
|
|
369
|
+
# If MAJOR version bump: show breaking changes, require confirmation
|
|
370
|
+
# If MINOR/PATCH: update silently
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
## Uninstall protocol
|
|
374
|
+
```bash
|
|
375
|
+
# Remove skill files
|
|
376
|
+
rm -rf "${TARGET_DIR}"
|
|
377
|
+
|
|
378
|
+
# Remove from MANIFEST.md
|
|
379
|
+
sed -i "/| ${SKILL_NAME} |/d" .mindforge/org/skills/MANIFEST.md
|
|
380
|
+
|
|
381
|
+
# Write AUDIT entry
|
|
382
|
+
# Commit: "chore(skills): uninstall ${SKILL_NAME}"
|
|
383
|
+
```
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
---
|
|
387
|
+
|
|
388
|
+
### `.mindforge/distribution/skill-validator.md`
|
|
389
|
+
|
|
390
|
+
```markdown
|
|
391
|
+
# MindForge Skills Registry — Skill Validator
|
|
392
|
+
|
|
393
|
+
## Purpose
|
|
394
|
+
Validate a SKILL.md file before installation or publication.
|
|
395
|
+
Run as part of both `install-skill` and `publish-skill` commands.
|
|
396
|
+
|
|
397
|
+
## Validation levels
|
|
398
|
+
|
|
399
|
+
### Level 1 — Schema validation (always runs)
|
|
400
|
+
```bash
|
|
401
|
+
npx mindforge-cc validate-skill ./SKILL.md
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Checks:
|
|
405
|
+
- [ ] File starts with `---` (YAML frontmatter delimiter)
|
|
406
|
+
- [ ] Frontmatter closes with `---`
|
|
407
|
+
- [ ] `name:` field present and matches kebab-case pattern `[a-z][a-z0-9-]+`
|
|
408
|
+
- [ ] `version:` field present and valid semver `\d+\.\d+\.\d+`
|
|
409
|
+
- [ ] `status:` is one of: `stable`, `beta`, `alpha`, `deprecated`
|
|
410
|
+
- [ ] `triggers:` field present and has >= 5 keywords
|
|
411
|
+
- [ ] No trigger keyword is fewer than 3 characters (too generic)
|
|
412
|
+
- [ ] `min_mindforge_version:` present and valid semver
|
|
413
|
+
|
|
414
|
+
### Level 2 — Content validation (runs after schema passes)
|
|
415
|
+
- [ ] File size between 1KB and 200KB (not too small, not too large)
|
|
416
|
+
- [ ] Contains `## Mandatory actions` or `## When this skill is active` section
|
|
417
|
+
- [ ] Contains at least one checklist item (`- [ ]`) for self-verification
|
|
418
|
+
- [ ] Does not contain any injection patterns (from `loader.md` guard)
|
|
419
|
+
- [ ] Code examples have language specifiers in code fences (not bare ```)
|
|
420
|
+
- [ ] No placeholder text: `[placeholder]`, `TODO`, `[your-name]`
|
|
421
|
+
|
|
422
|
+
### Level 3 — Quality validation (optional — runs for publication)
|
|
423
|
+
- [ ] At least 3 code examples
|
|
424
|
+
- [ ] CHANGELOG in frontmatter has at least current version entry
|
|
425
|
+
- [ ] `breaking_changes:` field present (even if empty list)
|
|
426
|
+
- [ ] Examples directory has at least one example file
|
|
427
|
+
- [ ] README.md exists in the package
|
|
428
|
+
|
|
429
|
+
## Validator output
|
|
430
|
+
|
|
431
|
+
```
|
|
432
|
+
MindForge Skill Validator — SKILL.md
|
|
433
|
+
──────────────────────────────────────────────────────────────
|
|
434
|
+
|
|
435
|
+
Schema validation:
|
|
436
|
+
✅ Frontmatter valid
|
|
437
|
+
✅ name: security-owasp (valid)
|
|
438
|
+
✅ version: 1.2.0 (valid semver)
|
|
439
|
+
✅ status: stable
|
|
440
|
+
✅ triggers: 31 keywords (min: 5)
|
|
441
|
+
✅ min_mindforge_version: 0.5.0
|
|
442
|
+
|
|
443
|
+
Content validation:
|
|
444
|
+
✅ File size: 8.4KB (1KB-200KB range)
|
|
445
|
+
✅ Mandatory actions section present
|
|
446
|
+
✅ Self-check checklist present (7 items)
|
|
447
|
+
✅ No injection patterns detected
|
|
448
|
+
✅ Code examples have language specifiers
|
|
449
|
+
✅ No placeholder text found
|
|
450
|
+
|
|
451
|
+
Quality validation:
|
|
452
|
+
✅ 5 code examples found
|
|
453
|
+
✅ CHANGELOG has version 1.2.0 entry
|
|
454
|
+
✅ Breaking changes documented
|
|
455
|
+
⚠️ Examples directory has 1 file (recommend: 3+)
|
|
456
|
+
|
|
457
|
+
──────────────────────────────────────────────────────────────
|
|
458
|
+
Result: VALID with 1 warning
|
|
459
|
+
Ready for: installation ✅ | publication ✅ (warning noted)
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
## `bin/validate-config.js` — MINDFORGE.md validator
|
|
463
|
+
|
|
464
|
+
```javascript
|
|
465
|
+
#!/usr/bin/env node
|
|
466
|
+
/**
|
|
467
|
+
* MindForge configuration validator
|
|
468
|
+
* Validates MINDFORGE.md against the JSON schema
|
|
469
|
+
* Usage: node bin/validate-config.js [path-to-MINDFORGE.md]
|
|
470
|
+
*/
|
|
471
|
+
|
|
472
|
+
'use strict';
|
|
473
|
+
|
|
474
|
+
const fs = require('fs');
|
|
475
|
+
const path = require('path');
|
|
476
|
+
|
|
477
|
+
const CONFIG_PATH = process.argv[2] || 'MINDFORGE.md';
|
|
478
|
+
const SCHEMA_PATH = '.mindforge/MINDFORGE-SCHEMA.json';
|
|
479
|
+
|
|
480
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
481
|
+
console.log('ℹ️ MINDFORGE.md not found — using all defaults. Create one to customise.');
|
|
482
|
+
process.exit(0);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (!fs.existsSync(SCHEMA_PATH)) {
|
|
486
|
+
console.log('ℹ️ MINDFORGE-SCHEMA.json not found — skipping schema validation.');
|
|
487
|
+
process.exit(0);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
const content = fs.readFileSync(CONFIG_PATH, 'utf8');
|
|
491
|
+
const schema = JSON.parse(fs.readFileSync(SCHEMA_PATH, 'utf8'));
|
|
492
|
+
|
|
493
|
+
const errors = [];
|
|
494
|
+
const warnings = [];
|
|
495
|
+
|
|
496
|
+
// Parse key=value pairs from MINDFORGE.md
|
|
497
|
+
const settings = {};
|
|
498
|
+
const lines = content.split('\n');
|
|
499
|
+
lines.forEach(line => {
|
|
500
|
+
const match = line.match(/^([A-Z_]+)=(.+)$/);
|
|
501
|
+
if (match) {
|
|
502
|
+
const [, key, value] = match;
|
|
503
|
+
settings[key] = value.trim();
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
// Validate against schema
|
|
508
|
+
for (const [key, def] of Object.entries(schema.properties || {})) {
|
|
509
|
+
const value = settings[key];
|
|
510
|
+
|
|
511
|
+
if (def.required && !value) {
|
|
512
|
+
errors.push(`${key} is required but not set`);
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (!value) continue;
|
|
517
|
+
|
|
518
|
+
if (def.type === 'number') {
|
|
519
|
+
const num = parseFloat(value);
|
|
520
|
+
if (isNaN(num)) errors.push(`${key}: expected number, got "${value}"`);
|
|
521
|
+
if (def.minimum !== undefined && num < def.minimum)
|
|
522
|
+
errors.push(`${key}: ${num} is below minimum ${def.minimum}`);
|
|
523
|
+
if (def.maximum !== undefined && num > def.maximum)
|
|
524
|
+
errors.push(`${key}: ${num} exceeds maximum ${def.maximum}`);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (def.type === 'enum' && !def.values.includes(value)) {
|
|
528
|
+
errors.push(`${key}: "${value}" is not valid. Options: ${def.values.join(', ')}`);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (def.type === 'boolean' && !['true','false'].includes(value)) {
|
|
532
|
+
errors.push(`${key}: expected true or false, got "${value}"`);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (def.nonOverridable) {
|
|
536
|
+
warnings.push(`${key}: this is a non-overridable governance primitive (value will be ignored)`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Report
|
|
541
|
+
const total = errors.length + warnings.length;
|
|
542
|
+
if (total === 0) {
|
|
543
|
+
console.log(`✅ MINDFORGE.md valid — ${Object.keys(settings).length} settings configured`);
|
|
544
|
+
process.exit(0);
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (errors.length) {
|
|
548
|
+
console.error(`❌ MINDFORGE.md has ${errors.length} error(s):`);
|
|
549
|
+
errors.forEach(e => console.error(` • ${e}`));
|
|
550
|
+
}
|
|
551
|
+
if (warnings.length) {
|
|
552
|
+
console.warn(`⚠️ MINDFORGE.md has ${warnings.length} warning(s):`);
|
|
553
|
+
warnings.forEach(w => console.warn(` • ${w}`));
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
process.exit(errors.length ? 1 : 0);
|
|
557
|
+
```
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**Commit:**
|
|
561
|
+
```bash
|
|
562
|
+
git add .mindforge/distribution/ bin/validate-config.js
|
|
563
|
+
git commit -m "feat(distribution): implement skills registry schema, client protocol, and validator"
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
## TASK 3 — Write MindForge CI Mode
|
|
569
|
+
|
|
570
|
+
### `.mindforge/ci/ci-mode.md`
|
|
571
|
+
|
|
572
|
+
```markdown
|
|
573
|
+
# MindForge CI Mode
|
|
574
|
+
|
|
575
|
+
## Purpose
|
|
576
|
+
Enable MindForge to run in non-interactive CI/CD environments.
|
|
577
|
+
CI mode is fully automated — no user prompts, no interactive approvals,
|
|
578
|
+
no waiting for human input. All decisions are pre-configured.
|
|
579
|
+
|
|
580
|
+
## Activating CI mode
|
|
581
|
+
|
|
582
|
+
CI mode activates automatically when:
|
|
583
|
+
1. `CI=true` environment variable is set (standard in GitHub Actions, GitLab CI, Jenkins)
|
|
584
|
+
2. `MINDFORGE_CI=true` is explicitly set
|
|
585
|
+
3. `process.stdin.isTTY === false` (piped or non-interactive shell)
|
|
586
|
+
|
|
587
|
+
In CI mode:
|
|
588
|
+
- All interactive prompts are skipped — pre-configured answers are used
|
|
589
|
+
- Approval workflows use the CI approval policy (see below)
|
|
590
|
+
- Progress output is structured JSON (parseable by CI log processors)
|
|
591
|
+
- Exit codes communicate status (0 = success, 1 = failure, 2 = warning)
|
|
592
|
+
- Slack/GitHub notifications are sent as configured (integrations still work)
|
|
593
|
+
|
|
594
|
+
## CI mode configuration
|
|
595
|
+
|
|
596
|
+
Add to `MINDFORGE.md` for CI-specific settings:
|
|
597
|
+
|
|
598
|
+
```
|
|
599
|
+
## CI/CD configuration
|
|
600
|
+
|
|
601
|
+
# Which phases to execute in CI (comma-separated phase numbers, or "all")
|
|
602
|
+
CI_EXECUTE_PHASES=all
|
|
603
|
+
|
|
604
|
+
# Auto-approve Tier 2 changes in CI (default: false — safer)
|
|
605
|
+
CI_AUTO_APPROVE_TIER2=false
|
|
606
|
+
|
|
607
|
+
# Block CI on MEDIUM security findings (default: false)
|
|
608
|
+
CI_BLOCK_ON_MEDIUM_SECURITY=false
|
|
609
|
+
|
|
610
|
+
# Run full security scan in CI (default: true)
|
|
611
|
+
CI_SECURITY_SCAN=true
|
|
612
|
+
|
|
613
|
+
# Skip human UAT in CI — only run automated verification (default: true)
|
|
614
|
+
CI_SKIP_UAT=true
|
|
615
|
+
|
|
616
|
+
# Fail CI if test coverage drops below this threshold
|
|
617
|
+
CI_MIN_COVERAGE_PCT=80
|
|
618
|
+
|
|
619
|
+
# Timeout for entire CI run in minutes (default: 60)
|
|
620
|
+
CI_TIMEOUT_MINUTES=60
|
|
621
|
+
|
|
622
|
+
# Output format for CI logs: json | text | github-annotations
|
|
623
|
+
CI_OUTPUT_FORMAT=github-annotations
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
## CI approval policy
|
|
627
|
+
|
|
628
|
+
Tier 1: always auto-approved (same as interactive mode)
|
|
629
|
+
Tier 2: auto-approved IF `CI_AUTO_APPROVE_TIER2=true` in MINDFORGE.md
|
|
630
|
+
rejected (build fails) IF `CI_AUTO_APPROVE_TIER2=false`
|
|
631
|
+
Tier 3: ALWAYS fails the build in CI — Tier 3 changes require human review.
|
|
632
|
+
|
|
633
|
+
The CI build should never be the first time a Tier 3 change is seen.
|
|
634
|
+
Engineers should get Tier 3 changes approved BEFORE pushing to CI.
|
|
635
|
+
|
|
636
|
+
## CI output format
|
|
637
|
+
|
|
638
|
+
### JSON format (`CI_OUTPUT_FORMAT=json`)
|
|
639
|
+
```json
|
|
640
|
+
{
|
|
641
|
+
"mindforge_version": "0.6.0",
|
|
642
|
+
"phase": 3,
|
|
643
|
+
"status": "running | success | failure | warning",
|
|
644
|
+
"timestamp": "ISO-8601",
|
|
645
|
+
"tasks_completed": 5,
|
|
646
|
+
"tasks_total": 8,
|
|
647
|
+
"current_task": "Plan 3-06: Implement auth middleware",
|
|
648
|
+
"gates": {
|
|
649
|
+
"secrets_clean": true,
|
|
650
|
+
"tests_passing": true,
|
|
651
|
+
"security_findings_critical": 0,
|
|
652
|
+
"security_findings_high": 0,
|
|
653
|
+
"coverage_pct": 84
|
|
654
|
+
},
|
|
655
|
+
"events": [
|
|
656
|
+
{ "time": "ISO-8601", "type": "task_completed", "plan": "3-01", "commit": "abc1234" },
|
|
657
|
+
{ "time": "ISO-8601", "type": "security_finding", "severity": "MEDIUM", "finding": "..." }
|
|
658
|
+
]
|
|
659
|
+
}
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
### GitHub Annotations format (`CI_OUTPUT_FORMAT=github-annotations`)
|
|
663
|
+
```
|
|
664
|
+
::group::MindForge Phase 3 Execution
|
|
665
|
+
::notice::Task 3-01 completed: Create auth middleware [abc1234]
|
|
666
|
+
::notice::Task 3-02 completed: Add JWT validation [def5678]
|
|
667
|
+
::warning file=src/auth/session.ts,line=47::Medium security finding: Verbose error message exposes stack trace
|
|
668
|
+
::notice::Phase 3 complete: 8/8 tasks, all tests passing
|
|
669
|
+
::endgroup::
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
### Error output
|
|
673
|
+
```
|
|
674
|
+
::error::MindForge CI failed: Task 3-06 verify step failed
|
|
675
|
+
::error file=src/api/users.ts,line=89::TypeScript error: Type 'string' is not assignable to 'number'
|
|
676
|
+
::error::Quality gate failed: test coverage 68% (minimum: 80%)
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
## CI environment requirements
|
|
680
|
+
|
|
681
|
+
```bash
|
|
682
|
+
# Required environment variables for full CI functionality:
|
|
683
|
+
ANTHROPIC_API_KEY= # Claude API access (required for AI features)
|
|
684
|
+
GITHUB_TOKEN= # For PR creation and status checks
|
|
685
|
+
JIRA_API_TOKEN= # Optional — for Jira sync
|
|
686
|
+
SLACK_BOT_TOKEN= # Optional — for notifications
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
## CI timeout and resource management
|
|
690
|
+
|
|
691
|
+
```
|
|
692
|
+
Default execution limits per CI run:
|
|
693
|
+
Maximum tasks per session: 25
|
|
694
|
+
Maximum parallel subagents: 5 (respect CI runner memory limits)
|
|
695
|
+
Task timeout: 10 minutes per task
|
|
696
|
+
Phase timeout: 45 minutes
|
|
697
|
+
Full CI timeout: 60 minutes
|
|
698
|
+
|
|
699
|
+
On timeout:
|
|
700
|
+
1. Write current state to HANDOFF.json
|
|
701
|
+
2. Exit with code 2 (warning — not failure)
|
|
702
|
+
3. The next CI run resumes from HANDOFF.json
|
|
703
|
+
4. Report: "CI timeout reached. Run will resume from: [next_task]"
|
|
704
|
+
```
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
709
|
+
### `.mindforge/ci/github-actions-adapter.md`
|
|
710
|
+
|
|
711
|
+
```markdown
|
|
712
|
+
# MindForge — GitHub Actions Integration
|
|
713
|
+
|
|
714
|
+
## Purpose
|
|
715
|
+
Define the GitHub Actions workflow that integrates MindForge into CI/CD pipelines.
|
|
716
|
+
|
|
717
|
+
## Workflow file: `.github/workflows/mindforge-ci.yml`
|
|
718
|
+
|
|
719
|
+
```yaml
|
|
720
|
+
name: MindForge CI
|
|
721
|
+
|
|
722
|
+
on:
|
|
723
|
+
push:
|
|
724
|
+
branches: [ main, 'feat/**' ]
|
|
725
|
+
pull_request:
|
|
726
|
+
branches: [ main ]
|
|
727
|
+
|
|
728
|
+
env:
|
|
729
|
+
CI: true
|
|
730
|
+
MINDFORGE_CI: true
|
|
731
|
+
NODE_VERSION: '20'
|
|
732
|
+
|
|
733
|
+
jobs:
|
|
734
|
+
mindforge-health:
|
|
735
|
+
name: MindForge Health Check
|
|
736
|
+
runs-on: ubuntu-latest
|
|
737
|
+
steps:
|
|
738
|
+
- uses: actions/checkout@v4
|
|
739
|
+
with:
|
|
740
|
+
fetch-depth: 0 # Full history for git-based checks
|
|
741
|
+
|
|
742
|
+
- uses: actions/setup-node@v4
|
|
743
|
+
with:
|
|
744
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
745
|
+
cache: 'npm'
|
|
746
|
+
|
|
747
|
+
- name: Install MindForge
|
|
748
|
+
run: npx mindforge-cc@latest --claude --local
|
|
749
|
+
|
|
750
|
+
- name: Validate MINDFORGE.md
|
|
751
|
+
run: node bin/validate-config.js
|
|
752
|
+
|
|
753
|
+
- name: Run MindForge health check
|
|
754
|
+
run: |
|
|
755
|
+
# Health check in CI mode — outputs structured JSON
|
|
756
|
+
echo "::group::MindForge Health Report"
|
|
757
|
+
node -e "
|
|
758
|
+
// CI health check simulation
|
|
759
|
+
// In full implementation: calls mindforge health engine
|
|
760
|
+
const fs = require('fs');
|
|
761
|
+
const files = ['.planning/AUDIT.jsonl', '.planning/STATE.md', '.planning/HANDOFF.json'];
|
|
762
|
+
let allPresent = true;
|
|
763
|
+
files.forEach(f => {
|
|
764
|
+
if (!fs.existsSync(f)) {
|
|
765
|
+
console.log('::warning::Missing state file: ' + f);
|
|
766
|
+
allPresent = false;
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
console.log(allPresent ? '::notice::All state files present' : '::warning::Some state files missing');
|
|
770
|
+
"
|
|
771
|
+
echo "::endgroup::"
|
|
772
|
+
|
|
773
|
+
mindforge-security:
|
|
774
|
+
name: Security Scan
|
|
775
|
+
runs-on: ubuntu-latest
|
|
776
|
+
needs: mindforge-health
|
|
777
|
+
steps:
|
|
778
|
+
- uses: actions/checkout@v4
|
|
779
|
+
|
|
780
|
+
- uses: actions/setup-node@v4
|
|
781
|
+
with:
|
|
782
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
783
|
+
cache: 'npm'
|
|
784
|
+
|
|
785
|
+
- name: Install dependencies
|
|
786
|
+
run: npm ci
|
|
787
|
+
|
|
788
|
+
- name: MindForge secret detection
|
|
789
|
+
run: |
|
|
790
|
+
echo "::group::Secret Detection"
|
|
791
|
+
# Secret patterns — exits non-zero if found
|
|
792
|
+
if grep -rE "(sk-[a-zA-Z0-9]{20,}|ghp_[a-zA-Z0-9]+|xoxb-[a-zA-Z0-9-]+)" \
|
|
793
|
+
--include="*.ts" --include="*.js" --include="*.json" \
|
|
794
|
+
--exclude-dir=node_modules --exclude-dir=.git \
|
|
795
|
+
. 2>/dev/null; then
|
|
796
|
+
echo "::error::Credentials detected in source files. Remove before merging."
|
|
797
|
+
exit 1
|
|
798
|
+
fi
|
|
799
|
+
echo "::notice::No credentials detected ✅"
|
|
800
|
+
echo "::endgroup::"
|
|
801
|
+
|
|
802
|
+
- name: Dependency audit
|
|
803
|
+
run: |
|
|
804
|
+
echo "::group::Dependency Audit"
|
|
805
|
+
npm audit --audit-level=high 2>&1 || {
|
|
806
|
+
echo "::error::High/critical vulnerabilities found. Run: npm audit fix"
|
|
807
|
+
exit 1
|
|
808
|
+
}
|
|
809
|
+
echo "::endgroup::"
|
|
810
|
+
|
|
811
|
+
mindforge-quality:
|
|
812
|
+
name: Code Quality Gates
|
|
813
|
+
runs-on: ubuntu-latest
|
|
814
|
+
needs: mindforge-health
|
|
815
|
+
env:
|
|
816
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
817
|
+
steps:
|
|
818
|
+
- uses: actions/checkout@v4
|
|
819
|
+
|
|
820
|
+
- uses: actions/setup-node@v4
|
|
821
|
+
with:
|
|
822
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
823
|
+
cache: 'npm'
|
|
824
|
+
|
|
825
|
+
- name: Install dependencies
|
|
826
|
+
run: npm ci
|
|
827
|
+
|
|
828
|
+
- name: Type check
|
|
829
|
+
run: npx tsc --noEmit 2>&1 | while read line; do
|
|
830
|
+
echo "::error::$line"
|
|
831
|
+
done
|
|
832
|
+
|
|
833
|
+
- name: Lint
|
|
834
|
+
run: npx eslint . --ext .ts,.tsx --max-warnings 0
|
|
835
|
+
|
|
836
|
+
- name: Test suite with coverage
|
|
837
|
+
run: npm test -- --coverage
|
|
838
|
+
env:
|
|
839
|
+
COVERAGE_THRESHOLD: 80
|
|
840
|
+
|
|
841
|
+
- name: Check coverage threshold
|
|
842
|
+
run: |
|
|
843
|
+
COVERAGE=$(cat coverage/coverage-summary.json 2>/dev/null | \
|
|
844
|
+
node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); \
|
|
845
|
+
console.log(Math.floor(d.total.lines.pct))" 2>/dev/null || echo "0")
|
|
846
|
+
MIN=${CI_MIN_COVERAGE_PCT:-80}
|
|
847
|
+
if [ "${COVERAGE}" -lt "${MIN}" ]; then
|
|
848
|
+
echo "::error::Coverage ${COVERAGE}% is below minimum ${MIN}%"
|
|
849
|
+
exit 1
|
|
850
|
+
fi
|
|
851
|
+
echo "::notice::Coverage: ${COVERAGE}% ✅"
|
|
852
|
+
|
|
853
|
+
mindforge-ai-review:
|
|
854
|
+
name: AI Code Review
|
|
855
|
+
runs-on: ubuntu-latest
|
|
856
|
+
needs: [mindforge-security, mindforge-quality]
|
|
857
|
+
if: github.event_name == 'pull_request'
|
|
858
|
+
env:
|
|
859
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
860
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
861
|
+
steps:
|
|
862
|
+
- uses: actions/checkout@v4
|
|
863
|
+
with:
|
|
864
|
+
fetch-depth: 0
|
|
865
|
+
|
|
866
|
+
- uses: actions/setup-node@v4
|
|
867
|
+
with:
|
|
868
|
+
node-version: ${{ env.NODE_VERSION }}
|
|
869
|
+
cache: 'npm'
|
|
870
|
+
|
|
871
|
+
- name: Install MindForge
|
|
872
|
+
run: npx mindforge-cc@latest --claude --local
|
|
873
|
+
|
|
874
|
+
- name: Run AI PR Review
|
|
875
|
+
run: |
|
|
876
|
+
# Get the diff for this PR
|
|
877
|
+
git diff ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} > /tmp/pr.diff
|
|
878
|
+
|
|
879
|
+
# Run MindForge AI review (outputs GitHub annotations)
|
|
880
|
+
node -e "
|
|
881
|
+
// Placeholder for AI review execution
|
|
882
|
+
// In full implementation: calls Claude API via the pr-review engine
|
|
883
|
+
console.log('::notice::AI PR review completed — see review comment on PR');
|
|
884
|
+
"
|
|
885
|
+
|
|
886
|
+
- name: Post review as PR comment
|
|
887
|
+
uses: actions/github-script@v7
|
|
888
|
+
with:
|
|
889
|
+
script: |
|
|
890
|
+
const fs = require('fs');
|
|
891
|
+
const review = fs.existsSync('/tmp/mindforge-review.md') ?
|
|
892
|
+
fs.readFileSync('/tmp/mindforge-review.md', 'utf8') :
|
|
893
|
+
'✅ MindForge AI review: no significant issues found.';
|
|
894
|
+
|
|
895
|
+
await github.rest.pulls.createReview({
|
|
896
|
+
owner: context.repo.owner,
|
|
897
|
+
repo: context.repo.repo,
|
|
898
|
+
pull_number: context.issue.number,
|
|
899
|
+
body: review,
|
|
900
|
+
event: 'COMMENT'
|
|
901
|
+
});
|
|
902
|
+
```
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
**Commit:**
|
|
906
|
+
```bash
|
|
907
|
+
git add .mindforge/ci/ .github/workflows/mindforge-ci.yml
|
|
908
|
+
git commit -m "feat(ci): implement CI mode engine and GitHub Actions workflow"
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
---
|
|
912
|
+
|
|
913
|
+
## TASK 4 — Write the AI PR Review Engine
|
|
914
|
+
|
|
915
|
+
### `.mindforge/pr-review/ai-reviewer.md`
|
|
916
|
+
|
|
917
|
+
```markdown
|
|
918
|
+
# MindForge — AI PR Review Engine
|
|
919
|
+
|
|
920
|
+
## Purpose
|
|
921
|
+
Use the Claude API directly to perform intelligent, contextual code reviews
|
|
922
|
+
on pull request diffs. Goes beyond static analysis to provide reasoning-based
|
|
923
|
+
feedback that understands architectural context.
|
|
924
|
+
|
|
925
|
+
## Review philosophy
|
|
926
|
+
The AI reviewer has three goals:
|
|
927
|
+
1. Catch what static analysis misses (logic errors, design flaws, missing edge cases)
|
|
928
|
+
2. Confirm that the PR delivers what it claims (requirements traceability)
|
|
929
|
+
3. Maintain code quality standards consistently across all reviewers
|
|
930
|
+
|
|
931
|
+
The AI reviewer does NOT replace human reviewers — it prepares them.
|
|
932
|
+
It surfaces issues, explains context, and asks questions.
|
|
933
|
+
Human reviewers make final decisions.
|
|
934
|
+
|
|
935
|
+
## Claude API integration
|
|
936
|
+
|
|
937
|
+
```javascript
|
|
938
|
+
// pr-review/ai-review-runner.js
|
|
939
|
+
|
|
940
|
+
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
|
|
941
|
+
if (!ANTHROPIC_API_KEY) {
|
|
942
|
+
console.error('ANTHROPIC_API_KEY not set — AI review unavailable');
|
|
943
|
+
process.exit(0); // Graceful skip, not failure
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
async function runAIReview(diff, context) {
|
|
947
|
+
const response = await fetch('https://api.anthropic.com/v1/messages', {
|
|
948
|
+
method: 'POST',
|
|
949
|
+
headers: {
|
|
950
|
+
'Content-Type': 'application/json',
|
|
951
|
+
'x-api-key': ANTHROPIC_API_KEY,
|
|
952
|
+
'anthropic-version': '2023-06-01',
|
|
953
|
+
},
|
|
954
|
+
body: JSON.stringify({
|
|
955
|
+
model: 'claude-sonnet-4-6',
|
|
956
|
+
max_tokens: 4096,
|
|
957
|
+
system: buildSystemPrompt(context),
|
|
958
|
+
messages: [
|
|
959
|
+
{ role: 'user', content: buildReviewPrompt(diff, context) }
|
|
960
|
+
],
|
|
961
|
+
}),
|
|
962
|
+
});
|
|
963
|
+
|
|
964
|
+
if (!response.ok) {
|
|
965
|
+
const err = await response.text();
|
|
966
|
+
throw new Error(`Claude API error ${response.status}: ${err}`);
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
const data = await response.json();
|
|
970
|
+
return data.content[0].text;
|
|
971
|
+
}
|
|
972
|
+
```
|
|
973
|
+
|
|
974
|
+
## System prompt construction
|
|
975
|
+
|
|
976
|
+
```javascript
|
|
977
|
+
function buildSystemPrompt(context) {
|
|
978
|
+
return `You are a senior code reviewer performing a pull request review for the project: ${context.projectName}.
|
|
979
|
+
|
|
980
|
+
## Your review context
|
|
981
|
+
Tech stack: ${context.techStack}
|
|
982
|
+
Architecture pattern: ${context.architecturePattern}
|
|
983
|
+
Coding conventions: ${context.conventions}
|
|
984
|
+
Security requirements: ${context.securityRequirements}
|
|
985
|
+
|
|
986
|
+
## Your review approach
|
|
987
|
+
1. Read the diff carefully — understand the INTENT of each change, not just the change itself
|
|
988
|
+
2. Check if the implementation matches the stated PR purpose
|
|
989
|
+
3. Look for: logic errors, missing error handling, security issues, performance concerns
|
|
990
|
+
4. Evaluate: code clarity, convention adherence, test coverage
|
|
991
|
+
5. Flag: anything that seems to contradict the architecture or conventions
|
|
992
|
+
|
|
993
|
+
## Output format
|
|
994
|
+
Produce a structured review in this exact format:
|
|
995
|
+
|
|
996
|
+
### Summary
|
|
997
|
+
[2-3 sentences: what this PR does and overall quality assessment]
|
|
998
|
+
|
|
999
|
+
### Findings
|
|
1000
|
+
For each finding, use exactly:
|
|
1001
|
+
**[CRITICAL|HIGH|MEDIUM|LOW]** \`file:line\` — [Issue description]
|
|
1002
|
+
> [Specific recommendation]
|
|
1003
|
+
|
|
1004
|
+
### Positive observations
|
|
1005
|
+
[What was done well — be genuine, not perfunctory]
|
|
1006
|
+
|
|
1007
|
+
### Questions for the author
|
|
1008
|
+
[Numbered questions about unclear decisions or assumptions]
|
|
1009
|
+
|
|
1010
|
+
### Verdict
|
|
1011
|
+
[APPROVE | REQUEST_CHANGES | COMMENT] — [one sentence rationale]
|
|
1012
|
+
|
|
1013
|
+
Be direct. Be specific. Cite line numbers. Do not be vague.
|
|
1014
|
+
Do not flag issues that are stylistic preferences when conventions permit them.
|
|
1015
|
+
Do not repeat findings from the automated checks (secret detection, type errors) — focus on logic and design.`;
|
|
1016
|
+
}
|
|
1017
|
+
```
|
|
1018
|
+
|
|
1019
|
+
## Review prompt construction
|
|
1020
|
+
|
|
1021
|
+
```javascript
|
|
1022
|
+
function buildReviewPrompt(diff, context) {
|
|
1023
|
+
return `Please review this pull request diff.
|
|
1024
|
+
|
|
1025
|
+
## PR Information
|
|
1026
|
+
Title: ${context.prTitle}
|
|
1027
|
+
Author: ${context.prAuthor}
|
|
1028
|
+
Branch: ${context.branch} → ${context.base}
|
|
1029
|
+
Files changed: ${context.filesChanged}
|
|
1030
|
+
|
|
1031
|
+
## Requirements being addressed
|
|
1032
|
+
${context.requirements || 'No specific requirements listed'}
|
|
1033
|
+
|
|
1034
|
+
## Architectural context
|
|
1035
|
+
${context.architectureContext || 'See ARCHITECTURE.md for system design'}
|
|
1036
|
+
|
|
1037
|
+
## The diff
|
|
1038
|
+
\`\`\`diff
|
|
1039
|
+
${diff.slice(0, 12000)} ${diff.length > 12000 ? '\n[diff truncated — ' + diff.length + ' chars total]' : ''}
|
|
1040
|
+
\`\`\`
|
|
1041
|
+
|
|
1042
|
+
Focus your review on correctness, security, and architectural alignment.
|
|
1043
|
+
Do not comment on formatting issues handled by the linter.`;
|
|
1044
|
+
}
|
|
1045
|
+
```
|
|
1046
|
+
|
|
1047
|
+
## Context loading
|
|
1048
|
+
|
|
1049
|
+
Before calling the API, load review context:
|
|
1050
|
+
```javascript
|
|
1051
|
+
function loadReviewContext() {
|
|
1052
|
+
const projectMd = readFile('.planning/PROJECT.md');
|
|
1053
|
+
const archMd = readFile('.planning/ARCHITECTURE.md');
|
|
1054
|
+
const convMd = readFile('.mindforge/org/CONVENTIONS.md');
|
|
1055
|
+
const secMd = readFile('.mindforge/org/SECURITY.md');
|
|
1056
|
+
|
|
1057
|
+
return {
|
|
1058
|
+
projectName: extractField(projectMd, 'NAME') || 'Unknown',
|
|
1059
|
+
techStack: extractTechStack(projectMd),
|
|
1060
|
+
architecturePattern: extractField(archMd, '## Architectural pattern') || 'Unknown',
|
|
1061
|
+
conventions: convMd.slice(0, 2000), // First 2000 chars
|
|
1062
|
+
securityRequirements: secMd.slice(0, 1500), // First 1500 chars
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
## Rate limiting and cost management
|
|
1068
|
+
|
|
1069
|
+
```javascript
|
|
1070
|
+
// Review request limits to control API costs
|
|
1071
|
+
const REVIEW_LIMITS = {
|
|
1072
|
+
maxDiffSize: 15000, // Characters — larger diffs get summarised
|
|
1073
|
+
maxFilesReviewed: 20, // Review the 20 most-changed files
|
|
1074
|
+
cacheTtlMinutes: 60, // Cache reviews for the same commit SHA
|
|
1075
|
+
maxDailyReviews: 50, // Stop AI reviews after 50 per day (configurable)
|
|
1076
|
+
};
|
|
1077
|
+
|
|
1078
|
+
// Before calling API: check daily limit
|
|
1079
|
+
function checkDailyLimit() {
|
|
1080
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
1081
|
+
const logFile = '.mindforge/metrics/ai-review-usage.jsonl';
|
|
1082
|
+
const todayCount = (readFileLines(logFile) || [])
|
|
1083
|
+
.filter(line => line.includes(today))
|
|
1084
|
+
.length;
|
|
1085
|
+
return todayCount < REVIEW_LIMITS.maxDailyReviews;
|
|
1086
|
+
}
|
|
1087
|
+
```
|
|
1088
|
+
|
|
1089
|
+
## Output formatting for GitHub
|
|
1090
|
+
|
|
1091
|
+
```javascript
|
|
1092
|
+
function formatForGitHub(aiReview, summary) {
|
|
1093
|
+
return `## 🤖 MindForge AI Code Review
|
|
1094
|
+
|
|
1095
|
+
${aiReview}
|
|
1096
|
+
|
|
1097
|
+
---
|
|
1098
|
+
*Generated by MindForge AI Review Engine v${VERSION}*
|
|
1099
|
+
*Model: claude-sonnet-4-6 | Context: PROJECT.md + ARCHITECTURE.md + CONVENTIONS.md*
|
|
1100
|
+
*This review supplements but does not replace human code review.*`;
|
|
1101
|
+
}
|
|
1102
|
+
```
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
---
|
|
1106
|
+
|
|
1107
|
+
### `.mindforge/pr-review/review-prompt-templates.md`
|
|
1108
|
+
|
|
1109
|
+
```markdown
|
|
1110
|
+
# MindForge — PR Review Prompt Templates
|
|
1111
|
+
|
|
1112
|
+
## Specialised review templates by change type
|
|
1113
|
+
|
|
1114
|
+
### Security-focused review (for Tier 3 changes)
|
|
1115
|
+
Used when: PR includes auth, payment, PII, or security changes.
|
|
1116
|
+
Additional system prompt addition:
|
|
1117
|
+
```
|
|
1118
|
+
SECURITY REVIEW MODE ACTIVE.
|
|
1119
|
+
Apply the OWASP Top 10 checklist systematically to this diff.
|
|
1120
|
+
Flag every instance of:
|
|
1121
|
+
- A01: Access control bypasses
|
|
1122
|
+
- A02: Cryptographic weaknesses (weak algorithms, insecure storage)
|
|
1123
|
+
- A03: Injection vectors (SQL, NoSQL, OS, LDAP)
|
|
1124
|
+
- A07: Authentication failures (session management, credential handling)
|
|
1125
|
+
Any CRITICAL security finding must be listed first, before other findings.
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
### Database migration review
|
|
1129
|
+
Used when: PR includes database schema changes.
|
|
1130
|
+
Additional prompt addition:
|
|
1131
|
+
```
|
|
1132
|
+
DATABASE MIGRATION REVIEW MODE.
|
|
1133
|
+
For this migration, verify:
|
|
1134
|
+
1. Migration is reversible — is there a DOWN migration?
|
|
1135
|
+
2. Migration is non-blocking — does it avoid full table locks?
|
|
1136
|
+
3. New columns with NOT NULL have either a DEFAULT or a two-phase migration
|
|
1137
|
+
4. No data-loss operations without explicit confirmation in PR description
|
|
1138
|
+
5. New indexes are added CONCURRENTLY (PostgreSQL) to avoid table locks
|
|
1139
|
+
6. Foreign key constraints are added with VALIDATE separately from creation
|
|
1140
|
+
```
|
|
1141
|
+
|
|
1142
|
+
### API breaking change review
|
|
1143
|
+
Used when: PR changes existing API endpoints or response schemas.
|
|
1144
|
+
Additional prompt addition:
|
|
1145
|
+
```
|
|
1146
|
+
API BREAKING CHANGE REVIEW MODE.
|
|
1147
|
+
Verify:
|
|
1148
|
+
1. Is this change documented as breaking in the PR description?
|
|
1149
|
+
2. Is the API version incremented appropriately?
|
|
1150
|
+
3. Are existing clients given a deprecation period?
|
|
1151
|
+
4. Is a migration guide included for API consumers?
|
|
1152
|
+
5. Do integration tests cover both old and new API contracts?
|
|
1153
|
+
```
|
|
1154
|
+
```
|
|
1155
|
+
|
|
1156
|
+
**Commit:**
|
|
1157
|
+
```bash
|
|
1158
|
+
git add .mindforge/pr-review/
|
|
1159
|
+
git commit -m "feat(pr-review): implement AI PR review engine with Claude API integration"
|
|
1160
|
+
```
|
|
1161
|
+
|
|
1162
|
+
---
|
|
1163
|
+
|
|
1164
|
+
## TASK 5 — Write the Monorepo Support Engine
|
|
1165
|
+
|
|
1166
|
+
### `.mindforge/monorepo/workspace-detector.md`
|
|
1167
|
+
|
|
1168
|
+
```markdown
|
|
1169
|
+
# MindForge — Monorepo Workspace Detector
|
|
1170
|
+
|
|
1171
|
+
## Purpose
|
|
1172
|
+
Detect and understand monorepo structures (npm workspaces, pnpm workspaces,
|
|
1173
|
+
Nx, Turborepo, Lerna) so MindForge can plan and execute phases across
|
|
1174
|
+
multiple packages correctly.
|
|
1175
|
+
|
|
1176
|
+
## Supported monorepo types
|
|
1177
|
+
|
|
1178
|
+
| Type | Detection file | Package locations |
|
|
1179
|
+
|---|---|---|
|
|
1180
|
+
| npm workspaces | `package.json` with `"workspaces":` | Defined in workspaces array |
|
|
1181
|
+
| pnpm workspaces | `pnpm-workspace.yaml` | Defined in packages array |
|
|
1182
|
+
| Nx | `nx.json` | `apps/` and `libs/` |
|
|
1183
|
+
| Turborepo | `turbo.json` | `apps/` and `packages/` |
|
|
1184
|
+
| Lerna | `lerna.json` | Defined in packages array |
|
|
1185
|
+
| Yarn workspaces | `package.json` with `"workspaces":` | Same as npm |
|
|
1186
|
+
|
|
1187
|
+
## Detection protocol
|
|
1188
|
+
|
|
1189
|
+
```bash
|
|
1190
|
+
detect_workspace_type() {
|
|
1191
|
+
local ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
1192
|
+
|
|
1193
|
+
# Check in priority order
|
|
1194
|
+
if [ -f "${ROOT}/nx.json" ]; then
|
|
1195
|
+
echo "nx"
|
|
1196
|
+
elif [ -f "${ROOT}/turbo.json" ]; then
|
|
1197
|
+
echo "turborepo"
|
|
1198
|
+
elif [ -f "${ROOT}/lerna.json" ]; then
|
|
1199
|
+
echo "lerna"
|
|
1200
|
+
elif [ -f "${ROOT}/pnpm-workspace.yaml" ]; then
|
|
1201
|
+
echo "pnpm"
|
|
1202
|
+
elif node -e "
|
|
1203
|
+
const p = require('./package.json');
|
|
1204
|
+
process.exit(p.workspaces ? 0 : 1)
|
|
1205
|
+
" 2>/dev/null; then
|
|
1206
|
+
echo "npm-workspaces"
|
|
1207
|
+
else
|
|
1208
|
+
echo "none"
|
|
1209
|
+
fi
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
list_packages() {
|
|
1213
|
+
local TYPE="$1"
|
|
1214
|
+
case "${TYPE}" in
|
|
1215
|
+
nx)
|
|
1216
|
+
find apps/ libs/ -name "package.json" -not -path "*/node_modules/*" \
|
|
1217
|
+
-exec dirname {} \; 2>/dev/null | sort
|
|
1218
|
+
;;
|
|
1219
|
+
turborepo)
|
|
1220
|
+
find apps/ packages/ -name "package.json" -not -path "*/node_modules/*" \
|
|
1221
|
+
-exec dirname {} \; 2>/dev/null | sort
|
|
1222
|
+
;;
|
|
1223
|
+
pnpm)
|
|
1224
|
+
cat pnpm-workspace.yaml | grep "^ -" | sed "s/ - '//;s/'$//"
|
|
1225
|
+
;;
|
|
1226
|
+
npm-workspaces|lerna)
|
|
1227
|
+
node -e "
|
|
1228
|
+
const p = require('./package.json');
|
|
1229
|
+
const ws = p.workspaces || require('./lerna.json').packages || [];
|
|
1230
|
+
ws.forEach(w => console.log(w.replace(/\/\*$/, '')));
|
|
1231
|
+
" 2>/dev/null
|
|
1232
|
+
;;
|
|
1233
|
+
esac
|
|
1234
|
+
}
|
|
1235
|
+
```
|
|
1236
|
+
|
|
1237
|
+
## Package metadata extraction
|
|
1238
|
+
|
|
1239
|
+
For each detected package, extract:
|
|
1240
|
+
```javascript
|
|
1241
|
+
{
|
|
1242
|
+
"name": "package name from package.json",
|
|
1243
|
+
"path": "relative path from monorepo root",
|
|
1244
|
+
"type": "app | lib | shared | api | web | cli",
|
|
1245
|
+
"dependencies": ["list of internal package dependencies"],
|
|
1246
|
+
"scripts": {
|
|
1247
|
+
"build": "build command",
|
|
1248
|
+
"test": "test command",
|
|
1249
|
+
"lint": "lint command"
|
|
1250
|
+
},
|
|
1251
|
+
"mindforge": {
|
|
1252
|
+
"phase-scope": "global | package-specific",
|
|
1253
|
+
"test-command": "override test command if needed",
|
|
1254
|
+
"affected-by": ["list of packages that affect this one"]
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
```
|
|
1258
|
+
|
|
1259
|
+
## Workspace manifest
|
|
1260
|
+
|
|
1261
|
+
Write to `.planning/WORKSPACE-MANIFEST.json`:
|
|
1262
|
+
|
|
1263
|
+
```json
|
|
1264
|
+
{
|
|
1265
|
+
"schema_version": "1.0.0",
|
|
1266
|
+
"workspace_type": "turborepo",
|
|
1267
|
+
"root": "/path/to/project",
|
|
1268
|
+
"packages": [
|
|
1269
|
+
{
|
|
1270
|
+
"name": "@myapp/api",
|
|
1271
|
+
"path": "apps/api",
|
|
1272
|
+
"type": "api",
|
|
1273
|
+
"dependencies": ["@myapp/shared"],
|
|
1274
|
+
"test_command": "npm test",
|
|
1275
|
+
"build_command": "npm run build"
|
|
1276
|
+
},
|
|
1277
|
+
{
|
|
1278
|
+
"name": "@myapp/web",
|
|
1279
|
+
"path": "apps/web",
|
|
1280
|
+
"type": "web",
|
|
1281
|
+
"dependencies": ["@myapp/shared", "@myapp/ui"],
|
|
1282
|
+
"test_command": "npm test",
|
|
1283
|
+
"build_command": "npm run build"
|
|
1284
|
+
},
|
|
1285
|
+
{
|
|
1286
|
+
"name": "@myapp/shared",
|
|
1287
|
+
"path": "packages/shared",
|
|
1288
|
+
"type": "lib",
|
|
1289
|
+
"dependencies": [],
|
|
1290
|
+
"test_command": "npm test",
|
|
1291
|
+
"build_command": "npm run build"
|
|
1292
|
+
}
|
|
1293
|
+
],
|
|
1294
|
+
"dependency_order": ["@myapp/shared", "@myapp/api", "@myapp/web"],
|
|
1295
|
+
"affected_packages": {}
|
|
1296
|
+
}
|
|
1297
|
+
```
|
|
1298
|
+
```
|
|
1299
|
+
|
|
1300
|
+
---
|
|
1301
|
+
|
|
1302
|
+
### `.mindforge/monorepo/cross-package-planner.md`
|
|
1303
|
+
|
|
1304
|
+
```markdown
|
|
1305
|
+
# MindForge — Cross-Package Planner
|
|
1306
|
+
|
|
1307
|
+
## Purpose
|
|
1308
|
+
When a phase spans multiple packages in a monorepo, coordinate execution
|
|
1309
|
+
so that shared dependencies are built and tested before packages that
|
|
1310
|
+
depend on them.
|
|
1311
|
+
|
|
1312
|
+
## Execution order algorithm
|
|
1313
|
+
|
|
1314
|
+
```
|
|
1315
|
+
Given: a set of packages involved in the current phase
|
|
1316
|
+
and their inter-package dependencies
|
|
1317
|
+
|
|
1318
|
+
Algorithm: topological sort of package dependency graph
|
|
1319
|
+
|
|
1320
|
+
Input: WORKSPACE-MANIFEST.json dependency_order
|
|
1321
|
+
Output: ordered list of packages to process
|
|
1322
|
+
|
|
1323
|
+
Example:
|
|
1324
|
+
Phase touches: @myapp/api, @myapp/shared, @myapp/web
|
|
1325
|
+
Dependencies: api→shared, web→shared, web→api
|
|
1326
|
+
Topological order: shared → api → web
|
|
1327
|
+
|
|
1328
|
+
Execution:
|
|
1329
|
+
Wave 1: process @myapp/shared (no dependencies on other changed packages)
|
|
1330
|
+
Wave 2: process @myapp/api (depends on Wave 1: shared)
|
|
1331
|
+
Wave 3: process @myapp/web (depends on Wave 2: api + Wave 1: shared)
|
|
1332
|
+
```
|
|
1333
|
+
|
|
1334
|
+
## Per-package PLAN file routing
|
|
1335
|
+
|
|
1336
|
+
When PLAN files are created for a monorepo phase, each plan specifies its target package:
|
|
1337
|
+
|
|
1338
|
+
```xml
|
|
1339
|
+
<task type="auto">
|
|
1340
|
+
<n>Add auth middleware to API</n>
|
|
1341
|
+
<package>@myapp/api</package>
|
|
1342
|
+
<working-dir>apps/api</working-dir>
|
|
1343
|
+
<files>
|
|
1344
|
+
apps/api/src/middleware/auth.ts
|
|
1345
|
+
apps/api/src/middleware/auth.test.ts
|
|
1346
|
+
</files>
|
|
1347
|
+
<action>...</action>
|
|
1348
|
+
<verify>cd apps/api && npm test -- --testPathPattern=auth.middleware</verify>
|
|
1349
|
+
<done>Auth middleware tests passing in apps/api</done>
|
|
1350
|
+
</task>
|
|
1351
|
+
```
|
|
1352
|
+
|
|
1353
|
+
## Cross-package test execution
|
|
1354
|
+
|
|
1355
|
+
After all packages in the phase are processed:
|
|
1356
|
+
```bash
|
|
1357
|
+
# Run tests across all affected packages
|
|
1358
|
+
AFFECTED_PACKAGES=$(cat .planning/WORKSPACE-MANIFEST.json | \
|
|
1359
|
+
node -e "const m=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); \
|
|
1360
|
+
console.log(m.packages.map(p=>p.path).join(' '))")
|
|
1361
|
+
|
|
1362
|
+
for PKG_PATH in ${AFFECTED_PACKAGES}; do
|
|
1363
|
+
echo "Testing ${PKG_PATH}..."
|
|
1364
|
+
cd "${PKG_PATH}" && npm test && cd -
|
|
1365
|
+
done
|
|
1366
|
+
|
|
1367
|
+
# Run integration tests from root (if configured)
|
|
1368
|
+
[ -f "package.json" ] && npm run test:integration 2>/dev/null || true
|
|
1369
|
+
```
|
|
1370
|
+
|
|
1371
|
+
## Affected package detection
|
|
1372
|
+
|
|
1373
|
+
When a PLAN modifies a shared library: automatically include all packages
|
|
1374
|
+
that depend on that library in the affected scope:
|
|
1375
|
+
|
|
1376
|
+
```bash
|
|
1377
|
+
# Detect affected packages from changed files
|
|
1378
|
+
CHANGED_PACKAGES=$(git diff --name-only | \
|
|
1379
|
+
awk -F/ '{print $1"/"$2}' | sort -u | \
|
|
1380
|
+
while read pkg; do
|
|
1381
|
+
node -e "
|
|
1382
|
+
const m = require('.planning/WORKSPACE-MANIFEST.json');
|
|
1383
|
+
const changed = '${pkg}';
|
|
1384
|
+
m.packages.forEach(p => {
|
|
1385
|
+
if (p.path === changed || p.dependencies.some(d => d.includes(changed))) {
|
|
1386
|
+
console.log(p.name);
|
|
1387
|
+
}
|
|
1388
|
+
});
|
|
1389
|
+
" 2>/dev/null
|
|
1390
|
+
done | sort -u)
|
|
1391
|
+
```
|
|
1392
|
+
```
|
|
1393
|
+
|
|
1394
|
+
**Commit:**
|
|
1395
|
+
```bash
|
|
1396
|
+
git add .mindforge/monorepo/
|
|
1397
|
+
git commit -m "feat(monorepo): implement workspace detector and cross-package planner"
|
|
1398
|
+
```
|
|
1399
|
+
|
|
1400
|
+
---
|
|
1401
|
+
|
|
1402
|
+
## TASK 6 — Write the MindForge SDK
|
|
1403
|
+
|
|
1404
|
+
### `sdk/package.json`
|
|
1405
|
+
|
|
1406
|
+
```json
|
|
1407
|
+
{
|
|
1408
|
+
"name": "@mindforge/sdk",
|
|
1409
|
+
"version": "0.6.0",
|
|
1410
|
+
"description": "MindForge SDK — Programmatic API for embedding MindForge in tools",
|
|
1411
|
+
"main": "dist/index.js",
|
|
1412
|
+
"types": "dist/index.d.ts",
|
|
1413
|
+
"scripts": {
|
|
1414
|
+
"build": "tsc",
|
|
1415
|
+
"test": "node tests/sdk.test.js",
|
|
1416
|
+
"lint": "eslint src/ --ext .ts"
|
|
1417
|
+
},
|
|
1418
|
+
"keywords": ["mindforge", "agentic-framework", "claude", "sdk"],
|
|
1419
|
+
"license": "MIT",
|
|
1420
|
+
"peerDependencies": {
|
|
1421
|
+
"typescript": ">=5.0.0"
|
|
1422
|
+
},
|
|
1423
|
+
"devDependencies": {
|
|
1424
|
+
"typescript": "^5.4.0"
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
```
|
|
1428
|
+
|
|
1429
|
+
### `sdk/src/types.ts`
|
|
1430
|
+
|
|
1431
|
+
```typescript
|
|
1432
|
+
/**
|
|
1433
|
+
* MindForge SDK — Type Definitions
|
|
1434
|
+
*/
|
|
1435
|
+
|
|
1436
|
+
export interface MindForgeConfig {
|
|
1437
|
+
/** Path to the project root (default: cwd) */
|
|
1438
|
+
projectRoot?: string;
|
|
1439
|
+
/** Claude API key (default: ANTHROPIC_API_KEY env var) */
|
|
1440
|
+
apiKey?: string;
|
|
1441
|
+
/** CI mode — disables interactive features */
|
|
1442
|
+
ciMode?: boolean;
|
|
1443
|
+
/** Output format for events */
|
|
1444
|
+
outputFormat?: 'json' | 'text' | 'github-annotations';
|
|
1445
|
+
/** Timeout per task in milliseconds (default: 600000 — 10 minutes) */
|
|
1446
|
+
taskTimeoutMs?: number;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
export interface PhaseResult {
|
|
1450
|
+
phase: number;
|
|
1451
|
+
status: 'success' | 'failure' | 'warning' | 'skipped';
|
|
1452
|
+
tasksCompleted: number;
|
|
1453
|
+
tasksTotal: number;
|
|
1454
|
+
commits: string[];
|
|
1455
|
+
securityFindings: SecurityFinding[];
|
|
1456
|
+
qualityGateResults: GateResult[];
|
|
1457
|
+
durationMs: number;
|
|
1458
|
+
errorMessage?: string;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
export interface TaskResult {
|
|
1462
|
+
planId: string;
|
|
1463
|
+
taskName: string;
|
|
1464
|
+
status: 'completed' | 'failed' | 'skipped';
|
|
1465
|
+
commitSha?: string;
|
|
1466
|
+
verifyOutput?: string;
|
|
1467
|
+
durationMs: number;
|
|
1468
|
+
errorMessage?: string;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
export interface SecurityFinding {
|
|
1472
|
+
severity: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
|
|
1473
|
+
owaspCategory: string;
|
|
1474
|
+
file: string;
|
|
1475
|
+
line: number;
|
|
1476
|
+
description: string;
|
|
1477
|
+
remediation: string;
|
|
1478
|
+
remediated: boolean;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
export interface GateResult {
|
|
1482
|
+
gate: string;
|
|
1483
|
+
status: 'passed' | 'failed' | 'warning' | 'skipped';
|
|
1484
|
+
detail?: string;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
export interface HealthReport {
|
|
1488
|
+
overallStatus: 'healthy' | 'warning' | 'error';
|
|
1489
|
+
errors: HealthIssue[];
|
|
1490
|
+
warnings: HealthIssue[];
|
|
1491
|
+
informational: HealthIssue[];
|
|
1492
|
+
timestamp: string;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
export interface HealthIssue {
|
|
1496
|
+
category: string;
|
|
1497
|
+
message: string;
|
|
1498
|
+
autoRepairable: boolean;
|
|
1499
|
+
fixCommand?: string;
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
export type MindForgeEvent =
|
|
1503
|
+
| { type: 'task_started'; phase: number; plan: string; taskName: string }
|
|
1504
|
+
| { type: 'task_completed'; phase: number; plan: string; commitSha: string }
|
|
1505
|
+
| { type: 'task_failed'; phase: number; plan: string; error: string }
|
|
1506
|
+
| { type: 'wave_started'; phase: number; wave: number; taskCount: number }
|
|
1507
|
+
| { type: 'wave_completed'; phase: number; wave: number }
|
|
1508
|
+
| { type: 'phase_completed'; phase: number; result: PhaseResult }
|
|
1509
|
+
| { type: 'security_finding'; finding: SecurityFinding }
|
|
1510
|
+
| { type: 'gate_result'; gate: GateResult }
|
|
1511
|
+
| { type: 'log'; level: 'info' | 'warn' | 'error'; message: string };
|
|
1512
|
+
```
|
|
1513
|
+
|
|
1514
|
+
### `sdk/src/client.ts`
|
|
1515
|
+
|
|
1516
|
+
```typescript
|
|
1517
|
+
/**
|
|
1518
|
+
* MindForge SDK — Main Client
|
|
1519
|
+
*/
|
|
1520
|
+
|
|
1521
|
+
import { EventEmitter } from 'events';
|
|
1522
|
+
import * as fs from 'fs';
|
|
1523
|
+
import * as path from 'path';
|
|
1524
|
+
import type {
|
|
1525
|
+
MindForgeConfig, PhaseResult, TaskResult, HealthReport, MindForgeEvent
|
|
1526
|
+
} from './types';
|
|
1527
|
+
|
|
1528
|
+
export class MindForgeClient extends EventEmitter {
|
|
1529
|
+
private config: Required<MindForgeConfig>;
|
|
1530
|
+
private projectRoot: string;
|
|
1531
|
+
|
|
1532
|
+
constructor(config: MindForgeConfig = {}) {
|
|
1533
|
+
super();
|
|
1534
|
+
this.projectRoot = config.projectRoot ?? process.cwd();
|
|
1535
|
+
this.config = {
|
|
1536
|
+
projectRoot: this.projectRoot,
|
|
1537
|
+
apiKey: config.apiKey ?? process.env.ANTHROPIC_API_KEY ?? '',
|
|
1538
|
+
ciMode: config.ciMode ?? (process.env.CI === 'true'),
|
|
1539
|
+
outputFormat: config.outputFormat ?? 'json',
|
|
1540
|
+
taskTimeoutMs: config.taskTimeoutMs ?? 600_000,
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
// ── Event emission helper ──────────────────────────────────────────────────
|
|
1545
|
+
private emit<T extends MindForgeEvent>(event: T): boolean {
|
|
1546
|
+
return super.emit(event.type, event);
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
// ── Project state ──────────────────────────────────────────────────────────
|
|
1550
|
+
isInitialised(): boolean {
|
|
1551
|
+
return fs.existsSync(path.join(this.projectRoot, '.planning', 'PROJECT.md'));
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
readState(): Record<string, unknown> | null {
|
|
1555
|
+
const statePath = path.join(this.projectRoot, '.planning', 'STATE.md');
|
|
1556
|
+
if (!fs.existsSync(statePath)) return null;
|
|
1557
|
+
return { raw: fs.readFileSync(statePath, 'utf8') };
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
readHandoff(): Record<string, unknown> | null {
|
|
1561
|
+
const handoffPath = path.join(this.projectRoot, '.planning', 'HANDOFF.json');
|
|
1562
|
+
if (!fs.existsSync(handoffPath)) return null;
|
|
1563
|
+
try {
|
|
1564
|
+
return JSON.parse(fs.readFileSync(handoffPath, 'utf8'));
|
|
1565
|
+
} catch {
|
|
1566
|
+
return null;
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// ── Health check ───────────────────────────────────────────────────────────
|
|
1571
|
+
async health(): Promise<HealthReport> {
|
|
1572
|
+
const errors: Array<{ category: string; message: string; autoRepairable: boolean }> = [];
|
|
1573
|
+
const warnings: Array<{ category: string; message: string; autoRepairable: boolean }> = [];
|
|
1574
|
+
const info: Array<{ category: string; message: string; autoRepairable: boolean }> = [];
|
|
1575
|
+
|
|
1576
|
+
const requiredFiles = [
|
|
1577
|
+
'.planning/STATE.md',
|
|
1578
|
+
'.planning/HANDOFF.json',
|
|
1579
|
+
'.planning/AUDIT.jsonl',
|
|
1580
|
+
'.mindforge/org/CONVENTIONS.md',
|
|
1581
|
+
];
|
|
1582
|
+
|
|
1583
|
+
for (const file of requiredFiles) {
|
|
1584
|
+
const fullPath = path.join(this.projectRoot, file);
|
|
1585
|
+
if (!fs.existsSync(fullPath)) {
|
|
1586
|
+
warnings.push({ category: 'installation', message: `Missing: ${file}`, autoRepairable: false });
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// Check HANDOFF.json validity
|
|
1591
|
+
const handoff = this.readHandoff();
|
|
1592
|
+
if (handoff && !handoff.schema_version) {
|
|
1593
|
+
errors.push({ category: 'state', message: 'HANDOFF.json missing schema_version field', autoRepairable: false });
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
// Check AUDIT.jsonl
|
|
1597
|
+
const auditPath = path.join(this.projectRoot, '.planning', 'AUDIT.jsonl');
|
|
1598
|
+
if (fs.existsSync(auditPath)) {
|
|
1599
|
+
const lineCount = fs.readFileSync(auditPath, 'utf8').split('\n').filter(Boolean).length;
|
|
1600
|
+
if (lineCount > 9000) {
|
|
1601
|
+
warnings.push({ category: 'audit', message: `AUDIT.jsonl has ${lineCount} lines — archive soon`, autoRepairable: true });
|
|
1602
|
+
}
|
|
1603
|
+
info.push({ category: 'audit', message: `AUDIT.jsonl: ${lineCount} entries`, autoRepairable: false });
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
return {
|
|
1607
|
+
overallStatus: errors.length > 0 ? 'error' : warnings.length > 0 ? 'warning' : 'healthy',
|
|
1608
|
+
errors,
|
|
1609
|
+
warnings,
|
|
1610
|
+
informational: info,
|
|
1611
|
+
timestamp: new Date().toISOString(),
|
|
1612
|
+
};
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
// ── Audit log reading ──────────────────────────────────────────────────────
|
|
1616
|
+
readAuditLog(filter?: { event?: string; phase?: number; since?: Date }): unknown[] {
|
|
1617
|
+
const auditPath = path.join(this.projectRoot, '.planning', 'AUDIT.jsonl');
|
|
1618
|
+
if (!fs.existsSync(auditPath)) return [];
|
|
1619
|
+
|
|
1620
|
+
return fs.readFileSync(auditPath, 'utf8')
|
|
1621
|
+
.split('\n')
|
|
1622
|
+
.filter(Boolean)
|
|
1623
|
+
.map(line => { try { return JSON.parse(line); } catch { return null; } })
|
|
1624
|
+
.filter(Boolean)
|
|
1625
|
+
.filter(entry => {
|
|
1626
|
+
if (filter?.event && entry.event !== filter.event) return false;
|
|
1627
|
+
if (filter?.phase !== undefined && entry.phase !== filter.phase) return false;
|
|
1628
|
+
if (filter?.since && new Date(entry.timestamp) < filter.since) return false;
|
|
1629
|
+
return true;
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
// ── Metrics reading ────────────────────────────────────────────────────────
|
|
1634
|
+
readSessionMetrics(limit = 10): unknown[] {
|
|
1635
|
+
const metricsPath = path.join(this.projectRoot, '.mindforge', 'metrics', 'session-quality.jsonl');
|
|
1636
|
+
if (!fs.existsSync(metricsPath)) return [];
|
|
1637
|
+
|
|
1638
|
+
return fs.readFileSync(metricsPath, 'utf8')
|
|
1639
|
+
.split('\n')
|
|
1640
|
+
.filter(Boolean)
|
|
1641
|
+
.slice(-limit)
|
|
1642
|
+
.map(line => { try { return JSON.parse(line); } catch { return null; } })
|
|
1643
|
+
.filter(Boolean);
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
// ── Config validation ──────────────────────────────────────────────────────
|
|
1647
|
+
validateConfig(): { valid: boolean; errors: string[]; warnings: string[] } {
|
|
1648
|
+
const configPath = path.join(this.projectRoot, 'MINDFORGE.md');
|
|
1649
|
+
if (!fs.existsSync(configPath)) {
|
|
1650
|
+
return { valid: true, errors: [], warnings: ['MINDFORGE.md not found — using defaults'] };
|
|
1651
|
+
}
|
|
1652
|
+
// Full validation via bin/validate-config.js
|
|
1653
|
+
return { valid: true, errors: [], warnings: [] };
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
```
|
|
1657
|
+
|
|
1658
|
+
### `sdk/src/commands.ts`
|
|
1659
|
+
|
|
1660
|
+
```typescript
|
|
1661
|
+
/**
|
|
1662
|
+
* MindForge SDK — Command Builders
|
|
1663
|
+
* Builds the command strings that can be sent to Claude Code / Antigravity
|
|
1664
|
+
* via their programmatic APIs.
|
|
1665
|
+
*/
|
|
1666
|
+
|
|
1667
|
+
export interface CommandOptions {
|
|
1668
|
+
flags?: string[];
|
|
1669
|
+
args?: string[];
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
export const commands = {
|
|
1673
|
+
/**
|
|
1674
|
+
* Build a /mindforge:health command string
|
|
1675
|
+
*/
|
|
1676
|
+
health(opts: CommandOptions = {}): string {
|
|
1677
|
+
const flags = opts.flags?.join(' ') ?? '';
|
|
1678
|
+
return `/mindforge:health ${flags}`.trim();
|
|
1679
|
+
},
|
|
1680
|
+
|
|
1681
|
+
/**
|
|
1682
|
+
* Build a /mindforge:plan-phase command string
|
|
1683
|
+
*/
|
|
1684
|
+
planPhase(phase: number, opts: CommandOptions = {}): string {
|
|
1685
|
+
const flags = opts.flags?.join(' ') ?? '';
|
|
1686
|
+
return `/mindforge:plan-phase ${phase} ${flags}`.trim();
|
|
1687
|
+
},
|
|
1688
|
+
|
|
1689
|
+
/**
|
|
1690
|
+
* Build a /mindforge:execute-phase command string
|
|
1691
|
+
*/
|
|
1692
|
+
executePhase(phase: number, opts: CommandOptions = {}): string {
|
|
1693
|
+
const flags = opts.flags?.join(' ') ?? '';
|
|
1694
|
+
return `/mindforge:execute-phase ${phase} ${flags}`.trim();
|
|
1695
|
+
},
|
|
1696
|
+
|
|
1697
|
+
/**
|
|
1698
|
+
* Build a /mindforge:security-scan command string
|
|
1699
|
+
*/
|
|
1700
|
+
securityScan(path?: string, opts: CommandOptions = {}): string {
|
|
1701
|
+
const flags = opts.flags?.join(' ') ?? '';
|
|
1702
|
+
return `/mindforge:security-scan ${path ?? ''} ${flags}`.trim();
|
|
1703
|
+
},
|
|
1704
|
+
|
|
1705
|
+
/**
|
|
1706
|
+
* Build a /mindforge:audit command string with filters
|
|
1707
|
+
*/
|
|
1708
|
+
audit(filter: { phase?: number; event?: string; since?: string } = {}): string {
|
|
1709
|
+
const parts = ['/mindforge:audit'];
|
|
1710
|
+
if (filter.phase) parts.push(`--phase ${filter.phase}`);
|
|
1711
|
+
if (filter.event) parts.push(`--event ${filter.event}`);
|
|
1712
|
+
if (filter.since) parts.push(`--since ${filter.since}`);
|
|
1713
|
+
return parts.join(' ');
|
|
1714
|
+
},
|
|
1715
|
+
|
|
1716
|
+
/**
|
|
1717
|
+
* Build a /mindforge:pr-review command string
|
|
1718
|
+
*/
|
|
1719
|
+
prReview(opts: CommandOptions = {}): string {
|
|
1720
|
+
const flags = opts.flags?.join(' ') ?? '';
|
|
1721
|
+
return `/mindforge:pr-review ${flags}`.trim();
|
|
1722
|
+
},
|
|
1723
|
+
};
|
|
1724
|
+
```
|
|
1725
|
+
|
|
1726
|
+
### `sdk/src/events.ts`
|
|
1727
|
+
|
|
1728
|
+
```typescript
|
|
1729
|
+
/**
|
|
1730
|
+
* MindForge SDK — Server-Sent Events (SSE) stream for real-time progress
|
|
1731
|
+
* Enables external tools to subscribe to MindForge execution progress.
|
|
1732
|
+
*/
|
|
1733
|
+
|
|
1734
|
+
import * as http from 'http';
|
|
1735
|
+
import * as fs from 'fs';
|
|
1736
|
+
import * as path from 'path';
|
|
1737
|
+
|
|
1738
|
+
interface SSEClient {
|
|
1739
|
+
id: string;
|
|
1740
|
+
response: http.ServerResponse;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
export class MindForgeEventStream {
|
|
1744
|
+
private clients: SSEClient[] = [];
|
|
1745
|
+
private server: http.Server | null = null;
|
|
1746
|
+
private auditWatcher: fs.FSWatcher | null = null;
|
|
1747
|
+
private lastAuditLine = 0;
|
|
1748
|
+
|
|
1749
|
+
/**
|
|
1750
|
+
* Start the SSE server on the given port
|
|
1751
|
+
*/
|
|
1752
|
+
start(port = 7337): Promise<void> {
|
|
1753
|
+
return new Promise((resolve, reject) => {
|
|
1754
|
+
this.server = http.createServer((req, res) => {
|
|
1755
|
+
if (req.url !== '/events') {
|
|
1756
|
+
res.writeHead(404);
|
|
1757
|
+
res.end();
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
// CORS for local tool integrations
|
|
1762
|
+
res.writeHead(200, {
|
|
1763
|
+
'Content-Type': 'text/event-stream',
|
|
1764
|
+
'Cache-Control': 'no-cache',
|
|
1765
|
+
'Connection': 'keep-alive',
|
|
1766
|
+
'Access-Control-Allow-Origin': 'http://localhost:*',
|
|
1767
|
+
});
|
|
1768
|
+
|
|
1769
|
+
const clientId = Date.now().toString();
|
|
1770
|
+
this.clients.push({ id: clientId, response: res });
|
|
1771
|
+
|
|
1772
|
+
// Send initial connection event
|
|
1773
|
+
this.sendEvent(res, 'connected', { clientId, timestamp: new Date().toISOString() });
|
|
1774
|
+
|
|
1775
|
+
// Clean up on disconnect
|
|
1776
|
+
req.on('close', () => {
|
|
1777
|
+
this.clients = this.clients.filter(c => c.id !== clientId);
|
|
1778
|
+
});
|
|
1779
|
+
});
|
|
1780
|
+
|
|
1781
|
+
this.server.listen(port, () => {
|
|
1782
|
+
console.log(`MindForge event stream listening on http://localhost:${port}/events`);
|
|
1783
|
+
resolve();
|
|
1784
|
+
});
|
|
1785
|
+
|
|
1786
|
+
this.server.on('error', reject);
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
/**
|
|
1791
|
+
* Watch AUDIT.jsonl for new entries and broadcast as SSE events
|
|
1792
|
+
*/
|
|
1793
|
+
watchAuditLog(projectRoot: string): void {
|
|
1794
|
+
const auditPath = path.join(projectRoot, '.planning', 'AUDIT.jsonl');
|
|
1795
|
+
|
|
1796
|
+
if (!fs.existsSync(auditPath)) {
|
|
1797
|
+
// Create the file if it doesn't exist yet
|
|
1798
|
+
fs.writeFileSync(auditPath, '');
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
// Set initial line count
|
|
1802
|
+
const content = fs.readFileSync(auditPath, 'utf8');
|
|
1803
|
+
this.lastAuditLine = content.split('\n').filter(Boolean).length;
|
|
1804
|
+
|
|
1805
|
+
this.auditWatcher = fs.watch(auditPath, () => {
|
|
1806
|
+
const lines = fs.readFileSync(auditPath, 'utf8')
|
|
1807
|
+
.split('\n')
|
|
1808
|
+
.filter(Boolean);
|
|
1809
|
+
|
|
1810
|
+
// Broadcast new lines
|
|
1811
|
+
for (let i = this.lastAuditLine; i < lines.length; i++) {
|
|
1812
|
+
try {
|
|
1813
|
+
const entry = JSON.parse(lines[i]);
|
|
1814
|
+
this.broadcast('audit_entry', entry);
|
|
1815
|
+
} catch {
|
|
1816
|
+
// Ignore parse errors for incomplete lines
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
this.lastAuditLine = lines.length;
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
/**
|
|
1825
|
+
* Broadcast an event to all connected clients
|
|
1826
|
+
*/
|
|
1827
|
+
broadcast(eventType: string, data: unknown): void {
|
|
1828
|
+
this.clients.forEach(client => {
|
|
1829
|
+
this.sendEvent(client.response, eventType, data);
|
|
1830
|
+
});
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
private sendEvent(res: http.ServerResponse, type: string, data: unknown): void {
|
|
1834
|
+
try {
|
|
1835
|
+
res.write(`event: ${type}\n`);
|
|
1836
|
+
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
1837
|
+
} catch {
|
|
1838
|
+
// Client disconnected
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
/**
|
|
1843
|
+
* Stop the event stream server
|
|
1844
|
+
*/
|
|
1845
|
+
stop(): void {
|
|
1846
|
+
this.auditWatcher?.close();
|
|
1847
|
+
this.server?.close();
|
|
1848
|
+
this.clients.forEach(c => c.response.end());
|
|
1849
|
+
this.clients = [];
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
```
|
|
1853
|
+
|
|
1854
|
+
### `sdk/src/index.ts`
|
|
1855
|
+
|
|
1856
|
+
```typescript
|
|
1857
|
+
/**
|
|
1858
|
+
* MindForge SDK — Public API
|
|
1859
|
+
* @module @mindforge/sdk
|
|
1860
|
+
*/
|
|
1861
|
+
|
|
1862
|
+
export { MindForgeClient } from './client';
|
|
1863
|
+
export { MindForgeEventStream } from './events';
|
|
1864
|
+
export { commands } from './commands';
|
|
1865
|
+
export type {
|
|
1866
|
+
MindForgeConfig,
|
|
1867
|
+
PhaseResult,
|
|
1868
|
+
TaskResult,
|
|
1869
|
+
SecurityFinding,
|
|
1870
|
+
GateResult,
|
|
1871
|
+
HealthReport,
|
|
1872
|
+
HealthIssue,
|
|
1873
|
+
MindForgeEvent,
|
|
1874
|
+
CommandOptions,
|
|
1875
|
+
} from './types';
|
|
1876
|
+
|
|
1877
|
+
export const VERSION = '0.6.0';
|
|
1878
|
+
```
|
|
1879
|
+
|
|
1880
|
+
### `sdk/README.md`
|
|
1881
|
+
|
|
1882
|
+
```markdown
|
|
1883
|
+
# @mindforge/sdk
|
|
1884
|
+
|
|
1885
|
+
TypeScript SDK for embedding MindForge in tools, dashboards, and CI pipelines.
|
|
1886
|
+
|
|
1887
|
+
## Installation
|
|
1888
|
+
|
|
1889
|
+
```bash
|
|
1890
|
+
npm install @mindforge/sdk
|
|
1891
|
+
```
|
|
1892
|
+
|
|
1893
|
+
## Quick start
|
|
1894
|
+
|
|
1895
|
+
```typescript
|
|
1896
|
+
import { MindForgeClient } from '@mindforge/sdk';
|
|
1897
|
+
|
|
1898
|
+
const client = new MindForgeClient({
|
|
1899
|
+
projectRoot: '/path/to/project',
|
|
1900
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
1901
|
+
});
|
|
1902
|
+
|
|
1903
|
+
// Health check
|
|
1904
|
+
const health = await client.health();
|
|
1905
|
+
console.log(health.overallStatus); // 'healthy' | 'warning' | 'error'
|
|
1906
|
+
|
|
1907
|
+
// Read audit log
|
|
1908
|
+
const findings = client.readAuditLog({ event: 'security_finding' });
|
|
1909
|
+
console.log(findings);
|
|
1910
|
+
|
|
1911
|
+
// Read metrics
|
|
1912
|
+
const metrics = client.readSessionMetrics(5);
|
|
1913
|
+
console.log(metrics);
|
|
1914
|
+
```
|
|
1915
|
+
|
|
1916
|
+
## Real-time event streaming
|
|
1917
|
+
|
|
1918
|
+
```typescript
|
|
1919
|
+
import { MindForgeEventStream } from '@mindforge/sdk';
|
|
1920
|
+
|
|
1921
|
+
const stream = new MindForgeEventStream();
|
|
1922
|
+
await stream.start(7337);
|
|
1923
|
+
stream.watchAuditLog('/path/to/project');
|
|
1924
|
+
|
|
1925
|
+
// Subscribe from browser or tool:
|
|
1926
|
+
const es = new EventSource('http://localhost:7337/events');
|
|
1927
|
+
es.addEventListener('audit_entry', (e) => {
|
|
1928
|
+
const entry = JSON.parse(e.data);
|
|
1929
|
+
if (entry.event === 'task_completed') {
|
|
1930
|
+
console.log('Task done:', entry.task_name);
|
|
1931
|
+
}
|
|
1932
|
+
});
|
|
1933
|
+
```
|
|
1934
|
+
|
|
1935
|
+
## Config validation
|
|
1936
|
+
|
|
1937
|
+
```typescript
|
|
1938
|
+
const { valid, errors } = client.validateConfig();
|
|
1939
|
+
if (!valid) console.error(errors);
|
|
1940
|
+
```
|
|
1941
|
+
|
|
1942
|
+
## TypeScript support
|
|
1943
|
+
|
|
1944
|
+
Full type definitions included. No `@types/` package needed.
|
|
1945
|
+
```
|
|
1946
|
+
|
|
1947
|
+
**Commit:**
|
|
1948
|
+
```bash
|
|
1949
|
+
git add sdk/
|
|
1950
|
+
git commit -m "feat(sdk): implement MindForge TypeScript SDK with client, events, and command builders"
|
|
1951
|
+
```
|
|
1952
|
+
|
|
1953
|
+
---
|
|
1954
|
+
|
|
1955
|
+
## TASK 7 — Write `/mindforge:init-org` command
|
|
1956
|
+
|
|
1957
|
+
### `.claude/commands/mindforge/init-org.md`
|
|
1958
|
+
|
|
1959
|
+
```markdown
|
|
1960
|
+
# MindForge — Init Org Command
|
|
1961
|
+
# Usage: /mindforge:init-org [--org-name "Name"] [--update]
|
|
1962
|
+
|
|
1963
|
+
## Purpose
|
|
1964
|
+
Set up MindForge at the organisation level — create standardised org-level
|
|
1965
|
+
context files that are shared across ALL projects in the organisation.
|
|
1966
|
+
|
|
1967
|
+
Intended to be run ONCE by a platform engineer or engineering lead.
|
|
1968
|
+
Output is committed to a shared `mindforge-org-config` repository
|
|
1969
|
+
and distributed to projects as a git submodule or npm package.
|
|
1970
|
+
|
|
1971
|
+
## Step 1 — Gather org information
|
|
1972
|
+
|
|
1973
|
+
Ask (one question at a time):
|
|
1974
|
+
1. "What is your organisation name?"
|
|
1975
|
+
2. "Describe what your organisation builds in 1-2 sentences."
|
|
1976
|
+
3. "What is your primary tech stack? (describe in plain English)"
|
|
1977
|
+
4. "What is your default deployment target? (AWS / GCP / Azure / self-hosted / hybrid)"
|
|
1978
|
+
5. "What regulatory frameworks apply to your organisation?"
|
|
1979
|
+
Options: GDPR / HIPAA / SOC 2 / PCI-DSS / ISO 27001 / None / Multiple
|
|
1980
|
+
6. "What is your source control platform?" (GitHub / GitLab / Bitbucket / Azure DevOps)
|
|
1981
|
+
7. "What is your issue tracker?" (Jira / GitHub Issues / Linear / Azure DevOps / None)
|
|
1982
|
+
8. "Who are your Tier 3 compliance approvers? (email addresses, comma-separated)"
|
|
1983
|
+
|
|
1984
|
+
## Step 2 — Generate org-level context files
|
|
1985
|
+
|
|
1986
|
+
Create (or update with `--update`) these files:
|
|
1987
|
+
|
|
1988
|
+
### `.mindforge/org/ORG.md`
|
|
1989
|
+
Populated from answers with:
|
|
1990
|
+
- Organisation identity and mission
|
|
1991
|
+
- Default tech stack with version recommendations
|
|
1992
|
+
- Architecture defaults
|
|
1993
|
+
- Team conventions
|
|
1994
|
+
- Compliance requirements
|
|
1995
|
+
|
|
1996
|
+
### `.mindforge/org/CONVENTIONS.md`
|
|
1997
|
+
Generate sensible defaults based on the tech stack detected.
|
|
1998
|
+
For TypeScript/Node.js stacks: strict TypeScript, ESLint, Conventional Commits
|
|
1999
|
+
For Python stacks: ruff, mypy, black formatting
|
|
2000
|
+
For Go: standard Go toolchain conventions
|
|
2001
|
+
Mark each section clearly: [DEFAULT] or [CUSTOMISE THIS]
|
|
2002
|
+
|
|
2003
|
+
### `.mindforge/org/SECURITY.md`
|
|
2004
|
+
Generate based on declared compliance frameworks:
|
|
2005
|
+
- GDPR → include GDPR-specific policies
|
|
2006
|
+
- HIPAA → include PHI handling requirements
|
|
2007
|
+
- PCI-DSS → include card data handling policies
|
|
2008
|
+
- SOC 2 → include access control requirements
|
|
2009
|
+
Mark critical sections: [REVIEW WITH SECURITY TEAM]
|
|
2010
|
+
|
|
2011
|
+
### `.mindforge/org/TOOLS.md`
|
|
2012
|
+
Generate approved library list based on tech stack.
|
|
2013
|
+
Include common forbidden libraries (moment.js, request, etc.)
|
|
2014
|
+
Mark: [ADD YOUR APPROVED LIBRARIES]
|
|
2015
|
+
|
|
2016
|
+
### `.mindforge/org/integrations/INTEGRATIONS-CONFIG.md`
|
|
2017
|
+
Pre-populate based on declared platforms:
|
|
2018
|
+
- GitHub → fill GitHub config section
|
|
2019
|
+
- Jira → fill Jira config section
|
|
2020
|
+
Mark credential fields clearly: [SET VIA ENVIRONMENT VARIABLE]
|
|
2021
|
+
|
|
2022
|
+
### `.mindforge/governance/GOVERNANCE-CONFIG.md`
|
|
2023
|
+
Pre-populate based on declared approvers and compliance frameworks.
|
|
2024
|
+
Higher compliance burden → lower Tier 2/3 thresholds.
|
|
2025
|
+
Stricter approval SLAs for HIPAA/PCI-DSS organisations.
|
|
2026
|
+
|
|
2027
|
+
## Step 3 — Generate skills recommendation
|
|
2028
|
+
|
|
2029
|
+
Based on the tech stack and compliance requirements, recommend skills to install:
|
|
2030
|
+
|
|
2031
|
+
```
|
|
2032
|
+
Recommended skills for your tech stack:
|
|
2033
|
+
|
|
2034
|
+
Core skills (already included — v0.6.0):
|
|
2035
|
+
✅ security-review, code-quality, api-design, testing-standards, documentation,
|
|
2036
|
+
performance, accessibility, data-privacy, incident-response, database-patterns
|
|
2037
|
+
|
|
2038
|
+
Additional skills recommended for your stack:
|
|
2039
|
+
[tech-stack-specific recommendations from registry]
|
|
2040
|
+
|
|
2041
|
+
For your compliance requirements:
|
|
2042
|
+
[compliance-specific skill recommendations]
|
|
2043
|
+
|
|
2044
|
+
Install all recommendations?
|
|
2045
|
+
yes → npx mindforge-skills install [list]
|
|
2046
|
+
no → I'll show you the install commands for each
|
|
2047
|
+
```
|
|
2048
|
+
|
|
2049
|
+
## Step 4 — Create distribution package
|
|
2050
|
+
|
|
2051
|
+
Offer to create an org-skills npm package for distributing org-level config:
|
|
2052
|
+
|
|
2053
|
+
```
|
|
2054
|
+
Create `@your-org/mindforge-config` npm package?
|
|
2055
|
+
This package will distribute your org-level MindForge configuration
|
|
2056
|
+
to all projects in your organisation via: npx @your-org/mindforge-config
|
|
2057
|
+
|
|
2058
|
+
Files included:
|
|
2059
|
+
.mindforge/org/ORG.md
|
|
2060
|
+
.mindforge/org/CONVENTIONS.md
|
|
2061
|
+
.mindforge/org/SECURITY.md
|
|
2062
|
+
.mindforge/org/TOOLS.md
|
|
2063
|
+
.mindforge/org/skills/MANIFEST.md
|
|
2064
|
+
.mindforge/org/integrations/INTEGRATIONS-CONFIG.md (without credentials)
|
|
2065
|
+
.mindforge/governance/GOVERNANCE-CONFIG.md (without credentials)
|
|
2066
|
+
```
|
|
2067
|
+
|
|
2068
|
+
## Step 5 — Write AUDIT entry and report
|
|
2069
|
+
|
|
2070
|
+
```json
|
|
2071
|
+
{ "event": "org_initialised", "org_name": "[name]", "compliance_frameworks": [...], "skills_recommended": [...] }
|
|
2072
|
+
```
|
|
2073
|
+
|
|
2074
|
+
Report:
|
|
2075
|
+
```
|
|
2076
|
+
✅ MindForge organisation configuration complete.
|
|
2077
|
+
|
|
2078
|
+
Files created:
|
|
2079
|
+
.mindforge/org/ORG.md
|
|
2080
|
+
.mindforge/org/CONVENTIONS.md
|
|
2081
|
+
.mindforge/org/SECURITY.md
|
|
2082
|
+
.mindforge/org/TOOLS.md
|
|
2083
|
+
.mindforge/governance/GOVERNANCE-CONFIG.md
|
|
2084
|
+
|
|
2085
|
+
Next steps:
|
|
2086
|
+
1. Review each file — look for [CUSTOMISE THIS] markers
|
|
2087
|
+
2. Fill in SECURITY.md with your security team
|
|
2088
|
+
3. Commit to your org's mindforge-config repository
|
|
2089
|
+
4. Share with all projects: npx @your-org/mindforge-config (if you created the package)
|
|
2090
|
+
```
|
|
2091
|
+
```
|
|
2092
|
+
|
|
2093
|
+
**Commit:**
|
|
2094
|
+
```bash
|
|
2095
|
+
cp .claude/commands/mindforge/init-org.md .agent/mindforge/init-org.md
|
|
2096
|
+
git add .claude/commands/mindforge/init-org.md .agent/mindforge/init-org.md
|
|
2097
|
+
git commit -m "feat(commands): add /mindforge:init-org organisation-wide setup command"
|
|
2098
|
+
```
|
|
2099
|
+
|
|
2100
|
+
---
|
|
2101
|
+
|
|
2102
|
+
## TASK 8 — Write remaining Day 6 commands
|
|
2103
|
+
|
|
2104
|
+
### `.claude/commands/mindforge/install-skill.md`
|
|
2105
|
+
|
|
2106
|
+
```markdown
|
|
2107
|
+
# MindForge — Install Skill Command
|
|
2108
|
+
# Usage: /mindforge:install-skill [skill-name|package-name] [--tier 1|2|3] [--registry URL]
|
|
2109
|
+
|
|
2110
|
+
Follow the full installation protocol from `.mindforge/distribution/registry-client.md`.
|
|
2111
|
+
|
|
2112
|
+
Steps:
|
|
2113
|
+
1. Resolve package name from skill name
|
|
2114
|
+
2. Check if already installed (skip if same version, offer upgrade if newer)
|
|
2115
|
+
3. Fetch from registry (npm or private if --registry specified)
|
|
2116
|
+
4. Validate the skill (Level 1 + Level 2 from skill-validator.md)
|
|
2117
|
+
5. Run injection guard check
|
|
2118
|
+
6. Install to tier directory (default: Tier 2 org skill)
|
|
2119
|
+
7. Register in MANIFEST.md
|
|
2120
|
+
8. Write AUDIT entry
|
|
2121
|
+
9. Confirm: "Run /mindforge:skills validate to verify installation"
|
|
2122
|
+
```
|
|
2123
|
+
|
|
2124
|
+
### `.claude/commands/mindforge/publish-skill.md`
|
|
2125
|
+
|
|
2126
|
+
```markdown
|
|
2127
|
+
# MindForge — Publish Skill Command
|
|
2128
|
+
# Usage: /mindforge:publish-skill [skill-dir] [--registry URL] [--dry-run]
|
|
2129
|
+
|
|
2130
|
+
Publish a skill to the npm registry (or private registry).
|
|
2131
|
+
|
|
2132
|
+
Pre-publication checklist:
|
|
2133
|
+
1. Run full skill validation (Level 1 + 2 + 3 from skill-validator.md)
|
|
2134
|
+
Fail if Level 1 or 2 fails. Warn if Level 3 fails.
|
|
2135
|
+
2. Verify package.json has `mindforge` field with all required sub-fields
|
|
2136
|
+
3. Verify CHANGELOG.md has an entry for the current version
|
|
2137
|
+
4. Check if version already published: `npm info [package-name]@[version]`
|
|
2138
|
+
If already published: error "Version already exists. Bump the version."
|
|
2139
|
+
5. Run `npm pack --dry-run` to preview what will be published
|
|
2140
|
+
6. Confirm with user: "These files will be published: [list]. Proceed? (yes/no)"
|
|
2141
|
+
7. If --dry-run: stop here, show preview only
|
|
2142
|
+
8. Publish: `npm publish --access public`
|
|
2143
|
+
9. Verify: `npm info [package-name]@[version]` — confirm publication succeeded
|
|
2144
|
+
10. Write AUDIT: `{ "event": "skill_published", "package": "...", "version": "..." }`
|
|
2145
|
+
11. Report: "✅ [package-name]@[version] published to npm registry"
|
|
2146
|
+
```
|
|
2147
|
+
|
|
2148
|
+
### `.claude/commands/mindforge/pr-review.md`
|
|
2149
|
+
|
|
2150
|
+
```markdown
|
|
2151
|
+
# MindForge — PR Review Command
|
|
2152
|
+
# Usage: /mindforge:pr-review [--diff path] [--sha base..head] [--output github|json|markdown]
|
|
2153
|
+
|
|
2154
|
+
Run the AI PR review engine on a pull request diff.
|
|
2155
|
+
|
|
2156
|
+
Steps:
|
|
2157
|
+
1. Determine diff source:
|
|
2158
|
+
- `--diff path`: read diff from file
|
|
2159
|
+
- `--sha base..head`: run `git diff base..head`
|
|
2160
|
+
- Default: `git diff HEAD~1` (last commit) or `git diff --staged` (staged changes)
|
|
2161
|
+
|
|
2162
|
+
2. Load review context (per ai-reviewer.md):
|
|
2163
|
+
- PROJECT.md, ARCHITECTURE.md, CONVENTIONS.md, SECURITY.md
|
|
2164
|
+
- Current phase's CONTEXT.md (if in an active phase)
|
|
2165
|
+
- Any active ADRs relevant to changed files
|
|
2166
|
+
|
|
2167
|
+
3. Detect change type and select review template:
|
|
2168
|
+
- Auth/security changes → Security-focused review template
|
|
2169
|
+
- Database migrations → Database migration review template
|
|
2170
|
+
- API changes → API breaking change review template
|
|
2171
|
+
- Default → Standard review template
|
|
2172
|
+
|
|
2173
|
+
4. Check API availability:
|
|
2174
|
+
- ANTHROPIC_API_KEY set? If not: warn and skip AI review
|
|
2175
|
+
- Check daily review limit (from ai-reviewer.md)
|
|
2176
|
+
- Check cache: has this SHA been reviewed in the last 60 minutes?
|
|
2177
|
+
|
|
2178
|
+
5. Call Claude API (per ai-reviewer.md buildSystemPrompt + buildReviewPrompt)
|
|
2179
|
+
- Handle errors gracefully — API unavailable is NOT a build failure
|
|
2180
|
+
- Timeout: 60 seconds
|
|
2181
|
+
|
|
2182
|
+
6. Format output per --output flag:
|
|
2183
|
+
- github: GitHub-flavoured markdown for PR comment
|
|
2184
|
+
- json: structured JSON with findings array
|
|
2185
|
+
- markdown: standard markdown
|
|
2186
|
+
|
|
2187
|
+
7. Write to output:
|
|
2188
|
+
- If in CI: write to /tmp/mindforge-review.md (read by GitHub Actions step)
|
|
2189
|
+
- If interactive: display to user
|
|
2190
|
+
|
|
2191
|
+
8. Write AUDIT entry
|
|
2192
|
+
```
|
|
2193
|
+
|
|
2194
|
+
### `.claude/commands/mindforge/workspace.md`
|
|
2195
|
+
|
|
2196
|
+
```markdown
|
|
2197
|
+
# MindForge — Workspace Command
|
|
2198
|
+
# Usage: /mindforge:workspace [detect|list|plan phase N|test]
|
|
2199
|
+
|
|
2200
|
+
Monorepo workspace management.
|
|
2201
|
+
|
|
2202
|
+
## detect
|
|
2203
|
+
Run workspace detector from `.mindforge/monorepo/workspace-detector.md`.
|
|
2204
|
+
Write WORKSPACE-MANIFEST.json.
|
|
2205
|
+
Report: workspace type, packages found, dependency order.
|
|
2206
|
+
|
|
2207
|
+
## list
|
|
2208
|
+
Read WORKSPACE-MANIFEST.json and display package list:
|
|
2209
|
+
```
|
|
2210
|
+
Workspace: Turborepo (4 packages)
|
|
2211
|
+
packages/shared → @myapp/shared (lib, 0 dependents)
|
|
2212
|
+
apps/api → @myapp/api (api, depends on: shared)
|
|
2213
|
+
apps/web → @myapp/web (web, depends on: shared, api)
|
|
2214
|
+
apps/mobile → @myapp/mobile (mobile, depends on: shared)
|
|
2215
|
+
Execution order: shared → api → (web, mobile in parallel)
|
|
2216
|
+
```
|
|
2217
|
+
|
|
2218
|
+
## plan phase N
|
|
2219
|
+
Create a phase plan that spans multiple packages.
|
|
2220
|
+
Uses cross-package-planner.md to determine package execution order.
|
|
2221
|
+
Each PLAN file includes a `<package>` and `<working-dir>` field.
|
|
2222
|
+
|
|
2223
|
+
## test
|
|
2224
|
+
Run tests across all packages in dependency order.
|
|
2225
|
+
Report per-package test results and aggregate coverage.
|
|
2226
|
+
```
|
|
2227
|
+
|
|
2228
|
+
### `.claude/commands/mindforge/benchmark.md`
|
|
2229
|
+
|
|
2230
|
+
```markdown
|
|
2231
|
+
# MindForge — Benchmark Command
|
|
2232
|
+
# Usage: /mindforge:benchmark [--skill skill-name] [--compare skill-a skill-b]
|
|
2233
|
+
|
|
2234
|
+
Measure skill effectiveness over time.
|
|
2235
|
+
|
|
2236
|
+
## Single skill benchmark
|
|
2237
|
+
For a named skill, analyse AUDIT.jsonl and skill-usage.jsonl:
|
|
2238
|
+
- How many times was the skill loaded this month?
|
|
2239
|
+
- What is the verify pass rate for tasks where this skill was loaded?
|
|
2240
|
+
- Are there anti-patterns less common after this skill is loaded?
|
|
2241
|
+
- What is the average session quality score when this skill is active?
|
|
2242
|
+
|
|
2243
|
+
Report:
|
|
2244
|
+
```
|
|
2245
|
+
Skill Benchmark: security-review v1.0.0
|
|
2246
|
+
────────────────────────────────────────
|
|
2247
|
+
Usage (last 30 days): 47 task loads
|
|
2248
|
+
Trigger distribution: text match 68%, file-path 22%, file-name 10%
|
|
2249
|
+
Verify pass rate: 91% (vs. 84% baseline without this skill)
|
|
2250
|
+
Security findings: 8 HIGH caught (0 CRITICAL missed in tasks using this skill)
|
|
2251
|
+
Session quality lift: +6.2 points average when loaded
|
|
2252
|
+
|
|
2253
|
+
Assessment: HIGH VALUE — clear quality improvement signal
|
|
2254
|
+
```
|
|
2255
|
+
|
|
2256
|
+
## Skill comparison
|
|
2257
|
+
Compare two skills head-to-head:
|
|
2258
|
+
- Load frequency
|
|
2259
|
+
- Verify pass rate improvement
|
|
2260
|
+
- Anti-pattern detection rate
|
|
2261
|
+
- Context budget cost (token estimate)
|
|
2262
|
+
|
|
2263
|
+
Helps decide: should you keep both skills, or deprecate the lower-performer?
|
|
2264
|
+
```
|
|
2265
|
+
|
|
2266
|
+
**Commit:**
|
|
2267
|
+
```bash
|
|
2268
|
+
for cmd in install-skill publish-skill pr-review workspace benchmark; do
|
|
2269
|
+
cp .claude/commands/mindforge/${cmd}.md .agent/mindforge/${cmd}.md
|
|
2270
|
+
done
|
|
2271
|
+
git add .claude/commands/mindforge/ .agent/mindforge/
|
|
2272
|
+
git commit -m "feat(commands): add install-skill, publish-skill, pr-review, workspace, benchmark commands"
|
|
2273
|
+
```
|
|
2274
|
+
|
|
2275
|
+
---
|
|
2276
|
+
|
|
2277
|
+
## TASK 9 — Write the MINDFORGE.md JSON Schema
|
|
2278
|
+
|
|
2279
|
+
### `.mindforge/MINDFORGE-SCHEMA.json`
|
|
2280
|
+
|
|
2281
|
+
```json
|
|
2282
|
+
{
|
|
2283
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
2284
|
+
"title": "MindForge Project Configuration Schema",
|
|
2285
|
+
"description": "JSON Schema for MINDFORGE.md key-value settings",
|
|
2286
|
+
"type": "object",
|
|
2287
|
+
"properties": {
|
|
2288
|
+
"MINDFORGE_VERSION_REQUIRED": {
|
|
2289
|
+
"type": "string",
|
|
2290
|
+
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
|
2291
|
+
"description": "Minimum MindForge version required for this config"
|
|
2292
|
+
},
|
|
2293
|
+
"PLANNER_MODEL": {
|
|
2294
|
+
"type": "enum",
|
|
2295
|
+
"values": ["claude-opus-4-5", "claude-sonnet-4-5", "claude-haiku-4-5", "inherit"],
|
|
2296
|
+
"description": "Claude model to use for the planning agent"
|
|
2297
|
+
},
|
|
2298
|
+
"EXECUTOR_MODEL": {
|
|
2299
|
+
"type": "enum",
|
|
2300
|
+
"values": ["claude-opus-4-5", "claude-sonnet-4-5", "claude-haiku-4-5", "inherit"],
|
|
2301
|
+
"description": "Claude model to use for execution agents"
|
|
2302
|
+
},
|
|
2303
|
+
"REVIEWER_MODEL": {
|
|
2304
|
+
"type": "enum",
|
|
2305
|
+
"values": ["claude-opus-4-5", "claude-sonnet-4-5", "claude-haiku-4-5", "inherit"],
|
|
2306
|
+
"description": "Claude model to use for the code reviewer"
|
|
2307
|
+
},
|
|
2308
|
+
"SECURITY_MODEL": {
|
|
2309
|
+
"type": "enum",
|
|
2310
|
+
"values": ["claude-opus-4-5", "claude-sonnet-4-5", "claude-haiku-4-5", "inherit"],
|
|
2311
|
+
"description": "Claude model to use for security review (recommend Opus for thoroughness)"
|
|
2312
|
+
},
|
|
2313
|
+
"TIER1_AUTO_APPROVE": {
|
|
2314
|
+
"type": "boolean",
|
|
2315
|
+
"description": "Auto-approve Tier 1 changes without user confirmation"
|
|
2316
|
+
},
|
|
2317
|
+
"WAVE_CONFIRMATION_REQUIRED": {
|
|
2318
|
+
"type": "boolean",
|
|
2319
|
+
"description": "Require user confirmation before each execution wave"
|
|
2320
|
+
},
|
|
2321
|
+
"AUTO_DISCUSS_PHASE": {
|
|
2322
|
+
"type": "boolean",
|
|
2323
|
+
"description": "Automatically run discuss-phase before every plan-phase"
|
|
2324
|
+
},
|
|
2325
|
+
"VERIFY_PASS_RATE_WARNING_THRESHOLD": {
|
|
2326
|
+
"type": "number",
|
|
2327
|
+
"minimum": 0,
|
|
2328
|
+
"maximum": 1,
|
|
2329
|
+
"description": "Warn when first-attempt verify pass rate drops below this"
|
|
2330
|
+
},
|
|
2331
|
+
"COMPACTION_THRESHOLD_PCT": {
|
|
2332
|
+
"type": "number",
|
|
2333
|
+
"minimum": 50,
|
|
2334
|
+
"maximum": 90,
|
|
2335
|
+
"description": "Context window percentage that triggers compaction"
|
|
2336
|
+
},
|
|
2337
|
+
"MAX_TASKS_PER_PHASE": {
|
|
2338
|
+
"type": "number",
|
|
2339
|
+
"minimum": 1,
|
|
2340
|
+
"maximum": 50,
|
|
2341
|
+
"description": "Suggest phase split when task count exceeds this"
|
|
2342
|
+
},
|
|
2343
|
+
"MIN_TEST_COVERAGE_PCT": {
|
|
2344
|
+
"type": "number",
|
|
2345
|
+
"minimum": 0,
|
|
2346
|
+
"maximum": 100,
|
|
2347
|
+
"description": "Minimum test coverage percentage required"
|
|
2348
|
+
},
|
|
2349
|
+
"MAX_FUNCTION_LINES": {
|
|
2350
|
+
"type": "number",
|
|
2351
|
+
"minimum": 10,
|
|
2352
|
+
"maximum": 200,
|
|
2353
|
+
"description": "Maximum allowed function length in lines"
|
|
2354
|
+
},
|
|
2355
|
+
"MAX_CYCLOMATIC_COMPLEXITY": {
|
|
2356
|
+
"type": "number",
|
|
2357
|
+
"minimum": 3,
|
|
2358
|
+
"maximum": 30,
|
|
2359
|
+
"description": "Maximum allowed cyclomatic complexity per function"
|
|
2360
|
+
},
|
|
2361
|
+
"BLOCK_ON_MEDIUM_SECURITY_FINDINGS": {
|
|
2362
|
+
"type": "boolean",
|
|
2363
|
+
"description": "Block PR creation on MEDIUM security findings (default: only HIGH+)"
|
|
2364
|
+
},
|
|
2365
|
+
"ALWAYS_LOAD_SKILLS": {
|
|
2366
|
+
"type": "string",
|
|
2367
|
+
"description": "Comma-separated list of skills to always load regardless of triggers"
|
|
2368
|
+
},
|
|
2369
|
+
"DISABLED_SKILLS": {
|
|
2370
|
+
"type": "string",
|
|
2371
|
+
"description": "Comma-separated list of skills to never load"
|
|
2372
|
+
},
|
|
2373
|
+
"MAX_FULL_SKILL_INJECTIONS": {
|
|
2374
|
+
"type": "number",
|
|
2375
|
+
"minimum": 1,
|
|
2376
|
+
"maximum": 10,
|
|
2377
|
+
"description": "Maximum number of skills to inject in full (rest are summarised)"
|
|
2378
|
+
},
|
|
2379
|
+
"COMMIT_FORMAT": {
|
|
2380
|
+
"type": "enum",
|
|
2381
|
+
"values": ["conventional-commits", "custom", "none"],
|
|
2382
|
+
"description": "Commit message format convention"
|
|
2383
|
+
},
|
|
2384
|
+
"BRANCHING_STRATEGY": {
|
|
2385
|
+
"type": "enum",
|
|
2386
|
+
"values": ["none", "phase", "milestone"],
|
|
2387
|
+
"description": "Git branching strategy for MindForge phases"
|
|
2388
|
+
},
|
|
2389
|
+
"NOTIFY_ON": {
|
|
2390
|
+
"type": "string",
|
|
2391
|
+
"description": "Comma-separated events that trigger Slack notifications"
|
|
2392
|
+
},
|
|
2393
|
+
"DISCUSS_PHASE_REQUIRED_ABOVE_DIFFICULTY": {
|
|
2394
|
+
"type": "number",
|
|
2395
|
+
"minimum": 1,
|
|
2396
|
+
"maximum": 5,
|
|
2397
|
+
"description": "Require discuss-phase when difficulty score exceeds this value"
|
|
2398
|
+
},
|
|
2399
|
+
"CI_AUTO_APPROVE_TIER2": {
|
|
2400
|
+
"type": "boolean",
|
|
2401
|
+
"nonOverridable": false,
|
|
2402
|
+
"description": "Auto-approve Tier 2 changes in CI mode"
|
|
2403
|
+
},
|
|
2404
|
+
"CI_SECURITY_SCAN": {
|
|
2405
|
+
"type": "boolean",
|
|
2406
|
+
"description": "Run security scan in CI mode"
|
|
2407
|
+
},
|
|
2408
|
+
"CI_MIN_COVERAGE_PCT": {
|
|
2409
|
+
"type": "number",
|
|
2410
|
+
"minimum": 0,
|
|
2411
|
+
"maximum": 100,
|
|
2412
|
+
"description": "Minimum test coverage in CI (may differ from interactive threshold)"
|
|
2413
|
+
},
|
|
2414
|
+
"CI_OUTPUT_FORMAT": {
|
|
2415
|
+
"type": "enum",
|
|
2416
|
+
"values": ["json", "text", "github-annotations"],
|
|
2417
|
+
"description": "Output format for CI execution logs"
|
|
2418
|
+
},
|
|
2419
|
+
"SECURITY_AUTOTRIGGER": {
|
|
2420
|
+
"type": "boolean",
|
|
2421
|
+
"nonOverridable": true,
|
|
2422
|
+
"description": "NON-OVERRIDABLE: security auto-trigger for auth/payment/PII changes"
|
|
2423
|
+
},
|
|
2424
|
+
"SECRET_DETECTION": {
|
|
2425
|
+
"type": "boolean",
|
|
2426
|
+
"nonOverridable": true,
|
|
2427
|
+
"description": "NON-OVERRIDABLE: secret detection compliance gate"
|
|
2428
|
+
},
|
|
2429
|
+
"PLAN_FIRST": {
|
|
2430
|
+
"type": "boolean",
|
|
2431
|
+
"nonOverridable": true,
|
|
2432
|
+
"description": "NON-OVERRIDABLE: plan-first rule (no implementation without a PLAN)"
|
|
2433
|
+
},
|
|
2434
|
+
"AUDIT_WRITING": {
|
|
2435
|
+
"type": "boolean",
|
|
2436
|
+
"nonOverridable": true,
|
|
2437
|
+
"description": "NON-OVERRIDABLE: AUDIT.jsonl writing for every significant action"
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
```
|
|
2442
|
+
|
|
2443
|
+
**Commit:**
|
|
2444
|
+
```bash
|
|
2445
|
+
git add .mindforge/MINDFORGE-SCHEMA.json bin/validate-config.js
|
|
2446
|
+
git commit -m "feat(config): add MINDFORGE.md JSON schema with non-overridable fields"
|
|
2447
|
+
```
|
|
2448
|
+
|
|
2449
|
+
---
|
|
2450
|
+
|
|
2451
|
+
## TASK 10 — Update CLAUDE.md for Day 6
|
|
2452
|
+
|
|
2453
|
+
Add to `.claude/CLAUDE.md` and `.agent/CLAUDE.md`:
|
|
2454
|
+
|
|
2455
|
+
```markdown
|
|
2456
|
+
---
|
|
2457
|
+
|
|
2458
|
+
## DISTRIBUTION & CI LAYER (Day 6)
|
|
2459
|
+
|
|
2460
|
+
### CI mode awareness
|
|
2461
|
+
If `CI=true` or `MINDFORGE_CI=true` environment variables are set:
|
|
2462
|
+
- All interactive prompts are skipped
|
|
2463
|
+
- Output structured JSON or GitHub annotations (per CI_OUTPUT_FORMAT in MINDFORGE.md)
|
|
2464
|
+
- Tier 3 changes automatically fail (never auto-approve Tier 3 in CI)
|
|
2465
|
+
- UAT is skipped (CI_SKIP_UAT=true default) — only automated verification runs
|
|
2466
|
+
- Log every gate result to stdout in the configured format
|
|
2467
|
+
|
|
2468
|
+
### Skill installation from registry
|
|
2469
|
+
When the user requests `/mindforge:install-skill [name]`:
|
|
2470
|
+
Follow the full protocol from `.mindforge/distribution/registry-client.md`.
|
|
2471
|
+
Always validate before installing. Always run injection guard.
|
|
2472
|
+
Never install a skill that fails Level 1 or Level 2 validation.
|
|
2473
|
+
|
|
2474
|
+
### Monorepo awareness
|
|
2475
|
+
If `WORKSPACE-MANIFEST.json` exists in `.planning/`:
|
|
2476
|
+
- The project uses a monorepo structure
|
|
2477
|
+
- Phase execution must follow the cross-package dependency order
|
|
2478
|
+
- Each PLAN file must declare its `<package>` and `<working-dir>`
|
|
2479
|
+
- Run tests per-package, then aggregate
|
|
2480
|
+
|
|
2481
|
+
### AI PR Review
|
|
2482
|
+
When the user requests `/mindforge:pr-review`:
|
|
2483
|
+
- Check for ANTHROPIC_API_KEY — if missing, skip gracefully (not a failure)
|
|
2484
|
+
- Load review context from PROJECT.md, ARCHITECTURE.md, CONVENTIONS.md
|
|
2485
|
+
- Select the appropriate review template based on change type
|
|
2486
|
+
- Never use the AI review as a substitute for human review
|
|
2487
|
+
- Always include the disclaimer in output
|
|
2488
|
+
|
|
2489
|
+
### Config validation
|
|
2490
|
+
At session start, if MINDFORGE.md exists:
|
|
2491
|
+
Run `node bin/validate-config.js` silently.
|
|
2492
|
+
If errors: warn the user before proceeding.
|
|
2493
|
+
If warnings about non-overridable settings: ignore the override silently (per ADR-013).
|
|
2494
|
+
|
|
2495
|
+
### New commands available (Day 6)
|
|
2496
|
+
- `/mindforge:init-org` — organisation-wide setup
|
|
2497
|
+
- `/mindforge:install-skill` — install skill from registry
|
|
2498
|
+
- `/mindforge:publish-skill` — publish skill to registry
|
|
2499
|
+
- `/mindforge:pr-review` — AI code review
|
|
2500
|
+
- `/mindforge:workspace` — monorepo workspace management
|
|
2501
|
+
- `/mindforge:benchmark` — skill effectiveness benchmarking
|
|
2502
|
+
|
|
2503
|
+
---
|
|
2504
|
+
```
|
|
2505
|
+
|
|
2506
|
+
**Commit:**
|
|
2507
|
+
```bash
|
|
2508
|
+
git add .claude/CLAUDE.md .agent/CLAUDE.md
|
|
2509
|
+
git commit -m "feat(core): update CLAUDE.md with Day 6 distribution and CI awareness"
|
|
2510
|
+
```
|
|
2511
|
+
|
|
2512
|
+
---
|
|
2513
|
+
|
|
2514
|
+
## TASK 11 — Write Day 6 test suites
|
|
2515
|
+
|
|
2516
|
+
### `tests/distribution.test.js`
|
|
2517
|
+
|
|
2518
|
+
```javascript
|
|
2519
|
+
/**
|
|
2520
|
+
* MindForge Day 6 — Distribution Tests
|
|
2521
|
+
* Run: node tests/distribution.test.js
|
|
2522
|
+
*/
|
|
2523
|
+
'use strict';
|
|
2524
|
+
const fs = require('fs'), path = require('path'), assert = require('assert');
|
|
2525
|
+
let passed = 0, failed = 0;
|
|
2526
|
+
|
|
2527
|
+
function test(name, fn) {
|
|
2528
|
+
try { fn(); console.log(` ✅ ${name}`); passed++; }
|
|
2529
|
+
catch(e) { console.error(` ❌ ${name}\n ${e.message}`); failed++; }
|
|
2530
|
+
}
|
|
2531
|
+
const read = p => fs.existsSync(p) ? fs.readFileSync(p, 'utf8') : '';
|
|
2532
|
+
|
|
2533
|
+
// ── Skill package name validation ─────────────────────────────────────────────
|
|
2534
|
+
function isValidSkillPackageName(name) {
|
|
2535
|
+
return /^mindforge-skill-[a-z][a-z0-9-]+$/.test(name);
|
|
2536
|
+
}
|
|
2537
|
+
|
|
2538
|
+
// ── Skill frontmatter parser (reused from earlier tests) ──────────────────────
|
|
2539
|
+
function parseSkillFrontmatter(content) {
|
|
2540
|
+
if (!content.startsWith('---')) throw new Error('Missing frontmatter');
|
|
2541
|
+
const end = content.indexOf('---', 3);
|
|
2542
|
+
if (end === -1) throw new Error('Unclosed frontmatter');
|
|
2543
|
+
const fm = content.slice(3, end).trim();
|
|
2544
|
+
const result = {};
|
|
2545
|
+
fm.split('\n').forEach(line => {
|
|
2546
|
+
const colon = line.indexOf(':');
|
|
2547
|
+
if (colon === -1) return;
|
|
2548
|
+
result[line.slice(0, colon).trim()] = line.slice(colon + 1).trim();
|
|
2549
|
+
});
|
|
2550
|
+
return result;
|
|
2551
|
+
}
|
|
2552
|
+
|
|
2553
|
+
// ── MINDFORGE-SCHEMA.json validation ─────────────────────────────────────────
|
|
2554
|
+
function parseMindforgeMd(content) {
|
|
2555
|
+
const settings = {};
|
|
2556
|
+
content.split('\n').forEach(line => {
|
|
2557
|
+
const m = line.match(/^([A-Z_]+)=(.+)$/);
|
|
2558
|
+
if (m) settings[m[1]] = m[2].trim();
|
|
2559
|
+
});
|
|
2560
|
+
return settings;
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
console.log('\nMindForge Day 6 — Distribution Tests\n');
|
|
2564
|
+
|
|
2565
|
+
console.log('Distribution engine files:');
|
|
2566
|
+
[
|
|
2567
|
+
'registry-client.md', 'skill-publisher.md', 'skill-validator.md', 'registry-schema.md'
|
|
2568
|
+
].forEach(f => test(`${f} exists`, () => {
|
|
2569
|
+
assert.ok(fs.existsSync(`.mindforge/distribution/${f}`), `Missing: ${f}`);
|
|
2570
|
+
}));
|
|
2571
|
+
|
|
2572
|
+
console.log('\nSkill package naming:');
|
|
2573
|
+
test('valid package name accepted', () => {
|
|
2574
|
+
assert.ok(isValidSkillPackageName('mindforge-skill-security-owasp'));
|
|
2575
|
+
assert.ok(isValidSkillPackageName('mindforge-skill-db-postgres'));
|
|
2576
|
+
assert.ok(isValidSkillPackageName('mindforge-skill-frontend-react-a11y'));
|
|
2577
|
+
});
|
|
2578
|
+
|
|
2579
|
+
test('invalid package names rejected', () => {
|
|
2580
|
+
assert.ok(!isValidSkillPackageName('security-review')); // missing prefix
|
|
2581
|
+
assert.ok(!isValidSkillPackageName('mindforge-skill-')); // empty name
|
|
2582
|
+
assert.ok(!isValidSkillPackageName('mindforge-skill-MY-SKILL')); // uppercase
|
|
2583
|
+
});
|
|
2584
|
+
|
|
2585
|
+
console.log('\nRegistry schema:');
|
|
2586
|
+
test('registry-schema.md defines npm-based distribution', () => {
|
|
2587
|
+
const c = read('.mindforge/distribution/registry-schema.md');
|
|
2588
|
+
assert.ok(c.includes('npm'), 'Should describe npm-based registry');
|
|
2589
|
+
assert.ok(c.includes('mindforge-skill-'), 'Should define naming convention');
|
|
2590
|
+
});
|
|
2591
|
+
|
|
2592
|
+
test('skill validator defines 3 validation levels', () => {
|
|
2593
|
+
const c = read('.mindforge/distribution/skill-validator.md');
|
|
2594
|
+
assert.ok(c.includes('Level 1'), 'Missing Level 1');
|
|
2595
|
+
assert.ok(c.includes('Level 2'), 'Missing Level 2');
|
|
2596
|
+
assert.ok(c.includes('Level 3'), 'Missing Level 3');
|
|
2597
|
+
});
|
|
2598
|
+
|
|
2599
|
+
test('registry client has injection guard step', () => {
|
|
2600
|
+
const c = read('.mindforge/distribution/registry-client.md');
|
|
2601
|
+
assert.ok(c.includes('injection guard') || c.includes('injection'), 'Should run injection guard before install');
|
|
2602
|
+
});
|
|
2603
|
+
|
|
2604
|
+
console.log('\nMINDFORGE.md schema:');
|
|
2605
|
+
test('MINDFORGE-SCHEMA.json exists', () => {
|
|
2606
|
+
assert.ok(fs.existsSync('.mindforge/MINDFORGE-SCHEMA.json'));
|
|
2607
|
+
});
|
|
2608
|
+
|
|
2609
|
+
test('schema is valid JSON', () => {
|
|
2610
|
+
const content = fs.readFileSync('.mindforge/MINDFORGE-SCHEMA.json', 'utf8');
|
|
2611
|
+
assert.doesNotThrow(() => JSON.parse(content));
|
|
2612
|
+
});
|
|
2613
|
+
|
|
2614
|
+
test('schema marks non-overridable fields', () => {
|
|
2615
|
+
const schema = JSON.parse(fs.readFileSync('.mindforge/MINDFORGE-SCHEMA.json', 'utf8'));
|
|
2616
|
+
const nonOverridable = Object.entries(schema.properties || {})
|
|
2617
|
+
.filter(([, def]) => def.nonOverridable === true)
|
|
2618
|
+
.map(([key]) => key);
|
|
2619
|
+
assert.ok(nonOverridable.includes('SECURITY_AUTOTRIGGER'), 'SECURITY_AUTOTRIGGER should be non-overridable');
|
|
2620
|
+
assert.ok(nonOverridable.includes('SECRET_DETECTION'), 'SECRET_DETECTION should be non-overridable');
|
|
2621
|
+
assert.ok(nonOverridable.includes('PLAN_FIRST'), 'PLAN_FIRST should be non-overridable');
|
|
2622
|
+
assert.ok(nonOverridable.includes('AUDIT_WRITING'), 'AUDIT_WRITING should be non-overridable');
|
|
2623
|
+
});
|
|
2624
|
+
|
|
2625
|
+
test('validate-config.js exists and is executable-looking', () => {
|
|
2626
|
+
assert.ok(fs.existsSync('bin/validate-config.js'));
|
|
2627
|
+
const content = read('bin/validate-config.js');
|
|
2628
|
+
assert.ok(content.includes('#!/usr/bin/env node'), 'Missing shebang');
|
|
2629
|
+
assert.ok(content.includes('MINDFORGE-SCHEMA.json'), 'Should reference schema file');
|
|
2630
|
+
});
|
|
2631
|
+
|
|
2632
|
+
console.log('\nSDK:');
|
|
2633
|
+
test('sdk directory structure exists', () => {
|
|
2634
|
+
assert.ok(fs.existsSync('sdk/src/index.ts'));
|
|
2635
|
+
assert.ok(fs.existsSync('sdk/src/client.ts'));
|
|
2636
|
+
assert.ok(fs.existsSync('sdk/src/types.ts'));
|
|
2637
|
+
assert.ok(fs.existsSync('sdk/src/events.ts'));
|
|
2638
|
+
assert.ok(fs.existsSync('sdk/src/commands.ts'));
|
|
2639
|
+
assert.ok(fs.existsSync('sdk/package.json'));
|
|
2640
|
+
});
|
|
2641
|
+
|
|
2642
|
+
test('sdk package.json has correct name', () => {
|
|
2643
|
+
const pkg = JSON.parse(fs.readFileSync('sdk/package.json', 'utf8'));
|
|
2644
|
+
assert.strictEqual(pkg.name, '@mindforge/sdk');
|
|
2645
|
+
});
|
|
2646
|
+
|
|
2647
|
+
test('sdk index.ts exports MindForgeClient', () => {
|
|
2648
|
+
const content = read('sdk/src/index.ts');
|
|
2649
|
+
assert.ok(content.includes('MindForgeClient'), 'Should export MindForgeClient');
|
|
2650
|
+
assert.ok(content.includes('MindForgeEventStream'), 'Should export MindForgeEventStream');
|
|
2651
|
+
});
|
|
2652
|
+
|
|
2653
|
+
test('sdk types.ts defines PhaseResult', () => {
|
|
2654
|
+
const content = read('sdk/src/types.ts');
|
|
2655
|
+
assert.ok(content.includes('PhaseResult'), 'Should define PhaseResult');
|
|
2656
|
+
assert.ok(content.includes('SecurityFinding'), 'Should define SecurityFinding');
|
|
2657
|
+
assert.ok(content.includes('MindForgeEvent'), 'Should define MindForgeEvent');
|
|
2658
|
+
});
|
|
2659
|
+
|
|
2660
|
+
console.log('\nAll 31 commands present:');
|
|
2661
|
+
const ALL_COMMANDS = [
|
|
2662
|
+
'help','init-project','plan-phase','execute-phase','verify-phase','ship',
|
|
2663
|
+
'next','quick','status','debug',
|
|
2664
|
+
'skills','review','security-scan','map-codebase','discuss-phase',
|
|
2665
|
+
'audit','milestone','complete-milestone','approve','sync-jira','sync-confluence',
|
|
2666
|
+
'health','retrospective','profile-team','metrics',
|
|
2667
|
+
'init-org','install-skill','publish-skill','pr-review','workspace','benchmark'
|
|
2668
|
+
];
|
|
2669
|
+
test(`all ${ALL_COMMANDS.length} commands in .claude/commands/mindforge/`, () => {
|
|
2670
|
+
ALL_COMMANDS.forEach(cmd => {
|
|
2671
|
+
assert.ok(fs.existsSync(`.claude/commands/mindforge/${cmd}.md`), `Missing: ${cmd}.md`);
|
|
2672
|
+
});
|
|
2673
|
+
});
|
|
2674
|
+
test(`all ${ALL_COMMANDS.length} commands mirrored to .agent/mindforge/`, () => {
|
|
2675
|
+
ALL_COMMANDS.forEach(cmd => {
|
|
2676
|
+
assert.ok(fs.existsSync(`.agent/mindforge/${cmd}.md`), `Missing .agent: ${cmd}.md`);
|
|
2677
|
+
});
|
|
2678
|
+
});
|
|
2679
|
+
|
|
2680
|
+
console.log(`\n${'─'.repeat(50)}`);
|
|
2681
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
2682
|
+
if (failed > 0) { console.error(`\n❌ ${failed} test(s) failed.\n`); process.exit(1); }
|
|
2683
|
+
else { console.log(`\n✅ All distribution tests passed.\n`); }
|
|
2684
|
+
```
|
|
2685
|
+
|
|
2686
|
+
### `tests/ci-mode.test.js`
|
|
2687
|
+
|
|
2688
|
+
```javascript
|
|
2689
|
+
/**
|
|
2690
|
+
* MindForge Day 6 — CI Mode Tests
|
|
2691
|
+
* Run: node tests/ci-mode.test.js
|
|
2692
|
+
*/
|
|
2693
|
+
'use strict';
|
|
2694
|
+
const fs = require('fs'), assert = require('assert');
|
|
2695
|
+
let passed = 0, failed = 0;
|
|
2696
|
+
function test(name, fn) {
|
|
2697
|
+
try { fn(); console.log(` ✅ ${name}`); passed++; }
|
|
2698
|
+
catch(e) { console.error(` ❌ ${name}\n ${e.message}`); failed++; }
|
|
2699
|
+
}
|
|
2700
|
+
const read = p => fs.existsSync(p) ? fs.readFileSync(p, 'utf8') : '';
|
|
2701
|
+
|
|
2702
|
+
// ── CI mode detection simulation ──────────────────────────────────────────────
|
|
2703
|
+
function isCiMode() {
|
|
2704
|
+
return process.env.CI === 'true' ||
|
|
2705
|
+
process.env.MINDFORGE_CI === 'true' ||
|
|
2706
|
+
process.stdin.isTTY === false;
|
|
2707
|
+
}
|
|
2708
|
+
|
|
2709
|
+
// ── GitHub annotations format ─────────────────────────────────────────────────
|
|
2710
|
+
function formatGitHubAnnotation(level, message, file, line) {
|
|
2711
|
+
const loc = file ? ` file=${file}${line ? `,line=${line}` : ''}` : '';
|
|
2712
|
+
return `::${level}${loc}::${message}`;
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
// ── Tier3 CI block simulation ─────────────────────────────────────────────────
|
|
2716
|
+
function checkCiTierPolicy(tier, ciAutoApproveTier2 = false) {
|
|
2717
|
+
if (tier === 1) return 'auto-approved';
|
|
2718
|
+
if (tier === 2) return ciAutoApproveTier2 ? 'auto-approved' : 'blocked';
|
|
2719
|
+
if (tier === 3) return 'blocked'; // Always blocked in CI
|
|
2720
|
+
return 'unknown';
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
console.log('\nMindForge Day 6 — CI Mode Tests\n');
|
|
2724
|
+
|
|
2725
|
+
console.log('CI engine files:');
|
|
2726
|
+
['ci-mode.md','github-actions-adapter.md','gitlab-ci-adapter.md'].forEach(f => {
|
|
2727
|
+
test(`${f} exists`, () => {
|
|
2728
|
+
assert.ok(fs.existsSync(`.mindforge/ci/${f}`), `Missing: .mindforge/ci/${f}`);
|
|
2729
|
+
});
|
|
2730
|
+
});
|
|
2731
|
+
|
|
2732
|
+
console.log('\nCI mode detection:');
|
|
2733
|
+
test('isCiMode returns false in normal test environment', () => {
|
|
2734
|
+
const origCI = process.env.CI;
|
|
2735
|
+
delete process.env.CI;
|
|
2736
|
+
delete process.env.MINDFORGE_CI;
|
|
2737
|
+
// In test environment, stdin.isTTY may be false — we test the env var logic only
|
|
2738
|
+
const ciFromEnv = process.env.CI === 'true' || process.env.MINDFORGE_CI === 'true';
|
|
2739
|
+
assert.strictEqual(ciFromEnv, false, 'Should not be CI mode from env vars');
|
|
2740
|
+
if (origCI) process.env.CI = origCI;
|
|
2741
|
+
});
|
|
2742
|
+
|
|
2743
|
+
test('CI=true activates CI mode', () => {
|
|
2744
|
+
process.env.MINDFORGE_CI = 'true';
|
|
2745
|
+
assert.strictEqual(process.env.MINDFORGE_CI, 'true');
|
|
2746
|
+
delete process.env.MINDFORGE_CI;
|
|
2747
|
+
});
|
|
2748
|
+
|
|
2749
|
+
console.log('\nCI tier policy:');
|
|
2750
|
+
test('Tier 1 is always auto-approved in CI', () => {
|
|
2751
|
+
assert.strictEqual(checkCiTierPolicy(1), 'auto-approved');
|
|
2752
|
+
});
|
|
2753
|
+
|
|
2754
|
+
test('Tier 2 blocked by default in CI (safety-first)', () => {
|
|
2755
|
+
assert.strictEqual(checkCiTierPolicy(2, false), 'blocked');
|
|
2756
|
+
});
|
|
2757
|
+
|
|
2758
|
+
test('Tier 2 can be auto-approved in CI when configured', () => {
|
|
2759
|
+
assert.strictEqual(checkCiTierPolicy(2, true), 'auto-approved');
|
|
2760
|
+
});
|
|
2761
|
+
|
|
2762
|
+
test('Tier 3 is ALWAYS blocked in CI regardless of config', () => {
|
|
2763
|
+
assert.strictEqual(checkCiTierPolicy(3, true), 'blocked'); // even with true
|
|
2764
|
+
assert.strictEqual(checkCiTierPolicy(3, false), 'blocked');
|
|
2765
|
+
});
|
|
2766
|
+
|
|
2767
|
+
console.log('\nGitHub annotations format:');
|
|
2768
|
+
test('notice annotation format is correct', () => {
|
|
2769
|
+
const ann = formatGitHubAnnotation('notice', 'Task 3-01 completed', null, null);
|
|
2770
|
+
assert.strictEqual(ann, '::notice::Task 3-01 completed');
|
|
2771
|
+
});
|
|
2772
|
+
|
|
2773
|
+
test('error annotation with file and line is correct', () => {
|
|
2774
|
+
const ann = formatGitHubAnnotation('error', 'TypeScript error', 'src/auth.ts', 47);
|
|
2775
|
+
assert.strictEqual(ann, '::error file=src/auth.ts,line=47::TypeScript error');
|
|
2776
|
+
});
|
|
2777
|
+
|
|
2778
|
+
test('warning annotation with file only (no line)', () => {
|
|
2779
|
+
const ann = formatGitHubAnnotation('warning', 'Security finding', 'src/utils.ts', null);
|
|
2780
|
+
assert.strictEqual(ann, '::warning file=src/utils.ts::Security finding');
|
|
2781
|
+
});
|
|
2782
|
+
|
|
2783
|
+
console.log('\nGitHub Actions workflow:');
|
|
2784
|
+
test('github-actions-adapter.md defines mindforge-ci.yml structure', () => {
|
|
2785
|
+
const c = read('.mindforge/ci/github-actions-adapter.md');
|
|
2786
|
+
assert.ok(c.includes('mindforge-ci.yml') || c.includes('on:'), 'Should define GitHub Actions workflow');
|
|
2787
|
+
assert.ok(c.includes('mindforge-health'), 'Should include health check job');
|
|
2788
|
+
assert.ok(c.includes('mindforge-security'), 'Should include security scan job');
|
|
2789
|
+
});
|
|
2790
|
+
|
|
2791
|
+
test('ci-mode.md defines Tier 3 block policy', () => {
|
|
2792
|
+
const c = read('.mindforge/ci/ci-mode.md');
|
|
2793
|
+
assert.ok(
|
|
2794
|
+
(c.includes('Tier 3') && c.includes('block')) || c.includes('ALWAYS fails'),
|
|
2795
|
+
'CI mode should block Tier 3 changes'
|
|
2796
|
+
);
|
|
2797
|
+
});
|
|
2798
|
+
|
|
2799
|
+
test('ci-mode.md has timeout configuration', () => {
|
|
2800
|
+
const c = read('.mindforge/ci/ci-mode.md');
|
|
2801
|
+
assert.ok(c.includes('timeout') || c.includes('TIMEOUT'), 'CI mode should have timeout config');
|
|
2802
|
+
});
|
|
2803
|
+
|
|
2804
|
+
console.log('\nMonorepo support:');
|
|
2805
|
+
['workspace-detector.md','cross-package-planner.md','dependency-graph-builder.md'].forEach(f => {
|
|
2806
|
+
test(`${f} exists`, () => {
|
|
2807
|
+
assert.ok(fs.existsSync(`.mindforge/monorepo/${f}`), `Missing: .mindforge/monorepo/${f}`);
|
|
2808
|
+
});
|
|
2809
|
+
});
|
|
2810
|
+
|
|
2811
|
+
test('workspace-detector supports major monorepo types', () => {
|
|
2812
|
+
const c = read('.mindforge/monorepo/workspace-detector.md');
|
|
2813
|
+
['nx', 'turborepo', 'lerna', 'pnpm'].forEach(type => {
|
|
2814
|
+
assert.ok(c.includes(type), `Should support ${type} monorepo type`);
|
|
2815
|
+
});
|
|
2816
|
+
});
|
|
2817
|
+
|
|
2818
|
+
test('cross-package-planner has topological sort', () => {
|
|
2819
|
+
const c = read('.mindforge/monorepo/cross-package-planner.md');
|
|
2820
|
+
assert.ok(
|
|
2821
|
+
c.includes('topological') || c.includes('dependency order') || c.includes('Execution order'),
|
|
2822
|
+
'Should use topological sort for package order'
|
|
2823
|
+
);
|
|
2824
|
+
});
|
|
2825
|
+
|
|
2826
|
+
console.log(`\n${'─'.repeat(50)}`);
|
|
2827
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
2828
|
+
if (failed > 0) { console.error(`\n❌ ${failed} test(s) failed.\n`); process.exit(1); }
|
|
2829
|
+
else { console.log(`\n✅ All CI mode tests passed.\n`); }
|
|
2830
|
+
```
|
|
2831
|
+
|
|
2832
|
+
### `tests/sdk.test.js`
|
|
2833
|
+
|
|
2834
|
+
```javascript
|
|
2835
|
+
/**
|
|
2836
|
+
* MindForge Day 6 — SDK Tests
|
|
2837
|
+
* Run: node tests/sdk.test.js
|
|
2838
|
+
*/
|
|
2839
|
+
'use strict';
|
|
2840
|
+
const fs = require('fs'), path = require('path'), assert = require('assert');
|
|
2841
|
+
let passed = 0, failed = 0;
|
|
2842
|
+
function test(name, fn) {
|
|
2843
|
+
try { fn(); console.log(` ✅ ${name}`); passed++; }
|
|
2844
|
+
catch(e) { console.error(` ❌ ${name}\n ${e.message}`); failed++; }
|
|
2845
|
+
}
|
|
2846
|
+
const read = p => fs.existsSync(p) ? fs.readFileSync(p, 'utf8') : '';
|
|
2847
|
+
|
|
2848
|
+
// ── Lightweight SDK client simulation (without TypeScript compilation) ─────────
|
|
2849
|
+
class MockMindForgeClient {
|
|
2850
|
+
constructor(config = {}) {
|
|
2851
|
+
this.projectRoot = config.projectRoot || process.cwd();
|
|
2852
|
+
}
|
|
2853
|
+
isInitialised() {
|
|
2854
|
+
return fs.existsSync(path.join(this.projectRoot, '.planning', 'PROJECT.md'));
|
|
2855
|
+
}
|
|
2856
|
+
readHandoff() {
|
|
2857
|
+
const p = path.join(this.projectRoot, '.planning', 'HANDOFF.json');
|
|
2858
|
+
if (!fs.existsSync(p)) return null;
|
|
2859
|
+
try { return JSON.parse(fs.readFileSync(p, 'utf8')); }
|
|
2860
|
+
catch { return null; }
|
|
2861
|
+
}
|
|
2862
|
+
readAuditLog(filter = {}) {
|
|
2863
|
+
const p = path.join(this.projectRoot, '.planning', 'AUDIT.jsonl');
|
|
2864
|
+
if (!fs.existsSync(p)) return [];
|
|
2865
|
+
return fs.readFileSync(p, 'utf8')
|
|
2866
|
+
.split('\n').filter(Boolean)
|
|
2867
|
+
.map(l => { try { return JSON.parse(l); } catch { return null; } })
|
|
2868
|
+
.filter(Boolean)
|
|
2869
|
+
.filter(e => !filter.event || e.event === filter.event);
|
|
2870
|
+
}
|
|
2871
|
+
async health() {
|
|
2872
|
+
const warnings = [], errors = [], info = [];
|
|
2873
|
+
const handoff = this.readHandoff();
|
|
2874
|
+
if (handoff && !handoff.schema_version) {
|
|
2875
|
+
errors.push({ category: 'state', message: 'HANDOFF.json missing schema_version' });
|
|
2876
|
+
}
|
|
2877
|
+
return {
|
|
2878
|
+
overallStatus: errors.length > 0 ? 'error' : warnings.length > 0 ? 'warning' : 'healthy',
|
|
2879
|
+
errors, warnings, informational: info,
|
|
2880
|
+
timestamp: new Date().toISOString(),
|
|
2881
|
+
};
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
// ── Command builder simulation ────────────────────────────────────────────────
|
|
2886
|
+
const commands = {
|
|
2887
|
+
health: (opts = {}) => `/mindforge:health ${(opts.flags||[]).join(' ')}`.trim(),
|
|
2888
|
+
planPhase: (n, opts = {}) => `/mindforge:plan-phase ${n} ${(opts.flags||[]).join(' ')}`.trim(),
|
|
2889
|
+
executePhase: (n, opts = {}) => `/mindforge:execute-phase ${n} ${(opts.flags||[]).join(' ')}`.trim(),
|
|
2890
|
+
audit: (f = {}) => {
|
|
2891
|
+
const parts = ['/mindforge:audit'];
|
|
2892
|
+
if (f.phase) parts.push(`--phase ${f.phase}`);
|
|
2893
|
+
if (f.event) parts.push(`--event ${f.event}`);
|
|
2894
|
+
return parts.join(' ');
|
|
2895
|
+
},
|
|
2896
|
+
};
|
|
2897
|
+
|
|
2898
|
+
console.log('\nMindForge Day 6 — SDK Tests\n');
|
|
2899
|
+
|
|
2900
|
+
console.log('SDK source files:');
|
|
2901
|
+
['index.ts','client.ts','types.ts','events.ts','commands.ts'].forEach(f => {
|
|
2902
|
+
test(`sdk/src/${f} exists`, () => {
|
|
2903
|
+
assert.ok(fs.existsSync(`sdk/src/${f}`), `Missing: sdk/src/${f}`);
|
|
2904
|
+
});
|
|
2905
|
+
});
|
|
2906
|
+
|
|
2907
|
+
console.log('\nSDK type exports:');
|
|
2908
|
+
test('index.ts exports VERSION', () => {
|
|
2909
|
+
const c = read('sdk/src/index.ts');
|
|
2910
|
+
assert.ok(c.includes("VERSION"), 'Should export VERSION');
|
|
2911
|
+
});
|
|
2912
|
+
|
|
2913
|
+
test('types.ts defines MindForgeConfig', () => {
|
|
2914
|
+
const c = read('sdk/src/types.ts');
|
|
2915
|
+
assert.ok(c.includes('MindForgeConfig'), 'Should define MindForgeConfig');
|
|
2916
|
+
});
|
|
2917
|
+
|
|
2918
|
+
test('types.ts defines all result types', () => {
|
|
2919
|
+
const c = read('sdk/src/types.ts');
|
|
2920
|
+
['PhaseResult','TaskResult','SecurityFinding','GateResult','HealthReport'].forEach(t => {
|
|
2921
|
+
assert.ok(c.includes(t), `Should define ${t}`);
|
|
2922
|
+
});
|
|
2923
|
+
});
|
|
2924
|
+
|
|
2925
|
+
test('events.ts defines MindForgeEventStream', () => {
|
|
2926
|
+
const c = read('sdk/src/events.ts');
|
|
2927
|
+
assert.ok(c.includes('MindForgeEventStream'), 'Should define MindForgeEventStream');
|
|
2928
|
+
assert.ok(c.includes('watchAuditLog'), 'Should have watchAuditLog method');
|
|
2929
|
+
});
|
|
2930
|
+
|
|
2931
|
+
console.log('\nSDK client behaviour:');
|
|
2932
|
+
|
|
2933
|
+
test('client.isInitialised() returns false when PROJECT.md missing', () => {
|
|
2934
|
+
const client = new MockMindForgeClient({ projectRoot: '/tmp/nonexistent-project' });
|
|
2935
|
+
assert.strictEqual(client.isInitialised(), false);
|
|
2936
|
+
});
|
|
2937
|
+
|
|
2938
|
+
test('client.isInitialised() returns true when PROJECT.md exists', () => {
|
|
2939
|
+
const client = new MockMindForgeClient({ projectRoot: process.cwd() });
|
|
2940
|
+
// May be true or false depending on whether we're in a MindForge project
|
|
2941
|
+
assert.ok(typeof client.isInitialised() === 'boolean');
|
|
2942
|
+
});
|
|
2943
|
+
|
|
2944
|
+
test('client.readHandoff() returns null when HANDOFF.json missing', () => {
|
|
2945
|
+
const client = new MockMindForgeClient({ projectRoot: '/tmp/nonexistent-project' });
|
|
2946
|
+
assert.strictEqual(client.readHandoff(), null);
|
|
2947
|
+
});
|
|
2948
|
+
|
|
2949
|
+
test('client.readHandoff() parses valid HANDOFF.json', () => {
|
|
2950
|
+
const client = new MockMindForgeClient({ projectRoot: process.cwd() });
|
|
2951
|
+
const handoff = client.readHandoff();
|
|
2952
|
+
if (handoff) {
|
|
2953
|
+
assert.ok(typeof handoff === 'object', 'HANDOFF.json should parse to object');
|
|
2954
|
+
assert.ok(handoff.schema_version, 'HANDOFF.json should have schema_version');
|
|
2955
|
+
}
|
|
2956
|
+
// null is acceptable if not in a MindForge project
|
|
2957
|
+
});
|
|
2958
|
+
|
|
2959
|
+
test('client.readAuditLog() returns array', () => {
|
|
2960
|
+
const client = new MockMindForgeClient({ projectRoot: process.cwd() });
|
|
2961
|
+
const log = client.readAuditLog();
|
|
2962
|
+
assert.ok(Array.isArray(log), 'readAuditLog should return array');
|
|
2963
|
+
});
|
|
2964
|
+
|
|
2965
|
+
test('client.readAuditLog() filters by event type', () => {
|
|
2966
|
+
const client = new MockMindForgeClient({ projectRoot: process.cwd() });
|
|
2967
|
+
const secFindings = client.readAuditLog({ event: 'security_finding' });
|
|
2968
|
+
assert.ok(Array.isArray(secFindings));
|
|
2969
|
+
secFindings.forEach(e => {
|
|
2970
|
+
assert.strictEqual(e.event, 'security_finding', 'All entries should match filter');
|
|
2971
|
+
});
|
|
2972
|
+
});
|
|
2973
|
+
|
|
2974
|
+
test('client.health() returns HealthReport shape', async () => {
|
|
2975
|
+
const client = new MockMindForgeClient({ projectRoot: process.cwd() });
|
|
2976
|
+
const report = await client.health();
|
|
2977
|
+
assert.ok(['healthy','warning','error'].includes(report.overallStatus));
|
|
2978
|
+
assert.ok(Array.isArray(report.errors));
|
|
2979
|
+
assert.ok(Array.isArray(report.warnings));
|
|
2980
|
+
assert.ok(report.timestamp);
|
|
2981
|
+
});
|
|
2982
|
+
|
|
2983
|
+
console.log('\nCommand builders:');
|
|
2984
|
+
test('commands.health() builds correct string', () => {
|
|
2985
|
+
assert.strictEqual(commands.health(), '/mindforge:health');
|
|
2986
|
+
assert.strictEqual(commands.health({ flags: ['--repair'] }), '/mindforge:health --repair');
|
|
2987
|
+
});
|
|
2988
|
+
|
|
2989
|
+
test('commands.planPhase() builds correct string', () => {
|
|
2990
|
+
assert.strictEqual(commands.planPhase(3), '/mindforge:plan-phase 3');
|
|
2991
|
+
});
|
|
2992
|
+
|
|
2993
|
+
test('commands.audit() builds filter string', () => {
|
|
2994
|
+
const cmd = commands.audit({ phase: 3, event: 'security_finding' });
|
|
2995
|
+
assert.ok(cmd.includes('--phase 3'));
|
|
2996
|
+
assert.ok(cmd.includes('--event security_finding'));
|
|
2997
|
+
});
|
|
2998
|
+
|
|
2999
|
+
test('commands.executePhase() includes phase number', () => {
|
|
3000
|
+
const cmd = commands.executePhase(2);
|
|
3001
|
+
assert.ok(cmd.includes('2'));
|
|
3002
|
+
assert.ok(cmd.startsWith('/mindforge:execute-phase'));
|
|
3003
|
+
});
|
|
3004
|
+
|
|
3005
|
+
console.log(`\n${'─'.repeat(50)}`);
|
|
3006
|
+
console.log(`Results: ${passed} passed, ${failed} failed`);
|
|
3007
|
+
if (failed > 0) { console.error(`\n❌ ${failed} test(s) failed.\n`); process.exit(1); }
|
|
3008
|
+
else { console.log(`\n✅ All SDK tests passed.\n`); }
|
|
3009
|
+
```
|
|
3010
|
+
|
|
3011
|
+
**Commit:**
|
|
3012
|
+
```bash
|
|
3013
|
+
git add tests/
|
|
3014
|
+
git commit -m "test(day6): add distribution, CI mode, and SDK test suites"
|
|
3015
|
+
```
|
|
3016
|
+
|
|
3017
|
+
---
|
|
3018
|
+
|
|
3019
|
+
## TASK 12 — Run full test battery, bump version, and push
|
|
3020
|
+
|
|
3021
|
+
```bash
|
|
3022
|
+
# Run all 10 test suites
|
|
3023
|
+
for suite in install wave-engine audit compaction skills-platform \
|
|
3024
|
+
integrations governance intelligence metrics \
|
|
3025
|
+
distribution ci-mode sdk; do
|
|
3026
|
+
node tests/${suite}.test.js | tail -1
|
|
3027
|
+
done
|
|
3028
|
+
|
|
3029
|
+
# Bump to v0.6.0
|
|
3030
|
+
node -e "
|
|
3031
|
+
const fs = require('fs');
|
|
3032
|
+
const p = JSON.parse(fs.readFileSync('package.json','utf8'));
|
|
3033
|
+
p.version = '0.6.0';
|
|
3034
|
+
fs.writeFileSync('package.json', JSON.stringify(p, null, 2) + '\n');
|
|
3035
|
+
console.log('Bumped to v0.6.0');
|
|
3036
|
+
"
|
|
3037
|
+
|
|
3038
|
+
git add package.json
|
|
3039
|
+
git commit -m "chore(release): bump to v0.6.0 — Day 6 distribution platform"
|
|
3040
|
+
git push origin feat/mindforge-distribution-platform
|
|
3041
|
+
```
|
|
3042
|
+
|
|
3043
|
+
---
|
|
3044
|
+
|
|
3045
|
+
# ═══════════════════════════════════════════════════════════════
|
|
3046
|
+
# PART 2: REVIEW PROMPT
|
|
3047
|
+
# ═══════════════════════════════════════════════════════════════
|
|
3048
|
+
|
|
3049
|
+
---
|
|
3050
|
+
|
|
3051
|
+
## DAY 6 REVIEW — Run after DAY6-IMPLEMENT is complete
|
|
3052
|
+
|
|
3053
|
+
Activate **`architect.md` + `security-reviewer.md`** simultaneously.
|
|
3054
|
+
|
|
3055
|
+
Day 6 risk profile:
|
|
3056
|
+
- **Supply chain security** — skills from external registry could contain malicious content
|
|
3057
|
+
- **CI/CD gate bypass** — CI mode must not weaken governance for speed
|
|
3058
|
+
- **API cost exposure** — AI PR reviews can be expensive at scale
|
|
3059
|
+
- **SDK data exposure** — SDK reads sensitive files (HANDOFF, AUDIT) programmatically
|
|
3060
|
+
|
|
3061
|
+
---
|
|
3062
|
+
|
|
3063
|
+
## REVIEW PASS 1 — Skills Registry: Supply Chain Security
|
|
3064
|
+
|
|
3065
|
+
Read `registry-client.md` and `skill-validator.md`.
|
|
3066
|
+
|
|
3067
|
+
- [ ] **Step 5 (injection guard)** runs AFTER download but BEFORE install.
|
|
3068
|
+
What if the skill downloads successfully but the temp directory is world-readable
|
|
3069
|
+
and another process reads it during the window between download and guard check?
|
|
3070
|
+
This is a TOCTOU (time-of-check, time-of-use) race condition.
|
|
3071
|
+
Mitigation: use `mktemp -d` with mode 700 (user-only access).
|
|
3072
|
+
Add: "`TEMP_DIR=$(mktemp -d)` then immediately `chmod 700 ${TEMP_DIR}`"
|
|
3073
|
+
|
|
3074
|
+
- [ ] The skill validator checks for "no placeholder text" — but what is the detection pattern?
|
|
3075
|
+
Add explicit patterns: `\[placeholder\]`, `\[your-name\]`, `TODO`, `FIXME`, `\[fill this in\]`
|
|
3076
|
+
|
|
3077
|
+
- [ ] Level 3 validation is listed as "optional — runs for publication."
|
|
3078
|
+
But the client protocol doesn't explicitly mention running Level 3 at install time.
|
|
3079
|
+
Should it? For a private registry (known, trusted source): Level 2 is sufficient.
|
|
3080
|
+
For the public registry (unknown authors): Level 3 should also run at install.
|
|
3081
|
+
Add: "For public registry installs: run Level 3 validation. Warn on failures but don't block."
|
|
3082
|
+
|
|
3083
|
+
- [ ] The `npm audit` step is not in the installation flow.
|
|
3084
|
+
A skill package could have vulnerable JavaScript dependencies in `scripts/`.
|
|
3085
|
+
Add: "Step 4.5: if the skill package has a `package.json` in `scripts/`:
|
|
3086
|
+
run `npm audit --prefix ${TEMP_DIR} --audit-level=high`.
|
|
3087
|
+
If HIGH or CRITICAL vulnerabilities: warn user but allow install.
|
|
3088
|
+
Document in AUDIT: `"skill_dependency_vulnerability": true`"
|
|
3089
|
+
|
|
3090
|
+
---
|
|
3091
|
+
|
|
3092
|
+
## REVIEW PASS 2 — CI Mode: Governance Integrity
|
|
3093
|
+
|
|
3094
|
+
Read `ci-mode.md` and `github-actions-adapter.md`.
|
|
3095
|
+
|
|
3096
|
+
- [ ] **The Tier 3 block in CI** is correct. But what message does the CI output?
|
|
3097
|
+
If the CI build fails without a clear message about WHY Tier 3 changes block CI,
|
|
3098
|
+
engineers will be confused. Add explicit CI failure message:
|
|
3099
|
+
```
|
|
3100
|
+
::error::MindForge CI: Tier 3 change detected (auth/payment/PII modification).
|
|
3101
|
+
Tier 3 changes require human compliance review before CI can pass.
|
|
3102
|
+
Get approval using: /mindforge:approve [approval-id]
|
|
3103
|
+
Then push again — a Tier 3 change with an approved approval request will pass CI.
|
|
3104
|
+
```
|
|
3105
|
+
|
|
3106
|
+
- [ ] **CI timeout handling** says "exit with code 2 (warning)." But GitHub Actions
|
|
3107
|
+
treats any non-zero exit code as failure (code 1 or 2 — both fail the job).
|
|
3108
|
+
The "warning" intent doesn't work with standard CI conventions.
|
|
3109
|
+
Fix: use exit code 0 for timeout with a clear warning message AND a "timeout" artifact.
|
|
3110
|
+
The next CI run reads the HANDOFF.json to resume.
|
|
3111
|
+
But: mark the job as "warning" using GitHub's `$GITHUB_STEP_SUMMARY` approach,
|
|
3112
|
+
not via exit codes.
|
|
3113
|
+
|
|
3114
|
+
- [ ] **ANTHROPIC_API_KEY in CI** — the workflow file has `ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}`.
|
|
3115
|
+
What happens when this secret is not configured?
|
|
3116
|
+
The AI review step should gracefully skip (not fail) if the key is absent.
|
|
3117
|
+
Verify the `pr-review` command already handles this — re-check in context of CI.
|
|
3118
|
+
|
|
3119
|
+
---
|
|
3120
|
+
|
|
3121
|
+
## REVIEW PASS 3 — AI PR Review: Cost and Safety
|
|
3122
|
+
|
|
3123
|
+
Read `ai-reviewer.md` and `review-prompt-templates.md`.
|
|
3124
|
+
|
|
3125
|
+
- [ ] **Daily limit check** (`checkDailyLimit`) reads from `.mindforge/metrics/ai-review-usage.jsonl`.
|
|
3126
|
+
But this file may not exist. Add graceful creation if missing.
|
|
3127
|
+
Also: the count logic needs to handle JSONL parse errors on individual lines.
|
|
3128
|
+
|
|
3129
|
+
- [ ] **Diff truncation at 12,000 characters** — large PRs get truncated.
|
|
3130
|
+
But the truncation might cut in the middle of a security-relevant code block.
|
|
3131
|
+
Better truncation strategy: truncate by number of FILES, not characters.
|
|
3132
|
+
Review the `REVIEW_LIMITS.maxFilesReviewed = 20` limit — this is better.
|
|
3133
|
+
But the implementation shows BOTH a character limit AND file limit.
|
|
3134
|
+
These should be coordinated: if diff is > 12K chars, reduce to the top 10 most-changed files.
|
|
3135
|
+
|
|
3136
|
+
- [ ] **The system prompt includes** `context.conventions` truncated to 2000 chars.
|
|
3137
|
+
For a large CONVENTIONS.md (which can be 5K+ chars), this loses important conventions.
|
|
3138
|
+
Better: extract only the "Forbidden patterns" and "Naming conventions" sections
|
|
3139
|
+
from CONVENTIONS.md — these are the most review-relevant.
|
|
3140
|
+
|
|
3141
|
+
- [ ] **Cost management** — the daily limit (50 reviews/day) is configurable via MINDFORGE.md?
|
|
3142
|
+
It's not currently listed in the schema. Add: `AI_REVIEW_DAILY_LIMIT` setting.
|
|
3143
|
+
|
|
3144
|
+
---
|
|
3145
|
+
|
|
3146
|
+
## REVIEW PASS 4 — SDK: Data Safety
|
|
3147
|
+
|
|
3148
|
+
Read all SDK source files.
|
|
3149
|
+
|
|
3150
|
+
- [ ] **`readHandoff()`** reads `.planning/HANDOFF.json` which contains sensitive project state.
|
|
3151
|
+
The SDK returns the raw parsed JSON to calling code.
|
|
3152
|
+
If the SDK is embedded in a web application or external tool, this data should be
|
|
3153
|
+
treated as sensitive. Add to SDK README: "HANDOFF.json may contain sensitive project
|
|
3154
|
+
state. Do not expose it to untrusted clients or log its contents."
|
|
3155
|
+
|
|
3156
|
+
- [ ] **`MindForgeEventStream`** starts an HTTP server on port 7337 with
|
|
3157
|
+
`Access-Control-Allow-Origin: http://localhost:*`.
|
|
3158
|
+
The wildcard `http://localhost:*` is not a valid CORS origin — browsers require
|
|
3159
|
+
exact origins. Fix: `Access-Control-Allow-Origin: *` (for localhost dev tools)
|
|
3160
|
+
OR implement origin allowlisting.
|
|
3161
|
+
Also: the server binds to all interfaces by default (`listen(port)`).
|
|
3162
|
+
In a shared development environment, this exposes MindForge state to other users.
|
|
3163
|
+
Fix: bind to localhost explicitly: `server.listen(port, '127.0.0.1', ...)`.
|
|
3164
|
+
|
|
3165
|
+
- [ ] **`watchAuditLog()`** uses `fs.watch()` which is known to have platform-specific
|
|
3166
|
+
behaviour (especially on Linux where inotify has limits).
|
|
3167
|
+
Add: "On Linux: if `fs.watch()` throws ENOSPC (inotify limit reached):
|
|
3168
|
+
fall back to polling with `fs.watchFile()` at 2-second intervals."
|
|
3169
|
+
|
|
3170
|
+
- [ ] **The SDK has no authentication** — any code that can access the local filesystem
|
|
3171
|
+
can read AUDIT.jsonl, HANDOFF.json, and session metrics.
|
|
3172
|
+
This is acceptable for a local development tool but should be documented:
|
|
3173
|
+
"The MindForge SDK operates on local files. It provides no network authentication.
|
|
3174
|
+
Do not expose SDK endpoints to the public internet."
|
|
3175
|
+
|
|
3176
|
+
---
|
|
3177
|
+
|
|
3178
|
+
## REVIEW PASS 5 — Monorepo: Cross-Package Correctness
|
|
3179
|
+
|
|
3180
|
+
Read `workspace-detector.md` and `cross-package-planner.md`.
|
|
3181
|
+
|
|
3182
|
+
- [ ] The workspace detector uses `npm info "${PACKAGE_NAME}" version` for update checks.
|
|
3183
|
+
But `npm info` makes a network request — in offline environments, this hangs.
|
|
3184
|
+
Add: `npm info "${PACKAGE_NAME}" version --prefer-offline` for the version check.
|
|
3185
|
+
|
|
3186
|
+
- [ ] **Affected package detection** uses `git diff --name-only | awk -F/ '{print $1"/"$2}'`
|
|
3187
|
+
This assumes packages are always at `apps/name` or `packages/name` (2 levels deep).
|
|
3188
|
+
For Nx with deeply nested libs (`libs/shared/utils/`), this produces wrong paths.
|
|
3189
|
+
Fix: compare against the package paths in WORKSPACE-MANIFEST.json rather than
|
|
3190
|
+
assuming a fixed depth. Match changed file paths against declared package paths.
|
|
3191
|
+
|
|
3192
|
+
---
|
|
3193
|
+
|
|
3194
|
+
## REVIEW PASS 6 — Test Suite Quality
|
|
3195
|
+
|
|
3196
|
+
- [ ] `tests/distribution.test.js` — the injection guard test checks that
|
|
3197
|
+
registry-client.md MENTIONS injection guard. But it doesn't test that a skill
|
|
3198
|
+
with injection patterns actually FAILS validation. Add a test that simulates
|
|
3199
|
+
a malicious SKILL.md and verifies it would be rejected.
|
|
3200
|
+
|
|
3201
|
+
- [ ] `tests/ci-mode.test.js` — the Tier 3 CI block test is correct.
|
|
3202
|
+
But there is no test for the CI timeout behaviour (exit code strategy).
|
|
3203
|
+
Add a test verifying that the ci-mode spec mentions the exit-code-0 approach
|
|
3204
|
+
(not exit-code-2) for timeouts.
|
|
3205
|
+
|
|
3206
|
+
- [ ] `tests/sdk.test.js` — the SSE server localhost binding test is missing.
|
|
3207
|
+
Add: verify that `events.ts` references `'127.0.0.1'` (localhost binding)
|
|
3208
|
+
not just `server.listen(port)` (all interfaces).
|
|
3209
|
+
|
|
3210
|
+
---
|
|
3211
|
+
|
|
3212
|
+
## REVIEW SUMMARY TABLE
|
|
3213
|
+
|
|
3214
|
+
```
|
|
3215
|
+
## Day 6 Review Summary
|
|
3216
|
+
|
|
3217
|
+
| Category | BLOCKING | MAJOR | MINOR | SUGGESTION |
|
|
3218
|
+
|-----------------|----------|-------|-------|------------|
|
|
3219
|
+
| Registry | | | | |
|
|
3220
|
+
| CI Mode | | | | |
|
|
3221
|
+
| AI PR Review | | | | |
|
|
3222
|
+
| SDK | | | | |
|
|
3223
|
+
| Monorepo | | | | |
|
|
3224
|
+
| Tests | | | | |
|
|
3225
|
+
| **TOTAL** | | | | |
|
|
3226
|
+
|
|
3227
|
+
## Verdict
|
|
3228
|
+
[ ] ✅ APPROVED — Proceed to HARDEN section
|
|
3229
|
+
[ ] ⚠️ APPROVED WITH CONDITIONS
|
|
3230
|
+
[ ] ❌ NOT APPROVED
|
|
3231
|
+
```
|
|
3232
|
+
|
|
3233
|
+
---
|
|
3234
|
+
|
|
3235
|
+
# ═══════════════════════════════════════════════════════════════
|
|
3236
|
+
# PART 3: HARDENING PROMPT
|
|
3237
|
+
# ═══════════════════════════════════════════════════════════════
|
|
3238
|
+
|
|
3239
|
+
---
|
|
3240
|
+
|
|
3241
|
+
## DAY 6 HARDENING — Run after REVIEW is APPROVED
|
|
3242
|
+
|
|
3243
|
+
Confirm all review findings resolved:
|
|
3244
|
+
```bash
|
|
3245
|
+
node tests/distribution.test.js && node tests/ci-mode.test.js && node tests/sdk.test.js
|
|
3246
|
+
```
|
|
3247
|
+
|
|
3248
|
+
---
|
|
3249
|
+
|
|
3250
|
+
## HARDEN 1 — Fix registry TOCTOU race condition
|
|
3251
|
+
|
|
3252
|
+
Update `registry-client.md` Step 3:
|
|
3253
|
+
|
|
3254
|
+
```markdown
|
|
3255
|
+
### Step 3 — Secure temp directory creation
|
|
3256
|
+
```bash
|
|
3257
|
+
# Create temp directory with user-only permissions (prevents TOCTOU attacks)
|
|
3258
|
+
TEMP_DIR=$(mktemp -d)
|
|
3259
|
+
chmod 700 "${TEMP_DIR}"
|
|
3260
|
+
|
|
3261
|
+
# All subsequent operations in this directory are protected
|
|
3262
|
+
npm pack "${PACKAGE_NAME}@latest" --pack-destination "${TEMP_DIR}" --quiet
|
|
3263
|
+
|
|
3264
|
+
# Verify the tarball was downloaded (not empty, not corrupted)
|
|
3265
|
+
TARBALL=$(ls "${TEMP_DIR}"/*.tgz 2>/dev/null | head -1)
|
|
3266
|
+
if [ -z "${TARBALL}" ]; then
|
|
3267
|
+
rm -rf "${TEMP_DIR}"
|
|
3268
|
+
echo "Error: Failed to download ${PACKAGE_NAME} — no tarball produced"
|
|
3269
|
+
exit 1
|
|
3270
|
+
fi
|
|
3271
|
+
|
|
3272
|
+
# Verify tarball size is reasonable (not 0 bytes, not suspiciously large)
|
|
3273
|
+
TARBALL_SIZE=$(wc -c < "${TARBALL}")
|
|
3274
|
+
if [ "${TARBALL_SIZE}" -lt 100 ]; then
|
|
3275
|
+
rm -rf "${TEMP_DIR}"
|
|
3276
|
+
echo "Error: Downloaded tarball is suspiciously small (${TARBALL_SIZE} bytes)"
|
|
3277
|
+
exit 1
|
|
3278
|
+
fi
|
|
3279
|
+
|
|
3280
|
+
tar -xzf "${TARBALL}" --strip-components=1 -C "${TEMP_DIR}"
|
|
3281
|
+
```
|
|
3282
|
+
```
|
|
3283
|
+
|
|
3284
|
+
**Commit:**
|
|
3285
|
+
```bash
|
|
3286
|
+
git add .mindforge/distribution/registry-client.md
|
|
3287
|
+
git commit -m "harden(registry): fix TOCTOU race with chmod 700 temp dir and tarball size check"
|
|
3288
|
+
```
|
|
3289
|
+
|
|
3290
|
+
---
|
|
3291
|
+
|
|
3292
|
+
## HARDEN 2 — Fix SSE server security issues
|
|
3293
|
+
|
|
3294
|
+
Update `sdk/src/events.ts`:
|
|
3295
|
+
|
|
3296
|
+
```typescript
|
|
3297
|
+
start(port = 7337): Promise<void> {
|
|
3298
|
+
return new Promise((resolve, reject) => {
|
|
3299
|
+
this.server = http.createServer((req, res) => {
|
|
3300
|
+
if (req.url !== '/events') {
|
|
3301
|
+
res.writeHead(404);
|
|
3302
|
+
res.end();
|
|
3303
|
+
return;
|
|
3304
|
+
}
|
|
3305
|
+
|
|
3306
|
+
// SECURITY: Only allow connections from localhost
|
|
3307
|
+
const clientIp = req.socket.remoteAddress;
|
|
3308
|
+
const isLocalhost = clientIp === '127.0.0.1' ||
|
|
3309
|
+
clientIp === '::1' ||
|
|
3310
|
+
clientIp === '::ffff:127.0.0.1';
|
|
3311
|
+
if (!isLocalhost) {
|
|
3312
|
+
res.writeHead(403);
|
|
3313
|
+
res.end('Forbidden: MindForge event stream is localhost-only');
|
|
3314
|
+
return;
|
|
3315
|
+
}
|
|
3316
|
+
|
|
3317
|
+
// CORS: Only allow localhost origins (exact match, not wildcard)
|
|
3318
|
+
const origin = req.headers.origin;
|
|
3319
|
+
const allowedOriginPattern = /^https?:\/\/localhost(:\d+)?$/;
|
|
3320
|
+
const corsOrigin = origin && allowedOriginPattern.test(origin)
|
|
3321
|
+
? origin
|
|
3322
|
+
: 'http://localhost'; // Default safe origin
|
|
3323
|
+
|
|
3324
|
+
res.writeHead(200, {
|
|
3325
|
+
'Content-Type': 'text/event-stream',
|
|
3326
|
+
'Cache-Control': 'no-cache',
|
|
3327
|
+
'Connection': 'keep-alive',
|
|
3328
|
+
'Access-Control-Allow-Origin': corsOrigin,
|
|
3329
|
+
'X-Content-Type-Options': 'nosniff',
|
|
3330
|
+
});
|
|
3331
|
+
|
|
3332
|
+
// ... rest of handler
|
|
3333
|
+
});
|
|
3334
|
+
|
|
3335
|
+
// SECURITY: Bind to localhost ONLY — not all interfaces
|
|
3336
|
+
this.server.listen(port, '127.0.0.1', () => {
|
|
3337
|
+
console.log(`MindForge event stream: http://localhost:${port}/events (localhost only)`);
|
|
3338
|
+
resolve();
|
|
3339
|
+
});
|
|
3340
|
+
|
|
3341
|
+
this.server.on('error', (err: NodeJS.ErrnoException) => {
|
|
3342
|
+
if (err.code === 'EADDRINUSE') {
|
|
3343
|
+
reject(new Error(
|
|
3344
|
+
`Port ${port} is already in use. Use: new MindForgeEventStream().start(${port + 1})`
|
|
3345
|
+
));
|
|
3346
|
+
} else {
|
|
3347
|
+
reject(err);
|
|
3348
|
+
}
|
|
3349
|
+
});
|
|
3350
|
+
});
|
|
3351
|
+
}
|
|
3352
|
+
```
|
|
3353
|
+
|
|
3354
|
+
Also add Linux inotify fallback to `watchAuditLog`:
|
|
3355
|
+
|
|
3356
|
+
```typescript
|
|
3357
|
+
watchAuditLog(projectRoot: string): void {
|
|
3358
|
+
const auditPath = path.join(projectRoot, '.planning', 'AUDIT.jsonl');
|
|
3359
|
+
|
|
3360
|
+
// ... existing setup ...
|
|
3361
|
+
|
|
3362
|
+
try {
|
|
3363
|
+
this.auditWatcher = fs.watch(auditPath, () => { /* ... */ });
|
|
3364
|
+
} catch (err: unknown) {
|
|
3365
|
+
if ((err as NodeJS.ErrnoException).code === 'ENOSPC') {
|
|
3366
|
+
// Linux inotify limit reached — fall back to polling
|
|
3367
|
+
console.warn('MindForge: inotify limit reached, falling back to 2s polling');
|
|
3368
|
+
setInterval(() => {
|
|
3369
|
+
const lines = fs.readFileSync(auditPath, 'utf8').split('\n').filter(Boolean);
|
|
3370
|
+
for (let i = this.lastAuditLine; i < lines.length; i++) {
|
|
3371
|
+
try { this.broadcast('audit_entry', JSON.parse(lines[i])); } catch { /* ignore */ }
|
|
3372
|
+
}
|
|
3373
|
+
this.lastAuditLine = lines.length;
|
|
3374
|
+
}, 2000);
|
|
3375
|
+
} else {
|
|
3376
|
+
throw err;
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
```
|
|
3381
|
+
|
|
3382
|
+
**Commit:**
|
|
3383
|
+
```bash
|
|
3384
|
+
git add sdk/src/events.ts
|
|
3385
|
+
git commit -m "harden(sdk): fix SSE server localhost-only binding, exact CORS origins, inotify fallback"
|
|
3386
|
+
```
|
|
3387
|
+
|
|
3388
|
+
---
|
|
3389
|
+
|
|
3390
|
+
## HARDEN 3 — Fix CI timeout exit code
|
|
3391
|
+
|
|
3392
|
+
Update `ci-mode.md` — replace the timeout section:
|
|
3393
|
+
|
|
3394
|
+
```markdown
|
|
3395
|
+
### CI timeout and exit codes
|
|
3396
|
+
|
|
3397
|
+
**Exit code policy:**
|
|
3398
|
+
| Situation | Exit code | Meaning |
|
|
3399
|
+
|---|---|---|
|
|
3400
|
+
| All phases complete, gates passed | 0 | Success |
|
|
3401
|
+
| Quality gate failed | 1 | Hard failure — fix required |
|
|
3402
|
+
| Tier 2/3 governance block | 1 | Hard failure — approval required |
|
|
3403
|
+
| CI timeout reached | 0 | Soft stop — work saved, resume next run |
|
|
3404
|
+
| No MindForge project found | 1 | Configuration error |
|
|
3405
|
+
|
|
3406
|
+
**Important:** GitHub Actions (and most CI systems) treat ANY non-zero exit code
|
|
3407
|
+
as failure. Exit code 2 does NOT mean "warning" in CI — it means failure.
|
|
3408
|
+
|
|
3409
|
+
**Timeout handling (exit 0 with state preservation):**
|
|
3410
|
+
```bash
|
|
3411
|
+
# Set up timeout trap
|
|
3412
|
+
CI_TIMEOUT_SECONDS=$((${CI_TIMEOUT_MINUTES:-60} * 60))
|
|
3413
|
+
timeout_handler() {
|
|
3414
|
+
echo "::warning::MindForge CI timeout reached after ${CI_TIMEOUT_MINUTES} minutes"
|
|
3415
|
+
echo "::warning::Progress saved. Next CI run will resume from: $(cat .planning/HANDOFF.json | python3 -c 'import sys,json; print(json.load(sys.stdin).get("next_task","unknown"))')"
|
|
3416
|
+
|
|
3417
|
+
# Write resume info to GitHub Actions step summary
|
|
3418
|
+
cat >> "${GITHUB_STEP_SUMMARY:-/dev/null}" << EOF
|
|
3419
|
+
## ⏱️ MindForge CI Timeout
|
|
3420
|
+
|
|
3421
|
+
CI timeout reached. Progress has been saved.
|
|
3422
|
+
|
|
3423
|
+
Next run will resume from:
|
|
3424
|
+
$(cat .planning/HANDOFF.json | python3 -c 'import sys,json; d=json.load(sys.stdin); print(d.get("next_task","unknown"))')
|
|
3425
|
+
|
|
3426
|
+
Run the CI pipeline again to continue.
|
|
3427
|
+
EOF
|
|
3428
|
+
|
|
3429
|
+
# Commit HANDOFF.json so next run can resume
|
|
3430
|
+
git add .planning/HANDOFF.json .planning/STATE.md
|
|
3431
|
+
git commit -m "ci: save MindForge progress on timeout [skip ci]" || true
|
|
3432
|
+
git push origin HEAD || true
|
|
3433
|
+
|
|
3434
|
+
exit 0 # Exit 0 — timeout is not a failure
|
|
3435
|
+
}
|
|
3436
|
+
|
|
3437
|
+
trap 'timeout_handler' TERM
|
|
3438
|
+
(sleep "${CI_TIMEOUT_SECONDS}"; kill -TERM $$) &
|
|
3439
|
+
TIMEOUT_PID=$!
|
|
3440
|
+
```
|
|
3441
|
+
```
|
|
3442
|
+
|
|
3443
|
+
**Commit:**
|
|
3444
|
+
```bash
|
|
3445
|
+
git add .mindforge/ci/ci-mode.md
|
|
3446
|
+
git commit -m "harden(ci): fix timeout exit code to 0 (soft stop), add GitHub step summary"
|
|
3447
|
+
```
|
|
3448
|
+
|
|
3449
|
+
---
|
|
3450
|
+
|
|
3451
|
+
## HARDEN 4 — Add Tier 3 CI error message
|
|
3452
|
+
|
|
3453
|
+
Update `github-actions-adapter.md` — add to the mindforge-quality job:
|
|
3454
|
+
|
|
3455
|
+
```yaml
|
|
3456
|
+
- name: Check governance tier (Tier 3 blocks CI)
|
|
3457
|
+
run: |
|
|
3458
|
+
# Check if any pending Tier 3 approvals exist without approval
|
|
3459
|
+
PENDING_T3=$(find .planning/approvals/ -name "*.json" 2>/dev/null | \
|
|
3460
|
+
xargs grep -l '"tier": 3' 2>/dev/null | \
|
|
3461
|
+
xargs grep -l '"status": "pending"' 2>/dev/null | wc -l)
|
|
3462
|
+
|
|
3463
|
+
if [ "${PENDING_T3}" -gt 0 ]; then
|
|
3464
|
+
echo "::error title=Tier 3 Governance Block::$PENDING_T3 Tier 3 change(s) require compliance review."
|
|
3465
|
+
echo "::error::Tier 3 changes (auth/payment/PII) cannot be auto-approved in CI."
|
|
3466
|
+
echo "::error::To resolve: get human approval with /mindforge:approve [id], then push again."
|
|
3467
|
+
cat >> "${GITHUB_STEP_SUMMARY}" << 'EOF'
|
|
3468
|
+
## 🔴 Governance Block: Tier 3 Approval Required
|
|
3469
|
+
|
|
3470
|
+
This PR contains changes that require compliance review (auth, payment, or PII handling).
|
|
3471
|
+
|
|
3472
|
+
**Next steps:**
|
|
3473
|
+
1. Run `/mindforge:approve` to see pending approval requests
|
|
3474
|
+
2. Have your compliance officer approve with `/mindforge:approve [id]`
|
|
3475
|
+
3. Push again — CI will pass once the approval is recorded
|
|
3476
|
+
|
|
3477
|
+
See `.planning/approvals/` for details.
|
|
3478
|
+
EOF
|
|
3479
|
+
exit 1
|
|
3480
|
+
fi
|
|
3481
|
+
|
|
3482
|
+
echo "::notice::Governance check passed — no pending Tier 3 blocks ✅"
|
|
3483
|
+
```
|
|
3484
|
+
|
|
3485
|
+
**Commit:**
|
|
3486
|
+
```bash
|
|
3487
|
+
git add .mindforge/ci/github-actions-adapter.md .github/workflows/mindforge-ci.yml
|
|
3488
|
+
git commit -m "harden(ci): add clear Tier 3 CI block message with resolution steps"
|
|
3489
|
+
```
|
|
3490
|
+
|
|
3491
|
+
---
|
|
3492
|
+
|
|
3493
|
+
## HARDEN 5 — Fix AI review daily limit and diff strategy
|
|
3494
|
+
|
|
3495
|
+
Update `ai-reviewer.md`:
|
|
3496
|
+
|
|
3497
|
+
```javascript
|
|
3498
|
+
// Replace the daily limit check with robust implementation:
|
|
3499
|
+
|
|
3500
|
+
const AI_REVIEW_LOG = path.join(projectRoot, '.mindforge', 'metrics', 'ai-review-usage.jsonl');
|
|
3501
|
+
|
|
3502
|
+
function logAIReview(prSha) {
|
|
3503
|
+
const entry = JSON.stringify({
|
|
3504
|
+
timestamp: new Date().toISOString(),
|
|
3505
|
+
pr_sha: prSha,
|
|
3506
|
+
date: new Date().toISOString().slice(0, 10),
|
|
3507
|
+
model: 'claude-sonnet-4-6',
|
|
3508
|
+
}) + '\n';
|
|
3509
|
+
|
|
3510
|
+
// Create parent directory if it doesn't exist
|
|
3511
|
+
const dir = path.dirname(AI_REVIEW_LOG);
|
|
3512
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
3513
|
+
|
|
3514
|
+
fs.appendFileSync(AI_REVIEW_LOG, entry);
|
|
3515
|
+
}
|
|
3516
|
+
|
|
3517
|
+
function checkDailyLimit(maxReviews) {
|
|
3518
|
+
if (!fs.existsSync(AI_REVIEW_LOG)) return true; // No log = no limit hit
|
|
3519
|
+
|
|
3520
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
3521
|
+
let count = 0;
|
|
3522
|
+
|
|
3523
|
+
const lines = fs.readFileSync(AI_REVIEW_LOG, 'utf8').split('\n').filter(Boolean);
|
|
3524
|
+
for (const line of lines) {
|
|
3525
|
+
try {
|
|
3526
|
+
const entry = JSON.parse(line);
|
|
3527
|
+
if (entry.date === today) count++;
|
|
3528
|
+
} catch {
|
|
3529
|
+
continue; // Skip malformed lines — don't let parse errors break the limit check
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
|
|
3533
|
+
return count < maxReviews;
|
|
3534
|
+
}
|
|
3535
|
+
|
|
3536
|
+
// Replace diff truncation strategy:
|
|
3537
|
+
|
|
3538
|
+
function buildDiffForReview(fullDiff, changedFiles) {
|
|
3539
|
+
const MAX_CHARS = 12000;
|
|
3540
|
+
const MAX_FILES = 20;
|
|
3541
|
+
|
|
3542
|
+
if (fullDiff.length <= MAX_CHARS) return fullDiff;
|
|
3543
|
+
|
|
3544
|
+
// Prefer showing fewer complete files over more truncated ones
|
|
3545
|
+
// Sort files by change size (largest first — most important to review)
|
|
3546
|
+
const fileDiffs = splitDiffByFile(fullDiff);
|
|
3547
|
+
const sortedFiles = fileDiffs.sort((a, b) => b.content.length - a.content.length);
|
|
3548
|
+
|
|
3549
|
+
let result = '';
|
|
3550
|
+
let fileCount = 0;
|
|
3551
|
+
for (const fileDiff of sortedFiles.slice(0, MAX_FILES)) {
|
|
3552
|
+
if (result.length + fileDiff.content.length > MAX_CHARS) break;
|
|
3553
|
+
result += fileDiff.content + '\n';
|
|
3554
|
+
fileCount++;
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
const omitted = sortedFiles.length - fileCount;
|
|
3558
|
+
if (omitted > 0) {
|
|
3559
|
+
result += `\n[${omitted} file(s) omitted from review — diff too large. Run review with --diff on individual files.]\n`;
|
|
3560
|
+
}
|
|
3561
|
+
|
|
3562
|
+
return result;
|
|
3563
|
+
}
|
|
3564
|
+
|
|
3565
|
+
function splitDiffByFile(diff) {
|
|
3566
|
+
const files = [];
|
|
3567
|
+
const parts = diff.split(/^diff --git/m).filter(Boolean);
|
|
3568
|
+
for (const part of parts) {
|
|
3569
|
+
const header = part.match(/^a\/(.+) b\//);
|
|
3570
|
+
files.push({
|
|
3571
|
+
filename: header ? header[1] : 'unknown',
|
|
3572
|
+
content: 'diff --git' + part,
|
|
3573
|
+
});
|
|
3574
|
+
}
|
|
3575
|
+
return files;
|
|
3576
|
+
}
|
|
3577
|
+
```
|
|
3578
|
+
|
|
3579
|
+
**Commit:**
|
|
3580
|
+
```bash
|
|
3581
|
+
git add .mindforge/pr-review/ai-reviewer.md
|
|
3582
|
+
git commit -m "harden(ai-review): robust daily limit with parse error tolerance, file-based diff truncation"
|
|
3583
|
+
```
|
|
3584
|
+
|
|
3585
|
+
---
|
|
3586
|
+
|
|
3587
|
+
## HARDEN 6 — Fix monorepo affected package detection
|
|
3588
|
+
|
|
3589
|
+
Update `cross-package-planner.md`:
|
|
3590
|
+
|
|
3591
|
+
```markdown
|
|
3592
|
+
## Affected package detection (revised)
|
|
3593
|
+
|
|
3594
|
+
```bash
|
|
3595
|
+
# CORRECTED: Match against declared package paths, not assume 2-level depth
|
|
3596
|
+
detect_affected_packages() {
|
|
3597
|
+
local MANIFEST=".planning/WORKSPACE-MANIFEST.json"
|
|
3598
|
+
|
|
3599
|
+
if [ ! -f "${MANIFEST}" ]; then
|
|
3600
|
+
echo "Run /mindforge:workspace detect first"
|
|
3601
|
+
return 1
|
|
3602
|
+
fi
|
|
3603
|
+
|
|
3604
|
+
# Get list of changed files
|
|
3605
|
+
CHANGED_FILES=$(git diff --name-only HEAD~1 2>/dev/null || git diff --cached --name-only)
|
|
3606
|
+
|
|
3607
|
+
# For each package in manifest, check if any changed file is within that package
|
|
3608
|
+
node -e "
|
|
3609
|
+
const fs = require('fs');
|
|
3610
|
+
const manifest = JSON.parse(fs.readFileSync('${MANIFEST}', 'utf8'));
|
|
3611
|
+
const changedFiles = \`${CHANGED_FILES}\`.split('\n').filter(Boolean);
|
|
3612
|
+
|
|
3613
|
+
const affected = new Set();
|
|
3614
|
+
manifest.packages.forEach(pkg => {
|
|
3615
|
+
// Check if any changed file is within this package's path
|
|
3616
|
+
const pkgPath = pkg.path.replace(/\/$/, ''); // remove trailing slash
|
|
3617
|
+
changedFiles.forEach(file => {
|
|
3618
|
+
if (file.startsWith(pkgPath + '/') || file === pkgPath) {
|
|
3619
|
+
affected.add(pkg.name);
|
|
3620
|
+
}
|
|
3621
|
+
});
|
|
3622
|
+
});
|
|
3623
|
+
|
|
3624
|
+
// Also add packages that depend on affected packages
|
|
3625
|
+
manifest.packages.forEach(pkg => {
|
|
3626
|
+
if (pkg.dependencies && pkg.dependencies.some(dep => affected.has(dep))) {
|
|
3627
|
+
affected.add(pkg.name);
|
|
3628
|
+
}
|
|
3629
|
+
});
|
|
3630
|
+
|
|
3631
|
+
console.log([...affected].join('\n'));
|
|
3632
|
+
"
|
|
3633
|
+
}
|
|
3634
|
+
```
|
|
3635
|
+
|
|
3636
|
+
This correctly handles:
|
|
3637
|
+
- Packages at any nesting depth (`libs/shared/utils/` → 3 levels deep)
|
|
3638
|
+
- Packages whose path is a prefix of another (avoid false matches)
|
|
3639
|
+
- Transitive dependencies (packages that depend on changed packages)
|
|
3640
|
+
```
|
|
3641
|
+
|
|
3642
|
+
**Commit:**
|
|
3643
|
+
```bash
|
|
3644
|
+
git add .mindforge/monorepo/cross-package-planner.md
|
|
3645
|
+
git commit -m "harden(monorepo): fix affected package detection to use manifest paths, handle deep nesting"
|
|
3646
|
+
```
|
|
3647
|
+
|
|
3648
|
+
---
|
|
3649
|
+
|
|
3650
|
+
## HARDEN 7 — Write 3 ADRs for Day 6 decisions
|
|
3651
|
+
|
|
3652
|
+
### `.planning/decisions/ADR-015-npm-based-skill-registry.md`
|
|
3653
|
+
|
|
3654
|
+
```markdown
|
|
3655
|
+
# ADR-015: npm as the MindForge Skills Registry
|
|
3656
|
+
|
|
3657
|
+
**Status:** Accepted
|
|
3658
|
+
**Date:** [today]
|
|
3659
|
+
|
|
3660
|
+
## Context
|
|
3661
|
+
MindForge needs a way to distribute community and organisation skills.
|
|
3662
|
+
Options: custom registry server, GitHub releases, npm registry.
|
|
3663
|
+
|
|
3664
|
+
## Decision
|
|
3665
|
+
Use the standard npm registry with `mindforge-skill-` package naming convention.
|
|
3666
|
+
|
|
3667
|
+
## Rationale
|
|
3668
|
+
npm is the world's largest package registry with existing infrastructure for:
|
|
3669
|
+
versioning, authentication, access control, search, and deprecation.
|
|
3670
|
+
Building a custom registry duplicates all of this at significant cost.
|
|
3671
|
+
The npm ecosystem's supply chain security tooling (npm audit, Dependabot,
|
|
3672
|
+
Snyk) works natively. Private registries (Verdaccio, Artifactory, GitHub Packages)
|
|
3673
|
+
are npm-compatible — organisations with private skills don't need separate tooling.
|
|
3674
|
+
|
|
3675
|
+
## Consequences
|
|
3676
|
+
- Skill names follow npm conventions (lowercase, hyphens)
|
|
3677
|
+
- Version management follows npm semver conventions
|
|
3678
|
+
- Private skills require a compatible private npm registry
|
|
3679
|
+
- The injection guard and validation pipeline are the primary
|
|
3680
|
+
supply chain security controls (npm audit is insufficient alone for SKILL.md content)
|
|
3681
|
+
```
|
|
3682
|
+
|
|
3683
|
+
### `.planning/decisions/ADR-016-ci-exit-code-0-on-timeout.md`
|
|
3684
|
+
|
|
3685
|
+
```markdown
|
|
3686
|
+
# ADR-016: CI timeout exits with code 0 (soft stop)
|
|
3687
|
+
|
|
3688
|
+
**Status:** Accepted
|
|
3689
|
+
**Date:** [today]
|
|
3690
|
+
|
|
3691
|
+
## Context
|
|
3692
|
+
MindForge CI may run out of time before completing all phases.
|
|
3693
|
+
The question is: should timeout exit with code 0 (success) or non-zero (failure)?
|
|
3694
|
+
|
|
3695
|
+
## Decision
|
|
3696
|
+
Timeout exits with code 0. State is saved. Next CI run resumes.
|
|
3697
|
+
|
|
3698
|
+
## Rationale
|
|
3699
|
+
A MindForge CI timeout is not a code failure — it is a resource limit.
|
|
3700
|
+
Failing the build on timeout would:
|
|
3701
|
+
1. Block the PR with a failure that has no fix (the code is fine — time ran out)
|
|
3702
|
+
2. Force teams to either increase CI limits or split phases artificially
|
|
3703
|
+
3. Make MindForge feel unreliable ("it randomly fails when there's a lot to do")
|
|
3704
|
+
|
|
3705
|
+
The correct behaviour: save progress, exit cleanly, let the next run continue.
|
|
3706
|
+
The CI pipeline is designed for continuation, not completion in a single run.
|
|
3707
|
+
|
|
3708
|
+
## Consequences
|
|
3709
|
+
- CI pipelines may run multiple times for a single phase
|
|
3710
|
+
- HANDOFF.json must be committed during CI runs (CI has write access)
|
|
3711
|
+
- Teams must monitor CI for timeout patterns (frequent timeouts → split phases)
|
|
3712
|
+
- GitHub Actions step summary provides visibility into timeout state
|
|
3713
|
+
```
|
|
3714
|
+
|
|
3715
|
+
### `.planning/decisions/ADR-017-sdk-localhost-only.md`
|
|
3716
|
+
|
|
3717
|
+
```markdown
|
|
3718
|
+
# ADR-017: MindForge SDK event stream is localhost-only
|
|
3719
|
+
|
|
3720
|
+
**Status:** Accepted
|
|
3721
|
+
**Date:** [today]
|
|
3722
|
+
|
|
3723
|
+
## Context
|
|
3724
|
+
The MindForge SDK includes an SSE event stream for real-time progress.
|
|
3725
|
+
The question is whether it should bind to all interfaces or localhost only.
|
|
3726
|
+
|
|
3727
|
+
## Decision
|
|
3728
|
+
The event stream binds to 127.0.0.1 (localhost) only.
|
|
3729
|
+
|
|
3730
|
+
## Rationale
|
|
3731
|
+
The event stream exposes:
|
|
3732
|
+
- AUDIT.jsonl entries (which contain sensitive project state)
|
|
3733
|
+
- Task completion events (which reveal code structure and timing)
|
|
3734
|
+
- Security finding events (which reveal vulnerability information)
|
|
3735
|
+
|
|
3736
|
+
Exposing this to all network interfaces in a shared development environment
|
|
3737
|
+
(VMs, shared cloud desktops, containers) would allow other users to monitor
|
|
3738
|
+
another developer's project state in real-time.
|
|
3739
|
+
Localhost binding provides adequate protection for the primary use case
|
|
3740
|
+
(local developer tooling) without requiring authentication infrastructure.
|
|
3741
|
+
|
|
3742
|
+
## Consequences
|
|
3743
|
+
- Remote integrations cannot use the event stream directly
|
|
3744
|
+
- For remote monitoring: use the audit log query API via SSH tunnel
|
|
3745
|
+
- Container environments: map the port explicitly if remote access is needed
|
|
3746
|
+
```
|
|
3747
|
+
|
|
3748
|
+
**Commit:**
|
|
3749
|
+
```bash
|
|
3750
|
+
git add .planning/decisions/
|
|
3751
|
+
git commit -m "docs(adr): add ADR-015 npm registry, ADR-016 CI timeout, ADR-017 localhost SDK"
|
|
3752
|
+
```
|
|
3753
|
+
|
|
3754
|
+
---
|
|
3755
|
+
|
|
3756
|
+
## HARDEN 8 — Add to test suites
|
|
3757
|
+
|
|
3758
|
+
```javascript
|
|
3759
|
+
// Add to tests/distribution.test.js:
|
|
3760
|
+
|
|
3761
|
+
console.log('\nHardening-prompted distribution tests:');
|
|
3762
|
+
|
|
3763
|
+
test('registry client uses chmod 700 for temp directory', () => {
|
|
3764
|
+
const c = read('.mindforge/distribution/registry-client.md');
|
|
3765
|
+
assert.ok(c.includes('chmod 700') || c.includes('700'), 'Should use chmod 700 for temp dir security');
|
|
3766
|
+
});
|
|
3767
|
+
|
|
3768
|
+
test('registry client verifies tarball size', () => {
|
|
3769
|
+
const c = read('.mindforge/distribution/registry-client.md');
|
|
3770
|
+
assert.ok(c.includes('TARBALL_SIZE') || c.includes('size'), 'Should check tarball size');
|
|
3771
|
+
});
|
|
3772
|
+
|
|
3773
|
+
test('MINDFORGE-SCHEMA.json has number type with min/max', () => {
|
|
3774
|
+
const schema = JSON.parse(fs.readFileSync('.mindforge/MINDFORGE-SCHEMA.json', 'utf8'));
|
|
3775
|
+
const compaction = schema.properties.COMPACTION_THRESHOLD_PCT;
|
|
3776
|
+
assert.ok(compaction, 'COMPACTION_THRESHOLD_PCT should be in schema');
|
|
3777
|
+
assert.strictEqual(compaction.type, 'number');
|
|
3778
|
+
assert.strictEqual(compaction.minimum, 50);
|
|
3779
|
+
assert.strictEqual(compaction.maximum, 90);
|
|
3780
|
+
});
|
|
3781
|
+
|
|
3782
|
+
// Add to tests/ci-mode.test.js:
|
|
3783
|
+
|
|
3784
|
+
console.log('\nHardening-prompted CI tests:');
|
|
3785
|
+
|
|
3786
|
+
test('ci-mode uses exit code 0 for timeout (not 2)', () => {
|
|
3787
|
+
const c = read('.mindforge/ci/ci-mode.md');
|
|
3788
|
+
assert.ok(
|
|
3789
|
+
c.includes('exit 0') && (c.includes('timeout') || c.includes('Timeout')),
|
|
3790
|
+
'Timeout should exit with code 0'
|
|
3791
|
+
);
|
|
3792
|
+
// Should NOT say exit 2 for timeout
|
|
3793
|
+
const exit2ForTimeout = c.match(/timeout.*exit 2|exit 2.*timeout/is);
|
|
3794
|
+
assert.ok(!exit2ForTimeout, 'Should not use exit 2 for timeout');
|
|
3795
|
+
});
|
|
3796
|
+
|
|
3797
|
+
test('github-actions adapter has Tier 3 governance block with clear message', () => {
|
|
3798
|
+
const c = read('.mindforge/ci/github-actions-adapter.md');
|
|
3799
|
+
assert.ok(
|
|
3800
|
+
c.includes('Tier 3') && c.includes('block'),
|
|
3801
|
+
'Should explain Tier 3 CI block clearly'
|
|
3802
|
+
);
|
|
3803
|
+
});
|
|
3804
|
+
|
|
3805
|
+
// Add to tests/sdk.test.js:
|
|
3806
|
+
|
|
3807
|
+
console.log('\nHardening-prompted SDK tests:');
|
|
3808
|
+
|
|
3809
|
+
test('SDK SSE server binds to 127.0.0.1 (localhost only)', () => {
|
|
3810
|
+
const c = read('sdk/src/events.ts');
|
|
3811
|
+
assert.ok(
|
|
3812
|
+
c.includes("'127.0.0.1'") || c.includes('"127.0.0.1"'),
|
|
3813
|
+
'SSE server should bind to 127.0.0.1 only'
|
|
3814
|
+
);
|
|
3815
|
+
});
|
|
3816
|
+
|
|
3817
|
+
test('SDK SSE server rejects non-localhost connections', () => {
|
|
3818
|
+
const c = read('sdk/src/events.ts');
|
|
3819
|
+
assert.ok(
|
|
3820
|
+
c.includes('isLocalhost') || c.includes('remoteAddress') || c.includes('Forbidden'),
|
|
3821
|
+
'SSE server should reject non-localhost connections'
|
|
3822
|
+
);
|
|
3823
|
+
});
|
|
3824
|
+
|
|
3825
|
+
test('SDK has inotify fallback for Linux', () => {
|
|
3826
|
+
const c = read('sdk/src/events.ts');
|
|
3827
|
+
assert.ok(
|
|
3828
|
+
c.includes('ENOSPC') || c.includes('polling') || c.includes('watchFile'),
|
|
3829
|
+
'SDK should handle Linux inotify limits'
|
|
3830
|
+
);
|
|
3831
|
+
});
|
|
3832
|
+
```
|
|
3833
|
+
|
|
3834
|
+
**Commit:**
|
|
3835
|
+
```bash
|
|
3836
|
+
git add tests/
|
|
3837
|
+
git commit -m "test(day6): add hardening-prompted tests for distribution, CI, and SDK security"
|
|
3838
|
+
```
|
|
3839
|
+
|
|
3840
|
+
---
|
|
3841
|
+
|
|
3842
|
+
## HARDEN 9 — Update CHANGELOG.md, final commit
|
|
3843
|
+
|
|
3844
|
+
Update `CHANGELOG.md`:
|
|
3845
|
+
|
|
3846
|
+
```markdown
|
|
3847
|
+
## [0.6.0] — Day 6 Distribution Platform
|
|
3848
|
+
|
|
3849
|
+
### Added
|
|
3850
|
+
- Public skills registry: `npx mindforge-skills install/publish/search` (npm-based)
|
|
3851
|
+
- Skill validator: 3-level validation schema (schema, content, quality)
|
|
3852
|
+
- MINDFORGE.md JSON Schema: validation with non-overridable field markers
|
|
3853
|
+
- MindForge CI mode: GitHub Actions / GitLab CI / Jenkins integration
|
|
3854
|
+
- GitHub Actions workflow: health, security, quality, AI review jobs
|
|
3855
|
+
- AI PR Review Engine: Claude API-powered code review with context loading
|
|
3856
|
+
- Monorepo/workspace support: npm/pnpm/Nx/Turborepo/Lerna detection
|
|
3857
|
+
- Cross-package planner: topological execution order for monorepo phases
|
|
3858
|
+
- @mindforge/sdk: TypeScript SDK with client, event stream, and command builders
|
|
3859
|
+
- SSE event stream: real-time progress events via Server-Sent Events
|
|
3860
|
+
- /mindforge:init-org — organisation-wide MindForge setup command
|
|
3861
|
+
- /mindforge:install-skill — install skill from public/private registry
|
|
3862
|
+
- /mindforge:publish-skill — publish skill to npm registry
|
|
3863
|
+
- /mindforge:pr-review — AI code review powered by Claude API
|
|
3864
|
+
- /mindforge:workspace — monorepo workspace management
|
|
3865
|
+
- /mindforge:benchmark — skill effectiveness benchmarking
|
|
3866
|
+
- 3 new ADRs: ADR-015 npm registry, ADR-016 CI timeout, ADR-017 localhost SDK
|
|
3867
|
+
|
|
3868
|
+
### Hardened
|
|
3869
|
+
- Registry: TOCTOU-safe temp directory (chmod 700), tarball size verification
|
|
3870
|
+
- CI: timeout exits with code 0 (soft stop), clear Tier 3 block messages
|
|
3871
|
+
- SDK: localhost-only SSE binding, Linux inotify fallback
|
|
3872
|
+
- AI review: robust daily limit (parse error tolerant), file-based diff truncation
|
|
3873
|
+
- Monorepo: affected package detection uses manifest paths (not depth assumption)
|
|
3874
|
+
```
|
|
3875
|
+
|
|
3876
|
+
```bash
|
|
3877
|
+
git add CHANGELOG.md package.json
|
|
3878
|
+
git commit -m "chore(release): v0.6.0 complete with all hardening"
|
|
3879
|
+
git push origin feat/mindforge-distribution-platform
|
|
3880
|
+
```
|
|
3881
|
+
|
|
3882
|
+
---
|
|
3883
|
+
|
|
3884
|
+
## DAY 6 COMPLETE — Final state of MindForge
|
|
3885
|
+
|
|
3886
|
+
| Metric | Day 1 | Day 2 | Day 3 | Day 4 | Day 5 | Day 6 |
|
|
3887
|
+
|---|---|---|---|---|---|---|
|
|
3888
|
+
| Commands | 6 | 10 | 15 | 21 | 25 | **31** |
|
|
3889
|
+
| Skills | 5 | 5 | 10 | 10 | 10 | **10+registry** |
|
|
3890
|
+
| ADRs | 3 | 5 | 8 | 11 | 14 | **17** |
|
|
3891
|
+
| Test suites | 1 | 4 | 5 | 7 | 9 | **12** |
|
|
3892
|
+
| Version | 0.1.0 | 0.2.0 | 0.3.0 | 0.4.0 | 0.5.0 | **0.6.0** |
|
|
3893
|
+
| MindForge version | — | — | — | — | — | **v0.6.0** |
|
|
3894
|
+
|
|
3895
|
+
**MindForge v0.6.0: 31 commands · 10 core skills + public registry · 8 personas**
|
|
3896
|
+
**17 ADRs · 12 test suites · SDK · CI integration · Monorepo support**
|
|
3897
|
+
|
|
3898
|
+
---
|
|
3899
|
+
|
|
3900
|
+
## DAY 7 PREVIEW
|
|
3901
|
+
|
|
3902
|
+
```
|
|
3903
|
+
Branch: feat/mindforge-production-hardening
|
|
3904
|
+
|
|
3905
|
+
Day 7 scope (Production hardening & polish):
|
|
3906
|
+
- Complete installer: npx mindforge-cc fully functional end-to-end
|
|
3907
|
+
- Self-update mechanism: /mindforge:update with changelog preview
|
|
3908
|
+
- Migration tool: upgrade between MindForge versions with state migration
|
|
3909
|
+
- Plugin system: third-party extensions to MindForge commands
|
|
3910
|
+
- Performance profiling: identify slow phases and optimise agent token usage
|
|
3911
|
+
- Cross-version compatibility: HANDOFF.json schema migration
|
|
3912
|
+
- Complete documentation site: docs.mindforge.dev structure and content
|
|
3913
|
+
- Security penetration testing simulation: adversarial review of all governance
|
|
3914
|
+
- Production readiness checklist: final audit before public release
|
|
3915
|
+
- npx mindforge-cc publish: full end-to-end skill publication workflow
|
|
3916
|
+
```
|
|
3917
|
+
|
|
3918
|
+
**Branch:** `feat/mindforge-distribution-platform`
|
|
3919
|
+
**Day 6 complete. Open PR → merge → tag v0.6.0**
|