paseo-room 0.4.1 → 0.5.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 +122 -70
- package/dist/index.js +338 -217
- package/dist/index.js.map +1 -1
- package/dist/prompts/contract/lead.md +152 -0
- package/dist/prompts/contract/peer.md +62 -0
- package/dist/prompts/contract/shared-authority.md +33 -0
- package/dist/prompts/contract/shared-seat-identity.md +18 -0
- package/dist/prompts/contract/supervisor.md +89 -0
- package/dist/skills/paseo-project-onboarding/SKILL.md +122 -0
- package/dist/skills/paseo-project-onboarding/references/workspace-protocol-template.md +56 -0
- package/package.json +2 -2
- package/dist/prompts/contract/lead/complete-peer-brief.md +0 -32
- package/dist/prompts/contract/lead/independent-review.md +0 -9
- package/dist/prompts/contract/lead/moving-write-ownership.md +0 -13
- package/dist/prompts/contract/lead/peer-seat-lifecycle.md +0 -39
- package/dist/prompts/contract/lead/project-technical-ownership.md +0 -12
- package/dist/prompts/contract/lead/technical-acceptance.md +0 -6
- package/dist/prompts/contract/peer/assignment-scope.md +0 -10
- package/dist/prompts/contract/peer/bounded-outcome.md +0 -13
- package/dist/prompts/contract/peer/independent-judgment.md +0 -11
- package/dist/prompts/contract/peer/no-orchestration.md +0 -5
- package/dist/prompts/contract/peer/no-self-acceptance.md +0 -5
- package/dist/prompts/contract/peer/reproducible-handoff.md +0 -17
- package/dist/prompts/contract/shared/evidence-and-event-waiting.md +0 -7
- package/dist/prompts/contract/shared/human-authority.md +0 -8
- package/dist/prompts/contract/shared/scope-and-unrelated-work.md +0 -4
- package/dist/prompts/contract/shared-supervisor-lead/workspace-protocol-precedence.md +0 -18
- package/dist/prompts/contract/supervisor/directive-integrity.md +0 -5
- package/dist/prompts/contract/supervisor/escalation-boundaries.md +0 -5
- package/dist/prompts/contract/supervisor/lead-discovery-and-recovery.md +0 -52
- package/dist/prompts/contract/supervisor/observation-and-advice.md +0 -13
- package/dist/prompts/contract/supervisor/technical-non-interference.md +0 -8
- package/dist/prompts/documents/workspace.md +0 -6
- package/dist/prompts/workspace/repository-conventions.md +0 -7
- package/dist/prompts/workspace/review.md +0 -4
- package/dist/prompts/workspace/topology.md +0 -13
- package/dist/prompts/workspace/verification.md +0 -10
- /package/dist/prompts/contract/{shared-lead-peer/challenge-signals.md → challenge-signals.md} +0 -0
package/dist/index.js
CHANGED
|
@@ -15,7 +15,7 @@ var init_package = __esm({
|
|
|
15
15
|
"package.json"() {
|
|
16
16
|
package_default = {
|
|
17
17
|
name: "paseo-room",
|
|
18
|
-
version: "0.
|
|
18
|
+
version: "0.5.0",
|
|
19
19
|
description: "One CLI that configures Codex, Claude Code, and Pi role homes in $HOME and registers them with your local Paseo daemon.",
|
|
20
20
|
keywords: [
|
|
21
21
|
"paseo",
|
|
@@ -49,7 +49,7 @@ var init_package = __esm({
|
|
|
49
49
|
lint: "eslint . --max-warnings 0",
|
|
50
50
|
test: "vitest run",
|
|
51
51
|
"test:package": "vitest run --config vitest.package.config.ts",
|
|
52
|
-
build: `tsup && node --input-type=module -e "import { cpSync, rmSync } from 'node:fs'; for (const [source, target] of [['src/room/prompts', 'dist/prompts'], ['src/plugin-assets', 'dist/plugin-assets']]) { rmSync(target, { recursive: true, force: true }); cpSync(source, target, { recursive: true }); }"`,
|
|
52
|
+
build: `tsup && node --input-type=module -e "import { cpSync, rmSync } from 'node:fs'; for (const [source, target] of [['src/room/prompts', 'dist/prompts'], ['src/room/skills', 'dist/skills'], ['src/plugin-assets', 'dist/plugin-assets']]) { rmSync(target, { recursive: true, force: true }); cpSync(source, target, { recursive: true }); }"`,
|
|
53
53
|
prepack: "npm run build",
|
|
54
54
|
verify: "npm run typecheck && npm run lint && npm test && npm run build && npm run test:package"
|
|
55
55
|
},
|
|
@@ -158,12 +158,6 @@ function preservedCredentialCheck(input) {
|
|
|
158
158
|
function presentNames(names, supported) {
|
|
159
159
|
return supported.filter((name) => names.has(name));
|
|
160
160
|
}
|
|
161
|
-
function configuredByNamesCheck(id, agent, role, names) {
|
|
162
|
-
return warn(
|
|
163
|
-
`${id}.configured-structurally`,
|
|
164
|
-
`${agent} ${role} auth: configured structurally; ${names.join(", ")} ${names.length === 1 ? "is" : "are"} present by name. Values, token validity, and freshness were not checked.`
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
161
|
function ambientNamesCheck(id, agent, role, names) {
|
|
168
162
|
return warn(
|
|
169
163
|
`${id}.ambient-auth-unverifiable`,
|
|
@@ -765,18 +759,11 @@ function validateHeadOrCapsule(source, kind) {
|
|
|
765
759
|
}
|
|
766
760
|
return trimmed;
|
|
767
761
|
}
|
|
768
|
-
function
|
|
769
|
-
|
|
770
|
-
if (!trimmed.trim()) throw new Error("is empty.");
|
|
771
|
-
const lines = trimmed.split("\n");
|
|
772
|
-
const heading = lines.shift() ?? "";
|
|
773
|
-
if (!/^## \S.*$/.test(heading) || headingCount(trimmed, 2) !== 1) {
|
|
774
|
-
throw new Error("section must begin with exactly one H2 heading.");
|
|
775
|
-
}
|
|
776
|
-
if (lines.some((line) => /^#{1,6}\s/.test(line))) {
|
|
762
|
+
function renderSection(heading, bodyLines) {
|
|
763
|
+
if (bodyLines.some((line) => /^#{1,6}\s/.test(line))) {
|
|
777
764
|
throw new Error("section must not contain additional Markdown headings.");
|
|
778
765
|
}
|
|
779
|
-
const body =
|
|
766
|
+
const body = bodyLines.join("\n").trim();
|
|
780
767
|
if (!body) throw new Error("section must contain at least one non-empty statement.");
|
|
781
768
|
const statements = body.split(/\n\s*\n/).map(
|
|
782
769
|
(statement) => statement.split("\n").map((line) => line.trim()).join(" ")
|
|
@@ -787,6 +774,40 @@ function validateSection(source) {
|
|
|
787
774
|
return `${heading}
|
|
788
775
|
${statements.map((statement) => `- ${statement}`).join("\n")}`;
|
|
789
776
|
}
|
|
777
|
+
function validateSection(source) {
|
|
778
|
+
const trimmed = source.trimEnd();
|
|
779
|
+
if (!trimmed.trim()) throw new Error("is empty.");
|
|
780
|
+
const lines = trimmed.split("\n");
|
|
781
|
+
const heading = lines.shift() ?? "";
|
|
782
|
+
if (!/^## \S.*$/.test(heading) || headingCount(trimmed, 2) !== 1) {
|
|
783
|
+
throw new Error("section must begin with exactly one H2 heading.");
|
|
784
|
+
}
|
|
785
|
+
return renderSection(heading, lines);
|
|
786
|
+
}
|
|
787
|
+
function splitSections(lines) {
|
|
788
|
+
const preface = [];
|
|
789
|
+
const sections = [];
|
|
790
|
+
for (const line of lines) {
|
|
791
|
+
if (/^## \S.*$/.test(line)) {
|
|
792
|
+
sections.push({ heading: line, lines: [] });
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
const current2 = sections.at(-1);
|
|
796
|
+
if (current2 === void 0) preface.push(line);
|
|
797
|
+
else current2.lines.push(line);
|
|
798
|
+
}
|
|
799
|
+
return { preface, sections };
|
|
800
|
+
}
|
|
801
|
+
function validateBody(source) {
|
|
802
|
+
const trimmed = source.trimEnd();
|
|
803
|
+
if (!trimmed.trim()) throw new Error("is empty.");
|
|
804
|
+
const { preface, sections } = splitSections(trimmed.split("\n"));
|
|
805
|
+
if (sections.length === 0) throw new Error("role body must contain at least one H2 section.");
|
|
806
|
+
if (preface.some((line) => line.trim())) {
|
|
807
|
+
throw new Error("role body must not contain content above its first H2 heading.");
|
|
808
|
+
}
|
|
809
|
+
return sections.map((section) => renderSection(section.heading, section.lines)).join("\n\n");
|
|
810
|
+
}
|
|
790
811
|
function loadPromptAsset(group, key) {
|
|
791
812
|
const logicalAsset = `${group}.${String(key)}`;
|
|
792
813
|
const cached = cache.get(logicalAsset);
|
|
@@ -796,7 +817,7 @@ function loadPromptAsset(group, key) {
|
|
|
796
817
|
try {
|
|
797
818
|
const source = readFileSync(assetUrl, "utf8");
|
|
798
819
|
if (source.includes("\uFFFD")) throw new Error("is not valid UTF-8 text.");
|
|
799
|
-
const validated =
|
|
820
|
+
const validated = validate(source, asset.kind);
|
|
800
821
|
cache.set(logicalAsset, validated);
|
|
801
822
|
return validated;
|
|
802
823
|
} catch (error) {
|
|
@@ -805,6 +826,16 @@ function loadPromptAsset(group, key) {
|
|
|
805
826
|
throw new PromptAssetError(logicalAsset, assetUrl, detail, { cause: error });
|
|
806
827
|
}
|
|
807
828
|
}
|
|
829
|
+
function validate(source, kind) {
|
|
830
|
+
switch (kind) {
|
|
831
|
+
case "section":
|
|
832
|
+
return validateSection(source);
|
|
833
|
+
case "body":
|
|
834
|
+
return validateBody(source);
|
|
835
|
+
default:
|
|
836
|
+
return validateHeadOrCapsule(source, kind);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
808
839
|
var PROMPT_ASSETS, PromptAssetError, cache;
|
|
809
840
|
var init_prompts = __esm({
|
|
810
841
|
"src/room/prompts.ts"() {
|
|
@@ -813,38 +844,15 @@ var init_prompts = __esm({
|
|
|
813
844
|
documents: {
|
|
814
845
|
supervisor: { path: "documents/supervisor.md", kind: "head" },
|
|
815
846
|
lead: { path: "documents/lead.md", kind: "head" },
|
|
816
|
-
peer: { path: "documents/peer.md", kind: "head" }
|
|
817
|
-
workspace: { path: "documents/workspace.md", kind: "head" }
|
|
847
|
+
peer: { path: "documents/peer.md", kind: "head" }
|
|
818
848
|
},
|
|
819
849
|
contract: {
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
leadDiscoveryAndRecovery: { path: "contract/supervisor/lead-discovery-and-recovery.md", kind: "section" },
|
|
827
|
-
observationAndAdvice: { path: "contract/supervisor/observation-and-advice.md", kind: "section" },
|
|
828
|
-
escalationBoundaries: { path: "contract/supervisor/escalation-boundaries.md", kind: "section" },
|
|
829
|
-
projectTechnicalOwnership: { path: "contract/lead/project-technical-ownership.md", kind: "section" },
|
|
830
|
-
movingWriteOwnership: { path: "contract/lead/moving-write-ownership.md", kind: "section" },
|
|
831
|
-
completePeerBrief: { path: "contract/lead/complete-peer-brief.md", kind: "section" },
|
|
832
|
-
challengeSignals: { path: "contract/shared-lead-peer/challenge-signals.md", kind: "section" },
|
|
833
|
-
technicalAcceptance: { path: "contract/lead/technical-acceptance.md", kind: "section" },
|
|
834
|
-
independentReview: { path: "contract/lead/independent-review.md", kind: "section" },
|
|
835
|
-
peerSeatLifecycle: { path: "contract/lead/peer-seat-lifecycle.md", kind: "section" },
|
|
836
|
-
boundedOutcome: { path: "contract/peer/bounded-outcome.md", kind: "section" },
|
|
837
|
-
independentJudgment: { path: "contract/peer/independent-judgment.md", kind: "section" },
|
|
838
|
-
assignmentScope: { path: "contract/peer/assignment-scope.md", kind: "section" },
|
|
839
|
-
noOrchestration: { path: "contract/peer/no-orchestration.md", kind: "section" },
|
|
840
|
-
reproducibleHandoff: { path: "contract/peer/reproducible-handoff.md", kind: "section" },
|
|
841
|
-
noSelfAcceptance: { path: "contract/peer/no-self-acceptance.md", kind: "section" }
|
|
842
|
-
},
|
|
843
|
-
workspace: {
|
|
844
|
-
topology: { path: "workspace/topology.md", kind: "section" },
|
|
845
|
-
verification: { path: "workspace/verification.md", kind: "section" },
|
|
846
|
-
review: { path: "workspace/review.md", kind: "section" },
|
|
847
|
-
repositoryConventions: { path: "workspace/repository-conventions.md", kind: "section" }
|
|
850
|
+
sharedAuthority: { path: "contract/shared-authority.md", kind: "body" },
|
|
851
|
+
sharedSeatIdentity: { path: "contract/shared-seat-identity.md", kind: "section" },
|
|
852
|
+
challengeSignals: { path: "contract/challenge-signals.md", kind: "section" },
|
|
853
|
+
supervisor: { path: "contract/supervisor.md", kind: "body" },
|
|
854
|
+
lead: { path: "contract/lead.md", kind: "body" },
|
|
855
|
+
peer: { path: "contract/peer.md", kind: "body" }
|
|
848
856
|
},
|
|
849
857
|
pi: {
|
|
850
858
|
communicationStyle: { path: "pi/communication-style.md", kind: "capsule" },
|
|
@@ -868,24 +876,12 @@ var init_prompts = __esm({
|
|
|
868
876
|
// src/room/instructions.ts
|
|
869
877
|
import { createHash } from "crypto";
|
|
870
878
|
function instructionKeys(role) {
|
|
871
|
-
return [...SHARED_KEYS[role],
|
|
872
|
-
}
|
|
873
|
-
function protocolKeys(role) {
|
|
874
|
-
return PROTOCOL_KEYS[role];
|
|
875
|
-
}
|
|
876
|
-
function protocol(keys) {
|
|
877
|
-
if (keys.length === 0) return [];
|
|
878
|
-
return [
|
|
879
|
-
loadPromptAsset("documents", "workspace"),
|
|
880
|
-
...keys.map((key) => loadPromptAsset("workspace", key))
|
|
881
|
-
];
|
|
879
|
+
return [...SHARED_KEYS[role], ROLE_KEYS[role]];
|
|
882
880
|
}
|
|
883
881
|
function renderInstructions(kind) {
|
|
884
|
-
if (kind === "workspace") return protocol(ALL_PROTOCOL_KEYS).join("\n\n") + "\n";
|
|
885
882
|
return [
|
|
886
883
|
loadPromptAsset("documents", kind),
|
|
887
|
-
...instructionKeys(kind).map((key) => loadPromptAsset("contract", key))
|
|
888
|
-
...protocol(protocolKeys(kind))
|
|
884
|
+
...instructionKeys(kind).map((key) => loadPromptAsset("contract", key))
|
|
889
885
|
].join("\n\n") + "\n";
|
|
890
886
|
}
|
|
891
887
|
function contractDigest() {
|
|
@@ -893,68 +889,23 @@ function contractDigest() {
|
|
|
893
889
|
for (const kind of INSTRUCTION_KINDS) hash.update(`${kind}\0${renderInstructions(kind)}\0`);
|
|
894
890
|
return `sha256:${hash.digest("hex").slice(0, 16)}`;
|
|
895
891
|
}
|
|
896
|
-
var
|
|
892
|
+
var SHARED_KEYS, ROLE_KEYS, INSTRUCTION_KINDS;
|
|
897
893
|
var init_instructions = __esm({
|
|
898
894
|
"src/room/instructions.ts"() {
|
|
899
895
|
"use strict";
|
|
900
896
|
init_roles();
|
|
901
897
|
init_prompts();
|
|
902
|
-
AUTHORITY_KEYS = [
|
|
903
|
-
"humanAuthority",
|
|
904
|
-
"evidenceAndEventWaiting",
|
|
905
|
-
"scopeAndUnrelatedWork"
|
|
906
|
-
];
|
|
907
|
-
PROTOCOL_READER_KEYS = [
|
|
908
|
-
"humanAuthority",
|
|
909
|
-
"workspaceProtocolPrecedence",
|
|
910
|
-
"evidenceAndEventWaiting",
|
|
911
|
-
"scopeAndUnrelatedWork"
|
|
912
|
-
];
|
|
913
898
|
SHARED_KEYS = {
|
|
914
|
-
supervisor:
|
|
915
|
-
lead:
|
|
916
|
-
peer:
|
|
899
|
+
supervisor: ["sharedAuthority", "sharedSeatIdentity"],
|
|
900
|
+
lead: ["sharedAuthority", "sharedSeatIdentity", "challengeSignals"],
|
|
901
|
+
peer: ["sharedAuthority", "challengeSignals"]
|
|
917
902
|
};
|
|
918
903
|
ROLE_KEYS = {
|
|
919
|
-
supervisor:
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
"leadDiscoveryAndRecovery",
|
|
923
|
-
"observationAndAdvice",
|
|
924
|
-
"escalationBoundaries"
|
|
925
|
-
],
|
|
926
|
-
lead: [
|
|
927
|
-
"projectTechnicalOwnership",
|
|
928
|
-
"movingWriteOwnership",
|
|
929
|
-
"completePeerBrief",
|
|
930
|
-
"challengeSignals",
|
|
931
|
-
"technicalAcceptance",
|
|
932
|
-
"independentReview",
|
|
933
|
-
"peerSeatLifecycle"
|
|
934
|
-
],
|
|
935
|
-
// Peer needs Challenge Signals as well as its own obligations.
|
|
936
|
-
peer: [
|
|
937
|
-
"challengeSignals",
|
|
938
|
-
"boundedOutcome",
|
|
939
|
-
"independentJudgment",
|
|
940
|
-
"assignmentScope",
|
|
941
|
-
"noOrchestration",
|
|
942
|
-
"reproducibleHandoff",
|
|
943
|
-
"noSelfAcceptance"
|
|
944
|
-
]
|
|
945
|
-
};
|
|
946
|
-
ALL_PROTOCOL_KEYS = [
|
|
947
|
-
"topology",
|
|
948
|
-
"verification",
|
|
949
|
-
"review",
|
|
950
|
-
"repositoryConventions"
|
|
951
|
-
];
|
|
952
|
-
PROTOCOL_KEYS = {
|
|
953
|
-
supervisor: ALL_PROTOCOL_KEYS,
|
|
954
|
-
lead: ALL_PROTOCOL_KEYS,
|
|
955
|
-
peer: []
|
|
904
|
+
supervisor: "supervisor",
|
|
905
|
+
lead: "lead",
|
|
906
|
+
peer: "peer"
|
|
956
907
|
};
|
|
957
|
-
INSTRUCTION_KINDS = [...ROLES
|
|
908
|
+
INSTRUCTION_KINDS = [...ROLES];
|
|
958
909
|
}
|
|
959
910
|
});
|
|
960
911
|
|
|
@@ -1040,14 +991,39 @@ function isPaseoSkill(name) {
|
|
|
1040
991
|
function receivesExecutableResources(role) {
|
|
1041
992
|
return role !== "peer";
|
|
1042
993
|
}
|
|
1043
|
-
|
|
994
|
+
function receivesRoomSkill(role) {
|
|
995
|
+
return role === "lead";
|
|
996
|
+
}
|
|
997
|
+
async function skillNames(path, reserved, keep) {
|
|
1044
998
|
try {
|
|
1045
|
-
return (await readdir2(path)).filter((name) =>
|
|
999
|
+
return (await readdir2(path)).filter((name) => keep(name) && !reserved.has(name.toLowerCase())).sort();
|
|
1046
1000
|
} catch (error) {
|
|
1047
1001
|
if (error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT") return [];
|
|
1048
1002
|
throw error;
|
|
1049
1003
|
}
|
|
1050
1004
|
}
|
|
1005
|
+
async function skillsProjection(input, path, alias, present, reserved) {
|
|
1006
|
+
const roomSkill = receivesRoomSkill(input.role) ? input.roomSkill : void 0;
|
|
1007
|
+
const projectsChildren = !receivesExecutableResources(input.role) || roomSkill !== void 0;
|
|
1008
|
+
if (!projectsChildren) return void 0;
|
|
1009
|
+
const reservedNames = new Set([...reserved].map((name) => name.toLowerCase()));
|
|
1010
|
+
const keep = roomSkill === void 0 ? (name) => !isPaseoSkill(name) : (name) => name.toLowerCase() !== roomSkill.name.toLowerCase();
|
|
1011
|
+
const operator = present.has(path) ? await skillNames(path, reservedNames, keep) : [];
|
|
1012
|
+
const children = roomSkill === void 0 ? operator : [...operator, roomSkill.name];
|
|
1013
|
+
const projection = input.role === "lead" ? input.leadSkillProjection : alias;
|
|
1014
|
+
return [
|
|
1015
|
+
{
|
|
1016
|
+
kind: "managed-dir",
|
|
1017
|
+
path: projection,
|
|
1018
|
+
children,
|
|
1019
|
+
...input.role === "peer" ? { legacyLink: path } : {},
|
|
1020
|
+
...reserved.size === 0 ? {} : { reserved: [...reserved] }
|
|
1021
|
+
},
|
|
1022
|
+
...operator.map((child) => ({ kind: "link", path: join6(projection, child), target: join6(path, child) })),
|
|
1023
|
+
...roomSkill === void 0 ? [] : [{ kind: "link", path: join6(projection, roomSkill.name), target: roomSkill.source }],
|
|
1024
|
+
...input.role === "lead" ? [{ kind: "link", path: alias, target: projection }] : []
|
|
1025
|
+
];
|
|
1026
|
+
}
|
|
1051
1027
|
async function roleResourceEntries(input) {
|
|
1052
1028
|
const present = new Set(input.shared);
|
|
1053
1029
|
const executable = new Set(input.executable);
|
|
@@ -1056,21 +1032,14 @@ async function roleResourceEntries(input) {
|
|
|
1056
1032
|
for (const name of input.names) {
|
|
1057
1033
|
const path = join6(input.home, name);
|
|
1058
1034
|
const alias = join6(input.target, name);
|
|
1059
|
-
if (
|
|
1060
|
-
|
|
1061
|
-
if (
|
|
1062
|
-
|
|
1063
|
-
entries.push({
|
|
1064
|
-
kind: "managed-dir",
|
|
1065
|
-
path: alias,
|
|
1066
|
-
children,
|
|
1067
|
-
legacyLink: path,
|
|
1068
|
-
...reserved.size === 0 ? {} : { reserved: [...reserved] }
|
|
1069
|
-
});
|
|
1070
|
-
for (const child of children) entries.push({ kind: "link", path: join6(alias, child), target: join6(path, child) });
|
|
1035
|
+
if (name === SKILLS) {
|
|
1036
|
+
const projection = await skillsProjection(input, path, alias, present, reserved);
|
|
1037
|
+
if (projection !== void 0) {
|
|
1038
|
+
entries.push(...projection);
|
|
1071
1039
|
continue;
|
|
1072
1040
|
}
|
|
1073
1041
|
}
|
|
1042
|
+
if (!receivesExecutableResources(input.role) && executable.has(name)) continue;
|
|
1074
1043
|
if (!present.has(path)) continue;
|
|
1075
1044
|
entries.push({ kind: "link", path: alias, target: path });
|
|
1076
1045
|
}
|
|
@@ -1084,9 +1053,99 @@ var init_resources = __esm({
|
|
|
1084
1053
|
}
|
|
1085
1054
|
});
|
|
1086
1055
|
|
|
1056
|
+
// src/room/skills.ts
|
|
1057
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
1058
|
+
import { join as join7 } from "path";
|
|
1059
|
+
function validateSkillAsset(path, source) {
|
|
1060
|
+
if (source.includes("\uFFFD")) throw new Error("is not valid UTF-8 text.");
|
|
1061
|
+
if (!source.trim()) throw new Error("is empty.");
|
|
1062
|
+
if (path === "SKILL.md") {
|
|
1063
|
+
const frontmatter = /^---\n([\s\S]*?)\n---\n/.exec(source)?.[1];
|
|
1064
|
+
if (frontmatter === void 0) throw new Error("must begin with YAML frontmatter.");
|
|
1065
|
+
const declaredName = /^name: (.+)$/m.exec(frontmatter)?.[1];
|
|
1066
|
+
if (declaredName !== ROOM_SKILL_NAME) {
|
|
1067
|
+
throw new Error(`frontmatter name must be ${ROOM_SKILL_NAME}.`);
|
|
1068
|
+
}
|
|
1069
|
+
if (!/^description: \S.+$/m.test(frontmatter)) {
|
|
1070
|
+
throw new Error("frontmatter must contain a non-empty single-line description.");
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
if (path === "references/workspace-protocol-template.md" && !source.startsWith("# Workspace protocol template (scaffold)\n")) {
|
|
1074
|
+
throw new Error("must begin with the workspace protocol scaffold heading.");
|
|
1075
|
+
}
|
|
1076
|
+
return source;
|
|
1077
|
+
}
|
|
1078
|
+
function loadSkillAsset(path) {
|
|
1079
|
+
const cached = cache2.get(path);
|
|
1080
|
+
if (cached !== void 0) return cached;
|
|
1081
|
+
const logicalAsset = `${ROOM_SKILL_NAME}/${path}`;
|
|
1082
|
+
const assetUrl = new URL(`./skills/${ROOM_SKILL_NAME}/${path}`, import.meta.url);
|
|
1083
|
+
try {
|
|
1084
|
+
const validated = validateSkillAsset(path, readFileSync2(assetUrl, "utf8"));
|
|
1085
|
+
cache2.set(path, validated);
|
|
1086
|
+
return validated;
|
|
1087
|
+
} catch (error) {
|
|
1088
|
+
if (error instanceof SkillAssetError) throw error;
|
|
1089
|
+
const detail = error instanceof Error ? error.message : "could not be read.";
|
|
1090
|
+
throw new SkillAssetError(logicalAsset, assetUrl, detail, { cause: error });
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
function roomSkillSource(layout) {
|
|
1094
|
+
return join7(sharedRoom(layout), "skills", ROOM_SKILL_NAME);
|
|
1095
|
+
}
|
|
1096
|
+
function skillProjectionRoot(layout) {
|
|
1097
|
+
return join7(sharedRoom(layout), "skill-projections");
|
|
1098
|
+
}
|
|
1099
|
+
function agentSkillProjectionRoot(layout, agent) {
|
|
1100
|
+
return join7(skillProjectionRoot(layout), agent);
|
|
1101
|
+
}
|
|
1102
|
+
function leadSkillProjection(layout, agent) {
|
|
1103
|
+
return join7(agentSkillProjectionRoot(layout, agent), "lead");
|
|
1104
|
+
}
|
|
1105
|
+
function roomSkillEntries(layout, agents) {
|
|
1106
|
+
const root = roomSkillSource(layout);
|
|
1107
|
+
return [
|
|
1108
|
+
{ kind: "dir", path: join7(sharedRoom(layout), "skills") },
|
|
1109
|
+
{ kind: "dir", path: skillProjectionRoot(layout) },
|
|
1110
|
+
...agents.map((agent) => ({ kind: "dir", path: agentSkillProjectionRoot(layout, agent) })),
|
|
1111
|
+
{ kind: "managed-dir", path: root, children: ["SKILL.md", ...SKILL_DIRECTORIES] },
|
|
1112
|
+
{
|
|
1113
|
+
kind: "managed-dir",
|
|
1114
|
+
path: join7(root, "references"),
|
|
1115
|
+
children: SKILL_FILES.filter((path) => path.startsWith("references/")).map((path) => path.slice("references/".length))
|
|
1116
|
+
},
|
|
1117
|
+
...SKILL_FILES.map((path) => ({
|
|
1118
|
+
kind: "file",
|
|
1119
|
+
path: join7(root, path),
|
|
1120
|
+
content: loadSkillAsset(path)
|
|
1121
|
+
}))
|
|
1122
|
+
];
|
|
1123
|
+
}
|
|
1124
|
+
var ROOM_SKILL_NAME, SKILL_DIRECTORIES, SKILL_FILES, SkillAssetError, cache2;
|
|
1125
|
+
var init_skills = __esm({
|
|
1126
|
+
"src/room/skills.ts"() {
|
|
1127
|
+
"use strict";
|
|
1128
|
+
init_layout();
|
|
1129
|
+
ROOM_SKILL_NAME = "paseo-project-onboarding";
|
|
1130
|
+
SKILL_DIRECTORIES = ["references"];
|
|
1131
|
+
SKILL_FILES = ["SKILL.md", "references/workspace-protocol-template.md"];
|
|
1132
|
+
SkillAssetError = class extends Error {
|
|
1133
|
+
logicalAsset;
|
|
1134
|
+
assetUrl;
|
|
1135
|
+
constructor(logicalAsset, assetUrl, detail, options) {
|
|
1136
|
+
super(`Skill asset ${logicalAsset} at ${assetUrl.pathname} ${detail} Reinstall paseo-room and try again.`, options);
|
|
1137
|
+
this.name = "SkillAssetError";
|
|
1138
|
+
this.logicalAsset = logicalAsset;
|
|
1139
|
+
this.assetUrl = assetUrl;
|
|
1140
|
+
}
|
|
1141
|
+
};
|
|
1142
|
+
cache2 = /* @__PURE__ */ new Map();
|
|
1143
|
+
}
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1087
1146
|
// src/agents/claude.ts
|
|
1088
1147
|
import { readFile as readFile2 } from "fs/promises";
|
|
1089
|
-
import { join as
|
|
1148
|
+
import { join as join8 } from "path";
|
|
1090
1149
|
function asObject(value) {
|
|
1091
1150
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
|
|
1092
1151
|
}
|
|
@@ -1105,17 +1164,21 @@ async function readStateIfPresent(path) {
|
|
|
1105
1164
|
throw error;
|
|
1106
1165
|
}
|
|
1107
1166
|
}
|
|
1108
|
-
function renderRoleSettings(
|
|
1109
|
-
const settings =
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1167
|
+
function renderRoleSettings(role, secureStorageDir) {
|
|
1168
|
+
const settings = {
|
|
1169
|
+
// Claude applies settings.env after the launch environment, so repeat provider closures here.
|
|
1170
|
+
env: {
|
|
1171
|
+
...CONTROL_PLANE_ENV,
|
|
1172
|
+
PASEO_ROOM_ROLE: role,
|
|
1173
|
+
...secureStorageDir === void 0 ? {} : { [SECURE_STORAGE_ENV]: secureStorageDir }
|
|
1174
|
+
},
|
|
1175
|
+
// Provider disallowedTools is not sufficient alone; the isolated home carries the same list.
|
|
1176
|
+
permissions: { deny: DISALLOWED_TOOLS },
|
|
1177
|
+
// These restrictive settings cannot be weakened by another settings scope.
|
|
1178
|
+
disableAgentView: true,
|
|
1179
|
+
disableWorkflows: true,
|
|
1180
|
+
crossSessionInbound: "refuse"
|
|
1115
1181
|
};
|
|
1116
|
-
settings.disableAgentView = true;
|
|
1117
|
-
settings.disableWorkflows = true;
|
|
1118
|
-
settings.crossSessionInbound = "refuse";
|
|
1119
1182
|
return JSON.stringify(settings, null, 2) + "\n";
|
|
1120
1183
|
}
|
|
1121
1184
|
function renderRoleState(source) {
|
|
@@ -1132,17 +1195,9 @@ function renderRoleMemory(source, role, contract = true) {
|
|
|
1132
1195
|
|
|
1133
1196
|
${renderInstructions(role)}` : renderInstructions(role);
|
|
1134
1197
|
}
|
|
1135
|
-
function
|
|
1136
|
-
const settings = readObject(settingsSource);
|
|
1137
|
-
const settingsEnv = asObject(settings.env);
|
|
1138
|
-
const names = new Set(presentNames(envNames, AUTH_ENV));
|
|
1139
|
-
for (const name of AUTH_ENV) if (Object.hasOwn(settingsEnv, name)) names.add(name);
|
|
1140
|
-
if (Object.hasOwn(settings, "apiKeyHelper")) names.add("apiKeyHelper");
|
|
1141
|
-
return [...names];
|
|
1142
|
-
}
|
|
1143
|
-
async function claudeCredentialDiagnostic(layout, role, settingsSource, platform = process.platform, binary = "claude") {
|
|
1198
|
+
async function claudeCredentialDiagnostic(layout, role, platform = process.platform, binary = "claude") {
|
|
1144
1199
|
const home = roleHome(layout, "claude", role);
|
|
1145
|
-
const path =
|
|
1200
|
+
const path = join8(home, ".credentials.json");
|
|
1146
1201
|
const roleEnvironment = { CLAUDE_CONFIG_DIR: home, [SECURE_STORAGE_ENV]: home };
|
|
1147
1202
|
const login = roleCommand(roleEnvironment, binary, ["auth", "login"]);
|
|
1148
1203
|
const status = roleCommand(roleEnvironment, binary, ["auth", "status"]);
|
|
@@ -1151,8 +1206,6 @@ async function claudeCredentialDiagnostic(layout, role, settingsSource, platform
|
|
|
1151
1206
|
if (state.kind !== "missing") {
|
|
1152
1207
|
return { path, checks: [preservedCredentialCheck({ id, agent: "Claude", role, path, state, login, status })] };
|
|
1153
1208
|
}
|
|
1154
|
-
const configured = claudeAuthMethodNames(settingsSource, /* @__PURE__ */ new Set());
|
|
1155
|
-
if (configured.length > 0) return { path, checks: [configuredByNamesCheck(id, "Claude", role, configured)] };
|
|
1156
1209
|
const ambient = presentNames(layout.envNames, AUTH_ENV);
|
|
1157
1210
|
if (ambient.length > 0) return { path, checks: [ambientNamesCheck(id, "Claude", role, ambient)] };
|
|
1158
1211
|
if (platform === "darwin") {
|
|
@@ -1166,7 +1219,7 @@ async function claudeCredentialDiagnostic(layout, role, settingsSource, platform
|
|
|
1166
1219
|
return { path, checks: [{
|
|
1167
1220
|
id: `${id}.login-required`,
|
|
1168
1221
|
status: "warn",
|
|
1169
|
-
message: `Claude ${role} auth: login-required; no role-owned .credentials.json or
|
|
1222
|
+
message: `Claude ${role} auth: login-required; no role-owned .credentials.json or supported ambient auth environment name was detected. Authentication was not attempted.`,
|
|
1170
1223
|
fix: `Authenticate this role with: ${login}. If that subcommand is unavailable, launch ${roleCommand(roleEnvironment, binary)} and run /login. Check status with: ${status}.`
|
|
1171
1224
|
}] };
|
|
1172
1225
|
}
|
|
@@ -1181,7 +1234,7 @@ function mcpDriftCheck(role, home, path, divergence) {
|
|
|
1181
1234
|
`If this role should have the same servers, add them with: CLAUDE_CONFIG_DIR=${home} claude mcp add ... \u2014 or delete ${path} and run setup --apply to reseed it from your current Claude state, which discards that role's other runtime state.`
|
|
1182
1235
|
);
|
|
1183
1236
|
}
|
|
1184
|
-
var SHARED, EXECUTABLE, RUNTIME_SKILLS, MCP_KEY, AUTH_ENV, CONTROL_PLANE_ENV, SECURE_STORAGE_ENV, SEEDED_KEYS, claudeAgent;
|
|
1237
|
+
var SHARED, EXECUTABLE, RUNTIME_SKILLS, MCP_KEY, AUTH_ENV, CONTROL_PLANE_ENV, DISALLOWED_TOOLS, SECURE_STORAGE_ENV, SEEDED_KEYS, claudeAgent;
|
|
1185
1238
|
var init_claude = __esm({
|
|
1186
1239
|
"src/agents/claude.ts"() {
|
|
1187
1240
|
"use strict";
|
|
@@ -1193,6 +1246,7 @@ var init_claude = __esm({
|
|
|
1193
1246
|
init_which();
|
|
1194
1247
|
init_mcp();
|
|
1195
1248
|
init_resources();
|
|
1249
|
+
init_skills();
|
|
1196
1250
|
SHARED = [
|
|
1197
1251
|
"skills",
|
|
1198
1252
|
"plugins",
|
|
@@ -1218,6 +1272,22 @@ var init_claude = __esm({
|
|
|
1218
1272
|
CLAUDE_CODE_DISABLE_AGENT_VIEW: "1",
|
|
1219
1273
|
CLAUDE_CODE_DISABLE_WORKFLOWS: "1"
|
|
1220
1274
|
};
|
|
1275
|
+
DISALLOWED_TOOLS = [
|
|
1276
|
+
"Task",
|
|
1277
|
+
"Agent",
|
|
1278
|
+
"Workflow",
|
|
1279
|
+
"ListAgents",
|
|
1280
|
+
"SendMessage",
|
|
1281
|
+
"TeamCreate",
|
|
1282
|
+
"TeamDelete",
|
|
1283
|
+
"TaskCreate",
|
|
1284
|
+
"TaskGet",
|
|
1285
|
+
"TaskList",
|
|
1286
|
+
"TaskUpdate",
|
|
1287
|
+
"CronCreate",
|
|
1288
|
+
"CronDelete",
|
|
1289
|
+
"CronList"
|
|
1290
|
+
];
|
|
1221
1291
|
SECURE_STORAGE_ENV = "CLAUDE_SECURESTORAGE_CONFIG_DIR";
|
|
1222
1292
|
SEEDED_KEYS = ["hasCompletedOnboarding", "theme", "installMethod", "userID", MCP_KEY];
|
|
1223
1293
|
claudeAgent = {
|
|
@@ -1230,24 +1300,7 @@ var init_claude = __esm({
|
|
|
1230
1300
|
// Keep the legacy Task name and block current native orchestration,
|
|
1231
1301
|
// shared task/cron coordination, and cross-session paths. Paseo alone owns
|
|
1232
1302
|
// agent lifecycle and coordination.
|
|
1233
|
-
pins: {
|
|
1234
|
-
disallowedTools: [
|
|
1235
|
-
"Task",
|
|
1236
|
-
"Agent",
|
|
1237
|
-
"Workflow",
|
|
1238
|
-
"ListAgents",
|
|
1239
|
-
"SendMessage",
|
|
1240
|
-
"TeamCreate",
|
|
1241
|
-
"TeamDelete",
|
|
1242
|
-
"TaskCreate",
|
|
1243
|
-
"TaskGet",
|
|
1244
|
-
"TaskList",
|
|
1245
|
-
"TaskUpdate",
|
|
1246
|
-
"CronCreate",
|
|
1247
|
-
"CronDelete",
|
|
1248
|
-
"CronList"
|
|
1249
|
-
]
|
|
1250
|
-
},
|
|
1303
|
+
pins: { disallowedTools: DISALLOWED_TOOLS },
|
|
1251
1304
|
async build(layout, roles, options = {}) {
|
|
1252
1305
|
const home = layout.agentHome.claude;
|
|
1253
1306
|
const binary = await which(layout.bin.claude, layout.searchPath);
|
|
@@ -1258,8 +1311,7 @@ var init_claude = __esm({
|
|
|
1258
1311
|
return { entries: [], checks: [fail("claude.home", `No Claude config directory at ${home}.`, "Run claude once to initialise it, or pass --claude-home.")] };
|
|
1259
1312
|
}
|
|
1260
1313
|
const checks = [pass("claude.home", `Claude Code found at ${binary} using ${home}.`)];
|
|
1261
|
-
const
|
|
1262
|
-
const memorySource = await readIfPresent(join7(home, "CLAUDE.md"));
|
|
1314
|
+
const memorySource = await readIfPresent(join8(home, "CLAUDE.md"));
|
|
1263
1315
|
let stateSource;
|
|
1264
1316
|
let operatorServers;
|
|
1265
1317
|
try {
|
|
@@ -1279,9 +1331,10 @@ var init_claude = __esm({
|
|
|
1279
1331
|
const credentials = [];
|
|
1280
1332
|
const providerEnv = {};
|
|
1281
1333
|
const drift = [];
|
|
1334
|
+
const roomSkill = { name: ROOM_SKILL_NAME, source: roomSkillSource(layout) };
|
|
1282
1335
|
for (const role of roles) {
|
|
1283
1336
|
const target = roleHome(layout, "claude", role);
|
|
1284
|
-
const statePath =
|
|
1337
|
+
const statePath = join8(target, ".claude.json");
|
|
1285
1338
|
let roleStateSource;
|
|
1286
1339
|
let roleServers;
|
|
1287
1340
|
try {
|
|
@@ -1301,13 +1354,23 @@ var init_claude = __esm({
|
|
|
1301
1354
|
if (divergence) drift.push(mcpDriftCheck(role, target, statePath, divergence));
|
|
1302
1355
|
}
|
|
1303
1356
|
entries.push({ kind: "dir", path: target });
|
|
1304
|
-
const memoryPath =
|
|
1357
|
+
const memoryPath = join8(target, "CLAUDE.md");
|
|
1305
1358
|
const memory = renderRoleMemory(memorySource, role, options.memoryContract ?? true);
|
|
1306
1359
|
entries.push(memory === void 0 ? { kind: "absent", path: memoryPath } : { kind: "file", path: memoryPath, content: memory });
|
|
1307
|
-
entries.push({ kind: "file", path:
|
|
1360
|
+
entries.push({ kind: "file", path: join8(target, "settings.json"), content: renderRoleSettings(role, target) });
|
|
1308
1361
|
entries.push({ kind: "file", path: statePath, content: renderRoleState(stateSource), once: true });
|
|
1309
|
-
entries.push(...await roleResourceEntries({
|
|
1310
|
-
|
|
1362
|
+
entries.push(...await roleResourceEntries({
|
|
1363
|
+
role,
|
|
1364
|
+
target,
|
|
1365
|
+
home,
|
|
1366
|
+
names: SHARED,
|
|
1367
|
+
shared,
|
|
1368
|
+
executable: EXECUTABLE,
|
|
1369
|
+
reservedSkills: RUNTIME_SKILLS,
|
|
1370
|
+
roomSkill,
|
|
1371
|
+
leadSkillProjection: leadSkillProjection(layout, "claude")
|
|
1372
|
+
}));
|
|
1373
|
+
credentials.push(await claudeCredentialDiagnostic(layout, role, process.platform, binary));
|
|
1311
1374
|
providerEnv[role] = { [SECURE_STORAGE_ENV]: target };
|
|
1312
1375
|
}
|
|
1313
1376
|
return { entries, credentials, checks: [...checks, ...drift], binary, providerEnv };
|
|
@@ -1317,7 +1380,7 @@ var init_claude = __esm({
|
|
|
1317
1380
|
});
|
|
1318
1381
|
|
|
1319
1382
|
// src/agents/codex.ts
|
|
1320
|
-
import { join as
|
|
1383
|
+
import { join as join9 } from "path";
|
|
1321
1384
|
import { parse, stringify } from "smol-toml";
|
|
1322
1385
|
function table(parent, key) {
|
|
1323
1386
|
const value = parent[key];
|
|
@@ -1374,7 +1437,7 @@ function codexCredentialStore(config) {
|
|
|
1374
1437
|
}
|
|
1375
1438
|
async function credentialDiagnostic(layout, role, store, binary) {
|
|
1376
1439
|
const home = roleHome(layout, "codex", role);
|
|
1377
|
-
const path =
|
|
1440
|
+
const path = join9(home, "auth.json");
|
|
1378
1441
|
const login = roleCommand({ CODEX_HOME: home }, binary, ["login"]);
|
|
1379
1442
|
const status = roleCommand({ CODEX_HOME: home }, binary, ["login", "status"]);
|
|
1380
1443
|
const state = await inspectCredentialPath(path);
|
|
@@ -1423,6 +1486,7 @@ var init_codex = __esm({
|
|
|
1423
1486
|
init_which();
|
|
1424
1487
|
init_mcp();
|
|
1425
1488
|
init_resources();
|
|
1489
|
+
init_skills();
|
|
1426
1490
|
SHARED2 = ["AGENTS.md", "skills", "plugins", "hooks.json"];
|
|
1427
1491
|
EXECUTABLE2 = ["plugins", "hooks.json"];
|
|
1428
1492
|
CATALOG = "model-catalog.json";
|
|
@@ -1441,7 +1505,7 @@ var init_codex = __esm({
|
|
|
1441
1505
|
if (!binary) {
|
|
1442
1506
|
return { entries: [], checks: [fail("codex.bin", "Codex executable not found.", "Install Codex, or pass --codex-bin /path/to/codex.")] };
|
|
1443
1507
|
}
|
|
1444
|
-
const configPath =
|
|
1508
|
+
const configPath = join9(home, "config.toml");
|
|
1445
1509
|
const raw = await readIfPresent(configPath);
|
|
1446
1510
|
if (raw === void 0) {
|
|
1447
1511
|
return { entries: [], checks: [fail("codex.home", `No config.toml in ${home}.`, "Run codex once to initialise it, or pass --codex-home.")] };
|
|
@@ -1489,19 +1553,29 @@ var init_codex = __esm({
|
|
|
1489
1553
|
}
|
|
1490
1554
|
checks.push(pass("codex.catalog", evidence2.markers === 0 ? `Codex model catalog captured from ${capture}; it declared no multi_agent_version field, and each role home gets that generated copy instead of Codex's built-in catalog.` : `Codex model catalog captured from ${capture}; ${String(evidence2.markers)} multi_agent_version field(s) are nulled in the generated copy in each role home.`));
|
|
1491
1555
|
const shared = await existingPaths(home, SHARED2);
|
|
1556
|
+
const roomSkill = { name: ROOM_SKILL_NAME, source: roomSkillSource(layout) };
|
|
1492
1557
|
for (const role of roles) {
|
|
1493
1558
|
const target = roleHome(layout, "codex", role);
|
|
1494
|
-
const catalogPath =
|
|
1559
|
+
const catalogPath = join9(target, CATALOG);
|
|
1495
1560
|
const roleDocument = renderInstructions(role);
|
|
1496
1561
|
entries.push({ kind: "dir", path: target });
|
|
1497
|
-
entries.push({ kind: "file", path:
|
|
1562
|
+
entries.push({ kind: "file", path: join9(target, "role-instructions.md"), content: roleDocument });
|
|
1498
1563
|
entries.push({
|
|
1499
1564
|
kind: "file",
|
|
1500
|
-
path:
|
|
1565
|
+
path: join9(target, "config.toml"),
|
|
1501
1566
|
content: renderRoleConfig(source, { roleDocument, catalogPath })
|
|
1502
1567
|
});
|
|
1503
1568
|
entries.push({ kind: "file", path: catalogPath, content: catalogSource });
|
|
1504
|
-
entries.push(...await roleResourceEntries({
|
|
1569
|
+
entries.push(...await roleResourceEntries({
|
|
1570
|
+
role,
|
|
1571
|
+
target,
|
|
1572
|
+
home,
|
|
1573
|
+
names: SHARED2,
|
|
1574
|
+
shared,
|
|
1575
|
+
executable: EXECUTABLE2,
|
|
1576
|
+
roomSkill,
|
|
1577
|
+
leadSkillProjection: leadSkillProjection(layout, "codex")
|
|
1578
|
+
}));
|
|
1505
1579
|
credentials.push(await credentialDiagnostic(layout, role, store, binary));
|
|
1506
1580
|
}
|
|
1507
1581
|
return { entries, credentials, checks, binary };
|
|
@@ -1514,7 +1588,7 @@ var init_codex = __esm({
|
|
|
1514
1588
|
import { spawn as spawn2 } from "child_process";
|
|
1515
1589
|
import { mkdtemp, readFile as readFile3, realpath, rm as rm2, stat } from "fs/promises";
|
|
1516
1590
|
import { tmpdir } from "os";
|
|
1517
|
-
import { isAbsolute as isAbsolute2, join as
|
|
1591
|
+
import { isAbsolute as isAbsolute2, join as join10, resolve as resolve3 } from "path";
|
|
1518
1592
|
function asObject2(value) {
|
|
1519
1593
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? { ...value } : void 0;
|
|
1520
1594
|
}
|
|
@@ -1538,12 +1612,12 @@ function adapterFailure(home, detail) {
|
|
|
1538
1612
|
return fail(
|
|
1539
1613
|
"pi.adapter",
|
|
1540
1614
|
detail,
|
|
1541
|
-
`Install ${PACKAGE_NAME} in ${
|
|
1615
|
+
`Install ${PACKAGE_NAME} in ${join10(home, "npm")} with Pi yourself, then run setup again.`
|
|
1542
1616
|
);
|
|
1543
1617
|
}
|
|
1544
1618
|
async function resolvePiAdapter(home) {
|
|
1545
|
-
const packageRoot =
|
|
1546
|
-
const manifestPath =
|
|
1619
|
+
const packageRoot = join10(home, "npm", "node_modules", PACKAGE_NAME);
|
|
1620
|
+
const manifestPath = join10(packageRoot, "package.json");
|
|
1547
1621
|
try {
|
|
1548
1622
|
const [canonicalRoot, manifestStat, raw] = await Promise.all([
|
|
1549
1623
|
realpath(packageRoot),
|
|
@@ -1651,7 +1725,7 @@ function runProbeProcess(executable, adapterEntry, env, options, cwd) {
|
|
|
1651
1725
|
});
|
|
1652
1726
|
}
|
|
1653
1727
|
async function runProbe(executable, adapterEntry, env, options) {
|
|
1654
|
-
const createdCwd = await mkdtemp(
|
|
1728
|
+
const createdCwd = await mkdtemp(join10(tmpdir(), "paseo-room-pi-probe-"));
|
|
1655
1729
|
const cwd = await realpath(createdCwd);
|
|
1656
1730
|
try {
|
|
1657
1731
|
return await runProbeProcess(executable, adapterEntry, {
|
|
@@ -1728,7 +1802,7 @@ async function readPiOptional(path) {
|
|
|
1728
1802
|
}
|
|
1729
1803
|
async function piCredentialDiagnostic(layout, role, binary = "pi") {
|
|
1730
1804
|
const home = roleHome(layout, "pi", role);
|
|
1731
|
-
const path =
|
|
1805
|
+
const path = join10(home, "auth.json");
|
|
1732
1806
|
const launch = piLoginCommand(home, binary);
|
|
1733
1807
|
const state = await inspectCredentialPath(path);
|
|
1734
1808
|
const id = `pi.auth.${role}`;
|
|
@@ -1768,6 +1842,7 @@ var init_pi = __esm({
|
|
|
1768
1842
|
init_which();
|
|
1769
1843
|
init_mcp();
|
|
1770
1844
|
init_resources();
|
|
1845
|
+
init_skills();
|
|
1771
1846
|
PACKAGE_NAME = "pi-mcp-adapter";
|
|
1772
1847
|
PROBE_ID = "paseo-room-pi-mcp-probe";
|
|
1773
1848
|
PROBE_TIMEOUT_MS = 1e4;
|
|
@@ -1824,7 +1899,7 @@ var init_pi = __esm({
|
|
|
1824
1899
|
PI_OFFLINE: "1"
|
|
1825
1900
|
});
|
|
1826
1901
|
if (capability.status === "fail") return { entries: [], checks: [resolved.check, capability] };
|
|
1827
|
-
const settingsPath =
|
|
1902
|
+
const settingsPath = join10(home, "settings.json");
|
|
1828
1903
|
let settings;
|
|
1829
1904
|
try {
|
|
1830
1905
|
settings = renderPiSettings(await readPiOptional(settingsPath));
|
|
@@ -1842,7 +1917,7 @@ var init_pi = __esm({
|
|
|
1842
1917
|
]
|
|
1843
1918
|
};
|
|
1844
1919
|
}
|
|
1845
|
-
const operatorAppendPath =
|
|
1920
|
+
const operatorAppendPath = join10(home, "APPEND_SYSTEM.md");
|
|
1846
1921
|
let operatorAppend;
|
|
1847
1922
|
try {
|
|
1848
1923
|
operatorAppend = await readPiOptional(operatorAppendPath);
|
|
@@ -1861,7 +1936,7 @@ var init_pi = __esm({
|
|
|
1861
1936
|
};
|
|
1862
1937
|
}
|
|
1863
1938
|
const shared = await existingPaths(home, SHARED3);
|
|
1864
|
-
const mcpPath =
|
|
1939
|
+
const mcpPath = join10(home, MCP_FILE);
|
|
1865
1940
|
let servers;
|
|
1866
1941
|
try {
|
|
1867
1942
|
servers = jsonServerTable(await readPiOptional(mcpPath), MCP_KEYS);
|
|
@@ -1884,13 +1959,23 @@ var init_pi = __esm({
|
|
|
1884
1959
|
const entries = [];
|
|
1885
1960
|
const credentials = [];
|
|
1886
1961
|
const argv = {};
|
|
1962
|
+
const roomSkill = { name: ROOM_SKILL_NAME, source: roomSkillSource(layout) };
|
|
1887
1963
|
for (const role of roles) {
|
|
1888
1964
|
const target = roleHome(layout, "pi", role);
|
|
1889
|
-
const appendPath =
|
|
1965
|
+
const appendPath = join10(target, "APPEND_SYSTEM.md");
|
|
1890
1966
|
entries.push({ kind: "dir", path: target });
|
|
1891
|
-
entries.push({ kind: "file", path:
|
|
1967
|
+
entries.push({ kind: "file", path: join10(target, "settings.json"), content: settings });
|
|
1892
1968
|
entries.push({ kind: "file", path: appendPath, content: renderPiAppend(operatorAppend, role) });
|
|
1893
|
-
entries.push(...await roleResourceEntries({
|
|
1969
|
+
entries.push(...await roleResourceEntries({
|
|
1970
|
+
role,
|
|
1971
|
+
target,
|
|
1972
|
+
home,
|
|
1973
|
+
names: SHARED3,
|
|
1974
|
+
shared,
|
|
1975
|
+
executable: EXECUTABLE3,
|
|
1976
|
+
roomSkill,
|
|
1977
|
+
leadSkillProjection: leadSkillProjection(layout, "pi")
|
|
1978
|
+
}));
|
|
1894
1979
|
credentials.push(await piCredentialDiagnostic(layout, role, binary));
|
|
1895
1980
|
argv[role] = ["--no-extensions", "--extension", resolved.adapter.entry, "--no-approve", "--append-system-prompt", appendPath];
|
|
1896
1981
|
}
|
|
@@ -2116,20 +2201,29 @@ var init_paseo = __esm({
|
|
|
2116
2201
|
});
|
|
2117
2202
|
|
|
2118
2203
|
// src/plugin.ts
|
|
2119
|
-
import { readFileSync as
|
|
2120
|
-
import { join as
|
|
2204
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
2205
|
+
import { join as join11 } from "path";
|
|
2121
2206
|
function loadAsset(path) {
|
|
2122
|
-
return
|
|
2207
|
+
return readFileSync3(new URL(`./plugin-assets/${path}`, import.meta.url), "utf8");
|
|
2123
2208
|
}
|
|
2124
2209
|
function claudeCarrierPluginDir(layout) {
|
|
2125
|
-
return
|
|
2210
|
+
return join11(layout.roomHome, "plugin");
|
|
2211
|
+
}
|
|
2212
|
+
function typescriptTemplateLiteral(value) {
|
|
2213
|
+
const escaped = value.replaceAll("\\", "\\\\").replaceAll("`", "\\`").replaceAll("${", "\\${");
|
|
2214
|
+
return `\`${escaped}\``;
|
|
2126
2215
|
}
|
|
2127
2216
|
function renderClaudeCarrierContract(roles) {
|
|
2128
|
-
const
|
|
2217
|
+
const entries = roles.map((role) => {
|
|
2218
|
+
const id = JSON.stringify(providerId("claude", role));
|
|
2219
|
+
return ` ${id}: ${typescriptTemplateLiteral(renderInstructions(role))},`;
|
|
2220
|
+
});
|
|
2129
2221
|
return [
|
|
2130
2222
|
"/** Generated by paseo-room. The Markdown prompt assets remain authoritative. */",
|
|
2131
2223
|
`export const GENERATION = ${JSON.stringify(contractDigest())};`,
|
|
2132
|
-
|
|
2224
|
+
"export const CONTRACT: Readonly<Record<string, string>> = {",
|
|
2225
|
+
...entries,
|
|
2226
|
+
"};",
|
|
2133
2227
|
""
|
|
2134
2228
|
].join("\n");
|
|
2135
2229
|
}
|
|
@@ -2137,9 +2231,9 @@ function claudeCarrierEntries(layout, roles) {
|
|
|
2137
2231
|
const root = claudeCarrierPluginDir(layout);
|
|
2138
2232
|
return [
|
|
2139
2233
|
{ kind: "dir", path: root },
|
|
2140
|
-
{ kind: "dir", path:
|
|
2141
|
-
...STATIC_ASSETS.map((path) => ({ kind: "file", path:
|
|
2142
|
-
{ kind: "file", path:
|
|
2234
|
+
{ kind: "dir", path: join11(root, "server") },
|
|
2235
|
+
...STATIC_ASSETS.map((path) => ({ kind: "file", path: join11(root, path), content: loadAsset(path) })),
|
|
2236
|
+
{ kind: "file", path: join11(root, "server", "contract.ts"), content: renderClaudeCarrierContract(roles) }
|
|
2143
2237
|
];
|
|
2144
2238
|
}
|
|
2145
2239
|
var CLAUDE_CARRIER_PLUGIN_ID, STATIC_ASSETS;
|
|
@@ -2160,7 +2254,7 @@ var init_plugin = __esm({
|
|
|
2160
2254
|
});
|
|
2161
2255
|
|
|
2162
2256
|
// src/commands.ts
|
|
2163
|
-
import { join as
|
|
2257
|
+
import { join as join12, sep as sep2 } from "path";
|
|
2164
2258
|
import { lstat as lstat4, rm as rm3 } from "fs/promises";
|
|
2165
2259
|
function sessionOptions(options) {
|
|
2166
2260
|
const password = (options.env ?? process.env).PASEO_PASSWORD;
|
|
@@ -2192,7 +2286,10 @@ async function buildDesired(layout, agents, roles, build = {}) {
|
|
|
2192
2286
|
const entries = [
|
|
2193
2287
|
{ kind: "dir", path: layout.roomHome },
|
|
2194
2288
|
{ kind: "dir", path: sharedRoom(layout) },
|
|
2195
|
-
|
|
2289
|
+
// Earlier versions generated a default workspace protocol template here. The room ships no
|
|
2290
|
+
// default protocol now, so the old generated regular file is declared absent and removed.
|
|
2291
|
+
{ kind: "absent", path: join12(sharedRoom(layout), "WORKSPACE_PROTOCOL.md") },
|
|
2292
|
+
...roomSkillEntries(layout, agents)
|
|
2196
2293
|
];
|
|
2197
2294
|
const providers = {};
|
|
2198
2295
|
const profiles = [];
|
|
@@ -2226,18 +2323,18 @@ async function buildDesired(layout, agents, roles, build = {}) {
|
|
|
2226
2323
|
if (agents.every((id) => binaries[id] !== void 0)) {
|
|
2227
2324
|
entries.push({
|
|
2228
2325
|
kind: "file",
|
|
2229
|
-
path:
|
|
2326
|
+
path: join12(layout.roomHome, AUTHENTICATION_GUIDE),
|
|
2230
2327
|
content: renderAuthenticationGuide(layout.roomHome, binaries, agents, roles)
|
|
2231
2328
|
});
|
|
2232
2329
|
checks.push(warn(
|
|
2233
2330
|
"room.authentication-not-validated",
|
|
2234
|
-
`Role authentication was not validated. Login instructions are managed at ${
|
|
2331
|
+
`Role authentication was not validated. Login instructions are managed at ${join12(layout.roomHome, AUTHENTICATION_GUIDE)}.`,
|
|
2235
2332
|
"After setup --apply, follow that guide or run: paseo-room auth login <agent> <role>"
|
|
2236
2333
|
));
|
|
2237
2334
|
}
|
|
2238
2335
|
const pluginPath = agents.includes("claude") ? claudeCarrierPluginDir(layout) : void 0;
|
|
2239
2336
|
if (pluginPath !== void 0) entries.push(...claudeCarrierEntries(layout, roles));
|
|
2240
|
-
entries.push({ kind: "file", path:
|
|
2337
|
+
entries.push({ kind: "file", path: join12(layout.roomHome, MARKER), content: renderMarker(package_default.version, agents, roles, contractDigest(), markerMemoryContract(agents, build.memoryContract ?? true)) });
|
|
2241
2338
|
return { entries, providers, profiles, checks, ...pluginPath === void 0 ? {} : { pluginPath } };
|
|
2242
2339
|
}
|
|
2243
2340
|
function contractProvenance(previous) {
|
|
@@ -2300,11 +2397,26 @@ async function safeDirectory(path, checks) {
|
|
|
2300
2397
|
async function roomPathSafety(layout, agents, roles) {
|
|
2301
2398
|
const checks = [];
|
|
2302
2399
|
if (!await safeDirectory(layout.roomHome, checks)) return checks;
|
|
2303
|
-
|
|
2304
|
-
const
|
|
2400
|
+
const room = sharedRoom(layout);
|
|
2401
|
+
const roomIsSafe = await safeDirectory(room, checks);
|
|
2402
|
+
if (roomIsSafe && agents.length > 0) {
|
|
2403
|
+
const skills = join12(room, "skills");
|
|
2404
|
+
if (await safeDirectory(skills, checks)) {
|
|
2405
|
+
const source = roomSkillSource(layout);
|
|
2406
|
+
if (await safeDirectory(source, checks)) await safeDirectory(join12(source, "references"), checks);
|
|
2407
|
+
}
|
|
2408
|
+
const projections = skillProjectionRoot(layout);
|
|
2409
|
+
if (await safeDirectory(projections, checks)) {
|
|
2410
|
+
for (const agent of agents) {
|
|
2411
|
+
const agentRoot = agentSkillProjectionRoot(layout, agent);
|
|
2412
|
+
if (await safeDirectory(agentRoot, checks)) await safeDirectory(leadSkillProjection(layout, agent), checks);
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
const rolesHome = join12(layout.roomHome, "roles");
|
|
2305
2417
|
if (!await safeDirectory(rolesHome, checks)) return checks;
|
|
2306
2418
|
for (const agent of agents) {
|
|
2307
|
-
const agentHome =
|
|
2419
|
+
const agentHome = join12(rolesHome, agent);
|
|
2308
2420
|
if (!await safeDirectory(agentHome, checks)) continue;
|
|
2309
2421
|
for (const role of roles) await safeDirectory(roleHome(layout, agent, role), checks);
|
|
2310
2422
|
}
|
|
@@ -2484,7 +2596,7 @@ async function setup(options = {}) {
|
|
|
2484
2596
|
operations
|
|
2485
2597
|
};
|
|
2486
2598
|
}
|
|
2487
|
-
const markerPath =
|
|
2599
|
+
const markerPath = join12(layout.roomHome, MARKER);
|
|
2488
2600
|
const markerEntries = desired.entries.filter((entry) => entry.path === markerPath);
|
|
2489
2601
|
await applyEntries(desired.entries.filter((entry) => entry.path !== markerPath));
|
|
2490
2602
|
const ids = Object.keys(desired.providers);
|
|
@@ -2673,6 +2785,7 @@ var init_commands = __esm({
|
|
|
2673
2785
|
init_plugin();
|
|
2674
2786
|
init_result();
|
|
2675
2787
|
init_instructions();
|
|
2788
|
+
init_skills();
|
|
2676
2789
|
init_room();
|
|
2677
2790
|
init_roles();
|
|
2678
2791
|
AGENTS = { codex: codexAgent, claude: claudeAgent, pi: piAgent };
|
|
@@ -2786,6 +2899,7 @@ function renderHuman(result) {
|
|
|
2786
2899
|
init_result();
|
|
2787
2900
|
init_roles();
|
|
2788
2901
|
init_prompts();
|
|
2902
|
+
init_skills();
|
|
2789
2903
|
function collectAgent(value, previous) {
|
|
2790
2904
|
const agent = AGENT_IDS.find((id) => id === value);
|
|
2791
2905
|
if (!agent) throw new InvalidArgumentError(`Unknown agent: ${value}`);
|
|
@@ -2817,6 +2931,13 @@ function failedFromThrown(command, error) {
|
|
|
2817
2931
|
"Reinstall paseo-room, then try again."
|
|
2818
2932
|
)]);
|
|
2819
2933
|
}
|
|
2934
|
+
if (error instanceof SkillAssetError) {
|
|
2935
|
+
return failed(command, [fail(
|
|
2936
|
+
`${command}.skill-asset`,
|
|
2937
|
+
error.message,
|
|
2938
|
+
"Reinstall paseo-room, then try again."
|
|
2939
|
+
)]);
|
|
2940
|
+
}
|
|
2820
2941
|
if (error instanceof ManagedPathError) {
|
|
2821
2942
|
return failed(command, [fail(`${command}.managed-path`, error.message, error.fix)]);
|
|
2822
2943
|
}
|