portable-agent-layer 0.35.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +2 -1
  2. package/assets/skills/analyze-pdf/tools/pdf-download.ts +1 -1
  3. package/assets/skills/analyze-youtube/tools/youtube-analyze.ts +1 -1
  4. package/assets/skills/consulting-report/tools/dev.ts +2 -2
  5. package/assets/skills/consulting-report/tools/generate-pdf.ts +9 -9
  6. package/assets/skills/consulting-report/tools/scaffold.ts +2 -2
  7. package/assets/skills/create-pdf/tools/md-to-html-pdf.ts +2 -2
  8. package/assets/skills/opinion/tools/opinion.ts +3 -2
  9. package/assets/skills/presentation/SKILL.md +1 -1
  10. package/assets/skills/presentation/tools/doctor.ts +2 -5
  11. package/assets/skills/presentation/tools/lib/inline.ts +6 -11
  12. package/assets/skills/presentation/tools/lib/lint-helpers.ts +2 -2
  13. package/assets/skills/presentation/tools/lib/lint-rules.ts +5 -2
  14. package/assets/skills/presentation/tools/setup-template.ts +10 -7
  15. package/assets/skills/projects/SKILL.md +44 -21
  16. package/assets/skills/research/tools/gemini-search.ts +2 -2
  17. package/assets/skills/research/tools/grok-search.ts +2 -2
  18. package/assets/skills/research/tools/perplexity-search.ts +2 -2
  19. package/assets/skills/telos/SKILL.md +7 -52
  20. package/assets/skills/telos/tools/update-telos.ts +0 -1
  21. package/assets/templates/PAL/ALGORITHM.md +54 -5
  22. package/assets/templates/PAL/PROJECT_LIFECYCLE.md +48 -0
  23. package/assets/templates/PAL/README.md +1 -1
  24. package/assets/templates/PAL/STEERING_RULES.md +4 -0
  25. package/assets/templates/PAL/SYSTEM_ARCHITECTURE.md +32 -17
  26. package/assets/templates/PAL/WORK_TRACKING.md +1 -1
  27. package/assets/templates/hooks.codex.json +44 -0
  28. package/assets/templates/hooks.cursor.json +11 -5
  29. package/assets/templates/pal-settings.json +1 -3
  30. package/assets/templates/settings.claude.json +2 -1
  31. package/package.json +2 -1
  32. package/src/cli/index.ts +112 -14
  33. package/src/cli/migrate.ts +299 -0
  34. package/src/cli/setup-identity.ts +3 -3
  35. package/src/cli/setup-telos.ts +12 -80
  36. package/src/hooks/CompactRecover.ts +11 -5
  37. package/src/hooks/LoadContext.ts +35 -11
  38. package/src/hooks/PreCompactPersist.ts +26 -34
  39. package/src/hooks/SecurityValidator.ts +43 -21
  40. package/src/hooks/StopOrchestrator.ts +4 -1
  41. package/src/hooks/UserPromptOrchestrator.ts +4 -2
  42. package/src/hooks/handlers/auto-graduate.ts +2 -2
  43. package/src/hooks/handlers/backup.ts +3 -3
  44. package/src/hooks/handlers/context-digests.ts +74 -0
  45. package/src/hooks/handlers/failure.ts +5 -3
  46. package/src/hooks/handlers/inject-retrieval.ts +29 -6
  47. package/src/hooks/handlers/persist-last-exchange.ts +76 -0
  48. package/src/hooks/handlers/rating.ts +2 -1
  49. package/src/hooks/handlers/readme-sync.ts +3 -2
  50. package/src/hooks/handlers/session-intelligence.ts +17 -93
  51. package/src/hooks/handlers/session-name.ts +2 -2
  52. package/src/hooks/handlers/synthesis.ts +5 -2
  53. package/src/hooks/handlers/update-counts.ts +3 -2
  54. package/src/hooks/lib/agent.ts +20 -18
  55. package/src/hooks/lib/claude-md.ts +69 -14
  56. package/src/hooks/lib/context.ts +92 -246
  57. package/src/hooks/lib/entities.ts +7 -7
  58. package/src/hooks/lib/frontmatter.ts +4 -4
  59. package/src/hooks/lib/graduation.ts +7 -6
  60. package/src/hooks/lib/inference.ts +6 -2
  61. package/src/hooks/lib/learning-category.ts +1 -1
  62. package/src/hooks/lib/learning-store.ts +6 -1
  63. package/src/hooks/lib/notify.ts +2 -2
  64. package/src/hooks/lib/opinions.ts +3 -3
  65. package/src/hooks/lib/paths.ts +2 -0
  66. package/src/hooks/lib/projects.ts +142 -74
  67. package/src/hooks/lib/readme-sync.ts +1 -1
  68. package/src/hooks/lib/relationship.ts +4 -16
  69. package/src/hooks/lib/retrieval-index.ts +5 -3
  70. package/src/hooks/lib/retrieval.ts +11 -12
  71. package/src/hooks/lib/security.ts +24 -18
  72. package/src/hooks/lib/semi-static.ts +188 -0
  73. package/src/hooks/lib/session-names.ts +1 -1
  74. package/src/hooks/lib/settings.ts +1 -1
  75. package/src/hooks/lib/setup.ts +2 -65
  76. package/src/hooks/lib/signals.ts +2 -2
  77. package/src/hooks/lib/stdin.ts +1 -1
  78. package/src/hooks/lib/stop.ts +16 -6
  79. package/src/hooks/lib/token-usage.ts +1 -2
  80. package/src/hooks/lib/transcript.ts +1 -1
  81. package/src/hooks/lib/wisdom.ts +5 -5
  82. package/src/hooks/lib/work-tracking.ts +8 -14
  83. package/src/targets/claude/uninstall.ts +1 -1
  84. package/src/targets/codex/install.ts +95 -0
  85. package/src/targets/codex/uninstall.ts +70 -0
  86. package/src/targets/copilot/install.ts +39 -8
  87. package/src/targets/copilot/uninstall.ts +58 -17
  88. package/src/targets/cursor/install.ts +8 -0
  89. package/src/targets/cursor/uninstall.ts +18 -1
  90. package/src/targets/lib.ts +166 -14
  91. package/src/targets/opencode/install.ts +29 -1
  92. package/src/targets/opencode/plugin.ts +23 -12
  93. package/src/targets/opencode/uninstall.ts +30 -3
  94. package/src/tools/agent/algorithm-reflect.ts +1 -1
  95. package/src/tools/agent/analyze.ts +18 -18
  96. package/src/tools/agent/handoff-note.ts +116 -0
  97. package/src/tools/agent/project.ts +375 -75
  98. package/src/tools/agent/relationship-note.ts +51 -0
  99. package/src/tools/agent/synthesize.ts +6 -42
  100. package/src/tools/agent/thread.ts +15 -14
  101. package/src/tools/agent/wisdom-frame.ts +9 -3
  102. package/src/tools/import.ts +1 -1
  103. package/src/tools/relationship-reflect.ts +15 -13
  104. package/src/tools/self-model.ts +23 -19
  105. package/src/tools/session-summary.ts +3 -3
  106. package/src/tools/token-cost.ts +15 -16
  107. package/assets/skills/telos/tools/update-projects.ts +0 -106
  108. package/assets/templates/telos/PROJECTS.md +0 -7
package/README.md CHANGED
@@ -82,6 +82,7 @@ pal cli status # check your setup
82
82
  | `pal cli import` | Import user state from a zip |
83
83
  | `pal cli status` | Show current PAL configuration |
84
84
  | `pal cli doctor` | Check prerequisites and system health |
85
+ | `pal cli migrate` | Run pending data migrations (non-destructive) |
85
86
  | `pal cli usage` | Summarize token usage and estimated cost |
86
87
 
87
88
  ### Target flags
@@ -102,7 +103,7 @@ pal cli install # all available (default)
102
103
  | Claude Code | Full | Yes | Yes | Yes | Yes |
103
104
  | opencode | Full | Yes | Yes (plugin) | Yes | Yes |
104
105
  | Cursor | Full | Yes | Yes | Yes (injected via hook) | Yes |
105
- | GitHub Copilot | Full | Yes | Yes | Yes (via copilot-instructions.md) | Yes |
106
+ | GitHub Copilot | Full | Yes | Yes | Yes (via `~/.copilot/instructions/*.instructions.md`) | Yes |
106
107
  | Codex | Partial | Yes | No | Yes | No |
107
108
 
108
109
  ---
@@ -99,4 +99,4 @@ async function main() {
99
99
  console.log(JSON.stringify(result, null, 2));
100
100
  }
101
101
 
102
- main();
102
+ void main();
@@ -102,4 +102,4 @@ async function main() {
102
102
  console.log(text);
103
103
  }
104
104
 
105
- main();
105
+ void main();
@@ -19,7 +19,7 @@ async function exists(p: string): Promise<boolean> {
19
19
  }
20
20
  }
21
21
 
22
- export async function dev(reportDir: string): Promise<number> {
22
+ async function dev(reportDir: string): Promise<number> {
23
23
  const dir = resolve(reportDir);
24
24
  const pkg = join(dir, "package.json");
25
25
  if (!(await exists(pkg))) {
@@ -33,7 +33,7 @@ export async function dev(reportDir: string): Promise<number> {
33
33
  return result.status ?? 1;
34
34
  }
35
35
 
36
- export async function run(argv: string[] = process.argv.slice(2)): Promise<void> {
36
+ async function run(argv: string[] = process.argv.slice(2)): Promise<void> {
37
37
  if (argv.length === 0) {
38
38
  console.error("usage: dev.ts <report-dir>");
39
39
  process.exit(1);
@@ -43,10 +43,10 @@ async function exists(p: string): Promise<boolean> {
43
43
 
44
44
  function escapeHtml(s: string): string {
45
45
  return s
46
- .replace(/&/g, "&amp;")
47
- .replace(/</g, "&lt;")
48
- .replace(/>/g, "&gt;")
49
- .replace(/"/g, "&quot;");
46
+ .replaceAll("&", "&amp;")
47
+ .replaceAll("<", "&lt;")
48
+ .replaceAll(">", "&gt;")
49
+ .replaceAll('"', "&quot;");
50
50
  }
51
51
 
52
52
  function slugify(s: string): string {
@@ -56,7 +56,7 @@ function slugify(s: string): string {
56
56
  .replace(/^-|-$/g, "");
57
57
  }
58
58
 
59
- export async function loadMeta(reportDir: string): Promise<ReportMeta> {
59
+ async function loadMeta(reportDir: string): Promise<ReportMeta> {
60
60
  const dataPath = join(reportDir, "lib", "report-data.ts");
61
61
  if (!(await exists(dataPath))) {
62
62
  throw new Error(`lib/report-data.ts not found at ${dataPath}`);
@@ -70,7 +70,7 @@ export async function loadMeta(reportDir: string): Promise<ReportMeta> {
70
70
  return mod.reportData;
71
71
  }
72
72
 
73
- export function buildNext(reportDir: string): void {
73
+ function buildNext(reportDir: string): void {
74
74
  const result = spawnSync("bun", ["run", "build"], {
75
75
  cwd: reportDir,
76
76
  stdio: "inherit",
@@ -121,7 +121,7 @@ function serveStatic(rootDir: string): Promise<{ server: Server; url: string }>
121
121
  });
122
122
  }
123
123
 
124
- export async function renderPdf(
124
+ async function renderPdf(
125
125
  htmlPath: string,
126
126
  pdfPath: string,
127
127
  meta: ReportMeta
@@ -186,7 +186,7 @@ interface GenerateOptions {
186
186
  skipBuild?: boolean;
187
187
  }
188
188
 
189
- export async function generate(opts: GenerateOptions): Promise<{
189
+ async function generate(opts: GenerateOptions): Promise<{
190
190
  htmlPath: string;
191
191
  pdfPath: string;
192
192
  }> {
@@ -227,7 +227,7 @@ function parseArgs(argv: string[]): GenerateOptions {
227
227
  return opts;
228
228
  }
229
229
 
230
- export async function run(argv: string[] = process.argv.slice(2)): Promise<void> {
230
+ async function run(argv: string[] = process.argv.slice(2)): Promise<void> {
231
231
  const opts = parseArgs(argv);
232
232
  const { htmlPath, pdfPath } = await generate(opts);
233
233
  const [htmlStat, pdfStat] = await Promise.all([stat(htmlPath), stat(pdfPath)]);
@@ -34,7 +34,7 @@ function templateDir(): string {
34
34
  return resolve(here, "..", "template");
35
35
  }
36
36
 
37
- export async function scaffold(opts: ScaffoldOptions): Promise<void> {
37
+ async function scaffold(opts: ScaffoldOptions): Promise<void> {
38
38
  const tpl = templateDir();
39
39
  if (!(await exists(tpl))) {
40
40
  throw new Error(`template not found at ${tpl}`);
@@ -91,7 +91,7 @@ function parseArgs(argv: string[]): ScaffoldOptions {
91
91
  return opts;
92
92
  }
93
93
 
94
- export async function run(argv: string[] = process.argv.slice(2)): Promise<void> {
94
+ async function run(argv: string[] = process.argv.slice(2)): Promise<void> {
95
95
  const opts = parseArgs(argv);
96
96
  await scaffold(opts);
97
97
  console.log(`Scaffolded: ${opts.targetDir}`);
@@ -30,8 +30,8 @@ for (let i = 1; i < args.length; i++) {
30
30
  }
31
31
  const stem = basename(input, extname(input));
32
32
  const dir = dirname(input);
33
- if (!htmlOut) htmlOut = resolve(dir, `${stem}.html`);
34
- if (!pdfOut) pdfOut = resolve(dir, `${stem}.pdf`);
33
+ htmlOut ??= resolve(dir, `${stem}.html`);
34
+ pdfOut ??= resolve(dir, `${stem}.pdf`);
35
35
 
36
36
  const md = await readFile(input, "utf8");
37
37
  marked.setOptions({ gfm: true, breaks: false });
@@ -70,8 +70,9 @@ switch (command) {
70
70
  console.log(` ${c.cyan(category)}`);
71
71
  for (const op of ops.sort((a, b) => b.confidence - a.confidence)) {
72
72
  const pct = `${Math.round(op.confidence * 100)}%`;
73
- const color =
74
- op.confidence >= 0.85 ? c.green : op.confidence <= 0.3 ? c.red : c.yellow;
73
+ let color = c.yellow;
74
+ if (op.confidence >= 0.85) color = c.green;
75
+ else if (op.confidence <= 0.3) color = c.red;
75
76
  console.log(` [${bar(op.confidence)}] ${color(pct)} ${op.statement}`);
76
77
  }
77
78
  console.log("");
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: presentation
3
- description: Build branded HTML presentations from markdown using Reveal.js. Multi-template registry per user (each template = brand color, logo, fonts, footer, aspect). Per-deck workflow: scaffold → edit one markdown file per slide in slides/ → build → present. Output: a `<deck-name>/` subdir with a self-contained HTML and a concatenated markdown sibling. 14 layouts including data-display patterns (big-stat, metric-grid). Use when creating slide decks, talks, workshop slides, lectures, or pitch decks.
3
+ description: "Build branded HTML presentations from markdown using Reveal.js. Multi-template registry per user (each template = brand color, logo, fonts, footer, aspect). Per-deck workflow: scaffold → edit one markdown file per slide in slides/ → build → present. Output: a `<deck-name>/` subdir with a self-contained HTML and a concatenated markdown sibling. 14 layouts including data-display patterns (big-stat, metric-grid). Use when creating slide decks, talks, workshop slides, lectures, or pitch decks."
4
4
  argument-hint: <deck-dir> to build, OR `setup-template` to add a brand template, OR `new <deck-dir> --template <name>` to scaffold a deck, OR `list-templates`
5
5
  ---
6
6
 
@@ -23,10 +23,8 @@ import {
23
23
  import { RULES } from "./lib/lint-rules";
24
24
  import type { DeckContext, Finding, SlideContext, SlideReport } from "./lib/lint-types";
25
25
 
26
- // Re-export for backward compatibility with external callers (tests, etc.)
27
- // that imported these directly from doctor.ts.
28
26
  export { extractLayout } from "./lib/lint-helpers";
29
- export type { DeckContext, Finding, SlideContext, SlideReport } from "./lib/lint-types";
27
+ export type { Finding } from "./lib/lint-types";
30
28
 
31
29
  async function loadSlides(deckDir: string): Promise<{ name: string; body: string }[]> {
32
30
  const slidesDir = join(deckDir, "slides");
@@ -64,7 +62,7 @@ function buildSlideContext(
64
62
  };
65
63
  }
66
64
 
67
- export async function lintDeck(deckDir: string): Promise<{
65
+ async function lintDeck(deckDir: string): Promise<{
68
66
  slides: SlideContext[];
69
67
  reports: SlideReport[];
70
68
  deckFindings: Finding[];
@@ -95,7 +93,6 @@ export async function lintDeck(deckDir: string): Promise<{
95
93
  return { slides, reports, deckFindings };
96
94
  }
97
95
 
98
- // Public: kept for any external caller that imported `lintSlide` directly.
99
96
  export async function lintSlide(
100
97
  slide: { name: string; body: string },
101
98
  deckDir: string
@@ -7,21 +7,16 @@ export async function readText(path: string): Promise<string> {
7
7
 
8
8
  export async function dataUri(path: string): Promise<string> {
9
9
  const ext = extname(path).toLowerCase();
10
- const mime =
11
- ext === ".svg"
12
- ? "image/svg+xml"
13
- : ext === ".png"
14
- ? "image/png"
15
- : ext === ".jpg" || ext === ".jpeg"
16
- ? "image/jpeg"
17
- : ext === ".webp"
18
- ? "image/webp"
19
- : "application/octet-stream";
10
+ let mime = "application/octet-stream";
11
+ if (ext === ".svg") mime = "image/svg+xml";
12
+ else if (ext === ".png") mime = "image/png";
13
+ else if (ext === ".jpg" || ext === ".jpeg") mime = "image/jpeg";
14
+ else if (ext === ".webp") mime = "image/webp";
20
15
 
21
16
  if (ext === ".svg") {
22
17
  // Inline SVGs as URL-encoded text — smaller than base64 and renders crisply at any size.
23
18
  const svg = await readFile(path, "utf8");
24
- const enc = encodeURIComponent(svg).replace(/'/g, "%27").replace(/"/g, "%22");
19
+ const enc = encodeURIComponent(svg).replaceAll("'", "%27").replaceAll('"', "%22");
25
20
  return `url("data:${mime};utf8,${enc}")`;
26
21
  }
27
22
 
@@ -39,7 +39,7 @@ export function extractNotes(body: string): string {
39
39
  }
40
40
 
41
41
  export function countAtxHeading(body: string, level: 1 | 2): string[] {
42
- const re = new RegExp(`^#{${level}}\\s+(.+?)\\s*$`, "gm");
42
+ const re = new RegExp(String.raw`^#{${level}}\s+(.+?)\s*$`, "gm");
43
43
  return Array.from(body.matchAll(re), (m) => m[1]);
44
44
  }
45
45
 
@@ -71,7 +71,7 @@ export type ListItem = {
71
71
  export function listItems(body: string): ListItem[] {
72
72
  const out: ListItem[] = [];
73
73
  for (const line of body.split("\n")) {
74
- const m = line.match(/^(\s*)(?:[-*]\s+|\d+\.\s+)(.*)$/);
74
+ const m = new RegExp(/^(\s*)(?:[-*]\s+|\d+\.\s+)(.*)$/).exec(line);
75
75
  if (!m) continue;
76
76
  out.push({ indent: m[1].length, content: m[2], raw: line });
77
77
  }
@@ -125,7 +125,7 @@ export const RULES: Rule[] = [
125
125
  // pretending to be a bullet. Convert to a sub-bullet instead.
126
126
  const findings: Finding[] = [];
127
127
  for (const line of ctx.bodyNoNotes.split("\n")) {
128
- const m = line.match(/^(\s*)(?:[-*]\s+|\d+\.\s+)(.*)$/);
128
+ const m = new RegExp(/^(\s*)(?:[-*]\s+|\d+\.\s+)(.*)$/).exec(line);
129
129
  if (!m) continue;
130
130
  const stripped = stripCodeAndLinks(m[2]);
131
131
  if (/\s—\s/.test(stripped)) {
@@ -338,7 +338,10 @@ export const RULES: Rule[] = [
338
338
  "Anticipated questions",
339
339
  ];
340
340
  for (const beat of required) {
341
- const re = new RegExp(`^[-*]\\s+${beat.replace(/\s+/g, "\\s+")}\\b`, "im");
341
+ const re = new RegExp(
342
+ String.raw`^[-*]\s+${beat.replace(/\s+/g, "\\s+")}\b`,
343
+ "im"
344
+ );
342
345
  if (!re.test(notes)) {
343
346
  findings.push({
344
347
  rule: "exercise-note-beats",
@@ -167,11 +167,14 @@ async function main() {
167
167
 
168
168
  // 2. Path
169
169
  const defaultPath = join(TEMPLATES_ROOT, name);
170
- const tplPath = args.path
171
- ? resolve(args.path)
172
- : rl
173
- ? resolve(await ask(rl, "Storage path:", defaultPath))
174
- : defaultPath;
170
+ let tplPath: string;
171
+ if (args.path) {
172
+ tplPath = resolve(args.path);
173
+ } else if (rl) {
174
+ tplPath = resolve(await ask(rl, "Storage path:", defaultPath));
175
+ } else {
176
+ tplPath = defaultPath;
177
+ }
175
178
 
176
179
  // 3. Logo
177
180
  let logo = args.logo;
@@ -231,7 +234,7 @@ async function main() {
231
234
  ["cover-only", "footer", "both", "none"].includes(a) ? a : "footer"
232
235
  ) as LogoPlacement;
233
236
  }
234
- if (!logoPlacement) logoPlacement = "footer";
237
+ logoPlacement ??= "footer";
235
238
 
236
239
  // 8. Fonts
237
240
  const fonts =
@@ -244,7 +247,7 @@ async function main() {
244
247
  const a = await ask(rl, "Aspect ratio [16:9 / 4:3 / 16:10]:", "16:9");
245
248
  aspect = (["16:9", "4:3", "16:10"].includes(a) ? a : "16:9") as Aspect;
246
249
  }
247
- if (!aspect) aspect = "16:9";
250
+ aspect ??= "16:9";
248
251
 
249
252
  // 10. Showcase deck?
250
253
  let showcase = args.showcase;
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: projects
3
3
  description: Project context management. PROACTIVE — use when the user references a project (by name or as "this repo", "current work"), asks to add/update/complete a project, says "store under <project>", "track this", "what am I working on", "my projects", "my priorities".
4
- argument-hint: [list | create | resume | add-fact | add-objective | add-next | add-blocker | add-decision | add-handoff | complete | archive | pause]
4
+ argument-hint: [list | create | resume | add-next | add-blocker | add-decision | add-handoff | update-section | criteria | isa-init | complete | archive | pause]
5
5
  ---
6
6
 
7
- Manage the user's project registry. Each project has its own state file at `~/.pal/memory/state/progress/{slug}.json`. The Stop hook auto-touches `updated` whenever the cwd resolves into a registered project — so just *being* in the project keeps it warm.
7
+ Manage the user's project registry. Each project lives at `~/.pal/memory/projects/{slug}/ISA.md`. Frontmatter holds operational state (next steps, blockers, handoff); the body holds ISA spec sections (Problem, Goal, Criteria, Context, Decisions, etc.). The Stop hook auto-touches `updated` whenever the cwd resolves into a registered project — just *being* in the project keeps it warm.
8
8
 
9
9
  ## CLI
10
10
 
@@ -20,16 +20,33 @@ Output is JSON.
20
20
  |---------|---------|
21
21
  | `list` | All registered projects with status, path, updated, stale flag, and counts |
22
22
  | `create [name] [--path PATH] [--objectives "a;b;c"]` | Register a project. Defaults: name=basename(cwd), path=cwd. Slug must be `[a-z0-9_-]+` |
23
- | `resume <name>` | Print the full project JSONfacts, objectives, next steps, blockers, decisions, handoff |
24
- | `add-fact <name> "text"` | Append a stable fact / reference (e.g., "reference impl lives in this repo") |
25
- | `add-objective <name> "text"` | Append an objective |
26
- | `add-next <name> "text"` | Append a next step |
27
- | `add-blocker <name> "text"` | Append a blocker |
28
- | `add-decision <name> "decision" "rationale"` | Log a timestamped decision |
29
- | `add-handoff <name> "text"` | Overwrite the handoff field (single-value) |
30
- | `rm-fact \| rm-objective \| rm-next \| rm-blocker <name> <index>` | Remove an entry by zero-based index |
23
+ | `resume <name>` | Print the full project ISAall frontmatter and body sections |
24
+ | `add-next <name> "text"` | Append a next step (array, instantly appendable) |
25
+ | `add-blocker <name> "text"` | Append a blocker (array, instantly appendable) |
26
+ | `add-decision <name> "decision" "rationale"` | Log a timestamped decision entry to the Decisions section |
27
+ | `add-handoff <name> "text"` | Overwrite the handoff field (single-value, replaces) |
28
+ | `rm-next \| rm-blocker <name> <index>` | Remove a next/blocker entry by zero-based index |
29
+ | `update-section <name> <section> "content"` | Set an ISA body section (problem, goal, criteria, vision, constraints, out_of_scope, context, decisions, changelog) |
30
+ | `criteria <name>` | Print the Criteria section (verifiable success conditions) |
31
+ | `isa-init <name>` | Mark a project as ISA-initialized |
31
32
  | `complete <name>` / `archive <name>` / `pause <name>` / `unpause <name>` | Status transitions |
32
- | `rm <name>` | Delete the project state file entirely |
33
+ | `rm <name>` | Delete the project directory entirely |
34
+
35
+ ## ISA Sections
36
+
37
+ The body of each ISA.md holds spec sections. Use `update-section` to set them:
38
+
39
+ | Section | Key | What goes here |
40
+ |---------|-----|---------------|
41
+ | Problem | `problem` | Why this project exists; the pain or gap being addressed |
42
+ | Goal | `goal` | What success looks like (may be bullet list) |
43
+ | Criteria | `criteria` | Verifiable done conditions — testable ISCs (Ideal State Criteria) |
44
+ | Vision | `vision` | Long-horizon aspiration beyond the immediate goal |
45
+ | Constraints | `constraints` | Non-negotiable limits (budget, time, tech, compatibility) |
46
+ | Out of Scope | `out_of_scope` | What this project explicitly does NOT cover |
47
+ | Context | `context` | Stable facts / references (e.g. "reference impl lives at ~/pai") |
48
+ | Decisions | `decisions` | Auto-managed by `add-decision`; dated bullet list |
49
+ | Changelog | `changelog` | Summary of completed milestones |
33
50
 
34
51
  ## Routing
35
52
 
@@ -38,9 +55,11 @@ Output is JSON.
38
55
  | "what am I working on", "my projects", "priorities" | `list` — summarize active and recently-touched projects |
39
56
  | "tell me about <project>" | `resume <name>` — present current state, highlight blockers and next steps |
40
57
  | "register this" / "track this" / cwd is unregistered work | `create` (default the name from cwd basename, confirm before writing) |
41
- | "store under <project>: X" / "note on <project>: X" | Pick the field — durable reference → `add-fact`, work item → `add-next`, obstacle → `add-blocker`. If unclear, ask. |
58
+ | "store under <project>: X" / "note on <project>: X" | Pick the field — durable reference → `update-section <slug> context "..."`, work item → `add-next`, obstacle → `add-blocker`. If unclear, ask. |
42
59
  | "we decided X because Y" | `add-decision <name> "X" "Y"` |
43
60
  | "handoff for <project>" / "next session pick up at X" | `add-handoff <name> "<text>"` |
61
+ | "set the goal for <project>" / "describe the problem" | `update-section <name> goal "..."` or `update-section <name> problem "..."` |
62
+ | "what are the criteria for <project>" / "what counts as done" | `criteria <name>` |
44
63
  | "mark X complete" / "X is done" | `complete <name>` |
45
64
  | "park <project>" / "pause <project>" | `pause <name>` |
46
65
  | "archive <project>" | `archive <name>` |
@@ -53,7 +72,7 @@ When SessionStart context flags the current cwd as unregistered (e.g. `💡 cwd
53
72
 
54
73
  - **Default name** = the FULL last path segment of cwd, lowercased. For `/repos/portable-agent-layer` → `portable-agent-layer`. Never split on `-`.
55
74
  - **Confirm before creating.** Never auto-create without explicit user approval ("yes", "do it", "register").
56
- - **Capture objectives in conversation.** If the user accepts but doesn't volunteer objectives, ask one short question, or infer from the last few messages and confirm.
75
+ - **Capture context in conversation.** If the user accepts but doesn't volunteer a goal, ask one short question, or infer from the last few messages and confirm.
57
76
 
58
77
  ### When NOT to suggest registration
59
78
 
@@ -64,7 +83,7 @@ When SessionStart context flags the current cwd as unregistered (e.g. `💡 cwd
64
83
 
65
84
  ## Append-as-you-go
66
85
 
67
- When the user describes plans, blockers, or decisions during normal work, invoke the relevant subcommand to keep state current — that's the dynamism this system is built for. Don't invoke for fleeting comments, hypotheticals, or things the user is just thinking through. Wait for a clear declarative ("let's add X", "Z is blocking us"), not a question or musing.
86
+ When the user describes next steps, blockers, or decisions during normal work, invoke the relevant subcommand to keep state current — that's the dynamism this system is built for. Don't invoke for fleeting comments, hypotheticals, or things the user is just thinking through. Wait for a clear declarative ("let's add X", "Z is blocking us"), not a question or musing.
68
87
 
69
88
  ## Examples
70
89
 
@@ -72,8 +91,8 @@ When the user describes plans, blockers, or decisions during normal work, invoke
72
91
  ```
73
92
  User: "store under <project> that a reference implementation exists in this repo"
74
93
  → Identify the project from `list` (or by name)
75
- → Durable reference, not a task → add-fact
76
- → bun ~/.pal/tools/project.ts add-fact <slug> "Reference implementation lives in this repo"
94
+ → Durable reference, not a task → update-section
95
+ → bun ~/.pal/tools/project.ts update-section <slug> context "Reference implementation lives in this repo"
77
96
  ```
78
97
 
79
98
  **Registering the current repo**
@@ -89,6 +108,12 @@ User: "we decided <decision> because <reason>"
89
108
  → bun ~/.pal/tools/project.ts add-decision <slug> "<decision>" "<reason>"
90
109
  ```
91
110
 
111
+ **Setting the goal and criteria**
112
+ ```
113
+ User: "set the goal for pal to 'ship ISA support with full test coverage'"
114
+ → bun ~/.pal/tools/project.ts update-section pal goal "ship ISA support with full test coverage"
115
+ ```
116
+
92
117
  **Completing a project**
93
118
  ```
94
119
  User: "mark <project> as complete"
@@ -98,11 +123,9 @@ User: "mark <project> as complete"
98
123
 
99
124
  ## Anti-patterns
100
125
 
101
- - **Don't dump the full JSON.** Summarize. The user can ask for the raw payload.
102
- - **Don't write without confirming the field choice on ambiguous "store" requests.** A "fact" sticks forever; a "next step" implies follow-up — these are different commitments.
103
- - **Don't edit the JSON files directly.** Always use the CLI — it timestamps `updated` and keeps the schema valid.
104
- - **Don't re-introduce `~/.pal/telos/PROJECTS.md`.** That file and its `update-projects.ts` tool are deprecated. The legacy `telos` skill carries a deprecation notice for this reason.
105
- - **Don't confuse `add-fact` with the `telos` skill's `LEARNED.md` or `IDEAS.md`.** Project facts are scoped to one project; TELOS lessons are cross-cutting.
126
+ - **Don't dump the full ISA.md.** Summarize. The user can ask for the raw payload.
127
+ - **Don't write without confirming the field choice on ambiguous "store" requests.** A context fact sticks forever; a next step implies follow-up — these are different commitments.
128
+ - **Don't edit ISA.md files directly.** Always use the CLI — it timestamps `updated` and keeps the schema valid.
106
129
 
107
130
  ## Rules
108
131
 
@@ -70,7 +70,7 @@ Distinguish between peer-reviewed findings and preprints/working papers.
70
70
  Note methodology limitations and sample sizes when relevant.
71
71
  Be thorough but concise.`;
72
72
 
73
- export async function geminiSearch(query: string, maxTokens: number): Promise<void> {
73
+ async function geminiSearch(query: string, maxTokens: number): Promise<void> {
74
74
  const apiKey = loadApiKey();
75
75
 
76
76
  const body = {
@@ -183,4 +183,4 @@ Examples:
183
183
  await geminiSearch(query, maxTokens);
184
184
  }
185
185
 
186
- if (import.meta.main) run();
186
+ if (import.meta.main) void run();
@@ -70,7 +70,7 @@ function sourcesToTools(sources: SourceType[]): ToolType[] {
70
70
  return sources.map((s) => map[s]);
71
71
  }
72
72
 
73
- export async function grokSearch(
73
+ async function grokSearch(
74
74
  query: string,
75
75
  sources: SourceType[],
76
76
  maxTokens: number
@@ -189,4 +189,4 @@ Examples:
189
189
  await grokSearch(query, sources, maxTokens);
190
190
  }
191
191
 
192
- if (import.meta.main) run();
192
+ if (import.meta.main) void run();
@@ -58,7 +58,7 @@ Distinguish between confirmed facts, single-source claims, and unverified allega
58
58
  Flag contradictions between sources.
59
59
  Be thorough but concise.`;
60
60
 
61
- export async function perplexitySearch(query: string, maxTokens: number): Promise<void> {
61
+ async function perplexitySearch(query: string, maxTokens: number): Promise<void> {
62
62
  const apiKey = loadApiKey();
63
63
 
64
64
  const body = {
@@ -147,4 +147,4 @@ Examples:
147
147
  await perplexitySearch(query, maxTokens);
148
148
  }
149
149
 
150
- if (import.meta.main) run();
150
+ if (import.meta.main) void run();
@@ -1,12 +1,9 @@
1
1
  ---
2
2
  name: telos
3
- description: Personal context management. Use when discussing goals, beliefs, challenges, identity, updating telos, life context, changing a goal, priorities, what do I believe, current obstacles, mission, or strategies.
3
+ description: Personal context management. Use when discussing goals, beliefs, challenges, identity, updating telos, life context, changing a goal, what do I believe, current obstacles, mission, or strategies.
4
4
  argument-hint: [area to view or update]
5
5
  ---
6
6
 
7
- > ⚠️ **DEPRECATION NOTICE — Project management has moved.**
8
- > Project tracking is now handled by the `projects` skill, backed by `~/.pal/tools/project.ts` and per-project state in `~/.pal/memory/state/progress/`. **Do not use this skill for projects.** The `PROJECTS.md` references and `update-projects.ts` tool below are legacy and slated for removal — they remain only because the initial setup wizard (`src/cli/setup-telos.ts`) still depends on them. For anything project-related, invoke the `projects` skill.
9
-
10
7
  Manage the user's TELOS files — the persistent personal context that drives PAL.
11
8
 
12
9
  ## TELOS Files
@@ -16,7 +13,6 @@ All files live in `~/.pal/telos/`:
16
13
  | File | Contains |
17
14
  |------|----------|
18
15
  | `GOALS.md` | Short/medium/long-term goals |
19
- | `PROJECTS.md` | Active projects, status, priority |
20
16
  | `BELIEFS.md` | Core principles and values |
21
17
  | `CHALLENGES.md` | Current obstacles |
22
18
  | `MISSION.md` | Purpose and direction |
@@ -32,33 +28,18 @@ Read the file directly from `~/.pal/telos/` when the user asks about any area. S
32
28
 
33
29
  ## Updating
34
30
 
35
- ### General TELOS files (append)
36
-
37
- For all files except PROJECTS.md — appends content, creates backup, logs the change:
31
+ Appends content, creates backup, logs the change:
38
32
 
39
33
  ```bash
40
34
  bun ~/.pal/skills/telos/tools/update-telos.ts <FILE> "<content>" "<description>"
41
35
  ```
42
36
 
43
- ### Projects (upsert by ID)
44
-
45
- For PROJECTS.md — upserts a row by the ID column. Replaces if the ID exists, appends if new:
46
-
47
- ```bash
48
- bun ~/.pal/skills/telos/tools/update-projects.ts <id> "<row>" "<description>"
49
- ```
50
-
51
- The ID is the first column of the table. Use short, lowercase, kebab-case slugs (e.g., `my-project`, `side-gig`).
52
-
53
37
  ## Routing
54
38
 
55
39
  | Intent | Action |
56
40
  |--------|--------|
57
- | "what am I working on", "my projects", "priorities" | Read `PROJECTS.md`, summarize active work |
58
41
  | "my goals", "what are my goals" | Read `GOALS.md`, present current state |
59
- | "update goals/projects/beliefs/challenges" | Read the target file, discuss changes with user, then run update tool |
60
- | "add a project", "new project" | Read `PROJECTS.md`, confirm with user, run update tool |
61
- | "complete/remove a project" | Read `PROJECTS.md`, confirm with user, update status via tool |
42
+ | "update goals/beliefs/challenges" | Read the target file, discuss changes with user, then run update tool |
62
43
  | "what do I believe", "my principles" | Read `BELIEFS.md` |
63
44
  | "current obstacles", "challenges" | Read `CHALLENGES.md` |
64
45
  | "I learned something", "lesson" | Discuss, then append to `LEARNED.md` via tool |
@@ -67,38 +48,12 @@ The ID is the first column of the table. Use short, lowercase, kebab-case slugs
67
48
 
68
49
  ## Examples
69
50
 
70
- **Example 1: Checking projects**
71
- ```
72
- User: "what am I working on?"
73
- → Read PROJECTS.md
74
- → Summarize active work by priority — don't list every column
75
- → Highlight status changes, blockers, what needs attention
76
- ```
77
-
78
- **Example 2: Adding a project**
79
- ```
80
- User: "add my new side project"
81
- → Ask: "What's the project name, status, and priority?"
82
- → User provides details
83
- → Show the row you'll add, confirm
84
- → Run: bun ~/.pal/skills/telos/tools/update-projects.ts side-project "| side-project | Side Project | In progress | Medium | Description |" "Added Side Project"
85
- ```
86
-
87
- **Example 3: Updating a project**
88
- ```
89
- User: "mark X as complete"
90
- → Read PROJECTS.md, find the entry and its ID
91
- → Show updated row, confirm
92
- → Run: bun ~/.pal/skills/telos/tools/update-projects.ts some-id "| some-id | Project Name | Complete | High | ... |" "Marked project as complete"
93
- → The existing row is replaced, not duplicated
94
- ```
95
-
96
- **Example 4: Updating goals**
51
+ **Updating goals**
97
52
  ```
98
53
  User: "I finished the migration, update my goals"
99
54
  → Read GOALS.md to see current state
100
55
  → Discuss what changed — what's done, what's next
101
- → Run tool with --id to update existing goal entry
56
+ → Run tool with updated content
102
57
  → Remind: CLAUDE.md regenerates next session
103
58
  ```
104
59
 
@@ -106,8 +61,8 @@ User: "I finished the migration, update my goals"
106
61
 
107
62
  - **Don't dump raw file contents.** Summarize what's relevant to the user's question. They can ask for the full file if needed.
108
63
  - **Don't update without confirming.** Always show what you'll change and get a "yes" before running the tool.
109
- - **Don't create new TELOS files.** Only the 10 listed files are valid. If something doesn't fit, suggest the closest match.
110
- - **Don't mix TELOS with identity.** AI/principal identity lives in `pal-settings.json`, not TELOS. TELOS is personal context — goals, beliefs, projects.
64
+ - **Don't create new TELOS files.** Only the 9 listed files are valid. If something doesn't fit, suggest the closest match.
65
+ - **Don't mix TELOS with identity.** AI/principal identity lives in `pal-settings.json`, not TELOS. TELOS is personal context — goals, beliefs, challenges.
111
66
  - **Don't reference stale data.** If TELOS was loaded earlier in the session via context routing, re-read the file before updating — it may have changed.
112
67
 
113
68
  ## Rules
@@ -10,7 +10,6 @@
10
10
  * - Appends content (preserves existing)
11
11
  * - Logs the change to updates.md
12
12
  *
13
- * For PROJECTS.md upserts (add/update by ID), use update-projects.ts instead.
14
13
  */
15
14
 
16
15
  import {