SLOPSHOPPER

ArcKit

The Enterprise Architecture Governance Harness - 76 slash commands across strategy, architecture, delivery, and assurance

newbandguard
★ 5v6.17.5MITupdated 2026-10-04tractorjuice/arckit-claude/plugins/arckit
A shopper browsing a rack in a slop shop
README

ArcKit Plugin for Claude Code

The Enterprise Architecture Governance Harness — a Claude Code plugin providing 76 slash commands across strategy, architecture, delivery, assurance, and interoperability.

Installation

Step 0: Make sure Claude Code is up to date

ArcKit needs Claude Code v2.1.287 or later (see Prerequisites below). The simplest way to land on a supported version — even if you've never installed Claude Code before — is:

claude install latest

If you don't yet have the claude CLI on your PATH, follow the official Claude Code install guide first, then run claude install latest. To check what you're on:

claude --version

Step 1: Add the marketplace

In Claude Code, run:

/plugin marketplace add tractorjuice/arckit-claude

Step 2: Install the plugin

/plugin

Go to the Discover tab, find arckit, and install it. Or via CLI:

claude plugin install arckit@arckit-claude

Installing with ArcKit overlay plugins

The same marketplace hosts the core plugin plus regional, sector, and tooling overlays. Install only the plugins you need:

claude plugin install arckit arckit-uae
claude plugin install arckit arckit-au arckit-au-energy
claude plugin install arckit arckit-uk-finance

The arckit-uk-gcloud overlay is public for installation and inspection, but remains proprietary and is not MIT licensed.

Alternative: Load for a single session

claude --plugin-dir /path/to/arckit-claude

Prerequisites

  • Claude Code v2.1.287 or later (recommended minimum)
  • Bash shell (for helper scripts)
  • For /arckit:aws-research: AWS Knowledge MCP server (included)
  • For /arckit:azure-research: Microsoft Learn MCP server (included)
  • For /arckit:gcp-research: Google Developer Knowledge MCP (requires GOOGLE_API_KEY — see MCP Servers)

Why v2.1.287? v2.1.287 adds Claude mods, which ArcKit uses to draw a status line above the prompt: how many projects and artefacts you have, how many are DRAFT, and how many reviews are overdue. It also fixes plugin SessionStart hooks not running in new cloud sessions, which skipped ArcKit's session set-up and version check there. The floor carries v2.1.284, which adds Claude Sonnet 5.5 (claude-sonnet-5-5), the default Sonnet model on the Anthropic API, with 1M context; earlier clients cannot select it. Like Opus 5.5 it always thinks and defaults to effort: medium, so ArcKit's effort: max commands run at max on it. The floor carries v2.1.280, which adds Claude Opus 5.5 (claude-opus-5-5), the default Opus model. Opus 5.5 always thinks, so ArcKit's effort: max commands can no longer be quietly sent as high by a session with thinking off, and the Effective Effort row in each artefact's Build Provenance is accurate. The floor carries v2.1.251, which stops the file tools following a symlink swapped inside the working directory after the permission check, and makes Grep and Glob honour Read() deny rules through symlinked paths — the class of bypass ArcKit's file-protection and secret-file-scanner gates sit in front of. It also sends Opus 5 effort: xhigh/max as high when thinking is off instead of failing, so ArcKit's effort: max commands complete on thinking-off sessions. v2.1.246 fixed four plugin-loading bugs that hit ArcKit's exact layout: /reload-plugins counted 0 skills for skills/*/SKILL.md plugins, hook errors showed a literal ${CLAUDE_PLUGIN_ROOT}, the plugin cache created duplicate SHA-named directories, and claude plugin update <bare-name> failed. The floor carries forward v2.1.234, which stops Claude Code's MCP diagnostics printing resolved secrets — ArcKit bundles two keyed MCP servers whose ${user_config.*} API keys sit in request headers, and on a keyless session those connections fail by design, so ArcKit routinely produces exactly the diagnostics this fixed. v2.1.221 fixed WebSearch returning a 400 at effort: xhigh/max with thinking disabled, which silently broke ArcKit's effort: max commands and research agents for anyone running with thinking off. v2.1.222-v2.1.224 close PreToolUse auto-allow bypasses in background agent tasks (load-bearing since v2.1.232 made spawns background by default), Bash permission-check bypasses, and a sandbox deny-path bypass. It also carries v2.1.219's Claude Opus 5 (claude-opus-5) with 1M context and fast mode (superseded as the default by Opus 5.5); v2.1.200's project-scoped plugin loading from git worktrees and claude agents --plugin-dir <dir> visibility; the v2.1.198-v2.1.199 background-subagent reliability, parent error-propagation, and hook stderr-visibility fixes; v2.1.197's Claude Sonnet 5 default with native 1M context; and v2.1.172's wildcard-domain WebFetch fix. It also carries the older MCP alwaysLoad, provenance hook, release validation, telemetry, /context, Auto mode, plugin update, MCP leak, retry, and subagent working-directory fixes ArcKit relies on.

Quick Start

After installing the plugin:

  1. Initialize a project (optional - commands will create structure automatically):
   /arckit:init
  1. Create architecture principles:
   /arckit:principles
  1. Create requirements for a project:
   /arckit:requirements NHS appointment booking system

What's Included

ComponentCountDescription
Commands75Slash commands for architecture artifacts and OKF interoperability
Skills1Conversational Wardley Mapping with interactive guidance
Agents20Autonomous research agents and subagent definitions
Templates68Document templates with UK Government compliance
Scripts15Helper bash, Python, and Node scripts
Hooks17Automation hooks across 7 event types
Guides167Command and reference documentation

Hooks

Automation hooks run automatically to provide context and enforce standards. See the Hooks Guide for full details. docs/ENFORCEMENT.md states which rules the hooks enforce in code, which are only asked of the model, and what your organisation supplies.

EventHooksPurpose
SessionStartarckit-session, version-checkInject version/context, check for updates
Stop / StopFailuresession-learnerRecord session activity for future context
UserPromptSubmitarckit-context, secret-detection, + 6 command-specificProject context, secret scanning, pre-processing
PreToolUsevalidate-arc-filename, score-validator, file-protection, secret-file-scannerFilename enforcement, security, validation
PostToolUseupdate-manifestKeep manifest.json in sync

OKF Interoperability

  • /arckit:export-okf exports ArcKit ARC-*.md artifacts as copied Markdown with OKF-compatible frontmatter.
  • /arckit:import-okf scans OKF Markdown bundles, writes .arckit/tmp/okf-import-report.json, and materializes safe imports as RSCH review notes.
  • Source ARC frontmatter stamping is opt-in via ARCKIT_OKF_FRONTMATTER=1 or .arckit/config.json with { "okfFrontmatter": true }.

Template Customization

ArcKit templates can be customized per-project to match your organization's requirements, branding, or compliance frameworks.

How It Works

  1. User templates override plugin templates: If a template exists in .arckit/templates/, it takes precedence over the plugin's default template.
  1. Copy and customize: Use /arckit:customize to copy templates to your project for editing.

Quick Start

# List available templates
/arckit:customize list

# Copy a template to customize
/arckit:customize requirements

# Copy all templates
/arckit:customize all

Common Customizations

For non-UK Government projects:

  • Remove "UK Government Alignment" sections
  • Change classification scheme from OFFICIAL-SENSITIVE to your organization's scheme
  • Remove TCoP, GDS Service Standard references

For your organization:

  • Add custom Document Control fields (Cost Centre, Programme, Department)
  • Change requirement ID prefixes (BR/FR/NFR → your taxonomy)
  • Add organization branding and headers
  • Modify compliance frameworks (ISO27001, SOX, HIPAA instead of UK Gov)

Template Location

project-root/
├── .arckit/
│   └── templates/              # Your customized templates
│       ├── requirements-template.md
│       ├── risk-register-template.md
│       └── ...
└── projects/
    └── ...

Keeping Templates Updated

When ArcKit plugin updates with new features:

  • Your customized templates are not automatically updated
  • Compare your templates with plugin versions periodically
  • Merge new sections you want to adopt

Skills

The plugin includes conversational skills that activate automatically when you ask relevant questions:

  • Wardley Mapping — Ask about evolution stages, doctrine maturity, build vs. buy decisions, gameplay patterns, or create interactive maps. For formal documents, use /arckit:wardley instead.

Commands Overview

Core Governance

  • /arckit:principles - Create architecture principles
  • /arckit:stakeholders - Analyze stakeholders and goals
  • /arckit:requirements - Generate comprehensive requirements
  • /arckit:risk - Create risk register (Orange Book)
  • /arckit:sobc - Strategic Outline Business Case (Green Book)

Technical Design

  • /arckit:data-model - Data model with GDPR compliance
  • /arckit:diagram - Architecture diagrams (Mermaid)
  • /arckit:wardley - Wardley Maps for strategy
  • /arckit:adr - Architecture Decision Records

Research & Procurement

  • /arckit:research - Technology market research
  • /arckit:aws-research - AWS service research (MCP)
  • /arckit:azure-research - Azure service research (MCP)
  • /arckit:datascout - External data source discovery
  • /arckit:evaluate - Vendor evaluation framework
  • /arckit:sow - Statement of Work / RFP
  • /arckit:gcloud-search - G-Cloud marketplace search
  • /arckit:dos - Digital Outcomes & Specialists

UK Government Compliance

  • /arckit:tcop - Technology Code of Practice review
  • /arckit:secure - Secure by Design assessment
  • /arckit:dpia - Data Protection Impact Assessment
  • /arckit:ai-playbook - AI Playbook compliance
  • /arckit:service-assessment - GDS Service Standard

Operations & Delivery

  • /arckit:devops - DevOps strategy
  • /arckit:finops - FinOps cloud cost management
  • /arckit:mlops - MLOps strategy
  • /arckit:operationalize - Operational readiness
  • /arckit:backlog - Product backlog generation
  • /arckit:roadmap - Architecture roadmap

See the full command list with /help arckit.

Project Structure

ArcKit creates this structure in your project:

projects/
├── 000-global/           # Cross-project artifacts
│   ├── policies/         # Organization policies
│   └── ARC-000-PRIN-*.md # Architecture principles
└── 001-project-name/     # Project artifacts
    ├── ARC-001-REQ-*.md  # Requirements
    ├── ARC-001-STKE-*.md # Stakeholders
    ├── vendors/          # Vendor evaluations
    └── external/         # External documents

MCP Servers

The plugin includes 7 MCP (Model Context Protocol) servers: six for cloud and government research, and Trello for backlog export:

MCP ServerAPI Key RequiredUsed By
AWS KnowledgeNo/arckit:aws-research
Microsoft LearnNo/arckit:azure-research
Google Developer KnowledgeYes (GOOGLE_API_KEY)/arckit:gcp-research
Data CommonsYes (DATA_COMMONS_API_KEY)Data statistics lookups
govreposcrapeNo/arckit:gov-reuse, /arckit:gov-code-search, /arckit:gov-landscape
UK TendersNo/arckit:tenders, /arckit:competitors
Trello (Atlassian)No; sign in once through /mcp/arckit:trello

AWS Knowledge and Microsoft Learn work out of the box with no configuration. The Google and Data Commons servers require API keys — if you don't set them, you'll see errors in the plugin UI, but all other commands work normally.

Setting up optional API keys

Google Developer Knowledge (for /arckit:gcp-research):

  1. Get a free API key from Google AI Studio
  2. Set the environment variable: export GOOGLE_API_KEY="your-key-here"

Data Commons (for data statistics lookups):

  1. Get an API key from datacommons.org
  2. Set the environment variable: export DATA_COMMONS_API_KEY="your-key-here"

Data and Privacy

ArcKit collects no usage data. It has no analytics and no account, and it runs no server of its own. Everything it writes stays in your repository: the artefacts under projects/, and the session log and telemetry the hooks keep under .arckit/memory/ on your machine.

The plugin sends data to other services only in these cases:

WhenWhere it goesWhat is sent
A command queries one of the six bundled research MCP servers (table above)AWS Knowledge (knowledge-mcp.global.api.aws), Microsoft Learn (learn.microsoft.com), Google Developer Knowledge (developerknowledge.googleapis.com), Data Commons (api.datacommons.org), govreposcrape (govreposcrape-api-1060386346356.us-central1.run.app), UK Tenders (tenders.run.cns.me)The search terms and document IDs Claude passes to that server's tools, which can include wording taken from your requirements. The Google and Data Commons servers also receive the API key you configure. Each service's own privacy policy applies
/arckit:trello exports a backlogAtlassian's Trello MCP server (mcp.trello.com)The backlog's stories, descriptions and acceptance criteria, written to a board in your Trello account under your own sign-in. Atlassian's privacy policy applies
Each session startsGitHub API (api.github.com)One anonymous request for the latest ArcKit release, to tell you when an update is available. No project data is sent
You run /arckit:trelloTrello API (api.trello.com)Your backlog's epics, stories and acceptance criteria, sent with the Trello key and token you provide
A research command uses web search or fetchThe sites Claude searches or fetchesSearch queries and URLs, through Claude's own web tools

No hook approves a permission request. Each command and skill pre-approves what it needs through its own allowed-tools rules, which Claude Code applies only while that command runs: reading the plugin's own files (Read(/${CLAUDE_PLUGIN_ROOT}/**), for its templates, references and schemas, which sit outside your working directory) and running the plugin's own scripts. Your own deny rules still take precedence. MCP tool calls ask the first time; to stop the prompts for a server, add it to permissions.allow, for example "mcp__plugin_arckit_aws-knowledge". Before running ArcKit on sensitive material, see docs/ENFORCEMENT.md and the privacy policy at <https://arckit.org/privacy.html>.

Migration from CLI

If you previously used arckit init --ai claude:

# Remove CLI-generated files (plugin replaces them)
rm -rf .claude/commands/arckit.*.md
rm -rf .claude/agents/arckit-*.md
rm -rf .arckit/templates/
rm -rf .arckit/scripts/

# Keep your project data
# projects/ directory stays (user data)

Note: The ArcKit CLI no longer distributes Claude Code commands. Claude Code users should use this plugin instead.

For Gemini/Codex Users

This plugin is for Claude Code. For other AI assistants:

Links

License

MIT

Source 2 files
hooks/mod/register.mjs 144 lines
1/**
2 * ArcKit status band: a Claude Code mod (hooks module).
3 *
4 * Draws one line above the prompt in an ArcKit repository (found by walking up
5 * from the session's folder, as findRepoRoot does, so a session started inside
6 * projects/ or a project still sees it): how many projects
7 * and artefacts there are, how many are DRAFT, and how many reviews are
8 * overdue, with a pointer to /arckit:health when something needs attention.
9 * In the terminal it starts counting when the session starts; the desktop
10 * app's Code tab joins its session later, so there it starts on session.attach.
11 *
12 * Claude Code only, and additive. It needs Claude Code v2.1.287+ (mods on by
13 * default); an older client never loads it, and the classic hooks in
14 * hooks.json run beside it unchanged. It only observes: every tool.call hook
15 * passes the call on untouched, so no gate depends on it. It lives in a
16 * subdirectory so the converter's hooks/*.mjs copy for Kimi never ships it.
17 *
18 * Set ARCKIT_NO_STATUS_BAND to switch it off.
19 *
20 * Counting rules live in status-model.mjs (pure, tested by
21 * tests/plugin/status-band.test.mjs).
22 */
23
24import { bandText, candidateDirs, isArtefactName, isProjectDir, isProjectsListing, needsAttention, summarise } from './status-model.mjs';
25
26const MAX_DEPTH = 4;
27const MAX_FILES = 2000;
28const MAX_BYTES = 4 * 1024 * 1024;
29// The surfaces Claude Code raises the AbovePrompt band on.
30const BAND_SURFACES = new Set(['terminal', 'desktop']);
31
32function localDate(ms) {
33  const at = new Date(ms);
34  const pad = (n) => String(n).padStart(2, '0');
35  return `${at.getFullYear()}-${pad(at.getMonth() + 1)}-${pad(at.getDate())}`;
36}
37
38let projectsDir = null;
39let summary = null;
40let isDirty = false;
41let scanning = null;
42
43async function collect($, dir, depth, found) {
44  if (depth > MAX_DEPTH || found.length >= MAX_FILES) return;
45  const entries = await $.fs.list(dir).catch(() => []);
46  for (const entry of entries) {
47    if (entry.isLink || found.length >= MAX_FILES) continue;
48    if (entry.kind === 'dir' && !entry.name.startsWith('.') && entry.name !== 'node_modules') {
49      await collect($, `${dir}/${entry.name}`, depth + 1, found);
50    } else if (entry.kind === 'file' && isArtefactName(entry.name) && entry.size <= MAX_BYTES) {
51      found.push(`${dir}/${entry.name}`);
52    }
53  }
54}
55
56async function scan($) {
57  const top = await $.fs.list(projectsDir);
58  const projectNames = top.filter((e) => e.kind === 'dir' && isProjectDir(e.name)).map((e) => e.name);
59  const paths = [];
60  for (const name of projectNames) {
61    await collect($, `${projectsDir}/${name}`, 0, paths);
62  }
63  const texts = await Promise.all(paths.map((path) => $.fs.read(path).catch(() => '')));
64  summary = summarise(projectNames, texts, localDate(await $.clock.now()));
65  $.ui.invalidate('ui.render');
66}
67
68function rescan($) {
69  if (!projectsDir || scanning) return;
70  isDirty = false;
71  scanning = scan($)
72    .catch(() => {
73      summary = null;
74    })
75    .finally(() => {
76      scanning = null;
77    });
78}
79
80async function findProjectsDir($, cwd) {
81  for (const candidate of candidateDirs(cwd)) {
82    const entries = await $.fs.list(candidate).catch(() => null);
83    if (entries && isProjectsListing(entries)) return candidate;
84  }
85  return null;
86}
87
88async function start($, cwd) {
89  if ((await $.env.get('ARCKIT_NO_STATUS_BAND')) !== undefined) return;
90  if (!projectsDir) projectsDir = await findProjectsDir($, cwd);
91  if (projectsDir) rescan($);
92}
93
94async function onSessionStart($, e, next) {
95  const result = await next(e);
96  if (e.isInteractive && e.surface === 'terminal') await start($, e.cwd);
97  return result;
98}
99
100// The desktop app runs the session headless and joins it afterwards, so its
101// session.start says no surface; it arrives here instead, before it first draws.
102async function onSessionAttach($, e, next) {
103  const result = await next(e);
104  if (BAND_SURFACES.has(e.surface)) await start($, await $.session.cwd());
105  return result;
106}
107
108async function markDirty($, e, next) {
109  const result = await next(e);
110  if (projectsDir) isDirty = true;
111  return result;
112}
113
114async function onTurnComplete($, e, next) {
115  const result = await next(e);
116  if (isDirty) rescan($);
117  return result;
118}
119
120async function drawBand($, e, next) {
121  if (!summary || summary.projects === 0 || e.props.hasSurvey) return next(e);
122  const { Box, Text } = await $.ui.resolve(e);
123  const text = bandText(summary);
124  return Box({
125    paddingX: 1,
126    children: [
127      needsAttention(summary)
128        ? Text({ color: 'yellow', wrap: 'truncate-end', children: text })
129        : Text({ dimColor: true, wrap: 'truncate-end', children: text }),
130    ],
131  });
132}
133
134/** @type {import('claude-code').Register} */
135export function register(on) {
136  on('session.start', onSessionStart);
137  on('session.attach', onSessionAttach);
138  on('tool.call', { tool: 'Write' }, markDirty);
139  on('tool.call', { tool: 'Edit' }, markDirty);
140  on('tool.call', { tool: 'Bash' }, markDirty);
141  on('turn.complete', onTurnComplete);
142  on('ui.render', { component: 'AbovePrompt' }, drawBand);
143}
144
hooks/mod/status-model.mjs 133 lines
1/**
2 * ArcKit status band: the pure half.
3 *
4 * Turns the artefacts under projects/ into the one line the status-band mod
5 * draws above the prompt. No I/O and no Node imports, so the mod (which runs
6 * in Claude Code's mods sandbox, with no Node) and `node --test` both load it.
7 *
8 * The DRAFT and review-overdue rules match detect-stale-artifacts.sh and the
9 * STALE-DRAFT / REVIEW-OVERDUE rules in graph-inject.mjs formatHealth, so the
10 * band never disagrees with /arckit:health.
11 */
12
13export const STALE_DRAFT_DAYS = 30;
14
15const ISO_DATE = /\d{4}-\d{2}-\d{2}/;
16const STATUSES = /(DRAFT|IN_REVIEW|APPROVED|PUBLISHED|SUPERSEDED|ARCHIVED)/i;
17
18export function isArtefactName(name) {
19  return /^ARC-.+\.md$/.test(name);
20}
21
22export function isProjectDir(name) {
23  return /^\d{3}-/.test(name);
24}
25
26/**
27 * Whether a directory listing is an ArcKit projects/ folder: it holds at
28 * least one numbered project directory. Matches findRepoRoot in
29 * hook-utils.mjs, so the band finds the same folder the other hooks do.
30 */
31export function isProjectsListing(entries) {
32  return entries.some((e) => e.kind === 'dir' && /^\d{3}(?:-|$)/.test(e.name));
33}
34
35/**
36 * The folders to try for projects/, from the session's folder up to the
37 * filesystem root, so a session started inside projects/ or a project still
38 * finds it. Handles both / and \ separators.
39 */
40export function candidateDirs(cwd, limit = 32) {
41  const out = [];
42  let dir = String(cwd).replace(/[\\/]+$/, '');
43  for (let i = 0; i < limit && dir; i += 1) {
44    out.push(`${dir}/projects`);
45    const cut = Math.max(dir.lastIndexOf('/'), dir.lastIndexOf('\\'));
46    if (cut <= 0) {
47      if (cut === 0 && dir.length > 1) out.push('/projects');
48      break;
49    }
50    dir = dir.slice(0, cut);
51    if (/^[A-Za-z]:$/.test(dir)) {
52      out.push(`${dir}/projects`);
53      break;
54    }
55  }
56  return out;
57}
58
59function dateOnRow(lines, label) {
60  const row = lines.find((line) => label.test(line));
61  const match = row && row.match(ISO_DATE);
62  return match ? match[0] : null;
63}
64
65/**
66 * The Document Control facts the band counts. Status is read only from a row
67 * labelled exactly "Status" (bold allowed), so an entity table further down
68 * the file cannot be mistaken for it.
69 */
70export function documentFacts(text) {
71  const lines = String(text).split('\n');
72  const statusRow = lines.find((line) => /^\|\s*\*{0,2}\s*Status\s*\*{0,2}\s*\|/i.test(line));
73  const statusMatch = statusRow && statusRow.match(STATUSES);
74  return {
75    status: statusMatch ? statusMatch[1].toUpperCase() : null,
76    nextReview: dateOnRow(lines, /Next Review Date/i),
77    lastModified: dateOnRow(lines, /Last Modified/i),
78  };
79}
80
81export function daysBefore(isoDate, days) {
82  const [y, m, d] = isoDate.split('-').map(Number);
83  const at = new Date(Date.UTC(y, m - 1, d - days));
84  return at.toISOString().slice(0, 10);
85}
86
87/**
88 * @param {string[]} projectNames the NNN-name directories under projects/
89 * @param {string[]} texts each artefact's content
90 * @param {string} today YYYY-MM-DD
91 */
92export function summarise(projectNames, texts, today) {
93  const staleBefore = daysBefore(today, STALE_DRAFT_DAYS);
94  const summary = { projects: projectNames.length, artefacts: texts.length, draft: 0, staleDraft: 0, overdue: 0 };
95  for (const text of texts) {
96    const facts = documentFacts(text);
97    if (facts.nextReview && facts.nextReview < today) {
98      summary.overdue += 1;
99    }
100    if (facts.status === 'DRAFT') {
101      summary.draft += 1;
102      if (facts.lastModified && facts.lastModified < staleBefore) {
103        summary.staleDraft += 1;
104      }
105    }
106  }
107  return summary;
108}
109
110export function needsAttention(summary) {
111  return summary.overdue > 0 || summary.staleDraft > 0;
112}
113
114function plural(count, one, many = `${one}s`) {
115  return `${count} ${count === 1 ? one : many}`;
116}
117
118export function bandText(summary) {
119  const parts = [
120    'ArcKit',
121    plural(summary.projects, 'project'),
122    plural(summary.artefacts, 'artefact'),
123  ];
124  if (summary.draft > 0) {
125    parts.push(summary.staleDraft > 0 ? `${summary.draft} draft (${summary.staleDraft} stale)` : `${summary.draft} draft`);
126  }
127  if (summary.overdue > 0) {
128    parts.push(`${plural(summary.overdue, 'review')} overdue`);
129  }
130  const line = parts.join(' · ');
131  return needsAttention(summary) ? `${line} — run /arckit:health` : line;
132}
133