attacca-forge 0.5.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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +159 -0
  3. package/bin/cli.js +79 -0
  4. package/docs/architecture.md +132 -0
  5. package/docs/getting-started.md +137 -0
  6. package/docs/methodology/factorial-stress-testing.md +64 -0
  7. package/docs/methodology/failure-modes.md +82 -0
  8. package/docs/methodology/intent-engineering.md +78 -0
  9. package/docs/methodology/progressive-autonomy.md +92 -0
  10. package/docs/methodology/spec-driven-development.md +52 -0
  11. package/docs/methodology/trust-tiers.md +52 -0
  12. package/examples/stress-test-matrix.md +98 -0
  13. package/examples/tier-2-saas-spec.md +142 -0
  14. package/package.json +44 -0
  15. package/plugins/attacca-forge/.claude-plugin/plugin.json +7 -0
  16. package/plugins/attacca-forge/skills/agent-economics-analyzer/SKILL.md +90 -0
  17. package/plugins/attacca-forge/skills/agent-readiness-audit/SKILL.md +90 -0
  18. package/plugins/attacca-forge/skills/agent-stack-opportunity-mapper/SKILL.md +93 -0
  19. package/plugins/attacca-forge/skills/ai-dev-level-assessment/SKILL.md +112 -0
  20. package/plugins/attacca-forge/skills/ai-dev-talent-strategy/SKILL.md +154 -0
  21. package/plugins/attacca-forge/skills/ai-difficulty-rapid-audit/SKILL.md +121 -0
  22. package/plugins/attacca-forge/skills/ai-native-org-redesign/SKILL.md +114 -0
  23. package/plugins/attacca-forge/skills/ai-output-taste-builder/SKILL.md +116 -0
  24. package/plugins/attacca-forge/skills/ai-workflow-capability-map/SKILL.md +98 -0
  25. package/plugins/attacca-forge/skills/ai-workflow-optimizer/SKILL.md +131 -0
  26. package/plugins/attacca-forge/skills/build-orchestrator/SKILL.md +320 -0
  27. package/plugins/attacca-forge/skills/codebase-discovery/SKILL.md +286 -0
  28. package/plugins/attacca-forge/skills/forge-help/SKILL.md +100 -0
  29. package/plugins/attacca-forge/skills/forge-start/SKILL.md +110 -0
  30. package/plugins/attacca-forge/skills/harness-simulator/SKILL.md +137 -0
  31. package/plugins/attacca-forge/skills/insight-to-action-compression-map/SKILL.md +134 -0
  32. package/plugins/attacca-forge/skills/intent-audit/SKILL.md +144 -0
  33. package/plugins/attacca-forge/skills/intent-gap-diagnostic/SKILL.md +63 -0
  34. package/plugins/attacca-forge/skills/intent-spec/SKILL.md +170 -0
  35. package/plugins/attacca-forge/skills/legacy-migration-roadmap/SKILL.md +126 -0
  36. package/plugins/attacca-forge/skills/personal-intent-layer-builder/SKILL.md +80 -0
  37. package/plugins/attacca-forge/skills/problem-difficulty-decomposition/SKILL.md +128 -0
  38. package/plugins/attacca-forge/skills/spec-architect/SKILL.md +210 -0
  39. package/plugins/attacca-forge/skills/spec-writer/SKILL.md +145 -0
  40. package/plugins/attacca-forge/skills/stress-test/SKILL.md +283 -0
  41. package/plugins/attacca-forge/skills/web-fork-strategic-briefing/SKILL.md +66 -0
  42. package/src/commands/help.js +44 -0
  43. package/src/commands/init.js +121 -0
  44. package/src/commands/install.js +77 -0
  45. package/src/commands/status.js +87 -0
  46. package/src/utils/context.js +141 -0
  47. package/src/utils/detect-claude.js +23 -0
  48. package/src/utils/prompt.js +44 -0
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: web-fork-strategic-briefing
3
+ description: >
4
+ Produces a concise strategic briefing for leadership on how the agent web fork affects a
5
+ specific industry, company, or competitive landscape. Written in the language of business
6
+ strategy, not tech hype. Evidence-based and actionable. Triggers: "write an agent web
7
+ briefing", "strategic briefing on AI agents for my industry", "brief leadership on agent
8
+ web", "executive briefing on agent infrastructure", "how does the agent web affect my
9
+ industry", "web fork impact analysis", "agent strategy briefing for executives",
10
+ "board briefing on AI agents".
11
+ ---
12
+
13
+ # Web Fork Strategic Briefing Generator
14
+
15
+ ## Role
16
+
17
+ You are a strategic advisor who translates technology infrastructure shifts into business strategy. You understand the agent web fork — the emergence of a parallel web layer built for software clients (AI agents) alongside the existing human web — and can explain its implications for specific industries without resorting to jargon or hype. You write for senior leaders who are skeptical of tech trends but need to understand when an infrastructure shift is real. Your tone is direct, evidence-grounded, and actionable. You cite specific companies, protocols, and data points rather than making vague claims about "the future of AI."
18
+
19
+ ## Instructions
20
+
21
+ 1. CONTEXT GATHERING — Ask the user these questions:
22
+
23
+ a) "What industry are you in, and what's your company's role in it? (e.g., 'mid-market SaaS in healthcare,' 'DTC e-commerce in fashion,' 'B2B financial services')"
24
+ b) "Who is this briefing for? (e.g., CEO, board of directors, investment committee, product leadership team) — and what decision does it need to inform? (e.g., 'whether to invest in agent-ready infrastructure this year,' 'how to respond to a competitor's agent strategy,' 'whether this is real or hype')"
25
+ c) "What's your audience's current understanding of AI agents? (e.g., 'they've seen ChatGPT but don't understand agents,' 'they're technically sophisticated,' 'they're skeptical of AI hype after previous overpromises')"
26
+ d) "Are there any specific competitive threats or opportunities you're already aware of that I should address? (e.g., 'a competitor just launched an API for agent access,' 'our customers are asking about AI purchasing')"
27
+
28
+ 2. BRIEFING CONSTRUCTION — Build the briefing with these sections:
29
+
30
+ **EXECUTIVE SUMMARY** (3-4 sentences): What's happening, why it matters for this industry, and the one thing the reader needs to understand.
31
+
32
+ **WHAT'S HAPPENING** (1 page max): The agent web fork explained for this specific audience. Use the mobile web analogy from the article — the audience will understand it. Reference specific infrastructure moves (Coinbase Agentic Wallets, Stripe ACS, Cloudflare Markdown for Agents, OpenAI Skills/Shell) but explain them in terms of what they enable, not how they work technically. Ground claims in data: 13,000 agent wallets registered in 24 hours, $12B in Polymarket volume, Stripe retraining Radar from scratch.
33
+
34
+ **INDUSTRY IMPACT** (1 page): How specifically this affects the user's industry. Map each infrastructure layer to a concrete industry implication:
35
+ - How agent payments change purchasing in this industry
36
+ - How agent-readable content changes discovery and access
37
+ - How agent search changes competitive dynamics
38
+ - How agent execution changes service delivery
39
+ - How agent economics change the cost structure
40
+
41
+ **COMPETITIVE IMPLICATIONS**: Who benefits, who's threatened, what new entrants become possible. Use the article's framework: businesses that couldn't exist on the human web (like Uber couldn't exist on the desktop web) will emerge on the agent web. What do those businesses look like in this industry?
42
+
43
+ **THE HONEST ASSESSMENT**: Where on the hype-to-reality spectrum is this for the user's specific industry? Use the domain accuracy framework: is this industry in the high-accuracy zone (structured, data-driven — agents will impact it fast) or the low-accuracy zone (cultural, aesthetic — agents will take much longer)? What's the realistic timeline?
44
+
45
+ **RECOMMENDED POSTURE**: One of four strategic postures with specific actions:
46
+ - **Lead**: Build agent-native infrastructure now. First-mover advantage is real.
47
+ - **Fast follow**: Monitor, prepare technical foundation, deploy when standards stabilize.
48
+ - **Selective engagement**: Automate specific high-accuracy subtasks, keep core human-driven.
49
+ - **Watch and wait**: This doesn't affect your industry yet. Revisit in 12-18 months.
50
+
51
+ **THREE THINGS TO DO THIS QUARTER**: Regardless of posture, three specific actions.
52
+
53
+ ## Output
54
+
55
+ Format as a clean strategic briefing document. Use headers, short paragraphs, and bullet points. No more than 3 pages equivalent. Include a one-paragraph "Bottom Line" at the very top that a busy executive can read in 30 seconds and get the key message.
56
+
57
+ Every claim must be grounded in a specific reference point (company, data point, protocol, or market event). No sentences like "AI is transforming everything" or "the future is autonomous." Instead: "Stripe rebuilt its entire fraud detection system because agent traffic doesn't exhibit human behavioral signals — that's a $50B+ company acknowledging that agent clients are fundamentally different from human clients."
58
+
59
+ ## Guardrails
60
+
61
+ - Match the language and framing to the audience the user described. A board briefing for a healthcare company reads very differently from a product strategy doc for a DTC brand.
62
+ - If the user's industry is in a low-accuracy domain for agents (creative, cultural, relationship-driven), do NOT oversell the impact. The honest answer might be "this matters less for you than the headlines suggest, but here's the narrow slice where it does matter."
63
+ - Do not use the word "transformative," "revolutionary," "paradigm shift," or "game-changing." Show the impact through specifics, not adjectives.
64
+ - Include the security dimension. Every briefing should acknowledge that the same infrastructure that enables agent capability also enables agent-driven attacks, fraud, and failure modes. Leaders need to understand both sides.
65
+ - If you don't have enough information about the user's industry to make specific claims, ask follow-up questions rather than generating vague generalities.
66
+ - Distinguish between what is live in production today versus what is announced/planned/theoretical. Label each clearly.
@@ -0,0 +1,44 @@
1
+ // =============================================================================
2
+ // attacca-forge help — CLI help (delegates to forge-help skill in Claude Code)
3
+ // =============================================================================
4
+
5
+ export default async function help({ cwd }) {
6
+ console.log(`
7
+ Attacca Forge — Spec-Driven AI Development Toolkit
8
+ ====================================================
9
+
10
+ Commands:
11
+ init Set up a new project (.attacca/ config + context)
12
+ install Install skills into Claude Code plugin directory
13
+ status Show current pipeline phase, artifacts, and next steps
14
+ help Show this help message
15
+
16
+ The 8-Phase Pipeline:
17
+ IDEA Capture intent, classify trust tier
18
+ DISCOVER Map existing codebase (brownfield only)
19
+ SPEC Write behavioral specification + intent contract
20
+ BUILD Execute implementation on deterministic rails
21
+ TEST Factorial stress testing against scenarios
22
+ CERTIFY Human sign-off (tier-appropriate)
23
+ DEPLOY Production deployment with gates
24
+ MAINTAIN Continuous flywheel + drift detection
25
+
26
+ Core Skills (invoke in Claude Code):
27
+ /spec-architect Full spec with intent contracts + eval scenarios
28
+ /spec-writer Streamlined spec (no intent, faster)
29
+ /stress-test 22 variation types × 4 failure modes
30
+ /intent-spec Agent intent specification + Klarna checklist
31
+ /intent-audit Organizational AI maturity audit
32
+ /codebase-discovery Brownfield behavioral snapshot
33
+ /build-orchestrator Build pipeline with 4-layer eval stack
34
+ /forge-help "What should I do next?" (phase-aware)
35
+ /forge-start IDEA phase onboarding
36
+
37
+ Getting Started:
38
+ 1. npx attacca-forge init Set up your project
39
+ 2. npx attacca-forge install Install skills to Claude Code
40
+ 3. Open Claude Code and say: "help me start"
41
+
42
+ Docs: https://github.com/attacca-ai/attacca-forge
43
+ `);
44
+ }
@@ -0,0 +1,121 @@
1
+ // =============================================================================
2
+ // attacca-forge init — Interactive project setup wizard
3
+ // =============================================================================
4
+
5
+ import { basename } from 'node:path';
6
+ import { ask, choose } from '../utils/prompt.js';
7
+ import { isInitialized, writeConfig, writeContext } from '../utils/context.js';
8
+
9
+ export default async function init({ args, cwd }) {
10
+ console.log('\n Attacca Forge — Project Setup');
11
+ console.log(' ==============================\n');
12
+
13
+ // Check if already initialized
14
+ if (isInitialized(cwd)) {
15
+ const proceed = await ask('Project already initialized. Reinitialize? (y/N)', 'N');
16
+ if (proceed.toLowerCase() !== 'y') {
17
+ console.log('\n Aborted.\n');
18
+ return;
19
+ }
20
+ }
21
+
22
+ // 1. Project name
23
+ const defaultName = args[0] || basename(cwd);
24
+ const name = await ask('Project name', defaultName);
25
+
26
+ // 2. Greenfield or Brownfield
27
+ const type = await choose('What kind of project?', [
28
+ {
29
+ label: 'Greenfield — building from scratch',
30
+ hint: 'No existing codebase. Starting from an idea.',
31
+ value: 'greenfield',
32
+ },
33
+ {
34
+ label: 'Brownfield — changing existing code',
35
+ hint: 'There is a codebase. You need to modify or extend it.',
36
+ value: 'brownfield',
37
+ },
38
+ ]);
39
+
40
+ // 3. Trust Tier
41
+ const tier = await choose('What happens if this system gets it wrong?', [
42
+ {
43
+ label: 'Tier 1 — Nothing bad happens',
44
+ hint: 'Hobby project, internal tool, prototype. Errors are annoying, not harmful.',
45
+ value: '1',
46
+ },
47
+ {
48
+ label: 'Tier 2 — We lose time or money',
49
+ hint: 'SaaS product, client deliverable, business tool. Errors cost real resources.',
50
+ value: '2',
51
+ },
52
+ {
53
+ label: 'Tier 3 — Legal, financial, or reputational risk',
54
+ hint: 'Compliance, financial decisions, public-facing. Errors have serious consequences.',
55
+ value: '3',
56
+ },
57
+ {
58
+ label: 'Tier 4 — Irreversible harm',
59
+ hint: 'Healthcare, safety-critical, regulatory. Errors can cause physical or legal harm.',
60
+ value: '4',
61
+ },
62
+ ]);
63
+
64
+ // 4. Experience level
65
+ const level = await choose('Your experience with AI-assisted development?', [
66
+ {
67
+ label: 'New — explain everything',
68
+ hint: 'First time using AI for development. Show me the ropes.',
69
+ value: 'new',
70
+ },
71
+ {
72
+ label: 'Comfortable — explain decisions, not basics',
73
+ hint: 'Used AI tools before. Know the workflow. Want methodology guidance.',
74
+ value: 'comfortable',
75
+ },
76
+ {
77
+ label: 'Expert — terse, just the framework',
78
+ hint: 'Deep experience. Skip explanations. Give me the structure.',
79
+ value: 'expert',
80
+ },
81
+ ]);
82
+
83
+ // Write config
84
+ const now = new Date().toISOString().split('T')[0];
85
+ const config = {
86
+ project: { name, type, tier, level, created: now },
87
+ phase: { current: 'IDEA' },
88
+ artifacts: { completed: [] },
89
+ };
90
+
91
+ writeConfig(cwd, config);
92
+
93
+ // Write initial context
94
+ const nextStep = type === 'brownfield'
95
+ ? 'Start with codebase discovery to map the existing system.\nInvoke: "discover this codebase" or /codebase-discovery'
96
+ : 'Start with the IDEA phase to capture your intent.\nInvoke: "help me start" or /forge-start';
97
+
98
+ writeContext(cwd, config, 'IDEA', [], [], nextStep);
99
+
100
+ // Summary
101
+ console.log('\n ✓ Project initialized\n');
102
+ console.log(` Name: ${name}`);
103
+ console.log(` Type: ${type}`);
104
+ console.log(` Trust Tier: ${tier}`);
105
+ console.log(` Level: ${level}`);
106
+ console.log(` Config: .attacca/config.yaml`);
107
+ console.log(` Context: .attacca/context.md`);
108
+ console.log('');
109
+
110
+ if (type === 'brownfield') {
111
+ console.log(' Next step: Run codebase discovery');
112
+ console.log(' In Claude Code, say: "discover this codebase" or /codebase-discovery');
113
+ } else {
114
+ console.log(' Next step: Capture your idea');
115
+ console.log(' In Claude Code, say: "help me start" or /forge-start');
116
+ }
117
+
118
+ console.log('');
119
+ console.log(' Run `attacca-forge status` anytime to see where you are.');
120
+ console.log('');
121
+ }
@@ -0,0 +1,77 @@
1
+ // =============================================================================
2
+ // attacca-forge install — Install skills into Claude Code plugin directory
3
+ // =============================================================================
4
+
5
+ import { cpSync, existsSync, mkdirSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import { getClaudeDir, getPluginDir, isClaudeInstalled } from '../utils/detect-claude.js';
8
+
9
+ export default async function install({ args, cwd, rootDir }) {
10
+ console.log('\n Attacca Forge — Install Skills');
11
+ console.log(' ===============================\n');
12
+
13
+ // Check Claude Code
14
+ if (!isClaudeInstalled()) {
15
+ console.log(' ✗ Claude Code not found (~/.claude directory missing)');
16
+ console.log('');
17
+ console.log(' Install Claude Code first: https://claude.ai/claude-code');
18
+ console.log(' Then run this command again.');
19
+ console.log('');
20
+ process.exit(1);
21
+ }
22
+
23
+ console.log(' ✓ Claude Code detected');
24
+
25
+ // Source plugin directory (shipped with this package)
26
+ const sourcePlugin = join(rootDir, 'plugins', 'attacca-forge');
27
+ if (!existsSync(sourcePlugin)) {
28
+ console.error(' ✗ Plugin source not found. Package may be corrupted.');
29
+ process.exit(1);
30
+ }
31
+
32
+ // Target plugin directory
33
+ const targetDir = getPluginDir();
34
+ const localDir = join(getClaudeDir(), 'plugins', 'local');
35
+
36
+ // Create directories
37
+ if (!existsSync(localDir)) mkdirSync(localDir, { recursive: true });
38
+
39
+ // Copy plugin files
40
+ const existed = existsSync(targetDir);
41
+ cpSync(sourcePlugin, targetDir, { recursive: true, force: true });
42
+
43
+ // Also copy root marketplace plugin.json
44
+ const rootPluginJson = join(rootDir, '.claude-plugin');
45
+ const targetRootPlugin = join(getClaudeDir(), 'plugins', 'local', 'attacca-forge-root', '.claude-plugin');
46
+ if (existsSync(rootPluginJson)) {
47
+ mkdirSync(join(getClaudeDir(), 'plugins', 'local', 'attacca-forge-root'), { recursive: true });
48
+ cpSync(rootPluginJson, targetRootPlugin, { recursive: true, force: true });
49
+ }
50
+
51
+ // Count skills
52
+ const skillsDir = join(targetDir, 'skills');
53
+ let skillCount = 0;
54
+ if (existsSync(skillsDir)) {
55
+ const { readdirSync } = await import('node:fs');
56
+ skillCount = readdirSync(skillsDir).filter((f) => {
57
+ return existsSync(join(skillsDir, f, 'SKILL.md'));
58
+ }).length;
59
+ }
60
+
61
+ console.log(` ✓ ${existed ? 'Updated' : 'Installed'} ${skillCount} skills to Claude Code`);
62
+ console.log(` Location: ${targetDir}`);
63
+ console.log('');
64
+ console.log(' Restart Claude Code to load the new skills.');
65
+ console.log('');
66
+ console.log(' Available skills:');
67
+ console.log(' /spec-architect Full behavioral spec with intent contracts');
68
+ console.log(' /spec-writer Streamlined spec (no intent layer)');
69
+ console.log(' /stress-test Factorial stress testing (22 variation types)');
70
+ console.log(' /intent-spec Agent intent specification');
71
+ console.log(' /intent-audit Organizational AI maturity audit');
72
+ console.log(' /codebase-discovery Brownfield codebase discovery');
73
+ console.log(' /build-orchestrator Build pipeline with 4-layer eval stack');
74
+ console.log(' /forge-help Phase-aware guidance ("what\'s next?")');
75
+ console.log(' /forge-start IDEA phase onboarding');
76
+ console.log('');
77
+ }
@@ -0,0 +1,87 @@
1
+ // =============================================================================
2
+ // attacca-forge status — Show current pipeline phase and next steps
3
+ // =============================================================================
4
+
5
+ import { readConfig, readContext, isInitialized } from '../utils/context.js';
6
+
7
+ const PIPELINE = ['IDEA', 'DISCOVER', 'SPEC', 'BUILD', 'TEST', 'CERTIFY', 'DEPLOY', 'MAINTAIN'];
8
+
9
+ const PHASE_SKILLS = {
10
+ IDEA: { skill: 'forge-start', desc: 'Capture your idea and classify risk' },
11
+ DISCOVER: { skill: 'codebase-discovery', desc: 'Map the existing codebase (brownfield only)' },
12
+ SPEC: { skill: 'spec-architect', desc: 'Write behavioral specification with intent contract' },
13
+ BUILD: { skill: 'build-orchestrator', desc: 'Execute implementation on deterministic rails' },
14
+ TEST: { skill: 'stress-test', desc: 'Run factorial stress testing against scenarios' },
15
+ CERTIFY: { skill: null, desc: 'Human sign-off (tier-appropriate review)' },
16
+ DEPLOY: { skill: 'build-orchestrator', desc: 'Production deployment with gates' },
17
+ MAINTAIN: { skill: null, desc: 'Continuous flywheel + drift detection' },
18
+ };
19
+
20
+ export default async function status({ cwd }) {
21
+ console.log('\n Attacca Forge — Project Status');
22
+ console.log(' ===============================\n');
23
+
24
+ if (!isInitialized(cwd)) {
25
+ console.log(' No project initialized in this directory.');
26
+ console.log(' Run: npx attacca-forge init\n');
27
+ return;
28
+ }
29
+
30
+ const config = readConfig(cwd);
31
+ if (!config) {
32
+ console.log(' Config file corrupted. Reinitialize with: npx attacca-forge init\n');
33
+ return;
34
+ }
35
+
36
+ const { project, phase, artifacts } = config;
37
+ const currentPhase = phase?.current || 'IDEA';
38
+ const currentIdx = PIPELINE.indexOf(currentPhase);
39
+
40
+ console.log(` Project: ${project.name}`);
41
+ console.log(` Type: ${project.type}`);
42
+ console.log(` Trust Tier: ${project.tier}`);
43
+ console.log(` Level: ${project.level}`);
44
+ console.log('');
45
+
46
+ // Pipeline visualization
47
+ console.log(' Pipeline:');
48
+ console.log('');
49
+ for (let i = 0; i < PIPELINE.length; i++) {
50
+ const p = PIPELINE[i];
51
+ const info = PHASE_SKILLS[p];
52
+
53
+ // Skip DISCOVER for greenfield
54
+ if (p === 'DISCOVER' && project.type === 'greenfield') continue;
55
+
56
+ let marker;
57
+ if (i < currentIdx) marker = ' ✓';
58
+ else if (i === currentIdx) marker = ' ▸';
59
+ else marker = ' ';
60
+
61
+ const line = `${marker} ${p.padEnd(10)} ${info.desc}`;
62
+ console.log(line);
63
+ }
64
+
65
+ console.log('');
66
+
67
+ // Next step
68
+ const phaseInfo = PHASE_SKILLS[currentPhase];
69
+ if (phaseInfo?.skill) {
70
+ console.log(` Next: /${phaseInfo.skill}`);
71
+ console.log(` ${phaseInfo.desc}`);
72
+ } else if (currentPhase === 'CERTIFY') {
73
+ console.log(` Next: Human review required (Tier ${project.tier})`);
74
+ } else if (currentPhase === 'MAINTAIN') {
75
+ console.log(' System is in production. Monitoring for drift.');
76
+ }
77
+
78
+ // Artifacts
79
+ const completed = artifacts?.completed || [];
80
+ if (completed.length > 0) {
81
+ console.log('');
82
+ console.log(' Artifacts:');
83
+ completed.forEach((a) => console.log(` - ${a}`));
84
+ }
85
+
86
+ console.log('');
87
+ }
@@ -0,0 +1,141 @@
1
+ // =============================================================================
2
+ // .attacca/ context management — read/write project state
3
+ // =============================================================================
4
+
5
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+
8
+ const ATTACCA_DIR = '.attacca';
9
+ const CONFIG_FILE = 'config.yaml';
10
+ const CONTEXT_FILE = 'context.md';
11
+
12
+ export function getAttaccaDir(cwd) {
13
+ return join(cwd, ATTACCA_DIR);
14
+ }
15
+
16
+ export function isInitialized(cwd) {
17
+ return existsSync(join(cwd, ATTACCA_DIR, CONFIG_FILE));
18
+ }
19
+
20
+ export function ensureDir(cwd) {
21
+ const dir = getAttaccaDir(cwd);
22
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
23
+ const artifacts = join(dir, 'artifacts');
24
+ if (!existsSync(artifacts)) mkdirSync(artifacts, { recursive: true });
25
+ return dir;
26
+ }
27
+
28
+ export function writeConfig(cwd, config) {
29
+ const dir = ensureDir(cwd);
30
+ const yaml = configToYaml(config);
31
+ writeFileSync(join(dir, CONFIG_FILE), yaml, 'utf-8');
32
+ }
33
+
34
+ export function readConfig(cwd) {
35
+ const file = join(cwd, ATTACCA_DIR, CONFIG_FILE);
36
+ if (!existsSync(file)) return null;
37
+ return yamlToConfig(readFileSync(file, 'utf-8'));
38
+ }
39
+
40
+ export function writeContext(cwd, config, phase, completedPhases, artifacts, nextStep) {
41
+ const dir = ensureDir(cwd);
42
+ const now = new Date().toISOString().split('T')[0];
43
+
44
+ const phases = completedPhases
45
+ .map((p) => `- [x] ${p.name} (${p.date}) — ${p.summary}`)
46
+ .join('\n');
47
+
48
+ const arts = artifacts.length > 0
49
+ ? artifacts.map((a) => `- \`${a.path}\` — ${a.description}`).join('\n')
50
+ : '_None yet_';
51
+
52
+ const content = `# Project Context — ${config.project.name}
53
+
54
+ ## Current Phase: ${phase}
55
+ ## Trust Tier: ${config.project.tier}
56
+ ## Type: ${config.project.type}
57
+ ## Experience: ${config.project.level}
58
+
59
+ ## Completed Phases
60
+ ${phases || '_None yet — run forge-start to begin_'}
61
+
62
+ ## Artifacts Generated
63
+ ${arts}
64
+
65
+ ## Next Step
66
+ ${nextStep}
67
+ `;
68
+
69
+ writeFileSync(join(dir, CONTEXT_FILE), content, 'utf-8');
70
+ }
71
+
72
+ export function readContext(cwd) {
73
+ const file = join(cwd, ATTACCA_DIR, CONTEXT_FILE);
74
+ if (!existsSync(file)) return null;
75
+ return readFileSync(file, 'utf-8');
76
+ }
77
+
78
+ // Minimal YAML serializer (no dependencies)
79
+ function configToYaml(config) {
80
+ const lines = [];
81
+ for (const [section, values] of Object.entries(config)) {
82
+ lines.push(`${section}:`);
83
+ if (typeof values === 'object' && values !== null) {
84
+ for (const [key, val] of Object.entries(values)) {
85
+ if (Array.isArray(val)) {
86
+ lines.push(` ${key}:`);
87
+ val.forEach((item) => lines.push(` - ${item}`));
88
+ } else {
89
+ lines.push(` ${key}: ${val}`);
90
+ }
91
+ }
92
+ } else {
93
+ lines.push(` ${values}`);
94
+ }
95
+ }
96
+ return lines.join('\n') + '\n';
97
+ }
98
+
99
+ // Minimal YAML parser (handles our flat config only)
100
+ function yamlToConfig(yaml) {
101
+ const config = {};
102
+ let currentSection = null;
103
+ let currentKey = null;
104
+
105
+ for (const line of yaml.split('\n')) {
106
+ if (!line.trim() || line.trim().startsWith('#')) continue;
107
+
108
+ if (!line.startsWith(' ') && line.endsWith(':')) {
109
+ currentSection = line.slice(0, -1).trim();
110
+ config[currentSection] = {};
111
+ currentKey = null;
112
+ } else if (line.startsWith(' ') && !line.startsWith(' ')) {
113
+ const trimmed = line.trim();
114
+ if (trimmed.startsWith('- ')) {
115
+ if (currentKey && currentSection) {
116
+ if (!Array.isArray(config[currentSection][currentKey])) {
117
+ config[currentSection][currentKey] = [];
118
+ }
119
+ config[currentSection][currentKey].push(trimmed.slice(2));
120
+ }
121
+ } else if (trimmed.includes(': ')) {
122
+ const colonIdx = trimmed.indexOf(': ');
123
+ currentKey = trimmed.slice(0, colonIdx);
124
+ const val = trimmed.slice(colonIdx + 2);
125
+ if (currentSection) config[currentSection][currentKey] = val;
126
+ } else if (trimmed.endsWith(':')) {
127
+ currentKey = trimmed.slice(0, -1);
128
+ if (currentSection) config[currentSection][currentKey] = [];
129
+ }
130
+ } else if (line.startsWith(' ') && line.trim().startsWith('- ')) {
131
+ if (currentKey && currentSection) {
132
+ if (!Array.isArray(config[currentSection][currentKey])) {
133
+ config[currentSection][currentKey] = [];
134
+ }
135
+ config[currentSection][currentKey].push(line.trim().slice(2));
136
+ }
137
+ }
138
+ }
139
+
140
+ return config;
141
+ }
@@ -0,0 +1,23 @@
1
+ // =============================================================================
2
+ // Detect Claude Code installation and plugin directories
3
+ // =============================================================================
4
+
5
+ import { existsSync } from 'node:fs';
6
+ import { join } from 'node:path';
7
+ import { homedir } from 'node:os';
8
+
9
+ export function getClaudeDir() {
10
+ return join(homedir(), '.claude');
11
+ }
12
+
13
+ export function getPluginDir() {
14
+ return join(getClaudeDir(), 'plugins', 'local', 'attacca-forge');
15
+ }
16
+
17
+ export function isClaudeInstalled() {
18
+ return existsSync(getClaudeDir());
19
+ }
20
+
21
+ export function isForgeInstalled() {
22
+ return existsSync(getPluginDir());
23
+ }
@@ -0,0 +1,44 @@
1
+ // =============================================================================
2
+ // Minimal interactive prompt utility — zero dependencies
3
+ // =============================================================================
4
+
5
+ import { createInterface } from 'node:readline';
6
+
7
+ const rl = () =>
8
+ createInterface({ input: process.stdin, output: process.stdout });
9
+
10
+ export async function ask(question, defaultValue) {
11
+ const r = rl();
12
+ const suffix = defaultValue ? ` (${defaultValue})` : '';
13
+ return new Promise((resolve) => {
14
+ r.question(` ${question}${suffix}: `, (answer) => {
15
+ r.close();
16
+ resolve(answer.trim() || defaultValue || '');
17
+ });
18
+ });
19
+ }
20
+
21
+ export async function choose(question, options) {
22
+ console.log(`\n ${question}\n`);
23
+ options.forEach((opt, i) => {
24
+ console.log(` ${i + 1}. ${opt.label}`);
25
+ if (opt.hint) console.log(` ${opt.hint}`);
26
+ });
27
+ console.log('');
28
+
29
+ const r = rl();
30
+ return new Promise((resolve) => {
31
+ const prompt = () => {
32
+ r.question(` Choose [1-${options.length}]: `, (answer) => {
33
+ const idx = parseInt(answer, 10) - 1;
34
+ if (idx >= 0 && idx < options.length) {
35
+ r.close();
36
+ resolve(options[idx].value);
37
+ } else {
38
+ prompt();
39
+ }
40
+ });
41
+ };
42
+ prompt();
43
+ });
44
+ }