compact-agent 1.1.0
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 +394 -0
- package/bin/anycode.js +2 -0
- package/bin/crowcoder.js +19 -0
- package/bin/ecc-hooks.cjs +138 -0
- package/dist/agents.d.ts +17 -0
- package/dist/agents.js +1603 -0
- package/dist/agents.js.map +1 -0
- package/dist/api.d.ts +16 -0
- package/dist/api.js +115 -0
- package/dist/api.js.map +1 -0
- package/dist/autonomous-loops.d.ts +108 -0
- package/dist/autonomous-loops.js +526 -0
- package/dist/autonomous-loops.js.map +1 -0
- package/dist/codemaps.d.ts +53 -0
- package/dist/codemaps.js +325 -0
- package/dist/codemaps.js.map +1 -0
- package/dist/compaction.d.ts +30 -0
- package/dist/compaction.js +125 -0
- package/dist/compaction.js.map +1 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +79 -0
- package/dist/config.js.map +1 -0
- package/dist/content-engine.d.ts +97 -0
- package/dist/content-engine.js +721 -0
- package/dist/content-engine.js.map +1 -0
- package/dist/cost-tracker.d.ts +49 -0
- package/dist/cost-tracker.js +150 -0
- package/dist/cost-tracker.js.map +1 -0
- package/dist/counter-button.d.ts +35 -0
- package/dist/counter-button.js +48 -0
- package/dist/counter-button.js.map +1 -0
- package/dist/counter.d.ts +21 -0
- package/dist/counter.js +31 -0
- package/dist/counter.js.map +1 -0
- package/dist/coverage.d.ts +23 -0
- package/dist/coverage.js +215 -0
- package/dist/coverage.js.map +1 -0
- package/dist/docs-sync.d.ts +23 -0
- package/dist/docs-sync.js +266 -0
- package/dist/docs-sync.js.map +1 -0
- package/dist/ecc.d.ts +41 -0
- package/dist/ecc.js +644 -0
- package/dist/ecc.js.map +1 -0
- package/dist/evaluation.d.ts +24 -0
- package/dist/evaluation.js +412 -0
- package/dist/evaluation.js.map +1 -0
- package/dist/export.d.ts +22 -0
- package/dist/export.js +109 -0
- package/dist/export.js.map +1 -0
- package/dist/git-workflow.d.ts +22 -0
- package/dist/git-workflow.js +197 -0
- package/dist/git-workflow.js.map +1 -0
- package/dist/hook-controls.d.ts +34 -0
- package/dist/hook-controls.js +90 -0
- package/dist/hook-controls.js.map +1 -0
- package/dist/hooks.d.ts +30 -0
- package/dist/hooks.js +130 -0
- package/dist/hooks.js.map +1 -0
- package/dist/html-parser.d.ts +18 -0
- package/dist/html-parser.js +101 -0
- package/dist/html-parser.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +1230 -0
- package/dist/index.js.map +1 -0
- package/dist/learning.d.ts +35 -0
- package/dist/learning.js +238 -0
- package/dist/learning.js.map +1 -0
- package/dist/login.d.ts +37 -0
- package/dist/login.js +191 -0
- package/dist/login.js.map +1 -0
- package/dist/memory.d.ts +39 -0
- package/dist/memory.js +183 -0
- package/dist/memory.js.map +1 -0
- package/dist/model-router.d.ts +23 -0
- package/dist/model-router.js +145 -0
- package/dist/model-router.js.map +1 -0
- package/dist/modes.d.ts +17 -0
- package/dist/modes.js +217 -0
- package/dist/modes.js.map +1 -0
- package/dist/orchestration.d.ts +37 -0
- package/dist/orchestration.js +139 -0
- package/dist/orchestration.js.map +1 -0
- package/dist/package-detect.d.ts +36 -0
- package/dist/package-detect.js +529 -0
- package/dist/package-detect.js.map +1 -0
- package/dist/permissions.d.ts +25 -0
- package/dist/permissions.js +50 -0
- package/dist/permissions.js.map +1 -0
- package/dist/pm2-manager.d.ts +40 -0
- package/dist/pm2-manager.js +127 -0
- package/dist/pm2-manager.js.map +1 -0
- package/dist/query.d.ts +15 -0
- package/dist/query.js +278 -0
- package/dist/query.js.map +1 -0
- package/dist/refactor.d.ts +22 -0
- package/dist/refactor.js +226 -0
- package/dist/refactor.js.map +1 -0
- package/dist/retry.d.ts +20 -0
- package/dist/retry.js +88 -0
- package/dist/retry.js.map +1 -0
- package/dist/rules.d.ts +34 -0
- package/dist/rules.js +942 -0
- package/dist/rules.js.map +1 -0
- package/dist/schema.d.ts +23 -0
- package/dist/schema.js +12 -0
- package/dist/schema.js.map +1 -0
- package/dist/search-first.d.ts +17 -0
- package/dist/search-first.js +301 -0
- package/dist/search-first.js.map +1 -0
- package/dist/security.d.ts +10 -0
- package/dist/security.js +145 -0
- package/dist/security.js.map +1 -0
- package/dist/sessions.d.ts +21 -0
- package/dist/sessions.js +112 -0
- package/dist/sessions.js.map +1 -0
- package/dist/skill-create.d.ts +38 -0
- package/dist/skill-create.js +389 -0
- package/dist/skill-create.js.map +1 -0
- package/dist/skills.d.ts +34 -0
- package/dist/skills.js +161 -0
- package/dist/skills.js.map +1 -0
- package/dist/strategic-compaction.d.ts +24 -0
- package/dist/strategic-compaction.js +144 -0
- package/dist/strategic-compaction.js.map +1 -0
- package/dist/system-prompt.d.ts +3 -0
- package/dist/system-prompt.js +101 -0
- package/dist/system-prompt.js.map +1 -0
- package/dist/theme.d.ts +60 -0
- package/dist/theme.js +220 -0
- package/dist/theme.js.map +1 -0
- package/dist/tools/bash.d.ts +2 -0
- package/dist/tools/bash.js +49 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit.d.ts +2 -0
- package/dist/tools/edit.js +76 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/glob.d.ts +2 -0
- package/dist/tools/glob.js +54 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/grep.d.ts +2 -0
- package/dist/tools/grep.js +64 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.js +27 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list-dir.d.ts +2 -0
- package/dist/tools/list-dir.js +51 -0
- package/dist/tools/list-dir.js.map +1 -0
- package/dist/tools/read.d.ts +2 -0
- package/dist/tools/read.js +56 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/types.d.ts +45 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +2 -0
- package/dist/tools/web-fetch.js +41 -0
- package/dist/tools/web-fetch.js.map +1 -0
- package/dist/tools/web-search.d.ts +27 -0
- package/dist/tools/web-search.js +139 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/write.d.ts +2 -0
- package/dist/tools/write.js +36 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/types.d.ts +28 -0
- package/dist/types.js +57 -0
- package/dist/types.js.map +1 -0
- package/dist/users.d.ts +51 -0
- package/dist/users.js +193 -0
- package/dist/users.js.map +1 -0
- package/dist/verification.d.ts +73 -0
- package/dist/verification.js +269 -0
- package/dist/verification.js.map +1 -0
- package/dist/walkthrough.d.ts +10 -0
- package/dist/walkthrough.js +121 -0
- package/dist/walkthrough.js.map +1 -0
- package/package.json +58 -0
- package/resources/ecc/agents/architect.json +16 -0
- package/resources/ecc/agents/architect.md +212 -0
- package/resources/ecc/agents/build-error-resolver.json +17 -0
- package/resources/ecc/agents/build-error-resolver.md +116 -0
- package/resources/ecc/agents/chief-of-staff.json +17 -0
- package/resources/ecc/agents/chief-of-staff.md +153 -0
- package/resources/ecc/agents/code-reviewer.json +16 -0
- package/resources/ecc/agents/code-reviewer.md +238 -0
- package/resources/ecc/agents/database-reviewer.json +16 -0
- package/resources/ecc/agents/database-reviewer.md +92 -0
- package/resources/ecc/agents/doc-updater.json +16 -0
- package/resources/ecc/agents/doc-updater.md +108 -0
- package/resources/ecc/agents/e2e-runner.json +17 -0
- package/resources/ecc/agents/e2e-runner.md +109 -0
- package/resources/ecc/agents/go-build-resolver.json +17 -0
- package/resources/ecc/agents/go-build-resolver.md +96 -0
- package/resources/ecc/agents/go-reviewer.json +16 -0
- package/resources/ecc/agents/go-reviewer.md +77 -0
- package/resources/ecc/agents/harness-optimizer.json +15 -0
- package/resources/ecc/agents/harness-optimizer.md +34 -0
- package/resources/ecc/agents/loop-operator.json +16 -0
- package/resources/ecc/agents/loop-operator.md +36 -0
- package/resources/ecc/agents/planner.json +15 -0
- package/resources/ecc/agents/planner.md +212 -0
- package/resources/ecc/agents/python-reviewer.json +16 -0
- package/resources/ecc/agents/python-reviewer.md +99 -0
- package/resources/ecc/agents/refactor-cleaner.json +17 -0
- package/resources/ecc/agents/refactor-cleaner.md +87 -0
- package/resources/ecc/agents/security-reviewer.json +16 -0
- package/resources/ecc/agents/security-reviewer.md +109 -0
- package/resources/ecc/agents/tdd-guide.json +17 -0
- package/resources/ecc/agents/tdd-guide.md +93 -0
- package/resources/ecc/commands/add-language-rules.md +39 -0
- package/resources/ecc/commands/database-migration.md +36 -0
- package/resources/ecc/commands/feature-development.md +38 -0
- package/resources/ecc/prompts/build-fix.prompt.md +47 -0
- package/resources/ecc/prompts/code-review.prompt.md +56 -0
- package/resources/ecc/prompts/plan.prompt.md +52 -0
- package/resources/ecc/prompts/refactor.prompt.md +50 -0
- package/resources/ecc/prompts/security-review.prompt.md +70 -0
- package/resources/ecc/prompts/tdd.prompt.md +47 -0
- package/resources/ecc/rules/common-agents.md +53 -0
- package/resources/ecc/rules/common-coding-style.md +52 -0
- package/resources/ecc/rules/common-development-workflow.md +33 -0
- package/resources/ecc/rules/common-git-workflow.md +28 -0
- package/resources/ecc/rules/common-hooks.md +34 -0
- package/resources/ecc/rules/common-patterns.md +35 -0
- package/resources/ecc/rules/common-performance.md +59 -0
- package/resources/ecc/rules/common-security.md +33 -0
- package/resources/ecc/rules/common-testing.md +33 -0
- package/resources/ecc/rules/golang-coding-style.md +31 -0
- package/resources/ecc/rules/golang-hooks.md +16 -0
- package/resources/ecc/rules/golang-patterns.md +44 -0
- package/resources/ecc/rules/golang-security.md +33 -0
- package/resources/ecc/rules/golang-testing.md +30 -0
- package/resources/ecc/rules/kotlin-coding-style.md +39 -0
- package/resources/ecc/rules/kotlin-hooks.md +16 -0
- package/resources/ecc/rules/kotlin-patterns.md +50 -0
- package/resources/ecc/rules/kotlin-security.md +58 -0
- package/resources/ecc/rules/kotlin-testing.md +38 -0
- package/resources/ecc/rules/php-coding-style.md +25 -0
- package/resources/ecc/rules/php-hooks.md +21 -0
- package/resources/ecc/rules/php-patterns.md +23 -0
- package/resources/ecc/rules/php-security.md +24 -0
- package/resources/ecc/rules/php-testing.md +26 -0
- package/resources/ecc/rules/python-coding-style.md +42 -0
- package/resources/ecc/rules/python-hooks.md +19 -0
- package/resources/ecc/rules/python-patterns.md +39 -0
- package/resources/ecc/rules/python-security.md +30 -0
- package/resources/ecc/rules/python-testing.md +38 -0
- package/resources/ecc/rules/swift-coding-style.md +47 -0
- package/resources/ecc/rules/swift-hooks.md +20 -0
- package/resources/ecc/rules/swift-patterns.md +66 -0
- package/resources/ecc/rules/swift-security.md +33 -0
- package/resources/ecc/rules/swift-testing.md +45 -0
- package/resources/ecc/rules/typescript-coding-style.md +63 -0
- package/resources/ecc/rules/typescript-hooks.md +20 -0
- package/resources/ecc/rules/typescript-patterns.md +50 -0
- package/resources/ecc/rules/typescript-security.md +26 -0
- package/resources/ecc/rules/typescript-testing.md +16 -0
- package/resources/ecc/skills/agent-introspection-debugging/SKILL.md +152 -0
- package/resources/ecc/skills/agent-introspection-debugging/agents/openai.yaml +7 -0
- package/resources/ecc/skills/agent-sort/SKILL.md +214 -0
- package/resources/ecc/skills/agent-sort/agents/openai.yaml +7 -0
- package/resources/ecc/skills/api-design/SKILL.md +522 -0
- package/resources/ecc/skills/api-design/agents/openai.yaml +7 -0
- package/resources/ecc/skills/article-writing/SKILL.md +78 -0
- package/resources/ecc/skills/article-writing/agents/openai.yaml +7 -0
- package/resources/ecc/skills/backend-patterns/SKILL.md +597 -0
- package/resources/ecc/skills/backend-patterns/agents/openai.yaml +7 -0
- package/resources/ecc/skills/brand-voice/SKILL.md +96 -0
- package/resources/ecc/skills/brand-voice/agents/openai.yaml +7 -0
- package/resources/ecc/skills/brand-voice/references/voice-profile-schema.md +55 -0
- package/resources/ecc/skills/bun-runtime/SKILL.md +83 -0
- package/resources/ecc/skills/bun-runtime/agents/openai.yaml +7 -0
- package/resources/ecc/skills/coding-standards/SKILL.md +548 -0
- package/resources/ecc/skills/coding-standards/agents/openai.yaml +7 -0
- package/resources/ecc/skills/content-engine/SKILL.md +130 -0
- package/resources/ecc/skills/content-engine/agents/openai.yaml +7 -0
- package/resources/ecc/skills/crosspost/SKILL.md +110 -0
- package/resources/ecc/skills/crosspost/agents/openai.yaml +7 -0
- package/resources/ecc/skills/deep-research/SKILL.md +154 -0
- package/resources/ecc/skills/deep-research/agents/openai.yaml +7 -0
- package/resources/ecc/skills/dmux-workflows/SKILL.md +143 -0
- package/resources/ecc/skills/dmux-workflows/agents/openai.yaml +7 -0
- package/resources/ecc/skills/documentation-lookup/SKILL.md +89 -0
- package/resources/ecc/skills/documentation-lookup/agents/openai.yaml +7 -0
- package/resources/ecc/skills/e2e-testing/SKILL.md +325 -0
- package/resources/ecc/skills/e2e-testing/agents/openai.yaml +7 -0
- package/resources/ecc/skills/eval-harness/SKILL.md +235 -0
- package/resources/ecc/skills/eval-harness/agents/openai.yaml +7 -0
- package/resources/ecc/skills/everything-claude-code/SKILL.md +442 -0
- package/resources/ecc/skills/everything-claude-code/agents/openai.yaml +7 -0
- package/resources/ecc/skills/exa-search/SKILL.md +169 -0
- package/resources/ecc/skills/exa-search/agents/openai.yaml +7 -0
- package/resources/ecc/skills/fal-ai-media/SKILL.md +276 -0
- package/resources/ecc/skills/fal-ai-media/agents/openai.yaml +7 -0
- package/resources/ecc/skills/frontend-patterns/SKILL.md +647 -0
- package/resources/ecc/skills/frontend-patterns/agents/openai.yaml +7 -0
- package/resources/ecc/skills/frontend-slides/SKILL.md +183 -0
- package/resources/ecc/skills/frontend-slides/STYLE_PRESETS.md +330 -0
- package/resources/ecc/skills/frontend-slides/agents/openai.yaml +7 -0
- package/resources/ecc/skills/investor-materials/SKILL.md +95 -0
- package/resources/ecc/skills/investor-materials/agents/openai.yaml +7 -0
- package/resources/ecc/skills/investor-outreach/SKILL.md +90 -0
- package/resources/ecc/skills/investor-outreach/agents/openai.yaml +7 -0
- package/resources/ecc/skills/market-research/SKILL.md +74 -0
- package/resources/ecc/skills/market-research/agents/openai.yaml +7 -0
- package/resources/ecc/skills/mcp-server-patterns/SKILL.md +66 -0
- package/resources/ecc/skills/mcp-server-patterns/agents/openai.yaml +7 -0
- package/resources/ecc/skills/mle-workflow/SKILL.md +346 -0
- package/resources/ecc/skills/mle-workflow/agents/openai.yaml +7 -0
- package/resources/ecc/skills/nextjs-turbopack/SKILL.md +43 -0
- package/resources/ecc/skills/nextjs-turbopack/agents/openai.yaml +7 -0
- package/resources/ecc/skills/product-capability/SKILL.md +140 -0
- package/resources/ecc/skills/product-capability/agents/openai.yaml +7 -0
- package/resources/ecc/skills/security-review/SKILL.md +494 -0
- package/resources/ecc/skills/security-review/agents/openai.yaml +7 -0
- package/resources/ecc/skills/strategic-compact/SKILL.md +102 -0
- package/resources/ecc/skills/strategic-compact/agents/openai.yaml +7 -0
- package/resources/ecc/skills/tdd-workflow/SKILL.md +409 -0
- package/resources/ecc/skills/tdd-workflow/agents/openai.yaml +7 -0
- package/resources/ecc/skills/verification-loop/SKILL.md +125 -0
- package/resources/ecc/skills/verification-loop/agents/openai.yaml +7 -0
- package/resources/ecc/skills/video-editing/SKILL.md +307 -0
- package/resources/ecc/skills/video-editing/agents/openai.yaml +7 -0
- package/resources/ecc/skills/x-api/SKILL.md +229 -0
- package/resources/ecc/skills/x-api/agents/openai.yaml +7 -0
package/dist/ecc.js
ADDED
|
@@ -0,0 +1,644 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ECC (everything-claude-code) integration.
|
|
3
|
+
*
|
|
4
|
+
* Imports skills, agents, slash commands, rules, and hook behaviors from the
|
|
5
|
+
* bundled `resources/ecc/` directory into Crowcoder's runtime stores:
|
|
6
|
+
* ~/.crowcoder/skills/ — JSON skills generated from SKILL.md
|
|
7
|
+
* ~/.crowcoder/rules/ — language rule files
|
|
8
|
+
* ~/.crowcoder/ecc-commands/ — markdown prompt templates for /ecc-<cmd>
|
|
9
|
+
* ~/.crowcoder/ecc-agents/ — agent prompt templates
|
|
10
|
+
* ~/.crowcoder/hooks.json — augmented with ECC hook entries
|
|
11
|
+
*
|
|
12
|
+
* Each ECC skill becomes a Crowcoder Skill (skills.ts schema) with id
|
|
13
|
+
* `ecc-<slug>`, triggers derived from name + description keywords, and the
|
|
14
|
+
* SKILL.md body as the prompt template.
|
|
15
|
+
*
|
|
16
|
+
* The import is idempotent — re-running overwrites prior ECC entries but
|
|
17
|
+
* leaves user-created skills/rules/hooks alone (ECC entries are scoped by
|
|
18
|
+
* id prefix `ecc-` or `ecc:` category).
|
|
19
|
+
*/
|
|
20
|
+
import { readFileSync, writeFileSync, readdirSync, mkdirSync, existsSync, statSync, copyFileSync, unlinkSync, } from 'node:fs';
|
|
21
|
+
import { join, dirname, basename, resolve } from 'node:path';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
23
|
+
import chalk from 'chalk';
|
|
24
|
+
import { getConfigDir } from './config.js';
|
|
25
|
+
import { saveSkill, listSkills, deleteSkill } from './skills.js';
|
|
26
|
+
import { listHooks, saveHooksConfig } from './hooks.js';
|
|
27
|
+
// ── Resource resolution ─────────────────────────────────
|
|
28
|
+
// resources/ live one level above the compiled dist/ (and one above src/).
|
|
29
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
30
|
+
const __dirname = dirname(__filename);
|
|
31
|
+
const RESOURCES_ROOT = resolve(__dirname, '..', 'resources', 'ecc');
|
|
32
|
+
const ECC_SKILLS_SRC = join(RESOURCES_ROOT, 'skills');
|
|
33
|
+
const ECC_AGENTS_SRC = join(RESOURCES_ROOT, 'agents');
|
|
34
|
+
const ECC_COMMANDS_SRC = join(RESOURCES_ROOT, 'commands');
|
|
35
|
+
const ECC_PROMPTS_SRC = join(RESOURCES_ROOT, 'prompts');
|
|
36
|
+
const ECC_RULES_SRC = join(RESOURCES_ROOT, 'rules');
|
|
37
|
+
const RULES_DIR = join(getConfigDir(), 'rules');
|
|
38
|
+
const ECC_COMMANDS_DST = join(getConfigDir(), 'ecc-commands');
|
|
39
|
+
const ECC_AGENTS_DST = join(getConfigDir(), 'ecc-agents');
|
|
40
|
+
const ECC_STATE_FILE = join(getConfigDir(), 'ecc-state.json');
|
|
41
|
+
const ECC_SKILL_ID_PREFIX = 'ecc-';
|
|
42
|
+
const ECC_HOOK_TAG = '__ecc__';
|
|
43
|
+
export function loadEccState() {
|
|
44
|
+
if (!existsSync(ECC_STATE_FILE))
|
|
45
|
+
return null;
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(readFileSync(ECC_STATE_FILE, 'utf-8'));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function saveEccState(s) {
|
|
54
|
+
mkdirSync(getConfigDir(), { recursive: true });
|
|
55
|
+
writeFileSync(ECC_STATE_FILE, JSON.stringify(s, null, 2), 'utf-8');
|
|
56
|
+
}
|
|
57
|
+
export function eccResourcesAvailable() {
|
|
58
|
+
return existsSync(RESOURCES_ROOT) && existsSync(ECC_SKILLS_SRC);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Minimal YAML frontmatter parser — handles the subset used by ECC:
|
|
62
|
+
* key: value
|
|
63
|
+
* key: "value with: colons"
|
|
64
|
+
* key:
|
|
65
|
+
* - item1
|
|
66
|
+
* - item2
|
|
67
|
+
* key: ["a", "b"]
|
|
68
|
+
* Anything more exotic is preserved as raw string.
|
|
69
|
+
*/
|
|
70
|
+
function parseFrontmatter(raw) {
|
|
71
|
+
if (!raw.startsWith('---'))
|
|
72
|
+
return { frontmatter: {}, body: raw };
|
|
73
|
+
const end = raw.indexOf('\n---', 3);
|
|
74
|
+
if (end < 0)
|
|
75
|
+
return { frontmatter: {}, body: raw };
|
|
76
|
+
const yamlBlock = raw.slice(3, end).replace(/^\r?\n/, '');
|
|
77
|
+
const body = raw.slice(end + 4).replace(/^\r?\n/, '');
|
|
78
|
+
const fm = {};
|
|
79
|
+
const lines = yamlBlock.split(/\r?\n/);
|
|
80
|
+
let currentKey = null;
|
|
81
|
+
let listAcc = null;
|
|
82
|
+
const stripQuotes = (s) => {
|
|
83
|
+
const t = s.trim();
|
|
84
|
+
if ((t.startsWith('"') && t.endsWith('"')) || (t.startsWith("'") && t.endsWith("'"))) {
|
|
85
|
+
return t.slice(1, -1);
|
|
86
|
+
}
|
|
87
|
+
return t;
|
|
88
|
+
};
|
|
89
|
+
for (const rawLine of lines) {
|
|
90
|
+
const line = rawLine.replace(/\s+$/, '');
|
|
91
|
+
if (!line.trim())
|
|
92
|
+
continue;
|
|
93
|
+
const listMatch = line.match(/^\s+-\s+(.*)$/);
|
|
94
|
+
if (listMatch && currentKey && listAcc) {
|
|
95
|
+
listAcc.push(stripQuotes(listMatch[1]));
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
// commit previous list
|
|
99
|
+
if (currentKey && listAcc) {
|
|
100
|
+
fm[currentKey] = listAcc;
|
|
101
|
+
listAcc = null;
|
|
102
|
+
currentKey = null;
|
|
103
|
+
}
|
|
104
|
+
const kv = line.match(/^([A-Za-z_][\w-]*)\s*:\s*(.*)$/);
|
|
105
|
+
if (!kv)
|
|
106
|
+
continue;
|
|
107
|
+
const key = kv[1];
|
|
108
|
+
const valRaw = kv[2];
|
|
109
|
+
if (valRaw === '' || valRaw === undefined) {
|
|
110
|
+
// start of multi-line value (list)
|
|
111
|
+
currentKey = key;
|
|
112
|
+
listAcc = [];
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
// inline array
|
|
116
|
+
if (valRaw.startsWith('[') && valRaw.endsWith(']')) {
|
|
117
|
+
const inner = valRaw.slice(1, -1).trim();
|
|
118
|
+
const items = inner.length
|
|
119
|
+
? inner.split(',').map(s => stripQuotes(s))
|
|
120
|
+
: [];
|
|
121
|
+
fm[key] = items;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
fm[key] = stripQuotes(valRaw);
|
|
125
|
+
}
|
|
126
|
+
// flush trailing list
|
|
127
|
+
if (currentKey && listAcc)
|
|
128
|
+
fm[currentKey] = listAcc;
|
|
129
|
+
return { frontmatter: fm, body };
|
|
130
|
+
}
|
|
131
|
+
// ── Trigger derivation ──────────────────────────────────
|
|
132
|
+
const STOPWORDS = new Set([
|
|
133
|
+
'the', 'and', 'for', 'with', 'this', 'that', 'when', 'use', 'used',
|
|
134
|
+
'using', 'from', 'into', 'onto', 'a', 'an', 'of', 'to', 'in', 'on',
|
|
135
|
+
'as', 'by', 'is', 'are', 'be', 'been', 'or', 'but', 'not', 'all',
|
|
136
|
+
'any', 'some', 'must', 'should', 'must', 'will', 'can', 'skill',
|
|
137
|
+
'workflow', 'pattern', 'patterns', 'rules', 'rule', 'using',
|
|
138
|
+
]);
|
|
139
|
+
function deriveTriggers(name, description, extras = []) {
|
|
140
|
+
const triggers = new Set();
|
|
141
|
+
// slug parts of name
|
|
142
|
+
for (const part of name.toLowerCase().split(/[-_\s]+/)) {
|
|
143
|
+
if (part.length >= 3)
|
|
144
|
+
triggers.add(part);
|
|
145
|
+
}
|
|
146
|
+
triggers.add(name.toLowerCase());
|
|
147
|
+
// first 60 chars of description, keywords
|
|
148
|
+
const descSample = description.toLowerCase().slice(0, 200);
|
|
149
|
+
for (const word of descSample.split(/[^a-z0-9]+/)) {
|
|
150
|
+
if (word.length >= 5 && !STOPWORDS.has(word))
|
|
151
|
+
triggers.add(word);
|
|
152
|
+
}
|
|
153
|
+
for (const e of extras) {
|
|
154
|
+
const norm = e.toLowerCase().trim();
|
|
155
|
+
if (norm.length >= 3)
|
|
156
|
+
triggers.add(norm);
|
|
157
|
+
}
|
|
158
|
+
return Array.from(triggers).slice(0, 12);
|
|
159
|
+
}
|
|
160
|
+
// ── Skill import ────────────────────────────────────────
|
|
161
|
+
function listSkillDirs() {
|
|
162
|
+
if (!existsSync(ECC_SKILLS_SRC))
|
|
163
|
+
return [];
|
|
164
|
+
return readdirSync(ECC_SKILLS_SRC)
|
|
165
|
+
.map(n => join(ECC_SKILLS_SRC, n))
|
|
166
|
+
.filter(p => {
|
|
167
|
+
try {
|
|
168
|
+
return statSync(p).isDirectory();
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
function readSkillMd(dir) {
|
|
176
|
+
const skillPath = join(dir, 'SKILL.md');
|
|
177
|
+
if (!existsSync(skillPath))
|
|
178
|
+
return null;
|
|
179
|
+
const raw = readFileSync(skillPath, 'utf-8');
|
|
180
|
+
// Pull in sibling reference docs if present (e.g. STYLE_PRESETS.md)
|
|
181
|
+
const references = [];
|
|
182
|
+
for (const f of readdirSync(dir)) {
|
|
183
|
+
if (f === 'SKILL.md')
|
|
184
|
+
continue;
|
|
185
|
+
if (!f.endsWith('.md'))
|
|
186
|
+
continue;
|
|
187
|
+
references.push(readFileSync(join(dir, f), 'utf-8'));
|
|
188
|
+
}
|
|
189
|
+
// Also pull in references/ subdir
|
|
190
|
+
const refDir = join(dir, 'references');
|
|
191
|
+
if (existsSync(refDir)) {
|
|
192
|
+
try {
|
|
193
|
+
for (const f of readdirSync(refDir)) {
|
|
194
|
+
if (f.endsWith('.md')) {
|
|
195
|
+
references.push(readFileSync(join(refDir, f), 'utf-8'));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch { /* ignore */ }
|
|
200
|
+
}
|
|
201
|
+
return { raw, references };
|
|
202
|
+
}
|
|
203
|
+
function eccSkillId(slug) {
|
|
204
|
+
return `${ECC_SKILL_ID_PREFIX}${slug}`;
|
|
205
|
+
}
|
|
206
|
+
function importSkills() {
|
|
207
|
+
const errors = [];
|
|
208
|
+
// Clean prior ECC skills first so renames don't leave stragglers
|
|
209
|
+
for (const s of listSkills()) {
|
|
210
|
+
if (s.id.startsWith(ECC_SKILL_ID_PREFIX)) {
|
|
211
|
+
deleteSkill(s.id);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
let count = 0;
|
|
215
|
+
for (const dir of listSkillDirs()) {
|
|
216
|
+
const slug = basename(dir);
|
|
217
|
+
try {
|
|
218
|
+
const doc = readSkillMd(dir);
|
|
219
|
+
if (!doc)
|
|
220
|
+
continue;
|
|
221
|
+
const { frontmatter, body } = parseFrontmatter(doc.raw);
|
|
222
|
+
const name = String(frontmatter.name || slug);
|
|
223
|
+
const description = String(frontmatter.description || `ECC skill: ${slug}`);
|
|
224
|
+
const triggers = deriveTriggers(name, description, [slug]);
|
|
225
|
+
const fullBody = doc.references.length
|
|
226
|
+
? body + '\n\n---\n\n' + doc.references.join('\n\n---\n\n')
|
|
227
|
+
: body;
|
|
228
|
+
const skill = {
|
|
229
|
+
id: eccSkillId(slug),
|
|
230
|
+
name,
|
|
231
|
+
description,
|
|
232
|
+
prompt: fullBody.trim(),
|
|
233
|
+
triggers,
|
|
234
|
+
category: 'ecc',
|
|
235
|
+
createdAt: new Date().toISOString(),
|
|
236
|
+
useCount: 0,
|
|
237
|
+
};
|
|
238
|
+
saveSkill(skill);
|
|
239
|
+
count++;
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
errors.push(`skill ${slug}: ${err instanceof Error ? err.message : String(err)}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return { count, errors };
|
|
246
|
+
}
|
|
247
|
+
// ── Agent import ────────────────────────────────────────
|
|
248
|
+
/**
|
|
249
|
+
* Kiro agents are JSON + MD pairs:
|
|
250
|
+
* <name>.json — { name, description, prompt, allowedTools, ... }
|
|
251
|
+
* <name>.md — frontmatter (name, description, allowedTools) + body prompt
|
|
252
|
+
*
|
|
253
|
+
* We materialize each as a markdown file in ~/.crowcoder/ecc-agents/<name>.md
|
|
254
|
+
* (canonical prompt for /ecc-agent <name>) and ALSO register a Crowcoder Skill
|
|
255
|
+
* with id `ecc-agent-<name>` so it surfaces in /skills and trigger search.
|
|
256
|
+
*/
|
|
257
|
+
function importAgents() {
|
|
258
|
+
const errors = [];
|
|
259
|
+
if (!existsSync(ECC_AGENTS_SRC))
|
|
260
|
+
return { count: 0, errors };
|
|
261
|
+
mkdirSync(ECC_AGENTS_DST, { recursive: true });
|
|
262
|
+
// Clean prior agent skills
|
|
263
|
+
for (const s of listSkills()) {
|
|
264
|
+
if (s.id.startsWith(`${ECC_SKILL_ID_PREFIX}agent-`)) {
|
|
265
|
+
deleteSkill(s.id);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
let count = 0;
|
|
269
|
+
const files = readdirSync(ECC_AGENTS_SRC).filter(f => f.endsWith('.json'));
|
|
270
|
+
for (const file of files) {
|
|
271
|
+
const slug = file.replace(/\.json$/, '');
|
|
272
|
+
try {
|
|
273
|
+
const jsonPath = join(ECC_AGENTS_SRC, file);
|
|
274
|
+
const mdPath = join(ECC_AGENTS_SRC, `${slug}.md`);
|
|
275
|
+
const jsonRaw = readFileSync(jsonPath, 'utf-8');
|
|
276
|
+
const json = JSON.parse(jsonRaw);
|
|
277
|
+
let prompt = String(json.prompt || '');
|
|
278
|
+
let description = String(json.description || `ECC agent: ${slug}`);
|
|
279
|
+
const allowed = json.allowedTools || [];
|
|
280
|
+
if (existsSync(mdPath)) {
|
|
281
|
+
const md = readFileSync(mdPath, 'utf-8');
|
|
282
|
+
const { frontmatter, body } = parseFrontmatter(md);
|
|
283
|
+
if (body.trim().length > prompt.length)
|
|
284
|
+
prompt = body.trim();
|
|
285
|
+
if (frontmatter.description)
|
|
286
|
+
description = String(frontmatter.description);
|
|
287
|
+
}
|
|
288
|
+
// Write canonical agent prompt to ecc-agents dir
|
|
289
|
+
const agentDoc = [
|
|
290
|
+
`# ${slug}`,
|
|
291
|
+
'',
|
|
292
|
+
`> ${description}`,
|
|
293
|
+
'',
|
|
294
|
+
`**Allowed tools**: ${allowed.length ? allowed.join(', ') : '(any)'}`,
|
|
295
|
+
'',
|
|
296
|
+
prompt,
|
|
297
|
+
].join('\n');
|
|
298
|
+
writeFileSync(join(ECC_AGENTS_DST, `${slug}.md`), agentDoc, 'utf-8');
|
|
299
|
+
// Register a skill so it surfaces in /skills + trigger search
|
|
300
|
+
const triggers = deriveTriggers(slug, description, [slug, 'agent']);
|
|
301
|
+
saveSkill({
|
|
302
|
+
id: `${ECC_SKILL_ID_PREFIX}agent-${slug}`,
|
|
303
|
+
name: `agent: ${slug}`,
|
|
304
|
+
description,
|
|
305
|
+
prompt,
|
|
306
|
+
triggers,
|
|
307
|
+
category: 'ecc-agent',
|
|
308
|
+
createdAt: new Date().toISOString(),
|
|
309
|
+
useCount: 0,
|
|
310
|
+
});
|
|
311
|
+
count++;
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
errors.push(`agent ${slug}: ${err instanceof Error ? err.message : String(err)}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return { count, errors };
|
|
318
|
+
}
|
|
319
|
+
// ── Command + prompt import ─────────────────────────────
|
|
320
|
+
/**
|
|
321
|
+
* .claude/commands/<name>.md and .github/prompts/<name>.prompt.md are both
|
|
322
|
+
* frontmatter+body prompt templates. We copy them verbatim into
|
|
323
|
+
* ~/.crowcoder/ecc-commands/ so /ecc-<name> can read them at runtime.
|
|
324
|
+
*/
|
|
325
|
+
function importCommandsAndPrompts() {
|
|
326
|
+
const errors = [];
|
|
327
|
+
let commands = 0;
|
|
328
|
+
let prompts = 0;
|
|
329
|
+
mkdirSync(ECC_COMMANDS_DST, { recursive: true });
|
|
330
|
+
// Wipe prior ECC commands
|
|
331
|
+
for (const f of readdirSync(ECC_COMMANDS_DST)) {
|
|
332
|
+
try {
|
|
333
|
+
unlinkSync(join(ECC_COMMANDS_DST, f));
|
|
334
|
+
}
|
|
335
|
+
catch { /* ignore */ }
|
|
336
|
+
}
|
|
337
|
+
const copyMd = (srcDir, suffix = '') => {
|
|
338
|
+
if (!existsSync(srcDir))
|
|
339
|
+
return 0;
|
|
340
|
+
let n = 0;
|
|
341
|
+
for (const f of readdirSync(srcDir)) {
|
|
342
|
+
if (!f.endsWith('.md'))
|
|
343
|
+
continue;
|
|
344
|
+
const src = join(srcDir, f);
|
|
345
|
+
try {
|
|
346
|
+
const stat = statSync(src);
|
|
347
|
+
if (!stat.isFile())
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
const baseName = f.replace(/\.prompt\.md$/, '').replace(/\.md$/, '');
|
|
354
|
+
const dst = join(ECC_COMMANDS_DST, `${baseName}${suffix}.md`);
|
|
355
|
+
copyFileSync(src, dst);
|
|
356
|
+
n++;
|
|
357
|
+
}
|
|
358
|
+
return n;
|
|
359
|
+
};
|
|
360
|
+
try {
|
|
361
|
+
commands = copyMd(ECC_COMMANDS_SRC);
|
|
362
|
+
}
|
|
363
|
+
catch (err) {
|
|
364
|
+
errors.push(`commands: ${err instanceof Error ? err.message : err}`);
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
prompts = copyMd(ECC_PROMPTS_SRC);
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
errors.push(`prompts: ${err instanceof Error ? err.message : err}`);
|
|
371
|
+
}
|
|
372
|
+
return { commands, prompts, errors };
|
|
373
|
+
}
|
|
374
|
+
export function listEccCommands() {
|
|
375
|
+
if (!existsSync(ECC_COMMANDS_DST))
|
|
376
|
+
return [];
|
|
377
|
+
return readdirSync(ECC_COMMANDS_DST)
|
|
378
|
+
.filter(f => f.endsWith('.md'))
|
|
379
|
+
.map(f => f.replace(/\.md$/, ''))
|
|
380
|
+
.sort();
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* Resolve a `/ecc-<name>` command to its prompt body. Frontmatter is stripped;
|
|
384
|
+
* if `allowed_tools` is set, it's preserved as a note at the top.
|
|
385
|
+
*/
|
|
386
|
+
export function getEccCommandPrompt(name) {
|
|
387
|
+
const path = join(ECC_COMMANDS_DST, `${name}.md`);
|
|
388
|
+
if (!existsSync(path))
|
|
389
|
+
return null;
|
|
390
|
+
const raw = readFileSync(path, 'utf-8');
|
|
391
|
+
const { frontmatter, body } = parseFrontmatter(raw);
|
|
392
|
+
const header = [];
|
|
393
|
+
if (frontmatter.description)
|
|
394
|
+
header.push(`> ${frontmatter.description}`);
|
|
395
|
+
if (frontmatter.allowed_tools) {
|
|
396
|
+
const tools = Array.isArray(frontmatter.allowed_tools)
|
|
397
|
+
? frontmatter.allowed_tools.join(', ')
|
|
398
|
+
: String(frontmatter.allowed_tools);
|
|
399
|
+
header.push(`> Allowed tools: ${tools}`);
|
|
400
|
+
}
|
|
401
|
+
return [...header, '', body].join('\n').trim();
|
|
402
|
+
}
|
|
403
|
+
// ── Rules import ────────────────────────────────────────
|
|
404
|
+
/**
|
|
405
|
+
* ECC ships per-language rules split into 5 files
|
|
406
|
+
* (coding-style, security, patterns, testing, hooks). Crowcoder reads
|
|
407
|
+
* `~/.crowcoder/rules/<language>.md` as a single bundle per language, so we
|
|
408
|
+
* concatenate the 5 files per language. Existing user content in those files
|
|
409
|
+
* is preserved by appending under a clearly-marked ECC section.
|
|
410
|
+
*/
|
|
411
|
+
function importRules() {
|
|
412
|
+
const errors = [];
|
|
413
|
+
if (!existsSync(ECC_RULES_SRC))
|
|
414
|
+
return { count: 0, errors };
|
|
415
|
+
mkdirSync(RULES_DIR, { recursive: true });
|
|
416
|
+
const byLang = new Map();
|
|
417
|
+
for (const f of readdirSync(ECC_RULES_SRC)) {
|
|
418
|
+
if (!f.endsWith('.md'))
|
|
419
|
+
continue;
|
|
420
|
+
const m = f.match(/^([a-z]+)-([a-z-]+)\.md$/);
|
|
421
|
+
if (!m)
|
|
422
|
+
continue;
|
|
423
|
+
const lang = m[1];
|
|
424
|
+
const section = m[2];
|
|
425
|
+
const content = readFileSync(join(ECC_RULES_SRC, f), 'utf-8');
|
|
426
|
+
if (!byLang.has(lang))
|
|
427
|
+
byLang.set(lang, []);
|
|
428
|
+
byLang.get(lang).push(`## ${section}\n\n${content.trim()}`);
|
|
429
|
+
}
|
|
430
|
+
let count = 0;
|
|
431
|
+
for (const [lang, sections] of byLang.entries()) {
|
|
432
|
+
try {
|
|
433
|
+
const dst = join(RULES_DIR, `${lang}.md`);
|
|
434
|
+
const ECC_BEGIN = '<!-- ECC-RULES:BEGIN -->';
|
|
435
|
+
const ECC_END = '<!-- ECC-RULES:END -->';
|
|
436
|
+
const eccBlock = [
|
|
437
|
+
ECC_BEGIN,
|
|
438
|
+
`# ${lang} — everything-claude-code rules`,
|
|
439
|
+
'',
|
|
440
|
+
sections.join('\n\n---\n\n'),
|
|
441
|
+
ECC_END,
|
|
442
|
+
].join('\n');
|
|
443
|
+
let final = eccBlock;
|
|
444
|
+
if (existsSync(dst)) {
|
|
445
|
+
const existing = readFileSync(dst, 'utf-8');
|
|
446
|
+
// Strip prior ECC block before appending
|
|
447
|
+
const stripped = existing.replace(new RegExp(`${ECC_BEGIN}[\\s\\S]*?${ECC_END}`, 'g'), '').trim();
|
|
448
|
+
final = stripped ? `${stripped}\n\n${eccBlock}` : eccBlock;
|
|
449
|
+
}
|
|
450
|
+
writeFileSync(dst, final, 'utf-8');
|
|
451
|
+
count++;
|
|
452
|
+
}
|
|
453
|
+
catch (err) {
|
|
454
|
+
errors.push(`rules ${lang}: ${err instanceof Error ? err.message : String(err)}`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return { count, errors };
|
|
458
|
+
}
|
|
459
|
+
// ── Hook seeding ────────────────────────────────────────
|
|
460
|
+
/**
|
|
461
|
+
* Seeds Crowcoder's hooks.json with ECC-compatible default hooks.
|
|
462
|
+
*
|
|
463
|
+
* The cursor hook scripts have a dense in-repo dependency tree (scripts/lib/,
|
|
464
|
+
* scripts/hooks/) and require their original directory layout — we don't try
|
|
465
|
+
* to run them from Crowcoder. Instead we install native equivalents for the
|
|
466
|
+
* highest-value ECC hook behaviors: block-no-verify, secret-in-prompt detection,
|
|
467
|
+
* console.log warnings post-edit, dev-server tmux reminder.
|
|
468
|
+
*
|
|
469
|
+
* Each hook entry is tagged with __ecc__ so /ecc-install can refresh them
|
|
470
|
+
* without touching user-defined hooks.
|
|
471
|
+
*/
|
|
472
|
+
function seedHooks() {
|
|
473
|
+
const installDir = resolve(__dirname, '..');
|
|
474
|
+
const hookScript = join(installDir, 'bin', 'ecc-hooks.cjs');
|
|
475
|
+
if (!existsSync(hookScript))
|
|
476
|
+
return 0;
|
|
477
|
+
const nodeBin = process.platform === 'win32' ? 'node' : 'node';
|
|
478
|
+
// Strip prior ECC-tagged hooks
|
|
479
|
+
const existing = listHooks().filter(h => !h.command.includes(ECC_HOOK_TAG));
|
|
480
|
+
const eccHooks = [
|
|
481
|
+
{
|
|
482
|
+
event: 'PreToolUse',
|
|
483
|
+
match: 'bash',
|
|
484
|
+
command: `${nodeBin} "${hookScript}" block-no-verify ${ECC_HOOK_TAG}`,
|
|
485
|
+
blocking: true,
|
|
486
|
+
enabled: true,
|
|
487
|
+
timeout: 5000,
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
event: 'PreToolUse',
|
|
491
|
+
match: 'bash',
|
|
492
|
+
command: `${nodeBin} "${hookScript}" dev-server-tmux ${ECC_HOOK_TAG}`,
|
|
493
|
+
blocking: false,
|
|
494
|
+
enabled: true,
|
|
495
|
+
timeout: 5000,
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
event: 'PreToolUse',
|
|
499
|
+
match: 'read_file',
|
|
500
|
+
command: `${nodeBin} "${hookScript}" sensitive-file ${ECC_HOOK_TAG}`,
|
|
501
|
+
blocking: false,
|
|
502
|
+
enabled: true,
|
|
503
|
+
timeout: 5000,
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
event: 'PostToolUse',
|
|
507
|
+
match: 'edit_file',
|
|
508
|
+
command: `${nodeBin} "${hookScript}" console-log-warn ${ECC_HOOK_TAG}`,
|
|
509
|
+
blocking: false,
|
|
510
|
+
enabled: true,
|
|
511
|
+
timeout: 5000,
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
event: 'PostToolUse',
|
|
515
|
+
match: 'write_file',
|
|
516
|
+
command: `${nodeBin} "${hookScript}" console-log-warn ${ECC_HOOK_TAG}`,
|
|
517
|
+
blocking: false,
|
|
518
|
+
enabled: true,
|
|
519
|
+
timeout: 5000,
|
|
520
|
+
},
|
|
521
|
+
];
|
|
522
|
+
saveHooksConfig({ hooks: [...existing, ...eccHooks] });
|
|
523
|
+
return eccHooks.length;
|
|
524
|
+
}
|
|
525
|
+
// ── Top-level install ───────────────────────────────────
|
|
526
|
+
export function installEcc(opts = {}) {
|
|
527
|
+
if (!eccResourcesAvailable()) {
|
|
528
|
+
return {
|
|
529
|
+
skills: 0, agents: 0, commands: 0, rules: 0, prompts: 0,
|
|
530
|
+
errors: [`ECC resources not found at ${RESOURCES_ROOT}`],
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
mkdirSync(getConfigDir(), { recursive: true });
|
|
534
|
+
const s = importSkills();
|
|
535
|
+
const a = importAgents();
|
|
536
|
+
const cp = importCommandsAndPrompts();
|
|
537
|
+
const r = importRules();
|
|
538
|
+
const hookCount = seedHooks();
|
|
539
|
+
const report = {
|
|
540
|
+
skills: s.count,
|
|
541
|
+
agents: a.count,
|
|
542
|
+
commands: cp.commands,
|
|
543
|
+
rules: r.count,
|
|
544
|
+
prompts: cp.prompts,
|
|
545
|
+
errors: [...s.errors, ...a.errors, ...cp.errors, ...r.errors],
|
|
546
|
+
};
|
|
547
|
+
saveEccState({
|
|
548
|
+
installedAt: new Date().toISOString(),
|
|
549
|
+
version: '1.0.0',
|
|
550
|
+
counts: {
|
|
551
|
+
skills: report.skills,
|
|
552
|
+
agents: report.agents,
|
|
553
|
+
commands: report.commands,
|
|
554
|
+
rules: report.rules,
|
|
555
|
+
prompts: report.prompts,
|
|
556
|
+
},
|
|
557
|
+
});
|
|
558
|
+
if (opts.verbose) {
|
|
559
|
+
console.log(chalk.cyan(`\n ECC install complete:`));
|
|
560
|
+
console.log(chalk.dim(` skills: ${report.skills}`));
|
|
561
|
+
console.log(chalk.dim(` agents: ${report.agents}`));
|
|
562
|
+
console.log(chalk.dim(` commands: ${report.commands}`));
|
|
563
|
+
console.log(chalk.dim(` prompts: ${report.prompts}`));
|
|
564
|
+
console.log(chalk.dim(` rules: ${report.rules} languages`));
|
|
565
|
+
console.log(chalk.dim(` hooks: ${hookCount} native hooks seeded`));
|
|
566
|
+
if (report.errors.length) {
|
|
567
|
+
console.log(chalk.yellow(`\n ${report.errors.length} errors:`));
|
|
568
|
+
for (const e of report.errors.slice(0, 5)) {
|
|
569
|
+
console.log(chalk.dim(` ${e}`));
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
return report;
|
|
574
|
+
}
|
|
575
|
+
// ── Status output ───────────────────────────────────────
|
|
576
|
+
export function printEccStatus() {
|
|
577
|
+
const state = loadEccState();
|
|
578
|
+
if (!state) {
|
|
579
|
+
console.log(chalk.yellow('\n ECC not yet installed.'));
|
|
580
|
+
console.log(chalk.dim(' Run /ecc-install to import skills, agents, commands, rules, and hooks.\n'));
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
console.log(chalk.cyan('\n everything-claude-code integration'));
|
|
584
|
+
console.log(chalk.dim(` installed: ${state.installedAt}`));
|
|
585
|
+
console.log(chalk.dim(` version: ${state.version}`));
|
|
586
|
+
console.log(chalk.dim(` skills: ${state.counts.skills}`));
|
|
587
|
+
console.log(chalk.dim(` agents: ${state.counts.agents}`));
|
|
588
|
+
console.log(chalk.dim(` commands: ${state.counts.commands}`));
|
|
589
|
+
console.log(chalk.dim(` prompts: ${state.counts.prompts}`));
|
|
590
|
+
console.log(chalk.dim(` rules: ${state.counts.rules} languages`));
|
|
591
|
+
console.log(chalk.dim(`\n Commands available: /ecc, /ecc-install, /ecc-skills, /ecc-agents,`));
|
|
592
|
+
console.log(chalk.dim(` /ecc-commands, /ecc-<command-name>`));
|
|
593
|
+
console.log();
|
|
594
|
+
}
|
|
595
|
+
export function printEccSkills() {
|
|
596
|
+
const skills = listSkills().filter(s => s.id.startsWith(ECC_SKILL_ID_PREFIX) && !s.id.startsWith(`${ECC_SKILL_ID_PREFIX}agent-`));
|
|
597
|
+
console.log(chalk.cyan(`\n ECC skills: ${skills.length}`));
|
|
598
|
+
for (const s of skills.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
599
|
+
const desc = s.description.length > 70 ? s.description.slice(0, 67) + '...' : s.description;
|
|
600
|
+
console.log(chalk.dim(` ${s.name.padEnd(28)} ${desc}`));
|
|
601
|
+
}
|
|
602
|
+
console.log();
|
|
603
|
+
}
|
|
604
|
+
export function printEccAgents() {
|
|
605
|
+
const agents = listSkills().filter(s => s.id.startsWith(`${ECC_SKILL_ID_PREFIX}agent-`));
|
|
606
|
+
console.log(chalk.cyan(`\n ECC agents: ${agents.length}`));
|
|
607
|
+
for (const a of agents.sort((x, y) => x.name.localeCompare(y.name))) {
|
|
608
|
+
const desc = a.description.length > 70 ? a.description.slice(0, 67) + '...' : a.description;
|
|
609
|
+
console.log(chalk.dim(` ${a.name.padEnd(28)} ${desc}`));
|
|
610
|
+
}
|
|
611
|
+
console.log();
|
|
612
|
+
}
|
|
613
|
+
export function printEccCommandList() {
|
|
614
|
+
const cmds = listEccCommands();
|
|
615
|
+
console.log(chalk.cyan(`\n ECC commands: ${cmds.length}`));
|
|
616
|
+
for (const c of cmds) {
|
|
617
|
+
console.log(chalk.dim(` /ecc-${c}`));
|
|
618
|
+
}
|
|
619
|
+
console.log();
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Match the user's free-form text against ECC skill triggers and return the
|
|
623
|
+
* top-matching skill prompt body, or null if nothing fires. Used to auto-inject
|
|
624
|
+
* skill content into the system prompt — bumps relevance for that turn.
|
|
625
|
+
*/
|
|
626
|
+
export function findEccSkillForQuery(query) {
|
|
627
|
+
if (!query)
|
|
628
|
+
return null;
|
|
629
|
+
const q = query.toLowerCase();
|
|
630
|
+
const candidates = listSkills().filter(s => s.id.startsWith(ECC_SKILL_ID_PREFIX));
|
|
631
|
+
let best = null;
|
|
632
|
+
for (const s of candidates) {
|
|
633
|
+
let score = 0;
|
|
634
|
+
for (const t of s.triggers) {
|
|
635
|
+
if (q.includes(t.toLowerCase()))
|
|
636
|
+
score += t.length;
|
|
637
|
+
}
|
|
638
|
+
if (score > 0 && (!best || score > best.score)) {
|
|
639
|
+
best = { skill: s, score };
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
return best?.skill ?? null;
|
|
643
|
+
}
|
|
644
|
+
//# sourceMappingURL=ecc.js.map
|