chainlesschain 0.37.10 → 0.37.12
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 +166 -10
- package/package.json +1 -1
- package/src/commands/a2a.js +374 -0
- package/src/commands/bi.js +240 -0
- package/src/commands/cowork.js +317 -0
- package/src/commands/economy.js +375 -0
- package/src/commands/evolution.js +398 -0
- package/src/commands/hmemory.js +273 -0
- package/src/commands/hook.js +260 -0
- package/src/commands/init.js +184 -0
- package/src/commands/lowcode.js +320 -0
- package/src/commands/plugin.js +55 -2
- package/src/commands/sandbox.js +366 -0
- package/src/commands/skill.js +254 -201
- package/src/commands/workflow.js +359 -0
- package/src/commands/zkp.js +277 -0
- package/src/index.js +44 -0
- package/src/lib/a2a-protocol.js +371 -0
- package/src/lib/agent-coordinator.js +273 -0
- package/src/lib/agent-economy.js +369 -0
- package/src/lib/app-builder.js +377 -0
- package/src/lib/bi-engine.js +299 -0
- package/src/lib/cowork/ab-comparator-cli.js +180 -0
- package/src/lib/cowork/code-knowledge-graph-cli.js +232 -0
- package/src/lib/cowork/debate-review-cli.js +144 -0
- package/src/lib/cowork/decision-kb-cli.js +153 -0
- package/src/lib/cowork/project-style-analyzer-cli.js +168 -0
- package/src/lib/cowork-adapter.js +106 -0
- package/src/lib/evolution-system.js +508 -0
- package/src/lib/hierarchical-memory.js +471 -0
- package/src/lib/hook-manager.js +387 -0
- package/src/lib/plugin-manager.js +118 -0
- package/src/lib/project-detector.js +53 -0
- package/src/lib/sandbox-v2.js +503 -0
- package/src/lib/service-container.js +183 -0
- package/src/lib/skill-loader.js +274 -0
- package/src/lib/workflow-engine.js +503 -0
- package/src/lib/zkp-engine.js +241 -0
- package/src/repl/agent-repl.js +117 -112
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook management commands
|
|
3
|
+
* chainlesschain hook list|add|remove|run|stats|events
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { logger } from "../lib/logger.js";
|
|
8
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
9
|
+
import {
|
|
10
|
+
HookPriority,
|
|
11
|
+
HookType,
|
|
12
|
+
HookEvents,
|
|
13
|
+
registerHook,
|
|
14
|
+
unregisterHook,
|
|
15
|
+
listHooks,
|
|
16
|
+
executeHooks,
|
|
17
|
+
getHookStats,
|
|
18
|
+
} from "../lib/hook-manager.js";
|
|
19
|
+
|
|
20
|
+
export function registerHookCommand(program) {
|
|
21
|
+
const hook = program.command("hook").description("Lifecycle hook management");
|
|
22
|
+
|
|
23
|
+
// hook list
|
|
24
|
+
hook
|
|
25
|
+
.command("list", { isDefault: true })
|
|
26
|
+
.description("List all registered hooks")
|
|
27
|
+
.option("--event <name>", "Filter by event name")
|
|
28
|
+
.option("--enabled", "Show only enabled hooks")
|
|
29
|
+
.option("--json", "Output as JSON")
|
|
30
|
+
.action(async (options) => {
|
|
31
|
+
try {
|
|
32
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
33
|
+
if (!ctx.db) {
|
|
34
|
+
logger.error("Database not available");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
const db = ctx.db.getDatabase();
|
|
38
|
+
const hooks = listHooks(db, {
|
|
39
|
+
event: options.event,
|
|
40
|
+
enabledOnly: options.enabled,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
if (options.json) {
|
|
44
|
+
console.log(
|
|
45
|
+
JSON.stringify(
|
|
46
|
+
hooks.map((h) => ({
|
|
47
|
+
id: h.id,
|
|
48
|
+
event: h.event,
|
|
49
|
+
name: h.name,
|
|
50
|
+
type: h.type,
|
|
51
|
+
priority: h.priority,
|
|
52
|
+
enabled: h.enabled === 1,
|
|
53
|
+
matcher: h.matcher,
|
|
54
|
+
description: h.description,
|
|
55
|
+
})),
|
|
56
|
+
null,
|
|
57
|
+
2,
|
|
58
|
+
),
|
|
59
|
+
);
|
|
60
|
+
} else if (hooks.length === 0) {
|
|
61
|
+
logger.info(
|
|
62
|
+
'No hooks registered. Add one with "chainlesschain hook add <event> <name>"',
|
|
63
|
+
);
|
|
64
|
+
} else {
|
|
65
|
+
logger.log(chalk.bold(`Hooks (${hooks.length}):\n`));
|
|
66
|
+
for (const h of hooks) {
|
|
67
|
+
const status = h.enabled
|
|
68
|
+
? chalk.green("enabled")
|
|
69
|
+
: chalk.gray("disabled");
|
|
70
|
+
const pLabel = Object.entries(HookPriority).find(
|
|
71
|
+
([, v]) => v === h.priority,
|
|
72
|
+
);
|
|
73
|
+
const priorityStr = pLabel ? pLabel[0] : String(h.priority);
|
|
74
|
+
logger.log(
|
|
75
|
+
` ${chalk.cyan(h.name)} [${h.event}] priority=${priorityStr} type=${h.type} [${status}]`,
|
|
76
|
+
);
|
|
77
|
+
if (h.description) logger.log(` ${chalk.gray(h.description)}`);
|
|
78
|
+
if (h.matcher)
|
|
79
|
+
logger.log(` matcher: ${chalk.yellow(h.matcher)}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await shutdown();
|
|
84
|
+
} catch (err) {
|
|
85
|
+
logger.error(`Failed: ${err.message}`);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// hook add
|
|
91
|
+
hook
|
|
92
|
+
.command("add")
|
|
93
|
+
.description("Register a new hook")
|
|
94
|
+
.argument("<event>", "Event name (e.g. PreIPCCall, PostToolUse)")
|
|
95
|
+
.argument("<name>", "Hook name")
|
|
96
|
+
.option("--type <type>", "Hook type (sync, async, command, script)", "sync")
|
|
97
|
+
.option(
|
|
98
|
+
"--priority <n>",
|
|
99
|
+
"Priority (0=system, 100=high, 500=normal, 900=low, 1000=monitor)",
|
|
100
|
+
"500",
|
|
101
|
+
)
|
|
102
|
+
.option(
|
|
103
|
+
"--command <cmd>",
|
|
104
|
+
"Shell command to execute (for command/script type)",
|
|
105
|
+
)
|
|
106
|
+
.option(
|
|
107
|
+
"--matcher <pattern>",
|
|
108
|
+
"Matcher pattern (wildcards, pipe-separated, or /regex/)",
|
|
109
|
+
)
|
|
110
|
+
.option("--description <desc>", "Hook description")
|
|
111
|
+
.action(async (event, name, options) => {
|
|
112
|
+
try {
|
|
113
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
114
|
+
if (!ctx.db) {
|
|
115
|
+
logger.error("Database not available");
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
const db = ctx.db.getDatabase();
|
|
119
|
+
|
|
120
|
+
const result = registerHook(db, {
|
|
121
|
+
event,
|
|
122
|
+
name,
|
|
123
|
+
type: options.type,
|
|
124
|
+
priority: parseInt(options.priority, 10),
|
|
125
|
+
handler: options.command || null,
|
|
126
|
+
matcher: options.matcher || null,
|
|
127
|
+
description: options.description || null,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
logger.success(
|
|
131
|
+
`Hook registered: ${result.name} [${result.event}] (id: ${result.id})`,
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
await shutdown();
|
|
135
|
+
} catch (err) {
|
|
136
|
+
logger.error(`Failed: ${err.message}`);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// hook remove
|
|
142
|
+
hook
|
|
143
|
+
.command("remove")
|
|
144
|
+
.description("Remove a hook by ID")
|
|
145
|
+
.argument("<id>", "Hook ID")
|
|
146
|
+
.action(async (id) => {
|
|
147
|
+
try {
|
|
148
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
149
|
+
if (!ctx.db) {
|
|
150
|
+
logger.error("Database not available");
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
const db = ctx.db.getDatabase();
|
|
154
|
+
const ok = unregisterHook(db, id);
|
|
155
|
+
|
|
156
|
+
if (ok) {
|
|
157
|
+
logger.success(`Hook removed: ${id}`);
|
|
158
|
+
} else {
|
|
159
|
+
logger.error(`Hook not found: ${id}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await shutdown();
|
|
163
|
+
} catch (err) {
|
|
164
|
+
logger.error(`Failed: ${err.message}`);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// hook run
|
|
170
|
+
hook
|
|
171
|
+
.command("run")
|
|
172
|
+
.description("Manually trigger hooks for an event")
|
|
173
|
+
.argument("<event>", "Event name to trigger")
|
|
174
|
+
.option("--context <json>", "JSON context to pass to hooks", "{}")
|
|
175
|
+
.action(async (event, options) => {
|
|
176
|
+
try {
|
|
177
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
178
|
+
if (!ctx.db) {
|
|
179
|
+
logger.error("Database not available");
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
const db = ctx.db.getDatabase();
|
|
183
|
+
|
|
184
|
+
let context = {};
|
|
185
|
+
try {
|
|
186
|
+
context = JSON.parse(options.context);
|
|
187
|
+
} catch (_err) {
|
|
188
|
+
logger.warn("Invalid JSON context, using empty object");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
logger.info(`Triggering hooks for event: ${event}`);
|
|
192
|
+
const results = await executeHooks(db, event, context);
|
|
193
|
+
|
|
194
|
+
if (results.length === 0) {
|
|
195
|
+
logger.info("No hooks matched this event");
|
|
196
|
+
} else {
|
|
197
|
+
for (const r of results) {
|
|
198
|
+
const icon = r.success ? chalk.green("OK") : chalk.red("FAIL");
|
|
199
|
+
logger.log(` [${icon}] ${r.hookName} (${r.executionTime}ms)`);
|
|
200
|
+
if (r.error) logger.log(` ${chalk.red(r.error)}`);
|
|
201
|
+
if (r.result) logger.log(` ${chalk.gray(r.result)}`);
|
|
202
|
+
}
|
|
203
|
+
logger.info(`Executed ${results.length} hook(s)`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
await shutdown();
|
|
207
|
+
} catch (err) {
|
|
208
|
+
logger.error(`Failed: ${err.message}`);
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// hook stats
|
|
214
|
+
hook
|
|
215
|
+
.command("stats")
|
|
216
|
+
.description("Show hook execution statistics")
|
|
217
|
+
.option("--json", "Output as JSON")
|
|
218
|
+
.action(async (options) => {
|
|
219
|
+
try {
|
|
220
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
221
|
+
if (!ctx.db) {
|
|
222
|
+
logger.error("Database not available");
|
|
223
|
+
process.exit(1);
|
|
224
|
+
}
|
|
225
|
+
const db = ctx.db.getDatabase();
|
|
226
|
+
const stats = getHookStats(db);
|
|
227
|
+
|
|
228
|
+
if (options.json) {
|
|
229
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
230
|
+
} else if (stats.length === 0) {
|
|
231
|
+
logger.info("No hooks registered");
|
|
232
|
+
} else {
|
|
233
|
+
logger.log(chalk.bold("Hook Statistics:\n"));
|
|
234
|
+
for (const s of stats) {
|
|
235
|
+
logger.log(` ${chalk.cyan(s.name)} [${s.event}]`);
|
|
236
|
+
logger.log(
|
|
237
|
+
` executions: ${s.executionCount} errors: ${s.errorCount} avg: ${s.avgExecutionTime}ms`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
await shutdown();
|
|
243
|
+
} catch (err) {
|
|
244
|
+
logger.error(`Failed: ${err.message}`);
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
// hook events
|
|
250
|
+
hook
|
|
251
|
+
.command("events")
|
|
252
|
+
.description("List all valid hook event types")
|
|
253
|
+
.action(() => {
|
|
254
|
+
const events = Object.values(HookEvents);
|
|
255
|
+
logger.log(chalk.bold(`Hook Events (${events.length}):\n`));
|
|
256
|
+
for (const ev of events) {
|
|
257
|
+
logger.log(` ${chalk.cyan(ev)}`);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project initialization command
|
|
3
|
+
* chainlesschain init [--template <name>] [--yes] [--bare]
|
|
4
|
+
*
|
|
5
|
+
* Creates .chainlesschain/ project structure in the current directory.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import fs from "fs";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { logger } from "../lib/logger.js";
|
|
12
|
+
import { isInsideProject, findProjectRoot } from "../lib/project-detector.js";
|
|
13
|
+
|
|
14
|
+
const TEMPLATES = {
|
|
15
|
+
"code-project": {
|
|
16
|
+
description:
|
|
17
|
+
"Software development project with code review and refactoring skills",
|
|
18
|
+
rules: `# Project Rules
|
|
19
|
+
|
|
20
|
+
## Code Style
|
|
21
|
+
- Follow the project's existing code style
|
|
22
|
+
- Use meaningful variable and function names
|
|
23
|
+
- Keep functions small and focused
|
|
24
|
+
|
|
25
|
+
## AI Assistant Guidelines
|
|
26
|
+
- Prefer editing existing files over creating new ones
|
|
27
|
+
- Run tests after making changes
|
|
28
|
+
- Use code-review skill before committing
|
|
29
|
+
`,
|
|
30
|
+
skills: ["code-review", "refactor", "unit-test", "debug"],
|
|
31
|
+
},
|
|
32
|
+
"data-science": {
|
|
33
|
+
description:
|
|
34
|
+
"Data science / ML project with analysis and visualization skills",
|
|
35
|
+
rules: `# Project Rules
|
|
36
|
+
|
|
37
|
+
## Data Handling
|
|
38
|
+
- Never commit raw data files
|
|
39
|
+
- Document data transformations
|
|
40
|
+
- Use reproducible random seeds
|
|
41
|
+
|
|
42
|
+
## AI Assistant Guidelines
|
|
43
|
+
- Use data-analysis skill for exploration
|
|
44
|
+
- Document findings in markdown
|
|
45
|
+
- Validate results before reporting
|
|
46
|
+
`,
|
|
47
|
+
skills: ["data-analysis", "summarize", "explain-code"],
|
|
48
|
+
},
|
|
49
|
+
devops: {
|
|
50
|
+
description:
|
|
51
|
+
"DevOps / infrastructure project with deployment and monitoring skills",
|
|
52
|
+
rules: `# Project Rules
|
|
53
|
+
|
|
54
|
+
## Infrastructure
|
|
55
|
+
- Use infrastructure as code
|
|
56
|
+
- Tag all resources appropriately
|
|
57
|
+
- Follow least-privilege principle
|
|
58
|
+
|
|
59
|
+
## AI Assistant Guidelines
|
|
60
|
+
- Always validate configs before applying
|
|
61
|
+
- Use dry-run when available
|
|
62
|
+
- Document infrastructure changes
|
|
63
|
+
`,
|
|
64
|
+
skills: ["debug", "summarize", "code-review"],
|
|
65
|
+
},
|
|
66
|
+
empty: {
|
|
67
|
+
description: "Bare project with minimal configuration",
|
|
68
|
+
rules: `# Project Rules
|
|
69
|
+
|
|
70
|
+
Add your project-specific rules and conventions here.
|
|
71
|
+
The AI assistant will follow these guidelines when working in this project.
|
|
72
|
+
`,
|
|
73
|
+
skills: [],
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export function registerInitCommand(program) {
|
|
78
|
+
program
|
|
79
|
+
.command("init")
|
|
80
|
+
.description(
|
|
81
|
+
"Initialize a .chainlesschain/ project in the current directory",
|
|
82
|
+
)
|
|
83
|
+
.option(
|
|
84
|
+
"-t, --template <name>",
|
|
85
|
+
"Project template (code-project, data-science, devops, empty)",
|
|
86
|
+
"empty",
|
|
87
|
+
)
|
|
88
|
+
.option("-y, --yes", "Skip prompts, use defaults")
|
|
89
|
+
.option(
|
|
90
|
+
"--bare",
|
|
91
|
+
"Create minimal structure (alias for --template empty --yes)",
|
|
92
|
+
)
|
|
93
|
+
.action(async (options) => {
|
|
94
|
+
const cwd = process.cwd();
|
|
95
|
+
const ccDir = path.join(cwd, ".chainlesschain");
|
|
96
|
+
|
|
97
|
+
// Check if already initialized
|
|
98
|
+
if (fs.existsSync(path.join(ccDir, "config.json"))) {
|
|
99
|
+
const existingRoot = findProjectRoot(cwd);
|
|
100
|
+
logger.error(
|
|
101
|
+
`Already initialized at ${existingRoot || cwd}. Remove .chainlesschain/ to reinitialize.`,
|
|
102
|
+
);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Determine template
|
|
107
|
+
let template = options.bare ? "empty" : options.template;
|
|
108
|
+
if (!TEMPLATES[template]) {
|
|
109
|
+
logger.error(
|
|
110
|
+
`Unknown template: ${template}. Available: ${Object.keys(TEMPLATES).join(", ")}`,
|
|
111
|
+
);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Interactive selection if not --yes/--bare
|
|
116
|
+
if (!options.yes && !options.bare) {
|
|
117
|
+
try {
|
|
118
|
+
const { select } = await import("@inquirer/prompts");
|
|
119
|
+
template = await select({
|
|
120
|
+
message: "Select a project template:",
|
|
121
|
+
choices: Object.entries(TEMPLATES).map(([key, val]) => ({
|
|
122
|
+
name: `${key} — ${val.description}`,
|
|
123
|
+
value: key,
|
|
124
|
+
})),
|
|
125
|
+
default: template,
|
|
126
|
+
});
|
|
127
|
+
} catch {
|
|
128
|
+
// Ctrl+C or non-interactive — use default
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const tmpl = TEMPLATES[template];
|
|
133
|
+
const projectName = path.basename(cwd);
|
|
134
|
+
|
|
135
|
+
// Create directory structure
|
|
136
|
+
try {
|
|
137
|
+
fs.mkdirSync(ccDir, { recursive: true });
|
|
138
|
+
fs.mkdirSync(path.join(ccDir, "skills"), { recursive: true });
|
|
139
|
+
|
|
140
|
+
// config.json
|
|
141
|
+
const config = {
|
|
142
|
+
name: projectName,
|
|
143
|
+
template,
|
|
144
|
+
version: "1.0.0",
|
|
145
|
+
createdAt: new Date().toISOString(),
|
|
146
|
+
skills: {
|
|
147
|
+
workspace: "./skills",
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
fs.writeFileSync(
|
|
151
|
+
path.join(ccDir, "config.json"),
|
|
152
|
+
JSON.stringify(config, null, 2),
|
|
153
|
+
"utf-8",
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
// rules.md
|
|
157
|
+
fs.writeFileSync(path.join(ccDir, "rules.md"), tmpl.rules, "utf-8");
|
|
158
|
+
|
|
159
|
+
logger.success(
|
|
160
|
+
`Initialized ChainlessChain project in ${chalk.cyan(cwd)}`,
|
|
161
|
+
);
|
|
162
|
+
logger.log("");
|
|
163
|
+
logger.log(` Template: ${chalk.cyan(template)}`);
|
|
164
|
+
logger.log(` Config: ${chalk.gray(".chainlesschain/config.json")}`);
|
|
165
|
+
logger.log(` Rules: ${chalk.gray(".chainlesschain/rules.md")}`);
|
|
166
|
+
logger.log(` Skills: ${chalk.gray(".chainlesschain/skills/")}`);
|
|
167
|
+
logger.log("");
|
|
168
|
+
logger.log(chalk.bold("Next steps:"));
|
|
169
|
+
logger.log(
|
|
170
|
+
` ${chalk.cyan("chainlesschain skill add <name>")} Create a custom project skill`,
|
|
171
|
+
);
|
|
172
|
+
logger.log(
|
|
173
|
+
` ${chalk.cyan("chainlesschain skill list")} List all available skills`,
|
|
174
|
+
);
|
|
175
|
+
logger.log(
|
|
176
|
+
` ${chalk.cyan("chainlesschain agent")} Start the AI agent`,
|
|
177
|
+
);
|
|
178
|
+
logger.log("");
|
|
179
|
+
} catch (err) {
|
|
180
|
+
logger.error(`Failed to initialize: ${err.message}`);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}
|