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.
Files changed (39) hide show
  1. package/README.md +166 -10
  2. package/package.json +1 -1
  3. package/src/commands/a2a.js +374 -0
  4. package/src/commands/bi.js +240 -0
  5. package/src/commands/cowork.js +317 -0
  6. package/src/commands/economy.js +375 -0
  7. package/src/commands/evolution.js +398 -0
  8. package/src/commands/hmemory.js +273 -0
  9. package/src/commands/hook.js +260 -0
  10. package/src/commands/init.js +184 -0
  11. package/src/commands/lowcode.js +320 -0
  12. package/src/commands/plugin.js +55 -2
  13. package/src/commands/sandbox.js +366 -0
  14. package/src/commands/skill.js +254 -201
  15. package/src/commands/workflow.js +359 -0
  16. package/src/commands/zkp.js +277 -0
  17. package/src/index.js +44 -0
  18. package/src/lib/a2a-protocol.js +371 -0
  19. package/src/lib/agent-coordinator.js +273 -0
  20. package/src/lib/agent-economy.js +369 -0
  21. package/src/lib/app-builder.js +377 -0
  22. package/src/lib/bi-engine.js +299 -0
  23. package/src/lib/cowork/ab-comparator-cli.js +180 -0
  24. package/src/lib/cowork/code-knowledge-graph-cli.js +232 -0
  25. package/src/lib/cowork/debate-review-cli.js +144 -0
  26. package/src/lib/cowork/decision-kb-cli.js +153 -0
  27. package/src/lib/cowork/project-style-analyzer-cli.js +168 -0
  28. package/src/lib/cowork-adapter.js +106 -0
  29. package/src/lib/evolution-system.js +508 -0
  30. package/src/lib/hierarchical-memory.js +471 -0
  31. package/src/lib/hook-manager.js +387 -0
  32. package/src/lib/plugin-manager.js +118 -0
  33. package/src/lib/project-detector.js +53 -0
  34. package/src/lib/sandbox-v2.js +503 -0
  35. package/src/lib/service-container.js +183 -0
  36. package/src/lib/skill-loader.js +274 -0
  37. package/src/lib/workflow-engine.js +503 -0
  38. package/src/lib/zkp-engine.js +241 -0
  39. 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
+ }