portable-agent-layer 0.61.0 → 0.61.2
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.
|
@@ -40,13 +40,15 @@ The body of each ISA.md holds spec sections. Use `update-section` to set them:
|
|
|
40
40
|
|---------|-----|---------------|
|
|
41
41
|
| Problem | `problem` | Why this project exists; the pain or gap being addressed |
|
|
42
42
|
| Goal | `goal` | What success looks like (may be bullet list) |
|
|
43
|
-
| Criteria | `criteria` |
|
|
43
|
+
| Criteria | `criteria` | Open ISCs (Ideal State Criteria) — testable done conditions. Holds only the open set; completing an ISC moves it to the Changelog |
|
|
44
44
|
| Vision | `vision` | Long-horizon aspiration beyond the immediate goal |
|
|
45
45
|
| Constraints | `constraints` | Non-negotiable limits (budget, time, tech, compatibility) |
|
|
46
46
|
| Out of Scope | `out_of_scope` | What this project explicitly does NOT cover |
|
|
47
47
|
| Context | `context` | Stable facts / references (e.g. "reference impl lives at ~/pai") |
|
|
48
48
|
| Decisions | `decisions` | Auto-managed by `add-decision`; dated bullet list |
|
|
49
|
-
| Changelog | `changelog` |
|
|
49
|
+
| Changelog | `changelog` | Archive of completed ISCs (`complete-isc` moves them here under a dated `### Archived` heading) plus any milestone notes |
|
|
50
|
+
|
|
51
|
+
**ISC archive model.** `complete-isc` does not just check a box — it moves the ISC line out of Criteria and into the Changelog archive, so Criteria always reflects exactly the open work and never bloats the context loaded at session start. `list-isc <name>` shows open ISCs by default; pass `--closed` (archived only) or `--all` (open + archived) to read finished ones. `reopen-isc` pulls an archived ISC back into the open set. `prune-isc <name>` backfills legacy projects by sweeping any done ISCs still sitting in Criteria into the archive in one pass.
|
|
50
52
|
|
|
51
53
|
## Routing
|
|
52
54
|
|
|
@@ -40,6 +40,7 @@ Thinking-only. No tool calls except context recovery (Grep/Glob/Read).
|
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
42
|
# If cwd matches a registered project — read its open ISCs (Ideal State Criteria):
|
|
43
|
+
# list-isc returns OPEN criteria by default; pass --all or --closed to see finished ones.
|
|
43
44
|
bun ~/.pal/tools/project.ts list-isc <project-name>
|
|
44
45
|
|
|
45
46
|
# If this is ad-hoc work with no registered project — scaffold a task ISA:
|
package/package.json
CHANGED
|
@@ -351,17 +351,38 @@ function parseIscs(criteria: string): Isc[] {
|
|
|
351
351
|
return out;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
|
|
355
|
-
|
|
354
|
+
// Scans Criteria AND Changelog so an archived id can never be handed out again.
|
|
355
|
+
function nextIscId(p: ProjectProgress): number {
|
|
356
|
+
const ids = [...parseIscs(p.criteria ?? ""), ...parseIscs(p.changelog ?? "")].map(
|
|
357
|
+
(i) => i.id
|
|
358
|
+
);
|
|
356
359
|
return ids.length > 0 ? Math.max(...ids) + 1 : 1;
|
|
357
360
|
}
|
|
358
361
|
|
|
359
|
-
function
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
362
|
+
function removeIscLine(
|
|
363
|
+
section: string,
|
|
364
|
+
id: number
|
|
365
|
+
): { line: string | null; rest: string } {
|
|
366
|
+
const lines = section.split("\n");
|
|
367
|
+
const idx = lines.findIndex((l) =>
|
|
368
|
+
new RegExp(String.raw`^-\s+\[[ x]\]\s+ISC-${id}:`).test(l)
|
|
364
369
|
);
|
|
370
|
+
if (idx === -1) return { line: null, rest: section };
|
|
371
|
+
const [line] = lines.splice(idx, 1);
|
|
372
|
+
return {
|
|
373
|
+
line,
|
|
374
|
+
rest: lines
|
|
375
|
+
.join("\n")
|
|
376
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
377
|
+
.trim(),
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function archiveLine(changelog: string | undefined, doneLine: string): string {
|
|
382
|
+
const heading = `### Archived ${new Date().toISOString().slice(0, 10)}`;
|
|
383
|
+
const base = (changelog ?? "").trim();
|
|
384
|
+
if (base.includes(heading)) return `${base}\n${doneLine}`;
|
|
385
|
+
return base ? `${base}\n\n${heading}\n${doneLine}` : `${heading}\n${doneLine}`;
|
|
365
386
|
}
|
|
366
387
|
|
|
367
388
|
function cmdAddIsc(args: string[]): void {
|
|
@@ -370,7 +391,7 @@ function cmdAddIsc(args: string[]): void {
|
|
|
370
391
|
if (!title) fail("Usage: add-isc <name> <title>");
|
|
371
392
|
const p = requireProject(name);
|
|
372
393
|
const current = p.criteria ?? "";
|
|
373
|
-
const id = nextIscId(
|
|
394
|
+
const id = nextIscId(p);
|
|
374
395
|
const newLine = `- [ ] ISC-${id}: ${title}`;
|
|
375
396
|
p.criteria = current ? `${current.trimEnd()}\n${newLine}` : newLine;
|
|
376
397
|
p.updated = now();
|
|
@@ -385,49 +406,94 @@ function cmdAddIsc(args: string[]): void {
|
|
|
385
406
|
});
|
|
386
407
|
}
|
|
387
408
|
|
|
409
|
+
// Completing an ISC moves its line out of Criteria and into the dated Changelog
|
|
410
|
+
// archive, so Criteria stays exactly the open set and never re-bloats context.
|
|
388
411
|
function cmdCompleteIsc(args: string[]): void {
|
|
389
412
|
const name = args[0] ?? fail("Usage: complete-isc <name> <id>");
|
|
390
413
|
const id = Number(args[1] ?? fail("Usage: complete-isc <name> <id>"));
|
|
391
414
|
if (!Number.isInteger(id) || id < 1) fail("ISC id must be a positive integer");
|
|
392
415
|
const p = requireProject(name);
|
|
393
|
-
|
|
394
|
-
const existing = parseIscs(current).find((i) => i.id === id);
|
|
395
|
-
if (!existing) fail(`ISC-${id} not found in project "${name}"`);
|
|
396
|
-
if (existing.checked) {
|
|
416
|
+
if (parseIscs(p.changelog ?? "").some((i) => i.id === id)) {
|
|
397
417
|
ok({ checked: true, id, alreadyDone: true });
|
|
398
418
|
return;
|
|
399
419
|
}
|
|
400
|
-
p.criteria
|
|
420
|
+
const { line, rest } = removeIscLine(p.criteria ?? "", id);
|
|
421
|
+
if (!line) fail(`ISC-${id} not found in project "${name}"`);
|
|
422
|
+
p.criteria = rest;
|
|
423
|
+
p.changelog = archiveLine(p.changelog, line.replace("[ ]", "[x]"));
|
|
401
424
|
p.updated = now();
|
|
402
425
|
writeProject(p);
|
|
403
|
-
ok({ checked: true, id });
|
|
426
|
+
ok({ checked: true, id, archived: true });
|
|
404
427
|
}
|
|
405
428
|
|
|
429
|
+
// Reopening pulls the line back out of the Changelog (or legacy Criteria) into
|
|
430
|
+
// the open set.
|
|
406
431
|
function cmdReopenIsc(args: string[]): void {
|
|
407
432
|
const name = args[0] ?? fail("Usage: reopen-isc <name> <id>");
|
|
408
433
|
const id = Number(args[1] ?? fail("Usage: reopen-isc <name> <id>"));
|
|
409
434
|
if (!Number.isInteger(id) || id < 1) fail("ISC id must be a positive integer");
|
|
410
435
|
const p = requireProject(name);
|
|
411
|
-
|
|
412
|
-
const existing = parseIscs(current).find((i) => i.id === id);
|
|
413
|
-
if (!existing) fail(`ISC-${id} not found in project "${name}"`);
|
|
414
|
-
if (!existing.checked) {
|
|
436
|
+
if (parseIscs(p.criteria ?? "").some((i) => i.id === id && !i.checked)) {
|
|
415
437
|
ok({ checked: false, id, alreadyOpen: true });
|
|
416
438
|
return;
|
|
417
439
|
}
|
|
418
|
-
p.
|
|
440
|
+
let removed = removeIscLine(p.changelog ?? "", id);
|
|
441
|
+
if (removed.line) {
|
|
442
|
+
p.changelog = removed.rest;
|
|
443
|
+
} else {
|
|
444
|
+
removed = removeIscLine(p.criteria ?? "", id);
|
|
445
|
+
if (removed.line) p.criteria = removed.rest;
|
|
446
|
+
}
|
|
447
|
+
if (!removed.line) fail(`ISC-${id} not found in project "${name}"`);
|
|
448
|
+
const openLine = removed.line.replace(/\[x\]/i, "[ ]");
|
|
449
|
+
p.criteria = p.criteria ? `${p.criteria.trimEnd()}\n${openLine}` : openLine;
|
|
419
450
|
p.updated = now();
|
|
420
451
|
writeProject(p);
|
|
421
452
|
ok({ checked: false, id });
|
|
422
453
|
}
|
|
423
454
|
|
|
455
|
+
function selectIscs(open: Isc[], done: Isc[], flags: Set<string>): Isc[] {
|
|
456
|
+
if (flags.has("--all")) return [...open, ...done];
|
|
457
|
+
if (flags.has("--closed")) return done;
|
|
458
|
+
return open;
|
|
459
|
+
}
|
|
460
|
+
|
|
424
461
|
function cmdListIsc(args: string[]): void {
|
|
425
|
-
const
|
|
462
|
+
const flags = new Set(args.filter((a) => a.startsWith("--")));
|
|
463
|
+
const name =
|
|
464
|
+
args.find((a) => !a.startsWith("--")) ??
|
|
465
|
+
fail("Usage: list-isc <name> [--all | --closed]");
|
|
426
466
|
const p = requireProject(name);
|
|
427
|
-
const
|
|
428
|
-
const open =
|
|
429
|
-
const done =
|
|
430
|
-
ok({
|
|
467
|
+
const criteria = parseIscs(p.criteria ?? "");
|
|
468
|
+
const open = criteria.filter((i) => !i.checked);
|
|
469
|
+
const done = [...criteria.filter((i) => i.checked), ...parseIscs(p.changelog ?? "")];
|
|
470
|
+
ok({
|
|
471
|
+
name,
|
|
472
|
+
total: open.length + done.length,
|
|
473
|
+
open: open.length,
|
|
474
|
+
done: done.length,
|
|
475
|
+
iscs: selectIscs(open, done, flags),
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Backfill: sweep any done ISCs still sitting in Criteria (legacy projects, or
|
|
480
|
+
// completions from before archive-on-complete) into the Changelog in one pass.
|
|
481
|
+
function cmdPruneIsc(args: string[]): void {
|
|
482
|
+
const name = args[0] ?? fail("Usage: prune-isc <name>");
|
|
483
|
+
const p = requireProject(name);
|
|
484
|
+
const done = parseIscs(p.criteria ?? "").filter((i) => i.checked);
|
|
485
|
+
for (const isc of done) {
|
|
486
|
+
const { line, rest } = removeIscLine(p.criteria ?? "", isc.id);
|
|
487
|
+
if (!line) continue;
|
|
488
|
+
p.criteria = rest;
|
|
489
|
+
p.changelog = archiveLine(p.changelog, line);
|
|
490
|
+
}
|
|
491
|
+
if (done.length > 0) {
|
|
492
|
+
p.updated = now();
|
|
493
|
+
writeProject(p);
|
|
494
|
+
}
|
|
495
|
+
const openLeft = parseIscs(p.criteria ?? "").filter((i) => !i.checked).length;
|
|
496
|
+
ok({ pruned: done.length, name, remaining_open: openLeft });
|
|
431
497
|
}
|
|
432
498
|
|
|
433
499
|
// ── Task ISA (work/) ──────────────────────────────────────────────
|
|
@@ -509,7 +575,8 @@ Commands:
|
|
|
509
575
|
add-isc <name> "title" append a new open ISC to Criteria
|
|
510
576
|
complete-isc <name> <id> mark ISC-N as done
|
|
511
577
|
reopen-isc <name> <id> reopen ISC-N (mark not done)
|
|
512
|
-
list-isc <name>
|
|
578
|
+
list-isc <name> [--all | --closed] list open ISCs (default); --all or --closed for done
|
|
579
|
+
prune-isc <name> archive done ISCs from Criteria into the Changelog
|
|
513
580
|
isa-init <name> mark project as ISA-initialized
|
|
514
581
|
scaffold-task-isa <title> create a one-shot task ISA in memory/work/
|
|
515
582
|
complete-task-isa <slug> mark a task ISA as complete
|
|
@@ -601,6 +668,9 @@ function run(): void {
|
|
|
601
668
|
case "list-isc":
|
|
602
669
|
cmdListIsc(rest);
|
|
603
670
|
return;
|
|
671
|
+
case "prune-isc":
|
|
672
|
+
cmdPruneIsc(rest);
|
|
673
|
+
return;
|
|
604
674
|
case "isa-init":
|
|
605
675
|
cmdIsaInit(rest);
|
|
606
676
|
return;
|