paseo-room 0.4.0 → 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 +345 -212
- 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`,
|
|
@@ -270,7 +264,7 @@ async function lstatOrAbsent(path) {
|
|
|
270
264
|
}
|
|
271
265
|
}
|
|
272
266
|
async function staleChildren(entry) {
|
|
273
|
-
const declared = new Set(entry.children);
|
|
267
|
+
const declared = /* @__PURE__ */ new Set([...entry.children, ...entry.reserved ?? []]);
|
|
274
268
|
let names;
|
|
275
269
|
try {
|
|
276
270
|
names = await readdir(entry.path);
|
|
@@ -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,30 +991,55 @@ 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);
|
|
1030
|
+
const reserved = new Set(input.reservedSkills ?? []);
|
|
1054
1031
|
const entries = [];
|
|
1055
1032
|
for (const name of input.names) {
|
|
1056
1033
|
const path = join6(input.home, name);
|
|
1057
1034
|
const alias = join6(input.target, name);
|
|
1058
|
-
if (
|
|
1059
|
-
|
|
1060
|
-
if (
|
|
1061
|
-
|
|
1062
|
-
entries.push({ kind: "managed-dir", path: alias, children, legacyLink: path });
|
|
1063
|
-
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);
|
|
1064
1039
|
continue;
|
|
1065
1040
|
}
|
|
1066
1041
|
}
|
|
1042
|
+
if (!receivesExecutableResources(input.role) && executable.has(name)) continue;
|
|
1067
1043
|
if (!present.has(path)) continue;
|
|
1068
1044
|
entries.push({ kind: "link", path: alias, target: path });
|
|
1069
1045
|
}
|
|
@@ -1077,9 +1053,99 @@ var init_resources = __esm({
|
|
|
1077
1053
|
}
|
|
1078
1054
|
});
|
|
1079
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
|
+
|
|
1080
1146
|
// src/agents/claude.ts
|
|
1081
1147
|
import { readFile as readFile2 } from "fs/promises";
|
|
1082
|
-
import { join as
|
|
1148
|
+
import { join as join8 } from "path";
|
|
1083
1149
|
function asObject(value) {
|
|
1084
1150
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
|
|
1085
1151
|
}
|
|
@@ -1098,17 +1164,21 @@ async function readStateIfPresent(path) {
|
|
|
1098
1164
|
throw error;
|
|
1099
1165
|
}
|
|
1100
1166
|
}
|
|
1101
|
-
function renderRoleSettings(
|
|
1102
|
-
const settings =
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
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"
|
|
1108
1181
|
};
|
|
1109
|
-
settings.disableAgentView = true;
|
|
1110
|
-
settings.disableWorkflows = true;
|
|
1111
|
-
settings.crossSessionInbound = "refuse";
|
|
1112
1182
|
return JSON.stringify(settings, null, 2) + "\n";
|
|
1113
1183
|
}
|
|
1114
1184
|
function renderRoleState(source) {
|
|
@@ -1125,17 +1195,9 @@ function renderRoleMemory(source, role, contract = true) {
|
|
|
1125
1195
|
|
|
1126
1196
|
${renderInstructions(role)}` : renderInstructions(role);
|
|
1127
1197
|
}
|
|
1128
|
-
function
|
|
1129
|
-
const settings = readObject(settingsSource);
|
|
1130
|
-
const settingsEnv = asObject(settings.env);
|
|
1131
|
-
const names = new Set(presentNames(envNames, AUTH_ENV));
|
|
1132
|
-
for (const name of AUTH_ENV) if (Object.hasOwn(settingsEnv, name)) names.add(name);
|
|
1133
|
-
if (Object.hasOwn(settings, "apiKeyHelper")) names.add("apiKeyHelper");
|
|
1134
|
-
return [...names];
|
|
1135
|
-
}
|
|
1136
|
-
async function claudeCredentialDiagnostic(layout, role, settingsSource, platform = process.platform, binary = "claude") {
|
|
1198
|
+
async function claudeCredentialDiagnostic(layout, role, platform = process.platform, binary = "claude") {
|
|
1137
1199
|
const home = roleHome(layout, "claude", role);
|
|
1138
|
-
const path =
|
|
1200
|
+
const path = join8(home, ".credentials.json");
|
|
1139
1201
|
const roleEnvironment = { CLAUDE_CONFIG_DIR: home, [SECURE_STORAGE_ENV]: home };
|
|
1140
1202
|
const login = roleCommand(roleEnvironment, binary, ["auth", "login"]);
|
|
1141
1203
|
const status = roleCommand(roleEnvironment, binary, ["auth", "status"]);
|
|
@@ -1144,8 +1206,6 @@ async function claudeCredentialDiagnostic(layout, role, settingsSource, platform
|
|
|
1144
1206
|
if (state.kind !== "missing") {
|
|
1145
1207
|
return { path, checks: [preservedCredentialCheck({ id, agent: "Claude", role, path, state, login, status })] };
|
|
1146
1208
|
}
|
|
1147
|
-
const configured = claudeAuthMethodNames(settingsSource, /* @__PURE__ */ new Set());
|
|
1148
|
-
if (configured.length > 0) return { path, checks: [configuredByNamesCheck(id, "Claude", role, configured)] };
|
|
1149
1209
|
const ambient = presentNames(layout.envNames, AUTH_ENV);
|
|
1150
1210
|
if (ambient.length > 0) return { path, checks: [ambientNamesCheck(id, "Claude", role, ambient)] };
|
|
1151
1211
|
if (platform === "darwin") {
|
|
@@ -1159,7 +1219,7 @@ async function claudeCredentialDiagnostic(layout, role, settingsSource, platform
|
|
|
1159
1219
|
return { path, checks: [{
|
|
1160
1220
|
id: `${id}.login-required`,
|
|
1161
1221
|
status: "warn",
|
|
1162
|
-
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.`,
|
|
1163
1223
|
fix: `Authenticate this role with: ${login}. If that subcommand is unavailable, launch ${roleCommand(roleEnvironment, binary)} and run /login. Check status with: ${status}.`
|
|
1164
1224
|
}] };
|
|
1165
1225
|
}
|
|
@@ -1174,7 +1234,7 @@ function mcpDriftCheck(role, home, path, divergence) {
|
|
|
1174
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.`
|
|
1175
1235
|
);
|
|
1176
1236
|
}
|
|
1177
|
-
var SHARED, EXECUTABLE, 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;
|
|
1178
1238
|
var init_claude = __esm({
|
|
1179
1239
|
"src/agents/claude.ts"() {
|
|
1180
1240
|
"use strict";
|
|
@@ -1186,6 +1246,7 @@ var init_claude = __esm({
|
|
|
1186
1246
|
init_which();
|
|
1187
1247
|
init_mcp();
|
|
1188
1248
|
init_resources();
|
|
1249
|
+
init_skills();
|
|
1189
1250
|
SHARED = [
|
|
1190
1251
|
"skills",
|
|
1191
1252
|
"plugins",
|
|
@@ -1197,6 +1258,7 @@ var init_claude = __esm({
|
|
|
1197
1258
|
"themes"
|
|
1198
1259
|
];
|
|
1199
1260
|
EXECUTABLE = ["plugins", "commands", "hooks"];
|
|
1261
|
+
RUNTIME_SKILLS = ["synced"];
|
|
1200
1262
|
MCP_KEY = "mcpServers";
|
|
1201
1263
|
AUTH_ENV = [
|
|
1202
1264
|
"ANTHROPIC_AUTH_TOKEN",
|
|
@@ -1210,6 +1272,22 @@ var init_claude = __esm({
|
|
|
1210
1272
|
CLAUDE_CODE_DISABLE_AGENT_VIEW: "1",
|
|
1211
1273
|
CLAUDE_CODE_DISABLE_WORKFLOWS: "1"
|
|
1212
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
|
+
];
|
|
1213
1291
|
SECURE_STORAGE_ENV = "CLAUDE_SECURESTORAGE_CONFIG_DIR";
|
|
1214
1292
|
SEEDED_KEYS = ["hasCompletedOnboarding", "theme", "installMethod", "userID", MCP_KEY];
|
|
1215
1293
|
claudeAgent = {
|
|
@@ -1222,24 +1300,7 @@ var init_claude = __esm({
|
|
|
1222
1300
|
// Keep the legacy Task name and block current native orchestration,
|
|
1223
1301
|
// shared task/cron coordination, and cross-session paths. Paseo alone owns
|
|
1224
1302
|
// agent lifecycle and coordination.
|
|
1225
|
-
pins: {
|
|
1226
|
-
disallowedTools: [
|
|
1227
|
-
"Task",
|
|
1228
|
-
"Agent",
|
|
1229
|
-
"Workflow",
|
|
1230
|
-
"ListAgents",
|
|
1231
|
-
"SendMessage",
|
|
1232
|
-
"TeamCreate",
|
|
1233
|
-
"TeamDelete",
|
|
1234
|
-
"TaskCreate",
|
|
1235
|
-
"TaskGet",
|
|
1236
|
-
"TaskList",
|
|
1237
|
-
"TaskUpdate",
|
|
1238
|
-
"CronCreate",
|
|
1239
|
-
"CronDelete",
|
|
1240
|
-
"CronList"
|
|
1241
|
-
]
|
|
1242
|
-
},
|
|
1303
|
+
pins: { disallowedTools: DISALLOWED_TOOLS },
|
|
1243
1304
|
async build(layout, roles, options = {}) {
|
|
1244
1305
|
const home = layout.agentHome.claude;
|
|
1245
1306
|
const binary = await which(layout.bin.claude, layout.searchPath);
|
|
@@ -1250,8 +1311,7 @@ var init_claude = __esm({
|
|
|
1250
1311
|
return { entries: [], checks: [fail("claude.home", `No Claude config directory at ${home}.`, "Run claude once to initialise it, or pass --claude-home.")] };
|
|
1251
1312
|
}
|
|
1252
1313
|
const checks = [pass("claude.home", `Claude Code found at ${binary} using ${home}.`)];
|
|
1253
|
-
const
|
|
1254
|
-
const memorySource = await readIfPresent(join7(home, "CLAUDE.md"));
|
|
1314
|
+
const memorySource = await readIfPresent(join8(home, "CLAUDE.md"));
|
|
1255
1315
|
let stateSource;
|
|
1256
1316
|
let operatorServers;
|
|
1257
1317
|
try {
|
|
@@ -1271,9 +1331,10 @@ var init_claude = __esm({
|
|
|
1271
1331
|
const credentials = [];
|
|
1272
1332
|
const providerEnv = {};
|
|
1273
1333
|
const drift = [];
|
|
1334
|
+
const roomSkill = { name: ROOM_SKILL_NAME, source: roomSkillSource(layout) };
|
|
1274
1335
|
for (const role of roles) {
|
|
1275
1336
|
const target = roleHome(layout, "claude", role);
|
|
1276
|
-
const statePath =
|
|
1337
|
+
const statePath = join8(target, ".claude.json");
|
|
1277
1338
|
let roleStateSource;
|
|
1278
1339
|
let roleServers;
|
|
1279
1340
|
try {
|
|
@@ -1293,13 +1354,23 @@ var init_claude = __esm({
|
|
|
1293
1354
|
if (divergence) drift.push(mcpDriftCheck(role, target, statePath, divergence));
|
|
1294
1355
|
}
|
|
1295
1356
|
entries.push({ kind: "dir", path: target });
|
|
1296
|
-
const memoryPath =
|
|
1357
|
+
const memoryPath = join8(target, "CLAUDE.md");
|
|
1297
1358
|
const memory = renderRoleMemory(memorySource, role, options.memoryContract ?? true);
|
|
1298
1359
|
entries.push(memory === void 0 ? { kind: "absent", path: memoryPath } : { kind: "file", path: memoryPath, content: memory });
|
|
1299
|
-
entries.push({ kind: "file", path:
|
|
1360
|
+
entries.push({ kind: "file", path: join8(target, "settings.json"), content: renderRoleSettings(role, target) });
|
|
1300
1361
|
entries.push({ kind: "file", path: statePath, content: renderRoleState(stateSource), once: true });
|
|
1301
|
-
entries.push(...await roleResourceEntries({
|
|
1302
|
-
|
|
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));
|
|
1303
1374
|
providerEnv[role] = { [SECURE_STORAGE_ENV]: target };
|
|
1304
1375
|
}
|
|
1305
1376
|
return { entries, credentials, checks: [...checks, ...drift], binary, providerEnv };
|
|
@@ -1309,7 +1380,7 @@ var init_claude = __esm({
|
|
|
1309
1380
|
});
|
|
1310
1381
|
|
|
1311
1382
|
// src/agents/codex.ts
|
|
1312
|
-
import { join as
|
|
1383
|
+
import { join as join9 } from "path";
|
|
1313
1384
|
import { parse, stringify } from "smol-toml";
|
|
1314
1385
|
function table(parent, key) {
|
|
1315
1386
|
const value = parent[key];
|
|
@@ -1366,7 +1437,7 @@ function codexCredentialStore(config) {
|
|
|
1366
1437
|
}
|
|
1367
1438
|
async function credentialDiagnostic(layout, role, store, binary) {
|
|
1368
1439
|
const home = roleHome(layout, "codex", role);
|
|
1369
|
-
const path =
|
|
1440
|
+
const path = join9(home, "auth.json");
|
|
1370
1441
|
const login = roleCommand({ CODEX_HOME: home }, binary, ["login"]);
|
|
1371
1442
|
const status = roleCommand({ CODEX_HOME: home }, binary, ["login", "status"]);
|
|
1372
1443
|
const state = await inspectCredentialPath(path);
|
|
@@ -1415,6 +1486,7 @@ var init_codex = __esm({
|
|
|
1415
1486
|
init_which();
|
|
1416
1487
|
init_mcp();
|
|
1417
1488
|
init_resources();
|
|
1489
|
+
init_skills();
|
|
1418
1490
|
SHARED2 = ["AGENTS.md", "skills", "plugins", "hooks.json"];
|
|
1419
1491
|
EXECUTABLE2 = ["plugins", "hooks.json"];
|
|
1420
1492
|
CATALOG = "model-catalog.json";
|
|
@@ -1433,7 +1505,7 @@ var init_codex = __esm({
|
|
|
1433
1505
|
if (!binary) {
|
|
1434
1506
|
return { entries: [], checks: [fail("codex.bin", "Codex executable not found.", "Install Codex, or pass --codex-bin /path/to/codex.")] };
|
|
1435
1507
|
}
|
|
1436
|
-
const configPath =
|
|
1508
|
+
const configPath = join9(home, "config.toml");
|
|
1437
1509
|
const raw = await readIfPresent(configPath);
|
|
1438
1510
|
if (raw === void 0) {
|
|
1439
1511
|
return { entries: [], checks: [fail("codex.home", `No config.toml in ${home}.`, "Run codex once to initialise it, or pass --codex-home.")] };
|
|
@@ -1481,19 +1553,29 @@ var init_codex = __esm({
|
|
|
1481
1553
|
}
|
|
1482
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.`));
|
|
1483
1555
|
const shared = await existingPaths(home, SHARED2);
|
|
1556
|
+
const roomSkill = { name: ROOM_SKILL_NAME, source: roomSkillSource(layout) };
|
|
1484
1557
|
for (const role of roles) {
|
|
1485
1558
|
const target = roleHome(layout, "codex", role);
|
|
1486
|
-
const catalogPath =
|
|
1559
|
+
const catalogPath = join9(target, CATALOG);
|
|
1487
1560
|
const roleDocument = renderInstructions(role);
|
|
1488
1561
|
entries.push({ kind: "dir", path: target });
|
|
1489
|
-
entries.push({ kind: "file", path:
|
|
1562
|
+
entries.push({ kind: "file", path: join9(target, "role-instructions.md"), content: roleDocument });
|
|
1490
1563
|
entries.push({
|
|
1491
1564
|
kind: "file",
|
|
1492
|
-
path:
|
|
1565
|
+
path: join9(target, "config.toml"),
|
|
1493
1566
|
content: renderRoleConfig(source, { roleDocument, catalogPath })
|
|
1494
1567
|
});
|
|
1495
1568
|
entries.push({ kind: "file", path: catalogPath, content: catalogSource });
|
|
1496
|
-
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
|
+
}));
|
|
1497
1579
|
credentials.push(await credentialDiagnostic(layout, role, store, binary));
|
|
1498
1580
|
}
|
|
1499
1581
|
return { entries, credentials, checks, binary };
|
|
@@ -1506,7 +1588,7 @@ var init_codex = __esm({
|
|
|
1506
1588
|
import { spawn as spawn2 } from "child_process";
|
|
1507
1589
|
import { mkdtemp, readFile as readFile3, realpath, rm as rm2, stat } from "fs/promises";
|
|
1508
1590
|
import { tmpdir } from "os";
|
|
1509
|
-
import { isAbsolute as isAbsolute2, join as
|
|
1591
|
+
import { isAbsolute as isAbsolute2, join as join10, resolve as resolve3 } from "path";
|
|
1510
1592
|
function asObject2(value) {
|
|
1511
1593
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? { ...value } : void 0;
|
|
1512
1594
|
}
|
|
@@ -1530,12 +1612,12 @@ function adapterFailure(home, detail) {
|
|
|
1530
1612
|
return fail(
|
|
1531
1613
|
"pi.adapter",
|
|
1532
1614
|
detail,
|
|
1533
|
-
`Install ${PACKAGE_NAME} in ${
|
|
1615
|
+
`Install ${PACKAGE_NAME} in ${join10(home, "npm")} with Pi yourself, then run setup again.`
|
|
1534
1616
|
);
|
|
1535
1617
|
}
|
|
1536
1618
|
async function resolvePiAdapter(home) {
|
|
1537
|
-
const packageRoot =
|
|
1538
|
-
const manifestPath =
|
|
1619
|
+
const packageRoot = join10(home, "npm", "node_modules", PACKAGE_NAME);
|
|
1620
|
+
const manifestPath = join10(packageRoot, "package.json");
|
|
1539
1621
|
try {
|
|
1540
1622
|
const [canonicalRoot, manifestStat, raw] = await Promise.all([
|
|
1541
1623
|
realpath(packageRoot),
|
|
@@ -1643,7 +1725,7 @@ function runProbeProcess(executable, adapterEntry, env, options, cwd) {
|
|
|
1643
1725
|
});
|
|
1644
1726
|
}
|
|
1645
1727
|
async function runProbe(executable, adapterEntry, env, options) {
|
|
1646
|
-
const createdCwd = await mkdtemp(
|
|
1728
|
+
const createdCwd = await mkdtemp(join10(tmpdir(), "paseo-room-pi-probe-"));
|
|
1647
1729
|
const cwd = await realpath(createdCwd);
|
|
1648
1730
|
try {
|
|
1649
1731
|
return await runProbeProcess(executable, adapterEntry, {
|
|
@@ -1720,7 +1802,7 @@ async function readPiOptional(path) {
|
|
|
1720
1802
|
}
|
|
1721
1803
|
async function piCredentialDiagnostic(layout, role, binary = "pi") {
|
|
1722
1804
|
const home = roleHome(layout, "pi", role);
|
|
1723
|
-
const path =
|
|
1805
|
+
const path = join10(home, "auth.json");
|
|
1724
1806
|
const launch = piLoginCommand(home, binary);
|
|
1725
1807
|
const state = await inspectCredentialPath(path);
|
|
1726
1808
|
const id = `pi.auth.${role}`;
|
|
@@ -1760,6 +1842,7 @@ var init_pi = __esm({
|
|
|
1760
1842
|
init_which();
|
|
1761
1843
|
init_mcp();
|
|
1762
1844
|
init_resources();
|
|
1845
|
+
init_skills();
|
|
1763
1846
|
PACKAGE_NAME = "pi-mcp-adapter";
|
|
1764
1847
|
PROBE_ID = "paseo-room-pi-mcp-probe";
|
|
1765
1848
|
PROBE_TIMEOUT_MS = 1e4;
|
|
@@ -1816,7 +1899,7 @@ var init_pi = __esm({
|
|
|
1816
1899
|
PI_OFFLINE: "1"
|
|
1817
1900
|
});
|
|
1818
1901
|
if (capability.status === "fail") return { entries: [], checks: [resolved.check, capability] };
|
|
1819
|
-
const settingsPath =
|
|
1902
|
+
const settingsPath = join10(home, "settings.json");
|
|
1820
1903
|
let settings;
|
|
1821
1904
|
try {
|
|
1822
1905
|
settings = renderPiSettings(await readPiOptional(settingsPath));
|
|
@@ -1834,7 +1917,7 @@ var init_pi = __esm({
|
|
|
1834
1917
|
]
|
|
1835
1918
|
};
|
|
1836
1919
|
}
|
|
1837
|
-
const operatorAppendPath =
|
|
1920
|
+
const operatorAppendPath = join10(home, "APPEND_SYSTEM.md");
|
|
1838
1921
|
let operatorAppend;
|
|
1839
1922
|
try {
|
|
1840
1923
|
operatorAppend = await readPiOptional(operatorAppendPath);
|
|
@@ -1853,7 +1936,7 @@ var init_pi = __esm({
|
|
|
1853
1936
|
};
|
|
1854
1937
|
}
|
|
1855
1938
|
const shared = await existingPaths(home, SHARED3);
|
|
1856
|
-
const mcpPath =
|
|
1939
|
+
const mcpPath = join10(home, MCP_FILE);
|
|
1857
1940
|
let servers;
|
|
1858
1941
|
try {
|
|
1859
1942
|
servers = jsonServerTable(await readPiOptional(mcpPath), MCP_KEYS);
|
|
@@ -1876,13 +1959,23 @@ var init_pi = __esm({
|
|
|
1876
1959
|
const entries = [];
|
|
1877
1960
|
const credentials = [];
|
|
1878
1961
|
const argv = {};
|
|
1962
|
+
const roomSkill = { name: ROOM_SKILL_NAME, source: roomSkillSource(layout) };
|
|
1879
1963
|
for (const role of roles) {
|
|
1880
1964
|
const target = roleHome(layout, "pi", role);
|
|
1881
|
-
const appendPath =
|
|
1965
|
+
const appendPath = join10(target, "APPEND_SYSTEM.md");
|
|
1882
1966
|
entries.push({ kind: "dir", path: target });
|
|
1883
|
-
entries.push({ kind: "file", path:
|
|
1967
|
+
entries.push({ kind: "file", path: join10(target, "settings.json"), content: settings });
|
|
1884
1968
|
entries.push({ kind: "file", path: appendPath, content: renderPiAppend(operatorAppend, role) });
|
|
1885
|
-
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
|
+
}));
|
|
1886
1979
|
credentials.push(await piCredentialDiagnostic(layout, role, binary));
|
|
1887
1980
|
argv[role] = ["--no-extensions", "--extension", resolved.adapter.entry, "--no-approve", "--append-system-prompt", appendPath];
|
|
1888
1981
|
}
|
|
@@ -2108,20 +2201,29 @@ var init_paseo = __esm({
|
|
|
2108
2201
|
});
|
|
2109
2202
|
|
|
2110
2203
|
// src/plugin.ts
|
|
2111
|
-
import { readFileSync as
|
|
2112
|
-
import { join as
|
|
2204
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
2205
|
+
import { join as join11 } from "path";
|
|
2113
2206
|
function loadAsset(path) {
|
|
2114
|
-
return
|
|
2207
|
+
return readFileSync3(new URL(`./plugin-assets/${path}`, import.meta.url), "utf8");
|
|
2115
2208
|
}
|
|
2116
2209
|
function claudeCarrierPluginDir(layout) {
|
|
2117
|
-
return
|
|
2210
|
+
return join11(layout.roomHome, "plugin");
|
|
2211
|
+
}
|
|
2212
|
+
function typescriptTemplateLiteral(value) {
|
|
2213
|
+
const escaped = value.replaceAll("\\", "\\\\").replaceAll("`", "\\`").replaceAll("${", "\\${");
|
|
2214
|
+
return `\`${escaped}\``;
|
|
2118
2215
|
}
|
|
2119
2216
|
function renderClaudeCarrierContract(roles) {
|
|
2120
|
-
const
|
|
2217
|
+
const entries = roles.map((role) => {
|
|
2218
|
+
const id = JSON.stringify(providerId("claude", role));
|
|
2219
|
+
return ` ${id}: ${typescriptTemplateLiteral(renderInstructions(role))},`;
|
|
2220
|
+
});
|
|
2121
2221
|
return [
|
|
2122
2222
|
"/** Generated by paseo-room. The Markdown prompt assets remain authoritative. */",
|
|
2123
2223
|
`export const GENERATION = ${JSON.stringify(contractDigest())};`,
|
|
2124
|
-
|
|
2224
|
+
"export const CONTRACT: Readonly<Record<string, string>> = {",
|
|
2225
|
+
...entries,
|
|
2226
|
+
"};",
|
|
2125
2227
|
""
|
|
2126
2228
|
].join("\n");
|
|
2127
2229
|
}
|
|
@@ -2129,9 +2231,9 @@ function claudeCarrierEntries(layout, roles) {
|
|
|
2129
2231
|
const root = claudeCarrierPluginDir(layout);
|
|
2130
2232
|
return [
|
|
2131
2233
|
{ kind: "dir", path: root },
|
|
2132
|
-
{ kind: "dir", path:
|
|
2133
|
-
...STATIC_ASSETS.map((path) => ({ kind: "file", path:
|
|
2134
|
-
{ 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) }
|
|
2135
2237
|
];
|
|
2136
2238
|
}
|
|
2137
2239
|
var CLAUDE_CARRIER_PLUGIN_ID, STATIC_ASSETS;
|
|
@@ -2152,7 +2254,7 @@ var init_plugin = __esm({
|
|
|
2152
2254
|
});
|
|
2153
2255
|
|
|
2154
2256
|
// src/commands.ts
|
|
2155
|
-
import { join as
|
|
2257
|
+
import { join as join12, sep as sep2 } from "path";
|
|
2156
2258
|
import { lstat as lstat4, rm as rm3 } from "fs/promises";
|
|
2157
2259
|
function sessionOptions(options) {
|
|
2158
2260
|
const password = (options.env ?? process.env).PASEO_PASSWORD;
|
|
@@ -2184,7 +2286,10 @@ async function buildDesired(layout, agents, roles, build = {}) {
|
|
|
2184
2286
|
const entries = [
|
|
2185
2287
|
{ kind: "dir", path: layout.roomHome },
|
|
2186
2288
|
{ kind: "dir", path: sharedRoom(layout) },
|
|
2187
|
-
|
|
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)
|
|
2188
2293
|
];
|
|
2189
2294
|
const providers = {};
|
|
2190
2295
|
const profiles = [];
|
|
@@ -2218,18 +2323,18 @@ async function buildDesired(layout, agents, roles, build = {}) {
|
|
|
2218
2323
|
if (agents.every((id) => binaries[id] !== void 0)) {
|
|
2219
2324
|
entries.push({
|
|
2220
2325
|
kind: "file",
|
|
2221
|
-
path:
|
|
2326
|
+
path: join12(layout.roomHome, AUTHENTICATION_GUIDE),
|
|
2222
2327
|
content: renderAuthenticationGuide(layout.roomHome, binaries, agents, roles)
|
|
2223
2328
|
});
|
|
2224
2329
|
checks.push(warn(
|
|
2225
2330
|
"room.authentication-not-validated",
|
|
2226
|
-
`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)}.`,
|
|
2227
2332
|
"After setup --apply, follow that guide or run: paseo-room auth login <agent> <role>"
|
|
2228
2333
|
));
|
|
2229
2334
|
}
|
|
2230
2335
|
const pluginPath = agents.includes("claude") ? claudeCarrierPluginDir(layout) : void 0;
|
|
2231
2336
|
if (pluginPath !== void 0) entries.push(...claudeCarrierEntries(layout, roles));
|
|
2232
|
-
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)) });
|
|
2233
2338
|
return { entries, providers, profiles, checks, ...pluginPath === void 0 ? {} : { pluginPath } };
|
|
2234
2339
|
}
|
|
2235
2340
|
function contractProvenance(previous) {
|
|
@@ -2292,11 +2397,26 @@ async function safeDirectory(path, checks) {
|
|
|
2292
2397
|
async function roomPathSafety(layout, agents, roles) {
|
|
2293
2398
|
const checks = [];
|
|
2294
2399
|
if (!await safeDirectory(layout.roomHome, checks)) return checks;
|
|
2295
|
-
|
|
2296
|
-
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");
|
|
2297
2417
|
if (!await safeDirectory(rolesHome, checks)) return checks;
|
|
2298
2418
|
for (const agent of agents) {
|
|
2299
|
-
const agentHome =
|
|
2419
|
+
const agentHome = join12(rolesHome, agent);
|
|
2300
2420
|
if (!await safeDirectory(agentHome, checks)) continue;
|
|
2301
2421
|
for (const role of roles) await safeDirectory(roleHome(layout, agent, role), checks);
|
|
2302
2422
|
}
|
|
@@ -2476,7 +2596,7 @@ async function setup(options = {}) {
|
|
|
2476
2596
|
operations
|
|
2477
2597
|
};
|
|
2478
2598
|
}
|
|
2479
|
-
const markerPath =
|
|
2599
|
+
const markerPath = join12(layout.roomHome, MARKER);
|
|
2480
2600
|
const markerEntries = desired.entries.filter((entry) => entry.path === markerPath);
|
|
2481
2601
|
await applyEntries(desired.entries.filter((entry) => entry.path !== markerPath));
|
|
2482
2602
|
const ids = Object.keys(desired.providers);
|
|
@@ -2665,6 +2785,7 @@ var init_commands = __esm({
|
|
|
2665
2785
|
init_plugin();
|
|
2666
2786
|
init_result();
|
|
2667
2787
|
init_instructions();
|
|
2788
|
+
init_skills();
|
|
2668
2789
|
init_room();
|
|
2669
2790
|
init_roles();
|
|
2670
2791
|
AGENTS = { codex: codexAgent, claude: claudeAgent, pi: piAgent };
|
|
@@ -2745,6 +2866,7 @@ var init_wizard = __esm({
|
|
|
2745
2866
|
init_package();
|
|
2746
2867
|
init_auth();
|
|
2747
2868
|
init_commands();
|
|
2869
|
+
init_fsops();
|
|
2748
2870
|
import { Command, CommanderError, InvalidArgumentError } from "commander";
|
|
2749
2871
|
|
|
2750
2872
|
// src/render.ts
|
|
@@ -2777,6 +2899,7 @@ function renderHuman(result) {
|
|
|
2777
2899
|
init_result();
|
|
2778
2900
|
init_roles();
|
|
2779
2901
|
init_prompts();
|
|
2902
|
+
init_skills();
|
|
2780
2903
|
function collectAgent(value, previous) {
|
|
2781
2904
|
const agent = AGENT_IDS.find((id) => id === value);
|
|
2782
2905
|
if (!agent) throw new InvalidArgumentError(`Unknown agent: ${value}`);
|
|
@@ -2808,6 +2931,16 @@ function failedFromThrown(command, error) {
|
|
|
2808
2931
|
"Reinstall paseo-room, then try again."
|
|
2809
2932
|
)]);
|
|
2810
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
|
+
}
|
|
2941
|
+
if (error instanceof ManagedPathError) {
|
|
2942
|
+
return failed(command, [fail(`${command}.managed-path`, error.message, error.fix)]);
|
|
2943
|
+
}
|
|
2811
2944
|
const detail = error instanceof Error ? error.message : "unknown error";
|
|
2812
2945
|
return failed(command, [fail(
|
|
2813
2946
|
`${command}.error`,
|