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.
|
|
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
|
+
}
|
package/src/commands/install.js
CHANGED
|
@@ -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(
|
|
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
|
|
124
|
+
// Copy .claude-plugin/ and skills/
|
|
40
125
|
const existed = existsSync(targetDir);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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(
|
|
142
|
+
if (existsSync(targetSkills)) {
|
|
55
143
|
const { readdirSync } = await import('node:fs');
|
|
56
|
-
skillCount = readdirSync(
|
|
57
|
-
return existsSync(join(
|
|
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('');
|