@akanjs/cli 3.1.0-rc.4 → 3.1.0-rc.5
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/.build-stamp +1 -1
- package/{CodeAgent-ymte1tv8.js → CodeAgent-rkjyxgec.js} +2 -2
- package/{CodeTui-4rhdm1qw.js → CodeTui-h7pyj6xa.js} +1 -1
- package/TunnelShare-661cxz5e.js +14 -0
- package/{agent.command-r5x1mch5.js → agent.command-3qxf4xcj.js} +5 -5
- package/akanCode.js +2 -2
- package/{application.command-3dkr7rdz.js → application.command-m1kcd7jf.js} +12 -11
- package/{applicationBuildRunner-h0j2swx1.js → applicationBuildRunner-dj0xkrep.js} +16 -9
- package/buildBatch.proc.js +7 -7
- package/{cloud.command-4kyxfw0y.js → cloud.command-3jzym8q7.js} +17 -16
- package/cloud.script-r4nskbc6.js +24 -0
- package/{code.command-sjvzt8fc.js → code.command-0ad6gw2a.js} +6 -6
- package/{context.command-j9frz0dz.js → context.command-0qx447mb.js} +17 -16
- package/context.runner-1vsjztjg.js +25 -0
- package/{dependencyScanner-06z2dmrr.js → dependencyScanner-3afj2vt1.js} +1 -1
- package/{guideline.command-1epw6hey.js → guideline.command-cv5bmrx2.js} +2 -2
- package/guidelines/conventions/conventions.instruction.md +450 -1084
- package/guidelines/cssRule/cssRule.instruction.md +16 -1
- package/guidelines/fieldRule/fieldRule.instruction.md +57 -0
- package/guidelines/framework/framework.instruction.md +4 -7
- package/guidelines/mcpRule/mcpRule.instruction.md +11 -8
- package/guidelines/modelConstant/modelConstant.instruction.md +2 -1
- package/guidelines/modelStore/modelStore.instruction.md +9 -0
- package/guidelines/pageRule/pageRule.instruction.md +80 -0
- package/guidelines/queryRule/queryRule.instruction.md +35 -8
- package/guidelines/runtimeRule/runtimeRule.instruction.md +89 -2
- package/guidelines/ssrRule/ssrRule.instruction.md +13 -0
- package/guidelines/transportRule/transportRule.instruction.md +76 -8
- package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +4 -4
- package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +49 -4
- package/incrementalBuilder.proc.js +4 -4
- package/{index-jw6st2rd.js → index-192ha4xh.js} +356 -307
- package/{index-1hkr7mrb.js → index-1p98exm2.js} +2 -2
- package/{index-8byzcvxp.js → index-1xj2t7cb.js} +7 -7
- package/{index-er3hekv9.js → index-20bxxyvn.js} +80 -80
- package/{index-xe3x2kw5.js → index-35hc95h5.js} +1 -1
- package/{index-p5nrc5he.js → index-4mhy6qme.js} +4 -4
- package/{index-0qc5nd61.js → index-5edzv8fn.js} +5 -5
- package/{index-k9rrten6.js → index-5nd1wtkv.js} +92 -94
- package/index-62md4dwy.js +260 -0
- package/{index-z3v7abrk.js → index-665zp9rs.js} +38 -15
- package/{index-1t2xw0p0.js → index-6xn2n442.js} +4 -4
- package/{index-jg3fj8h0.js → index-86jt2a0e.js} +1 -1
- package/{index-k33me5sp.js → index-8fp3b47h.js} +419 -418
- package/{index-shastb7m.js → index-9z426msb.js} +17 -12
- package/{index-gdybjw3x.js → index-awbdfcf2.js} +4 -4
- package/{index-3hx1z96c.js → index-bqbf654f.js} +1 -1
- package/{index-jppthsff.js → index-cpb3nvma.js} +57 -58
- package/{index-b60ccq2s.js → index-d47ypebv.js} +17 -15
- package/{index-gc05vqd2.js → index-d72hfmdm.js} +13 -13
- package/{index-akha197v.js → index-fmzcs8v6.js} +4 -4
- package/{index-28ftaz3d.js → index-fyq1z6kt.js} +29 -27
- package/{index-vwmv64ad.js → index-g1mbppa9.js} +238 -236
- package/{index-hedt951c.js → index-h47cf1gg.js} +7 -7
- package/{index-hfqs9jvc.js → index-j4mkhz93.js} +3 -3
- package/{index-08a5ga6n.js → index-jw1zm273.js} +28 -30
- package/{index-q958tqqg.js → index-m9jj544v.js} +1 -1
- package/{index-8935h3n1.js → index-mdjc93e1.js} +1 -1
- package/{index-2wyn17fa.js → index-pydbnfez.js} +2 -2
- package/{index-bjh4wbtk.js → index-qwa5k1j8.js} +1 -1
- package/{index-81a1ywp6.js → index-tamvyvj3.js} +3 -3
- package/{index-dez4t5sd.js → index-trpasv9q.js} +260 -259
- package/{index-2ahj896a.js → index-tvehn2sp.js} +1 -1
- package/{index-2k0t8p3p.js → index-va06nr10.js} +1086 -1075
- package/{index-9c3cd767.js → index-vk0pa0mn.js} +20 -21
- package/{index-qyaqzh8e.js → index-w44cy4vs.js} +2216 -2216
- package/index.js +22 -22
- package/{library.command-n58dgakb.js → library.command-erp2f56n.js} +3 -3
- package/{localRegistry.command-f2768sww.js → localRegistry.command-xxpqgxjw.js} +15 -14
- package/{module.command-xzjg5k7n.js → module.command-gs2r615e.js} +5 -5
- package/{package.command-24sh1j1q.js → package.command-7343f74r.js} +4 -4
- package/package.json +2 -2
- package/{page.command-fqnm88na.js → page.command-4h4a8pnh.js} +3 -3
- package/{pipeline.command-vn6taz75.js → pipeline.command-a2nrk26m.js} +3 -3
- package/{primitive.command-4j1g1a0d.js → primitive.command-w6t0asqz.js} +6 -6
- package/{projectTypecheck-3y7ayty0.js → projectTypecheck-f534enqj.js} +1 -1
- package/{quality.command-5hqmcdwt.js → quality.command-77x7vbbx.js} +4 -4
- package/{repair.command-rw5tk460.js → repair.command-y6sstev7.js} +5 -5
- package/{scalar.command-0jpap45m.js → scalar.command-20b8cy0k.js} +4 -4
- package/{subspace.command-y7n9sghk.js → subspace.command-92xkc0s0.js} +11 -10
- package/templates/lib/cnst.ts +0 -1
- package/templates/workspaceRoot/.gitignore.template +24 -53
- package/{tunnel.command-5gjwh1f8.js → tunnel.command-2kw8vt9g.js} +8 -7
- package/{workflow.command-3t22f7eb.js → workflow.command-2djwmpqf.js} +10 -10
- package/{workspace.command-cq5xg2jq.js → workspace.command-5se6spqb.js} +28 -27
- package/TunnelShare-p8ymkww8.js +0 -13
- package/cloud.script-jc3hc5d9.js +0 -23
- package/context.runner-v0def5my.js +0 -24
- package/index-n68jx79w.js +0 -120
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
akanCodePaths2,
|
|
5
5
|
McpServerConfig,
|
|
6
6
|
McpSignIn
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-20bxxyvn.js";
|
|
8
8
|
import {
|
|
9
9
|
stripAnsi
|
|
10
10
|
} from "./index-7md2g9vw.js";
|
|
@@ -15,136 +15,433 @@ import {
|
|
|
15
15
|
akanCodeModelWarnings2
|
|
16
16
|
} from "./index-c6eggbee.js";
|
|
17
17
|
|
|
18
|
-
// pkgs/@akanjs/devkit/codeAgent/agent/
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} from "@earendil-works/pi-coding-agent";
|
|
23
|
-
import {
|
|
24
|
-
codeAgentClip as codeAgentClip5,
|
|
25
|
-
codeAgentLabelChars as codeAgentLabelChars2,
|
|
26
|
-
codeAgentRenderAnswer,
|
|
27
|
-
codeAgentSessionName as codeAgentSessionName2
|
|
28
|
-
} from "akanjs/common";
|
|
18
|
+
// pkgs/@akanjs/devkit/codeAgent/agent/AkanCodeServices.ts
|
|
19
|
+
import { existsSync as existsSync4, mkdirSync, mkdtempSync } from "fs";
|
|
20
|
+
import { tmpdir } from "os";
|
|
21
|
+
import path4 from "path";
|
|
22
|
+
import { ModelRuntime, SessionManager, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
29
23
|
|
|
30
|
-
// pkgs/@akanjs/devkit/codeAgent/
|
|
31
|
-
import { existsSync,
|
|
24
|
+
// pkgs/@akanjs/devkit/codeAgent/tools/AkanEnvKeys.ts
|
|
25
|
+
import { existsSync, readFileSync } from "fs";
|
|
32
26
|
import path from "path";
|
|
33
27
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
paths,
|
|
47
|
-
apps: AkanEditScope2.#targets(paths, "apps"),
|
|
48
|
-
libs: AkanEditScope2.#targets(paths, "libs"),
|
|
49
|
-
needsSync: entries.some((entry) => /[ARD?]/.test(entry.code) && /^(apps|libs)\//.test(entry.file)),
|
|
50
|
-
touchesTsx: paths.some((file) => file.endsWith(".tsx"))
|
|
51
|
-
};
|
|
28
|
+
class AkanEnvKeys2 {
|
|
29
|
+
static async apply(runtime, workspaceRoot) {
|
|
30
|
+
const applied = [];
|
|
31
|
+
for (const [name, value] of AkanEnvKeys2.#entries(workspaceRoot)) {
|
|
32
|
+
const provider = AkanEnvKeys2.#providerOf(name);
|
|
33
|
+
if (!provider || !value)
|
|
34
|
+
continue;
|
|
35
|
+
await runtime.setRuntimeApiKey(provider, value);
|
|
36
|
+
applied.push(provider);
|
|
37
|
+
}
|
|
38
|
+
return applied;
|
|
52
39
|
}
|
|
53
|
-
static
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
40
|
+
static #entries(workspaceRoot) {
|
|
41
|
+
const fromProcess = Object.entries(process.env).filter(([, value]) => typeof value === "string");
|
|
42
|
+
const file = path.join(workspaceRoot, ".env");
|
|
43
|
+
if (!existsSync(file))
|
|
44
|
+
return fromProcess;
|
|
45
|
+
const fromFile = readFileSync(file, "utf8").split(`
|
|
46
|
+
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && line.includes("=")).map((line) => {
|
|
47
|
+
const index = line.indexOf("=");
|
|
48
|
+
return [
|
|
49
|
+
line.slice(0, index).trim(),
|
|
50
|
+
line.slice(index + 1).trim().replace(/^["']|["']$/g, "")
|
|
51
|
+
];
|
|
58
52
|
});
|
|
59
|
-
|
|
60
|
-
await proc.exited;
|
|
61
|
-
return new Set(text.split(`
|
|
62
|
-
`).filter((line) => line.trim().length > 3));
|
|
53
|
+
return [...fromFile, ...fromProcess];
|
|
63
54
|
}
|
|
64
|
-
static #
|
|
65
|
-
const
|
|
66
|
-
return [
|
|
55
|
+
static #providerOf(name) {
|
|
56
|
+
const match = /^([A-Z0-9_]+)_API_KEY$/.exec(name);
|
|
57
|
+
return match?.[1]?.toLowerCase().replace(/_/g, "-");
|
|
67
58
|
}
|
|
68
59
|
}
|
|
69
60
|
|
|
70
|
-
// pkgs/@akanjs/devkit/codeAgent/
|
|
71
|
-
|
|
72
|
-
|
|
61
|
+
// pkgs/@akanjs/devkit/codeAgent/agent/AkanContextFiles.ts
|
|
62
|
+
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2, statSync } from "fs";
|
|
63
|
+
import path2 from "path";
|
|
73
64
|
|
|
74
|
-
class
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
65
|
+
class AkanContextFiles {
|
|
66
|
+
static #frontmatter = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
67
|
+
static extend(base, options) {
|
|
68
|
+
const seen = new Set(base.map((file) => file.path));
|
|
69
|
+
const contents = new Set(base.map((file) => file.content.trim()));
|
|
70
|
+
const added = [];
|
|
71
|
+
for (const file of [...AkanContextFiles.#claude(base), ...AkanContextFiles.#cursor(options)]) {
|
|
72
|
+
if (seen.has(file.path) || contents.has(file.content.trim()) || !file.content.trim())
|
|
73
|
+
continue;
|
|
74
|
+
seen.add(file.path);
|
|
75
|
+
contents.add(file.content.trim());
|
|
76
|
+
added.push(file);
|
|
77
|
+
}
|
|
78
|
+
return [...base, ...added];
|
|
81
79
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
80
|
+
static #claude(base) {
|
|
81
|
+
const files = [];
|
|
82
|
+
for (const file of base) {
|
|
83
|
+
if (!/^AGENTS\.(md|MD)$/.test(path2.basename(file.path)))
|
|
84
|
+
continue;
|
|
85
|
+
for (const name of ["CLAUDE.md", "CLAUDE.MD"]) {
|
|
86
|
+
const candidate = path2.join(path2.dirname(file.path), name);
|
|
87
|
+
const content = AkanContextFiles.#read(candidate);
|
|
88
|
+
if (content !== undefined)
|
|
89
|
+
files.push({ path: candidate, content });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return files;
|
|
86
93
|
}
|
|
87
|
-
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
static #cursor(options) {
|
|
95
|
+
const files = [];
|
|
96
|
+
const roots = [...new Set([options.workspaceRoot, options.cwd])];
|
|
97
|
+
for (const root of roots) {
|
|
98
|
+
const legacy = AkanContextFiles.#read(path2.join(root, ".cursorrules"));
|
|
99
|
+
if (legacy !== undefined)
|
|
100
|
+
files.push({ path: path2.join(root, ".cursorrules"), content: legacy });
|
|
101
|
+
for (const file of AkanContextFiles.#mdcFiles(path2.join(root, ".cursor", "rules"))) {
|
|
102
|
+
const raw = AkanContextFiles.#read(file);
|
|
103
|
+
if (raw === undefined)
|
|
104
|
+
continue;
|
|
105
|
+
const body = AkanContextFiles.#applied(raw);
|
|
106
|
+
if (body !== undefined)
|
|
107
|
+
files.push({ path: file, content: body });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return files;
|
|
111
|
+
}
|
|
112
|
+
static #applied(raw) {
|
|
113
|
+
const match = AkanContextFiles.#frontmatter.exec(raw);
|
|
114
|
+
if (!match)
|
|
115
|
+
return raw;
|
|
116
|
+
if (!/^\s*alwaysApply\s*:\s*true\s*$/m.test(match[1] ?? ""))
|
|
92
117
|
return;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
\`\`\``);
|
|
118
|
+
return raw.slice(match[0].length);
|
|
119
|
+
}
|
|
120
|
+
static #mdcFiles(dir) {
|
|
121
|
+
if (!existsSync2(dir))
|
|
122
|
+
return [];
|
|
123
|
+
try {
|
|
124
|
+
return readdirSync(dir, { recursive: true, withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".mdc")).map((entry) => path2.join(entry.parentPath ?? dir, entry.name)).sort();
|
|
125
|
+
} catch {
|
|
126
|
+
return [];
|
|
103
127
|
}
|
|
104
|
-
|
|
128
|
+
}
|
|
129
|
+
static #read(file) {
|
|
130
|
+
try {
|
|
131
|
+
if (!existsSync2(file) || !statSync(file).isFile())
|
|
132
|
+
return;
|
|
133
|
+
return readFileSync2(file, "utf8").replace(/^\uFEFF/, "");
|
|
134
|
+
} catch {
|
|
105
135
|
return;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
`Full log: ${running.map((app) => path.relative(this.#options.workspaceRoot, this.#pathOf(app))).join(", ")}`
|
|
110
|
-
].join(`
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
111
139
|
|
|
140
|
+
// pkgs/@akanjs/devkit/codeAgent/agent/akanSystemPrompt.ts
|
|
141
|
+
var akanSystemPrompt2 = (profile) => {
|
|
142
|
+
const lines = [
|
|
143
|
+
"You are the akan coding agent, running inside an Akan.js workspace.",
|
|
144
|
+
"",
|
|
145
|
+
"- Prefer an akan workflow to a direct source edit. `list_workflows` / `explain_workflow` / `plan_workflow`, then `apply_workflow({ planPath })`. Editing source by hand is the fallback, not the default.",
|
|
146
|
+
"- Read context with `inspect_akan_context` before reading source bodies; it answers most structural questions without spending the file.",
|
|
147
|
+
"- Fetch the guideline for an area before a deep pass on it: `get_guideline`.",
|
|
148
|
+
"- A question about `akan code` itself \u2014 its slash commands, MCP servers, sessions, models, keys, or where its files live \u2014 is answered by the `akan-code` skill. Read it rather than describing the tool from memory.",
|
|
149
|
+
"- Never hand-edit a generated file (`lib/cnst.ts`, `db.ts`, `st.ts`, any `*/index.ts` barrel). Fix the owning source and run `repair_generated` or `akan sync`."
|
|
150
|
+
];
|
|
151
|
+
if (profile.tools.builtin.includes("write"))
|
|
152
|
+
lines.push("- After editing, call `akan_verify`. It runs sync, lint, typecheck and the SSR check over what the working tree changed. A turn that edits and does not verify is not finished.");
|
|
153
|
+
else
|
|
154
|
+
lines.push("- This session is read-only: there is no write, edit or bash tool. Report what should change instead of changing it.");
|
|
155
|
+
if (profile.tools.subagent)
|
|
156
|
+
lines.push('- Hand a self-contained search or read to `task` \u2014 `type: "explore"` reads and reports, `type: "code"` makes the change. Only its answer comes back, so give it everything it needs and ask for exactly what you want returned. Do it yourself when the reading is short or when you need the files in front of you.');
|
|
157
|
+
if (profile.ui.canPrompt)
|
|
158
|
+
lines.push("- Ask with `ask_user` when two readings of the request would lead to materially different work, and offer the options you can name. Do not ask for a choice that has an obvious default, and do not ask permission for work you were already asked to do \u2014 make the call, say the assumption, and carry on.");
|
|
159
|
+
if (profile.approval !== "never")
|
|
160
|
+
lines.push(`- Some tool calls need the user's approval (policy: ${profile.approval}). A refusal is an answer; do not retry it a different way.`);
|
|
161
|
+
lines.push("- Secrets are refused at the tool boundary. If you need a value from `.env`, ask the user for it rather than looking for another way to read the file.", "", "## How to answer", "", "- Answer in as few words as carry the answer. A short question gets one sentence with no heading.", "- Do not open with what you are about to do, and do not close with a summary of what you did. The tool calls are already on the user's screen.", "- Skip headings, bold labels and bullet lists unless the answer is genuinely a list. Prose is shorter.", "- State a file as `path:line`. Do not paste a file back that the user can open.", "- Say what you could not do, and why, in one line. Do not offer alternatives nobody asked for.");
|
|
162
|
+
return lines.join(`
|
|
112
163
|
`);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// pkgs/@akanjs/devkit/codeAgent/agent/CodeSessionIndex.ts
|
|
167
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
|
|
168
|
+
import path3 from "path";
|
|
169
|
+
|
|
170
|
+
// pkgs/@akanjs/devkit/codeAgent/agent/jsonLines.ts
|
|
171
|
+
import { closeSync, openSync, readFileSync as readFileSync3, readSync, statSync as statSync2 } from "fs";
|
|
172
|
+
var readHead = (file, maxBytes) => {
|
|
173
|
+
const fd = openSync(file, "r");
|
|
174
|
+
try {
|
|
175
|
+
const head = Buffer.alloc(maxBytes);
|
|
176
|
+
return head.subarray(0, readSync(fd, head, 0, maxBytes, 0)).toString("utf8");
|
|
177
|
+
} finally {
|
|
178
|
+
closeSync(fd);
|
|
113
179
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
if (size <= from)
|
|
119
|
-
return [];
|
|
120
|
-
const text = await Bun.file(file).slice(from, size).text();
|
|
121
|
-
this.#marks.set(app, size);
|
|
180
|
+
};
|
|
181
|
+
var readJsonLines = (file, maxBytes) => {
|
|
182
|
+
try {
|
|
183
|
+
const text = maxBytes !== undefined && statSync2(file).size > maxBytes ? readHead(file, maxBytes) : readFileSync3(file, "utf8");
|
|
122
184
|
return text.split(`
|
|
123
|
-
`).filter((line) =>
|
|
185
|
+
`).filter((line) => !!line.trim());
|
|
186
|
+
} catch {
|
|
187
|
+
return [];
|
|
124
188
|
}
|
|
125
|
-
|
|
126
|
-
|
|
189
|
+
};
|
|
190
|
+
var parseJsonLine = (line) => {
|
|
191
|
+
try {
|
|
192
|
+
return JSON.parse(line);
|
|
193
|
+
} catch {
|
|
194
|
+
return;
|
|
127
195
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// pkgs/@akanjs/devkit/codeAgent/agent/CodeSessionIndex.ts
|
|
199
|
+
class CodeSessionIndex2 {
|
|
200
|
+
static readLimit = 512 * 1024;
|
|
201
|
+
static list(dir, limit = 30) {
|
|
202
|
+
if (!existsSync3(dir))
|
|
203
|
+
return [];
|
|
204
|
+
const files = readdirSync2(dir).filter((entry) => entry.endsWith(".jsonl")).map((entry) => path3.join(dir, entry)).map((file) => ({ file, updatedAt: CodeSessionIndex2.#mtime(file) })).sort((left, right) => right.updatedAt - left.updatedAt).slice(0, limit);
|
|
205
|
+
return files.map((entry) => CodeSessionIndex2.#read(entry.file, entry.updatedAt)).filter((entry) => !!entry);
|
|
206
|
+
}
|
|
207
|
+
static fileOf(dir, id) {
|
|
208
|
+
if (!existsSync3(dir))
|
|
131
209
|
return;
|
|
132
|
-
const
|
|
133
|
-
return
|
|
210
|
+
const match = readdirSync2(dir).find((entry) => entry.endsWith(`${id}.jsonl`) || entry === `${id}.jsonl`);
|
|
211
|
+
return match ? path3.join(dir, match) : undefined;
|
|
134
212
|
}
|
|
135
|
-
static #
|
|
136
|
-
|
|
213
|
+
static #mtime(file) {
|
|
214
|
+
try {
|
|
215
|
+
return statSync3(file).mtimeMs;
|
|
216
|
+
} catch {
|
|
217
|
+
return 0;
|
|
218
|
+
}
|
|
137
219
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
220
|
+
static #read(file, updatedAt) {
|
|
221
|
+
const lines = readJsonLines(file, CodeSessionIndex2.readLimit);
|
|
222
|
+
if (!lines.length)
|
|
223
|
+
return;
|
|
224
|
+
const entry = { id: "", file, name: undefined, opening: "", updatedAt, turns: 0 };
|
|
225
|
+
for (const line of lines) {
|
|
226
|
+
const record = parseJsonLine(line);
|
|
227
|
+
if (!record)
|
|
228
|
+
continue;
|
|
229
|
+
if (record.type === "session" && typeof record.id === "string")
|
|
230
|
+
entry.id = record.id;
|
|
231
|
+
if (record.type === "session_info" && typeof record.name === "string")
|
|
232
|
+
entry.name = record.name;
|
|
233
|
+
if (record.type !== "message")
|
|
234
|
+
continue;
|
|
235
|
+
const message = record.message;
|
|
236
|
+
if (message?.role !== "user")
|
|
237
|
+
continue;
|
|
238
|
+
entry.turns += 1;
|
|
239
|
+
if (!entry.opening)
|
|
240
|
+
entry.opening = CodeSessionIndex2.#text(message.content);
|
|
241
|
+
}
|
|
242
|
+
return entry.id ? entry : undefined;
|
|
243
|
+
}
|
|
244
|
+
static #text(content) {
|
|
245
|
+
const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => typeof part === "object" && part && ("text" in part) ? String(part.text) : "").join(" ") : "";
|
|
246
|
+
return text.replace(/\s+/g, " ").trim();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// pkgs/@akanjs/devkit/codeAgent/agent/AkanCodeServices.ts
|
|
251
|
+
class AkanCodeServices2 {
|
|
252
|
+
static settings() {
|
|
253
|
+
return SettingsManager.inMemory({
|
|
254
|
+
enableInstallTelemetry: false,
|
|
255
|
+
enableAnalytics: false,
|
|
256
|
+
quietStartup: true,
|
|
257
|
+
compaction: { reserveTokens: 32768 }
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
static async runtime(workspaceRoot, proxy = null) {
|
|
261
|
+
mkdirSync(akanCodePaths2.globalDir(), { recursive: true, mode: 448 });
|
|
262
|
+
const runtime = await ModelRuntime.create({
|
|
263
|
+
authPath: proxy ? path4.join(mkdtempSync(path4.join(tmpdir(), "akan-code-proxy-")), "auth.json") : akanCodePaths2.authFile(),
|
|
264
|
+
modelsPath: akanCodePaths2.modelsFile()
|
|
265
|
+
});
|
|
266
|
+
if (proxy)
|
|
267
|
+
await proxy.apply(runtime);
|
|
268
|
+
else
|
|
269
|
+
await AkanEnvKeys2.apply(runtime, workspaceRoot);
|
|
270
|
+
return runtime;
|
|
271
|
+
}
|
|
272
|
+
static sessions(workspaceRoot, cwd, profile, resume) {
|
|
273
|
+
if (profile.session.store === "memory")
|
|
274
|
+
return SessionManager.inMemory(cwd);
|
|
275
|
+
const dir = akanCodePaths2.sessionsDir(workspaceRoot);
|
|
276
|
+
mkdirSync(dir, { recursive: true });
|
|
277
|
+
const manager = SessionManager.create(cwd, dir);
|
|
278
|
+
if (!resume)
|
|
279
|
+
return manager;
|
|
280
|
+
const file = CodeSessionIndex2.fileOf(dir, resume);
|
|
281
|
+
if (!file)
|
|
282
|
+
throw new Error(`No stored session ${resume} in ${dir}`);
|
|
283
|
+
manager.setSessionFile(file);
|
|
284
|
+
return manager;
|
|
285
|
+
}
|
|
286
|
+
static #skillPaths(profile, workspaceRoot) {
|
|
287
|
+
if (!profile.context.skills)
|
|
288
|
+
return [];
|
|
289
|
+
const dirs = [
|
|
290
|
+
akanCodePaths2.builtinSkillsDir(),
|
|
291
|
+
akanCodePaths2.globalSkillsDir(),
|
|
292
|
+
akanCodePaths2.skillsDir(workspaceRoot)
|
|
293
|
+
];
|
|
294
|
+
return dirs.filter((dir) => !!dir && existsSync4(dir));
|
|
295
|
+
}
|
|
296
|
+
static resourceOptions(options) {
|
|
297
|
+
const present = (dir) => existsSync4(dir) ? [dir] : [];
|
|
298
|
+
return {
|
|
299
|
+
noExtensions: true,
|
|
300
|
+
noSkills: true,
|
|
301
|
+
noPromptTemplates: true,
|
|
302
|
+
noThemes: true,
|
|
303
|
+
noContextFiles: !options.profile.context.projectFiles,
|
|
304
|
+
agentsFilesOverride: ({ agentsFiles }) => ({
|
|
305
|
+
agentsFiles: options.profile.context.projectFiles ? AkanContextFiles.extend(agentsFiles, { workspaceRoot: options.workspaceRoot, cwd: options.cwd }) : agentsFiles
|
|
306
|
+
}),
|
|
307
|
+
appendSystemPrompt: [akanSystemPrompt2(options.profile)],
|
|
308
|
+
additionalExtensionPaths: present(akanCodePaths2.extensionsDir(options.workspaceRoot)),
|
|
309
|
+
additionalSkillPaths: AkanCodeServices2.#skillPaths(options.profile, options.workspaceRoot),
|
|
310
|
+
extensionFactories: options.extensions
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// pkgs/@akanjs/devkit/codeAgent/agent/CodeAgent.ts
|
|
316
|
+
import {
|
|
317
|
+
createAgentSession,
|
|
318
|
+
DefaultResourceLoader
|
|
319
|
+
} from "@earendil-works/pi-coding-agent";
|
|
320
|
+
import {
|
|
321
|
+
codeAgentClip as codeAgentClip5,
|
|
322
|
+
codeAgentLabelChars as codeAgentLabelChars2,
|
|
323
|
+
codeAgentRenderAnswer,
|
|
324
|
+
codeAgentSessionName as codeAgentSessionName2
|
|
325
|
+
} from "akanjs/common";
|
|
326
|
+
|
|
327
|
+
// pkgs/@akanjs/devkit/codeAgent/feedback/DevLogFeedback.ts
|
|
328
|
+
import { existsSync as existsSync5, statSync as statSync4 } from "fs";
|
|
329
|
+
import path5 from "path";
|
|
330
|
+
|
|
331
|
+
// pkgs/@akanjs/devkit/codeAgent/tools/AkanEditScope.ts
|
|
332
|
+
var empty = { paths: [], apps: [], libs: [], needsSync: false, touchesTsx: false };
|
|
333
|
+
|
|
334
|
+
class AkanEditScope2 {
|
|
335
|
+
static async since(cwd, baseline) {
|
|
336
|
+
const current = await AkanEditScope2.baseline(cwd);
|
|
337
|
+
const changed = [...current].filter((line) => !baseline.has(line));
|
|
338
|
+
if (!changed.length)
|
|
339
|
+
return empty;
|
|
340
|
+
const entries = changed.map((line) => ({ code: line.slice(0, 2), file: line.slice(3).split(" -> ").at(-1) ?? "" }));
|
|
341
|
+
const paths = entries.map((entry) => entry.file).filter(Boolean);
|
|
342
|
+
return {
|
|
343
|
+
paths,
|
|
344
|
+
apps: AkanEditScope2.#targets(paths, "apps"),
|
|
345
|
+
libs: AkanEditScope2.#targets(paths, "libs"),
|
|
346
|
+
needsSync: entries.some((entry) => /[ARD?]/.test(entry.code) && /^(apps|libs)\//.test(entry.file)),
|
|
347
|
+
touchesTsx: paths.some((file) => file.endsWith(".tsx"))
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
static async baseline(cwd) {
|
|
351
|
+
const proc = Bun.spawn(["git", "status", "--porcelain=v1", "--untracked-files=all"], {
|
|
352
|
+
cwd,
|
|
353
|
+
stdout: "pipe",
|
|
354
|
+
stderr: "ignore"
|
|
355
|
+
});
|
|
356
|
+
const text = await new Response(proc.stdout).text();
|
|
357
|
+
await proc.exited;
|
|
358
|
+
return new Set(text.split(`
|
|
359
|
+
`).filter((line) => line.trim().length > 3));
|
|
360
|
+
}
|
|
361
|
+
static #targets(paths, root) {
|
|
362
|
+
const names = paths.filter((file) => file.startsWith(`${root}/`)).map((file) => file.split("/")[1] ?? "").filter(Boolean);
|
|
363
|
+
return [...new Set(names)];
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// pkgs/@akanjs/devkit/codeAgent/feedback/DevLogFeedback.ts
|
|
368
|
+
var errorPattern = /\bERROR\b|\bFATAL\b|\[stderr\]|error:|Error:|\u2716|Unhandled|Cannot find|is not a function/;
|
|
369
|
+
var noisePattern = /DEBUG|\bWARN\b|MCP catalogue|rate limit/;
|
|
370
|
+
|
|
371
|
+
class DevLogFeedback2 {
|
|
372
|
+
key = "dev-server";
|
|
373
|
+
#options;
|
|
374
|
+
#marks = new Map;
|
|
375
|
+
#baseline = new Set;
|
|
376
|
+
constructor(options) {
|
|
377
|
+
this.#options = { graceMs: 1500, maxLines: 30, ...options };
|
|
378
|
+
}
|
|
379
|
+
async begin() {
|
|
380
|
+
for (const app of this.#options.apps)
|
|
381
|
+
this.#marks.set(app, DevLogFeedback2.#sizeOf(this.#pathOf(app)));
|
|
382
|
+
this.#baseline = await AkanEditScope2.baseline(this.#options.cwd);
|
|
383
|
+
}
|
|
384
|
+
async observe() {
|
|
385
|
+
const running = this.#options.apps.filter((app) => existsSync5(this.#pathOf(app)));
|
|
386
|
+
if (!running.length)
|
|
387
|
+
return;
|
|
388
|
+
if (!(await AkanEditScope2.since(this.#options.cwd, this.#baseline)).paths.length)
|
|
389
|
+
return;
|
|
390
|
+
await Bun.sleep(this.#options.graceMs);
|
|
391
|
+
const reports = [];
|
|
392
|
+
for (const app of running) {
|
|
393
|
+
const lines = await this.#linesSince(app);
|
|
394
|
+
if (lines.length)
|
|
395
|
+
reports.push(`**${app}** dev server:
|
|
396
|
+
\`\`\`
|
|
397
|
+
${lines.join(`
|
|
398
|
+
`)}
|
|
399
|
+
\`\`\``);
|
|
400
|
+
}
|
|
401
|
+
if (!reports.length)
|
|
402
|
+
return;
|
|
403
|
+
return [
|
|
404
|
+
"The dev server logged errors after your edits:",
|
|
405
|
+
...reports,
|
|
406
|
+
`Full log: ${running.map((app) => path5.relative(this.#options.workspaceRoot, this.#pathOf(app))).join(", ")}`
|
|
407
|
+
].join(`
|
|
408
|
+
|
|
409
|
+
`);
|
|
410
|
+
}
|
|
411
|
+
async#linesSince(app) {
|
|
412
|
+
const file = this.#pathOf(app);
|
|
413
|
+
const from = this.#marks.get(app) ?? 0;
|
|
414
|
+
const size = DevLogFeedback2.#sizeOf(file);
|
|
415
|
+
if (size <= from)
|
|
416
|
+
return [];
|
|
417
|
+
const text = await Bun.file(file).slice(from, size).text();
|
|
418
|
+
this.#marks.set(app, size);
|
|
419
|
+
return text.split(`
|
|
420
|
+
`).filter((line) => errorPattern.test(line) && !noisePattern.test(line)).slice(-this.#options.maxLines);
|
|
421
|
+
}
|
|
422
|
+
#pathOf(app) {
|
|
423
|
+
return path5.join(this.#options.workspaceRoot, "local", "apps", app, "runtime", "dev.log");
|
|
424
|
+
}
|
|
425
|
+
static async previewUrl(workspaceRoot, app) {
|
|
426
|
+
const file = path5.join(workspaceRoot, "local", "apps", app, "runtime", "dev.log");
|
|
427
|
+
if (!existsSync5(file))
|
|
428
|
+
return;
|
|
429
|
+
const matches = [...(await Bun.file(file).text()).matchAll(/ready \(pid=\d+\) \u2014 (https?:\/\/\S+)/g)];
|
|
430
|
+
return matches.at(-1)?.[1];
|
|
431
|
+
}
|
|
432
|
+
static #sizeOf(file) {
|
|
433
|
+
return existsSync5(file) ? statSync4(file).size : 0;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// pkgs/@akanjs/devkit/codeAgent/feedback/PreviewChrome.ts
|
|
438
|
+
import { existsSync as existsSync6 } from "fs";
|
|
439
|
+
import { homedir } from "os";
|
|
440
|
+
import path6 from "path";
|
|
441
|
+
|
|
442
|
+
class PreviewChrome2 {
|
|
443
|
+
static pathEnvKey = "BUN_CHROME_PATH";
|
|
444
|
+
static backendEnvKey = "AKAN_CODE_WEBVIEW_BACKEND";
|
|
148
445
|
static executables = [
|
|
149
446
|
"chromium",
|
|
150
447
|
"chromium-browser",
|
|
@@ -178,10 +475,10 @@ class PreviewChrome2 {
|
|
|
178
475
|
}
|
|
179
476
|
if (process.platform !== "darwin")
|
|
180
477
|
return;
|
|
181
|
-
for (const root of ["/Applications",
|
|
478
|
+
for (const root of ["/Applications", path6.join(homedir(), "Applications")])
|
|
182
479
|
for (const bundle of PreviewChrome2.appBundles) {
|
|
183
|
-
const candidate =
|
|
184
|
-
if (
|
|
480
|
+
const candidate = path6.join(root, bundle);
|
|
481
|
+
if (existsSync6(candidate))
|
|
185
482
|
return candidate;
|
|
186
483
|
}
|
|
187
484
|
return;
|
|
@@ -388,8 +685,8 @@ class TurnFeedback2 {
|
|
|
388
685
|
}
|
|
389
686
|
|
|
390
687
|
// pkgs/@akanjs/devkit/codeAgent/tools/AkanCli.ts
|
|
391
|
-
import { existsSync as
|
|
392
|
-
import
|
|
688
|
+
import { existsSync as existsSync7 } from "fs";
|
|
689
|
+
import path7 from "path";
|
|
393
690
|
class AkanCli2 {
|
|
394
691
|
#cwd;
|
|
395
692
|
#entry;
|
|
@@ -416,10 +713,10 @@ class AkanCli2 {
|
|
|
416
713
|
}
|
|
417
714
|
static #resolveEntry() {
|
|
418
715
|
const candidates = [
|
|
419
|
-
|
|
420
|
-
|
|
716
|
+
path7.join(import.meta.dir, "index.js"),
|
|
717
|
+
path7.join(import.meta.dir, "..", "..", "..", "cli", "index.ts")
|
|
421
718
|
];
|
|
422
|
-
return candidates.find((candidate) =>
|
|
719
|
+
return candidates.find((candidate) => existsSync7(candidate)) ?? Bun.main;
|
|
423
720
|
}
|
|
424
721
|
}
|
|
425
722
|
|
|
@@ -511,7 +808,7 @@ class AkanToolPack2 {
|
|
|
511
808
|
return { name: "akan-tools", factory: async (pi) => await this.#register(pi) };
|
|
512
809
|
}
|
|
513
810
|
async names() {
|
|
514
|
-
const { ContextRunner } = await import("./context.runner-
|
|
811
|
+
const { ContextRunner } = await import("./context.runner-1vsjztjg.js");
|
|
515
812
|
return [
|
|
516
813
|
...new ContextRunner().listMcpTools(this.#options.mode).map((tool) => tool.name),
|
|
517
814
|
AkanToolPack2.verifyToolName
|
|
@@ -519,7 +816,7 @@ class AkanToolPack2 {
|
|
|
519
816
|
}
|
|
520
817
|
static verifyToolName = "akan_verify";
|
|
521
818
|
async#register(pi) {
|
|
522
|
-
const { ContextRunner } = await import("./context.runner-
|
|
819
|
+
const { ContextRunner } = await import("./context.runner-1vsjztjg.js");
|
|
523
820
|
const runner = new ContextRunner;
|
|
524
821
|
const guidelineNames = await AkanToolPack2.#guidelineNames();
|
|
525
822
|
for (const tool of runner.listMcpTools(this.#options.mode, { guidelineNames })) {
|
|
@@ -1027,7 +1324,7 @@ class McpToolPack2 {
|
|
|
1027
1324
|
}
|
|
1028
1325
|
|
|
1029
1326
|
// pkgs/@akanjs/devkit/codeAgent/tools/SessionToolPack.ts
|
|
1030
|
-
import { SessionManager } from "@earendil-works/pi-coding-agent";
|
|
1327
|
+
import { SessionManager as SessionManager2 } from "@earendil-works/pi-coding-agent";
|
|
1031
1328
|
import { codeAgentClip as codeAgentClip2, codeAgentOutputChars as codeAgentOutputChars2 } from "akanjs/common";
|
|
1032
1329
|
import { Type as Type5 } from "typebox";
|
|
1033
1330
|
var snippetChars = 240;
|
|
@@ -1093,7 +1390,7 @@ class SessionToolPack2 {
|
|
|
1093
1390
|
return codeAgentClip2(session.allMessagesText, codeAgentOutputChars2 * 2);
|
|
1094
1391
|
}
|
|
1095
1392
|
async#list() {
|
|
1096
|
-
return await
|
|
1393
|
+
return await SessionManager2.list(this.#options.cwd, akanCodePaths2.sessionsDir(this.#options.workspaceRoot));
|
|
1097
1394
|
}
|
|
1098
1395
|
}
|
|
1099
1396
|
|
|
@@ -1220,7 +1517,7 @@ ${codeAgentClip3(text, codeAgentOutputChars3)}` }
|
|
|
1220
1517
|
return;
|
|
1221
1518
|
}
|
|
1222
1519
|
static #readable(html) {
|
|
1223
|
-
return html.replace(/<script[\s\S]*?<\/script
|
|
1520
|
+
return html.replace(/<script[\s\S]*?<\/script[^<>]*>/gi, " ").replace(/<style[\s\S]*?<\/style[^<>]*>/gi, " ").replace(/<[^<>]+>/g, " ").replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/&/g, "&").replace(/[ \t]+/g, " ").replace(/\n{3,}/g, `
|
|
1224
1521
|
|
|
1225
1522
|
`).trim();
|
|
1226
1523
|
}
|
|
@@ -1314,303 +1611,6 @@ class AkanCodePlugins2 {
|
|
|
1314
1611
|
}
|
|
1315
1612
|
}
|
|
1316
1613
|
|
|
1317
|
-
// pkgs/@akanjs/devkit/codeAgent/agent/AkanCodeServices.ts
|
|
1318
|
-
import { existsSync as existsSync7, mkdirSync, mkdtempSync } from "fs";
|
|
1319
|
-
import { tmpdir } from "os";
|
|
1320
|
-
import path7 from "path";
|
|
1321
|
-
import { ModelRuntime, SessionManager as SessionManager2, SettingsManager } from "@earendil-works/pi-coding-agent";
|
|
1322
|
-
|
|
1323
|
-
// pkgs/@akanjs/devkit/codeAgent/tools/AkanEnvKeys.ts
|
|
1324
|
-
import { existsSync as existsSync4, readFileSync } from "fs";
|
|
1325
|
-
import path4 from "path";
|
|
1326
|
-
|
|
1327
|
-
class AkanEnvKeys2 {
|
|
1328
|
-
static async apply(runtime, workspaceRoot) {
|
|
1329
|
-
const applied = [];
|
|
1330
|
-
for (const [name, value] of AkanEnvKeys2.#entries(workspaceRoot)) {
|
|
1331
|
-
const provider = AkanEnvKeys2.#providerOf(name);
|
|
1332
|
-
if (!provider || !value)
|
|
1333
|
-
continue;
|
|
1334
|
-
await runtime.setRuntimeApiKey(provider, value);
|
|
1335
|
-
applied.push(provider);
|
|
1336
|
-
}
|
|
1337
|
-
return applied;
|
|
1338
|
-
}
|
|
1339
|
-
static #entries(workspaceRoot) {
|
|
1340
|
-
const fromProcess = Object.entries(process.env).filter(([, value]) => typeof value === "string");
|
|
1341
|
-
const file = path4.join(workspaceRoot, ".env");
|
|
1342
|
-
if (!existsSync4(file))
|
|
1343
|
-
return fromProcess;
|
|
1344
|
-
const fromFile = readFileSync(file, "utf8").split(`
|
|
1345
|
-
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && line.includes("=")).map((line) => {
|
|
1346
|
-
const index = line.indexOf("=");
|
|
1347
|
-
return [
|
|
1348
|
-
line.slice(0, index).trim(),
|
|
1349
|
-
line.slice(index + 1).trim().replace(/^["']|["']$/g, "")
|
|
1350
|
-
];
|
|
1351
|
-
});
|
|
1352
|
-
return [...fromFile, ...fromProcess];
|
|
1353
|
-
}
|
|
1354
|
-
static #providerOf(name) {
|
|
1355
|
-
const match = /^([A-Z0-9_]+)_API_KEY$/.exec(name);
|
|
1356
|
-
return match?.[1]?.toLowerCase().replace(/_/g, "-");
|
|
1357
|
-
}
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
|
-
// pkgs/@akanjs/devkit/codeAgent/agent/AkanContextFiles.ts
|
|
1361
|
-
import { existsSync as existsSync5, readdirSync, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
|
|
1362
|
-
import path5 from "path";
|
|
1363
|
-
|
|
1364
|
-
class AkanContextFiles {
|
|
1365
|
-
static #frontmatter = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
1366
|
-
static extend(base, options) {
|
|
1367
|
-
const seen = new Set(base.map((file) => file.path));
|
|
1368
|
-
const contents = new Set(base.map((file) => file.content.trim()));
|
|
1369
|
-
const added = [];
|
|
1370
|
-
for (const file of [...AkanContextFiles.#claude(base), ...AkanContextFiles.#cursor(options)]) {
|
|
1371
|
-
if (seen.has(file.path) || contents.has(file.content.trim()) || !file.content.trim())
|
|
1372
|
-
continue;
|
|
1373
|
-
seen.add(file.path);
|
|
1374
|
-
contents.add(file.content.trim());
|
|
1375
|
-
added.push(file);
|
|
1376
|
-
}
|
|
1377
|
-
return [...base, ...added];
|
|
1378
|
-
}
|
|
1379
|
-
static #claude(base) {
|
|
1380
|
-
const files = [];
|
|
1381
|
-
for (const file of base) {
|
|
1382
|
-
if (!/^AGENTS\.(md|MD)$/.test(path5.basename(file.path)))
|
|
1383
|
-
continue;
|
|
1384
|
-
for (const name of ["CLAUDE.md", "CLAUDE.MD"]) {
|
|
1385
|
-
const candidate = path5.join(path5.dirname(file.path), name);
|
|
1386
|
-
const content = AkanContextFiles.#read(candidate);
|
|
1387
|
-
if (content !== undefined)
|
|
1388
|
-
files.push({ path: candidate, content });
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
return files;
|
|
1392
|
-
}
|
|
1393
|
-
static #cursor(options) {
|
|
1394
|
-
const files = [];
|
|
1395
|
-
const roots = [...new Set([options.workspaceRoot, options.cwd])];
|
|
1396
|
-
for (const root of roots) {
|
|
1397
|
-
const legacy = AkanContextFiles.#read(path5.join(root, ".cursorrules"));
|
|
1398
|
-
if (legacy !== undefined)
|
|
1399
|
-
files.push({ path: path5.join(root, ".cursorrules"), content: legacy });
|
|
1400
|
-
for (const file of AkanContextFiles.#mdcFiles(path5.join(root, ".cursor", "rules"))) {
|
|
1401
|
-
const raw = AkanContextFiles.#read(file);
|
|
1402
|
-
if (raw === undefined)
|
|
1403
|
-
continue;
|
|
1404
|
-
const body = AkanContextFiles.#applied(raw);
|
|
1405
|
-
if (body !== undefined)
|
|
1406
|
-
files.push({ path: file, content: body });
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
return files;
|
|
1410
|
-
}
|
|
1411
|
-
static #applied(raw) {
|
|
1412
|
-
const match = AkanContextFiles.#frontmatter.exec(raw);
|
|
1413
|
-
if (!match)
|
|
1414
|
-
return raw;
|
|
1415
|
-
if (!/^\s*alwaysApply\s*:\s*true\s*$/m.test(match[1] ?? ""))
|
|
1416
|
-
return;
|
|
1417
|
-
return raw.slice(match[0].length);
|
|
1418
|
-
}
|
|
1419
|
-
static #mdcFiles(dir) {
|
|
1420
|
-
if (!existsSync5(dir))
|
|
1421
|
-
return [];
|
|
1422
|
-
try {
|
|
1423
|
-
return readdirSync(dir, { recursive: true, withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".mdc")).map((entry) => path5.join(entry.parentPath ?? dir, entry.name)).sort();
|
|
1424
|
-
} catch {
|
|
1425
|
-
return [];
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
static #read(file) {
|
|
1429
|
-
try {
|
|
1430
|
-
if (!existsSync5(file) || !statSync2(file).isFile())
|
|
1431
|
-
return;
|
|
1432
|
-
return readFileSync2(file, "utf8").replace(/^\uFEFF/, "");
|
|
1433
|
-
} catch {
|
|
1434
|
-
return;
|
|
1435
|
-
}
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
// pkgs/@akanjs/devkit/codeAgent/agent/akanSystemPrompt.ts
|
|
1440
|
-
var akanSystemPrompt2 = (profile) => {
|
|
1441
|
-
const lines = [
|
|
1442
|
-
"You are the akan coding agent, running inside an Akan.js workspace.",
|
|
1443
|
-
"",
|
|
1444
|
-
"- Prefer an akan workflow to a direct source edit. `list_workflows` / `explain_workflow` / `plan_workflow`, then `apply_workflow({ planPath })`. Editing source by hand is the fallback, not the default.",
|
|
1445
|
-
"- Read context with `inspect_akan_context` before reading source bodies; it answers most structural questions without spending the file.",
|
|
1446
|
-
"- Fetch the guideline for an area before a deep pass on it: `get_guideline`.",
|
|
1447
|
-
"- A question about `akan code` itself \u2014 its slash commands, MCP servers, sessions, models, keys, or where its files live \u2014 is answered by the `akan-code` skill. Read it rather than describing the tool from memory.",
|
|
1448
|
-
"- Never hand-edit a generated file (`lib/cnst.ts`, `db.ts`, `st.ts`, any `*/index.ts` barrel). Fix the owning source and run `repair_generated` or `akan sync`."
|
|
1449
|
-
];
|
|
1450
|
-
if (profile.tools.builtin.includes("write"))
|
|
1451
|
-
lines.push("- After editing, call `akan_verify`. It runs sync, lint, typecheck and the SSR check over what the working tree changed. A turn that edits and does not verify is not finished.");
|
|
1452
|
-
else
|
|
1453
|
-
lines.push("- This session is read-only: there is no write, edit or bash tool. Report what should change instead of changing it.");
|
|
1454
|
-
if (profile.tools.subagent)
|
|
1455
|
-
lines.push('- Hand a self-contained search or read to `task` \u2014 `type: "explore"` reads and reports, `type: "code"` makes the change. Only its answer comes back, so give it everything it needs and ask for exactly what you want returned. Do it yourself when the reading is short or when you need the files in front of you.');
|
|
1456
|
-
if (profile.ui.canPrompt)
|
|
1457
|
-
lines.push("- Ask with `ask_user` when two readings of the request would lead to materially different work, and offer the options you can name. Do not ask for a choice that has an obvious default, and do not ask permission for work you were already asked to do \u2014 make the call, say the assumption, and carry on.");
|
|
1458
|
-
if (profile.approval !== "never")
|
|
1459
|
-
lines.push(`- Some tool calls need the user's approval (policy: ${profile.approval}). A refusal is an answer; do not retry it a different way.`);
|
|
1460
|
-
lines.push("- Secrets are refused at the tool boundary. If you need a value from `.env`, ask the user for it rather than looking for another way to read the file.", "", "## How to answer", "", "- Answer in as few words as carry the answer. A short question gets one sentence with no heading.", "- Do not open with what you are about to do, and do not close with a summary of what you did. The tool calls are already on the user's screen.", "- Skip headings, bold labels and bullet lists unless the answer is genuinely a list. Prose is shorter.", "- State a file as `path:line`. Do not paste a file back that the user can open.", "- Say what you could not do, and why, in one line. Do not offer alternatives nobody asked for.");
|
|
1461
|
-
return lines.join(`
|
|
1462
|
-
`);
|
|
1463
|
-
};
|
|
1464
|
-
|
|
1465
|
-
// pkgs/@akanjs/devkit/codeAgent/agent/CodeSessionIndex.ts
|
|
1466
|
-
import { existsSync as existsSync6, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
|
|
1467
|
-
import path6 from "path";
|
|
1468
|
-
|
|
1469
|
-
// pkgs/@akanjs/devkit/codeAgent/agent/jsonLines.ts
|
|
1470
|
-
import { closeSync, openSync, readFileSync as readFileSync3, readSync, statSync as statSync3 } from "fs";
|
|
1471
|
-
var readHead = (file, maxBytes) => {
|
|
1472
|
-
const fd = openSync(file, "r");
|
|
1473
|
-
try {
|
|
1474
|
-
const head = Buffer.alloc(maxBytes);
|
|
1475
|
-
return head.subarray(0, readSync(fd, head, 0, maxBytes, 0)).toString("utf8");
|
|
1476
|
-
} finally {
|
|
1477
|
-
closeSync(fd);
|
|
1478
|
-
}
|
|
1479
|
-
};
|
|
1480
|
-
var readJsonLines = (file, maxBytes) => {
|
|
1481
|
-
try {
|
|
1482
|
-
const text = maxBytes !== undefined && statSync3(file).size > maxBytes ? readHead(file, maxBytes) : readFileSync3(file, "utf8");
|
|
1483
|
-
return text.split(`
|
|
1484
|
-
`).filter((line) => !!line.trim());
|
|
1485
|
-
} catch {
|
|
1486
|
-
return [];
|
|
1487
|
-
}
|
|
1488
|
-
};
|
|
1489
|
-
var parseJsonLine = (line) => {
|
|
1490
|
-
try {
|
|
1491
|
-
return JSON.parse(line);
|
|
1492
|
-
} catch {
|
|
1493
|
-
return;
|
|
1494
|
-
}
|
|
1495
|
-
};
|
|
1496
|
-
|
|
1497
|
-
// pkgs/@akanjs/devkit/codeAgent/agent/CodeSessionIndex.ts
|
|
1498
|
-
class CodeSessionIndex2 {
|
|
1499
|
-
static readLimit = 512 * 1024;
|
|
1500
|
-
static list(dir, limit = 30) {
|
|
1501
|
-
if (!existsSync6(dir))
|
|
1502
|
-
return [];
|
|
1503
|
-
const files = readdirSync2(dir).filter((entry) => entry.endsWith(".jsonl")).map((entry) => path6.join(dir, entry)).map((file) => ({ file, updatedAt: CodeSessionIndex2.#mtime(file) })).sort((left, right) => right.updatedAt - left.updatedAt).slice(0, limit);
|
|
1504
|
-
return files.map((entry) => CodeSessionIndex2.#read(entry.file, entry.updatedAt)).filter((entry) => !!entry);
|
|
1505
|
-
}
|
|
1506
|
-
static fileOf(dir, id) {
|
|
1507
|
-
if (!existsSync6(dir))
|
|
1508
|
-
return;
|
|
1509
|
-
const match = readdirSync2(dir).find((entry) => entry.endsWith(`${id}.jsonl`) || entry === `${id}.jsonl`);
|
|
1510
|
-
return match ? path6.join(dir, match) : undefined;
|
|
1511
|
-
}
|
|
1512
|
-
static #mtime(file) {
|
|
1513
|
-
try {
|
|
1514
|
-
return statSync4(file).mtimeMs;
|
|
1515
|
-
} catch {
|
|
1516
|
-
return 0;
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
static #read(file, updatedAt) {
|
|
1520
|
-
const lines = readJsonLines(file, CodeSessionIndex2.readLimit);
|
|
1521
|
-
if (!lines.length)
|
|
1522
|
-
return;
|
|
1523
|
-
const entry = { id: "", file, name: undefined, opening: "", updatedAt, turns: 0 };
|
|
1524
|
-
for (const line of lines) {
|
|
1525
|
-
const record = parseJsonLine(line);
|
|
1526
|
-
if (!record)
|
|
1527
|
-
continue;
|
|
1528
|
-
if (record.type === "session" && typeof record.id === "string")
|
|
1529
|
-
entry.id = record.id;
|
|
1530
|
-
if (record.type === "session_info" && typeof record.name === "string")
|
|
1531
|
-
entry.name = record.name;
|
|
1532
|
-
if (record.type !== "message")
|
|
1533
|
-
continue;
|
|
1534
|
-
const message = record.message;
|
|
1535
|
-
if (message?.role !== "user")
|
|
1536
|
-
continue;
|
|
1537
|
-
entry.turns += 1;
|
|
1538
|
-
if (!entry.opening)
|
|
1539
|
-
entry.opening = CodeSessionIndex2.#text(message.content);
|
|
1540
|
-
}
|
|
1541
|
-
return entry.id ? entry : undefined;
|
|
1542
|
-
}
|
|
1543
|
-
static #text(content) {
|
|
1544
|
-
const text = typeof content === "string" ? content : Array.isArray(content) ? content.map((part) => typeof part === "object" && part && ("text" in part) ? String(part.text) : "").join(" ") : "";
|
|
1545
|
-
return text.replace(/\s+/g, " ").trim();
|
|
1546
|
-
}
|
|
1547
|
-
}
|
|
1548
|
-
|
|
1549
|
-
// pkgs/@akanjs/devkit/codeAgent/agent/AkanCodeServices.ts
|
|
1550
|
-
class AkanCodeServices2 {
|
|
1551
|
-
static settings() {
|
|
1552
|
-
return SettingsManager.inMemory({
|
|
1553
|
-
enableInstallTelemetry: false,
|
|
1554
|
-
enableAnalytics: false,
|
|
1555
|
-
quietStartup: true,
|
|
1556
|
-
compaction: { reserveTokens: 32768 }
|
|
1557
|
-
});
|
|
1558
|
-
}
|
|
1559
|
-
static async runtime(workspaceRoot, proxy = null) {
|
|
1560
|
-
mkdirSync(akanCodePaths2.globalDir(), { recursive: true, mode: 448 });
|
|
1561
|
-
const runtime = await ModelRuntime.create({
|
|
1562
|
-
authPath: proxy ? path7.join(mkdtempSync(path7.join(tmpdir(), "akan-code-proxy-")), "auth.json") : akanCodePaths2.authFile(),
|
|
1563
|
-
modelsPath: akanCodePaths2.modelsFile()
|
|
1564
|
-
});
|
|
1565
|
-
if (proxy)
|
|
1566
|
-
await proxy.apply(runtime);
|
|
1567
|
-
else
|
|
1568
|
-
await AkanEnvKeys2.apply(runtime, workspaceRoot);
|
|
1569
|
-
return runtime;
|
|
1570
|
-
}
|
|
1571
|
-
static sessions(workspaceRoot, cwd, profile, resume) {
|
|
1572
|
-
if (profile.session.store === "memory")
|
|
1573
|
-
return SessionManager2.inMemory(cwd);
|
|
1574
|
-
const dir = akanCodePaths2.sessionsDir(workspaceRoot);
|
|
1575
|
-
mkdirSync(dir, { recursive: true });
|
|
1576
|
-
const manager = SessionManager2.create(cwd, dir);
|
|
1577
|
-
if (!resume)
|
|
1578
|
-
return manager;
|
|
1579
|
-
const file = CodeSessionIndex2.fileOf(dir, resume);
|
|
1580
|
-
if (!file)
|
|
1581
|
-
throw new Error(`No stored session ${resume} in ${dir}`);
|
|
1582
|
-
manager.setSessionFile(file);
|
|
1583
|
-
return manager;
|
|
1584
|
-
}
|
|
1585
|
-
static #skillPaths(profile, workspaceRoot) {
|
|
1586
|
-
if (!profile.context.skills)
|
|
1587
|
-
return [];
|
|
1588
|
-
const dirs = [
|
|
1589
|
-
akanCodePaths2.builtinSkillsDir(),
|
|
1590
|
-
akanCodePaths2.globalSkillsDir(),
|
|
1591
|
-
akanCodePaths2.skillsDir(workspaceRoot)
|
|
1592
|
-
];
|
|
1593
|
-
return dirs.filter((dir) => !!dir && existsSync7(dir));
|
|
1594
|
-
}
|
|
1595
|
-
static resourceOptions(options) {
|
|
1596
|
-
const present = (dir) => existsSync7(dir) ? [dir] : [];
|
|
1597
|
-
return {
|
|
1598
|
-
noExtensions: true,
|
|
1599
|
-
noSkills: true,
|
|
1600
|
-
noPromptTemplates: true,
|
|
1601
|
-
noThemes: true,
|
|
1602
|
-
noContextFiles: !options.profile.context.projectFiles,
|
|
1603
|
-
agentsFilesOverride: ({ agentsFiles }) => ({
|
|
1604
|
-
agentsFiles: options.profile.context.projectFiles ? AkanContextFiles.extend(agentsFiles, { workspaceRoot: options.workspaceRoot, cwd: options.cwd }) : agentsFiles
|
|
1605
|
-
}),
|
|
1606
|
-
appendSystemPrompt: [akanSystemPrompt2(options.profile)],
|
|
1607
|
-
additionalExtensionPaths: present(akanCodePaths2.extensionsDir(options.workspaceRoot)),
|
|
1608
|
-
additionalSkillPaths: AkanCodeServices2.#skillPaths(options.profile, options.workspaceRoot),
|
|
1609
|
-
extensionFactories: options.extensions
|
|
1610
|
-
};
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
|
|
1614
1614
|
// pkgs/@akanjs/devkit/codeAgent/agent/CodeAgentAsks.ts
|
|
1615
1615
|
class CodeAgentAsks2 {
|
|
1616
1616
|
#question;
|
|
@@ -1964,6 +1964,7 @@ class CodeAgentGate2 {
|
|
|
1964
1964
|
|
|
1965
1965
|
// pkgs/@akanjs/devkit/codeAgent/agent/CodeAgentProxy.ts
|
|
1966
1966
|
import { readFile } from "fs/promises";
|
|
1967
|
+
import { trimTrailingSlashes } from "akanjs/common";
|
|
1967
1968
|
class CodeAgentProxy {
|
|
1968
1969
|
static placeholderKey = "akan-code-proxy";
|
|
1969
1970
|
baseUrl;
|
|
@@ -1971,7 +1972,7 @@ class CodeAgentProxy {
|
|
|
1971
1972
|
#tokenFile;
|
|
1972
1973
|
#token;
|
|
1973
1974
|
constructor({ baseUrl, providers, tokenFile, token }) {
|
|
1974
|
-
this.baseUrl = baseUrl
|
|
1975
|
+
this.baseUrl = trimTrailingSlashes(baseUrl);
|
|
1975
1976
|
this.providers = providers;
|
|
1976
1977
|
this.#tokenFile = tokenFile;
|
|
1977
1978
|
this.#token = token;
|