portable-agent-layer 0.34.0 → 0.36.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/README.md +1 -1
- package/assets/skills/presentation/SKILL.md +2 -0
- package/assets/skills/presentation/demo/slides/004-content.md +27 -1
- package/assets/skills/presentation/theme-base/base.css +206 -0
- package/assets/skills/presentation/theme-base/skeleton.html +49 -0
- package/assets/skills/presentation/tools/lib/lint-rules.ts +25 -0
- package/assets/skills/projects/SKILL.md +0 -1
- package/assets/skills/telos/SKILL.md +7 -52
- package/assets/templates/AGENTS.md.template +2 -1
- package/assets/templates/PAL/ALGORITHM.md +28 -3
- package/assets/templates/PAL/PROJECT_LIFECYCLE.md +48 -0
- package/assets/templates/PAL/README.md +1 -1
- package/assets/templates/PAL/STEERING_RULES.md +4 -0
- package/assets/templates/PAL/SYSTEM_ARCHITECTURE.md +32 -17
- package/assets/templates/PAL/WORK_TRACKING.md +1 -1
- package/assets/templates/pal-settings.json +1 -3
- package/assets/templates/settings.claude.json +2 -1
- package/package.json +1 -1
- package/src/cli/setup-telos.ts +12 -79
- package/src/hooks/LoadContext.ts +22 -10
- package/src/hooks/handlers/context-digests.ts +74 -0
- package/src/hooks/handlers/session-intelligence.ts +9 -86
- package/src/hooks/lib/claude-md.ts +69 -14
- package/src/hooks/lib/context.ts +57 -139
- package/src/hooks/lib/relationship.ts +3 -3
- package/src/hooks/lib/security.ts +2 -0
- package/src/hooks/lib/semi-static.ts +186 -0
- package/src/hooks/lib/setup.ts +0 -5
- package/src/hooks/lib/stop.ts +3 -0
- package/src/targets/claude/uninstall.ts +1 -1
- package/src/targets/copilot/install.ts +39 -8
- package/src/targets/copilot/uninstall.ts +58 -17
- package/src/targets/cursor/install.ts +8 -0
- package/src/targets/cursor/uninstall.ts +18 -1
- package/src/targets/lib.ts +26 -0
- package/src/targets/opencode/install.ts +29 -1
- package/src/targets/opencode/plugin.ts +1 -1
- package/src/targets/opencode/uninstall.ts +30 -3
- package/src/tools/agent/handoff-note.ts +116 -0
- package/src/tools/agent/relationship-note.ts +51 -0
- package/src/tools/relationship-reflect.ts +2 -2
- package/src/tools/self-model.ts +4 -4
- package/assets/templates/telos/PROJECTS.md +0 -7
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PAL — Copilot uninstaller
|
|
3
|
-
* Removes pal-hooks.json, skill symlinks, agents,
|
|
3
|
+
* Removes pal-hooks.json, skill symlinks, agents, instruction files,
|
|
4
|
+
* and the VS Code chat.instructionsFilesLocations entry.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
import { copyFileSync, existsSync, lstatSync, readlinkSync, unlinkSync } from "node:fs";
|
|
7
8
|
import { resolve } from "node:path";
|
|
8
9
|
import { platform } from "../../hooks/lib/paths";
|
|
9
|
-
import {
|
|
10
|
+
import { copilotFilename, getSemiStaticSources } from "../../hooks/lib/semi-static";
|
|
11
|
+
import {
|
|
12
|
+
log,
|
|
13
|
+
readJson,
|
|
14
|
+
removeAgentsFromCopilot,
|
|
15
|
+
removePalDocs,
|
|
16
|
+
removeSkills,
|
|
17
|
+
vscodeSettingsFile,
|
|
18
|
+
writeJson,
|
|
19
|
+
} from "../lib";
|
|
10
20
|
|
|
11
21
|
const COPILOT_DIR = platform.copilotDir();
|
|
12
22
|
const HOOKS_FILE = resolve(COPILOT_DIR, "hooks", "pal-hooks.json");
|
|
@@ -35,26 +45,57 @@ if (removedAgents.length > 0) {
|
|
|
35
45
|
log.success(`Removed ${removedAgents.length} agent(s): ${removedAgents.join(", ")}`);
|
|
36
46
|
}
|
|
37
47
|
|
|
38
|
-
// --- Remove
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
// --- Remove PAL docs ---
|
|
49
|
+
removePalDocs();
|
|
50
|
+
|
|
51
|
+
// --- Remove ~/.copilot/instructions/pal-*.instructions.md ---
|
|
52
|
+
for (const src of getSemiStaticSources()) {
|
|
53
|
+
try {
|
|
54
|
+
unlinkSync(resolve(COPILOT_DIR, "instructions", copilotFilename(src)));
|
|
55
|
+
} catch {
|
|
56
|
+
/* gone */
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// pal-session.instructions.md is written by LoadContext (not a semi-static source)
|
|
60
|
+
try {
|
|
61
|
+
unlinkSync(resolve(COPILOT_DIR, "instructions", "pal-session.instructions.md"));
|
|
62
|
+
} catch {
|
|
63
|
+
/* gone */
|
|
64
|
+
}
|
|
65
|
+
log.success("Removed ~/.copilot/instructions/pal-*.instructions.md");
|
|
66
|
+
|
|
67
|
+
// --- Backward compat: remove old copilot-instructions.md symlink if present ---
|
|
68
|
+
const legacyPath = resolve(COPILOT_DIR, "copilot-instructions.md");
|
|
69
|
+
if (existsSync(legacyPath)) {
|
|
41
70
|
try {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
} else {
|
|
49
|
-
log.info("copilot-instructions.md is not a PAL symlink — leaving it");
|
|
50
|
-
}
|
|
71
|
+
if (
|
|
72
|
+
lstatSync(legacyPath).isSymbolicLink() &&
|
|
73
|
+
readlinkSync(legacyPath).includes("AGENTS.md")
|
|
74
|
+
) {
|
|
75
|
+
unlinkSync(legacyPath);
|
|
76
|
+
log.success("Removed legacy copilot-instructions.md symlink");
|
|
51
77
|
}
|
|
52
78
|
} catch {
|
|
53
|
-
|
|
79
|
+
/* ignore */
|
|
54
80
|
}
|
|
55
81
|
}
|
|
56
82
|
|
|
57
|
-
// --- Remove
|
|
58
|
-
|
|
83
|
+
// --- Remove ~/.copilot/instructions entry from VS Code settings ---
|
|
84
|
+
const vsSettingsPath = vscodeSettingsFile();
|
|
85
|
+
if (vsSettingsPath && existsSync(vsSettingsPath)) {
|
|
86
|
+
const settings = readJson<Record<string, unknown>>(vsSettingsPath, {});
|
|
87
|
+
const locs = settings["chat.instructionsFilesLocations"];
|
|
88
|
+
if (typeof locs === "object" && locs !== null) {
|
|
89
|
+
const obj = locs as Record<string, unknown>;
|
|
90
|
+
delete obj["~/.copilot/instructions"];
|
|
91
|
+
if (Object.keys(obj).length === 0) {
|
|
92
|
+
delete settings["chat.instructionsFilesLocations"];
|
|
93
|
+
} else {
|
|
94
|
+
settings["chat.instructionsFilesLocations"] = obj;
|
|
95
|
+
}
|
|
96
|
+
writeJson(vsSettingsPath, settings);
|
|
97
|
+
log.success("Removed ~/.copilot/instructions from VS Code settings");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
59
100
|
|
|
60
101
|
log.success("Copilot uninstall complete");
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { copyFileSync, existsSync, mkdirSync } from "node:fs";
|
|
8
8
|
import { resolve } from "node:path";
|
|
9
|
+
import { writeContextDigests } from "../../hooks/handlers/context-digests";
|
|
9
10
|
import { regenerateIfNeeded } from "../../hooks/lib/claude-md";
|
|
10
11
|
import { assets, palPkg, platform } from "../../hooks/lib/paths";
|
|
11
12
|
import {
|
|
@@ -64,6 +65,13 @@ scaffoldPalSettings();
|
|
|
64
65
|
regenerateIfNeeded();
|
|
65
66
|
log.success("Generated AGENTS.md");
|
|
66
67
|
|
|
68
|
+
// --- Write ~/.cursor/rules/pal-*.mdc ---
|
|
69
|
+
mkdirSync(resolve(CURSOR_DIR, "rules"), { recursive: true });
|
|
70
|
+
writeContextDigests();
|
|
71
|
+
log.success(
|
|
72
|
+
"Written ~/.cursor/rules/pal-self-model.mdc + pal-wisdom.mdc + pal-opinions.mdc"
|
|
73
|
+
);
|
|
74
|
+
|
|
67
75
|
log.success("Cursor installation complete");
|
|
68
76
|
console.log("");
|
|
69
77
|
log.info(`Skills: ${countSkills()}`);
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* Removes PAL skill symlinks.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { copyFileSync, existsSync } from "node:fs";
|
|
7
|
+
import { copyFileSync, existsSync, unlinkSync } from "node:fs";
|
|
8
8
|
import { resolve } from "node:path";
|
|
9
9
|
import { assets, palPkg, platform } from "../../hooks/lib/paths";
|
|
10
|
+
import { cursorFilename, getSemiStaticSources } from "../../hooks/lib/semi-static";
|
|
10
11
|
import {
|
|
11
12
|
loadCursorHooksTemplate,
|
|
12
13
|
log,
|
|
@@ -56,4 +57,20 @@ if (removedAgents.length > 0) {
|
|
|
56
57
|
// --- Remove PAL system docs ---
|
|
57
58
|
removePalDocs();
|
|
58
59
|
|
|
60
|
+
// --- Remove ~/.cursor/rules/pal-*.mdc ---
|
|
61
|
+
for (const src of getSemiStaticSources()) {
|
|
62
|
+
try {
|
|
63
|
+
unlinkSync(resolve(CURSOR_DIR, "rules", cursorFilename(src)));
|
|
64
|
+
} catch {
|
|
65
|
+
/* gone */
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Backward compat: remove legacy merged file if present
|
|
69
|
+
try {
|
|
70
|
+
unlinkSync(resolve(CURSOR_DIR, "rules", "pal-context.mdc"));
|
|
71
|
+
} catch {
|
|
72
|
+
/* gone */
|
|
73
|
+
}
|
|
74
|
+
log.success("Removed ~/.cursor/rules/pal-*.mdc");
|
|
75
|
+
|
|
59
76
|
log.success("Cursor uninstall complete");
|
package/src/targets/lib.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
unlinkSync,
|
|
15
15
|
writeFileSync,
|
|
16
16
|
} from "node:fs";
|
|
17
|
+
import { homedir } from "node:os";
|
|
17
18
|
import { resolve } from "node:path";
|
|
18
19
|
import { assets, palHome, platform } from "../hooks/lib/paths";
|
|
19
20
|
|
|
@@ -41,6 +42,31 @@ export function writeJson(path: string, data: unknown): void {
|
|
|
41
42
|
writeFileSync(path, `${JSON.stringify(data, null, 2)}\n`, "utf-8");
|
|
42
43
|
}
|
|
43
44
|
|
|
45
|
+
/** Resolve the VS Code user settings.json path cross-platform. Returns null on unknown platforms. */
|
|
46
|
+
export function vscodeSettingsFile(): string | null {
|
|
47
|
+
const h = homedir();
|
|
48
|
+
if (process.platform === "darwin") {
|
|
49
|
+
return resolve(h, "Library", "Application Support", "Code", "User", "settings.json");
|
|
50
|
+
}
|
|
51
|
+
if (process.platform === "linux") {
|
|
52
|
+
return resolve(
|
|
53
|
+
process.env.XDG_CONFIG_HOME ?? resolve(h, ".config"),
|
|
54
|
+
"Code",
|
|
55
|
+
"User",
|
|
56
|
+
"settings.json"
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
if (process.platform === "win32") {
|
|
60
|
+
return resolve(
|
|
61
|
+
process.env.APPDATA ?? resolve(h, "AppData", "Roaming"),
|
|
62
|
+
"Code",
|
|
63
|
+
"User",
|
|
64
|
+
"settings.json"
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
44
70
|
// --- Settings template merge/unmerge ---
|
|
45
71
|
|
|
46
72
|
type HookEntry = { matcher?: string; hooks?: Array<{ type: string; command: string }> };
|
|
@@ -3,10 +3,18 @@
|
|
|
3
3
|
* Deploys plugin, installs skills, generates AGENTS.md.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
existsSync,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
statSync,
|
|
11
|
+
unlinkSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
} from "node:fs";
|
|
7
14
|
import { resolve } from "node:path";
|
|
8
15
|
import { regenerateIfNeeded } from "../../hooks/lib/claude-md";
|
|
9
16
|
import { palPkg, platform } from "../../hooks/lib/paths";
|
|
17
|
+
import { getSemiStaticSources } from "../../hooks/lib/semi-static";
|
|
10
18
|
import {
|
|
11
19
|
copyAgentsForOpencode,
|
|
12
20
|
copyPalDocs,
|
|
@@ -70,6 +78,26 @@ log.success(`Installed ${palDocsCount} PAL docs to ~/.pal/docs/`);
|
|
|
70
78
|
regenerateIfNeeded();
|
|
71
79
|
log.success("Generated ~/.config/opencode/AGENTS.md");
|
|
72
80
|
|
|
81
|
+
// --- 7. Add semi-static digest files to instructions[] in config.json ---
|
|
82
|
+
const configPath = resolve(OC_GLOBAL_DIR, "config.json");
|
|
83
|
+
const staticFiles = getSemiStaticSources().map((s) => s.path);
|
|
84
|
+
let ocConfig: Record<string, unknown> = {};
|
|
85
|
+
if (existsSync(configPath) && statSync(configPath).size > 0) {
|
|
86
|
+
try {
|
|
87
|
+
ocConfig = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
88
|
+
} catch {
|
|
89
|
+
/* start fresh */
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const existingInstructions = Array.isArray(ocConfig.instructions)
|
|
93
|
+
? (ocConfig.instructions as string[])
|
|
94
|
+
: [];
|
|
95
|
+
ocConfig.instructions = [...new Set([...existingInstructions, ...staticFiles])];
|
|
96
|
+
writeFileSync(configPath, `${JSON.stringify(ocConfig, null, 2)}\n`, "utf-8");
|
|
97
|
+
log.success(
|
|
98
|
+
`Updated config.json: ${(ocConfig.instructions as string[]).length} instructions`
|
|
99
|
+
);
|
|
100
|
+
|
|
73
101
|
log.success("opencode installation complete");
|
|
74
102
|
console.log("");
|
|
75
103
|
log.info(`Plugin: ${pluginDst}`);
|
|
@@ -74,7 +74,7 @@ const PALPlugin: Plugin = async ({ directory, client }: PluginInput) => {
|
|
|
74
74
|
return {
|
|
75
75
|
// --- Per-message: Inject dynamic system reminder ---
|
|
76
76
|
"experimental.chat.system.transform": async (_input, output) => {
|
|
77
|
-
const reminder = buildSystemReminder();
|
|
77
|
+
const reminder = buildSystemReminder({ agent: "opencode" });
|
|
78
78
|
if (reminder) output.system.push(reminder);
|
|
79
79
|
},
|
|
80
80
|
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PAL — opencode uninstaller (TypeScript)
|
|
3
|
-
* Removes plugin
|
|
3
|
+
* Removes plugin, AGENTS.md, and PAL entries from config.json.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { unlinkSync } from "node:fs";
|
|
6
|
+
import { existsSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { resolve } from "node:path";
|
|
8
8
|
import { platform } from "../../hooks/lib/paths";
|
|
9
|
+
import { getSemiStaticSources } from "../../hooks/lib/semi-static";
|
|
9
10
|
import { log, removeAgentsFromOpencode, removePalDocs, removeSkills } from "../lib";
|
|
10
11
|
|
|
11
12
|
const OC_GLOBAL_DIR = platform.opencodeDir() || "";
|
|
@@ -41,7 +42,7 @@ if (removedAgents.length > 0)
|
|
|
41
42
|
// --- Remove PAL system docs ---
|
|
42
43
|
removePalDocs();
|
|
43
44
|
|
|
44
|
-
// --- Remove AGENTS.md and CLAUDE.md
|
|
45
|
+
// --- Remove AGENTS.md and CLAUDE.md ---
|
|
45
46
|
const agentsMd = resolve(OC_GLOBAL_DIR, "AGENTS.md");
|
|
46
47
|
const claudeMd = resolve(PAL_CLAUDE_DIR, "CLAUDE.md");
|
|
47
48
|
try {
|
|
@@ -57,4 +58,30 @@ try {
|
|
|
57
58
|
/* gone */
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
// --- Remove PAL entries from config.json instructions[] ---
|
|
62
|
+
const configPath = resolve(OC_GLOBAL_DIR, "config.json");
|
|
63
|
+
if (existsSync(configPath) && statSync(configPath).size > 0) {
|
|
64
|
+
try {
|
|
65
|
+
const ocConfig = JSON.parse(readFileSync(configPath, "utf-8")) as Record<
|
|
66
|
+
string,
|
|
67
|
+
unknown
|
|
68
|
+
>;
|
|
69
|
+
if (Array.isArray(ocConfig.instructions)) {
|
|
70
|
+
const palFiles = new Set(getSemiStaticSources().map((s) => s.path));
|
|
71
|
+
const filtered = (ocConfig.instructions as string[]).filter(
|
|
72
|
+
(p) => !palFiles.has(p)
|
|
73
|
+
);
|
|
74
|
+
if (filtered.length === 0) {
|
|
75
|
+
delete ocConfig.instructions;
|
|
76
|
+
} else {
|
|
77
|
+
ocConfig.instructions = filtered;
|
|
78
|
+
}
|
|
79
|
+
writeFileSync(configPath, `${JSON.stringify(ocConfig, null, 2)}\n`, "utf-8");
|
|
80
|
+
log.success("Removed PAL entries from config.json instructions[]");
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
log.warn("Could not clean config.json instructions[] — check manually");
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
60
87
|
log.success("opencode uninstall complete");
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* HandoffNote — Write or clear a handoff note for the current project.
|
|
4
|
+
*
|
|
5
|
+
* Called in the ALGORITHM LEARN phase when work is unfinished.
|
|
6
|
+
* Written by Claude in-session — no inference call needed.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* bun ~/.pal/tools/handoff-note.ts --title "what we were doing" --text "what remains + next steps"
|
|
10
|
+
* bun ~/.pal/tools/handoff-note.ts --done # mark completed, suppress next-session injection
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
14
|
+
import { resolve } from "node:path";
|
|
15
|
+
import { parseArgs } from "node:util";
|
|
16
|
+
import { ensureDir, paths } from "../../hooks/lib/paths";
|
|
17
|
+
|
|
18
|
+
interface HandoffEntry {
|
|
19
|
+
timestamp: string;
|
|
20
|
+
title: string;
|
|
21
|
+
status: "in-progress" | "completed";
|
|
22
|
+
handoff: string;
|
|
23
|
+
artifacts: string[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function handoffPath(): string {
|
|
27
|
+
return resolve(ensureDir(paths.state()), "last-handoff.json");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function readHandoffs(): Record<string, HandoffEntry> {
|
|
31
|
+
const p = handoffPath();
|
|
32
|
+
if (!existsSync(p)) return {};
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(readFileSync(p, "utf-8"));
|
|
35
|
+
} catch {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function writeHandoffs(handoffs: Record<string, HandoffEntry>): void {
|
|
41
|
+
const entries = Object.entries(handoffs);
|
|
42
|
+
const trimmed = entries.length > 20 ? Object.fromEntries(entries.slice(-20)) : handoffs;
|
|
43
|
+
writeFileSync(handoffPath(), JSON.stringify(trimmed, null, 2), "utf-8");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function writeHandoffNote(
|
|
47
|
+
cwd: string,
|
|
48
|
+
title: string,
|
|
49
|
+
text: string,
|
|
50
|
+
done: boolean
|
|
51
|
+
): { success: boolean; message: string } {
|
|
52
|
+
const handoffs = readHandoffs();
|
|
53
|
+
handoffs[cwd] = {
|
|
54
|
+
timestamp: new Date().toISOString(),
|
|
55
|
+
title,
|
|
56
|
+
status: done ? "completed" : "in-progress",
|
|
57
|
+
handoff: text,
|
|
58
|
+
artifacts: [],
|
|
59
|
+
};
|
|
60
|
+
writeHandoffs(handoffs);
|
|
61
|
+
return {
|
|
62
|
+
success: true,
|
|
63
|
+
message: done ? "Handoff cleared (marked completed)" : "Handoff note written",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function run() {
|
|
68
|
+
const { values } = parseArgs({
|
|
69
|
+
args: Bun.argv.slice(2),
|
|
70
|
+
options: {
|
|
71
|
+
title: { type: "string" },
|
|
72
|
+
text: { type: "string" },
|
|
73
|
+
done: { type: "boolean" },
|
|
74
|
+
help: { type: "boolean", short: "h" },
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
if (values.help) {
|
|
79
|
+
console.log(`
|
|
80
|
+
HandoffNote — Write a handoff note for the current project
|
|
81
|
+
|
|
82
|
+
Usage:
|
|
83
|
+
bun ~/.pal/tools/handoff-note.ts --title "what we were doing" --text "what remains"
|
|
84
|
+
bun ~/.pal/tools/handoff-note.ts --done # mark session completed
|
|
85
|
+
|
|
86
|
+
Arguments:
|
|
87
|
+
--title Brief title of what was being worked on (5-10 words)
|
|
88
|
+
--text What remains unfinished — decisions made, next steps, blockers
|
|
89
|
+
--done Mark as completed; suppresses "pick up where you left off" injection
|
|
90
|
+
|
|
91
|
+
Output: writes to memory/state/last-handoff.json keyed by cwd
|
|
92
|
+
`);
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (values.done) {
|
|
97
|
+
const result = writeHandoffNote(
|
|
98
|
+
process.cwd(),
|
|
99
|
+
values.title || "session",
|
|
100
|
+
values.text || "",
|
|
101
|
+
true
|
|
102
|
+
);
|
|
103
|
+
console.log(JSON.stringify(result, null, 2));
|
|
104
|
+
process.exit(0);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!values.title || !values.text) {
|
|
108
|
+
console.error("Required: --title and --text (or --done to close)");
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const result = writeHandoffNote(process.cwd(), values.title, values.text, false);
|
|
113
|
+
console.log(JSON.stringify(result, null, 2));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (import.meta.main) run();
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* RelationshipNote — Write a B entry to today's relationship log.
|
|
4
|
+
*
|
|
5
|
+
* Called in the ALGORITHM LEARN phase. Claude writes the B entry directly
|
|
6
|
+
* from full session context — no inference call needed.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* bun ~/.pal/tools/relationship-note.ts --b "what I did this session"
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { parseArgs } from "node:util";
|
|
13
|
+
import { appendNotes } from "../../hooks/lib/relationship";
|
|
14
|
+
|
|
15
|
+
function run() {
|
|
16
|
+
const { values } = parseArgs({
|
|
17
|
+
args: Bun.argv.slice(2),
|
|
18
|
+
options: {
|
|
19
|
+
b: { type: "string" },
|
|
20
|
+
help: { type: "boolean", short: "h" },
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (values.help) {
|
|
25
|
+
console.log(`
|
|
26
|
+
RelationshipNote — Append a B entry to today's relationship log
|
|
27
|
+
|
|
28
|
+
Usage:
|
|
29
|
+
bun ~/.pal/tools/relationship-note.ts --b "description"
|
|
30
|
+
|
|
31
|
+
Arguments:
|
|
32
|
+
--b What happened this session (1-2 sentences, first-person, specific)
|
|
33
|
+
|
|
34
|
+
Output: appends to memory/relationship/YYYY-MM/YYYY-MM-DD.md
|
|
35
|
+
`);
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!values.b) {
|
|
40
|
+
console.error("Required: --b");
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
appendNotes([{ type: "Session", text: values.b }]);
|
|
45
|
+
|
|
46
|
+
console.log(
|
|
47
|
+
JSON.stringify({ success: true, message: "Relationship note written" }, null, 2)
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (import.meta.main) run();
|
|
@@ -37,7 +37,7 @@ interface Rating {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
interface ParsedNote {
|
|
40
|
-
type: "W" | "O" | "
|
|
40
|
+
type: "W" | "O" | "Session";
|
|
41
41
|
text: string;
|
|
42
42
|
confidence?: number;
|
|
43
43
|
date: string;
|
|
@@ -94,7 +94,7 @@ export function loadNotes(daysBack: number): ParsedNote[] {
|
|
|
94
94
|
const obMatch = line.match(/^- ([OB])\(c=([\d.]+)\):\s*(.+)$/);
|
|
95
95
|
if (obMatch) {
|
|
96
96
|
notes.push({
|
|
97
|
-
type: obMatch[1] as "O" | "
|
|
97
|
+
type: obMatch[1] as "O" | "Session",
|
|
98
98
|
confidence: Number.parseFloat(obMatch[2]),
|
|
99
99
|
text: obMatch[3],
|
|
100
100
|
date: dateStr,
|
package/src/tools/self-model.ts
CHANGED
|
@@ -71,7 +71,7 @@ interface AlgorithmReflection {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
interface RelationshipNote {
|
|
74
|
-
type: "O" | "W" | "
|
|
74
|
+
type: "O" | "W" | "Session";
|
|
75
75
|
content: string;
|
|
76
76
|
confidence?: number;
|
|
77
77
|
date: string;
|
|
@@ -292,9 +292,9 @@ function readRelationshipNotes(since: Date): RelationshipNote[] {
|
|
|
292
292
|
continue;
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
const behaviorMatch = noteContent.match(/^
|
|
295
|
+
const behaviorMatch = noteContent.match(/^Session:\s*(.+)$/);
|
|
296
296
|
if (behaviorMatch) {
|
|
297
|
-
notes.push({ type: "
|
|
297
|
+
notes.push({ type: "Session", content: behaviorMatch[1], date: dateStr });
|
|
298
298
|
}
|
|
299
299
|
}
|
|
300
300
|
}
|
|
@@ -376,7 +376,7 @@ function gatherData(days: number): SelfModelData {
|
|
|
376
376
|
wisdomFrames,
|
|
377
377
|
graduated,
|
|
378
378
|
reflections,
|
|
379
|
-
behaviorNotes: relNotes.filter((n) => n.type === "
|
|
379
|
+
behaviorNotes: relNotes.filter((n) => n.type === "Session").map((n) => n.content),
|
|
380
380
|
wisdomNotes: relNotes.filter((n) => n.type === "W").map((n) => n.content),
|
|
381
381
|
selfObservations: reflections.map((r) => r.q1).filter(Boolean),
|
|
382
382
|
algorithmObservations: reflections.map((r) => r.q2).filter(Boolean),
|