@arthai/agents 1.0.5 → 1.0.6

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 (130) hide show
  1. package/README.md +33 -3
  2. package/VERSION +1 -1
  3. package/agents/troubleshooter.md +132 -0
  4. package/bin/cli.js +297 -0
  5. package/bundles/canvas.json +1 -1
  6. package/bundles/compass.json +1 -1
  7. package/bundles/counsel.json +1 -0
  8. package/bundles/cruise.json +1 -1
  9. package/bundles/forge.json +12 -1
  10. package/bundles/prism.json +1 -0
  11. package/bundles/scalpel.json +5 -2
  12. package/bundles/sentinel.json +8 -2
  13. package/bundles/shield.json +1 -0
  14. package/bundles/spark.json +1 -0
  15. package/compiler.sh +14 -0
  16. package/dist/plugins/canvas/.claude-plugin/plugin.json +1 -1
  17. package/dist/plugins/canvas/VERSION +1 -0
  18. package/dist/plugins/canvas/commands/planning.md +100 -11
  19. package/dist/plugins/canvas/hooks/hooks.json +16 -0
  20. package/dist/plugins/canvas/hooks/project-setup.sh +109 -0
  21. package/dist/plugins/canvas/templates/CLAUDE.md.managed-block +123 -0
  22. package/dist/plugins/canvas/templates/CLAUDE.md.template +111 -0
  23. package/dist/plugins/compass/.claude-plugin/plugin.json +1 -1
  24. package/dist/plugins/compass/VERSION +1 -0
  25. package/dist/plugins/compass/commands/planning.md +100 -11
  26. package/dist/plugins/compass/hooks/hooks.json +16 -0
  27. package/dist/plugins/compass/hooks/project-setup.sh +109 -0
  28. package/dist/plugins/compass/templates/CLAUDE.md.managed-block +123 -0
  29. package/dist/plugins/compass/templates/CLAUDE.md.template +111 -0
  30. package/dist/plugins/counsel/.claude-plugin/plugin.json +1 -1
  31. package/dist/plugins/counsel/VERSION +1 -0
  32. package/dist/plugins/counsel/hooks/hooks.json +10 -0
  33. package/dist/plugins/counsel/hooks/project-setup.sh +109 -0
  34. package/dist/plugins/counsel/templates/CLAUDE.md.managed-block +123 -0
  35. package/dist/plugins/counsel/templates/CLAUDE.md.template +111 -0
  36. package/dist/plugins/cruise/.claude-plugin/plugin.json +1 -1
  37. package/dist/plugins/cruise/VERSION +1 -0
  38. package/dist/plugins/cruise/hooks/hooks.json +16 -0
  39. package/dist/plugins/cruise/hooks/project-setup.sh +109 -0
  40. package/dist/plugins/cruise/templates/CLAUDE.md.managed-block +123 -0
  41. package/dist/plugins/cruise/templates/CLAUDE.md.template +111 -0
  42. package/dist/plugins/forge/.claude-plugin/plugin.json +1 -1
  43. package/dist/plugins/forge/VERSION +1 -0
  44. package/dist/plugins/forge/agents/troubleshooter.md +132 -0
  45. package/dist/plugins/forge/commands/implement.md +99 -1
  46. package/dist/plugins/forge/commands/planning.md +100 -11
  47. package/dist/plugins/forge/hooks/escalation-guard.sh +177 -0
  48. package/dist/plugins/forge/hooks/hooks.json +22 -0
  49. package/dist/plugins/forge/hooks/project-setup.sh +109 -0
  50. package/dist/plugins/forge/templates/CLAUDE.md.managed-block +123 -0
  51. package/dist/plugins/forge/templates/CLAUDE.md.template +111 -0
  52. package/dist/plugins/prime/.claude-plugin/plugin.json +1 -1
  53. package/dist/plugins/prime/VERSION +1 -0
  54. package/dist/plugins/prime/agents/troubleshooter.md +132 -0
  55. package/dist/plugins/prime/commands/calibrate.md +20 -0
  56. package/dist/plugins/prime/commands/ci-fix.md +36 -0
  57. package/dist/plugins/prime/commands/fix.md +23 -0
  58. package/dist/plugins/prime/commands/implement.md +99 -1
  59. package/dist/plugins/prime/commands/planning.md +100 -11
  60. package/dist/plugins/prime/commands/qa-incident.md +54 -0
  61. package/dist/plugins/prime/commands/restart.md +186 -30
  62. package/dist/plugins/prime/hooks/escalation-guard.sh +177 -0
  63. package/dist/plugins/prime/hooks/hooks.json +60 -0
  64. package/dist/plugins/prime/hooks/post-config-change-restart-reminder.sh +86 -0
  65. package/dist/plugins/prime/hooks/post-server-crash-watch.sh +120 -0
  66. package/dist/plugins/prime/hooks/pre-server-port-guard.sh +110 -0
  67. package/dist/plugins/prime/hooks/project-setup.sh +109 -0
  68. package/dist/plugins/prime/hooks/sync-agents.sh +99 -12
  69. package/dist/plugins/prime/templates/CLAUDE.md.managed-block +123 -0
  70. package/dist/plugins/prime/templates/CLAUDE.md.template +111 -0
  71. package/dist/plugins/prism/.claude-plugin/plugin.json +1 -1
  72. package/dist/plugins/prism/VERSION +1 -0
  73. package/dist/plugins/prism/commands/qa-incident.md +54 -0
  74. package/dist/plugins/prism/hooks/hooks.json +12 -0
  75. package/dist/plugins/prism/hooks/project-setup.sh +109 -0
  76. package/dist/plugins/prism/templates/CLAUDE.md.managed-block +123 -0
  77. package/dist/plugins/prism/templates/CLAUDE.md.template +111 -0
  78. package/dist/plugins/scalpel/.claude-plugin/plugin.json +1 -1
  79. package/dist/plugins/scalpel/VERSION +1 -0
  80. package/dist/plugins/scalpel/agents/troubleshooter.md +132 -0
  81. package/dist/plugins/scalpel/commands/ci-fix.md +36 -0
  82. package/dist/plugins/scalpel/commands/fix.md +23 -0
  83. package/dist/plugins/scalpel/hooks/escalation-guard.sh +177 -0
  84. package/dist/plugins/scalpel/hooks/hooks.json +24 -0
  85. package/dist/plugins/scalpel/hooks/project-setup.sh +109 -0
  86. package/dist/plugins/scalpel/templates/CLAUDE.md.managed-block +123 -0
  87. package/dist/plugins/scalpel/templates/CLAUDE.md.template +111 -0
  88. package/dist/plugins/sentinel/.claude-plugin/plugin.json +1 -1
  89. package/dist/plugins/sentinel/VERSION +1 -0
  90. package/dist/plugins/sentinel/agents/troubleshooter.md +132 -0
  91. package/dist/plugins/sentinel/commands/restart.md +186 -30
  92. package/dist/plugins/sentinel/hooks/escalation-guard.sh +177 -0
  93. package/dist/plugins/sentinel/hooks/hooks.json +64 -0
  94. package/dist/plugins/sentinel/hooks/post-config-change-restart-reminder.sh +86 -0
  95. package/dist/plugins/sentinel/hooks/post-server-crash-watch.sh +120 -0
  96. package/dist/plugins/sentinel/hooks/pre-server-port-guard.sh +110 -0
  97. package/dist/plugins/sentinel/hooks/project-setup.sh +109 -0
  98. package/dist/plugins/sentinel/templates/CLAUDE.md.managed-block +123 -0
  99. package/dist/plugins/sentinel/templates/CLAUDE.md.template +111 -0
  100. package/dist/plugins/shield/.claude-plugin/plugin.json +1 -1
  101. package/dist/plugins/shield/VERSION +1 -0
  102. package/dist/plugins/shield/hooks/hooks.json +22 -12
  103. package/dist/plugins/shield/hooks/project-setup.sh +109 -0
  104. package/dist/plugins/shield/templates/CLAUDE.md.managed-block +123 -0
  105. package/dist/plugins/shield/templates/CLAUDE.md.template +111 -0
  106. package/dist/plugins/spark/.claude-plugin/plugin.json +1 -1
  107. package/dist/plugins/spark/VERSION +1 -0
  108. package/dist/plugins/spark/commands/calibrate.md +20 -0
  109. package/dist/plugins/spark/hooks/hooks.json +10 -0
  110. package/dist/plugins/spark/hooks/project-setup.sh +109 -0
  111. package/dist/plugins/spark/templates/CLAUDE.md.managed-block +123 -0
  112. package/dist/plugins/spark/templates/CLAUDE.md.template +111 -0
  113. package/hook-defs.json +31 -0
  114. package/hooks/escalation-guard.sh +177 -0
  115. package/hooks/post-config-change-restart-reminder.sh +86 -0
  116. package/hooks/post-server-crash-watch.sh +120 -0
  117. package/hooks/pre-server-port-guard.sh +110 -0
  118. package/hooks/project-setup.sh +109 -0
  119. package/hooks/sync-agents.sh +99 -12
  120. package/install.sh +2 -2
  121. package/package.json +1 -1
  122. package/portable.manifest +7 -1
  123. package/skills/calibrate/SKILL.md +20 -0
  124. package/skills/ci-fix/SKILL.md +36 -0
  125. package/skills/fix/SKILL.md +23 -0
  126. package/skills/implement/SKILL.md +99 -1
  127. package/skills/license/SKILL.md +159 -0
  128. package/skills/planning/SKILL.md +100 -11
  129. package/skills/qa-incident/SKILL.md +54 -0
  130. 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 (format: `ARTH-XXXX-XXXX-XXXX-XXXX`)
156
+ - License key (get one at arthai.dev/pricing)
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.5
1
+ 1.0.6
@@ -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,21 @@ 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
+
16
24
  const args = process.argv.slice(2);
17
25
  const command = args[0];
18
26
 
19
27
  switch (command) {
28
+ case 'activate':
29
+ handleActivate(args[1]);
30
+ break;
20
31
  case 'install':
21
32
  handleInstall(args.slice(1));
22
33
  break;
@@ -88,6 +99,24 @@ function handleInstall(rawArgs) {
88
99
  process.exit(1);
89
100
  }
90
101
 
102
+ // License gate — must validate before any installation
103
+ const licenseKey = readLicenseKey(rawArgs);
104
+ if (!licenseKey) {
105
+ console.error('License required. Run: npx @arthai/agents activate ARTH-XXXX-XXXX-XXXX-XXXX');
106
+ console.error('Get a license at arthai.dev/pricing');
107
+ process.exit(1);
108
+ }
109
+ const licenseResult = validateWithWorker(licenseKey);
110
+ if (!licenseResult.valid) {
111
+ if (licenseResult.networkError) {
112
+ console.error('License server unavailable. Cannot install without a validated license.');
113
+ console.error('Check your internet connection and try again.');
114
+ } else {
115
+ console.error('Invalid license key. Check your key or get one at arthai.dev/pricing');
116
+ }
117
+ process.exit(1);
118
+ }
119
+
91
120
  // Resolve dependencies (topological order)
92
121
  const resolved = resolveDependencies(bundleNames);
93
122
  console.log(`Installing: ${resolved.join(' → ')}`);
@@ -123,6 +152,9 @@ function handleInstall(rawArgs) {
123
152
  totalHooks += counts.hooks;
124
153
  }
125
154
 
155
+ // Post-install project setup (parity with install.sh)
156
+ setupProjectClaude(targetDir);
157
+
126
158
  console.log(`\nDone. ${resolved.length} bundle(s) installed: ${totalCommands} skills, ${totalAgents} agents, ${totalHooks} hooks`);
127
159
  }
128
160
 
@@ -211,6 +243,181 @@ function handleLegacy(legacyArgs) {
211
243
  process.exit(result.status || 0);
212
244
  }
213
245
 
246
+ // ── activate ──────────────────────────────────────────────────────────────────
247
+
248
+ function handleActivate(key) {
249
+ if (!key) {
250
+ console.error('Usage: arthai activate <ARTH-XXXX-XXXX-XXXX-XXXX>');
251
+ process.exit(1);
252
+ }
253
+
254
+ if (!KEY_PATTERN.test(key)) {
255
+ console.error(`Invalid key format: "${key}"`);
256
+ console.error('Keys must be in the format ARTH-XXXX-XXXX-XXXX-XXXX (uppercase letters and digits)');
257
+ process.exit(1);
258
+ }
259
+
260
+ const machineId = computeMachineId();
261
+ const result = postToWorker('/activate', { key, machine_id: machineId });
262
+
263
+ if (result.networkError) {
264
+ console.error('Activation failed: License server unavailable.');
265
+ console.error('Check your internet connection and try again.');
266
+ process.exit(1);
267
+ }
268
+
269
+ if (!result.valid) {
270
+ const reason = result.reason === 'revoked'
271
+ ? 'This license key has been revoked.'
272
+ : 'Invalid license key. Check your key or get one at arthai.dev/pricing';
273
+ console.error(`Activation failed: ${reason}`);
274
+ process.exit(1);
275
+ }
276
+
277
+ fs.mkdirSync(ARTHAI_DIR, { recursive: true });
278
+ fs.writeFileSync(LICENSE_FILE_ARTHAI, key + '\n', { mode: 0o600 });
279
+
280
+ // Store validation cache immediately so first install doesn't re-validate
281
+ writeValidationCache(key);
282
+
283
+ console.log(`License activated (org: ${result.org}, tier: ${result.tier})`);
284
+ console.log(`Key stored at: ${LICENSE_FILE_ARTHAI}`);
285
+ }
286
+
287
+ // ── license helpers ───────────────────────────────────────────────────────────
288
+
289
+ // Read license key in precedence order:
290
+ // 1. ARTHAI_LICENSE_KEY env var
291
+ // 2. ~/.claude-agents/.license (existing clone-install path)
292
+ // 3. ~/.arthai/license (npm activate path)
293
+ // 4. --key flag in args
294
+ function readLicenseKey(rawArgs) {
295
+ if (process.env.ARTHAI_LICENSE_KEY) {
296
+ return process.env.ARTHAI_LICENSE_KEY.trim() || null;
297
+ }
298
+
299
+ if (fs.existsSync(LICENSE_FILE_AGENTS)) {
300
+ const key = fs.readFileSync(LICENSE_FILE_AGENTS, 'utf8').trim();
301
+ if (key) return key;
302
+ }
303
+
304
+ if (fs.existsSync(LICENSE_FILE_ARTHAI)) {
305
+ const key = fs.readFileSync(LICENSE_FILE_ARTHAI, 'utf8').trim();
306
+ if (key) return key;
307
+ }
308
+
309
+ if (Array.isArray(rawArgs)) {
310
+ const flagIdx = rawArgs.indexOf('--key');
311
+ if (flagIdx !== -1 && rawArgs[flagIdx + 1]) {
312
+ return rawArgs[flagIdx + 1].trim() || null;
313
+ }
314
+ }
315
+
316
+ return null;
317
+ }
318
+
319
+ // POST to Worker synchronously using curl via stdin (payload not visible in process list).
320
+ // Returns { valid, org, tier, reason, networkError } — never throws.
321
+ function postToWorker(endpoint, body) {
322
+ try {
323
+ const payload = JSON.stringify(body);
324
+ const result = spawnSync(
325
+ 'curl',
326
+ [
327
+ '--silent',
328
+ '--max-time', '10',
329
+ '--connect-timeout', '5',
330
+ '--fail-with-body',
331
+ '--request', 'POST',
332
+ '--header', 'Content-Type: application/json',
333
+ '--data', '@-', // read payload from stdin, not command line
334
+ WORKER_URL + endpoint,
335
+ ],
336
+ {
337
+ input: payload, // passed as stdin — not visible in ps/proc
338
+ timeout: 12000,
339
+ }
340
+ );
341
+
342
+ if (result.status !== 0 || result.error) {
343
+ return { valid: false, networkError: true };
344
+ }
345
+
346
+ const stdout = result.stdout.toString().trim();
347
+ if (!stdout) return { valid: false, networkError: true };
348
+
349
+ const json = JSON.parse(stdout);
350
+ return { ...json, networkError: false };
351
+ } catch (e) {
352
+ return { valid: false, networkError: true };
353
+ }
354
+ }
355
+
356
+ // Validate with Worker, using 24h cache for subsequent calls.
357
+ // First call (no cache): fail-closed — Worker must respond.
358
+ function validateWithWorker(key) {
359
+ const cached = readValidationCache(key);
360
+ if (cached !== null) {
361
+ return { valid: cached, networkError: false };
362
+ }
363
+
364
+ const result = postToWorker('/validate', { key });
365
+
366
+ if (result.networkError) {
367
+ // No cache exists — fail closed for first install
368
+ return { valid: false, networkError: true };
369
+ }
370
+
371
+ if (result.valid) {
372
+ writeValidationCache(key);
373
+ }
374
+
375
+ return result;
376
+ }
377
+
378
+ // Returns true/false if cache is fresh, null if cache is missing or expired.
379
+ // Cache stores full SHA-256 key_hash (not plaintext, not truncated) to detect key changes.
380
+ function readValidationCache(key) {
381
+ try {
382
+ if (!fs.existsSync(VALIDATION_CACHE_FILE)) return null;
383
+ const cache = JSON.parse(fs.readFileSync(VALIDATION_CACHE_FILE, 'utf8'));
384
+ const nowSec = Math.floor(Date.now() / 1000);
385
+ const { createHash } = require('crypto');
386
+ const keyHash = createHash('sha256').update(key).digest('hex');
387
+ if (cache.key_hash !== keyHash) return null;
388
+ // Reject future timestamps — prevents cache poisoning via pre-written cache files
389
+ if (typeof cache.timestamp !== 'number' || cache.timestamp > nowSec) return null;
390
+ if ((nowSec - cache.timestamp) >= VALIDATION_CACHE_TTL) return null;
391
+ return cache.valid === true;
392
+ } catch (e) {
393
+ return null;
394
+ }
395
+ }
396
+
397
+ function writeValidationCache(key) {
398
+ try {
399
+ fs.mkdirSync(ARTHAI_DIR, { recursive: true });
400
+ const { createHash } = require('crypto');
401
+ // Full SHA-256 hash — not truncated, prevents brute-force of short hashes
402
+ const keyHash = createHash('sha256').update(key).digest('hex');
403
+ const cache = { key_hash: keyHash, valid: true, timestamp: Math.floor(Date.now() / 1000) };
404
+ fs.writeFileSync(VALIDATION_CACHE_FILE, JSON.stringify(cache) + '\n', { mode: 0o600 });
405
+ } catch (e) {
406
+ // Non-fatal — validation will just re-check next time
407
+ }
408
+ }
409
+
410
+ function computeMachineId() {
411
+ try {
412
+ const { createHash } = require('crypto');
413
+ const hostname = require('os').hostname();
414
+ const username = process.env.USER || process.env.USERNAME || '';
415
+ return createHash('sha256').update(hostname + username).digest('hex');
416
+ } catch (e) {
417
+ return 'unknown';
418
+ }
419
+ }
420
+
214
421
  // ── uninstall ────────────────────────────────────────────────────────────────
215
422
 
216
423
  function handleUninstall(rawArgs) {
@@ -406,6 +613,95 @@ function installBundle(pluginDir, claudeDir, skillsDir, agentsDir, name) {
406
613
  return counts;
407
614
  }
408
615
 
616
+ // ── project setup (M6 — parity with install.sh) ───────────────────────────────
617
+
618
+ const MANAGED_BLOCK_START = '<!-- >>> claude-agents toolkit (DO NOT EDIT THIS BLOCK) >>> -->';
619
+ const MANAGED_BLOCK_END = '<!-- <<< claude-agents toolkit <<< -->';
620
+ const GITIGNORE_BLOCK_START = '# >>> claude-agents managed (DO NOT EDIT THIS BLOCK) >>>';
621
+ const GITIGNORE_BLOCK_END = '# <<< claude-agents managed <<<';
622
+
623
+ function setupProjectClaude(targetDir) {
624
+ const claudeDir = path.join(targetDir, '.claude');
625
+ const version = readVersion();
626
+
627
+ // 1. CLAUDE.md — create from template if missing, inject managed block if absent
628
+ const claudeMdPath = path.join(targetDir, 'CLAUDE.md');
629
+ const templatePath = path.join(TOOLKIT_DIR, 'templates', 'CLAUDE.md.template');
630
+ const managedBlockPath = path.join(TOOLKIT_DIR, 'templates', 'CLAUDE.md.managed-block');
631
+
632
+ if (!fs.existsSync(claudeMdPath) && fs.existsSync(templatePath)) {
633
+ const projectName = path.basename(targetDir);
634
+ const content = fs.readFileSync(templatePath, 'utf8').replace(/\{\{PROJECT_NAME\}\}/g, projectName);
635
+ fs.writeFileSync(claudeMdPath, content);
636
+ console.log(' Created CLAUDE.md from template');
637
+ }
638
+
639
+ if (fs.existsSync(claudeMdPath) && fs.existsSync(managedBlockPath)) {
640
+ injectOrUpdateManagedBlock(claudeMdPath, managedBlockPath, version);
641
+ }
642
+
643
+ // 2. .gitignore — inject toolkit marker block if missing
644
+ const gitignorePath = path.join(targetDir, '.gitignore');
645
+ injectGitignoreBlock(gitignorePath);
646
+
647
+ // 3. .claude/.claude-agents.conf — record installed bundles for re-sync tracking
648
+ // (Only write if we've already created the .claude dir; it should exist by now)
649
+ if (fs.existsSync(claudeDir)) {
650
+ const confPath = path.join(claudeDir, '.claude-agents.conf');
651
+ if (!fs.existsSync(confPath)) {
652
+ fs.writeFileSync(confPath, `# claude-agents configuration\nversion=${version}\n`);
653
+ }
654
+ }
655
+ }
656
+
657
+ function injectOrUpdateManagedBlock(claudeMdPath, managedBlockPath, version) {
658
+ const existing = fs.readFileSync(claudeMdPath, 'utf8');
659
+ const blockContent = fs.readFileSync(managedBlockPath, 'utf8').trimEnd();
660
+
661
+ const newBlock = `${MANAGED_BLOCK_START}\n<!-- version: ${version} -->\n${blockContent}\n${MANAGED_BLOCK_END}`;
662
+
663
+ const startIdx = existing.indexOf(MANAGED_BLOCK_START);
664
+ const endIdx = existing.indexOf(MANAGED_BLOCK_END);
665
+
666
+ if (startIdx === -1) {
667
+ // Block missing — append it
668
+ const separator = existing.endsWith('\n') ? '\n' : '\n\n';
669
+ fs.writeFileSync(claudeMdPath, existing + separator + newBlock + '\n');
670
+ console.log(' Injected toolkit managed block into CLAUDE.md');
671
+ } else if (startIdx !== -1 && endIdx !== -1) {
672
+ // Block exists — check if version is stale
673
+ const existingBlock = existing.slice(startIdx, endIdx + MANAGED_BLOCK_END.length);
674
+ const versionMatch = existingBlock.match(/<!-- version: ([^>]+) -->/);
675
+ const existingVersion = versionMatch ? versionMatch[1].trim() : null;
676
+ if (existingVersion !== version) {
677
+ const updated = existing.slice(0, startIdx) + newBlock + existing.slice(endIdx + MANAGED_BLOCK_END.length);
678
+ fs.writeFileSync(claudeMdPath, updated);
679
+ console.log(` Updated toolkit managed block in CLAUDE.md (${existingVersion} → ${version})`);
680
+ }
681
+ }
682
+ }
683
+
684
+ function injectGitignoreBlock(gitignorePath) {
685
+ const lines = [
686
+ GITIGNORE_BLOCK_START,
687
+ '.claude/.toolkit-last-seen-sha',
688
+ '.claude/.toolkit-setup-done',
689
+ '.claude/.claude-agents.conf',
690
+ GITIGNORE_BLOCK_END,
691
+ ].join('\n');
692
+
693
+ if (!fs.existsSync(gitignorePath)) {
694
+ fs.writeFileSync(gitignorePath, lines + '\n');
695
+ return;
696
+ }
697
+
698
+ const existing = fs.readFileSync(gitignorePath, 'utf8');
699
+ if (existing.includes(GITIGNORE_BLOCK_START)) return; // Already present
700
+
701
+ const separator = existing.endsWith('\n') ? '\n' : '\n\n';
702
+ fs.writeFileSync(gitignorePath, existing + separator + lines + '\n');
703
+ }
704
+
409
705
  function copyDirRecursive(src, dest) {
410
706
  for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
411
707
  const srcPath = path.join(src, entry.name);
@@ -432,6 +728,7 @@ function printUsage() {
432
728
  console.log(`arthai v${version} — AI-powered development toolkit for Claude Code
433
729
 
434
730
  Usage:
731
+ arthai activate <key> Activate your license key
435
732
  arthai install <bundle> [bundle...] [path] Install bundle(s) into a project
436
733
  arthai list List available bundles
437
734
  arthai info <bundle> Show bundle details
@@ -11,6 +11,6 @@
11
11
  "skills": [
12
12
  "skills/planning"
13
13
  ],
14
- "hooks": [],
14
+ "hooks": ["project-setup"],
15
15
  "dependencies": []
16
16
  }
@@ -11,6 +11,6 @@
11
11
  "skills": [
12
12
  "skills/planning"
13
13
  ],
14
- "hooks": [],
14
+ "hooks": ["project-setup"],
15
15
  "dependencies": []
16
16
  }
@@ -24,6 +24,7 @@
24
24
  "skills/templates"
25
25
  ],
26
26
  "hooks": [
27
+ "project-setup",
27
28
  "ensure-client-dir",
28
29
  "check-deliverable"
29
30
  ],
@@ -6,6 +6,6 @@
6
6
  "skills": [
7
7
  "skills/autopilot"
8
8
  ],
9
- "hooks": [],
9
+ "hooks": ["project-setup"],
10
10
  "dependencies": ["forge", "scalpel", "sentinel"]
11
11
  }
@@ -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
  }
@@ -16,6 +16,7 @@
16
16
  "skills/qa-incident"
17
17
  ],
18
18
  "hooks": [
19
+ "project-setup",
19
20
  "post-test-summary",
20
21
  "post-diff-test-compare"
21
22
  ],
@@ -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
- "pre-edit-guard"
15
+ "project-setup",
16
+ "pre-edit-guard",
17
+ "escalation-guard"
15
18
  ],
16
19
  "dependencies": []
17
20
  }
@@ -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
  }
@@ -5,6 +5,7 @@
5
5
  "agents": [],
6
6
  "skills": [],
7
7
  "hooks": [
8
+ "project-setup",
8
9
  "pre-bash-guard",
9
10
  "pre-edit-guard",
10
11
  "session-bootstrap",
@@ -13,6 +13,7 @@
13
13
  "skills/setup"
14
14
  ],
15
15
  "hooks": [
16
+ "project-setup",
16
17
  "session-bootstrap"
17
18
  ],
18
19
  "dependencies": []