@arthai/agents 1.0.5 → 1.0.7
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.
- package/README.md +33 -3
- package/VERSION +1 -1
- package/agents/troubleshooter.md +132 -0
- package/bin/cli.js +296 -0
- package/bundles/canvas.json +1 -1
- package/bundles/compass.json +1 -1
- package/bundles/counsel.json +1 -0
- package/bundles/cruise.json +1 -1
- package/bundles/forge.json +12 -1
- package/bundles/prism.json +1 -0
- package/bundles/scalpel.json +5 -2
- package/bundles/sentinel.json +8 -2
- package/bundles/shield.json +1 -0
- package/bundles/spark.json +1 -0
- package/compiler.sh +14 -0
- package/dist/plugins/canvas/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/canvas/VERSION +1 -0
- package/dist/plugins/canvas/commands/planning.md +100 -11
- package/dist/plugins/canvas/hooks/hooks.json +16 -0
- package/dist/plugins/canvas/hooks/project-setup.sh +109 -0
- package/dist/plugins/canvas/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/canvas/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/compass/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/compass/VERSION +1 -0
- package/dist/plugins/compass/commands/planning.md +100 -11
- package/dist/plugins/compass/hooks/hooks.json +16 -0
- package/dist/plugins/compass/hooks/project-setup.sh +109 -0
- package/dist/plugins/compass/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/compass/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/counsel/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/counsel/VERSION +1 -0
- package/dist/plugins/counsel/hooks/hooks.json +10 -0
- package/dist/plugins/counsel/hooks/project-setup.sh +109 -0
- package/dist/plugins/counsel/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/counsel/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/cruise/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/cruise/VERSION +1 -0
- package/dist/plugins/cruise/hooks/hooks.json +16 -0
- package/dist/plugins/cruise/hooks/project-setup.sh +109 -0
- package/dist/plugins/cruise/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/cruise/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/forge/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/forge/VERSION +1 -0
- package/dist/plugins/forge/agents/troubleshooter.md +132 -0
- package/dist/plugins/forge/commands/implement.md +99 -1
- package/dist/plugins/forge/commands/planning.md +100 -11
- package/dist/plugins/forge/hooks/escalation-guard.sh +177 -0
- package/dist/plugins/forge/hooks/hooks.json +22 -0
- package/dist/plugins/forge/hooks/project-setup.sh +109 -0
- package/dist/plugins/forge/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/forge/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/prime/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/prime/VERSION +1 -0
- package/dist/plugins/prime/agents/troubleshooter.md +132 -0
- package/dist/plugins/prime/commands/calibrate.md +20 -0
- package/dist/plugins/prime/commands/ci-fix.md +36 -0
- package/dist/plugins/prime/commands/fix.md +23 -0
- package/dist/plugins/prime/commands/implement.md +99 -1
- package/dist/plugins/prime/commands/planning.md +100 -11
- package/dist/plugins/prime/commands/qa-incident.md +54 -0
- package/dist/plugins/prime/commands/restart.md +186 -30
- package/dist/plugins/prime/hooks/escalation-guard.sh +177 -0
- package/dist/plugins/prime/hooks/hooks.json +60 -0
- package/dist/plugins/prime/hooks/post-config-change-restart-reminder.sh +86 -0
- package/dist/plugins/prime/hooks/post-server-crash-watch.sh +120 -0
- package/dist/plugins/prime/hooks/pre-server-port-guard.sh +110 -0
- package/dist/plugins/prime/hooks/project-setup.sh +109 -0
- package/dist/plugins/prime/hooks/sync-agents.sh +99 -12
- package/dist/plugins/prime/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/prime/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/prism/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/prism/VERSION +1 -0
- package/dist/plugins/prism/commands/qa-incident.md +54 -0
- package/dist/plugins/prism/hooks/hooks.json +12 -0
- package/dist/plugins/prism/hooks/project-setup.sh +109 -0
- package/dist/plugins/prism/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/prism/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/scalpel/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/scalpel/VERSION +1 -0
- package/dist/plugins/scalpel/agents/troubleshooter.md +132 -0
- package/dist/plugins/scalpel/commands/ci-fix.md +36 -0
- package/dist/plugins/scalpel/commands/fix.md +23 -0
- package/dist/plugins/scalpel/hooks/escalation-guard.sh +177 -0
- package/dist/plugins/scalpel/hooks/hooks.json +24 -0
- package/dist/plugins/scalpel/hooks/project-setup.sh +109 -0
- package/dist/plugins/scalpel/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/scalpel/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/sentinel/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/sentinel/VERSION +1 -0
- package/dist/plugins/sentinel/agents/troubleshooter.md +132 -0
- package/dist/plugins/sentinel/commands/restart.md +186 -30
- package/dist/plugins/sentinel/hooks/escalation-guard.sh +177 -0
- package/dist/plugins/sentinel/hooks/hooks.json +64 -0
- package/dist/plugins/sentinel/hooks/post-config-change-restart-reminder.sh +86 -0
- package/dist/plugins/sentinel/hooks/post-server-crash-watch.sh +120 -0
- package/dist/plugins/sentinel/hooks/pre-server-port-guard.sh +110 -0
- package/dist/plugins/sentinel/hooks/project-setup.sh +109 -0
- package/dist/plugins/sentinel/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/sentinel/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/shield/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/shield/VERSION +1 -0
- package/dist/plugins/shield/hooks/hooks.json +22 -12
- package/dist/plugins/shield/hooks/project-setup.sh +109 -0
- package/dist/plugins/shield/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/shield/templates/CLAUDE.md.template +111 -0
- package/dist/plugins/spark/.claude-plugin/plugin.json +1 -1
- package/dist/plugins/spark/VERSION +1 -0
- package/dist/plugins/spark/commands/calibrate.md +20 -0
- package/dist/plugins/spark/hooks/hooks.json +10 -0
- package/dist/plugins/spark/hooks/project-setup.sh +109 -0
- package/dist/plugins/spark/templates/CLAUDE.md.managed-block +123 -0
- package/dist/plugins/spark/templates/CLAUDE.md.template +111 -0
- package/hook-defs.json +31 -0
- package/hooks/escalation-guard.sh +177 -0
- package/hooks/post-config-change-restart-reminder.sh +86 -0
- package/hooks/post-server-crash-watch.sh +120 -0
- package/hooks/pre-server-port-guard.sh +110 -0
- package/hooks/project-setup.sh +109 -0
- package/hooks/sync-agents.sh +99 -12
- package/install.sh +2 -2
- package/package.json +1 -1
- package/portable.manifest +7 -1
- package/skills/calibrate/SKILL.md +20 -0
- package/skills/ci-fix/SKILL.md +36 -0
- package/skills/fix/SKILL.md +23 -0
- package/skills/implement/SKILL.md +99 -1
- package/skills/license/SKILL.md +159 -0
- package/skills/planning/SKILL.md +100 -11
- package/skills/qa-incident/SKILL.md +54 -0
- package/skills/restart/SKILL.md +187 -31
package/README.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
AI development toolkit for Claude Code — agents, skills, and hooks organized into installable bundles.
|
|
4
4
|
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# 1. Activate your license (one time)
|
|
9
|
+
npx @arthai/agents activate ARTH-XXXX-XXXX-XXXX-XXXX
|
|
10
|
+
|
|
11
|
+
# 2. Install a bundle into your project
|
|
12
|
+
npx @arthai/agents install forge .
|
|
13
|
+
|
|
14
|
+
# 3. Open Claude Code — skills are ready
|
|
15
|
+
/calibrate # auto-configure for your codebase
|
|
16
|
+
/planning my-feature # start building
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Don't have a key? Contact us at arthai.dev/pricing
|
|
20
|
+
|
|
5
21
|
## Install
|
|
6
22
|
|
|
7
23
|
```bash
|
|
@@ -11,10 +27,10 @@ npx @arthai/agents install forge .
|
|
|
11
27
|
# Install multiple bundles
|
|
12
28
|
npx @arthai/agents install forge spark shield .
|
|
13
29
|
|
|
14
|
-
# See all available bundles
|
|
30
|
+
# See all available bundles (no license needed)
|
|
15
31
|
npx @arthai/agents list
|
|
16
32
|
|
|
17
|
-
# Show what's in a bundle
|
|
33
|
+
# Show what's in a bundle (no license needed)
|
|
18
34
|
npx @arthai/agents info forge
|
|
19
35
|
```
|
|
20
36
|
|
|
@@ -119,8 +135,22 @@ Note: hooks merged into `.claude/settings.json` need to be removed manually.
|
|
|
119
135
|
/plugin install forge@arthai-marketplace
|
|
120
136
|
```
|
|
121
137
|
|
|
138
|
+
## License Activation
|
|
139
|
+
|
|
140
|
+
A license key is required to install bundles. Browse and explore bundles without a key using `list` and `info`.
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Activate (one time — saves key locally)
|
|
144
|
+
npx @arthai/agents activate ARTH-XXXX-XXXX-XXXX-XXXX
|
|
145
|
+
|
|
146
|
+
# Key is stored at ~/.arthai/license — all future installs use it automatically
|
|
147
|
+
# You can also set ARTHAI_LICENSE_KEY env var (useful for CI/teams)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Key precedence:** `ARTHAI_LICENSE_KEY` env var > `~/.arthai/license` file > `--key` flag
|
|
151
|
+
|
|
122
152
|
## Requirements
|
|
123
153
|
|
|
124
154
|
- [Claude Code CLI](https://docs.anthropic.com/en/docs/claude-code)
|
|
125
155
|
- Node.js 18+
|
|
126
|
-
- License key (
|
|
156
|
+
- License key (get one at arthai.dev/pricing)
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.7
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: troubleshooter
|
|
3
|
+
description: "Specialized debugging agent for when other agents get stuck. Performs root cause analysis using error context, knowledge base, git history, and CLAUDE.md. Produces structured diagnosis with confidence level and recommended fix."
|
|
4
|
+
model: sonnet
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Troubleshooter Agent
|
|
8
|
+
|
|
9
|
+
You are a specialized debugging agent. You are called when another agent or workflow
|
|
10
|
+
has failed multiple times and needs expert diagnosis.
|
|
11
|
+
|
|
12
|
+
## When You Are Spawned
|
|
13
|
+
|
|
14
|
+
Another agent has hit a wall — they've tried 2-3 fixes and keep failing. Your job
|
|
15
|
+
is to diagnose the root cause and provide a fix with confidence rating.
|
|
16
|
+
|
|
17
|
+
## Your Process (follow in order)
|
|
18
|
+
|
|
19
|
+
### 1. Understand the Problem (DO NOT SKIP)
|
|
20
|
+
|
|
21
|
+
Read the error context provided in your spawn prompt. Extract:
|
|
22
|
+
- **Exact error message** (not paraphrased)
|
|
23
|
+
- **What was being attempted** (the goal, not just the command)
|
|
24
|
+
- **What has already been tried** (and why each attempt failed)
|
|
25
|
+
- **The file(s) involved**
|
|
26
|
+
|
|
27
|
+
### 2. Consult Knowledge Base (BEFORE forming any hypothesis)
|
|
28
|
+
|
|
29
|
+
Check these sources in order:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
.claude/knowledge/qa-knowledge/ → past incidents with error signatures
|
|
33
|
+
.claude/knowledge/shared/conventions.md → project-specific gotchas and rules
|
|
34
|
+
.claude/knowledge/shared/patterns.md → architecture patterns that may explain the error
|
|
35
|
+
.claude/knowledge/agents/ → per-agent learning files
|
|
36
|
+
CLAUDE.md → project configuration, test commands, services
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Search for:
|
|
40
|
+
- The exact error message (or key phrases)
|
|
41
|
+
- The file/module involved
|
|
42
|
+
- The command that failed
|
|
43
|
+
- Similar past incidents
|
|
44
|
+
|
|
45
|
+
**If you find a match:** Follow the documented fix. Do not reinvent.
|
|
46
|
+
**If no match:** Proceed to step 3.
|
|
47
|
+
|
|
48
|
+
### 3. Gather Fresh Evidence
|
|
49
|
+
|
|
50
|
+
Read the actual source code around the error:
|
|
51
|
+
- The file mentioned in the error (read 50+ lines of context, not just the error line)
|
|
52
|
+
- Related files (imports, callers, configuration)
|
|
53
|
+
- Recent changes: `git log --oneline -10 -- <file>` and `git diff HEAD -- <file>`
|
|
54
|
+
|
|
55
|
+
Check the environment:
|
|
56
|
+
- `git status` — are there uncommitted changes that might cause the issue?
|
|
57
|
+
- Check if the right dependencies are installed (node_modules, venv, etc.)
|
|
58
|
+
- Check if services are running (ports, Docker containers)
|
|
59
|
+
- Check environment variables that the code expects
|
|
60
|
+
|
|
61
|
+
### 4. Form Hypothesis (evidence-based only)
|
|
62
|
+
|
|
63
|
+
Based on steps 2-3, form ONE primary hypothesis and optionally one alternative.
|
|
64
|
+
Each hypothesis MUST cite evidence:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
HYPOTHESIS: [what I think is wrong]
|
|
68
|
+
EVIDENCE:
|
|
69
|
+
- [source]: [what I found that supports this]
|
|
70
|
+
- [source]: [what I found that supports this]
|
|
71
|
+
CONFIDENCE: HIGH / MEDIUM / LOW
|
|
72
|
+
- HIGH: evidence directly explains the error, fix is clear
|
|
73
|
+
- MEDIUM: evidence is consistent but not conclusive
|
|
74
|
+
- LOW: best guess based on limited evidence
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 5. Recommend Fix
|
|
78
|
+
|
|
79
|
+
Provide a specific, actionable fix:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
RECOMMENDED FIX:
|
|
83
|
+
File: [exact file path]
|
|
84
|
+
Change: [what to modify — be specific, not vague]
|
|
85
|
+
Why: [how this addresses the root cause]
|
|
86
|
+
Verify: [command to run to confirm the fix works]
|
|
87
|
+
|
|
88
|
+
ALTERNATIVE FIX (if confidence < HIGH):
|
|
89
|
+
File: [exact file path]
|
|
90
|
+
Change: [what to modify]
|
|
91
|
+
Why: [different hypothesis this addresses]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 6. Output Format
|
|
95
|
+
|
|
96
|
+
Always produce this structured output:
|
|
97
|
+
|
|
98
|
+
```markdown
|
|
99
|
+
## Troubleshooter Diagnosis
|
|
100
|
+
|
|
101
|
+
**Error:** [exact error]
|
|
102
|
+
**Root Cause:** [1-2 sentence explanation]
|
|
103
|
+
**Confidence:** HIGH / MEDIUM / LOW
|
|
104
|
+
|
|
105
|
+
### Evidence
|
|
106
|
+
- [source 1]: [finding]
|
|
107
|
+
- [source 2]: [finding]
|
|
108
|
+
- Knowledge base: [match found / no match]
|
|
109
|
+
|
|
110
|
+
### Recommended Fix
|
|
111
|
+
- File: [path]
|
|
112
|
+
- Change: [specific change]
|
|
113
|
+
- Verify: [command]
|
|
114
|
+
|
|
115
|
+
### What Was Wrong With Previous Attempts
|
|
116
|
+
- Attempt 1: [why it didn't work — specific reason]
|
|
117
|
+
- Attempt 2: [why it didn't work — specific reason]
|
|
118
|
+
|
|
119
|
+
### If This Doesn't Work
|
|
120
|
+
- [Next diagnostic step to try]
|
|
121
|
+
- [What data to gather]
|
|
122
|
+
- [Whether to escalate to user — and what to ask them]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Rules
|
|
126
|
+
|
|
127
|
+
1. **Never guess.** Every claim must cite evidence from code, logs, KB, or git history.
|
|
128
|
+
2. **Check KB first.** If a past incident matches, use that fix. Don't reinvent.
|
|
129
|
+
3. **Be specific.** "Check the config" is not a fix. "Change line 42 of config.ts from X to Y" is.
|
|
130
|
+
4. **Explain why previous attempts failed.** This is as valuable as the fix itself.
|
|
131
|
+
5. **Know when to escalate.** If confidence is LOW and you can't gather more evidence, say so. Recommend what data to ask the user for.
|
|
132
|
+
6. **Don't try the fix yourself.** Your job is diagnosis. The calling agent implements the fix.
|
package/bin/cli.js
CHANGED
|
@@ -13,10 +13,25 @@ const BUNDLES_DIR = path.join(TOOLKIT_DIR, 'bundles');
|
|
|
13
13
|
const PLUGINS_DIR = path.join(TOOLKIT_DIR, 'dist', 'plugins');
|
|
14
14
|
const INSTALL_SH = path.join(TOOLKIT_DIR, 'install.sh');
|
|
15
15
|
|
|
16
|
+
const WORKER_URL = 'https://license-worker.muddassar-shaikh.workers.dev';
|
|
17
|
+
const KEY_PATTERN = /^ARTH-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}-[A-Z0-9]{4}$/;
|
|
18
|
+
const ARTHAI_DIR = path.join(process.env.HOME || '~', '.arthai');
|
|
19
|
+
const LICENSE_FILE_ARTHAI = path.join(ARTHAI_DIR, 'license');
|
|
20
|
+
const LICENSE_FILE_AGENTS = path.join(process.env.HOME || '~', '.claude-agents', '.license');
|
|
21
|
+
const VALIDATION_CACHE_FILE = path.join(ARTHAI_DIR, 'validation-cache');
|
|
22
|
+
const VALIDATION_CACHE_TTL = 86400; // 24 hours in seconds
|
|
23
|
+
const MANAGED_BLOCK_START = '<!-- >>> claude-agents toolkit (DO NOT EDIT THIS BLOCK) >>> -->';
|
|
24
|
+
const MANAGED_BLOCK_END = '<!-- <<< claude-agents toolkit <<< -->';
|
|
25
|
+
const GITIGNORE_BLOCK_START = '# >>> claude-agents managed (DO NOT EDIT THIS BLOCK) >>>';
|
|
26
|
+
const GITIGNORE_BLOCK_END = '# <<< claude-agents managed <<<';
|
|
27
|
+
|
|
16
28
|
const args = process.argv.slice(2);
|
|
17
29
|
const command = args[0];
|
|
18
30
|
|
|
19
31
|
switch (command) {
|
|
32
|
+
case 'activate':
|
|
33
|
+
handleActivate(args[1]);
|
|
34
|
+
break;
|
|
20
35
|
case 'install':
|
|
21
36
|
handleInstall(args.slice(1));
|
|
22
37
|
break;
|
|
@@ -88,6 +103,24 @@ function handleInstall(rawArgs) {
|
|
|
88
103
|
process.exit(1);
|
|
89
104
|
}
|
|
90
105
|
|
|
106
|
+
// License gate — must validate before any installation
|
|
107
|
+
const licenseKey = readLicenseKey(rawArgs);
|
|
108
|
+
if (!licenseKey) {
|
|
109
|
+
console.error('License required. Run: npx @arthai/agents activate ARTH-XXXX-XXXX-XXXX-XXXX');
|
|
110
|
+
console.error('Get a license at arthai.dev/pricing');
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
const licenseResult = validateWithWorker(licenseKey);
|
|
114
|
+
if (!licenseResult.valid) {
|
|
115
|
+
if (licenseResult.networkError) {
|
|
116
|
+
console.error('License server unavailable. Cannot install without a validated license.');
|
|
117
|
+
console.error('Check your internet connection and try again.');
|
|
118
|
+
} else {
|
|
119
|
+
console.error('Invalid license key. Check your key or get one at arthai.dev/pricing');
|
|
120
|
+
}
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
91
124
|
// Resolve dependencies (topological order)
|
|
92
125
|
const resolved = resolveDependencies(bundleNames);
|
|
93
126
|
console.log(`Installing: ${resolved.join(' → ')}`);
|
|
@@ -123,6 +156,9 @@ function handleInstall(rawArgs) {
|
|
|
123
156
|
totalHooks += counts.hooks;
|
|
124
157
|
}
|
|
125
158
|
|
|
159
|
+
// Post-install project setup (parity with install.sh)
|
|
160
|
+
setupProjectClaude(targetDir);
|
|
161
|
+
|
|
126
162
|
console.log(`\nDone. ${resolved.length} bundle(s) installed: ${totalCommands} skills, ${totalAgents} agents, ${totalHooks} hooks`);
|
|
127
163
|
}
|
|
128
164
|
|
|
@@ -211,6 +247,181 @@ function handleLegacy(legacyArgs) {
|
|
|
211
247
|
process.exit(result.status || 0);
|
|
212
248
|
}
|
|
213
249
|
|
|
250
|
+
// ── activate ──────────────────────────────────────────────────────────────────
|
|
251
|
+
|
|
252
|
+
function handleActivate(key) {
|
|
253
|
+
if (!key) {
|
|
254
|
+
console.error('Usage: arthai activate <ARTH-XXXX-XXXX-XXXX-XXXX>');
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (!KEY_PATTERN.test(key)) {
|
|
259
|
+
console.error(`Invalid key format: "${key}"`);
|
|
260
|
+
console.error('Keys must be in the format ARTH-XXXX-XXXX-XXXX-XXXX (uppercase letters and digits)');
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const machineId = computeMachineId();
|
|
265
|
+
const result = postToWorker('/activate', { key, machine_id: machineId });
|
|
266
|
+
|
|
267
|
+
if (result.networkError) {
|
|
268
|
+
console.error('Activation failed: License server unavailable.');
|
|
269
|
+
console.error('Check your internet connection and try again.');
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (!result.valid) {
|
|
274
|
+
const reason = result.reason === 'revoked'
|
|
275
|
+
? 'This license key has been revoked.'
|
|
276
|
+
: 'Invalid license key. Check your key or get one at arthai.dev/pricing';
|
|
277
|
+
console.error(`Activation failed: ${reason}`);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
fs.mkdirSync(ARTHAI_DIR, { recursive: true });
|
|
282
|
+
fs.writeFileSync(LICENSE_FILE_ARTHAI, key + '\n', { mode: 0o600 });
|
|
283
|
+
|
|
284
|
+
// Store validation cache immediately so first install doesn't re-validate
|
|
285
|
+
writeValidationCache(key);
|
|
286
|
+
|
|
287
|
+
console.log(`License activated (org: ${result.org}, tier: ${result.tier})`);
|
|
288
|
+
console.log(`Key stored at: ${LICENSE_FILE_ARTHAI}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ── license helpers ───────────────────────────────────────────────────────────
|
|
292
|
+
|
|
293
|
+
// Read license key in precedence order:
|
|
294
|
+
// 1. ARTHAI_LICENSE_KEY env var
|
|
295
|
+
// 2. ~/.claude-agents/.license (existing clone-install path)
|
|
296
|
+
// 3. ~/.arthai/license (npm activate path)
|
|
297
|
+
// 4. --key flag in args
|
|
298
|
+
function readLicenseKey(rawArgs) {
|
|
299
|
+
if (process.env.ARTHAI_LICENSE_KEY) {
|
|
300
|
+
return process.env.ARTHAI_LICENSE_KEY.trim() || null;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (fs.existsSync(LICENSE_FILE_AGENTS)) {
|
|
304
|
+
const key = fs.readFileSync(LICENSE_FILE_AGENTS, 'utf8').trim();
|
|
305
|
+
if (key) return key;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (fs.existsSync(LICENSE_FILE_ARTHAI)) {
|
|
309
|
+
const key = fs.readFileSync(LICENSE_FILE_ARTHAI, 'utf8').trim();
|
|
310
|
+
if (key) return key;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (Array.isArray(rawArgs)) {
|
|
314
|
+
const flagIdx = rawArgs.indexOf('--key');
|
|
315
|
+
if (flagIdx !== -1 && rawArgs[flagIdx + 1]) {
|
|
316
|
+
return rawArgs[flagIdx + 1].trim() || null;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// POST to Worker synchronously using curl via stdin (payload not visible in process list).
|
|
324
|
+
// Returns { valid, org, tier, reason, networkError } — never throws.
|
|
325
|
+
function postToWorker(endpoint, body) {
|
|
326
|
+
try {
|
|
327
|
+
const payload = JSON.stringify(body);
|
|
328
|
+
const result = spawnSync(
|
|
329
|
+
'curl',
|
|
330
|
+
[
|
|
331
|
+
'--silent',
|
|
332
|
+
'--max-time', '10',
|
|
333
|
+
'--connect-timeout', '5',
|
|
334
|
+
'--fail-with-body',
|
|
335
|
+
'--request', 'POST',
|
|
336
|
+
'--header', 'Content-Type: application/json',
|
|
337
|
+
'--data', '@-', // read payload from stdin, not command line
|
|
338
|
+
WORKER_URL + endpoint,
|
|
339
|
+
],
|
|
340
|
+
{
|
|
341
|
+
input: payload, // passed as stdin — not visible in ps/proc
|
|
342
|
+
timeout: 12000,
|
|
343
|
+
}
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
if (result.status !== 0 || result.error) {
|
|
347
|
+
return { valid: false, networkError: true };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const stdout = result.stdout.toString().trim();
|
|
351
|
+
if (!stdout) return { valid: false, networkError: true };
|
|
352
|
+
|
|
353
|
+
const json = JSON.parse(stdout);
|
|
354
|
+
return { ...json, networkError: false };
|
|
355
|
+
} catch (e) {
|
|
356
|
+
return { valid: false, networkError: true };
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Validate with Worker, using 24h cache for subsequent calls.
|
|
361
|
+
// First call (no cache): fail-closed — Worker must respond.
|
|
362
|
+
function validateWithWorker(key) {
|
|
363
|
+
const cached = readValidationCache(key);
|
|
364
|
+
if (cached !== null) {
|
|
365
|
+
return { valid: cached, networkError: false };
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const result = postToWorker('/validate', { key });
|
|
369
|
+
|
|
370
|
+
if (result.networkError) {
|
|
371
|
+
// No cache exists — fail closed for first install
|
|
372
|
+
return { valid: false, networkError: true };
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (result.valid) {
|
|
376
|
+
writeValidationCache(key);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// Returns true/false if cache is fresh, null if cache is missing or expired.
|
|
383
|
+
// Cache stores full SHA-256 key_hash (not plaintext, not truncated) to detect key changes.
|
|
384
|
+
function readValidationCache(key) {
|
|
385
|
+
try {
|
|
386
|
+
if (!fs.existsSync(VALIDATION_CACHE_FILE)) return null;
|
|
387
|
+
const cache = JSON.parse(fs.readFileSync(VALIDATION_CACHE_FILE, 'utf8'));
|
|
388
|
+
const nowSec = Math.floor(Date.now() / 1000);
|
|
389
|
+
const { createHash } = require('crypto');
|
|
390
|
+
const keyHash = createHash('sha256').update(key).digest('hex');
|
|
391
|
+
if (cache.key_hash !== keyHash) return null;
|
|
392
|
+
// Reject future timestamps — prevents cache poisoning via pre-written cache files
|
|
393
|
+
if (typeof cache.timestamp !== 'number' || cache.timestamp > nowSec) return null;
|
|
394
|
+
if ((nowSec - cache.timestamp) >= VALIDATION_CACHE_TTL) return null;
|
|
395
|
+
return cache.valid === true;
|
|
396
|
+
} catch (e) {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function writeValidationCache(key) {
|
|
402
|
+
try {
|
|
403
|
+
fs.mkdirSync(ARTHAI_DIR, { recursive: true });
|
|
404
|
+
const { createHash } = require('crypto');
|
|
405
|
+
// Full SHA-256 hash — not truncated, prevents brute-force of short hashes
|
|
406
|
+
const keyHash = createHash('sha256').update(key).digest('hex');
|
|
407
|
+
const cache = { key_hash: keyHash, valid: true, timestamp: Math.floor(Date.now() / 1000) };
|
|
408
|
+
fs.writeFileSync(VALIDATION_CACHE_FILE, JSON.stringify(cache) + '\n', { mode: 0o600 });
|
|
409
|
+
} catch (e) {
|
|
410
|
+
// Non-fatal — validation will just re-check next time
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function computeMachineId() {
|
|
415
|
+
try {
|
|
416
|
+
const { createHash } = require('crypto');
|
|
417
|
+
const hostname = require('os').hostname();
|
|
418
|
+
const username = process.env.USER || process.env.USERNAME || '';
|
|
419
|
+
return createHash('sha256').update(hostname + username).digest('hex');
|
|
420
|
+
} catch (e) {
|
|
421
|
+
return 'unknown';
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
214
425
|
// ── uninstall ────────────────────────────────────────────────────────────────
|
|
215
426
|
|
|
216
427
|
function handleUninstall(rawArgs) {
|
|
@@ -406,6 +617,90 @@ function installBundle(pluginDir, claudeDir, skillsDir, agentsDir, name) {
|
|
|
406
617
|
return counts;
|
|
407
618
|
}
|
|
408
619
|
|
|
620
|
+
// ── project setup (M6 — parity with install.sh) ───────────────────────────────
|
|
621
|
+
|
|
622
|
+
function setupProjectClaude(targetDir) {
|
|
623
|
+
const claudeDir = path.join(targetDir, '.claude');
|
|
624
|
+
const version = readVersion();
|
|
625
|
+
|
|
626
|
+
// 1. CLAUDE.md — create from template if missing, inject managed block if absent
|
|
627
|
+
const claudeMdPath = path.join(targetDir, 'CLAUDE.md');
|
|
628
|
+
const templatePath = path.join(TOOLKIT_DIR, 'templates', 'CLAUDE.md.template');
|
|
629
|
+
const managedBlockPath = path.join(TOOLKIT_DIR, 'templates', 'CLAUDE.md.managed-block');
|
|
630
|
+
|
|
631
|
+
if (!fs.existsSync(claudeMdPath) && fs.existsSync(templatePath)) {
|
|
632
|
+
const projectName = path.basename(targetDir);
|
|
633
|
+
const content = fs.readFileSync(templatePath, 'utf8').replace(/\{\{PROJECT_NAME\}\}/g, projectName);
|
|
634
|
+
fs.writeFileSync(claudeMdPath, content);
|
|
635
|
+
console.log(' Created CLAUDE.md from template');
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (fs.existsSync(claudeMdPath) && fs.existsSync(managedBlockPath)) {
|
|
639
|
+
injectOrUpdateManagedBlock(claudeMdPath, managedBlockPath, version);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// 2. .gitignore — inject toolkit marker block if missing
|
|
643
|
+
const gitignorePath = path.join(targetDir, '.gitignore');
|
|
644
|
+
injectGitignoreBlock(gitignorePath);
|
|
645
|
+
|
|
646
|
+
// 3. .claude/.claude-agents.conf — record installed bundles for re-sync tracking
|
|
647
|
+
// (Only write if we've already created the .claude dir; it should exist by now)
|
|
648
|
+
if (fs.existsSync(claudeDir)) {
|
|
649
|
+
const confPath = path.join(claudeDir, '.claude-agents.conf');
|
|
650
|
+
if (!fs.existsSync(confPath)) {
|
|
651
|
+
fs.writeFileSync(confPath, `# claude-agents configuration\nversion=${version}\n`);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
function injectOrUpdateManagedBlock(claudeMdPath, managedBlockPath, version) {
|
|
657
|
+
const existing = fs.readFileSync(claudeMdPath, 'utf8');
|
|
658
|
+
const blockContent = fs.readFileSync(managedBlockPath, 'utf8').trimEnd();
|
|
659
|
+
|
|
660
|
+
const newBlock = `${MANAGED_BLOCK_START}\n<!-- version: ${version} -->\n${blockContent}\n${MANAGED_BLOCK_END}`;
|
|
661
|
+
|
|
662
|
+
const startIdx = existing.indexOf(MANAGED_BLOCK_START);
|
|
663
|
+
const endIdx = existing.indexOf(MANAGED_BLOCK_END);
|
|
664
|
+
|
|
665
|
+
if (startIdx === -1) {
|
|
666
|
+
// Block missing — append it
|
|
667
|
+
const separator = existing.endsWith('\n') ? '\n' : '\n\n';
|
|
668
|
+
fs.writeFileSync(claudeMdPath, existing + separator + newBlock + '\n');
|
|
669
|
+
console.log(' Injected toolkit managed block into CLAUDE.md');
|
|
670
|
+
} else if (startIdx !== -1 && endIdx !== -1) {
|
|
671
|
+
// Block exists — check if version is stale
|
|
672
|
+
const existingBlock = existing.slice(startIdx, endIdx + MANAGED_BLOCK_END.length);
|
|
673
|
+
const versionMatch = existingBlock.match(/<!-- version: ([^>]+) -->/);
|
|
674
|
+
const existingVersion = versionMatch ? versionMatch[1].trim() : null;
|
|
675
|
+
if (existingVersion !== version) {
|
|
676
|
+
const updated = existing.slice(0, startIdx) + newBlock + existing.slice(endIdx + MANAGED_BLOCK_END.length);
|
|
677
|
+
fs.writeFileSync(claudeMdPath, updated);
|
|
678
|
+
console.log(` Updated toolkit managed block in CLAUDE.md (${existingVersion} → ${version})`);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function injectGitignoreBlock(gitignorePath) {
|
|
684
|
+
const lines = [
|
|
685
|
+
GITIGNORE_BLOCK_START,
|
|
686
|
+
'.claude/.toolkit-last-seen-sha',
|
|
687
|
+
'.claude/.toolkit-setup-done',
|
|
688
|
+
'.claude/.claude-agents.conf',
|
|
689
|
+
GITIGNORE_BLOCK_END,
|
|
690
|
+
].join('\n');
|
|
691
|
+
|
|
692
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
693
|
+
fs.writeFileSync(gitignorePath, lines + '\n');
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const existing = fs.readFileSync(gitignorePath, 'utf8');
|
|
698
|
+
if (existing.includes(GITIGNORE_BLOCK_START)) return; // Already present
|
|
699
|
+
|
|
700
|
+
const separator = existing.endsWith('\n') ? '\n' : '\n\n';
|
|
701
|
+
fs.writeFileSync(gitignorePath, existing + separator + lines + '\n');
|
|
702
|
+
}
|
|
703
|
+
|
|
409
704
|
function copyDirRecursive(src, dest) {
|
|
410
705
|
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
411
706
|
const srcPath = path.join(src, entry.name);
|
|
@@ -432,6 +727,7 @@ function printUsage() {
|
|
|
432
727
|
console.log(`arthai v${version} — AI-powered development toolkit for Claude Code
|
|
433
728
|
|
|
434
729
|
Usage:
|
|
730
|
+
arthai activate <key> Activate your license key
|
|
435
731
|
arthai install <bundle> [bundle...] [path] Install bundle(s) into a project
|
|
436
732
|
arthai list List available bundles
|
|
437
733
|
arthai info <bundle> Show bundle details
|
package/bundles/canvas.json
CHANGED
package/bundles/compass.json
CHANGED
package/bundles/counsel.json
CHANGED
package/bundles/cruise.json
CHANGED
package/bundles/forge.json
CHANGED
|
@@ -18,9 +18,20 @@
|
|
|
18
18
|
"skills/precheck",
|
|
19
19
|
"skills/review-pr"
|
|
20
20
|
],
|
|
21
|
+
"agents": [
|
|
22
|
+
"agents/architect.md",
|
|
23
|
+
"agents/product-manager.md",
|
|
24
|
+
"agents/python-backend.md",
|
|
25
|
+
"agents/frontend.md",
|
|
26
|
+
"agents/qa.md",
|
|
27
|
+
"agents/code-reviewer.md",
|
|
28
|
+
"agents/troubleshooter.md"
|
|
29
|
+
],
|
|
21
30
|
"hooks": [
|
|
31
|
+
"project-setup",
|
|
22
32
|
"triage-router",
|
|
23
|
-
"post-test-summary"
|
|
33
|
+
"post-test-summary",
|
|
34
|
+
"escalation-guard"
|
|
24
35
|
],
|
|
25
36
|
"dependencies": []
|
|
26
37
|
}
|
package/bundles/prism.json
CHANGED
package/bundles/scalpel.json
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
"description": "Surgical bug fixing — targeted fixes, CI repair, issue triage",
|
|
4
4
|
"version": "1.0.0",
|
|
5
5
|
"agents": [
|
|
6
|
-
"agents/code-reviewer.md"
|
|
6
|
+
"agents/code-reviewer.md",
|
|
7
|
+
"agents/troubleshooter.md"
|
|
7
8
|
],
|
|
8
9
|
"skills": [
|
|
9
10
|
"skills/fix",
|
|
@@ -11,7 +12,9 @@
|
|
|
11
12
|
"skills/issue"
|
|
12
13
|
],
|
|
13
14
|
"hooks": [
|
|
14
|
-
"
|
|
15
|
+
"project-setup",
|
|
16
|
+
"pre-edit-guard",
|
|
17
|
+
"escalation-guard"
|
|
15
18
|
],
|
|
16
19
|
"dependencies": []
|
|
17
20
|
}
|
package/bundles/sentinel.json
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
"version": "1.0.0",
|
|
5
5
|
"agents": [
|
|
6
6
|
"agents/sre.md",
|
|
7
|
-
"agents/ops.md"
|
|
7
|
+
"agents/ops.md",
|
|
8
|
+
"agents/troubleshooter.md"
|
|
8
9
|
],
|
|
9
10
|
"skills": [
|
|
10
11
|
"skills/sre",
|
|
@@ -12,8 +13,13 @@
|
|
|
12
13
|
"skills/restart"
|
|
13
14
|
],
|
|
14
15
|
"hooks": [
|
|
16
|
+
"project-setup",
|
|
15
17
|
"post-deploy-health",
|
|
16
|
-
"post-git-state"
|
|
18
|
+
"post-git-state",
|
|
19
|
+
"pre-server-port-guard",
|
|
20
|
+
"post-config-change-restart-reminder",
|
|
21
|
+
"post-server-crash-watch",
|
|
22
|
+
"escalation-guard"
|
|
17
23
|
],
|
|
18
24
|
"dependencies": []
|
|
19
25
|
}
|
package/bundles/shield.json
CHANGED