kc-beta 0.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.
Files changed (141) hide show
  1. package/bin/kc-beta.js +16 -0
  2. package/package.json +32 -0
  3. package/src/agent/confidence-scorer.js +120 -0
  4. package/src/agent/context.js +124 -0
  5. package/src/agent/corner-case-registry.js +119 -0
  6. package/src/agent/engine.js +224 -0
  7. package/src/agent/events.js +27 -0
  8. package/src/agent/history.js +101 -0
  9. package/src/agent/llm-client.js +131 -0
  10. package/src/agent/pipelines/base.js +14 -0
  11. package/src/agent/pipelines/distillation.js +113 -0
  12. package/src/agent/pipelines/extraction.js +92 -0
  13. package/src/agent/pipelines/index.js +23 -0
  14. package/src/agent/pipelines/initializer.js +163 -0
  15. package/src/agent/pipelines/production-qc.js +99 -0
  16. package/src/agent/pipelines/skill-authoring.js +83 -0
  17. package/src/agent/pipelines/skill-testing.js +111 -0
  18. package/src/agent/tools/agent-tool.js +100 -0
  19. package/src/agent/tools/base.js +35 -0
  20. package/src/agent/tools/dashboard-render.js +146 -0
  21. package/src/agent/tools/document-parse.js +184 -0
  22. package/src/agent/tools/document-search.js +111 -0
  23. package/src/agent/tools/evolution-cycle.js +150 -0
  24. package/src/agent/tools/qc-sample.js +94 -0
  25. package/src/agent/tools/registry.js +55 -0
  26. package/src/agent/tools/rule-catalog.js +113 -0
  27. package/src/agent/tools/sandbox-exec.js +106 -0
  28. package/src/agent/tools/tier-downgrade.js +114 -0
  29. package/src/agent/tools/worker-llm-call.js +109 -0
  30. package/src/agent/tools/workflow-run.js +138 -0
  31. package/src/agent/tools/workspace-file.js +122 -0
  32. package/src/agent/version-manager.js +130 -0
  33. package/src/agent/workspace.js +82 -0
  34. package/src/cli/components.js +164 -0
  35. package/src/cli/index.js +329 -0
  36. package/src/cli/init.js +80 -0
  37. package/src/cli/onboard.js +182 -0
  38. package/src/cli/terminal.js +143 -0
  39. package/src/config.js +93 -0
  40. package/template/.env.template +31 -0
  41. package/template/CLAUDE.md +137 -0
  42. package/template/Input/.gitkeep +0 -0
  43. package/template/Output/.gitkeep +0 -0
  44. package/template/Rules/.gitkeep +0 -0
  45. package/template/Samples/.gitkeep +0 -0
  46. package/template/skills/en/meta/compliance-judgment/SKILL.md +114 -0
  47. package/template/skills/en/meta/compliance-judgment/references/output-format.md +151 -0
  48. package/template/skills/en/meta/confidence-system/SKILL.md +117 -0
  49. package/template/skills/en/meta/corner-case-management/SKILL.md +111 -0
  50. package/template/skills/en/meta/cross-document-verification/SKILL.md +131 -0
  51. package/template/skills/en/meta/cross-document-verification/references/contradiction-taxonomy.md +73 -0
  52. package/template/skills/en/meta/data-sensibility/SKILL.md +115 -0
  53. package/template/skills/en/meta/document-parsing/SKILL.md +108 -0
  54. package/template/skills/en/meta/document-parsing/references/parser-catalog.md +40 -0
  55. package/template/skills/en/meta/entity-extraction/SKILL.md +129 -0
  56. package/template/skills/en/meta/tree-processing/SKILL.md +103 -0
  57. package/template/skills/en/meta-meta/bootstrap-workspace/SKILL.md +70 -0
  58. package/template/skills/en/meta-meta/dashboard-reporting/SKILL.md +106 -0
  59. package/template/skills/en/meta-meta/dashboard-reporting/scripts/generate_dashboard.py +178 -0
  60. package/template/skills/en/meta-meta/evolution-loop/SKILL.md +210 -0
  61. package/template/skills/en/meta-meta/evolution-loop/references/convergence-guide.md +62 -0
  62. package/template/skills/en/meta-meta/quality-control/SKILL.md +138 -0
  63. package/template/skills/en/meta-meta/quality-control/references/qa-layers.md +92 -0
  64. package/template/skills/en/meta-meta/quality-control/references/sampling-strategies.md +76 -0
  65. package/template/skills/en/meta-meta/rule-extraction/SKILL.md +100 -0
  66. package/template/skills/en/meta-meta/rule-extraction/references/chunking-strategies.md +80 -0
  67. package/template/skills/en/meta-meta/rule-graph/SKILL.md +118 -0
  68. package/template/skills/en/meta-meta/skill-authoring/SKILL.md +108 -0
  69. package/template/skills/en/meta-meta/skill-authoring/references/skill-format-spec.md +78 -0
  70. package/template/skills/en/meta-meta/skill-to-workflow/SKILL.md +150 -0
  71. package/template/skills/en/meta-meta/skill-to-workflow/references/worker-llm-catalog.md +50 -0
  72. package/template/skills/en/meta-meta/task-decomposition/SKILL.md +129 -0
  73. package/template/skills/en/meta-meta/task-decomposition/references/decision-matrix.md +81 -0
  74. package/template/skills/en/meta-meta/version-control/SKILL.md +152 -0
  75. package/template/skills/en/meta-meta/version-control/references/trace-id-spec.md +79 -0
  76. package/template/skills/en/skill-creator/LICENSE.txt +202 -0
  77. package/template/skills/en/skill-creator/SKILL.md +479 -0
  78. package/template/skills/en/skill-creator/agents/analyzer.md +274 -0
  79. package/template/skills/en/skill-creator/agents/comparator.md +202 -0
  80. package/template/skills/en/skill-creator/agents/grader.md +223 -0
  81. package/template/skills/en/skill-creator/assets/eval_review.html +146 -0
  82. package/template/skills/en/skill-creator/eval-viewer/generate_review.py +471 -0
  83. package/template/skills/en/skill-creator/eval-viewer/viewer.html +1325 -0
  84. package/template/skills/en/skill-creator/references/schemas.md +430 -0
  85. package/template/skills/en/skill-creator/scripts/__init__.py +0 -0
  86. package/template/skills/en/skill-creator/scripts/aggregate_benchmark.py +401 -0
  87. package/template/skills/en/skill-creator/scripts/generate_report.py +326 -0
  88. package/template/skills/en/skill-creator/scripts/improve_description.py +248 -0
  89. package/template/skills/en/skill-creator/scripts/package_skill.py +136 -0
  90. package/template/skills/en/skill-creator/scripts/quick_validate.py +103 -0
  91. package/template/skills/en/skill-creator/scripts/run_eval.py +310 -0
  92. package/template/skills/en/skill-creator/scripts/run_loop.py +332 -0
  93. package/template/skills/en/skill-creator/scripts/utils.py +47 -0
  94. package/template/skills/zh/meta/compliance-judgment/SKILL.md +303 -0
  95. package/template/skills/zh/meta/compliance-judgment/references/output-format.md +151 -0
  96. package/template/skills/zh/meta/confidence-system/SKILL.md +228 -0
  97. package/template/skills/zh/meta/corner-case-management/SKILL.md +235 -0
  98. package/template/skills/zh/meta/cross-document-verification/SKILL.md +241 -0
  99. package/template/skills/zh/meta/cross-document-verification/references/contradiction-taxonomy.md +73 -0
  100. package/template/skills/zh/meta/data-sensibility/SKILL.md +235 -0
  101. package/template/skills/zh/meta/document-parsing/SKILL.md +168 -0
  102. package/template/skills/zh/meta/document-parsing/references/parser-catalog.md +40 -0
  103. package/template/skills/zh/meta/entity-extraction/SKILL.md +276 -0
  104. package/template/skills/zh/meta/tree-processing/SKILL.md +233 -0
  105. package/template/skills/zh/meta-meta/bootstrap-workspace/SKILL.md +147 -0
  106. package/template/skills/zh/meta-meta/dashboard-reporting/SKILL.md +281 -0
  107. package/template/skills/zh/meta-meta/dashboard-reporting/scripts/generate_dashboard.py +178 -0
  108. package/template/skills/zh/meta-meta/evolution-loop/SKILL.md +302 -0
  109. package/template/skills/zh/meta-meta/evolution-loop/references/convergence-guide.md +62 -0
  110. package/template/skills/zh/meta-meta/quality-control/SKILL.md +269 -0
  111. package/template/skills/zh/meta-meta/quality-control/references/qa-layers.md +92 -0
  112. package/template/skills/zh/meta-meta/quality-control/references/sampling-strategies.md +76 -0
  113. package/template/skills/zh/meta-meta/rule-extraction/SKILL.md +208 -0
  114. package/template/skills/zh/meta-meta/rule-extraction/references/chunking-strategies.md +80 -0
  115. package/template/skills/zh/meta-meta/rule-graph/SKILL.md +203 -0
  116. package/template/skills/zh/meta-meta/skill-authoring/SKILL.md +235 -0
  117. package/template/skills/zh/meta-meta/skill-authoring/references/skill-format-spec.md +78 -0
  118. package/template/skills/zh/meta-meta/skill-to-workflow/SKILL.md +275 -0
  119. package/template/skills/zh/meta-meta/skill-to-workflow/references/worker-llm-catalog.md +50 -0
  120. package/template/skills/zh/meta-meta/task-decomposition/SKILL.md +224 -0
  121. package/template/skills/zh/meta-meta/task-decomposition/references/decision-matrix.md +81 -0
  122. package/template/skills/zh/meta-meta/version-control/SKILL.md +284 -0
  123. package/template/skills/zh/meta-meta/version-control/references/trace-id-spec.md +79 -0
  124. package/template/skills/zh/skill-creator/LICENSE.txt +202 -0
  125. package/template/skills/zh/skill-creator/SKILL.md +479 -0
  126. package/template/skills/zh/skill-creator/agents/analyzer.md +274 -0
  127. package/template/skills/zh/skill-creator/agents/comparator.md +202 -0
  128. package/template/skills/zh/skill-creator/agents/grader.md +223 -0
  129. package/template/skills/zh/skill-creator/assets/eval_review.html +146 -0
  130. package/template/skills/zh/skill-creator/eval-viewer/generate_review.py +471 -0
  131. package/template/skills/zh/skill-creator/eval-viewer/viewer.html +1325 -0
  132. package/template/skills/zh/skill-creator/references/schemas.md +430 -0
  133. package/template/skills/zh/skill-creator/scripts/__init__.py +0 -0
  134. package/template/skills/zh/skill-creator/scripts/aggregate_benchmark.py +401 -0
  135. package/template/skills/zh/skill-creator/scripts/generate_report.py +326 -0
  136. package/template/skills/zh/skill-creator/scripts/improve_description.py +248 -0
  137. package/template/skills/zh/skill-creator/scripts/package_skill.py +136 -0
  138. package/template/skills/zh/skill-creator/scripts/quick_validate.py +103 -0
  139. package/template/skills/zh/skill-creator/scripts/run_eval.py +310 -0
  140. package/template/skills/zh/skill-creator/scripts/run_loop.py +332 -0
  141. package/template/skills/zh/skill-creator/scripts/utils.py +47 -0
@@ -0,0 +1,122 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { BaseTool, ToolResult } from "./base.js";
4
+
5
+ const MAX_READ = 50_000;
6
+
7
+ /**
8
+ * Read, write, or list files in the workspace directory.
9
+ * All paths are resolved relative to the workspace root with
10
+ * traversal protection. VersionManager hooks into writes for automatic versioning.
11
+ */
12
+ export class WorkspaceFileTool extends BaseTool {
13
+ /**
14
+ * @param {import('../workspace.js').Workspace} workspace
15
+ * @param {import('../version-manager.js').VersionManager} [versionManager]
16
+ */
17
+ constructor(workspace, versionManager) {
18
+ super();
19
+ this._workspace = workspace;
20
+ this._versionManager = versionManager || null;
21
+ }
22
+
23
+ get name() { return "workspace_file"; }
24
+
25
+ get description() {
26
+ return (
27
+ "Read, write, or list files in the workspace directory. " +
28
+ "Operations: read (returns file content), write (creates/overwrites a file), " +
29
+ "list (shows directory contents)."
30
+ );
31
+ }
32
+
33
+ get inputSchema() {
34
+ return {
35
+ type: "object",
36
+ properties: {
37
+ operation: {
38
+ type: "string",
39
+ enum: ["read", "write", "list"],
40
+ description: "The file operation to perform",
41
+ },
42
+ path: {
43
+ type: "string",
44
+ description: "Relative path within the workspace. Defaults to '.' for list.",
45
+ },
46
+ content: {
47
+ type: "string",
48
+ description: "File content to write (required for write operation)",
49
+ },
50
+ },
51
+ required: ["operation"],
52
+ };
53
+ }
54
+
55
+ async execute(input) {
56
+ const op = input.operation || "";
57
+ const filePath = input.path || ".";
58
+ const content = input.content || "";
59
+
60
+ try {
61
+ if (op === "read") return this._read(filePath);
62
+ if (op === "write") return this._write(filePath, content);
63
+ if (op === "list") return this._list(filePath);
64
+ return new ToolResult(`Unknown operation: ${op}`, true);
65
+ } catch (err) {
66
+ return new ToolResult(`File error: ${err.message}`, true);
67
+ }
68
+ }
69
+
70
+ _read(filePath) {
71
+ const resolved = this._workspace.resolvePath(filePath);
72
+ if (!fs.existsSync(resolved) || !fs.statSync(resolved).isFile()) {
73
+ return new ToolResult(`File not found: ${filePath}`, true);
74
+ }
75
+ let text = fs.readFileSync(resolved, { encoding: "utf-8" });
76
+ if (text.length > MAX_READ) {
77
+ text = text.slice(0, MAX_READ) + "\n[truncated]";
78
+ }
79
+ return new ToolResult(text);
80
+ }
81
+
82
+ _write(filePath, content) {
83
+ if (!filePath || filePath === ".") {
84
+ return new ToolResult("Path required for write operation", true);
85
+ }
86
+ const resolved = this._workspace.resolvePath(filePath);
87
+ fs.mkdirSync(path.dirname(resolved), { recursive: true });
88
+ fs.writeFileSync(resolved, content, "utf-8");
89
+
90
+ // Version tracking (structural — cannot be bypassed)
91
+ let traceId = null;
92
+ if (this._versionManager) {
93
+ traceId = this._versionManager.onWrite(filePath, content);
94
+ }
95
+
96
+ let msg = `Wrote ${content.length} chars to ${filePath}`;
97
+ if (traceId) msg += ` [trace: ${traceId}]`;
98
+ return new ToolResult(msg);
99
+ }
100
+
101
+ _list(filePath) {
102
+ const resolved = this._workspace.resolvePath(filePath);
103
+ if (!fs.existsSync(resolved) || !fs.statSync(resolved).isDirectory()) {
104
+ return new ToolResult(`Not a directory: ${filePath}`, true);
105
+ }
106
+ const entries = fs.readdirSync(resolved, { withFileTypes: true });
107
+ entries.sort((a, b) => {
108
+ if (a.isDirectory() && !b.isDirectory()) return -1;
109
+ if (!a.isDirectory() && b.isDirectory()) return 1;
110
+ return a.name.localeCompare(b.name);
111
+ });
112
+ if (entries.length === 0) {
113
+ return new ToolResult("(empty directory)");
114
+ }
115
+ const lines = entries.map((e) => {
116
+ const rel = path.relative(this._workspace.cwd, path.join(resolved, e.name));
117
+ const marker = e.isDirectory() ? "[dir] " : " ";
118
+ return `${marker}${rel}`;
119
+ });
120
+ return new ToolResult(lines.join("\n"));
121
+ }
122
+ }
@@ -0,0 +1,130 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ const VERSIONED_DIRS = new Set(["workflows", "rule_skills", "rules"]);
5
+ const VERSIONED_EXTS = new Set([".py", ".json", ".md", ".txt"]);
6
+
7
+ /**
8
+ * Structural component: every write to versioned directories gets tracked.
9
+ * - Immutable version copies tracked in manifest
10
+ * - Manifest at versions.json: tracks lineage, timestamps, change reasons
11
+ * - Trace ID generation: {timestamp}_{rule_id}_{version}
12
+ * Cannot be bypassed — hooks into WorkspaceFileTool.
13
+ */
14
+ export class VersionManager {
15
+ /**
16
+ * @param {string} workspacePath
17
+ */
18
+ constructor(workspacePath) {
19
+ this._workspace = workspacePath;
20
+ this._manifestPath = path.join(workspacePath, "versions.json");
21
+ this._manifest = this._loadManifest();
22
+ }
23
+
24
+ _loadManifest() {
25
+ if (fs.existsSync(this._manifestPath)) {
26
+ try {
27
+ return JSON.parse(fs.readFileSync(this._manifestPath, "utf-8"));
28
+ } catch {
29
+ // fall through
30
+ }
31
+ }
32
+ return { version: "0.1.0", entries: [] };
33
+ }
34
+
35
+ _saveManifest() {
36
+ fs.writeFileSync(
37
+ this._manifestPath,
38
+ JSON.stringify(this._manifest, null, 2),
39
+ "utf-8",
40
+ );
41
+ }
42
+
43
+ /**
44
+ * Check if a path falls within versioned directories.
45
+ * @param {string} relPath
46
+ * @returns {boolean}
47
+ */
48
+ shouldVersion(relPath) {
49
+ const parts = relPath.split(path.sep);
50
+ if (parts.length === 0) return false;
51
+ const topDir = parts[0];
52
+ const ext = path.extname(relPath);
53
+ return VERSIONED_DIRS.has(topDir) && VERSIONED_EXTS.has(ext);
54
+ }
55
+
56
+ /**
57
+ * Called on file write. Returns trace ID if versioned, null otherwise.
58
+ * @param {string} relPath
59
+ * @param {string} content
60
+ * @returns {string|null}
61
+ */
62
+ onWrite(relPath, content) {
63
+ if (!this.shouldVersion(relPath)) return null;
64
+
65
+ const version = this._nextVersion(relPath);
66
+ const now = new Date().toISOString().replace(/[-:T]/g, (m) =>
67
+ m === "T" ? "_" : ""
68
+ ).slice(0, 15);
69
+
70
+ // Extract rule_id from path if possible (e.g., rule_skills/R001/SKILL.md → R001)
71
+ const parts = relPath.split(path.sep);
72
+ const ruleId = parts.length > 1 ? parts[1] : "global";
73
+
74
+ const traceId = `${now}_${ruleId}_v${version}`;
75
+
76
+ const entry = {
77
+ file: relPath,
78
+ version,
79
+ trace_id: traceId,
80
+ timestamp: new Date().toISOString(),
81
+ rule_id: ruleId,
82
+ size_chars: content.length,
83
+ };
84
+
85
+ this._manifest.entries.push(entry);
86
+ this._saveManifest();
87
+
88
+ return traceId;
89
+ }
90
+
91
+ _nextVersion(relPath) {
92
+ const existing = this._manifest.entries
93
+ .filter((e) => e.file === relPath)
94
+ .map((e) => e.version);
95
+ return Math.max(0, ...existing) + 1;
96
+ }
97
+
98
+ /**
99
+ * Get all version entries for a file.
100
+ * @param {string} relPath
101
+ * @returns {Array<object>}
102
+ */
103
+ getVersions(relPath) {
104
+ return this._manifest.entries.filter((e) => e.file === relPath);
105
+ }
106
+
107
+ /**
108
+ * Get the most recent version entry for a file.
109
+ * @param {string} relPath
110
+ * @returns {object|null}
111
+ */
112
+ latestVersion(relPath) {
113
+ const versions = this.getVersions(relPath);
114
+ return versions.length > 0 ? versions[versions.length - 1] : null;
115
+ }
116
+
117
+ /**
118
+ * Generate a standalone trace ID for results, QC records, etc.
119
+ * @param {string} ruleId
120
+ * @param {string} [label]
121
+ * @returns {string}
122
+ */
123
+ generateTraceId(ruleId, label = "") {
124
+ const now = new Date().toISOString().replace(/[-:T]/g, (m) =>
125
+ m === "T" ? "_" : ""
126
+ ).slice(0, 15);
127
+ const suffix = label ? `_${label}` : "";
128
+ return `${now}_${ruleId}${suffix}`;
129
+ }
130
+ }
@@ -0,0 +1,82 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import crypto from "node:crypto";
4
+
5
+ /**
6
+ * Per-session workspace directory with path traversal protection.
7
+ * Each agent session gets its own directory under the workspace root.
8
+ * All file operations by tools must go through resolvePath().
9
+ */
10
+ export class Workspace {
11
+ /**
12
+ * @param {string} root - Workspace root directory
13
+ * @param {string} [sessionId] - Session identifier (auto-generated if omitted)
14
+ */
15
+ constructor(root, sessionId) {
16
+ this.root = path.resolve(root);
17
+ this.sessionId = sessionId || crypto.randomUUID().replace(/-/g, "").slice(0, 12);
18
+ this.path = path.resolve(this.root, this.sessionId);
19
+ fs.mkdirSync(this.path, { recursive: true });
20
+ }
21
+
22
+ /** @returns {string} Current workspace directory */
23
+ get cwd() {
24
+ return this.path;
25
+ }
26
+
27
+ /**
28
+ * Resolve a user-supplied relative path against the workspace.
29
+ * Rejects absolute paths and any path that escapes the workspace via .. or symlinks.
30
+ * @param {string} userPath
31
+ * @returns {string}
32
+ */
33
+ resolvePath(userPath) {
34
+ if (path.isAbsolute(userPath)) {
35
+ throw new Error(`Absolute paths not allowed: ${userPath}`);
36
+ }
37
+ const resolved = path.resolve(this.path, userPath);
38
+ const base = path.resolve(this.path);
39
+ if (resolved !== base && !resolved.startsWith(base + path.sep)) {
40
+ throw new Error(`Path escapes workspace: ${userPath}`);
41
+ }
42
+ return resolved;
43
+ }
44
+
45
+ /**
46
+ * Rename the workspace folder. Returns the new sessionId.
47
+ * @param {string} newName
48
+ * @returns {string}
49
+ */
50
+ rename(newName) {
51
+ newName = newName.trim().replace(/ /g, "_").replace(/\//g, "_");
52
+ if (!newName) throw new Error("Name cannot be empty");
53
+ const newPath = path.join(this.root, newName);
54
+ if (fs.existsSync(newPath) && path.resolve(newPath) !== path.resolve(this.path)) {
55
+ throw new Error(`Session '${newName}' already exists`);
56
+ }
57
+ if (path.resolve(newPath) !== path.resolve(this.path)) {
58
+ fs.renameSync(this.path, newPath);
59
+ this.path = path.resolve(newPath);
60
+ this.sessionId = newName;
61
+ }
62
+ return this.sessionId;
63
+ }
64
+
65
+ /**
66
+ * List all workspace sessions with names.
67
+ * @param {string} root
68
+ * @returns {Array<{id: string}>}
69
+ */
70
+ static listSessions(root) {
71
+ root = path.resolve(root);
72
+ if (!fs.existsSync(root)) return [];
73
+ const sessions = [];
74
+ const entries = fs.readdirSync(root, { withFileTypes: true });
75
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
76
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
77
+ sessions.push({ id: entry.name });
78
+ }
79
+ }
80
+ return sessions;
81
+ }
82
+ }
@@ -0,0 +1,164 @@
1
+ import React, { useState, useEffect, useRef, useCallback } from "react";
2
+ import { Box, Text, useInput, useApp, useStdout } from "ink";
3
+
4
+ const h = React.createElement;
5
+
6
+ // --- Cooking spinner ---
7
+
8
+ const COOKING_WORDS = [
9
+ "Baking", "Blanching", "Brewing", "Caramelizing", "Cooking",
10
+ "Fermenting", "Flambéing", "Julienning", "Kneading", "Leavening",
11
+ "Marinating", "Proofing", "Sautéing", "Seasoning", "Simmering",
12
+ "Stewing", "Tempering", "Whisking", "Zesting", "Garnishing", "Drizzling",
13
+ ];
14
+
15
+ export function CookingSpinner() {
16
+ const [idx, setIdx] = useState(Math.floor(Math.random() * COOKING_WORDS.length));
17
+
18
+ useEffect(() => {
19
+ const timer = setInterval(() => setIdx((i) => (i + 1) % COOKING_WORDS.length), 2000);
20
+ return () => clearInterval(timer);
21
+ }, []);
22
+
23
+ return h(Box, null,
24
+ h(Text, { color: "yellow" }, " * "),
25
+ h(Text, { dimColor: true }, `${COOKING_WORDS[idx]}...`),
26
+ );
27
+ }
28
+
29
+ // --- Status bar ---
30
+
31
+ const LENAT_QUOTE = "Intelligence is ten million rules.";
32
+
33
+ export function StatusBar({ sessionId, phase }) {
34
+ return h(Box, { marginTop: 0 },
35
+ h(Text, { dimColor: true }, " ⏵⏵ KC Agent CLI "),
36
+ h(Text, { dimColor: true }, sessionId ? `[${sessionId}]` : ""),
37
+ phase ? h(Text, { color: "cyan" }, ` ${phase.toUpperCase()}`) : null,
38
+ h(Text, { color: "green" }, " ● "),
39
+ h(Text, { dimColor: true }, `· ${LENAT_QUOTE}`),
40
+ );
41
+ }
42
+
43
+ // --- Welcome banner ---
44
+
45
+ export function WelcomeBanner() {
46
+ return h(Box, { flexDirection: "column", marginBottom: 1 },
47
+ h(Text, null, ""),
48
+ h(Box, { justifyContent: "center" },
49
+ h(Text, { bold: true }, "KC AGENT CLI"),
50
+ h(Text, { dimColor: true }, " (beta)"),
51
+ ),
52
+ h(Text, null, ""),
53
+ h(Text, { dimColor: true }, " Hope you never know what KC was."),
54
+ h(Text, null, ""),
55
+ h(Text, { dimColor: true }, " Product of Memium"),
56
+ h(Text, { dimColor: true }, " kitchen-engineer42"),
57
+ );
58
+ }
59
+
60
+ // --- Tool block ---
61
+
62
+ export function ToolBlock({ name, input, output, isError, isRunning }) {
63
+ const borderColor = isRunning ? "yellow" : isError ? "red" : "green";
64
+
65
+ return h(Box, { flexDirection: "column", marginLeft: 2 },
66
+ h(Box, null,
67
+ h(Text, { color: borderColor }, "┃ "),
68
+ h(Text, { dimColor: true }, name),
69
+ input ? h(Text, { dimColor: true }, ` ${JSON.stringify(input)}`) : null,
70
+ ),
71
+ output ? h(Box, { flexDirection: "column" },
72
+ ...output.split("\n").slice(0, 20).map((line, i) =>
73
+ h(Box, { key: i },
74
+ h(Text, { color: borderColor }, "┃ "),
75
+ h(Text, null, line),
76
+ ),
77
+ ),
78
+ output.split("\n").length > 20
79
+ ? h(Box, null,
80
+ h(Text, { color: borderColor }, "┃ "),
81
+ h(Text, { dimColor: true }, `... ${output.split("\n").length - 20} more lines`),
82
+ )
83
+ : null,
84
+ ) : null,
85
+ );
86
+ }
87
+
88
+ // --- Message display ---
89
+
90
+ export function MessageBlock({ role, content, toolName, toolInput, toolOutput, toolIsError }) {
91
+ if (role === "user") {
92
+ return h(Box, null,
93
+ h(Text, { dimColor: true }, "❯ "),
94
+ h(Text, null, content),
95
+ );
96
+ }
97
+ if (role === "agent") {
98
+ return h(Box, null,
99
+ h(Text, null, content),
100
+ );
101
+ }
102
+ if (role === "tool") {
103
+ return h(ToolBlock, {
104
+ name: toolName,
105
+ input: toolInput,
106
+ output: toolOutput,
107
+ isError: toolIsError,
108
+ isRunning: false,
109
+ });
110
+ }
111
+ if (role === "system") {
112
+ return h(Box, null,
113
+ h(Text, { dimColor: true }, content),
114
+ );
115
+ }
116
+ return null;
117
+ }
118
+
119
+ // --- Horizontal rule ---
120
+
121
+ export function HRule() {
122
+ const { stdout } = useStdout();
123
+ const cols = stdout?.columns || 80;
124
+ return h(Text, { dimColor: true }, "─".repeat(cols));
125
+ }
126
+
127
+ // --- Messages list ---
128
+
129
+ export function MessagesList({ messages }) {
130
+ return h(Box, { flexDirection: "column" },
131
+ ...messages.map((msg, i) =>
132
+ h(MessageBlock, { key: i, ...msg }),
133
+ ),
134
+ );
135
+ }
136
+
137
+ // --- Input prompt ---
138
+
139
+ export function InputPrompt({ value, onChange, onSubmit, isActive }) {
140
+ useInput((input, key) => {
141
+ if (!isActive) return;
142
+
143
+ if (key.return) {
144
+ onSubmit(value);
145
+ return;
146
+ }
147
+ if (key.backspace || key.delete) {
148
+ onChange(value.slice(0, -1));
149
+ return;
150
+ }
151
+ // Skip control characters
152
+ if (key.ctrl || key.meta || key.escape) return;
153
+ // Append printable characters
154
+ if (input) {
155
+ onChange(value + input);
156
+ }
157
+ }, { isActive });
158
+
159
+ return h(Box, null,
160
+ h(Text, { dimColor: true }, "❯ "),
161
+ h(Text, null, value),
162
+ isActive ? h(Text, { color: "gray" }, "█") : null,
163
+ );
164
+ }