@hailer/mcp 1.1.11 â 1.1.13
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/dist/app.js +18 -5
- package/dist/bot/bot-config.d.ts +12 -1
- package/dist/bot/bot-config.js +98 -14
- package/dist/bot/bot-manager.d.ts +13 -3
- package/dist/bot/bot-manager.js +80 -25
- package/dist/bot/bot.d.ts +46 -0
- package/dist/bot/bot.js +542 -166
- package/dist/bot/services/message-classifier.js +17 -0
- package/dist/bot/services/permission-guard.d.ts +52 -0
- package/dist/bot/services/permission-guard.js +149 -0
- package/dist/bot/services/types.d.ts +5 -0
- package/dist/bot/services/typing-indicator.d.ts +6 -1
- package/dist/bot/services/typing-indicator.js +19 -3
- package/dist/config.d.ts +6 -1
- package/dist/config.js +43 -0
- package/dist/core.js +3 -6
- package/dist/mcp/UserContextCache.d.ts +5 -0
- package/dist/mcp/UserContextCache.js +51 -19
- package/dist/mcp/hailer-clients.d.ts +19 -1
- package/dist/mcp/hailer-clients.js +157 -20
- package/dist/mcp/session-store.d.ts +68 -0
- package/dist/mcp/session-store.js +169 -0
- package/dist/mcp/signal-handler.js +12 -12
- package/dist/mcp/tool-registry.d.ts +17 -4
- package/dist/mcp/tool-registry.js +37 -7
- package/dist/mcp/tools/activity.js +99 -7
- package/dist/mcp/tools/app-scaffold.js +304 -336
- package/dist/mcp/tools/company.d.ts +9 -0
- package/dist/mcp/tools/company.js +88 -0
- package/dist/mcp/tools/discussion.js +68 -0
- package/dist/mcp/tools/workflow-permissions.d.ts +15 -0
- package/dist/mcp/tools/workflow-permissions.js +204 -0
- package/dist/mcp/tools/workflow.js +57 -18
- package/dist/mcp/utils/index.d.ts +2 -0
- package/dist/mcp/utils/index.js +12 -1
- package/dist/mcp/utils/role-utils.d.ts +74 -0
- package/dist/mcp/utils/role-utils.js +151 -0
- package/dist/mcp/utils/types.d.ts +43 -1
- package/dist/mcp/utils/types.js +14 -0
- package/dist/mcp/webhook-handler.d.ts +6 -0
- package/dist/mcp/webhook-handler.js +11 -0
- package/dist/mcp-server.d.ts +23 -2
- package/dist/mcp-server.js +639 -111
- package/dist/plugins/vipunen/client.d.ts +150 -0
- package/dist/plugins/vipunen/client.js +535 -0
- package/dist/plugins/vipunen/config/schema-config.json +19 -0
- package/dist/plugins/vipunen/config/schema-doc.json +22 -0
- package/dist/plugins/vipunen/index.d.ts +41 -0
- package/dist/plugins/vipunen/index.js +88 -0
- package/dist/plugins/vipunen/tools.d.ts +26 -0
- package/dist/plugins/vipunen/tools.js +501 -0
- package/package.json +2 -1
- package/.claude/.context-watchdog.json +0 -1
- package/.claude/.session-checked +0 -1
- package/.claude/CLAUDE.md +0 -370
- package/.claude/agents/agent-ada-skill-builder.md +0 -94
- package/.claude/agents/agent-alejandro-function-fields.md +0 -342
- package/.claude/agents/agent-bjorn-config-audit.md +0 -103
- package/.claude/agents/agent-builder-agent-creator.md +0 -130
- package/.claude/agents/agent-code-simplifier.md +0 -53
- package/.claude/agents/agent-dmitri-activity-crud.md +0 -159
- package/.claude/agents/agent-giuseppe-app-builder.md +0 -247
- package/.claude/agents/agent-gunther-mcp-tools.md +0 -39
- package/.claude/agents/agent-helga-workflow-config.md +0 -204
- package/.claude/agents/agent-igor-activity-mover-automation.md +0 -125
- package/.claude/agents/agent-ingrid-doc-templates.md +0 -261
- package/.claude/agents/agent-ivan-monolith.md +0 -154
- package/.claude/agents/agent-kenji-data-reader.md +0 -86
- package/.claude/agents/agent-lars-code-inspector.md +0 -102
- package/.claude/agents/agent-marco-mockup-builder.md +0 -110
- package/.claude/agents/agent-marcus-api-documenter.md +0 -323
- package/.claude/agents/agent-marketplace-publisher.md +0 -280
- package/.claude/agents/agent-marketplace-reviewer.md +0 -309
- package/.claude/agents/agent-permissions-handler.md +0 -208
- package/.claude/agents/agent-simple-writer.md +0 -48
- package/.claude/agents/agent-svetlana-code-review.md +0 -171
- package/.claude/agents/agent-tanya-test-runner.md +0 -333
- package/.claude/agents/agent-ui-designer.md +0 -100
- package/.claude/agents/agent-viktor-sql-insights.md +0 -212
- package/.claude/agents/agent-web-search.md +0 -55
- package/.claude/agents/agent-yevgeni-discussions.md +0 -45
- package/.claude/agents/agent-zara-zapier.md +0 -159
- package/.claude/commands/app-squad.md +0 -135
- package/.claude/commands/audit-squad.md +0 -158
- package/.claude/commands/autoplan.md +0 -563
- package/.claude/commands/cleanup-squad.md +0 -98
- package/.claude/commands/config-squad.md +0 -106
- package/.claude/commands/crud-squad.md +0 -87
- package/.claude/commands/data-squad.md +0 -97
- package/.claude/commands/debug-squad.md +0 -303
- package/.claude/commands/doc-squad.md +0 -65
- package/.claude/commands/handoff.md +0 -137
- package/.claude/commands/health.md +0 -49
- package/.claude/commands/help.md +0 -29
- package/.claude/commands/help:agents.md +0 -151
- package/.claude/commands/help:commands.md +0 -78
- package/.claude/commands/help:faq.md +0 -79
- package/.claude/commands/help:plugins.md +0 -50
- package/.claude/commands/help:skills.md +0 -93
- package/.claude/commands/help:tools.md +0 -75
- package/.claude/commands/hotfix-squad.md +0 -112
- package/.claude/commands/integration-squad.md +0 -82
- package/.claude/commands/janitor-squad.md +0 -167
- package/.claude/commands/learn-auto.md +0 -120
- package/.claude/commands/learn.md +0 -120
- package/.claude/commands/mcp-list.md +0 -27
- package/.claude/commands/onboard-squad.md +0 -140
- package/.claude/commands/plan-workspace.md +0 -732
- package/.claude/commands/prd.md +0 -130
- package/.claude/commands/project-status.md +0 -82
- package/.claude/commands/publish.md +0 -138
- package/.claude/commands/recap.md +0 -69
- package/.claude/commands/restore.md +0 -64
- package/.claude/commands/review-squad.md +0 -152
- package/.claude/commands/save.md +0 -24
- package/.claude/commands/stats.md +0 -19
- package/.claude/commands/swarm.md +0 -210
- package/.claude/commands/tool-builder.md +0 -39
- package/.claude/commands/ws-pull.md +0 -44
- package/.claude/hooks/_shared-memory.cjs +0 -305
- package/.claude/hooks/_utils.cjs +0 -108
- package/.claude/hooks/agent-failure-detector.cjs +0 -383
- package/.claude/hooks/agent-usage-logger.cjs +0 -204
- package/.claude/hooks/app-edit-guard.cjs +0 -494
- package/.claude/hooks/auto-learn.cjs +0 -304
- package/.claude/hooks/bash-guard.cjs +0 -272
- package/.claude/hooks/builder-mode-manager.cjs +0 -354
- package/.claude/hooks/bulk-activity-guard.cjs +0 -271
- package/.claude/hooks/context-watchdog.cjs +0 -230
- package/.claude/hooks/delegation-reminder.cjs +0 -465
- package/.claude/hooks/design-system-lint.cjs +0 -271
- package/.claude/hooks/post-scaffold-hook.cjs +0 -181
- package/.claude/hooks/prompt-guard.cjs +0 -354
- package/.claude/hooks/publish-template-guard.cjs +0 -147
- package/.claude/hooks/session-start.cjs +0 -35
- package/.claude/hooks/shared-memory-writer.cjs +0 -147
- package/.claude/hooks/skill-injector.cjs +0 -140
- package/.claude/hooks/skill-usage-logger.cjs +0 -258
- package/.claude/hooks/src-edit-guard.cjs +0 -240
- package/.claude/hooks/sync-marketplace-agents.cjs +0 -346
- package/.claude/settings.json +0 -257
- package/.claude/skills/SDK-activity-patterns/SKILL.md +0 -428
- package/.claude/skills/SDK-document-templates/SKILL.md +0 -1033
- package/.claude/skills/SDK-function-fields/SKILL.md +0 -542
- package/.claude/skills/SDK-generate-skill/SKILL.md +0 -92
- package/.claude/skills/SDK-init-skill/SKILL.md +0 -127
- package/.claude/skills/SDK-insight-queries/SKILL.md +0 -787
- package/.claude/skills/SDK-ws-config-skill/SKILL.md +0 -1139
- package/.claude/skills/agent-structure/SKILL.md +0 -98
- package/.claude/skills/api-documentation-patterns/SKILL.md +0 -474
- package/.claude/skills/chrome-mcp-reference/SKILL.md +0 -370
- package/.claude/skills/delegation-routing/SKILL.md +0 -202
- package/.claude/skills/frontend-design/SKILL.md +0 -254
- package/.claude/skills/hailer-activity-mover/SKILL.md +0 -213
- package/.claude/skills/hailer-api-client/SKILL.md +0 -518
- package/.claude/skills/hailer-app-builder/SKILL.md +0 -1434
- package/.claude/skills/hailer-apps-pictures/SKILL.md +0 -269
- package/.claude/skills/hailer-design-system/SKILL.md +0 -235
- package/.claude/skills/hailer-monolith-automations/SKILL.md +0 -686
- package/.claude/skills/hailer-permissions-system/SKILL.md +0 -121
- package/.claude/skills/hailer-project-protocol/SKILL.md +0 -488
- package/.claude/skills/hailer-rest-api/SKILL.md +0 -61
- package/.claude/skills/hailer-rest-api/hailer-activities.md +0 -184
- package/.claude/skills/hailer-rest-api/hailer-admin.md +0 -473
- package/.claude/skills/hailer-rest-api/hailer-calendar.md +0 -256
- package/.claude/skills/hailer-rest-api/hailer-feed.md +0 -249
- package/.claude/skills/hailer-rest-api/hailer-insights.md +0 -195
- package/.claude/skills/hailer-rest-api/hailer-messaging.md +0 -276
- package/.claude/skills/hailer-rest-api/hailer-workflows.md +0 -283
- package/.claude/skills/insight-join-patterns/SKILL.md +0 -174
- package/.claude/skills/integration-patterns/SKILL.md +0 -421
- package/.claude/skills/json-only-output/SKILL.md +0 -72
- package/.claude/skills/lsp-setup/SKILL.md +0 -160
- package/.claude/skills/mcp-direct-tools/SKILL.md +0 -153
- package/.claude/skills/optional-parameters/SKILL.md +0 -72
- package/.claude/skills/publish-hailer-app/SKILL.md +0 -244
- package/.claude/skills/testing-patterns/SKILL.md +0 -630
- package/.claude/skills/tool-builder/SKILL.md +0 -250
- package/.claude/skills/tool-parameter-usage/SKILL.md +0 -126
- package/.claude/skills/tool-response-verification/SKILL.md +0 -92
- package/.claude/skills/zapier-hailer-patterns/SKILL.md +0 -581
- package/.hailer-mcp-port +0 -1
- package/.mcp.json +0 -13
- package/.opencode/agent/agent-ada-skill-builder.md +0 -35
- package/.opencode/agent/agent-alejandro-function-fields.md +0 -39
- package/.opencode/agent/agent-bjorn-config-audit.md +0 -36
- package/.opencode/agent/agent-builder-agent-creator.md +0 -39
- package/.opencode/agent/agent-code-simplifier.md +0 -31
- package/.opencode/agent/agent-dmitri-activity-crud.md +0 -40
- package/.opencode/agent/agent-giuseppe-app-builder.md +0 -37
- package/.opencode/agent/agent-gunther-mcp-tools.md +0 -39
- package/.opencode/agent/agent-helga-workflow-config.md +0 -204
- package/.opencode/agent/agent-igor-activity-mover-automation.md +0 -46
- package/.opencode/agent/agent-ingrid-doc-templates.md +0 -39
- package/.opencode/agent/agent-ivan-monolith.md +0 -46
- package/.opencode/agent/agent-kenji-data-reader.md +0 -53
- package/.opencode/agent/agent-lars-code-inspector.md +0 -28
- package/.opencode/agent/agent-marco-mockup-builder.md +0 -42
- package/.opencode/agent/agent-marcus-api-documenter.md +0 -53
- package/.opencode/agent/agent-marketplace-publisher.md +0 -44
- package/.opencode/agent/agent-marketplace-reviewer.md +0 -42
- package/.opencode/agent/agent-permissions-handler.md +0 -50
- package/.opencode/agent/agent-simple-writer.md +0 -45
- package/.opencode/agent/agent-svetlana-code-review.md +0 -39
- package/.opencode/agent/agent-tanya-test-runner.md +0 -57
- package/.opencode/agent/agent-ui-designer.md +0 -56
- package/.opencode/agent/agent-viktor-sql-insights.md +0 -34
- package/.opencode/agent/agent-web-search.md +0 -42
- package/.opencode/agent/agent-yevgeni-discussions.md +0 -37
- package/.opencode/agent/agent-zara-zapier.md +0 -53
- package/.opencode/commands/app-squad.md +0 -135
- package/.opencode/commands/audit-squad.md +0 -158
- package/.opencode/commands/autoplan.md +0 -563
- package/.opencode/commands/cleanup-squad.md +0 -98
- package/.opencode/commands/config-squad.md +0 -106
- package/.opencode/commands/crud-squad.md +0 -87
- package/.opencode/commands/data-squad.md +0 -97
- package/.opencode/commands/debug-squad.md +0 -303
- package/.opencode/commands/doc-squad.md +0 -65
- package/.opencode/commands/handoff.md +0 -137
- package/.opencode/commands/health.md +0 -49
- package/.opencode/commands/help-agents.md +0 -151
- package/.opencode/commands/help-commands.md +0 -32
- package/.opencode/commands/help-faq.md +0 -29
- package/.opencode/commands/help-plugins.md +0 -28
- package/.opencode/commands/help-skills.md +0 -7
- package/.opencode/commands/help-tools.md +0 -40
- package/.opencode/commands/help.md +0 -28
- package/.opencode/commands/hotfix-squad.md +0 -112
- package/.opencode/commands/integration-squad.md +0 -82
- package/.opencode/commands/janitor-squad.md +0 -167
- package/.opencode/commands/learn-auto.md +0 -120
- package/.opencode/commands/learn.md +0 -120
- package/.opencode/commands/mcp-list.md +0 -27
- package/.opencode/commands/onboard-squad.md +0 -140
- package/.opencode/commands/plan-workspace.md +0 -732
- package/.opencode/commands/prd.md +0 -131
- package/.opencode/commands/project-status.md +0 -82
- package/.opencode/commands/publish.md +0 -138
- package/.opencode/commands/recap.md +0 -69
- package/.opencode/commands/restore.md +0 -64
- package/.opencode/commands/review-squad.md +0 -152
- package/.opencode/commands/save.md +0 -24
- package/.opencode/commands/stats.md +0 -19
- package/.opencode/commands/swarm.md +0 -210
- package/.opencode/commands/tool-builder.md +0 -39
- package/.opencode/commands/ws-pull.md +0 -44
- package/.opencode/opencode.json +0 -21
- package/inbox/failures.log +0 -1
- package/inbox/usage.jsonl +0 -4
- package/scripts/postinstall.cjs +0 -64
- package/scripts/test-hal-tools.ts +0 -154
|
@@ -1,494 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* <hook-name>app-edit-guard</hook-name>
|
|
4
|
-
*
|
|
5
|
-
* <purpose>
|
|
6
|
-
* Blocks direct edits to Hailer app directories.
|
|
7
|
-
* Forces use of builder agents for app development.
|
|
8
|
-
* </purpose>
|
|
9
|
-
*
|
|
10
|
-
* <triggers>
|
|
11
|
-
* - PreToolUse on Write and Edit tools
|
|
12
|
-
* - Only when file is inside a detected Hailer app directory
|
|
13
|
-
* </triggers>
|
|
14
|
-
*
|
|
15
|
-
* <detection>
|
|
16
|
-
* A directory is a Hailer app if it contains:
|
|
17
|
-
* - public/manifest.json with "appId" field
|
|
18
|
-
* - package.json with @hailer/app-sdk dependency
|
|
19
|
-
* - vite.config.ts referencing hailer
|
|
20
|
-
* </detection>
|
|
21
|
-
*
|
|
22
|
-
* <allowed-when>
|
|
23
|
-
* - Builder agent is active (/tmp/.claude-builder-agent-active exists)
|
|
24
|
-
* - Builder mode enabled for specific app (--builder-on)
|
|
25
|
-
* - App released for manual editing (--release)
|
|
26
|
-
* </allowed-when>
|
|
27
|
-
*
|
|
28
|
-
* <cli-commands>
|
|
29
|
-
* --agent-on/off: Global builder mode (affects all apps + src/)
|
|
30
|
-
* --builder-on/off: Per-app builder mode
|
|
31
|
-
* --release/revoke: Manual editing permission
|
|
32
|
-
* --check: Verify if path is Hailer app
|
|
33
|
-
* --list: Show released apps
|
|
34
|
-
* </cli-commands>
|
|
35
|
-
*
|
|
36
|
-
* <shared-files>
|
|
37
|
-
* /tmp/.claude-builder-agent-active (shared with builder-mode-manager, src-edit-guard)
|
|
38
|
-
* /tmp/.claude-released-apps.json
|
|
39
|
-
* /tmp/.claude-builder-mode/
|
|
40
|
-
* </shared-files>
|
|
41
|
-
*/
|
|
42
|
-
|
|
43
|
-
const fs = require('fs');
|
|
44
|
-
const path = require('path');
|
|
45
|
-
const os = require('os');
|
|
46
|
-
|
|
47
|
-
// Skip in subagent context - subagents can't use AskUserQuestion or Bash to recover
|
|
48
|
-
// Orchestrator should use --agent-on before spawning, but this is a safety fallback
|
|
49
|
-
if (process.env.CLAUDE_AGENT_ID || process.env.CLAUDE_SUBAGENT) {
|
|
50
|
-
console.log(JSON.stringify({ decision: 'allow' }));
|
|
51
|
-
process.exit(0);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const TEMP_DIR = os.tmpdir();
|
|
55
|
-
const RELEASE_TRACKER = path.join(TEMP_DIR, '.claude-released-apps.json');
|
|
56
|
-
const BUILDER_MODE_DIR = path.join(TEMP_DIR, '.claude-builder-mode');
|
|
57
|
-
const BUILDER_AGENT_ACTIVE = path.join(TEMP_DIR, '.claude-builder-agent-active');
|
|
58
|
-
|
|
59
|
-
// Read hook input from stdin
|
|
60
|
-
let input = '';
|
|
61
|
-
process.stdin.setEncoding('utf8');
|
|
62
|
-
process.stdin.on('data', chunk => input += chunk);
|
|
63
|
-
process.stdin.on('end', () => {
|
|
64
|
-
try {
|
|
65
|
-
const data = JSON.parse(input);
|
|
66
|
-
processHook(data);
|
|
67
|
-
} catch (e) {
|
|
68
|
-
// Invalid JSON - fail safe by BLOCKING
|
|
69
|
-
outputBlock('Hook received invalid input - blocking for safety');
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
function outputAllow() {
|
|
74
|
-
console.log(JSON.stringify({ decision: 'allow' }));
|
|
75
|
-
process.exit(0);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function outputBlock(message) {
|
|
79
|
-
console.log(JSON.stringify({
|
|
80
|
-
decision: 'block',
|
|
81
|
-
reason: message
|
|
82
|
-
}));
|
|
83
|
-
process.exit(0);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Check if a directory is a Hailer app by examining its contents
|
|
88
|
-
*/
|
|
89
|
-
function isHailerAppDirectory(dirPath) {
|
|
90
|
-
try {
|
|
91
|
-
// Check 1: public/manifest.json with appId
|
|
92
|
-
const manifestPath = path.join(dirPath, 'public', 'manifest.json');
|
|
93
|
-
if (fs.existsSync(manifestPath)) {
|
|
94
|
-
try {
|
|
95
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
96
|
-
if (manifest.appId || manifest.name) {
|
|
97
|
-
return { isApp: true, name: manifest.name || path.basename(dirPath), reason: 'manifest.json' };
|
|
98
|
-
}
|
|
99
|
-
} catch {
|
|
100
|
-
// Invalid JSON in manifest, still might be a Hailer app structure
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Check 2: package.json with @hailer/app-sdk
|
|
105
|
-
const packagePath = path.join(dirPath, 'package.json');
|
|
106
|
-
if (fs.existsSync(packagePath)) {
|
|
107
|
-
try {
|
|
108
|
-
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
109
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
110
|
-
if (deps['@hailer/app-sdk']) {
|
|
111
|
-
return { isApp: true, name: pkg.name || path.basename(dirPath), reason: '@hailer/app-sdk dependency' };
|
|
112
|
-
}
|
|
113
|
-
} catch {
|
|
114
|
-
// Invalid package.json
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Check 3: vite.config with hailer in comments or specific CORS config
|
|
119
|
-
const viteConfigPath = path.join(dirPath, 'vite.config.ts');
|
|
120
|
-
if (fs.existsSync(viteConfigPath)) {
|
|
121
|
-
try {
|
|
122
|
-
const viteConfig = fs.readFileSync(viteConfigPath, 'utf8');
|
|
123
|
-
if (viteConfig.includes('hailer') || viteConfig.includes('app.hailer.com')) {
|
|
124
|
-
return { isApp: true, name: path.basename(dirPath), reason: 'vite.config.ts hailer reference' };
|
|
125
|
-
}
|
|
126
|
-
} catch {
|
|
127
|
-
// Can't read vite config
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return { isApp: false };
|
|
132
|
-
} catch {
|
|
133
|
-
return { isApp: false };
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Walk up the directory tree to find if file is inside a Hailer app
|
|
139
|
-
*/
|
|
140
|
-
function findHailerAppRoot(filePath) {
|
|
141
|
-
let currentDir = path.dirname(filePath);
|
|
142
|
-
const root = path.parse(currentDir).root;
|
|
143
|
-
|
|
144
|
-
// Walk up max 10 levels
|
|
145
|
-
for (let i = 0; i < 10 && currentDir !== root; i++) {
|
|
146
|
-
const result = isHailerAppDirectory(currentDir);
|
|
147
|
-
if (result.isApp) {
|
|
148
|
-
return { ...result, path: currentDir };
|
|
149
|
-
}
|
|
150
|
-
currentDir = path.dirname(currentDir);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Check if an app directory has been released for manual editing
|
|
158
|
-
*/
|
|
159
|
-
function isAppReleased(appPath) {
|
|
160
|
-
try {
|
|
161
|
-
if (!fs.existsSync(RELEASE_TRACKER)) {
|
|
162
|
-
return false;
|
|
163
|
-
}
|
|
164
|
-
const released = JSON.parse(fs.readFileSync(RELEASE_TRACKER, 'utf8'));
|
|
165
|
-
return released.includes(path.resolve(appPath));
|
|
166
|
-
} catch {
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Check if a builder agent is currently active (global flag)
|
|
173
|
-
* Main Claude enables this before spawning builder agents
|
|
174
|
-
*/
|
|
175
|
-
function isBuilderAgentActive() {
|
|
176
|
-
return fs.existsSync(BUILDER_AGENT_ACTIVE);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Check if builder mode is active for an app directory
|
|
181
|
-
* Builder mode is enabled by creating a marker file in BUILDER_MODE_DIR
|
|
182
|
-
*/
|
|
183
|
-
function isBuilderModeActive(appPath) {
|
|
184
|
-
try {
|
|
185
|
-
if (!fs.existsSync(BUILDER_MODE_DIR)) {
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
// Hash the app path to create a unique marker filename
|
|
189
|
-
const normalizedPath = path.resolve(appPath);
|
|
190
|
-
const markerFile = path.join(BUILDER_MODE_DIR, Buffer.from(normalizedPath).toString('base64').replace(/[/+=]/g, '_'));
|
|
191
|
-
return fs.existsSync(markerFile);
|
|
192
|
-
} catch {
|
|
193
|
-
return false;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Enable builder mode for an app directory
|
|
199
|
-
*/
|
|
200
|
-
function enableBuilderMode(appPath) {
|
|
201
|
-
const normalizedPath = path.resolve(appPath);
|
|
202
|
-
if (!fs.existsSync(BUILDER_MODE_DIR)) {
|
|
203
|
-
fs.mkdirSync(BUILDER_MODE_DIR, { recursive: true });
|
|
204
|
-
}
|
|
205
|
-
const markerFile = path.join(BUILDER_MODE_DIR, Buffer.from(normalizedPath).toString('base64').replace(/[/+=]/g, '_'));
|
|
206
|
-
fs.writeFileSync(markerFile, JSON.stringify({ appPath: normalizedPath, enabledAt: new Date().toISOString() }));
|
|
207
|
-
return markerFile;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Disable builder mode for an app directory
|
|
212
|
-
*/
|
|
213
|
-
function disableBuilderMode(appPath) {
|
|
214
|
-
const normalizedPath = path.resolve(appPath);
|
|
215
|
-
const markerFile = path.join(BUILDER_MODE_DIR, Buffer.from(normalizedPath).toString('base64').replace(/[/+=]/g, '_'));
|
|
216
|
-
if (fs.existsSync(markerFile)) {
|
|
217
|
-
fs.unlinkSync(markerFile);
|
|
218
|
-
return true;
|
|
219
|
-
}
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function processHook(data) {
|
|
224
|
-
const { tool_name, tool_input } = data;
|
|
225
|
-
|
|
226
|
-
// Only guard Write and Edit tools
|
|
227
|
-
if (tool_name !== 'Write' && tool_name !== 'Edit') {
|
|
228
|
-
outputAllow();
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const filePath = tool_input?.file_path;
|
|
233
|
-
if (!filePath) {
|
|
234
|
-
outputAllow();
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Normalize path
|
|
239
|
-
const normalizedPath = path.resolve(filePath);
|
|
240
|
-
|
|
241
|
-
// Find if this file is inside a Hailer app
|
|
242
|
-
const appInfo = findHailerAppRoot(normalizedPath);
|
|
243
|
-
|
|
244
|
-
if (!appInfo) {
|
|
245
|
-
// Not in a Hailer app directory
|
|
246
|
-
outputAllow();
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// Check if a builder agent is globally active - ALLOW
|
|
251
|
-
// Main Claude enables this before spawning builder agents
|
|
252
|
-
if (isBuilderAgentActive()) {
|
|
253
|
-
outputAllow();
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// Check if builder mode is active for this app - ALLOW
|
|
258
|
-
// This is the primary mechanism for spawned builder agents
|
|
259
|
-
if (isBuilderModeActive(appInfo.path)) {
|
|
260
|
-
outputAllow();
|
|
261
|
-
return;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Check if this app has been released for manual editing - ALLOW
|
|
265
|
-
if (isAppReleased(appInfo.path)) {
|
|
266
|
-
outputAllow();
|
|
267
|
-
return;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// BLOCK with helpful message
|
|
271
|
-
outputBlock(`
|
|
272
|
-
đĢ BLOCKED: Direct edit to Hailer app "${appInfo.name}"
|
|
273
|
-
|
|
274
|
-
Detected as Hailer app via: ${appInfo.reason}
|
|
275
|
-
App directory: ${appInfo.path}
|
|
276
|
-
|
|
277
|
-
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
|
|
278
|
-
WHY: Hailer apps must be built by a specialized builder agent
|
|
279
|
-
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
|
|
280
|
-
|
|
281
|
-
The builder agent has:
|
|
282
|
-
âĸ Access to @hailer/app-sdk documentation
|
|
283
|
-
âĸ Live dev server feedback for iteration
|
|
284
|
-
âĸ Proper TypeScript patterns for Hailer apps
|
|
285
|
-
|
|
286
|
-
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
|
|
287
|
-
TO PROCEED: Spawn a builder agent
|
|
288
|
-
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
|
|
289
|
-
|
|
290
|
-
1. Load the spawn skill:
|
|
291
|
-
Skill("spawn-app-builder")
|
|
292
|
-
|
|
293
|
-
2. Follow the skill instructions to spawn the agent
|
|
294
|
-
|
|
295
|
-
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
|
|
296
|
-
OR: Release for manual editing (if user explicitly requests)
|
|
297
|
-
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
|
|
298
|
-
|
|
299
|
-
Ask the user first! If they confirm manual editing, run:
|
|
300
|
-
Bash: node "${process.argv[1].replace(/'/g, "'\\''")}" --release "${appInfo.path}"
|
|
301
|
-
|
|
302
|
-
Then retry your edit.
|
|
303
|
-
`);
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// CLI: Release an app for manual editing
|
|
307
|
-
if (process.argv[2] === '--release' && process.argv[3]) {
|
|
308
|
-
const appPath = path.resolve(process.argv[3]);
|
|
309
|
-
|
|
310
|
-
// Verify it's actually a Hailer app
|
|
311
|
-
const appInfo = isHailerAppDirectory(appPath);
|
|
312
|
-
if (!appInfo.isApp) {
|
|
313
|
-
console.error(`Error: ${appPath} is not a Hailer app directory`);
|
|
314
|
-
process.exit(1);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Load existing releases
|
|
318
|
-
let released = [];
|
|
319
|
-
try {
|
|
320
|
-
if (fs.existsSync(RELEASE_TRACKER)) {
|
|
321
|
-
released = JSON.parse(fs.readFileSync(RELEASE_TRACKER, 'utf8'));
|
|
322
|
-
}
|
|
323
|
-
} catch {
|
|
324
|
-
released = [];
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Add this app if not already released
|
|
328
|
-
if (!released.includes(appPath)) {
|
|
329
|
-
released.push(appPath);
|
|
330
|
-
fs.writeFileSync(RELEASE_TRACKER, JSON.stringify(released, null, 2));
|
|
331
|
-
console.log(`â
Released "${appInfo.name}" for manual editing`);
|
|
332
|
-
console.log(` Path: ${appPath}`);
|
|
333
|
-
} else {
|
|
334
|
-
console.log(`âšī¸ "${appInfo.name}" was already released`);
|
|
335
|
-
}
|
|
336
|
-
process.exit(0);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// CLI: List released apps
|
|
340
|
-
if (process.argv[2] === '--list') {
|
|
341
|
-
try {
|
|
342
|
-
if (fs.existsSync(RELEASE_TRACKER)) {
|
|
343
|
-
const released = JSON.parse(fs.readFileSync(RELEASE_TRACKER, 'utf8'));
|
|
344
|
-
if (released.length === 0) {
|
|
345
|
-
console.log('No apps released for manual editing');
|
|
346
|
-
} else {
|
|
347
|
-
console.log('Released apps:');
|
|
348
|
-
released.forEach(p => console.log(` - ${p}`));
|
|
349
|
-
}
|
|
350
|
-
} else {
|
|
351
|
-
console.log('No apps released for manual editing');
|
|
352
|
-
}
|
|
353
|
-
} catch {
|
|
354
|
-
console.log('No apps released for manual editing');
|
|
355
|
-
}
|
|
356
|
-
process.exit(0);
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// CLI: Enable builder mode for an app
|
|
360
|
-
if (process.argv[2] === '--builder-on' && process.argv[3]) {
|
|
361
|
-
const appPath = path.resolve(process.argv[3]);
|
|
362
|
-
|
|
363
|
-
// Verify it's actually a Hailer app
|
|
364
|
-
const appInfo = isHailerAppDirectory(appPath);
|
|
365
|
-
if (!appInfo.isApp) {
|
|
366
|
-
console.error(`Error: ${appPath} is not a Hailer app directory`);
|
|
367
|
-
process.exit(1);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
enableBuilderMode(appPath);
|
|
371
|
-
console.log(`đ§ Builder mode ENABLED for "${appInfo.name}"`);
|
|
372
|
-
console.log(` Path: ${appPath}`);
|
|
373
|
-
console.log(` Spawned agents can now edit this app`);
|
|
374
|
-
process.exit(0);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// CLI: Disable builder mode for an app
|
|
378
|
-
if (process.argv[2] === '--builder-off' && process.argv[3]) {
|
|
379
|
-
const appPath = path.resolve(process.argv[3]);
|
|
380
|
-
|
|
381
|
-
if (disableBuilderMode(appPath)) {
|
|
382
|
-
console.log(`đ Builder mode DISABLED for: ${appPath}`);
|
|
383
|
-
} else {
|
|
384
|
-
console.log(`âšī¸ Builder mode was not active for: ${appPath}`);
|
|
385
|
-
}
|
|
386
|
-
process.exit(0);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// CLI: Revoke release
|
|
390
|
-
if (process.argv[2] === '--revoke' && process.argv[3]) {
|
|
391
|
-
const appPath = path.resolve(process.argv[3]);
|
|
392
|
-
|
|
393
|
-
try {
|
|
394
|
-
if (fs.existsSync(RELEASE_TRACKER)) {
|
|
395
|
-
let released = JSON.parse(fs.readFileSync(RELEASE_TRACKER, 'utf8'));
|
|
396
|
-
const before = released.length;
|
|
397
|
-
released = released.filter(p => p !== appPath);
|
|
398
|
-
if (released.length < before) {
|
|
399
|
-
fs.writeFileSync(RELEASE_TRACKER, JSON.stringify(released, null, 2));
|
|
400
|
-
console.log(`â
Revoked manual editing for: ${appPath}`);
|
|
401
|
-
} else {
|
|
402
|
-
console.log(`âšī¸ App was not in released list: ${appPath}`);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
} catch {
|
|
406
|
-
console.error('Error reading release tracker');
|
|
407
|
-
}
|
|
408
|
-
process.exit(0);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// CLI: Check if a path is a Hailer app
|
|
412
|
-
if (process.argv[2] === '--check' && process.argv[3]) {
|
|
413
|
-
const checkPath = path.resolve(process.argv[3]);
|
|
414
|
-
const result = isHailerAppDirectory(checkPath);
|
|
415
|
-
if (result.isApp) {
|
|
416
|
-
console.log(`â
Hailer app detected: ${result.name}`);
|
|
417
|
-
console.log(` Detected via: ${result.reason}`);
|
|
418
|
-
console.log(` Builder mode: ${isBuilderModeActive(checkPath) ? 'ACTIVE' : 'Off'}`);
|
|
419
|
-
console.log(` Manual release: ${isAppReleased(checkPath) ? 'Yes' : 'No'}`);
|
|
420
|
-
} else {
|
|
421
|
-
console.log(`â Not a Hailer app: ${checkPath}`);
|
|
422
|
-
}
|
|
423
|
-
process.exit(0);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// CLI: Enable global builder agent mode
|
|
427
|
-
if (process.argv[2] === '--agent-on') {
|
|
428
|
-
fs.writeFileSync(BUILDER_AGENT_ACTIVE, JSON.stringify({ enabledAt: new Date().toISOString() }));
|
|
429
|
-
console.log('đ§ Builder agent mode ENABLED globally');
|
|
430
|
-
console.log(' All Hailer app edits are now allowed');
|
|
431
|
-
console.log(' Run --agent-off when done');
|
|
432
|
-
process.exit(0);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// CLI: Disable global builder agent mode
|
|
436
|
-
if (process.argv[2] === '--agent-off') {
|
|
437
|
-
if (fs.existsSync(BUILDER_AGENT_ACTIVE)) {
|
|
438
|
-
fs.unlinkSync(BUILDER_AGENT_ACTIVE);
|
|
439
|
-
console.log('đ Builder agent mode DISABLED');
|
|
440
|
-
} else {
|
|
441
|
-
console.log('âšī¸ Builder agent mode was not active');
|
|
442
|
-
}
|
|
443
|
-
process.exit(0);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// CLI: Check global builder agent status
|
|
447
|
-
if (process.argv[2] === '--agent-status') {
|
|
448
|
-
if (fs.existsSync(BUILDER_AGENT_ACTIVE)) {
|
|
449
|
-
console.log('đ§ Builder agent mode is ACTIVE');
|
|
450
|
-
} else {
|
|
451
|
-
console.log('đ Builder agent mode is OFF');
|
|
452
|
-
}
|
|
453
|
-
process.exit(0);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// CLI: Help
|
|
457
|
-
if (process.argv[2] === '--help' || process.argv[2] === '-h') {
|
|
458
|
-
console.log(`
|
|
459
|
-
Hailer App Edit Guard - Bulletproof protection for Hailer apps
|
|
460
|
-
|
|
461
|
-
UNIFIED SYSTEM: --agent-on/--agent-off controls BOTH this hook AND src-edit-guard.cjs.
|
|
462
|
-
One command enables builder mode for Hailer apps AND src/ directory edits.
|
|
463
|
-
|
|
464
|
-
Usage:
|
|
465
|
-
Global Builder Agent Mode (RECOMMENDED for spawning agents):
|
|
466
|
-
node app-edit-guard.cjs --agent-on Enable global builder mode
|
|
467
|
-
node app-edit-guard.cjs --agent-off Disable global builder mode
|
|
468
|
-
node app-edit-guard.cjs --agent-status Check if builder mode is active
|
|
469
|
-
|
|
470
|
-
Per-App Builder Mode:
|
|
471
|
-
node app-edit-guard.cjs --builder-on <path> Enable builder mode for an app
|
|
472
|
-
node app-edit-guard.cjs --builder-off <path> Disable builder mode for an app
|
|
473
|
-
|
|
474
|
-
Manual Editing (for direct edits by main agent):
|
|
475
|
-
node app-edit-guard.cjs --release <path> Release an app for manual editing
|
|
476
|
-
node app-edit-guard.cjs --revoke <path> Revoke manual editing permission
|
|
477
|
-
|
|
478
|
-
Utilities:
|
|
479
|
-
node app-edit-guard.cjs --check <path> Check if path is a Hailer app
|
|
480
|
-
node app-edit-guard.cjs --list List all released apps
|
|
481
|
-
node app-edit-guard.cjs --help Show this help
|
|
482
|
-
|
|
483
|
-
Workflow for spawning builder agents (RECOMMENDED):
|
|
484
|
-
1. Main agent: node app-edit-guard.cjs --agent-on
|
|
485
|
-
2. Main agent spawns builder agent via Task tool
|
|
486
|
-
3. Builder agent can freely edit ANY Hailer app AND src/ files
|
|
487
|
-
4. Main agent: node app-edit-guard.cjs --agent-off
|
|
488
|
-
|
|
489
|
-
As a hook:
|
|
490
|
-
Reads JSON from stdin with tool_name and tool_input
|
|
491
|
-
Outputs JSON with decision: "allow" or "block"
|
|
492
|
-
`);
|
|
493
|
-
process.exit(0);
|
|
494
|
-
}
|