pi-landstrip 0.18.12 → 0.18.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +0 -5
  2. package/dist/index.ts +77 -259
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -143,11 +143,6 @@ Pi Markdown agents are loaded from `~/.pi/agent/agents/` and `.pi/agents/`.
143
143
  `landstrip.agent` settings override Markdown agents with the same name.
144
144
  Project definitions override global definitions.
145
145
 
146
- OpenCode agent imports are enabled by default. Set
147
- `landstrip.opencode.showGlobalAgents` or `showLocalAgents` to `false` to disable
148
- a source. OpenCode agents fill missing names; Pi agents take precedence.
149
- Project imports require a trusted project.
150
-
151
146
  See the main [Landstrip documentation](https://github.com/landstrip/landstrip#readme)
152
147
  for `sandbox.json` fields and platform policy semantics.
153
148
 
package/dist/index.ts CHANGED
@@ -18,8 +18,8 @@ import {
18
18
  isIP,
19
19
  Socket as NetSocket
20
20
  } from "node:net";
21
- import { homedir as homedir4, tmpdir as tmpdir2 } from "node:os";
22
- import { basename as basename3, dirname as dirname5, isAbsolute as isAbsolute3, join as join7, parse, resolve as resolve3 } from "node:path";
21
+ import { homedir as homedir3, tmpdir as tmpdir2 } from "node:os";
22
+ import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute3, join as join7, parse, resolve as resolve3 } from "node:path";
23
23
  import { StringDecoder as StringDecoder2 } from "node:string_decoder";
24
24
  import { fileURLToPath as fileURLToPath2, URL } from "node:url";
25
25
  import {
@@ -80,8 +80,8 @@ import {
80
80
  rmSync,
81
81
  writeFileSync as writeFileSync3
82
82
  } from "node:fs";
83
- import { homedir as homedir3, tmpdir } from "node:os";
84
- import { basename as basename2, dirname as dirname4, isAbsolute as isAbsolute2, join as join6, relative as relative3, resolve as resolve2, sep as sep3 } from "node:path";
83
+ import { homedir as homedir2, tmpdir } from "node:os";
84
+ import { basename, dirname as dirname3, isAbsolute as isAbsolute2, join as join6, relative as relative3, resolve as resolve2, sep as sep3 } from "node:path";
85
85
  import { fileURLToPath } from "node:url";
86
86
  import {
87
87
  getAgentDir as getAgentDir4,
@@ -363,38 +363,7 @@ class PermissionPromptCoordinator {
363
363
 
364
364
  // config.ts
365
365
  var MAX_SUBAGENTS = 16;
366
- var DEFAULT_OPENCODE = {
367
- showGlobalAgents: true,
368
- showLocalAgents: true
369
- };
370
- var LANDSTRIP_KEYS = new Set(["maxSubagents", "agent", "permission", "opencode"]);
371
- var OPENCODE_KEYS = new Set(["showGlobalAgents", "showLocalAgents"]);
372
- function readBooleanField(section, key, path) {
373
- if (!(key in section))
374
- return;
375
- const value = section[key];
376
- if (typeof value === "boolean")
377
- return value;
378
- throw new Error(`${path}.${key} must be a boolean`);
379
- }
380
- function normalizeOpenCode(value, path) {
381
- if (value === undefined)
382
- return {};
383
- if (!isRecord2(value))
384
- throw new Error(`${path} must be a JSON object`);
385
- for (const key of Object.keys(value)) {
386
- if (!OPENCODE_KEYS.has(key))
387
- throw new Error(`${path}: unknown field ${key}`);
388
- }
389
- const result = {};
390
- const showGlobalAgents = readBooleanField(value, "showGlobalAgents", path);
391
- if (showGlobalAgents !== undefined)
392
- result.showGlobalAgents = showGlobalAgents;
393
- const showLocalAgents = readBooleanField(value, "showLocalAgents", path);
394
- if (showLocalAgents !== undefined)
395
- result.showLocalAgents = showLocalAgents;
396
- return result;
397
- }
366
+ var LANDSTRIP_KEYS = new Set(["maxSubagents", "agent", "permission"]);
398
367
  function readJsonObject(path) {
399
368
  let value;
400
369
  try {
@@ -448,7 +417,6 @@ function readLandstripSettings(path) {
448
417
  }
449
418
  if ("permission" in settings.landstrip)
450
419
  config.permission = settings.landstrip.permission;
451
- config.opencode = normalizeOpenCode(settings.landstrip.opencode, `${path}: landstrip.opencode`);
452
420
  expandAgentPromptReferences(config, path);
453
421
  return config;
454
422
  }
@@ -588,8 +556,7 @@ function loadLandstripConfig(cwd, includeProject = true, agentDir = getAgentDir(
588
556
  }
589
557
  if (!isRecord2(config.agent))
590
558
  throw new Error("landstrip.agent must be an object");
591
- const opencode = { ...DEFAULT_OPENCODE, ...config.opencode };
592
- return { ...config, maxSubagents, agent: config.agent, opencode, agentSources };
559
+ return { ...config, maxSubagents, agent: config.agent, agentSources };
593
560
  }
594
561
  function loadMaxSubagentsSettings(cwd, includeProject = true, agentDir = getAgentDir()) {
595
562
  const global = loadLandstripConfig(cwd, false, agentDir).maxSubagents;
@@ -605,17 +572,8 @@ function loadMaxSubagentsSettings(cwd, includeProject = true, agentDir = getAgen
605
572
 
606
573
  // opencode-agents.ts
607
574
  import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync3, statSync } from "node:fs";
608
- import { homedir as homedir2 } from "node:os";
609
- import { dirname as dirname2, extname, join as join3, relative, sep } from "node:path";
575
+ import { extname, join as join3, relative, sep } from "node:path";
610
576
  import { parse as parseYaml } from "yaml";
611
- import { parse as parseJsonc, printParseErrorCode } from "jsonc-parser";
612
- var AGENT_DIR_PREFIXES = ["agent/", "agents/"];
613
- function openCodeGlobalConfigDir() {
614
- if (process.env.OPENCODE_CONFIG_DIR)
615
- return process.env.OPENCODE_CONFIG_DIR;
616
- const xdg = process.env.XDG_CONFIG_HOME;
617
- return join3(xdg && xdg.length > 0 ? xdg : join3(homedir2(), ".config"), "opencode");
618
- }
619
577
  function listMarkdownFiles(root) {
620
578
  if (!existsSync2(root))
621
579
  return [];
@@ -646,13 +604,6 @@ function listMarkdownFiles(root) {
646
604
  }
647
605
  function agentNameFromPath(dir, filePath) {
648
606
  const relativePath = relative(dir, filePath).split(sep).join("/");
649
- for (const prefix of AGENT_DIR_PREFIXES) {
650
- if (relativePath.startsWith(prefix)) {
651
- const candidate = relativePath.slice(prefix.length);
652
- const extension2 = extname(candidate);
653
- return extension2.length > 0 ? candidate.slice(0, -extension2.length) : candidate;
654
- }
655
- }
656
607
  const extension = extname(relativePath);
657
608
  return extension.length > 0 ? relativePath.slice(0, -extension.length) : relativePath;
658
609
  }
@@ -684,7 +635,7 @@ function convertToolsToPermission(tools) {
684
635
  }
685
636
  return permission;
686
637
  }
687
- function normalizeOpenCodeAgentRaw(data, prompt) {
638
+ function normalizeMarkdownAgentRaw(data, prompt) {
688
639
  const known = new Set([
689
640
  "name",
690
641
  "model",
@@ -744,98 +695,27 @@ function normalizeOpenCodeAgentRaw(data, prompt) {
744
695
  }
745
696
  function parseMarkdownAgentRaw(content) {
746
697
  const { data, body } = parseFrontmatter(content);
747
- return normalizeOpenCodeAgentRaw(data, body.trim());
698
+ return normalizeMarkdownAgentRaw(data, body.trim());
748
699
  }
749
- function parseOpenCodeConfig(path) {
750
- const errors = [];
751
- const value = parseJsonc(readFileSync3(path, "utf8"), errors, {
752
- allowTrailingComma: true
753
- });
754
- if (errors.length > 0) {
755
- const error = errors[0];
756
- throw new Error(`${printParseErrorCode(error.error)} at offset ${error.offset}`);
757
- }
758
- if (!isRecord2(value))
759
- throw new Error("config must contain a JSON object");
760
- return value;
761
- }
762
- function loadAgentsFromConfigFile(path, source, agents, diagnostics) {
763
- if (!existsSync2(path))
764
- return;
765
- try {
766
- const config = parseOpenCodeConfig(path);
767
- if (config.agent === undefined)
768
- return;
769
- if (!isRecord2(config.agent))
770
- throw new Error("agent must be a JSON object");
771
- for (const [name, value] of Object.entries(config.agent)) {
700
+ function loadPiMarkdownAgents(options) {
701
+ const agents = new Map;
702
+ const diagnostics = [];
703
+ for (const directory of options.directories) {
704
+ for (const filePath of listMarkdownFiles(directory.path)) {
772
705
  try {
773
- if (!name)
774
- throw new Error("agent name is empty");
775
- if (!isRecord2(value))
776
- throw new Error(`agent ${name} must be a JSON object`);
777
- if (value.prompt !== undefined && typeof value.prompt !== "string") {
778
- throw new Error(`agent ${name} prompt must be a string`);
706
+ const content = readFileSync3(filePath, "utf8");
707
+ const raw = parseMarkdownAgentRaw(content);
708
+ const name = agentNameFromPath(directory.path, filePath);
709
+ if (!name) {
710
+ diagnostics.push(`${filePath}: agent name is empty`);
711
+ continue;
779
712
  }
780
- const prompt = typeof value.prompt === "string" ? expandFileReferences(value.prompt, dirname2(path)) : "";
781
- const raw = normalizeOpenCodeAgentRaw(value, prompt);
782
- agents.set(name, { name, source, path, format: "json", raw });
713
+ agents.set(name, { name, source: directory.source, path: filePath, raw });
783
714
  } catch (error) {
784
- diagnostics.push(`${path}: ${formatError(error)}`);
785
- }
786
- }
787
- } catch (error) {
788
- diagnostics.push(`${path}: ${formatError(error)}`);
789
- }
790
- }
791
- function loadConfigAgentsFromDir(dir, source, agents, diagnostics) {
792
- for (const file of ["opencode.json", "opencode.jsonc"]) {
793
- loadAgentsFromConfigFile(join3(dir, file), source, agents, diagnostics);
794
- }
795
- }
796
- function loadAgentsFromRoot(root, nameRoot, source, agents, diagnostics) {
797
- for (const filePath of listMarkdownFiles(root)) {
798
- try {
799
- const content = readFileSync3(filePath, "utf8");
800
- const raw = parseMarkdownAgentRaw(content);
801
- const name = agentNameFromPath(nameRoot, filePath);
802
- if (!name) {
803
- diagnostics.push(`${filePath}: agent name is empty`);
804
- continue;
715
+ diagnostics.push(`${filePath}: ${formatError(error)}`);
805
716
  }
806
- agents.set(name, { name, source, path: filePath, format: "markdown", raw });
807
- } catch (error) {
808
- diagnostics.push(`${filePath}: ${formatError(error)}`);
809
717
  }
810
718
  }
811
- }
812
- function loadAgentsFromDir(dir, source, agents, diagnostics) {
813
- for (const subdir of ["agent", "agents"]) {
814
- loadAgentsFromRoot(join3(dir, subdir), dir, source, agents, diagnostics);
815
- }
816
- }
817
- function loadPiMarkdownAgents(options) {
818
- const agents = new Map;
819
- const diagnostics = [];
820
- for (const directory of options.directories) {
821
- loadAgentsFromRoot(directory.path, directory.path, directory.source, agents, diagnostics);
822
- }
823
- return { agents, diagnostics };
824
- }
825
- function loadOpenCodeAgents(options) {
826
- const agents = new Map;
827
- const diagnostics = [];
828
- if (options.includeGlobal) {
829
- const globalDir = options.globalConfigDir ?? openCodeGlobalConfigDir();
830
- loadConfigAgentsFromDir(globalDir, "global", agents, diagnostics);
831
- loadAgentsFromDir(globalDir, "global", agents, diagnostics);
832
- }
833
- if (options.includeProject) {
834
- loadConfigAgentsFromDir(options.cwd, "local", agents, diagnostics);
835
- const projectDir = join3(options.cwd, ".opencode");
836
- loadConfigAgentsFromDir(projectDir, "local", agents, diagnostics);
837
- loadAgentsFromDir(projectDir, "local", agents, diagnostics);
838
- }
839
719
  return { agents, diagnostics };
840
720
  }
841
721
 
@@ -950,38 +830,16 @@ function loadAgentCatalog(cwd, piAgentDir = getAgentDir2(), includeProject = tru
950
830
  let configuredAgents = {};
951
831
  let configuredPermission;
952
832
  let agentSources = new Map;
953
- let showGlobalAgents = false;
954
- let showLocalAgents = false;
955
833
  try {
956
834
  const config = loadLandstripConfig(cwd, includeProject, piAgentDir);
957
835
  maxSubagents = config.maxSubagents;
958
836
  configuredAgents = config.agent;
959
837
  configuredPermission = config.permission;
960
838
  agentSources = new Map(config.agentSources);
961
- showGlobalAgents = config.opencode.showGlobalAgents;
962
- showLocalAgents = config.opencode.showLocalAgents;
963
839
  } catch (error) {
964
840
  diagnostics.push(formatError(error));
965
841
  }
966
- const openCode = loadOpenCodeAgents({
967
- cwd,
968
- includeGlobal: showGlobalAgents,
969
- includeProject: showLocalAgents && includeProject
970
- });
971
- diagnostics.push(...openCode.diagnostics);
972
842
  const normalized = new Map;
973
- for (const imported of openCode.agents.values()) {
974
- try {
975
- const kind = imported.format === "markdown" ? "opencode-markdown" : "opencode-json";
976
- normalized.set(imported.name, normalizeAgent(imported.name, imported.raw, imported.source, {
977
- kind,
978
- source: imported.source,
979
- path: imported.path
980
- }));
981
- } catch (error) {
982
- diagnostics.push(`${imported.path}: ${formatError(error)}`);
983
- }
984
- }
985
843
  const piMarkdown = loadPiMarkdownAgents({
986
844
  directories: [
987
845
  { path: join4(piAgentDir, "agents"), source: "global" },
@@ -1076,13 +934,13 @@ function agentSupportsMode(agent, mode) {
1076
934
 
1077
935
  // agent-files.ts
1078
936
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync4, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
1079
- import { basename, dirname as dirname3, join as join5, relative as relative2, sep as sep2 } from "node:path";
937
+ import { dirname as dirname2, join as join5, relative as relative2, sep as sep2 } from "node:path";
1080
938
  import { getAgentDir as getAgentDir3, withFileMutationQueue as withFileMutationQueue2 } from "@earendil-works/pi-coding-agent";
1081
939
  import {
1082
940
  applyEdits,
1083
941
  modify,
1084
- parse as parseJsonc2,
1085
- printParseErrorCode as printParseErrorCode2
942
+ parse as parseJsonc,
943
+ printParseErrorCode
1086
944
  } from "jsonc-parser";
1087
945
  var JSON_FORMAT = {
1088
946
  formattingOptions: { insertSpaces: true, tabSize: 2, eol: `
@@ -1090,10 +948,10 @@ var JSON_FORMAT = {
1090
948
  };
1091
949
  function parseJsonDocument(content, label) {
1092
950
  const errors = [];
1093
- const value = parseJsonc2(content, errors, { allowTrailingComma: true });
951
+ const value = parseJsonc(content, errors, { allowTrailingComma: true });
1094
952
  if (errors.length > 0) {
1095
953
  const error = errors[0];
1096
- throw new Error(`${label}: ${printParseErrorCode2(error.error)} at offset ${error.offset}`);
954
+ throw new Error(`${label}: ${printParseErrorCode(error.error)} at offset ${error.offset}`);
1097
955
  }
1098
956
  if (!isRecord2(value))
1099
957
  throw new Error(`${label} must contain a JSON object`);
@@ -1108,38 +966,22 @@ function valueAtPath(root, path) {
1108
966
  }
1109
967
  return value;
1110
968
  }
1111
- function agentJsonPath(kind, name) {
1112
- return kind === "landstrip" ? ["landstrip", "agent", name] : ["agent", name];
1113
- }
1114
- function agentContainerPath(kind) {
1115
- return kind === "landstrip" ? ["landstrip", "agent"] : ["agent"];
969
+ function agentJsonPath(name) {
970
+ return ["landstrip", "agent", name];
1116
971
  }
1117
- function editorSnippet(kind, name, raw) {
1118
- const value = kind === "landstrip" ? { landstrip: { agent: { [name]: raw } } } : { agent: { [name]: raw } };
1119
- return `${JSON.stringify(value, null, 2)}
972
+ function editorSnippet(name, raw) {
973
+ return `${JSON.stringify({ landstrip: { agent: { [name]: raw } } }, null, 2)}
1120
974
  `;
1121
975
  }
1122
- function parseEditorSnippet(content, kind, name) {
976
+ function parseEditorSnippet(content, name) {
1123
977
  const root = parseJsonDocument(content, "Edited agent");
1124
- const parent = kind === "landstrip" ? valueAtPath(root, ["landstrip"]) : root;
1125
- const container = valueAtPath(root, agentContainerPath(kind));
1126
- const rootKey = kind === "landstrip" ? "landstrip" : "agent";
1127
- if (Object.keys(root).length !== 1 || root[rootKey] === undefined || !isRecord2(parent) || Object.keys(parent).length !== 1 || parent.agent === undefined || !isRecord2(container) || Object.keys(container).length !== 1 || !isRecord2(container[name])) {
978
+ const parent = valueAtPath(root, ["landstrip"]);
979
+ const container = valueAtPath(root, ["landstrip", "agent"]);
980
+ if (Object.keys(root).length !== 1 || root.landstrip === undefined || !isRecord2(parent) || Object.keys(parent).length !== 1 || parent.agent === undefined || !isRecord2(container) || Object.keys(container).length !== 1 || !isRecord2(container[name])) {
1128
981
  throw new Error(`Edited JSON must contain only agent ${name}`);
1129
982
  }
1130
983
  return container[name];
1131
984
  }
1132
- function validateEditedAgent(kind, name, raw) {
1133
- if (kind === "landstrip") {
1134
- validateAgentRaw(name, raw);
1135
- return;
1136
- }
1137
- const prompt = raw.prompt;
1138
- if (prompt !== undefined && typeof prompt !== "string") {
1139
- throw new Error(`agent ${name} prompt must be a string`);
1140
- }
1141
- validateAgentRaw(name, normalizeOpenCodeAgentRaw(raw, typeof prompt === "string" ? prompt : ""));
1142
- }
1143
985
  function ensureFinalNewline(content) {
1144
986
  return content.endsWith(`
1145
987
  `) ? content : `${content}
@@ -1151,7 +993,7 @@ async function writeJsonNode(path, nodePath, value) {
1151
993
  `;
1152
994
  parseJsonDocument(content, path);
1153
995
  const edited = applyEdits(content, modify(content, nodePath, value, JSON_FORMAT));
1154
- mkdirSync2(dirname3(path), { recursive: true });
996
+ mkdirSync2(dirname2(path), { recursive: true });
1155
997
  writeFileSync2(path, ensureFinalNewline(edited), { mode: 384 });
1156
998
  });
1157
999
  }
@@ -1177,11 +1019,11 @@ async function deleteJsonNode(path, nodePath, containerPaths) {
1177
1019
  });
1178
1020
  return deleted;
1179
1021
  }
1180
- function readAgentNode(path, kind, name) {
1022
+ function readAgentNode(path, name) {
1181
1023
  if (!existsSync3(path))
1182
1024
  return;
1183
1025
  const root = parseJsonDocument(readFileSync4(path, "utf8"), path);
1184
- const value = valueAtPath(root, agentJsonPath(kind, name));
1026
+ const value = valueAtPath(root, agentJsonPath(name));
1185
1027
  return isRecord2(value) ? { ...value } : undefined;
1186
1028
  }
1187
1029
  function permissionConfig(agent) {
@@ -1220,7 +1062,7 @@ function relocateGlobalPrompt(raw, agent) {
1220
1062
  if (agent.origin?.source !== "global" || !agent.origin.path || typeof raw.prompt !== "string") {
1221
1063
  return raw;
1222
1064
  }
1223
- return { ...raw, prompt: expandFileReferences(raw.prompt, dirname3(agent.origin.path)) };
1065
+ return { ...raw, prompt: expandFileReferences(raw.prompt, dirname2(agent.origin.path)) };
1224
1066
  }
1225
1067
  function safeRelative(root, path) {
1226
1068
  const value = relative2(root, path);
@@ -1234,19 +1076,13 @@ function projectMarkdownPath(cwd, agentDir, agent) {
1234
1076
  const suffix = safeRelative(join5(agentDir, "agents"), origin.path) ?? `${agent.name}.md`;
1235
1077
  return join5(cwd, ".pi", "agents", suffix);
1236
1078
  }
1237
- if (origin.kind === "opencode-markdown" && origin.path) {
1238
- return join5(cwd, ".opencode", "agents", `${agent.name}.md`);
1239
- }
1240
1079
  throw new Error(`Agent ${agent.name} has no Markdown source`);
1241
1080
  }
1242
- function openCodeProjectPath(cwd, sourcePath) {
1243
- return join5(cwd, ".opencode", basename(sourcePath));
1244
- }
1245
1081
  function projectSettingsPath(cwd, agentDir) {
1246
1082
  return getPiConfigPaths(cwd, "settings.json", agentDir).projectPath;
1247
1083
  }
1248
1084
  function projectSettingsHasAgent(cwd, name, agentDir) {
1249
- return readAgentNode(projectSettingsPath(cwd, agentDir), "landstrip", name) !== undefined;
1085
+ return readAgentNode(projectSettingsPath(cwd, agentDir), name) !== undefined;
1250
1086
  }
1251
1087
  function canDeleteProjectAgent(cwd, agent, agentDir = getAgentDir3()) {
1252
1088
  try {
@@ -1256,21 +1092,16 @@ function canDeleteProjectAgent(cwd, agent, agentDir = getAgentDir3()) {
1256
1092
  return false;
1257
1093
  }
1258
1094
  const origin = agent.origin;
1259
- return Boolean(origin?.source === "local" && origin.path && (origin.kind === "pi-markdown" || origin.kind === "opencode-markdown" || origin.kind === "opencode-json"));
1095
+ return Boolean(origin?.source === "local" && origin.path && origin.kind === "pi-markdown");
1260
1096
  }
1261
1097
  async function deleteProjectAgent(cwd, agent, agentDir = getAgentDir3()) {
1262
1098
  const settingsPath = projectSettingsPath(cwd, agentDir);
1263
- const deletedOverride = await deleteJsonNode(settingsPath, agentJsonPath("landstrip", agent.name), [["landstrip", "agent"], ["landstrip"]]);
1099
+ const deletedOverride = await deleteJsonNode(settingsPath, agentJsonPath(agent.name), [
1100
+ ["landstrip", "agent"],
1101
+ ["landstrip"]
1102
+ ]);
1264
1103
  const origin = agent.origin;
1265
- if (origin?.source !== "local" || !origin.path)
1266
- return deletedOverride;
1267
- if (origin.kind === "opencode-json") {
1268
- const deletedSource = await deleteJsonNode(origin.path, agentJsonPath("opencode", agent.name), [
1269
- ["agent"]
1270
- ]);
1271
- return deletedOverride || deletedSource;
1272
- }
1273
- if (origin.kind !== "pi-markdown" && origin.kind !== "opencode-markdown") {
1104
+ if (origin?.source !== "local" || !origin.path || origin.kind !== "pi-markdown") {
1274
1105
  return deletedOverride;
1275
1106
  }
1276
1107
  if (!existsSync3(origin.path))
@@ -1280,44 +1111,31 @@ async function deleteProjectAgent(cwd, agent, agentDir = getAgentDir3()) {
1280
1111
  }
1281
1112
  function prepareProjectAgentEditor(cwd, agent, agentDir = getAgentDir3()) {
1282
1113
  const origin = agent.origin;
1283
- const openCodeProjectEnabled = loadLandstripConfig(cwd, true, agentDir).opencode.showLocalAgents;
1284
- if (origin?.path && (origin.kind === "pi-markdown" || origin.kind === "opencode-markdown")) {
1285
- const useMarkdown = origin.kind === "pi-markdown" || origin.source === "local" || openCodeProjectEnabled;
1286
- const target2 = useMarkdown ? projectMarkdownPath(cwd, agentDir, agent) : projectSettingsPath(cwd, agentDir);
1287
- const projectOverride = useMarkdown ? undefined : readAgentNode(target2, "landstrip", agent.name);
1288
- const disabledOverride = projectOverride && Object.keys(projectOverride).length === 1 && typeof projectOverride.disable === "boolean" ? projectOverride.disable : undefined;
1114
+ if (origin?.path && origin.kind === "pi-markdown") {
1115
+ const target2 = projectMarkdownPath(cwd, agentDir, agent);
1289
1116
  return {
1290
1117
  title: `Edit @${agent.name}`,
1291
1118
  content: readFileSync4(origin.path, "utf8"),
1292
1119
  async save(content) {
1293
- const edited = parseMarkdownAgentRaw(content);
1294
- const raw2 = !useMarkdown && edited.disable === undefined && disabledOverride !== undefined ? { ...edited, disable: disabledOverride } : edited;
1295
- validateAgentRaw(agent.name, raw2);
1296
- if (!useMarkdown) {
1297
- await writeJsonNode(target2, agentJsonPath("landstrip", agent.name), raw2);
1298
- return;
1299
- }
1120
+ validateAgentRaw(agent.name, parseMarkdownAgentRaw(content));
1300
1121
  await withFileMutationQueue2(target2, async () => {
1301
- mkdirSync2(dirname3(target2), { recursive: true });
1122
+ mkdirSync2(dirname2(target2), { recursive: true });
1302
1123
  writeFileSync2(target2, ensureFinalNewline(content), { mode: 384 });
1303
1124
  });
1304
1125
  }
1305
1126
  };
1306
1127
  }
1307
- const useOpenCode = origin?.kind === "opencode-json" && origin.path !== undefined && (origin.source === "local" || openCodeProjectEnabled);
1308
- const kind = useOpenCode ? "opencode" : "landstrip";
1309
1128
  const sourcePath = origin?.path;
1310
- const sourceKind = origin?.kind === "opencode-json" ? "opencode" : "landstrip";
1311
- const sourceRaw = sourcePath ? readAgentNode(sourcePath, sourceKind, agent.name) : undefined;
1312
- const raw = relocateGlobalPrompt(sourceKind === kind && sourceRaw ? sourceRaw : agent.raw ? { ...agent.raw } : normalizedAgentRaw(agent), agent);
1313
- const target = kind === "opencode" && sourcePath ? origin?.source === "local" ? sourcePath : openCodeProjectPath(cwd, sourcePath) : projectSettingsPath(cwd, agentDir);
1129
+ const sourceRaw = origin?.kind === "settings" && sourcePath ? readAgentNode(sourcePath, agent.name) : undefined;
1130
+ const raw = relocateGlobalPrompt(sourceRaw ? sourceRaw : agent.raw ? { ...agent.raw } : normalizedAgentRaw(agent), agent);
1131
+ const target = projectSettingsPath(cwd, agentDir);
1314
1132
  return {
1315
1133
  title: `Edit @${agent.name}`,
1316
- content: editorSnippet(kind, agent.name, raw),
1134
+ content: editorSnippet(agent.name, raw),
1317
1135
  async save(content) {
1318
- const edited = parseEditorSnippet(content, kind, agent.name);
1319
- validateEditedAgent(kind, agent.name, edited);
1320
- await writeJsonNode(target, agentJsonPath(kind, agent.name), edited);
1136
+ const edited = parseEditorSnippet(content, agent.name);
1137
+ validateAgentRaw(agent.name, edited);
1138
+ await writeJsonNode(target, agentJsonPath(agent.name), edited);
1321
1139
  }
1322
1140
  };
1323
1141
  }
@@ -1775,7 +1593,7 @@ var PRIMARY_AGENT_ENTRY = "landstrip.primary-agent";
1775
1593
  var WORKER_ENV = "PI_LANDSTRIP_WORKER";
1776
1594
  var CONTROL_TITLE = "pi-landstrip:control:v1";
1777
1595
  var MAX_DEPTH = 3;
1778
- var packageDir = dirname4(fileURLToPath(import.meta.url));
1596
+ var packageDir = dirname3(fileURLToPath(import.meta.url));
1779
1597
  var MAX_TASK_OUTPUT_BYTES = 64 * 1024;
1780
1598
  var INSPECTOR_BODY_LINES = 16;
1781
1599
  var PI_THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
@@ -1821,7 +1639,7 @@ function resolvePiPackage() {
1821
1639
  piPackageResolved = true;
1822
1640
  try {
1823
1641
  const entry = fileURLToPath(import.meta.resolve("@earendil-works/pi-coding-agent"));
1824
- let dir2 = dirname4(entry);
1642
+ let dir2 = dirname3(entry);
1825
1643
  for (;; ) {
1826
1644
  const pkgPath = join6(dir2, "package.json");
1827
1645
  if (existsSync4(pkgPath)) {
@@ -1829,7 +1647,7 @@ function resolvePiPackage() {
1829
1647
  if (pkg)
1830
1648
  return pkg;
1831
1649
  }
1832
- const parent = dirname4(dir2);
1650
+ const parent = dirname3(dir2);
1833
1651
  if (parent === dir2)
1834
1652
  break;
1835
1653
  dir2 = parent;
@@ -1843,7 +1661,7 @@ function resolvePiPackage() {
1843
1661
  if (pkg)
1844
1662
  return pkg;
1845
1663
  }
1846
- const parent = dirname4(dir);
1664
+ const parent = dirname3(dir);
1847
1665
  if (parent === dir)
1848
1666
  return;
1849
1667
  dir = parent;
@@ -1861,7 +1679,7 @@ function readPiPackage(pkgPath) {
1861
1679
  const binFile = typeof value.bin === "string" ? value.bin : isRecord2(value.bin) && typeof value.bin.pi === "string" ? value.bin.pi : undefined;
1862
1680
  if (!binFile)
1863
1681
  return;
1864
- const cliEntry = join6(dirname4(pkgPath), binFile);
1682
+ const cliEntry = join6(dirname3(pkgPath), binFile);
1865
1683
  if (!existsSync4(cliEntry))
1866
1684
  return;
1867
1685
  cachedPiPackage = { cliEntry, version: parts.slice(0, 3) };
@@ -2298,15 +2116,15 @@ function canonicalPermissionPath(path, seen = new Set) {
2298
2116
  if (seen.has(existing))
2299
2117
  return path;
2300
2118
  seen.add(existing);
2301
- const target = resolve2(dirname4(existing), readlinkSync(existing), ...missing);
2119
+ const target = resolve2(dirname3(existing), readlinkSync(existing), ...missing);
2302
2120
  return canonicalPermissionPath(target, seen);
2303
2121
  }
2304
2122
  break;
2305
2123
  } catch {
2306
- const parent = dirname4(existing);
2124
+ const parent = dirname3(existing);
2307
2125
  if (parent === existing)
2308
2126
  return path;
2309
- missing.unshift(basename2(existing));
2127
+ missing.unshift(basename(existing));
2310
2128
  existing = parent;
2311
2129
  }
2312
2130
  }
@@ -3603,11 +3421,11 @@ ${catalog.diagnostics.join(`
3603
3421
  return;
3604
3422
  });
3605
3423
  try {
3606
- const sessionWritePath = task.sessionDir ?? (task.sessionFile ? dirname4(task.sessionFile) : undefined);
3424
+ const sessionWritePath = task.sessionDir ?? (task.sessionFile ? dirname3(task.sessionFile) : undefined);
3607
3425
  if (!sessionWritePath)
3608
3426
  throw new Error("Subagent task has no session directory or file");
3609
3427
  const cliEntry = invocation.args[0] ?? invocation.command;
3610
- const cliRoot = dependencyRoot(cliEntry) ?? dirname4(dirname4(cliEntry));
3428
+ const cliRoot = dependencyRoot(cliEntry) ?? dirname3(dirname3(cliEntry));
3611
3429
  const extensionRoot = dependencyRoot(packageDir);
3612
3430
  launch = await this.integration.prepareRpcWorker({
3613
3431
  command: invocation.command,
@@ -3627,7 +3445,7 @@ ${catalog.diagnostics.join(`
3627
3445
  ...new Set([
3628
3446
  ctx.cwd,
3629
3447
  ...agentBootstrapPaths(agentDir),
3630
- join6(homedir3(), ".agents", "skills"),
3448
+ join6(homedir2(), ".agents", "skills"),
3631
3449
  packageDir,
3632
3450
  join6(packageDir, "node_modules"),
3633
3451
  invocation.command,
@@ -3635,7 +3453,7 @@ ${catalog.diagnostics.join(`
3635
3453
  extensionRoot,
3636
3454
  ...workerExtensions.flatMap((extension) => [
3637
3455
  extension.entry,
3638
- dirname4(extension.entry),
3456
+ dirname3(extension.entry),
3639
3457
  dependencyRoot(extension.entry)
3640
3458
  ]),
3641
3459
  task.sessionDir,
@@ -3849,7 +3667,7 @@ ${renderTaskResult(task.id, "error", message)}`);
3849
3667
  storeTaskText(task, value, kind) {
3850
3668
  if (Buffer.byteLength(value) <= MAX_TASK_OUTPUT_BYTES)
3851
3669
  return value;
3852
- const directory = task.sessionDir ?? (task.sessionFile ? dirname4(task.sessionFile) : undefined);
3670
+ const directory = task.sessionDir ?? (task.sessionFile ? dirname3(task.sessionFile) : undefined);
3853
3671
  if (!directory)
3854
3672
  throw new Error(`Task ${task.id} has no artifact directory`);
3855
3673
  mkdirSync3(directory, { recursive: true });
@@ -4111,7 +3929,7 @@ var PROXY_ENVIRONMENT_VARIABLES = [
4111
3929
  "https_proxy",
4112
3930
  "all_proxy"
4113
3931
  ];
4114
- var packageDir2 = dirname5(fileURLToPath2(import.meta.url));
3932
+ var packageDir2 = dirname4(fileURLToPath2(import.meta.url));
4115
3933
  var PERMISSION_OPTIONS = [
4116
3934
  { label: "Allow once", action: "once" },
4117
3935
  { label: "Allow for this session", action: "session" },
@@ -4139,7 +3957,7 @@ function loadSandboxConfig(cwd, includeProject) {
4139
3957
  const globalConfigPath = join7(getAgentDir5(), "sandbox.json");
4140
3958
  if (!existsSync5(globalConfigPath)) {
4141
3959
  const templatePath = join7(packageDir2, "sandbox.json");
4142
- mkdirSync4(dirname5(globalConfigPath), { recursive: true });
3960
+ mkdirSync4(dirname4(globalConfigPath), { recursive: true });
4143
3961
  writeFileSync4(globalConfigPath, readFileSync6(templatePath, "utf-8"), "utf-8");
4144
3962
  }
4145
3963
  let globalConfig = JSON.parse(readFileSync6(join7(packageDir2, "sandbox.json"), "utf-8"));
@@ -4240,7 +4058,7 @@ function readOrEmptyConfig(configPath) {
4240
4058
  }
4241
4059
  }
4242
4060
  function writeConfigFile(configPath, config) {
4243
- mkdirSync4(dirname5(configPath), { recursive: true });
4061
+ mkdirSync4(dirname4(configPath), { recursive: true });
4244
4062
  writeFileSync4(configPath, `${JSON.stringify(config, null, 2)}
4245
4063
  `, "utf-8");
4246
4064
  }
@@ -4336,10 +4154,10 @@ function canonicalizePath(filePath, cwd) {
4336
4154
  const tail = [];
4337
4155
  let probe = abs;
4338
4156
  while (!existsSync5(probe)) {
4339
- const parent = dirname5(probe);
4157
+ const parent = dirname4(probe);
4340
4158
  if (parent === probe)
4341
4159
  return abs;
4342
- tail.unshift(basename3(probe));
4160
+ tail.unshift(basename2(probe));
4343
4161
  probe = parent;
4344
4162
  }
4345
4163
  try {
@@ -4377,9 +4195,9 @@ function pathUnderDirectory(filePath, dir) {
4377
4195
  return normalizedFilePath.startsWith(normalizedDir + sep4);
4378
4196
  }
4379
4197
  function sessionScopeFor(filePath, baseDirectory) {
4380
- const dir = dirname5(filePath);
4198
+ const dir = dirname4(filePath);
4381
4199
  const normalizedDir = normalizePathSeparators(dir);
4382
- const home = homedir4();
4200
+ const home = homedir3();
4383
4201
  const boundaries = new Set;
4384
4202
  if (home)
4385
4203
  boundaries.add(home);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-landstrip",
3
- "version": "0.18.12",
3
+ "version": "0.18.13",
4
4
  "description": "Sandboxed Bash and process-backed agents for Pi",
5
5
  "keywords": [
6
6
  "landstrip",
@@ -39,7 +39,7 @@
39
39
  "ci:test": "npm run test"
40
40
  },
41
41
  "dependencies": {
42
- "@landstrip/landstrip": "^0.18.12",
42
+ "@landstrip/landstrip": "^0.18.13",
43
43
  "jsonc-parser": "^3.3.1",
44
44
  "minimatch": "^10.2.5",
45
45
  "yaml": "^2.9.0"