@soleri/cli 9.14.3 → 9.16.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.
Files changed (87) hide show
  1. package/dist/commands/add-domain.js +65 -0
  2. package/dist/commands/add-domain.js.map +1 -1
  3. package/dist/commands/agent.js +51 -20
  4. package/dist/commands/agent.js.map +1 -1
  5. package/dist/commands/brain.d.ts +8 -0
  6. package/dist/commands/brain.js +83 -0
  7. package/dist/commands/brain.js.map +1 -0
  8. package/dist/commands/chat.d.ts +11 -0
  9. package/dist/commands/chat.js +295 -0
  10. package/dist/commands/chat.js.map +1 -0
  11. package/dist/commands/create.js +11 -9
  12. package/dist/commands/create.js.map +1 -1
  13. package/dist/commands/dev.js +19 -0
  14. package/dist/commands/dev.js.map +1 -1
  15. package/dist/commands/dream.js +1 -12
  16. package/dist/commands/dream.js.map +1 -1
  17. package/dist/commands/hooks.js +29 -0
  18. package/dist/commands/hooks.js.map +1 -1
  19. package/dist/commands/install.js +3 -9
  20. package/dist/commands/install.js.map +1 -1
  21. package/dist/commands/knowledge.d.ts +9 -0
  22. package/dist/commands/knowledge.js +99 -0
  23. package/dist/commands/knowledge.js.map +1 -0
  24. package/dist/commands/pack.js +164 -3
  25. package/dist/commands/pack.js.map +1 -1
  26. package/dist/commands/schedule.d.ts +11 -0
  27. package/dist/commands/schedule.js +130 -0
  28. package/dist/commands/schedule.js.map +1 -0
  29. package/dist/commands/staging.d.ts +2 -17
  30. package/dist/commands/staging.js +4 -4
  31. package/dist/commands/staging.js.map +1 -1
  32. package/dist/commands/validate-skills.d.ts +10 -0
  33. package/dist/commands/validate-skills.js +47 -0
  34. package/dist/commands/validate-skills.js.map +1 -0
  35. package/dist/commands/vault.js +2 -11
  36. package/dist/commands/vault.js.map +1 -1
  37. package/dist/main.js +10 -0
  38. package/dist/main.js.map +1 -1
  39. package/dist/utils/checks.js +17 -32
  40. package/dist/utils/checks.js.map +1 -1
  41. package/dist/utils/core-resolver.d.ts +3 -0
  42. package/dist/utils/core-resolver.js +38 -0
  43. package/dist/utils/core-resolver.js.map +1 -0
  44. package/dist/utils/vault-db.d.ts +5 -0
  45. package/dist/utils/vault-db.js +17 -0
  46. package/dist/utils/vault-db.js.map +1 -0
  47. package/package.json +1 -1
  48. package/src/__tests__/doctor.test.ts +46 -1
  49. package/src/__tests__/hook-packs.test.ts +0 -18
  50. package/src/__tests__/hooks-convert.test.ts +0 -28
  51. package/src/__tests__/hooks-sync.test.ts +109 -0
  52. package/src/__tests__/hooks.test.ts +0 -20
  53. package/src/__tests__/install-verify.test.ts +1 -1
  54. package/src/__tests__/install.test.ts +7 -10
  55. package/src/__tests__/update.test.ts +0 -19
  56. package/src/__tests__/validator.test.ts +0 -16
  57. package/src/commands/add-domain.ts +89 -1
  58. package/src/commands/agent.ts +53 -17
  59. package/src/commands/brain.ts +93 -0
  60. package/src/commands/chat.ts +373 -0
  61. package/src/commands/create.ts +11 -8
  62. package/src/commands/dev.ts +21 -0
  63. package/src/commands/dream.ts +1 -11
  64. package/src/commands/hooks.ts +32 -0
  65. package/src/commands/install.ts +3 -8
  66. package/src/commands/knowledge.ts +124 -0
  67. package/src/commands/pack.ts +219 -1
  68. package/src/commands/schedule.ts +150 -0
  69. package/src/commands/staging.ts +5 -5
  70. package/src/commands/validate-skills.ts +58 -0
  71. package/src/commands/vault.ts +2 -11
  72. package/src/main.ts +10 -0
  73. package/src/utils/checks.ts +18 -30
  74. package/src/utils/core-resolver.ts +39 -0
  75. package/src/utils/vault-db.ts +15 -0
  76. package/dist/hook-packs/converter/template.test.ts +0 -133
  77. package/dist/hook-packs/yolo-safety/scripts/anti-deletion.sh +0 -274
  78. package/dist/prompts/archetypes.d.ts +0 -22
  79. package/dist/prompts/archetypes.js +0 -298
  80. package/dist/prompts/archetypes.js.map +0 -1
  81. package/dist/prompts/playbook.d.ts +0 -64
  82. package/dist/prompts/playbook.js +0 -436
  83. package/dist/prompts/playbook.js.map +0 -1
  84. package/dist/utils/format-paths.d.ts +0 -14
  85. package/dist/utils/format-paths.js +0 -27
  86. package/dist/utils/format-paths.js.map +0 -1
  87. package/src/__tests__/dream.test.ts +0 -119
@@ -4,8 +4,9 @@
4
4
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
5
5
  import { join } from 'node:path';
6
6
  import { execFileSync } from 'node:child_process';
7
- import { homedir } from 'node:os';
8
7
  import { detectAgent, type AgentFormat } from './agent-context.js';
8
+ import { detectArtifacts } from './agent-artifacts.js';
9
+ import { resolveCorePackageJsonPath } from './core-resolver.js';
9
10
  import { getInstalledPacks } from '../hook-packs/registry.js';
10
11
 
11
12
  export interface CheckResult {
@@ -166,49 +167,36 @@ export function checkInstructionsDir(agentPath: string): CheckResult {
166
167
  }
167
168
 
168
169
  export function checkEngineReachable(): CheckResult {
169
- try {
170
- require.resolve('@soleri/core/package.json');
170
+ if (resolveCorePackageJsonPath() !== null) {
171
171
  return { status: 'pass', label: 'Engine', detail: '@soleri/core reachable' };
172
- } catch {
173
- return {
174
- status: 'fail',
175
- label: 'Engine',
176
- detail: '@soleri/core not found — engine is required for file-tree agents',
177
- };
178
172
  }
173
+
174
+ return {
175
+ status: 'fail',
176
+ label: 'Engine',
177
+ detail: '@soleri/core not found — engine is required for file-tree agents',
178
+ };
179
179
  }
180
180
 
181
181
  function checkMcpRegistration(dir?: string): CheckResult {
182
182
  const ctx = detectAgent(dir);
183
183
  if (!ctx) return { status: 'warn', label: 'MCP registration', detail: 'no agent detected' };
184
184
 
185
- const claudeJsonPath = join(homedir(), '.claude.json');
186
- if (!existsSync(claudeJsonPath)) {
185
+ const artifacts = detectArtifacts(ctx.agentId, ctx.agentPath);
186
+ if (artifacts.mcpServerEntries.length === 0) {
187
187
  return {
188
188
  status: 'warn',
189
189
  label: 'MCP registration',
190
- detail: '~/.claude.json not found',
190
+ detail: `not found in ~/.claude.json, ~/.codex/config.toml, or ~/.config/opencode/opencode.json`,
191
191
  };
192
192
  }
193
193
 
194
- try {
195
- const config = JSON.parse(readFileSync(claudeJsonPath, 'utf-8'));
196
- const servers = config.mcpServers ?? {};
197
- if (ctx.agentId in servers) {
198
- return {
199
- status: 'pass',
200
- label: 'MCP registration',
201
- detail: `registered as "${ctx.agentId}"`,
202
- };
203
- }
204
- return {
205
- status: 'warn',
206
- label: 'MCP registration',
207
- detail: `"${ctx.agentId}" not found in ~/.claude.json`,
208
- };
209
- } catch {
210
- return { status: 'fail', label: 'MCP registration', detail: 'failed to parse ~/.claude.json' };
211
- }
194
+ const targets = [...new Set(artifacts.mcpServerEntries.map((entry) => entry.target))];
195
+ return {
196
+ status: 'pass',
197
+ label: 'MCP registration',
198
+ detail: `registered in ${targets.join(', ')}`,
199
+ };
212
200
  }
213
201
 
214
202
  function checkCognee(): CheckResult {
@@ -0,0 +1,39 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ function resolveCoreEntryPath(): string | null {
6
+ try {
7
+ return fileURLToPath(import.meta.resolve('@soleri/core'));
8
+ } catch {
9
+ return null;
10
+ }
11
+ }
12
+
13
+ export function resolveCorePackageJsonPath(): string | null {
14
+ const entryPath = resolveCoreEntryPath();
15
+ if (!entryPath) return null;
16
+
17
+ const packageJsonPath = join(dirname(entryPath), '..', 'package.json');
18
+ return existsSync(packageJsonPath) ? packageJsonPath : null;
19
+ }
20
+
21
+ export function readInstalledCoreVersion(): string | null {
22
+ const packageJsonPath = resolveCorePackageJsonPath();
23
+ if (!packageJsonPath) return null;
24
+
25
+ try {
26
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as { version?: unknown };
27
+ return typeof pkg.version === 'string' ? pkg.version : null;
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+
33
+ export function resolveInstalledEngineBin(): string | null {
34
+ const entryPath = resolveCoreEntryPath();
35
+ if (!entryPath) return null;
36
+
37
+ const engineBinPath = join(dirname(entryPath), 'engine', 'bin', 'soleri-engine.js');
38
+ return existsSync(engineBinPath) ? engineBinPath : null;
39
+ }
@@ -0,0 +1,15 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { SOLERI_HOME } from '@soleri/core';
4
+
5
+ /**
6
+ * Resolve the vault DB path for a given agent.
7
+ * Checks the current path first, then falls back to the legacy dot-prefixed path.
8
+ */
9
+ export function resolveVaultDbPath(agentId: string): string | null {
10
+ const newDbPath = join(SOLERI_HOME, agentId, 'vault.db');
11
+ const legacyDbPath = join(SOLERI_HOME, '..', `.${agentId}`, 'vault.db');
12
+ if (existsSync(newDbPath)) return newDbPath;
13
+ if (existsSync(legacyDbPath)) return legacyDbPath;
14
+ return null;
15
+ }
@@ -1,133 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { generateHookScript, generateManifest, HOOK_EVENTS, ACTION_LEVELS } from './template.js';
3
- import type { HookConversionConfig } from './template.js';
4
-
5
- describe('generateHookScript', () => {
6
- const baseConfig: HookConversionConfig = {
7
- name: 'test-hook',
8
- event: 'PreToolUse',
9
- toolMatcher: 'Write|Edit',
10
- filePatterns: ['**/marketing/**'],
11
- action: 'remind',
12
- message: 'Check brand guidelines before editing marketing files',
13
- };
14
-
15
- it('should generate a valid POSIX shell script', () => {
16
- const script = generateHookScript(baseConfig);
17
- expect(script).toContain('#!/bin/sh');
18
- expect(script).toContain('set -eu');
19
- expect(script).toContain('INPUT=$(cat)');
20
- });
21
-
22
- it('should include tool matcher for PreToolUse', () => {
23
- const script = generateHookScript(baseConfig);
24
- expect(script).toContain('TOOL_NAME=');
25
- expect(script).toContain('Write|Edit');
26
- expect(script).toContain('case "$TOOL_NAME" in');
27
- });
28
-
29
- it('should include file pattern matching', () => {
30
- const script = generateHookScript(baseConfig);
31
- expect(script).toContain('FILE_PATH=');
32
- expect(script).toContain('MATCHED=false');
33
- expect(script).toContain('marketing');
34
- });
35
-
36
- it('should output remind action by default', () => {
37
- const script = generateHookScript(baseConfig);
38
- expect(script).toContain('REMINDER:');
39
- expect(script).toContain('continue: true');
40
- });
41
-
42
- it('should output warn action', () => {
43
- const script = generateHookScript({ ...baseConfig, action: 'warn' });
44
- expect(script).toContain('WARNING:');
45
- expect(script).toContain('continue: true');
46
- });
47
-
48
- it('should output block action', () => {
49
- const script = generateHookScript({ ...baseConfig, action: 'block' });
50
- expect(script).toContain('BLOCKED:');
51
- expect(script).toContain('continue: false');
52
- });
53
-
54
- it('should skip tool matcher for non-tool events', () => {
55
- const script = generateHookScript({ ...baseConfig, event: 'PreCompact' });
56
- expect(script).not.toContain('TOOL_NAME');
57
- expect(script).not.toContain('case');
58
- });
59
-
60
- it('should skip file pattern matching when no patterns', () => {
61
- const script = generateHookScript({ ...baseConfig, filePatterns: undefined });
62
- expect(script).not.toContain('FILE_PATH');
63
- expect(script).not.toContain('MATCHED');
64
- });
65
-
66
- it('should generate scripts for all 5 hook events', () => {
67
- for (const event of HOOK_EVENTS) {
68
- const script = generateHookScript({ ...baseConfig, event });
69
- expect(script).toContain(`# Event: ${event}`);
70
- expect(script).toContain('#!/bin/sh');
71
- }
72
- });
73
-
74
- it('should escape single quotes in messages', () => {
75
- const script = generateHookScript({ ...baseConfig, message: "Don't forget the guidelines" });
76
- // Should not have unbalanced quotes
77
- expect(script).toContain('forget');
78
- });
79
- });
80
-
81
- describe('generateManifest', () => {
82
- const config: HookConversionConfig = {
83
- name: 'my-hook',
84
- event: 'PreToolUse',
85
- toolMatcher: 'Write',
86
- action: 'remind',
87
- message: 'Test message',
88
- };
89
-
90
- it('should generate valid manifest with required fields', () => {
91
- const manifest = generateManifest(config);
92
- expect(manifest.name).toBe('my-hook');
93
- expect(manifest.version).toBe('1.0.0');
94
- expect(manifest.hooks).toEqual([]);
95
- expect(manifest.scripts).toHaveLength(1);
96
- expect(manifest.lifecycleHooks).toHaveLength(1);
97
- });
98
-
99
- it('should set script name and file correctly', () => {
100
- const manifest = generateManifest(config);
101
- expect(manifest.scripts![0].name).toBe('my-hook');
102
- expect(manifest.scripts![0].file).toBe('my-hook.sh');
103
- expect(manifest.scripts![0].targetDir).toBe('hooks');
104
- });
105
-
106
- it('should set lifecycle hook event and command', () => {
107
- const manifest = generateManifest(config);
108
- const lc = manifest.lifecycleHooks![0];
109
- expect(lc.event).toBe('PreToolUse');
110
- expect(lc.command).toBe('sh ~/.claude/hooks/my-hook.sh');
111
- expect(lc.type).toBe('command');
112
- expect(lc.timeout).toBe(10);
113
- });
114
-
115
- it('should use description from config or fallback to message', () => {
116
- expect(generateManifest(config).description).toBe('Test message');
117
- expect(generateManifest({ ...config, description: 'Custom desc' }).description).toBe(
118
- 'Custom desc',
119
- );
120
- });
121
-
122
- it('should include actionLevel', () => {
123
- expect(generateManifest(config).actionLevel).toBe('remind');
124
- expect(generateManifest({ ...config, action: 'block' }).actionLevel).toBe('block');
125
- });
126
-
127
- it('should generate manifests for all action levels', () => {
128
- for (const action of ACTION_LEVELS) {
129
- const manifest = generateManifest({ ...config, action });
130
- expect(manifest.actionLevel).toBe(action);
131
- }
132
- });
133
- });
@@ -1,274 +0,0 @@
1
- #!/bin/sh
2
- # Anti-Deletion Staging Hook for Claude Code (Soleri Hook Pack: yolo-safety)
3
- # PreToolUse -> Bash: intercepts destructive commands, stages files, blocks execution.
4
- #
5
- # Intercepted patterns:
6
- # - rm / rmdir (files/dirs — stages first, then blocks)
7
- # - git push --force (blocks outright)
8
- # - git reset --hard (blocks outright)
9
- # - git clean (blocks outright)
10
- # - git checkout -- . (blocks outright)
11
- # - git restore . (blocks outright)
12
- # - mv ~/projects/... (blocks outright)
13
- # - drop table (SQL — blocks outright)
14
- # - docker rm / rmi (blocks outright)
15
- #
16
- # Catastrophic commands (rm -rf /, rm -rf ~) should stay in deny rules —
17
- # this hook handles targeted deletes only.
18
- #
19
- # Dependencies: jq (required)
20
- # POSIX sh compatible — no bash-specific features.
21
-
22
- set -eu
23
-
24
- STAGING_ROOT="$HOME/.soleri/staging"
25
- INPUT=$(cat)
26
-
27
- # Extract the command from stdin JSON
28
- CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
29
-
30
- # No command found — let it through
31
- if [ -z "$CMD" ]; then
32
- exit 0
33
- fi
34
-
35
- # --- Strip heredocs and quoted strings to avoid false positives ---
36
- # Commands like: gh issue comment --body "$(cat <<'EOF' ... rmdir ... EOF)"
37
- # contain destructive keywords in text, not as actual commands.
38
-
39
- # Remove heredoc blocks (best-effort with sed)
40
- STRIPPED=$(printf '%s' "$CMD" | sed -e "s/<<'[A-Za-z_]*'.*//g" -e 's/<<[A-Za-z_]*.*//g' 2>/dev/null || printf '%s' "$CMD")
41
- # Remove double-quoted strings
42
- STRIPPED=$(printf '%s' "$STRIPPED" | sed 's/"[^"]*"//g' 2>/dev/null || printf '%s' "$STRIPPED")
43
- # Remove single-quoted strings
44
- STRIPPED=$(printf '%s' "$STRIPPED" | sed "s/'[^']*'//g" 2>/dev/null || printf '%s' "$STRIPPED")
45
-
46
- # --- Helper: check if pattern matches stripped command ---
47
- matches() {
48
- printf '%s' "$STRIPPED" | grep -qE "$1"
49
- }
50
-
51
- # --- Detect destructive commands (on stripped command only) ---
52
-
53
- IS_RM=false
54
- IS_RMDIR=false
55
- IS_MV_PROJECT=false
56
- IS_GIT_CLEAN=false
57
- IS_RESET_HARD=false
58
- IS_GIT_CHECKOUT_DOT=false
59
- IS_GIT_RESTORE_DOT=false
60
- IS_GIT_PUSH_FORCE=false
61
- IS_DROP_TABLE=false
62
- IS_DOCKER_RM=false
63
-
64
- # rm (but not git rm which stages, doesn't destroy)
65
- if matches '(^|\s|;|&&|\|\|)rm\s'; then
66
- if ! matches '(^|\s)git\s+rm\s'; then
67
- IS_RM=true
68
- fi
69
- fi
70
-
71
- # rmdir
72
- if matches '(^|\s|;|&&|\|\|)rmdir\s'; then
73
- IS_RMDIR=true
74
- fi
75
-
76
- # mv of project directories or git repos
77
- if matches '(^|\s|;|&&|\|\|)mv\s'; then
78
- MV_TAIL=$(printf '%s' "$STRIPPED" | sed 's/^.*\bmv //' | sed 's/-[finv] //g')
79
- if printf '%s' "$MV_TAIL" | grep -qE '(~/projects|\.git)'; then
80
- IS_MV_PROJECT=true
81
- fi
82
- fi
83
-
84
- # git clean
85
- if matches '(^|\s|;|&&|\|\|)git\s+clean\b'; then
86
- IS_GIT_CLEAN=true
87
- fi
88
-
89
- # git reset --hard
90
- if matches '(^|\s|;|&&|\|\|)git\s+reset\s+--hard'; then
91
- IS_RESET_HARD=true
92
- fi
93
-
94
- # git checkout -- .
95
- if matches '(^|\s|;|&&|\|\|)git\s+checkout\s+--\s+\.'; then
96
- IS_GIT_CHECKOUT_DOT=true
97
- fi
98
-
99
- # git restore .
100
- if matches '(^|\s|;|&&|\|\|)git\s+restore\s+\.'; then
101
- IS_GIT_RESTORE_DOT=true
102
- fi
103
-
104
- # git push --force / -f (but not --force-with-lease which is safer)
105
- if matches '(^|\s|;|&&|\|\|)git\s+push\s'; then
106
- if matches 'git\s+push\s.*--force([^-]|$)' || matches 'git\s+push\s+-f(\s|$)' || matches 'git\s+push\s.*\s-f(\s|$)'; then
107
- IS_GIT_PUSH_FORCE=true
108
- fi
109
- fi
110
-
111
- # SQL drop table (case-insensitive)
112
- if printf '%s' "$STRIPPED" | grep -qiE '(^|\s|;)drop\s+table'; then
113
- IS_DROP_TABLE=true
114
- fi
115
-
116
- # docker rm / docker rmi
117
- if matches '(^|\s|;|&&|\|\|)docker\s+(rm|rmi)\b'; then
118
- IS_DOCKER_RM=true
119
- fi
120
-
121
- # --- Not a destructive command — let it through ---
122
-
123
- if [ "$IS_RM" = false ] && [ "$IS_RMDIR" = false ] && [ "$IS_MV_PROJECT" = false ] && \
124
- [ "$IS_GIT_CLEAN" = false ] && [ "$IS_RESET_HARD" = false ] && \
125
- [ "$IS_GIT_CHECKOUT_DOT" = false ] && [ "$IS_GIT_RESTORE_DOT" = false ] && \
126
- [ "$IS_GIT_PUSH_FORCE" = false ] && [ "$IS_DROP_TABLE" = false ] && \
127
- [ "$IS_DOCKER_RM" = false ]; then
128
- exit 0
129
- fi
130
-
131
- # --- Block: git clean ---
132
- if [ "$IS_GIT_CLEAN" = true ]; then
133
- jq -n '{
134
- continue: false,
135
- stopReason: "BLOCKED: git clean would remove untracked files. Use git stash --include-untracked to save them first, or ask the user to run git clean manually."
136
- }'
137
- exit 0
138
- fi
139
-
140
- # --- Block: git reset --hard ---
141
- if [ "$IS_RESET_HARD" = true ]; then
142
- jq -n '{
143
- continue: false,
144
- stopReason: "BLOCKED: git reset --hard would discard uncommitted changes. Use git stash to save them first, or ask the user to run this manually."
145
- }'
146
- exit 0
147
- fi
148
-
149
- # --- Block: git checkout -- . ---
150
- if [ "$IS_GIT_CHECKOUT_DOT" = true ]; then
151
- jq -n '{
152
- continue: false,
153
- stopReason: "BLOCKED: git checkout -- . would discard all uncommitted changes. Use git stash to save them first, or ask the user to run this manually."
154
- }'
155
- exit 0
156
- fi
157
-
158
- # --- Block: git restore . ---
159
- if [ "$IS_GIT_RESTORE_DOT" = true ]; then
160
- jq -n '{
161
- continue: false,
162
- stopReason: "BLOCKED: git restore . would discard all uncommitted changes. Use git stash to save them first, or ask the user to run this manually."
163
- }'
164
- exit 0
165
- fi
166
-
167
- # --- Block: git push --force ---
168
- if [ "$IS_GIT_PUSH_FORCE" = true ]; then
169
- jq -n '{
170
- continue: false,
171
- stopReason: "BLOCKED: git push --force can overwrite remote history and cause data loss for collaborators. Use --force-with-lease instead, or ask the user to run this manually."
172
- }'
173
- exit 0
174
- fi
175
-
176
- # --- Block: mv of project directories ---
177
- if [ "$IS_MV_PROJECT" = true ]; then
178
- jq -n '{
179
- continue: false,
180
- stopReason: "BLOCKED: mv of a project directory or git repo detected. Moving project directories can cause data loss if the operation fails midway. Ask the user to run this manually, or use cp + verify + rm instead."
181
- }'
182
- exit 0
183
- fi
184
-
185
- # --- Block: rmdir ---
186
- if [ "$IS_RMDIR" = true ]; then
187
- jq -n '{
188
- continue: false,
189
- stopReason: "BLOCKED: rmdir detected. Removing directories can break project structure. Ask the user to confirm this operation manually."
190
- }'
191
- exit 0
192
- fi
193
-
194
- # --- Block: drop table ---
195
- if [ "$IS_DROP_TABLE" = true ]; then
196
- jq -n '{
197
- continue: false,
198
- stopReason: "BLOCKED: DROP TABLE detected. This would permanently destroy database data. Ask the user to run this SQL statement manually after confirming intent."
199
- }'
200
- exit 0
201
- fi
202
-
203
- # --- Block: docker rm / rmi ---
204
- if [ "$IS_DOCKER_RM" = true ]; then
205
- jq -n '{
206
- continue: false,
207
- stopReason: "BLOCKED: docker rm/rmi detected. Removing containers or images can cause data loss. Ask the user to run this manually."
208
- }'
209
- exit 0
210
- fi
211
-
212
- # --- Handle rm commands — copy to staging, then block ---
213
-
214
- # Create timestamped staging directory
215
- TIMESTAMP=$(date +%Y-%m-%d_%H%M%S)
216
- STAGE_DIR="$STAGING_ROOT/$TIMESTAMP"
217
-
218
- # Extract file paths from the rm command
219
- # Strip rm and its flags, keeping only the file arguments
220
- FILES=$(printf '%s' "$CMD" | sed 's/^.*\brm //' | sed 's/-[rRfivd]* //g' | tr ' ' '\n' | grep -v '^-' | grep -v '^$' || true)
221
-
222
- if [ -z "$FILES" ]; then
223
- jq -n '{
224
- continue: false,
225
- stopReason: "BLOCKED: rm command detected but could not parse file targets. Please specify files explicitly."
226
- }'
227
- exit 0
228
- fi
229
-
230
- STAGED_COUNT=0
231
- STAGED_LIST=""
232
- MISSING_COUNT=0
233
-
234
- mkdir -p "$STAGE_DIR"
235
-
236
- printf '%s\n' "$FILES" | while IFS= read -r filepath; do
237
- # Expand path (handle ~, relative paths)
238
- expanded=$(eval printf '%s' "$filepath" 2>/dev/null || printf '%s' "$filepath")
239
-
240
- if [ -e "$expanded" ]; then
241
- # Preserve directory structure in staging
242
- target_dir="$STAGE_DIR/$(dirname "$expanded")"
243
- mkdir -p "$target_dir"
244
- # COPY instead of MOVE — originals stay intact, staging is a backup
245
- if [ -d "$expanded" ]; then
246
- # Use rsync if available (excludes node_modules/dist/.git), fall back to cp
247
- if command -v rsync >/dev/null 2>&1; then
248
- rsync -a --exclude='node_modules' --exclude='dist' --exclude='.git' "$expanded/" "$target_dir/$(basename "$expanded")/" 2>/dev/null
249
- else
250
- cp -R "$expanded" "$target_dir/" 2>/dev/null
251
- fi
252
- else
253
- cp "$expanded" "$target_dir/" 2>/dev/null
254
- fi
255
- fi
256
- done
257
-
258
- # Count what was staged (check if staging dir has content)
259
- if [ -d "$STAGE_DIR" ] && [ "$(ls -A "$STAGE_DIR" 2>/dev/null)" ]; then
260
- STAGED_COUNT=$(find "$STAGE_DIR" -mindepth 1 -maxdepth 1 | wc -l | tr -d ' ')
261
- fi
262
-
263
- if [ "$STAGED_COUNT" -eq 0 ]; then
264
- # All files were missing — let the rm fail naturally
265
- rmdir "$STAGE_DIR" 2>/dev/null || true
266
- exit 0
267
- fi
268
-
269
- jq -n \
270
- --arg dir "$STAGE_DIR" \
271
- '{
272
- continue: false,
273
- stopReason: ("BLOCKED & BACKED UP: Files copied to " + $dir + ". The originals are untouched. To proceed with deletion, ask the user to run the rm command manually.")
274
- }'
@@ -1,22 +0,0 @@
1
- /**
2
- * Pre-built agent archetypes that pre-fill the wizard.
3
- * Each archetype provides sensible defaults for role, description,
4
- * domains, principles, skills, and greeting — so the user can
5
- * scaffold a full agent with minimal typing.
6
- */
7
- export interface Archetype {
8
- value: string;
9
- label: string;
10
- hint: string;
11
- tier: 'free' | 'premium';
12
- defaults: {
13
- role: string;
14
- description: string;
15
- domains: string[];
16
- principles: string[];
17
- skills: string[];
18
- tone: 'precise' | 'mentor' | 'pragmatic';
19
- greetingTemplate: (name: string) => string;
20
- };
21
- }
22
- export declare const ARCHETYPES: Archetype[];