larkway 0.3.10 → 0.3.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 +1 -1
- package/README.zh.md +1 -1
- package/dist/cli/index.js +96 -2
- package/dist/main.js +150 -10
- package/package.json +16 -14
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
You @ the bot in a Feishu thread. It runs on your machine — reading your real codebase, executing commands, opening MRs — and posts the result back. You define what the agent knows and what it can do. Larkway just carries the messages.
|
|
10
10
|
|
|
11
|
-
**Current release: v0.3.
|
|
11
|
+
**Current release: v0.3.12**
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
package/README.zh.md
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -107597,6 +107597,10 @@ function stringField(obj, key) {
|
|
|
107597
107597
|
const value = obj[key];
|
|
107598
107598
|
return typeof value === "string" ? value : void 0;
|
|
107599
107599
|
}
|
|
107600
|
+
function nonEmptyStringField(obj, key) {
|
|
107601
|
+
const value = stringField(obj, key);
|
|
107602
|
+
return value && value.length > 0 ? value : void 0;
|
|
107603
|
+
}
|
|
107600
107604
|
function parseLarkCliMessages(stdout2) {
|
|
107601
107605
|
const parsed = JSON.parse(stdout2);
|
|
107602
107606
|
if (Array.isArray(parsed)) return parsed;
|
|
@@ -107610,6 +107614,25 @@ function parseLarkCliMessages(stdout2) {
|
|
|
107610
107614
|
}
|
|
107611
107615
|
return null;
|
|
107612
107616
|
}
|
|
107617
|
+
function expandMessagesWithThreadReplies(messages) {
|
|
107618
|
+
const expanded = [];
|
|
107619
|
+
for (const raw of messages) {
|
|
107620
|
+
expanded.push(raw);
|
|
107621
|
+
if (!raw || typeof raw !== "object") continue;
|
|
107622
|
+
const parent = raw;
|
|
107623
|
+
const parentRootId = nonEmptyStringField(parent, "root_id") ?? nonEmptyStringField(parent, "message_id");
|
|
107624
|
+
const replies = arrayField(parent, "thread_replies") ?? [];
|
|
107625
|
+
for (const replyRaw of replies) {
|
|
107626
|
+
if (!replyRaw || typeof replyRaw !== "object") continue;
|
|
107627
|
+
const reply = replyRaw;
|
|
107628
|
+
expanded.push({
|
|
107629
|
+
...reply,
|
|
107630
|
+
root_id: nonEmptyStringField(reply, "root_id") ?? parentRootId
|
|
107631
|
+
});
|
|
107632
|
+
}
|
|
107633
|
+
}
|
|
107634
|
+
return expanded;
|
|
107635
|
+
}
|
|
107613
107636
|
function cardActionChoice(value) {
|
|
107614
107637
|
if (value && typeof value === "object") {
|
|
107615
107638
|
const choice = value["larkway_choice"];
|
|
@@ -107942,8 +107965,9 @@ var init_channelClient = __esm({
|
|
|
107942
107965
|
log(`gap-fill: failed to parse lark-cli output for chat ${chatId}`);
|
|
107943
107966
|
continue;
|
|
107944
107967
|
}
|
|
107945
|
-
|
|
107946
|
-
|
|
107968
|
+
const messagesWithReplies = expandMessagesWithThreadReplies(messages);
|
|
107969
|
+
totalFetched += messagesWithReplies.length;
|
|
107970
|
+
for (const raw of messagesWithReplies) {
|
|
107947
107971
|
if (this.closed) break;
|
|
107948
107972
|
const m = raw;
|
|
107949
107973
|
const messageId = m["message_id"];
|
|
@@ -120457,6 +120481,7 @@ function renderAgentsMd(input) {
|
|
|
120457
120481
|
"",
|
|
120458
120482
|
"## Workspace Contract",
|
|
120459
120483
|
"",
|
|
120484
|
+
"- \u5F00\u573A\u4E0D\u53EF\u8DF3\u8FC7:\u56DE\u5E94 owner \u524D,\u5148 Read `memory/index.md`,\u5E76\u6309\u76F8\u5173\u6027 Read \u76F8\u5173 category \u6587\u4EF6,\u518D\u5F00\u59CB\u5E72\u6D3B(\u9632\u6B62\u65B0 session \u5931\u5FC6)\u3002",
|
|
120460
120485
|
"- Larkway is a thin Feishu bridge. It passes scene/context pointers; you decide what to inspect and what work to do.",
|
|
120461
120486
|
"- Each Feishu topic is one task session under `sessions/<thread_id>/`.",
|
|
120462
120487
|
"- Keep durable notes, repo clones, session summaries, and permission decisions inside this workspace.",
|
|
@@ -120776,6 +120801,75 @@ async function ensureAgentWorkspace(input) {
|
|
|
120776
120801
|
path7.join(input.workspacePath, "permissions-granted.md"),
|
|
120777
120802
|
renderPermissionsGranted(input)
|
|
120778
120803
|
);
|
|
120804
|
+
await ensureMemoryScaffold(input.workspacePath);
|
|
120805
|
+
}
|
|
120806
|
+
var MEMORY_CATEGORY_FILES = [
|
|
120807
|
+
{
|
|
120808
|
+
name: "preferences.md",
|
|
120809
|
+
title: "Owner Preferences",
|
|
120810
|
+
purpose: "owner \u6216\u56E2\u961F\u957F\u671F\u504F\u597D(\u6C47\u62A5\u683C\u5F0F\u3001\u9ED8\u8BA4\u8BED\u8A00\u3001\u9A8C\u8BC1\u504F\u597D\u7B49)\u3002"
|
|
120811
|
+
},
|
|
120812
|
+
{
|
|
120813
|
+
name: "reusable-knowledge.md",
|
|
120814
|
+
title: "Reusable Knowledge",
|
|
120815
|
+
purpose: "\u591A\u4E2A session \u540E\u6C89\u6DC0\u51FA\u7684\u53EF\u590D\u7528\u7ECF\u9A8C\u3001\u5E38\u89C1\u5751\u3001\u65B9\u6848\u5224\u65AD\u3002"
|
|
120816
|
+
},
|
|
120817
|
+
{
|
|
120818
|
+
name: "workflows.md",
|
|
120819
|
+
title: "Workflows",
|
|
120820
|
+
purpose: "\u8FD9\u4E2A Agent \u81EA\u5DF1\u7684\u957F\u671F\u5DE5\u4F5C\u65B9\u5F0F(\u9879\u76EE repo \u7684\u5DE5\u7A0B\u89C4\u8303\u4ECD\u5199\u8FDB\u9879\u76EE repo)\u3002"
|
|
120821
|
+
},
|
|
120822
|
+
{
|
|
120823
|
+
name: "decisions.md",
|
|
120824
|
+
title: "Decisions",
|
|
120825
|
+
purpose: "\u957F\u671F\u51B3\u7B56\u8BB0\u5F55,\u4F8B\u5982\u4E3A\u4EC0\u4E48\u67D0\u6743\u9650\u5FC5\u987B\u4EBA\u5DE5\u786E\u8BA4\u3002"
|
|
120826
|
+
},
|
|
120827
|
+
{
|
|
120828
|
+
name: "assets.md",
|
|
120829
|
+
title: "Assets",
|
|
120830
|
+
purpose: "\u957F\u671F\u56FE\u7247/\u622A\u56FE/\u9644\u4EF6\u7684\u7D22\u5F15,\u53EA\u8BB0\u5F15\u7528\u548C\u7528\u9014,\u4E0D\u5185\u8054\u5927\u6587\u4EF6(\u5B9E\u4F53\u653E assets/)\u3002"
|
|
120831
|
+
}
|
|
120832
|
+
];
|
|
120833
|
+
function renderMemoryIndex() {
|
|
120834
|
+
return [
|
|
120835
|
+
"# Memory Index",
|
|
120836
|
+
"",
|
|
120837
|
+
"\u8FD9\u662F\u8DE8 session \u957F\u671F\u8BB0\u5FC6\u3002\u65B0 session \u8D77\u624B\u5148\u8BFB\u8FD9\u91CC,\u628A\u8FC7\u5F80\u79EF\u7D2F\u62C9\u8D77\u6765\u3002",
|
|
120838
|
+
"Larkway \u53EA\u521D\u59CB\u5316\u7A7A\u5BB9\u5668;\u63D0\u70BC\u3001\u5206\u7C7B\u3001\u5199\u5165\u6B63\u6587\u90FD\u7531\u4F60(Agent)\u5728 owner \u786E\u8BA4\u540E\u5B8C\u6210\u3002",
|
|
120839
|
+
"",
|
|
120840
|
+
"## \u5206\u7C7B\u6587\u4EF6",
|
|
120841
|
+
"",
|
|
120842
|
+
...MEMORY_CATEGORY_FILES.map((f) => `- \`${f.name}\` \u2014 ${f.purpose}`),
|
|
120843
|
+
"",
|
|
120844
|
+
"## \u7EF4\u62A4\u89C4\u5219",
|
|
120845
|
+
"",
|
|
120846
|
+
"- \u70ED\u8DEF\u5F84(\u6BCF\u8F6E)\u53EA\u505A\u52A0\u6CD5:\u628A\u5019\u9009\u5199\u8FDB session \u7684 `memory-candidates.md`,\u6216\u5F80 category \u6587\u4EF6\u8FFD\u52A0\u65B0\u6761\u76EE;\u4E0D\u5728\u70ED\u8DEF\u5F84\u6539\u5199/\u5220\u9664\u5DF2\u6709\u6761\u76EE\u3002",
|
|
120847
|
+
"- \u6539\u5199\u3001\u5220\u9664\u3001\u89E3\u51B3\u51B2\u7A81 \u2192 \u63A8\u8FDF\u5230 owner \u663E\u5F0F\u8BF4\u300C\u6574\u7406\u8BB0\u5FC6\u300D\u65F6\u79BB\u7EBF\u505A\u3002UPDATE/DELETE \u7684\u65E7\u6761\u76EE\u79FB `archive/`(\u6CE8\u4E00\u53E5\u539F\u56E0+\u5BF9\u5E94 commit),\u4E0D\u624B\u5199 superseded \u6233,\u4E0D\u7269\u7406\u5220;archive/ \u7684\u957F\u671F\u6E05\u7406\u4EA4\u7ED9 git history\u3002",
|
|
120848
|
+
"- \u88C1\u51B3\u4EE5 source \u4F18\u5148:user \u4EB2\u53E3\u8BF4\u7684 >> agent \u63A8\u65AD;\u51B2\u7A81\u65F6\u4FDD\u7559\u65E7\u7684 user \u6761\u76EE,\u628A\u65B0\u63A8\u65AD\u964D\u7EA7\u4E3A candidate\u3002\u540C source \u5185 recency \u4EE5 git \u5386\u53F2\u4E3A\u51C6,\u4E0D\u624B\u5199\u65E5\u671F\u6233\u3002",
|
|
120849
|
+
"- \u53EA\u6709\u8DE8 session \u8FD8\u4F1A\u7528\u5230\u7684\u624D\u8FDB\u8FD9\u91CC(\u5355\u6B21\u4EFB\u52A1\u7559\u5728 session summary)\u3002\u672C\u6587\u4EF6\u4FDD\u6301\u7B80\u77ED(\u2264~50 \u884C),\u4E0D\u653E changelog/\u7EDF\u8BA1\u3002",
|
|
120850
|
+
""
|
|
120851
|
+
].join("\n");
|
|
120852
|
+
}
|
|
120853
|
+
function renderMemoryCategorySkeleton(title) {
|
|
120854
|
+
return [
|
|
120855
|
+
`# ${title}`,
|
|
120856
|
+
"",
|
|
120857
|
+
"\u5199\u524D\u5148\u8BFB\u672C\u6587\u4EF6;\u6709\u76F8\u540C/\u76F8\u5173\u6761\u76EE\u5C31 NOOP \u4E0D\u91CD\u590D\u5199(\u6539\u5199\u3001\u5220\u9664\u3001\u79FB archive \u7559\u5230 owner \u8BF4\u300C\u6574\u7406\u8BB0\u5FC6\u300D\u65F6\u7684\u79BB\u7EBF\u6B65\u9AA4);\u4FE1\u53F7\u4E0D\u8DE8 session \u590D\u7528\u5C31\u4E0D\u5199\u3002",
|
|
120858
|
+
""
|
|
120859
|
+
].join("\n");
|
|
120860
|
+
}
|
|
120861
|
+
async function ensureMemoryScaffold(workspacePath) {
|
|
120862
|
+
const memoryDir = path7.join(workspacePath, "memory");
|
|
120863
|
+
await fs.mkdir(memoryDir, { recursive: true });
|
|
120864
|
+
await fs.mkdir(path7.join(memoryDir, "assets"), { recursive: true });
|
|
120865
|
+
await fs.mkdir(path7.join(memoryDir, "archive"), { recursive: true });
|
|
120866
|
+
await writeIfMissing(path7.join(memoryDir, "index.md"), renderMemoryIndex());
|
|
120867
|
+
for (const file of MEMORY_CATEGORY_FILES) {
|
|
120868
|
+
await writeIfMissing(
|
|
120869
|
+
path7.join(memoryDir, file.name),
|
|
120870
|
+
renderMemoryCategorySkeleton(file.title)
|
|
120871
|
+
);
|
|
120872
|
+
}
|
|
120779
120873
|
}
|
|
120780
120874
|
|
|
120781
120875
|
// src/cli/commands/init.ts
|
package/dist/main.js
CHANGED
|
@@ -111823,6 +111823,10 @@ function stringField(obj, key) {
|
|
|
111823
111823
|
const value = obj[key];
|
|
111824
111824
|
return typeof value === "string" ? value : void 0;
|
|
111825
111825
|
}
|
|
111826
|
+
function nonEmptyStringField(obj, key) {
|
|
111827
|
+
const value = stringField(obj, key);
|
|
111828
|
+
return value && value.length > 0 ? value : void 0;
|
|
111829
|
+
}
|
|
111826
111830
|
function parseLarkCliMessages(stdout) {
|
|
111827
111831
|
const parsed = JSON.parse(stdout);
|
|
111828
111832
|
if (Array.isArray(parsed)) return parsed;
|
|
@@ -111836,6 +111840,25 @@ function parseLarkCliMessages(stdout) {
|
|
|
111836
111840
|
}
|
|
111837
111841
|
return null;
|
|
111838
111842
|
}
|
|
111843
|
+
function expandMessagesWithThreadReplies(messages) {
|
|
111844
|
+
const expanded = [];
|
|
111845
|
+
for (const raw of messages) {
|
|
111846
|
+
expanded.push(raw);
|
|
111847
|
+
if (!raw || typeof raw !== "object") continue;
|
|
111848
|
+
const parent = raw;
|
|
111849
|
+
const parentRootId = nonEmptyStringField(parent, "root_id") ?? nonEmptyStringField(parent, "message_id");
|
|
111850
|
+
const replies = arrayField(parent, "thread_replies") ?? [];
|
|
111851
|
+
for (const replyRaw of replies) {
|
|
111852
|
+
if (!replyRaw || typeof replyRaw !== "object") continue;
|
|
111853
|
+
const reply = replyRaw;
|
|
111854
|
+
expanded.push({
|
|
111855
|
+
...reply,
|
|
111856
|
+
root_id: nonEmptyStringField(reply, "root_id") ?? parentRootId
|
|
111857
|
+
});
|
|
111858
|
+
}
|
|
111859
|
+
}
|
|
111860
|
+
return expanded;
|
|
111861
|
+
}
|
|
111839
111862
|
function cardActionChoice(value) {
|
|
111840
111863
|
if (value && typeof value === "object") {
|
|
111841
111864
|
const choice = value["larkway_choice"];
|
|
@@ -112154,8 +112177,9 @@ var ChannelClient = class {
|
|
|
112154
112177
|
log(`gap-fill: failed to parse lark-cli output for chat ${chatId}`);
|
|
112155
112178
|
continue;
|
|
112156
112179
|
}
|
|
112157
|
-
|
|
112158
|
-
|
|
112180
|
+
const messagesWithReplies = expandMessagesWithThreadReplies(messages);
|
|
112181
|
+
totalFetched += messagesWithReplies.length;
|
|
112182
|
+
for (const raw of messagesWithReplies) {
|
|
112159
112183
|
if (this.closed) break;
|
|
112160
112184
|
const m = raw;
|
|
112161
112185
|
const messageId = m["message_id"];
|
|
@@ -113246,6 +113270,9 @@ function senderOpenIdOf(senderId) {
|
|
|
113246
113270
|
return "";
|
|
113247
113271
|
}
|
|
113248
113272
|
|
|
113273
|
+
// src/claude/prompt.ts
|
|
113274
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
113275
|
+
|
|
113249
113276
|
// src/agent/triggerFacts.ts
|
|
113250
113277
|
function stringField2(value) {
|
|
113251
113278
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
@@ -113282,6 +113309,23 @@ function deriveTriggerFacts(parsed, isNewThread, larkCliProfile) {
|
|
|
113282
113309
|
}
|
|
113283
113310
|
|
|
113284
113311
|
// src/claude/prompt.ts
|
|
113312
|
+
var MEMORY_CATEGORY_FILE_NAMES = [
|
|
113313
|
+
"preferences.md",
|
|
113314
|
+
"reusable-knowledge.md",
|
|
113315
|
+
"workflows.md",
|
|
113316
|
+
"decisions.md",
|
|
113317
|
+
"assets.md"
|
|
113318
|
+
];
|
|
113319
|
+
var MEMORY_FILE_LINE_LIMIT = 200;
|
|
113320
|
+
function statMemoryLines(filePath) {
|
|
113321
|
+
try {
|
|
113322
|
+
const text = readFileSync2(filePath, "utf8");
|
|
113323
|
+
if (text.length === 0) return 0;
|
|
113324
|
+
return (text.match(/\n/g)?.length ?? 0) + (text.endsWith("\n") ? 0 : 1);
|
|
113325
|
+
} catch {
|
|
113326
|
+
return 0;
|
|
113327
|
+
}
|
|
113328
|
+
}
|
|
113285
113329
|
function csv(items) {
|
|
113286
113330
|
return items.length > 0 ? items.join(",") : "(none)";
|
|
113287
113331
|
}
|
|
@@ -113403,6 +113447,8 @@ function renderWorkspaceBlock(primarySlug, primaryCachePath, defaultBranch, extr
|
|
|
113403
113447
|
}
|
|
113404
113448
|
function renderAgentWorkspaceBlock(conventions, extraRepos) {
|
|
113405
113449
|
const summaryFilePath = conventions.workspaceSessionPath ? `${conventions.workspaceSessionPath}/summary.md` : void 0;
|
|
113450
|
+
const memoryDir = conventions.agentWorkspacePath ? `${conventions.agentWorkspacePath}/memory` : void 0;
|
|
113451
|
+
const memoryIndex = memoryDir ? `${memoryDir}/index.md` : void 0;
|
|
113406
113452
|
const lines = [
|
|
113407
113453
|
"<agent-workspace>",
|
|
113408
113454
|
"Larkway \u662F thin bridge:\u5B83\u53EA\u628A\u98DE\u4E66\u89E6\u53D1\u573A\u666F\u548C\u672C\u5730\u8DEF\u5F84\u6307\u9488\u4EA4\u7ED9\u4F60,\u4E0D\u66FF\u4F60\u7F16\u6392\u4EFB\u52A1\u3002",
|
|
@@ -113411,12 +113457,29 @@ function renderAgentWorkspaceBlock(conventions, extraRepos) {
|
|
|
113411
113457
|
`- summary_file_path: ${summaryFilePath ?? "(topic_session_path)/summary.md"}`,
|
|
113412
113458
|
`- state_file_path: ${conventions.stateFilePath}`,
|
|
113413
113459
|
`- workspace_repos_dir: ${conventions.workspaceReposPath}`,
|
|
113460
|
+
`- memory_dir: ${memoryDir ?? "(agent_workspace_path)/memory"}`,
|
|
113461
|
+
`- memory_index: ${memoryIndex ?? "(memory_dir)/index.md"}`,
|
|
113414
113462
|
"- \u4E00\u4E2A\u98DE\u4E66\u8BDD\u9898 = \u4E00\u4E2A task/session\u3002\u8BDD\u9898\u5185\u7EED\u63A5\u65F6,\u7EE7\u7EED\u4F7F\u7528\u540C\u4E00\u4E2A topic_session_path\u3002",
|
|
113415
113463
|
"- \u7FA4\u91CC @ \u4F60\u65F6,bridge \u4F1A\u62C9\u8D77/\u5173\u8054\u4E00\u4E2A\u8BDD\u9898;\u662F\u5426\u8BFB\u53D6\u7FA4\u5386\u53F2\u3001\u8BDD\u9898\u5386\u53F2\u3001\u9644\u4EF6\u3001\u6587\u6863,\u7531\u4F60\u6839\u636E\u4EFB\u52A1\u81EA\u884C\u51B3\u5B9A\u3002",
|
|
113416
113464
|
"- \u4E0D\u8981\u5047\u8BBE bridge \u5DF2\u7ECF clone/fetch/worktree/pnpm install;\u9700\u8981\u4EE3\u7801\u65F6,\u4F60\u5728 workspace \u91CC\u81EA\u5DF1 clone/branch/install/test\u3002",
|
|
113417
113465
|
"- summary.md \u662F\u4F60\u7EF4\u62A4\u672C\u8BDD\u9898\u6458\u8981\u3001\u51B3\u7B56\u548C\u4E0B\u4E00\u6B65 notes \u7684\u5730\u65B9;bridge \u53EA\u521B\u5EFA\u5360\u4F4D,\u4E0D\u66FF\u4F60\u603B\u7ED3\u3002",
|
|
113418
|
-
"- \
|
|
113466
|
+
"- \u8D77\u624B\u5148\u8BFB memory/index.md \u62C9\u8D77\u8DE8 session \u957F\u671F\u8BB0\u5FC6(preferences / reusable-knowledge / workflows / decisions / assets),\u518D\u8BFB workspace \u5185\u7684 AGENTS.md\u3001CLAUDE.md(\u5982\u5B58\u5728)\u3001permissions-request.md\u3001permissions-granted.md\u3002",
|
|
113467
|
+
"- \u672C session \u91CC\u8DE8 session \u53EF\u590D\u7528\u7684\u5185\u5BB9,\u5148\u8BB0\u5230 topic_session_path/memory-candidates.md;owner \u786E\u8BA4\u540E,\u7531\u4F60\u5199\u8FDB memory/<category>.md\u3002",
|
|
113468
|
+
"- \u70ED\u8DEF\u5F84(\u6BCF\u8F6E)\u53EA\u5141\u8BB8 ADD / NOOP:\u628A\u5019\u9009 append \u5230 memory-candidates.md,\u6216\u5F80 category \u6587\u4EF6\u8FFD\u52A0\u65B0\u6761\u76EE;\u4E0D\u5728\u70ED\u8DEF\u5F84\u505A UPDATE/DELETE/\u6539\u5199\u5DF2\u6709\u6761\u76EE\u3002",
|
|
113469
|
+
"- \u6539\u5199\u3001\u5220\u9664\u3001\u89E3\u51B3\u51B2\u7A81 \u2192 \u63A8\u8FDF\u5230 owner \u663E\u5F0F\u8BF4\u300C\u6574\u7406\u8BB0\u5FC6\u300D\u65F6\u7684\u79BB\u7EBF\u6B65\u9AA4\u505A\u3002\u5931\u6548/\u88AB\u63A8\u7FFB\u7684\u6761\u76EE\u79FB memory/archive/(\u6CE8\u4E00\u53E5\u539F\u56E0),\u4E0D\u7269\u7406\u5220\u3002",
|
|
113470
|
+
"- \u79BB\u7EBF\u6574\u7406/\u6539\u5199\u5DF2\u6709\u8BB0\u5FC6\u524D,\u5148\u7528 rg \u5728 sessions/*/transcript.md \u6838\u5230\u6765\u6E90\u884C;commit/\u7B14\u8BB0\u5F15\u7528\u8BE5\u884C;\u6838\u4E0D\u5230\u6765\u6E90\u7684\u7ED3\u8BBA\u964D\u7EA7\u4E3A candidate,\u4E0D\u5199\u8FDB\u6B63\u6587\u3002(\u5355 agent \u81EA\u5DF1\u505A,\u522B spawn \u522B\u7684 agent\u3002)",
|
|
113471
|
+
"- \u53EA\u6709\u8DE8 session \u8FD8\u4F1A\u518D\u7528\u5230\u7684\u624D\u8FDB memory/(\u5355\u6B21\u4EFB\u52A1\u7559\u5728 summary,\u968F session \u8FC7\u671F);\u65B0\u589E\u3001\u4EE5\u53CA\u4F1A\u6539\u53D8\u884C\u4E3A\u6216\u8FB9\u754C\u7684\u6539\u5199/\u5220\u9664\u90FD\u8981 owner \u786E\u8BA4\u3002"
|
|
113419
113472
|
];
|
|
113473
|
+
if (memoryDir) {
|
|
113474
|
+
for (const fileName of MEMORY_CATEGORY_FILE_NAMES) {
|
|
113475
|
+
const count = statMemoryLines(`${memoryDir}/${fileName}`);
|
|
113476
|
+
if (count > MEMORY_FILE_LINE_LIMIT) {
|
|
113477
|
+
lines.push(
|
|
113478
|
+
`- \u26A0\uFE0F ${fileName} \u5DF2 ${count} \u884C,\u8D85\u9650\u2014\u2014\u4E0B\u6B21\u300C\u6574\u7406\u8BB0\u5FC6\u300D\u65F6\u5148\u84B8\u998F\u538B\u7F29\u3002`
|
|
113479
|
+
);
|
|
113480
|
+
}
|
|
113481
|
+
}
|
|
113482
|
+
}
|
|
113420
113483
|
if (conventions.defaultProjectSlug) {
|
|
113421
113484
|
lines.push("");
|
|
113422
113485
|
lines.push("Repo pointers(\u53EA\u662F\u6307\u9488,\u4E0D\u662F\u5DF2\u51C6\u5907\u597D\u7684 clone):");
|
|
@@ -113693,6 +113756,15 @@ function renderSummaryPlaceholder() {
|
|
|
113693
113756
|
""
|
|
113694
113757
|
].join("\n");
|
|
113695
113758
|
}
|
|
113759
|
+
function renderMemoryCandidatesPlaceholder() {
|
|
113760
|
+
return [
|
|
113761
|
+
"# Memory Candidates",
|
|
113762
|
+
"",
|
|
113763
|
+
"\u672C session \u91CC\u503C\u5F97\u63D0\u5347\u4E3A\u8DE8 session \u957F\u671F\u8BB0\u5FC6\u7684\u5019\u9009,\u8BB0\u5728\u8FD9\u3002",
|
|
113764
|
+
"owner \u786E\u8BA4\u540E,\u7531\u4F60(Agent)\u5199\u8FDB ../../memory/<category>.md\u3002",
|
|
113765
|
+
""
|
|
113766
|
+
].join("\n");
|
|
113767
|
+
}
|
|
113696
113768
|
function indentBlock(value) {
|
|
113697
113769
|
const trimmed = value.trim();
|
|
113698
113770
|
if (!trimmed) return " (empty)";
|
|
@@ -113748,6 +113820,10 @@ function renderTranscriptEntry(input) {
|
|
|
113748
113820
|
async function ensureSessionArtifacts(input) {
|
|
113749
113821
|
await fs.mkdir(input.sessionPath, { recursive: true });
|
|
113750
113822
|
await writeIfMissing(path4.join(input.sessionPath, "summary.md"), renderSummaryPlaceholder());
|
|
113823
|
+
await writeIfMissing(
|
|
113824
|
+
path4.join(input.sessionPath, "memory-candidates.md"),
|
|
113825
|
+
renderMemoryCandidatesPlaceholder()
|
|
113826
|
+
);
|
|
113751
113827
|
await fs.appendFile(
|
|
113752
113828
|
path4.join(input.sessionPath, "transcript.md"),
|
|
113753
113829
|
`${renderTranscriptEntry(input)}
|
|
@@ -113828,6 +113904,7 @@ function renderAgentsMd(input) {
|
|
|
113828
113904
|
"",
|
|
113829
113905
|
"## Workspace Contract",
|
|
113830
113906
|
"",
|
|
113907
|
+
"- \u5F00\u573A\u4E0D\u53EF\u8DF3\u8FC7:\u56DE\u5E94 owner \u524D,\u5148 Read `memory/index.md`,\u5E76\u6309\u76F8\u5173\u6027 Read \u76F8\u5173 category \u6587\u4EF6,\u518D\u5F00\u59CB\u5E72\u6D3B(\u9632\u6B62\u65B0 session \u5931\u5FC6)\u3002",
|
|
113831
113908
|
"- Larkway is a thin Feishu bridge. It passes scene/context pointers; you decide what to inspect and what work to do.",
|
|
113832
113909
|
"- Each Feishu topic is one task session under `sessions/<thread_id>/`.",
|
|
113833
113910
|
"- Keep durable notes, repo clones, session summaries, and permission decisions inside this workspace.",
|
|
@@ -113994,6 +114071,75 @@ async function ensureAgentWorkspace(input) {
|
|
|
113994
114071
|
path5.join(input.workspacePath, "permissions-granted.md"),
|
|
113995
114072
|
renderPermissionsGranted(input)
|
|
113996
114073
|
);
|
|
114074
|
+
await ensureMemoryScaffold(input.workspacePath);
|
|
114075
|
+
}
|
|
114076
|
+
var MEMORY_CATEGORY_FILES = [
|
|
114077
|
+
{
|
|
114078
|
+
name: "preferences.md",
|
|
114079
|
+
title: "Owner Preferences",
|
|
114080
|
+
purpose: "owner \u6216\u56E2\u961F\u957F\u671F\u504F\u597D(\u6C47\u62A5\u683C\u5F0F\u3001\u9ED8\u8BA4\u8BED\u8A00\u3001\u9A8C\u8BC1\u504F\u597D\u7B49)\u3002"
|
|
114081
|
+
},
|
|
114082
|
+
{
|
|
114083
|
+
name: "reusable-knowledge.md",
|
|
114084
|
+
title: "Reusable Knowledge",
|
|
114085
|
+
purpose: "\u591A\u4E2A session \u540E\u6C89\u6DC0\u51FA\u7684\u53EF\u590D\u7528\u7ECF\u9A8C\u3001\u5E38\u89C1\u5751\u3001\u65B9\u6848\u5224\u65AD\u3002"
|
|
114086
|
+
},
|
|
114087
|
+
{
|
|
114088
|
+
name: "workflows.md",
|
|
114089
|
+
title: "Workflows",
|
|
114090
|
+
purpose: "\u8FD9\u4E2A Agent \u81EA\u5DF1\u7684\u957F\u671F\u5DE5\u4F5C\u65B9\u5F0F(\u9879\u76EE repo \u7684\u5DE5\u7A0B\u89C4\u8303\u4ECD\u5199\u8FDB\u9879\u76EE repo)\u3002"
|
|
114091
|
+
},
|
|
114092
|
+
{
|
|
114093
|
+
name: "decisions.md",
|
|
114094
|
+
title: "Decisions",
|
|
114095
|
+
purpose: "\u957F\u671F\u51B3\u7B56\u8BB0\u5F55,\u4F8B\u5982\u4E3A\u4EC0\u4E48\u67D0\u6743\u9650\u5FC5\u987B\u4EBA\u5DE5\u786E\u8BA4\u3002"
|
|
114096
|
+
},
|
|
114097
|
+
{
|
|
114098
|
+
name: "assets.md",
|
|
114099
|
+
title: "Assets",
|
|
114100
|
+
purpose: "\u957F\u671F\u56FE\u7247/\u622A\u56FE/\u9644\u4EF6\u7684\u7D22\u5F15,\u53EA\u8BB0\u5F15\u7528\u548C\u7528\u9014,\u4E0D\u5185\u8054\u5927\u6587\u4EF6(\u5B9E\u4F53\u653E assets/)\u3002"
|
|
114101
|
+
}
|
|
114102
|
+
];
|
|
114103
|
+
function renderMemoryIndex() {
|
|
114104
|
+
return [
|
|
114105
|
+
"# Memory Index",
|
|
114106
|
+
"",
|
|
114107
|
+
"\u8FD9\u662F\u8DE8 session \u957F\u671F\u8BB0\u5FC6\u3002\u65B0 session \u8D77\u624B\u5148\u8BFB\u8FD9\u91CC,\u628A\u8FC7\u5F80\u79EF\u7D2F\u62C9\u8D77\u6765\u3002",
|
|
114108
|
+
"Larkway \u53EA\u521D\u59CB\u5316\u7A7A\u5BB9\u5668;\u63D0\u70BC\u3001\u5206\u7C7B\u3001\u5199\u5165\u6B63\u6587\u90FD\u7531\u4F60(Agent)\u5728 owner \u786E\u8BA4\u540E\u5B8C\u6210\u3002",
|
|
114109
|
+
"",
|
|
114110
|
+
"## \u5206\u7C7B\u6587\u4EF6",
|
|
114111
|
+
"",
|
|
114112
|
+
...MEMORY_CATEGORY_FILES.map((f) => `- \`${f.name}\` \u2014 ${f.purpose}`),
|
|
114113
|
+
"",
|
|
114114
|
+
"## \u7EF4\u62A4\u89C4\u5219",
|
|
114115
|
+
"",
|
|
114116
|
+
"- \u70ED\u8DEF\u5F84(\u6BCF\u8F6E)\u53EA\u505A\u52A0\u6CD5:\u628A\u5019\u9009\u5199\u8FDB session \u7684 `memory-candidates.md`,\u6216\u5F80 category \u6587\u4EF6\u8FFD\u52A0\u65B0\u6761\u76EE;\u4E0D\u5728\u70ED\u8DEF\u5F84\u6539\u5199/\u5220\u9664\u5DF2\u6709\u6761\u76EE\u3002",
|
|
114117
|
+
"- \u6539\u5199\u3001\u5220\u9664\u3001\u89E3\u51B3\u51B2\u7A81 \u2192 \u63A8\u8FDF\u5230 owner \u663E\u5F0F\u8BF4\u300C\u6574\u7406\u8BB0\u5FC6\u300D\u65F6\u79BB\u7EBF\u505A\u3002UPDATE/DELETE \u7684\u65E7\u6761\u76EE\u79FB `archive/`(\u6CE8\u4E00\u53E5\u539F\u56E0+\u5BF9\u5E94 commit),\u4E0D\u624B\u5199 superseded \u6233,\u4E0D\u7269\u7406\u5220;archive/ \u7684\u957F\u671F\u6E05\u7406\u4EA4\u7ED9 git history\u3002",
|
|
114118
|
+
"- \u88C1\u51B3\u4EE5 source \u4F18\u5148:user \u4EB2\u53E3\u8BF4\u7684 >> agent \u63A8\u65AD;\u51B2\u7A81\u65F6\u4FDD\u7559\u65E7\u7684 user \u6761\u76EE,\u628A\u65B0\u63A8\u65AD\u964D\u7EA7\u4E3A candidate\u3002\u540C source \u5185 recency \u4EE5 git \u5386\u53F2\u4E3A\u51C6,\u4E0D\u624B\u5199\u65E5\u671F\u6233\u3002",
|
|
114119
|
+
"- \u53EA\u6709\u8DE8 session \u8FD8\u4F1A\u7528\u5230\u7684\u624D\u8FDB\u8FD9\u91CC(\u5355\u6B21\u4EFB\u52A1\u7559\u5728 session summary)\u3002\u672C\u6587\u4EF6\u4FDD\u6301\u7B80\u77ED(\u2264~50 \u884C),\u4E0D\u653E changelog/\u7EDF\u8BA1\u3002",
|
|
114120
|
+
""
|
|
114121
|
+
].join("\n");
|
|
114122
|
+
}
|
|
114123
|
+
function renderMemoryCategorySkeleton(title) {
|
|
114124
|
+
return [
|
|
114125
|
+
`# ${title}`,
|
|
114126
|
+
"",
|
|
114127
|
+
"\u5199\u524D\u5148\u8BFB\u672C\u6587\u4EF6;\u6709\u76F8\u540C/\u76F8\u5173\u6761\u76EE\u5C31 NOOP \u4E0D\u91CD\u590D\u5199(\u6539\u5199\u3001\u5220\u9664\u3001\u79FB archive \u7559\u5230 owner \u8BF4\u300C\u6574\u7406\u8BB0\u5FC6\u300D\u65F6\u7684\u79BB\u7EBF\u6B65\u9AA4);\u4FE1\u53F7\u4E0D\u8DE8 session \u590D\u7528\u5C31\u4E0D\u5199\u3002",
|
|
114128
|
+
""
|
|
114129
|
+
].join("\n");
|
|
114130
|
+
}
|
|
114131
|
+
async function ensureMemoryScaffold(workspacePath) {
|
|
114132
|
+
const memoryDir = path5.join(workspacePath, "memory");
|
|
114133
|
+
await fs2.mkdir(memoryDir, { recursive: true });
|
|
114134
|
+
await fs2.mkdir(path5.join(memoryDir, "assets"), { recursive: true });
|
|
114135
|
+
await fs2.mkdir(path5.join(memoryDir, "archive"), { recursive: true });
|
|
114136
|
+
await writeIfMissing2(path5.join(memoryDir, "index.md"), renderMemoryIndex());
|
|
114137
|
+
for (const file of MEMORY_CATEGORY_FILES) {
|
|
114138
|
+
await writeIfMissing2(
|
|
114139
|
+
path5.join(memoryDir, file.name),
|
|
114140
|
+
renderMemoryCategorySkeleton(file.title)
|
|
114141
|
+
);
|
|
114142
|
+
}
|
|
113997
114143
|
}
|
|
113998
114144
|
|
|
113999
114145
|
// src/bridge/stateFile.ts
|
|
@@ -119265,13 +119411,7 @@ async function runV2Mode({
|
|
|
119265
119411
|
);
|
|
119266
119412
|
}
|
|
119267
119413
|
const larkCliProfile = deriveLarkCliProfile(bot.lark_cli_profile, bot.app_id);
|
|
119268
|
-
|
|
119269
|
-
ensureLarkCliProfile(bot.id, larkCliProfile, bot.app_id, appSecret);
|
|
119270
|
-
} else {
|
|
119271
|
-
if (bot.lark_cli_profile) {
|
|
119272
|
-
ensureLarkCliProfile(bot.id, larkCliProfile, bot.app_id, appSecret);
|
|
119273
|
-
}
|
|
119274
|
-
}
|
|
119414
|
+
ensureLarkCliProfile(bot.id, larkCliProfile, bot.app_id, appSecret);
|
|
119275
119415
|
const botDir = resolveLarkwayDir(bot.id);
|
|
119276
119416
|
const sharedReposDir = path13.join(larkwayHome(), "repos");
|
|
119277
119417
|
const worktreesDir = path13.join(botDir, "worktrees");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "larkway",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
4
4
|
"description": "Thin bridge: Feishu thread to local Claude Code CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Chuck Wu (chuckwu0)",
|
|
@@ -19,6 +19,18 @@
|
|
|
19
19
|
"bin": {
|
|
20
20
|
"larkway": "bin/larkway"
|
|
21
21
|
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"start": "tsx src/main.ts",
|
|
24
|
+
"cli": "tsx src/cli/index.ts",
|
|
25
|
+
"start:supervised": "bin/start-bridge.sh",
|
|
26
|
+
"dev": "tsx watch src/main.ts",
|
|
27
|
+
"build": "node scripts/build.mjs",
|
|
28
|
+
"prepack": "node scripts/build.mjs",
|
|
29
|
+
"typecheck": "tsc --noEmit",
|
|
30
|
+
"check:links": "bash bin/check-doc-links.sh",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:v0.3": "bash bin/v0.3-local-acceptance.sh"
|
|
33
|
+
},
|
|
22
34
|
"dependencies": {},
|
|
23
35
|
"devDependencies": {
|
|
24
36
|
"@types/js-yaml": "^4.0.9",
|
|
@@ -36,6 +48,7 @@
|
|
|
36
48
|
"qrcode-terminal": "^0.12.0",
|
|
37
49
|
"zod": "^3.22.0"
|
|
38
50
|
},
|
|
51
|
+
"packageManager": "pnpm@9.0.0",
|
|
39
52
|
"engines": {
|
|
40
53
|
"node": ">=20"
|
|
41
54
|
},
|
|
@@ -43,16 +56,5 @@
|
|
|
43
56
|
"dist/",
|
|
44
57
|
"bin/larkway",
|
|
45
58
|
"README.md"
|
|
46
|
-
]
|
|
47
|
-
|
|
48
|
-
"start": "tsx src/main.ts",
|
|
49
|
-
"cli": "tsx src/cli/index.ts",
|
|
50
|
-
"start:supervised": "bin/start-bridge.sh",
|
|
51
|
-
"dev": "tsx watch src/main.ts",
|
|
52
|
-
"build": "node scripts/build.mjs",
|
|
53
|
-
"typecheck": "tsc --noEmit",
|
|
54
|
-
"check:links": "bash bin/check-doc-links.sh",
|
|
55
|
-
"test": "vitest run",
|
|
56
|
-
"test:v0.3": "bash bin/v0.3-local-acceptance.sh"
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
+
]
|
|
60
|
+
}
|