attacca-forge 0.5.0 → 0.5.1

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.
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
3
+ "name": "attacca-forge",
4
+ "description": "Open-source AI agent development toolkit: spec writing, evaluation, intent engineering, and build orchestration for production-grade agents",
5
+ "owner": {
6
+ "name": "Attacca"
7
+ },
8
+ "plugins": [
9
+ {
10
+ "name": "attacca-forge",
11
+ "description": "AI agent development methodology — design, evaluate, and align autonomous agents with production-grade specifications, factorial stress testing, and intent engineering",
12
+ "version": "0.5.1",
13
+ "author": {
14
+ "name": "Attacca"
15
+ },
16
+ "source": "./plugins/attacca-forge",
17
+ "category": "development"
18
+ }
19
+ ]
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "attacca-forge",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Spec-driven AI development toolkit — design, evaluate, stress-test, and certify autonomous agents from your terminal",
5
5
  "keywords": [
6
6
  "ai",
@@ -33,6 +33,7 @@
33
33
  "bin/",
34
34
  "src/",
35
35
  "plugins/",
36
+ ".claude-plugin/",
36
37
  "docs/",
37
38
  "examples/",
38
39
  "LICENSE",
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
3
+ "name": "attacca-forge",
4
+ "description": "Attacca Forge — AI agent development methodology: design, evaluate, align, and orchestrate autonomous agents",
5
+ "owner": {
6
+ "name": "Attacca"
7
+ },
8
+ "plugins": [
9
+ {
10
+ "name": "attacca-forge",
11
+ "description": "AI agent development methodology — design, evaluate, and align autonomous agents. 26 skills across spec writing, stress testing, intent engineering, and build orchestration.",
12
+ "version": "0.5.1",
13
+ "author": {
14
+ "name": "Attacca"
15
+ },
16
+ "source": ".",
17
+ "category": "development"
18
+ }
19
+ ]
20
+ }
@@ -2,10 +2,90 @@
2
2
  // attacca-forge install — Install skills into Claude Code plugin directory
3
3
  // =============================================================================
4
4
 
5
- import { cpSync, existsSync, mkdirSync } from 'node:fs';
5
+ import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
6
6
  import { join } from 'node:path';
7
7
  import { getClaudeDir, getPluginDir, isClaudeInstalled } from '../utils/detect-claude.js';
8
8
 
9
+ const MARKETPLACE_NAME = 'attacca-forge';
10
+ const PLUGIN_NAME = 'attacca-forge';
11
+
12
+ function readJsonFile(path) {
13
+ try {
14
+ return JSON.parse(readFileSync(path, 'utf-8'));
15
+ } catch {
16
+ return null;
17
+ }
18
+ }
19
+
20
+ function writeJsonFile(path, data) {
21
+ writeFileSync(path, JSON.stringify(data, null, 2) + '\n', 'utf-8');
22
+ }
23
+
24
+ function registerMarketplace(claudeDir, targetDir) {
25
+ const marketplacesPath = join(claudeDir, 'plugins', 'known_marketplaces.json');
26
+ const marketplaces = readJsonFile(marketplacesPath) || {};
27
+
28
+ if (marketplaces[MARKETPLACE_NAME]) {
29
+ // Update timestamp
30
+ marketplaces[MARKETPLACE_NAME].lastUpdated = new Date().toISOString();
31
+ } else {
32
+ marketplaces[MARKETPLACE_NAME] = {
33
+ source: {
34
+ source: 'directory',
35
+ path: targetDir,
36
+ },
37
+ installLocation: targetDir,
38
+ lastUpdated: new Date().toISOString(),
39
+ };
40
+ }
41
+
42
+ writeJsonFile(marketplacesPath, marketplaces);
43
+ return true;
44
+ }
45
+
46
+ function registerPlugin(claudeDir, targetDir, version) {
47
+ const installedPath = join(claudeDir, 'plugins', 'installed_plugins.json');
48
+ const installed = readJsonFile(installedPath) || { version: 2, plugins: {} };
49
+
50
+ // Ensure version 2 format
51
+ if (!installed.version) installed.version = 2;
52
+ if (!installed.plugins) installed.plugins = {};
53
+
54
+ const pluginKey = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
55
+ const now = new Date().toISOString();
56
+
57
+ // Set up cache directory
58
+ const cachePath = join(claudeDir, 'plugins', 'cache', MARKETPLACE_NAME, PLUGIN_NAME, version);
59
+ if (!existsSync(cachePath)) mkdirSync(cachePath, { recursive: true });
60
+
61
+ // Copy skills to cache
62
+ const sourceSkills = join(targetDir, 'skills');
63
+ const cacheSkills = join(cachePath, 'skills');
64
+ if (existsSync(sourceSkills)) {
65
+ cpSync(sourceSkills, cacheSkills, { recursive: true, force: true });
66
+ }
67
+
68
+ if (installed.plugins[pluginKey]) {
69
+ // Update existing entry
70
+ installed.plugins[pluginKey][0].version = version;
71
+ installed.plugins[pluginKey][0].installPath = cachePath;
72
+ installed.plugins[pluginKey][0].lastUpdated = now;
73
+ } else {
74
+ installed.plugins[pluginKey] = [
75
+ {
76
+ scope: 'user',
77
+ installPath: cachePath,
78
+ version,
79
+ installedAt: now,
80
+ lastUpdated: now,
81
+ },
82
+ ];
83
+ }
84
+
85
+ writeJsonFile(installedPath, installed);
86
+ return true;
87
+ }
88
+
9
89
  export default async function install({ args, cwd, rootDir }) {
10
90
  console.log('\n Attacca Forge — Install Skills');
11
91
  console.log(' ===============================\n');
@@ -29,37 +109,56 @@ export default async function install({ args, cwd, rootDir }) {
29
109
  process.exit(1);
30
110
  }
31
111
 
112
+ // Read version from package.json
113
+ const pkg = readJsonFile(join(rootDir, 'package.json'));
114
+ const version = pkg?.version || '0.5.1';
115
+
32
116
  // Target plugin directory
117
+ const claudeDir = getClaudeDir();
33
118
  const targetDir = getPluginDir();
34
- const localDir = join(getClaudeDir(), 'plugins', 'local');
119
+ const localDir = join(claudeDir, 'plugins', 'local');
35
120
 
36
121
  // Create directories
37
122
  if (!existsSync(localDir)) mkdirSync(localDir, { recursive: true });
38
123
 
39
- // Copy plugin files
124
+ // Copy .claude-plugin/ and skills/
40
125
  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 });
126
+ if (!existsSync(targetDir)) mkdirSync(targetDir, { recursive: true });
127
+
128
+ const sourceClaudePlugin = join(sourcePlugin, '.claude-plugin');
129
+ const sourceSkills = join(sourcePlugin, 'skills');
130
+ const targetClaudePlugin = join(targetDir, '.claude-plugin');
131
+ const targetSkills = join(targetDir, 'skills');
132
+
133
+ if (existsSync(sourceClaudePlugin)) {
134
+ cpSync(sourceClaudePlugin, targetClaudePlugin, { recursive: true, force: true });
135
+ }
136
+ if (existsSync(sourceSkills)) {
137
+ cpSync(sourceSkills, targetSkills, { recursive: true, force: true });
49
138
  }
50
139
 
51
140
  // Count skills
52
- const skillsDir = join(targetDir, 'skills');
53
141
  let skillCount = 0;
54
- if (existsSync(skillsDir)) {
142
+ if (existsSync(targetSkills)) {
55
143
  const { readdirSync } = await import('node:fs');
56
- skillCount = readdirSync(skillsDir).filter((f) => {
57
- return existsSync(join(skillsDir, f, 'SKILL.md'));
144
+ skillCount = readdirSync(targetSkills).filter((f) => {
145
+ return existsSync(join(targetSkills, f, 'SKILL.md'));
58
146
  }).length;
59
147
  }
60
148
 
61
149
  console.log(` ✓ ${existed ? 'Updated' : 'Installed'} ${skillCount} skills to Claude Code`);
62
150
  console.log(` Location: ${targetDir}`);
151
+
152
+ // Register marketplace and plugin
153
+ const pluginsDir = join(claudeDir, 'plugins');
154
+ if (!existsSync(pluginsDir)) mkdirSync(pluginsDir, { recursive: true });
155
+
156
+ registerMarketplace(claudeDir, targetDir);
157
+ console.log(' ✓ Marketplace registered');
158
+
159
+ registerPlugin(claudeDir, targetDir, version);
160
+ console.log(' ✓ Plugin registered');
161
+
63
162
  console.log('');
64
163
  console.log(' Restart Claude Code to load the new skills.');
65
164
  console.log('');