projectkeeper 0.0.0-stage → 1.0.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.
- package/CHANGELOG.md +56 -0
- package/LICENSE +21 -0
- package/README.md +283 -2
- package/README.zh-CN.md +285 -0
- package/THIRD_PARTY_NOTICES.md +53 -0
- package/bin/pk.mjs +6 -0
- package/bin/postinstall.mjs +5 -0
- package/bin/strip-types.mjs +28 -0
- package/package.json +68 -4
- package/patches/README.md +111 -0
- package/patches/pi-ai-0.85.0.ts +122 -0
- package/patches/pi-ai-0.87.1.ts +122 -0
- package/scripts/apply-pi-streaming-patch.ts +86 -0
- package/skills/clerk/coverage/SKILL.md +35 -0
- package/skills/clerk/cross-check/SKILL.md +118 -0
- package/skills/clerk/deepen-dispatch/SKILL.md +69 -0
- package/skills/clerk/lane-follow-up/SKILL.md +94 -0
- package/skills/clerk/lane-plan/SKILL.md +107 -0
- package/skills/clerk/lane-slot/SKILL.md +113 -0
- package/skills/clerk/lane-topic/SKILL.md +116 -0
- package/skills/clerk/orientation/SKILL.md +160 -0
- package/skills/clerk/reconcile/SKILL.md +77 -0
- package/skills/clerk/skeleton-dispatch/SKILL.md +43 -0
- package/skills/clerk/spot-check/SKILL.md +52 -0
- package/skills/clerk/synthesis/SKILL.md +65 -0
- package/skills/table-follow-up/SKILL.md +42 -0
- package/src/cli.ts +319 -0
- package/src/codemap/README.md +13 -0
- package/src/codemap/engines.ts +102 -0
- package/src/codemap/facts.ts +504 -0
- package/src/context/agent-entry.ts +232 -0
- package/src/context/ask.ts +97 -0
- package/src/context/assemble.ts +1058 -0
- package/src/context/code-source.ts +103 -0
- package/src/context/k-briefs.ts +114 -0
- package/src/context/lineage.ts +100 -0
- package/src/context/object-brief.ts +300 -0
- package/src/context/owner-words.ts +191 -0
- package/src/context/pack-fixture.ts +342 -0
- package/src/context/project-rules.ts +160 -0
- package/src/intake/intake.ts +392 -0
- package/src/intake/material-rules.ts +166 -0
- package/src/intake/removal.ts +316 -0
- package/src/intake/skipped.ts +72 -0
- package/src/keeper/adjustment.ts +565 -0
- package/src/keeper/bounds/boundary.ts +509 -0
- package/src/keeper/bounds/command.ts +1665 -0
- package/src/keeper/bounds/paths.ts +164 -0
- package/src/keeper/bounds/reads.ts +718 -0
- package/src/keeper/bounds/shell-write-guard.ts +521 -0
- package/src/keeper/capabilities.ts +136 -0
- package/src/keeper/clerk-steps.ts +151 -0
- package/src/keeper/clerk-tools.ts +1424 -0
- package/src/keeper/conversation.ts +253 -0
- package/src/keeper/evidence.ts +662 -0
- package/src/keeper/fake-provider.ts +132 -0
- package/src/keeper/held.ts +15 -0
- package/src/keeper/history-tools.ts +163 -0
- package/src/keeper/jobs/answering.ts +53 -0
- package/src/keeper/key-check.ts +105 -0
- package/src/keeper/ledger-tools.ts +295 -0
- package/src/keeper/link-check.ts +185 -0
- package/src/keeper/merge.ts +260 -0
- package/src/keeper/note-origin.ts +56 -0
- package/src/keeper/numbers-check.ts +102 -0
- package/src/keeper/open-in-pi.ts +57 -0
- package/src/keeper/organize/absence.ts +266 -0
- package/src/keeper/organize/carried-on.ts +214 -0
- package/src/keeper/organize/clerk-coverage.ts +293 -0
- package/src/keeper/organize/clerk-prompts.ts +132 -0
- package/src/keeper/organize/clerk.ts +1395 -0
- package/src/keeper/organize/coverage-gate.ts +19 -0
- package/src/keeper/organize/coverage-tools.ts +765 -0
- package/src/keeper/organize/entries.ts +153 -0
- package/src/keeper/organize/entry-gate.ts +289 -0
- package/src/keeper/organize/fill-tools.ts +718 -0
- package/src/keeper/organize/follow-up.ts +280 -0
- package/src/keeper/organize/generation-check.ts +504 -0
- package/src/keeper/organize/graph.ts +230 -0
- package/src/keeper/organize/lane-tools.ts +289 -0
- package/src/keeper/organize/looked-tools.ts +54 -0
- package/src/keeper/organize/materials.ts +243 -0
- package/src/keeper/organize/owner-lines.ts +493 -0
- package/src/keeper/organize/place-tools.ts +216 -0
- package/src/keeper/organize/placement-inference.ts +1045 -0
- package/src/keeper/organize/placement-time.ts +320 -0
- package/src/keeper/organize/placing.ts +821 -0
- package/src/keeper/organize/positioning.ts +33 -0
- package/src/keeper/organize/readiness.ts +47 -0
- package/src/keeper/organize/reading.ts +401 -0
- package/src/keeper/organize/round-blocks.ts +65 -0
- package/src/keeper/organize/round-news.ts +164 -0
- package/src/keeper/organize/round-open.ts +586 -0
- package/src/keeper/organize/schedule-state.ts +39 -0
- package/src/keeper/organize/schedule.ts +137 -0
- package/src/keeper/organize/service.ts +409 -0
- package/src/keeper/organize/skills.ts +116 -0
- package/src/keeper/organize/slots.ts +85 -0
- package/src/keeper/organize/stage-gate.ts +175 -0
- package/src/keeper/organize/stage-tools.ts +471 -0
- package/src/keeper/organize/standing-notes.ts +141 -0
- package/src/keeper/organize/table-follow-up.ts +330 -0
- package/src/keeper/organize/takeover-clerk.ts +832 -0
- package/src/keeper/organize/takeover-state.ts +103 -0
- package/src/keeper/organize/takeover.ts +216 -0
- package/src/keeper/organize/update-pending.ts +535 -0
- package/src/keeper/organize/workbench-placement.ts +92 -0
- package/src/keeper/own-keys.ts +114 -0
- package/src/keeper/owner-text.ts +165 -0
- package/src/keeper/project-folder.ts +334 -0
- package/src/keeper/prompts.ts +101 -0
- package/src/keeper/references.ts +177 -0
- package/src/keeper/relook/compose.ts +120 -0
- package/src/keeper/relook/prompts.ts +36 -0
- package/src/keeper/roles.ts +259 -0
- package/src/keeper/round-tools.ts +227 -0
- package/src/keeper/route.ts +148 -0
- package/src/keeper/rules.ts +97 -0
- package/src/keeper/runtime.ts +1858 -0
- package/src/keeper/scope-tools.ts +194 -0
- package/src/keeper/sendback-text.ts +83 -0
- package/src/keeper/step-timing.ts +207 -0
- package/src/keeper/testing/clerk-script.ts +212 -0
- package/src/keeper/testing/streaming-arguments.ts +57 -0
- package/src/keeper/tools.ts +2340 -0
- package/src/keeper/utterance-tools.ts +64 -0
- package/src/keeper/verbatim.ts +397 -0
- package/src/ledger/adapters.ts +186 -0
- package/src/ledger/arrangements.ts +257 -0
- package/src/ledger/blame.ts +58 -0
- package/src/ledger/code-engine-child.ts +69 -0
- package/src/ledger/code-engine.ts +548 -0
- package/src/ledger/code.ts +607 -0
- package/src/ledger/docs.ts +294 -0
- package/src/ledger/git-read.ts +321 -0
- package/src/ledger/index.ts +1850 -0
- package/src/ledger/item-range.ts +16 -0
- package/src/ledger/lines.ts +328 -0
- package/src/ledger/numbering.ts +269 -0
- package/src/ledger/ranges.ts +74 -0
- package/src/ledger/rebuild-worker.ts +15 -0
- package/src/ledger/rebuild.ts +387 -0
- package/src/ledger/repo-scan.ts +181 -0
- package/src/ledger/schema.ts +450 -0
- package/src/ledger/sessions.ts +134 -0
- package/src/ledger/text-scan.ts +466 -0
- package/src/ledger/time.ts +185 -0
- package/src/ledger/ts-symbols.ts +180 -0
- package/src/ledger/views.ts +377 -0
- package/src/mcp/server.ts +127 -0
- package/src/mcp/tools.ts +187 -0
- package/src/model/ids.ts +23 -0
- package/src/model/k-types.ts +912 -0
- package/src/model/time.ts +85 -0
- package/src/model/types.ts +1382 -0
- package/src/model/views-k.ts +707 -0
- package/src/model/vocab.ts +269 -0
- package/src/process/analysis.ts +79 -0
- package/src/process/breakpoint-candidates.ts +383 -0
- package/src/process/breakpoints.ts +746 -0
- package/src/process/claim-check.ts +295 -0
- package/src/process/delivery-links.ts +52 -0
- package/src/process/facts.ts +487 -0
- package/src/process/generations.ts +138 -0
- package/src/process/index.ts +243 -0
- package/src/process/integration.ts +128 -0
- package/src/process/placement.ts +107 -0
- package/src/process/plan.ts +276 -0
- package/src/process/sendbacks.ts +244 -0
- package/src/process/text.ts +185 -0
- package/src/process/units.ts +543 -0
- package/src/process/unnumbered.ts +431 -0
- package/src/process/work.ts +884 -0
- package/src/scope/candidates.ts +86 -0
- package/src/scope/decisions.ts +229 -0
- package/src/scope/discover.ts +741 -0
- package/src/scope/ignore.ts +126 -0
- package/src/scope/ignored-place.ts +93 -0
- package/src/scope/skip.ts +161 -0
- package/src/scope/toolchain.ts +180 -0
- package/src/scope/worktree.ts +187 -0
- package/src/server/api.ts +549 -0
- package/src/server/app.ts +595 -0
- package/src/server/drafts-api.ts +170 -0
- package/src/server/folders-api.ts +243 -0
- package/src/server/graph-view.ts +639 -0
- package/src/server/http.ts +224 -0
- package/src/server/k-agent-api.ts +56 -0
- package/src/server/k-rounds-d99.ts +91 -0
- package/src/server/k-views.ts +405 -0
- package/src/server/keeper-page.ts +446 -0
- package/src/server/keys-api.ts +239 -0
- package/src/server/project-folder-api.ts +104 -0
- package/src/server/vendor.ts +14 -0
- package/src/server/workbench-content.ts +561 -0
- package/src/sources/anchor.ts +129 -0
- package/src/sources/files.ts +267 -0
- package/src/sources/gitobs.ts +41 -0
- package/src/sources/history.ts +231 -0
- package/src/sources/scan-worker.ts +10 -0
- package/src/sources/sessions/locate.ts +292 -0
- package/src/sources/sessions/read.ts +346 -0
- package/src/sources/sessions/scope.ts +42 -0
- package/src/sources/sessions/utterances.ts +223 -0
- package/src/sources/watch.ts +215 -0
- package/src/store/json-file.ts +50 -0
- package/src/store/paths.ts +39 -0
- package/src/store/project-store.ts +295 -0
- package/src/store/versions.ts +172 -0
- package/src/store/workspace.ts +216 -0
- package/src/table/check.ts +333 -0
- package/src/table/follow-up-tools.ts +30 -0
- package/src/table/follow-up.ts +344 -0
- package/src/table/import.ts +610 -0
- package/src/table/project-home.ts +61 -0
- package/src/table/sessions.ts +168 -0
- package/src/table/takeover.ts +483 -0
- package/src/util/git-check.ts +29 -0
- package/src/util/git.ts +411 -0
- package/src/util/own-ts.ts +15 -0
- package/src/util/paths.ts +225 -0
- package/template/FILLING-IN.md +148 -0
- package/template/examples/papertrail.json +747 -0
- package/template/projectkeeper.schema.json +1001 -0
- package/ui/app.js +1135 -0
- package/ui/failures.js +82 -0
- package/ui/flyout.js +120 -0
- package/ui/folder-chooser.js +264 -0
- package/ui/glossary.js +156 -0
- package/ui/graph-fit.js +157 -0
- package/ui/graph-material.js +61 -0
- package/ui/graph-palette.js +109 -0
- package/ui/graph-tools.js +62 -0
- package/ui/graph.js +1712 -0
- package/ui/index.html +31 -0
- package/ui/k/code-view.js +230 -0
- package/ui/k/draft-view.js +93 -0
- package/ui/k/k-views.css +224 -0
- package/ui/k/round-tree.js +450 -0
- package/ui/k/scope-k.js +159 -0
- package/ui/k-data.js +118 -0
- package/ui/k-fold.js +106 -0
- package/ui/k-process.css +302 -0
- package/ui/k-process.js +934 -0
- package/ui/keeper-activity.js +175 -0
- package/ui/keeper-view.js +801 -0
- package/ui/keeper.css +101 -0
- package/ui/key-order.js +62 -0
- package/ui/list-fold.js +67 -0
- package/ui/markdown.d.ts +21 -0
- package/ui/markdown.js +472 -0
- package/ui/placement.d.ts +92 -0
- package/ui/placement.js +445 -0
- package/ui/popover-place.d.ts +16 -0
- package/ui/popover-place.js +89 -0
- package/ui/popover.js +198 -0
- package/ui/project-folder.js +37 -0
- package/ui/shell-state.js +40 -0
- package/ui/strip-rows.js +171 -0
- package/ui/styles.css +492 -0
- package/ui/theme-pick.js +36 -0
- package/ui/theme.js +74 -0
- package/ui/themes/LICENSE.md +13 -0
- package/ui/themes/README.md +44 -0
- package/ui/themes/TOKENS.md +291 -0
- package/ui/themes/a1/board.css +1557 -0
- package/ui/themes/a1/pk.css +392 -0
- package/ui/themes/b1/board.css +1163 -0
- package/ui/themes/b1/pk.css +218 -0
- package/ui/themes/d2/board.css +518 -0
- package/ui/themes/d2/pk.css +243 -0
- package/ui/themes/guard.css +73 -0
- package/ui/themes/i1/board.css +776 -0
- package/ui/themes/i1/pk.css +223 -0
- package/ui/themes/manifest.json +13 -0
- package/ui/views.js +2796 -0
package/src/mcp/tools.ts
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tools of `pk mcp` (D108): `pk context`, `pk get`, `pk options`, `pk ask` and `pk check`, each a thin wrapper over
|
|
3
|
+
* the same code the command line runs (src/context/agent-entry.ts, src/table/check.ts), so a tool says what the command
|
|
4
|
+
* prints. Every tool but the table check reads the running workbench; when it is not running the tool says so, in the
|
|
5
|
+
* command's own words. Descriptions are short on purpose: a client loads them into every session it starts.
|
|
6
|
+
*/
|
|
7
|
+
import { resolve } from 'node:path';
|
|
8
|
+
import { agentEntry, askEntry, Exit, type EntryOutput, type EntryTarget } from '../context/agent-entry.ts';
|
|
9
|
+
import { Workspace } from '../store/workspace.ts';
|
|
10
|
+
import { checkTableFile, readSessionMaterial, tableCheckReport } from '../table/check.ts';
|
|
11
|
+
|
|
12
|
+
export interface ToolResult {
|
|
13
|
+
content: { type: 'text'; text: string }[];
|
|
14
|
+
isError?: true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface McpTool {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly description: string;
|
|
20
|
+
readonly inputSchema: Record<string, unknown>;
|
|
21
|
+
readonly annotations?: Record<string, unknown>;
|
|
22
|
+
call(args: Record<string, unknown>): Promise<ToolResult>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** What a tool falls back on when its call does not say: the flags `pk mcp` was started with. */
|
|
26
|
+
export interface McpDefaults {
|
|
27
|
+
/** The workbench port (`--port`); otherwise the one in the home's settings, read at each call. */
|
|
28
|
+
readonly port: string | null;
|
|
29
|
+
/** The project id (`--project`); otherwise the project containing `cwd`. */
|
|
30
|
+
readonly project: string | null;
|
|
31
|
+
/** The folder the client started the server in (`--cwd`, or the server's own working directory). */
|
|
32
|
+
readonly cwd: string;
|
|
33
|
+
/** ProjectKeeper's home (`--home` or PROJECTKEEPER_HOME), whose settings name the port. */
|
|
34
|
+
readonly home?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** An input given with the wrong type: said back to the agent as the tool's error, not as a protocol error. */
|
|
38
|
+
class InputError extends Error {}
|
|
39
|
+
|
|
40
|
+
const text = (lines: readonly string[], error: boolean): ToolResult => ({ content: [{ type: 'text', text: lines.join('\n') || '(nothing)' }], ...(error ? { isError: true as const } : {}) });
|
|
41
|
+
|
|
42
|
+
function stringArg(args: Record<string, unknown>, key: string): string | null {
|
|
43
|
+
const v = args[key];
|
|
44
|
+
if (v === undefined || v === null || v === '') return null;
|
|
45
|
+
if (typeof v !== 'string') throw new InputError(`${key} must be a string.`);
|
|
46
|
+
return v;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Run an entry as the command line would, collecting what it says; a code other than 0 is the tool's error. */
|
|
50
|
+
async function collect(run: (io: EntryOutput) => Promise<number>): Promise<ToolResult> {
|
|
51
|
+
const lines: string[] = [];
|
|
52
|
+
const io: EntryOutput = { out: (t) => lines.push(t), err: (t) => lines.push(t) };
|
|
53
|
+
let code: number;
|
|
54
|
+
try { code = await run(io); }
|
|
55
|
+
catch (e) {
|
|
56
|
+
if (e instanceof Exit) code = e.code;
|
|
57
|
+
else { lines.push(e instanceof Error ? e.message : String(e)); code = 1; }
|
|
58
|
+
}
|
|
59
|
+
return text(lines, code !== 0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const PROJECT_INPUTS = {
|
|
63
|
+
project: { type: 'string', description: 'Project id (`pk projects` lists them). Default: the project containing cwd.' },
|
|
64
|
+
cwd: { type: 'string', description: 'A folder inside the project. Default: the folder the server was started in.' },
|
|
65
|
+
port: { type: 'integer', description: 'Workbench port. Default: the one in ProjectKeeper\'s settings (4870).' },
|
|
66
|
+
} as const;
|
|
67
|
+
|
|
68
|
+
const READ_ONLY = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false } as const;
|
|
69
|
+
|
|
70
|
+
export function projectKeeperTools(defaults: McpDefaults): McpTool[] {
|
|
71
|
+
/** Which workbench and project a call reads: its own inputs first, then the server's flags, then the settings. */
|
|
72
|
+
const target = (args: Record<string, unknown>): EntryTarget => {
|
|
73
|
+
const givenPort = args.port;
|
|
74
|
+
if (givenPort !== undefined && givenPort !== null && !(Number.isInteger(givenPort) || (typeof givenPort === 'string' && /^\d+$/.test(givenPort)))) throw new InputError('port must be an integer.');
|
|
75
|
+
const port = givenPort ?? defaults.port ?? Workspace.open(defaults.home).settings.port;
|
|
76
|
+
const project = stringArg(args, 'project');
|
|
77
|
+
const cwd = stringArg(args, 'cwd');
|
|
78
|
+
// A folder given in the call names its project; the server's --project stands only when the call names neither.
|
|
79
|
+
return { base: `http://127.0.0.1:${String(port)}`, project: project ?? (cwd ? null : defaults.project), cwd: resolve(defaults.cwd, cwd ?? '.') };
|
|
80
|
+
};
|
|
81
|
+
const guarded = (call: (args: Record<string, unknown>) => Promise<ToolResult>) => async (args: Record<string, unknown>): Promise<ToolResult> => {
|
|
82
|
+
try { return await call(args); }
|
|
83
|
+
catch (e) { return text([e instanceof Error ? e.message : String(e)], true); }
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return [
|
|
87
|
+
{
|
|
88
|
+
name: 'projectkeeper_context',
|
|
89
|
+
description: 'Read-only. The context pack to read before starting work on this project (like `pk context`): purpose and the owner\'s words, direction, plan, relevant work, recent changes, open notes, with numbered sources. Whole project by default; give `work` for one work item or area.',
|
|
90
|
+
inputSchema: {
|
|
91
|
+
type: 'object',
|
|
92
|
+
properties: {
|
|
93
|
+
work: { type: 'string', description: 'A work item or area, by id or name. Omit for the whole project.' },
|
|
94
|
+
purpose: { type: 'string', enum: ['Start', 'Work'], description: 'Start (default for the project) or Work (default with work).' },
|
|
95
|
+
kind: { type: 'string', enum: ['Implement', 'Review', 'Plan', 'Discuss product', 'Investigate'], description: 'What the pack is for. Default: Implement.' },
|
|
96
|
+
recipient: { type: 'string', description: 'Who reads it: "Incoming agent" (default) or a role the project recognises.' },
|
|
97
|
+
since: { type: 'string', description: 'Time of your last pack; gives only what changed since.' },
|
|
98
|
+
...PROJECT_INPUTS,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
annotations: { title: 'ProjectKeeper context pack', ...READ_ONLY },
|
|
102
|
+
call: guarded(async (args) => {
|
|
103
|
+
const options = { work: stringArg(args, 'work'), purpose: stringArg(args, 'purpose'), kind: stringArg(args, 'kind'), recipient: stringArg(args, 'recipient'), since: stringArg(args, 'since') };
|
|
104
|
+
const t = target(args);
|
|
105
|
+
return collect((io) => agentEntry('context', t, io, [], options));
|
|
106
|
+
}),
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: 'projectkeeper_get',
|
|
110
|
+
description: 'Read-only. Read in full any id a pack names (like `pk get`): a work item or area (its pack), a source src_… (the original; for code, where it is and the version read), a note, a rule, a breakpoint, a send-back, or any other object with its neighbours. Ids, not paths.',
|
|
111
|
+
inputSchema: {
|
|
112
|
+
type: 'object',
|
|
113
|
+
properties: { id: { type: 'string', description: 'An id from a pack or from projectkeeper_options.' }, ...PROJECT_INPUTS },
|
|
114
|
+
required: ['id'],
|
|
115
|
+
},
|
|
116
|
+
annotations: { title: 'ProjectKeeper get', ...READ_ONLY },
|
|
117
|
+
call: guarded(async (args) => {
|
|
118
|
+
const id = stringArg(args, 'id');
|
|
119
|
+
if (!id) return text(['Give the id to read.'], true);
|
|
120
|
+
const t = target(args);
|
|
121
|
+
return collect((io) => agentEntry('get', t, io, [id]));
|
|
122
|
+
}),
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: 'projectkeeper_options',
|
|
126
|
+
description: 'Read-only. What can be asked for (like `pk options`): areas and work items with ids and progress, roles, how each pack kind and recipient differs, lit breakpoints, open send-backs, the six things.',
|
|
127
|
+
inputSchema: { type: 'object', properties: { ...PROJECT_INPUTS } },
|
|
128
|
+
annotations: { title: 'ProjectKeeper options', ...READ_ONLY },
|
|
129
|
+
call: guarded(async (args) => {
|
|
130
|
+
const t = target(args);
|
|
131
|
+
return collect((io) => agentEntry('options', t, io, []));
|
|
132
|
+
}),
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: 'projectkeeper_ask',
|
|
136
|
+
description: 'Ask the Keeper about the project (like `pk ask`). Returns what is already organized at once, and an investigation job id; call again with jobId to fetch its answer. Read-only toward the project; the investigation uses the owner\'s model key.',
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {
|
|
140
|
+
question: { type: 'string', description: 'The question. Not needed with jobId.' },
|
|
141
|
+
jobId: { type: 'string', description: 'An investigation job id from an earlier ask: fetches its answer.' },
|
|
142
|
+
thread: { type: 'string', description: 'Keep asking in this thread (from an earlier ask).' },
|
|
143
|
+
...PROJECT_INPUTS,
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
annotations: { title: 'Ask the ProjectKeeper Keeper', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false },
|
|
147
|
+
call: guarded(async (args) => {
|
|
148
|
+
const question = stringArg(args, 'question');
|
|
149
|
+
const jobId = stringArg(args, 'jobId');
|
|
150
|
+
if (!question && !jobId) return text(['Give a question, or jobId to fetch the answer of an earlier one.'], true);
|
|
151
|
+
const t = target(args);
|
|
152
|
+
const portArg = args.port !== undefined && args.port !== null ? `, "port": ${JSON.stringify(args.port)}` : '';
|
|
153
|
+
return collect((io) => askEntry(t, io, {
|
|
154
|
+
question: question ?? '', fetch: jobId, thread: stringArg(args, 'thread'), wait: false,
|
|
155
|
+
fetchLater: (projectId, job) => `projectkeeper_ask with {"project": ${JSON.stringify(projectId)}${portArg}, "jobId": ${JSON.stringify(job)}}`,
|
|
156
|
+
askAgain: (projectId, thread) => `projectkeeper_ask with {"project": ${JSON.stringify(projectId)}${portArg}, "thread": ${JSON.stringify(thread)}, "question": "…"}`,
|
|
157
|
+
}));
|
|
158
|
+
}),
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'projectkeeper_check_table',
|
|
162
|
+
description: 'Read-only. Check a filled ProjectKeeper table (like `pk check`): its shape against the schema, its ids and references, and every source against the project\'s files. Lists every problem, or says there are none. Needs no workbench.',
|
|
163
|
+
inputSchema: {
|
|
164
|
+
type: 'object',
|
|
165
|
+
properties: {
|
|
166
|
+
table: { type: 'string', description: 'The table file, e.g. projectkeeper/project.json. Relative paths start at the server\'s folder.' },
|
|
167
|
+
project: { type: 'string', description: 'The project folder. Default: the folder holding the table\'s projectkeeper/ folder.' },
|
|
168
|
+
sessions: { type: 'string', description: 'The session-material.jsonl the table quotes the owner from (written by `pk sessions`).' },
|
|
169
|
+
},
|
|
170
|
+
required: ['table'],
|
|
171
|
+
},
|
|
172
|
+
annotations: { title: 'Check a ProjectKeeper table', ...READ_ONLY },
|
|
173
|
+
call: guarded(async (args) => {
|
|
174
|
+
const table = stringArg(args, 'table');
|
|
175
|
+
if (!table) return text(['Give the table file to check.'], true);
|
|
176
|
+
const project = stringArg(args, 'project');
|
|
177
|
+
const sessions = stringArg(args, 'sessions');
|
|
178
|
+
const tableFile = resolve(defaults.cwd, table);
|
|
179
|
+
const problems = checkTableFile(tableFile, {
|
|
180
|
+
projectDir: project ? resolve(defaults.cwd, project) : undefined,
|
|
181
|
+
sessions: sessions ? readSessionMaterial(resolve(defaults.cwd, sessions)) : null,
|
|
182
|
+
});
|
|
183
|
+
return text(tableCheckReport(tableFile, problems), problems.length > 0);
|
|
184
|
+
}),
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
}
|
package/src/model/ids.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
/** Time-ordered random id with a collection prefix, e.g. `note_m1k3…`. */
|
|
4
|
+
export function newId(prefix: string): string {
|
|
5
|
+
const time = Date.now().toString(36);
|
|
6
|
+
const random = randomBytes(4).toString('hex');
|
|
7
|
+
return `${prefix}_${time}${random}`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Deterministic id from stable parts: re-scanning the same anchor yields the same id. */
|
|
11
|
+
export function stableId(prefix: string, ...parts: readonly (string | number | null | undefined)[]): string {
|
|
12
|
+
const hash = createHash('sha1').update(parts.map((p) => String(p ?? '')).join(String.fromCharCode(0))).digest('hex');
|
|
13
|
+
return `${prefix}_${hash.slice(0, 16)}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function fingerprint(text: string | Buffer): string {
|
|
17
|
+
return `sha256:${createHash('sha256').update(text).digest('hex')}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function slug(text: string): string {
|
|
21
|
+
const s = text.toLowerCase().replace(/[^a-z0-9一-鿿]+/g, '-').replace(/^-+|-+$/g, '');
|
|
22
|
+
return s.length > 0 ? s.slice(0, 40) : 'project';
|
|
23
|
+
}
|