attacca-forge 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@
9
9
  {
10
10
  "name": "attacca-forge",
11
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",
12
+ "version": "0.5.2",
13
13
  "author": {
14
14
  "name": "Attacca"
15
15
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "attacca-forge",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
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",
@@ -2,7 +2,7 @@
2
2
  // attacca-forge install — Install skills into Claude Code plugin directory
3
3
  // =============================================================================
4
4
 
5
- import { cpSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
5
+ import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
6
6
  import { join } from 'node:path';
7
7
  import { getClaudeDir, getPluginDir, isClaudeInstalled } from '../utils/detect-claude.js';
8
8
 
@@ -26,7 +26,6 @@ function registerMarketplace(claudeDir, targetDir) {
26
26
  const marketplaces = readJsonFile(marketplacesPath) || {};
27
27
 
28
28
  if (marketplaces[MARKETPLACE_NAME]) {
29
- // Update timestamp
30
29
  marketplaces[MARKETPLACE_NAME].lastUpdated = new Date().toISOString();
31
30
  } else {
32
31
  marketplaces[MARKETPLACE_NAME] = {
@@ -43,30 +42,27 @@ function registerMarketplace(claudeDir, targetDir) {
43
42
  return true;
44
43
  }
45
44
 
46
- function registerPlugin(claudeDir, targetDir, version) {
45
+ function registerPlugin(claudeDir, pluginDir, version) {
47
46
  const installedPath = join(claudeDir, 'plugins', 'installed_plugins.json');
48
47
  const installed = readJsonFile(installedPath) || { version: 2, plugins: {} };
49
48
 
50
- // Ensure version 2 format
51
49
  if (!installed.version) installed.version = 2;
52
50
  if (!installed.plugins) installed.plugins = {};
53
51
 
54
52
  const pluginKey = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
55
53
  const now = new Date().toISOString();
56
54
 
57
- // Set up cache directory
55
+ // Set up cache directory and copy skills there
58
56
  const cachePath = join(claudeDir, 'plugins', 'cache', MARKETPLACE_NAME, PLUGIN_NAME, version);
59
57
  if (!existsSync(cachePath)) mkdirSync(cachePath, { recursive: true });
60
58
 
61
- // Copy skills to cache
62
- const sourceSkills = join(targetDir, 'skills');
59
+ const sourceSkills = join(pluginDir, 'skills');
63
60
  const cacheSkills = join(cachePath, 'skills');
64
61
  if (existsSync(sourceSkills)) {
65
62
  cpSync(sourceSkills, cacheSkills, { recursive: true, force: true });
66
63
  }
67
64
 
68
65
  if (installed.plugins[pluginKey]) {
69
- // Update existing entry
70
66
  installed.plugins[pluginKey][0].version = version;
71
67
  installed.plugins[pluginKey][0].installPath = cachePath;
72
68
  installed.plugins[pluginKey][0].lastUpdated = now;
@@ -102,8 +98,13 @@ export default async function install({ args, cwd, rootDir }) {
102
98
 
103
99
  console.log(' ✓ Claude Code detected');
104
100
 
105
- // Source plugin directory (shipped with this package)
101
+ // Source directories (shipped with this package)
102
+ // Repo layout:
103
+ // .claude-plugin/marketplace.json ← marketplace descriptor
104
+ // plugins/attacca-forge/ ← plugin with .claude-plugin/plugin.json + skills/
105
+ const sourceMarketplace = join(rootDir, '.claude-plugin', 'marketplace.json');
106
106
  const sourcePlugin = join(rootDir, 'plugins', 'attacca-forge');
107
+
107
108
  if (!existsSync(sourcePlugin)) {
108
109
  console.error(' ✗ Plugin source not found. Package may be corrupted.');
109
110
  process.exit(1);
@@ -111,36 +112,35 @@ export default async function install({ args, cwd, rootDir }) {
111
112
 
112
113
  // Read version from package.json
113
114
  const pkg = readJsonFile(join(rootDir, 'package.json'));
114
- const version = pkg?.version || '0.5.1';
115
+ const version = pkg?.version || '0.5.2';
115
116
 
116
- // Target plugin directory
117
+ // Target: ~/.claude/plugins/local/attacca-forge/
118
+ // Installed layout (mirrors nirbound-marketplace):
119
+ // .claude-plugin/marketplace.json
120
+ // plugins/attacca-forge/.claude-plugin/plugin.json
121
+ // plugins/attacca-forge/skills/
117
122
  const claudeDir = getClaudeDir();
118
123
  const targetDir = getPluginDir();
119
- const localDir = join(claudeDir, 'plugins', 'local');
120
-
121
- // Create directories
122
- if (!existsSync(localDir)) mkdirSync(localDir, { recursive: true });
123
124
 
124
- // Copy .claude-plugin/ and skills/
125
125
  const existed = existsSync(targetDir);
126
126
  if (!existsSync(targetDir)) mkdirSync(targetDir, { recursive: true });
127
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 });
128
+ // 1. Copy marketplace descriptor to target root
129
+ const targetMarketplaceDir = join(targetDir, '.claude-plugin');
130
+ if (!existsSync(targetMarketplaceDir)) mkdirSync(targetMarketplaceDir, { recursive: true });
131
+ if (existsSync(sourceMarketplace)) {
132
+ cpSync(sourceMarketplace, join(targetMarketplaceDir, 'marketplace.json'), { force: true });
138
133
  }
139
134
 
135
+ // 2. Copy plugin (with .claude-plugin/plugin.json + skills/) into nested directory
136
+ const targetPlugin = join(targetDir, 'plugins', 'attacca-forge');
137
+ if (!existsSync(targetPlugin)) mkdirSync(targetPlugin, { recursive: true });
138
+ cpSync(sourcePlugin, targetPlugin, { recursive: true, force: true });
139
+
140
140
  // Count skills
141
+ const targetSkills = join(targetPlugin, 'skills');
141
142
  let skillCount = 0;
142
143
  if (existsSync(targetSkills)) {
143
- const { readdirSync } = await import('node:fs');
144
144
  skillCount = readdirSync(targetSkills).filter((f) => {
145
145
  return existsSync(join(targetSkills, f, 'SKILL.md'));
146
146
  }).length;
@@ -149,14 +149,15 @@ export default async function install({ args, cwd, rootDir }) {
149
149
  console.log(` ✓ ${existed ? 'Updated' : 'Installed'} ${skillCount} skills to Claude Code`);
150
150
  console.log(` Location: ${targetDir}`);
151
151
 
152
- // Register marketplace and plugin
152
+ // 3. Register marketplace in known_marketplaces.json
153
153
  const pluginsDir = join(claudeDir, 'plugins');
154
154
  if (!existsSync(pluginsDir)) mkdirSync(pluginsDir, { recursive: true });
155
155
 
156
156
  registerMarketplace(claudeDir, targetDir);
157
157
  console.log(' ✓ Marketplace registered');
158
158
 
159
- registerPlugin(claudeDir, targetDir, version);
159
+ // 4. Register plugin in installed_plugins.json + populate cache
160
+ registerPlugin(claudeDir, targetPlugin, version);
160
161
  console.log(' ✓ Plugin registered');
161
162
 
162
163
  console.log('');
@@ -1,20 +0,0 @@
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
- }