omniagent 0.1.1 → 0.1.3

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 (4) hide show
  1. package/CLAUDE.md +29 -0
  2. package/README.md +49 -0
  3. package/dist/cli.js +1232 -163
  4. package/package.json +4 -1
package/dist/cli.js CHANGED
@@ -1,15 +1,131 @@
1
1
  #!/usr/bin/env node
2
- import { constants, realpathSync } from "node:fs";
3
- import { pathToFileURL } from "node:url";
2
+ import { constants, realpathSync, existsSync, readFileSync } from "node:fs";
3
+ import { pathToFileURL, fileURLToPath } from "node:url";
4
4
  import yargs from "yargs";
5
5
  import { hideBin } from "yargs/helpers";
6
- import { stat, access, readFile, mkdir, writeFile, readdir, rm, rename } from "node:fs/promises";
6
+ import { stat, rm, readdir, access, readFile, mkdir, writeFile, rename } from "node:fs/promises";
7
7
  import path from "node:path";
8
+ import { createHash, randomUUID } from "node:crypto";
9
+ import os from "node:os";
8
10
  import { createInterface } from "node:readline/promises";
9
11
  import { TextDecoder } from "node:util";
10
- import { createHash } from "node:crypto";
11
- import os from "node:os";
12
12
  import { spawn } from "node:child_process";
13
+ async function pathExists$2(candidate) {
14
+ try {
15
+ await stat(candidate);
16
+ return true;
17
+ } catch {
18
+ return false;
19
+ }
20
+ }
21
+ async function findUp(startDir, markerRelativePath) {
22
+ let current = path.resolve(startDir);
23
+ let previous = "";
24
+ while (current !== previous) {
25
+ if (await pathExists$2(path.join(current, markerRelativePath))) {
26
+ return current;
27
+ }
28
+ previous = current;
29
+ current = path.dirname(current);
30
+ }
31
+ return null;
32
+ }
33
+ async function findRepoRoot(startDir) {
34
+ const gitRoot = await findUp(startDir, ".git");
35
+ if (gitRoot) {
36
+ return gitRoot;
37
+ }
38
+ return await findUp(startDir, "package.json");
39
+ }
40
+ function hashIdentifier$3(value) {
41
+ return createHash("sha256").update(value).digest("hex");
42
+ }
43
+ async function pathExists$1(candidate) {
44
+ try {
45
+ await stat(candidate);
46
+ return true;
47
+ } catch {
48
+ return false;
49
+ }
50
+ }
51
+ async function listRepoSlashCommandManifestPaths(repoRoot) {
52
+ const matches = [];
53
+ const stack = [repoRoot];
54
+ while (stack.length > 0) {
55
+ const current = stack.pop();
56
+ if (!current) {
57
+ continue;
58
+ }
59
+ const entries = await readdir(current, { withFileTypes: true });
60
+ for (const entry2 of entries) {
61
+ const childPath = path.join(current, entry2.name);
62
+ if (entry2.isDirectory()) {
63
+ stack.push(childPath);
64
+ continue;
65
+ }
66
+ if (entry2.isFile() && entry2.name === ".omniagent-slash-commands.toml") {
67
+ matches.push(childPath);
68
+ }
69
+ }
70
+ }
71
+ return matches;
72
+ }
73
+ function resolveRepoScopedStatePaths(repoRoot, homeDir) {
74
+ const repoHash = hashIdentifier$3(repoRoot);
75
+ return [
76
+ path.join(homeDir, ".omniagent", "state", "managed-outputs", "projects", repoHash),
77
+ path.join(homeDir, ".omniagent", "state", "instructions", "projects", repoHash),
78
+ path.join(homeDir, ".omniagent", "state", "subagents", "projects", repoHash),
79
+ path.join(homeDir, ".omniagent", "state", "slash-commands", "projects", repoHash),
80
+ path.join(homeDir, ".omniagent", "state", "ignore-rules", "projects", `${repoHash}.json`),
81
+ path.join(homeDir, ".omniagent", "slash-commands", "projects", repoHash),
82
+ path.join(homeDir, ".omniagent", "slash-commands", "skills", "projects", repoHash),
83
+ path.join(repoRoot, ".omniagent", "slash-commands")
84
+ ];
85
+ }
86
+ async function resetProjectState(repoRoot, homeDir = os.homedir()) {
87
+ const candidates = [
88
+ ...resolveRepoScopedStatePaths(repoRoot, homeDir),
89
+ ...await listRepoSlashCommandManifestPaths(repoRoot)
90
+ ];
91
+ const uniqueCandidates = Array.from(new Set(candidates));
92
+ const removedPaths = [];
93
+ for (const candidate of uniqueCandidates) {
94
+ if (!await pathExists$1(candidate)) {
95
+ continue;
96
+ }
97
+ await rm(candidate, { recursive: true, force: true });
98
+ removedPaths.push(candidate);
99
+ }
100
+ return { removedPaths };
101
+ }
102
+ const resetStateCommand = {
103
+ command: "reset-state",
104
+ describe: "Reset omniagent project state for the current repository",
105
+ handler: async () => {
106
+ const startDir = process.cwd();
107
+ const repoRoot = await findRepoRoot(startDir);
108
+ if (!repoRoot) {
109
+ console.error("Error: Could not find a project root from the current directory.");
110
+ process.exit(1);
111
+ return;
112
+ }
113
+ const result = await resetProjectState(repoRoot);
114
+ if (result.removedPaths.length === 0) {
115
+ console.log(`No project state found for: ${repoRoot}`);
116
+ return;
117
+ }
118
+ console.log(`Reset project state for: ${repoRoot}`);
119
+ console.log(`Removed ${result.removedPaths.length} path(s).`);
120
+ }
121
+ };
122
+ const devCommand = {
123
+ command: "dev <command>",
124
+ describe: "Developer utilities",
125
+ builder: (yargs2) => yargs2.command(resetStateCommand).demandCommand(1, "Specify a dev command.").strictCommands(),
126
+ handler: () => {
127
+ }
128
+ };
13
129
  const echoCommand = {
14
130
  command: "echo [message]",
15
131
  describe: "Echo a message with optional repetition and prefix",
@@ -718,6 +834,74 @@ const codexTarget = {
718
834
  }
719
835
  }
720
836
  };
837
+ const FRONTMATTER_MARKER$2 = "---";
838
+ function stripFrontmatterFields(contents, keysToRemove) {
839
+ const lines = contents.split(/\r?\n/);
840
+ if (lines[0]?.trim() !== FRONTMATTER_MARKER$2) {
841
+ return contents;
842
+ }
843
+ let endIndex = -1;
844
+ for (let i = 1; i < lines.length; i += 1) {
845
+ if (lines[i].trim() === FRONTMATTER_MARKER$2) {
846
+ endIndex = i;
847
+ break;
848
+ }
849
+ }
850
+ if (endIndex === -1) {
851
+ return contents;
852
+ }
853
+ const normalizedKeys = new Set(Array.from(keysToRemove).map((key) => key.toLowerCase()));
854
+ const frontmatterLines = lines.slice(1, endIndex);
855
+ const filtered = [];
856
+ let skippingList = false;
857
+ for (const line of frontmatterLines) {
858
+ const trimmed = line.trim();
859
+ const match = trimmed.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
860
+ if (skippingList) {
861
+ if (match && !normalizedKeys.has(match[1].toLowerCase())) {
862
+ skippingList = false;
863
+ } else if (!match) {
864
+ const shouldSkip = trimmed === "" || trimmed.startsWith("-") || trimmed.startsWith("#");
865
+ if (shouldSkip) {
866
+ continue;
867
+ }
868
+ skippingList = false;
869
+ } else {
870
+ continue;
871
+ }
872
+ }
873
+ if (match) {
874
+ const [, key, rawValue] = match;
875
+ if (normalizedKeys.has(key.toLowerCase())) {
876
+ const rest = rawValue.trim();
877
+ if (!rest || rest.startsWith("#")) {
878
+ skippingList = true;
879
+ }
880
+ continue;
881
+ }
882
+ }
883
+ if (!skippingList) {
884
+ filtered.push(line);
885
+ }
886
+ }
887
+ const eol = contents.includes("\r\n") ? "\r\n" : "\n";
888
+ const outputLines = [lines[0], ...filtered, ...lines.slice(endIndex)];
889
+ return outputLines.join(eol);
890
+ }
891
+ const TARGET_FRONTMATTER_KEYS$3 = /* @__PURE__ */ new Set(["targets", "targetagents"]);
892
+ function isSlashCommandLike(value) {
893
+ if (!value || typeof value !== "object") {
894
+ return false;
895
+ }
896
+ const command = value;
897
+ return typeof command.name === "string" && typeof command.rawContents === "string";
898
+ }
899
+ function renderPromptReference(agentName) {
900
+ return `---
901
+ agent: ${JSON.stringify(agentName)}
902
+ ---
903
+ `;
904
+ }
721
905
  const copilotTarget = {
722
906
  id: "copilot",
723
907
  displayName: "GitHub Copilot CLI",
@@ -740,13 +924,41 @@ const copilotTarget = {
740
924
  },
741
925
  outputs: {
742
926
  skills: "{repoRoot}/.github/skills/{itemName}",
743
- subagents: {
744
- path: "{repoRoot}/.github/skills/{itemName}",
745
- fallback: { mode: "convert", targetType: "skills" }
746
- },
927
+ subagents: "{repoRoot}/.github/agents/{itemName}.agent.md",
747
928
  commands: {
748
- projectPath: "{repoRoot}/.github/skills/{itemName}",
749
- fallback: { mode: "convert", targetType: "skills" }
929
+ projectPath: "{repoRoot}/.github/agents/{itemName}.agent.md",
930
+ converter: {
931
+ id: "copilot-command-to-agent-and-prompt",
932
+ convert: (item, context) => {
933
+ if (!isSlashCommandLike(item)) {
934
+ return { error: "Invalid slash command payload for Copilot conversion." };
935
+ }
936
+ const agentPath = path.join(
937
+ context.repoRoot,
938
+ ".github",
939
+ "agents",
940
+ `${item.name}.agent.md`
941
+ );
942
+ const promptPath = path.join(
943
+ context.repoRoot,
944
+ ".github",
945
+ "prompts",
946
+ `${item.name}.prompt.md`
947
+ );
948
+ return {
949
+ outputs: [
950
+ {
951
+ outputPath: agentPath,
952
+ content: stripFrontmatterFields(item.rawContents, TARGET_FRONTMATTER_KEYS$3)
953
+ },
954
+ {
955
+ outputPath: promptPath,
956
+ content: renderPromptReference(item.name)
957
+ }
958
+ ]
959
+ };
960
+ }
961
+ }
750
962
  },
751
963
  instructions: {
752
964
  filename: "AGENTS.md",
@@ -812,7 +1024,7 @@ const BUILTIN_TARGETS = [
812
1024
  copilotTarget
813
1025
  ];
814
1026
  Object.freeze(BUILTIN_TARGETS.map((target) => target.id));
815
- const FRONTMATTER_MARKER$2 = "---";
1027
+ const FRONTMATTER_MARKER$1 = "---";
816
1028
  function parseScalar$1(rawValue) {
817
1029
  const trimmed = rawValue.trim();
818
1030
  if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
@@ -864,12 +1076,12 @@ function parseFrontmatter(lines) {
864
1076
  }
865
1077
  function extractFrontmatter$1(contents) {
866
1078
  const lines = contents.split(/\r?\n/);
867
- if (lines[0]?.trim() !== FRONTMATTER_MARKER$2) {
1079
+ if (lines[0]?.trim() !== FRONTMATTER_MARKER$1) {
868
1080
  return { frontmatter: {}, body: contents.trimEnd() };
869
1081
  }
870
1082
  let endIndex = -1;
871
1083
  for (let i = 1; i < lines.length; i += 1) {
872
- if (lines[i].trim() === FRONTMATTER_MARKER$2) {
1084
+ if (lines[i].trim() === FRONTMATTER_MARKER$1) {
873
1085
  endIndex = i;
874
1086
  break;
875
1087
  }
@@ -933,6 +1145,7 @@ function parseInstructionFrontmatter(options) {
933
1145
  resolvedOutputDir
934
1146
  };
935
1147
  }
1148
+ const RESERVED_MANAGED_SOURCE_DIRS = /* @__PURE__ */ new Set(["skills", "commands", "agents", "instructions"]);
936
1149
  function isTemplateFile(fileName) {
937
1150
  if (!fileName.toLowerCase().endsWith(".md")) {
938
1151
  return false;
@@ -962,6 +1175,19 @@ async function listTemplateFiles(root) {
962
1175
  }
963
1176
  return files;
964
1177
  }
1178
+ function isInReservedManagedSourceDir(filePath, repoRoot, agentsDir) {
1179
+ const templatesRoot = resolveAgentsDirPath(repoRoot, agentsDir);
1180
+ const relative = path.relative(templatesRoot, filePath);
1181
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
1182
+ return false;
1183
+ }
1184
+ const segments = relative.split(path.sep).filter(Boolean);
1185
+ if (segments.length < 2) {
1186
+ return false;
1187
+ }
1188
+ const first = segments[0] === ".local" ? segments[1] : segments[0];
1189
+ return first ? RESERVED_MANAGED_SOURCE_DIRS.has(first) : false;
1190
+ }
965
1191
  async function scanInstructionTemplateSources(options) {
966
1192
  const includeLocal = options.includeLocal ?? true;
967
1193
  const templatesRoot = resolveAgentsDirPath(options.repoRoot, options.agentsDir);
@@ -973,6 +1199,9 @@ async function scanInstructionTemplateSources(options) {
973
1199
  }
974
1200
  const entries = [];
975
1201
  for (const filePath of templateFiles) {
1202
+ if (isInReservedManagedSourceDir(filePath, options.repoRoot, options.agentsDir)) {
1203
+ continue;
1204
+ }
976
1205
  const markerType = detectLocalMarker$1(filePath);
977
1206
  const sourceType = markerType ? "local" : "shared";
978
1207
  if (!includeLocal && sourceType === "local") {
@@ -1630,59 +1859,456 @@ async function writeManagedOutputs(repoRoot, manifest, homeDir) {
1630
1859
  await writeFile(manifestPath, `${JSON.stringify({ entries: sorted }, null, 2)}
1631
1860
  `, "utf8");
1632
1861
  }
1633
- const FRONTMATTER_MARKER$1 = "---";
1634
- function stripFrontmatterFields(contents, keysToRemove) {
1635
- const lines = contents.split(/\r?\n/);
1636
- if (lines[0]?.trim() !== FRONTMATTER_MARKER$1) {
1637
- return contents;
1862
+ const SCRIPT_TAG_DEFINITIONS = [
1863
+ { kind: "nodejs", openTag: "<nodejs>", closeTag: "</nodejs>" },
1864
+ { kind: "shell", openTag: "<shell>", closeTag: "</shell>" }
1865
+ ];
1866
+ const SCRIPT_HEARTBEAT_INTERVAL_MS = 3e4;
1867
+ const PREVIEW_LIMIT = 200;
1868
+ const WINDOWS_DEFAULT_SHELL = "cmd.exe";
1869
+ const POSIX_DEFAULT_SHELL = "/bin/sh";
1870
+ const RUNNER_SOURCE = String.raw`
1871
+ import path from "node:path";
1872
+ import { writeFileSync } from "node:fs";
1873
+ import { createRequire } from "node:module";
1874
+
1875
+ const encoded = process.env.OMNIAGENT_SCRIPT_B64 ?? "";
1876
+ const sourceLabel = process.env.OMNIAGENT_SCRIPT_SOURCE ?? "template";
1877
+ const sourcePathValue = process.env.OMNIAGENT_SCRIPT_TEMPLATE_PATH ?? sourceLabel;
1878
+ const script = Buffer.from(encoded, "base64").toString("utf8");
1879
+ const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
1880
+ const sourcePath = path.isAbsolute(sourcePathValue)
1881
+ ? sourcePathValue
1882
+ : path.resolve(process.cwd(), sourcePathValue);
1883
+ const __filename = sourcePath;
1884
+ const __dirname = path.dirname(__filename);
1885
+ const require = createRequire(__filename);
1886
+
1887
+ function encodeResult(value) {
1888
+ if (value === undefined) {
1889
+ return { kind: "undefined" };
1890
+ }
1891
+ if (value === null) {
1892
+ return { kind: "null" };
1893
+ }
1894
+ const valueType = typeof value;
1895
+ if (valueType === "string") {
1896
+ return { kind: "string", value };
1897
+ }
1898
+ if (valueType === "number") {
1899
+ return { kind: "number", value };
1900
+ }
1901
+ if (valueType === "boolean") {
1902
+ return { kind: "boolean", value };
1903
+ }
1904
+ if (valueType === "bigint") {
1905
+ return { kind: "bigint", value: value.toString() };
1906
+ }
1907
+ if (valueType === "symbol") {
1908
+ return { kind: "symbol", value: String(value) };
1909
+ }
1910
+ if (valueType === "function") {
1911
+ return { kind: "function", value: String(value) };
1912
+ }
1913
+ try {
1914
+ return { kind: "json", value: JSON.stringify(value) };
1915
+ } catch {
1916
+ return { kind: "coerced", value: String(value) };
1917
+ }
1918
+ }
1919
+
1920
+ async function main() {
1921
+ try {
1922
+ const wrapped = script + "\n//# sourceURL=" + sourceLabel;
1923
+ const fn = new AsyncFunction("require", "__dirname", "__filename", wrapped);
1924
+ const result = await fn(require, __dirname, __filename);
1925
+ writeFileSync(3, JSON.stringify({ ok: true, payload: encodeResult(result) }), "utf8");
1926
+ } catch (error) {
1927
+ const message =
1928
+ error instanceof Error
1929
+ ? (error.stack ?? error.message)
1930
+ : String(error);
1931
+ writeFileSync(3, JSON.stringify({ ok: false, error: message }), "utf8");
1932
+ process.exitCode = 1;
1933
+ }
1934
+ }
1935
+
1936
+ await main();
1937
+ `;
1938
+ class TemplateScriptExecutionError extends Error {
1939
+ templatePath;
1940
+ blockId;
1941
+ constructor(message, options) {
1942
+ super(message);
1943
+ this.name = "TemplateScriptExecutionError";
1944
+ this.templatePath = options.templatePath;
1945
+ this.blockId = options.blockId ?? null;
1638
1946
  }
1639
- let endIndex = -1;
1640
- for (let i = 1; i < lines.length; i += 1) {
1641
- if (lines[i].trim() === FRONTMATTER_MARKER$1) {
1642
- endIndex = i;
1947
+ }
1948
+ function formatParseError(templatePath, message) {
1949
+ return new TemplateScriptExecutionError(
1950
+ `Invalid template script markup in ${templatePath}: ${message}`,
1951
+ { templatePath }
1952
+ );
1953
+ }
1954
+ function createStillRunningWarning(options) {
1955
+ return {
1956
+ code: "still_running",
1957
+ message: `Template script is still running for ${options.templatePath} (${options.blockId}).`,
1958
+ templatePath: options.templatePath,
1959
+ blockId: options.blockId
1960
+ };
1961
+ }
1962
+ function appendWarning(runtime, warning) {
1963
+ runtime.warnings.push(warning);
1964
+ runtime.onWarning?.(warning);
1965
+ }
1966
+ function emitVerbose(runtime, message) {
1967
+ if (!runtime.verbose) {
1968
+ return;
1969
+ }
1970
+ runtime.onVerbose?.(message);
1971
+ }
1972
+ function hasTemplateScriptMarkup(content) {
1973
+ return SCRIPT_TAG_DEFINITIONS.some((definition) => content.includes(definition.openTag));
1974
+ }
1975
+ function findNextScriptToken(content, cursor) {
1976
+ let nextToken = null;
1977
+ for (const definition of SCRIPT_TAG_DEFINITIONS) {
1978
+ const openIndex = content.indexOf(definition.openTag, cursor);
1979
+ if (openIndex !== -1 && (!nextToken || openIndex < nextToken.index)) {
1980
+ nextToken = {
1981
+ index: openIndex,
1982
+ kind: "open",
1983
+ definition
1984
+ };
1985
+ }
1986
+ const closeIndex = content.indexOf(definition.closeTag, cursor);
1987
+ if (closeIndex !== -1 && (!nextToken || closeIndex < nextToken.index)) {
1988
+ nextToken = {
1989
+ index: closeIndex,
1990
+ kind: "close",
1991
+ definition
1992
+ };
1993
+ }
1994
+ }
1995
+ return nextToken;
1996
+ }
1997
+ function resolveShellInvocation() {
1998
+ if (process.platform === "win32") {
1999
+ const command2 = process.env.ComSpec?.trim() || WINDOWS_DEFAULT_SHELL;
2000
+ return { command: command2, args: ["/d", "/s", "/c"] };
2001
+ }
2002
+ const command = process.env.SHELL?.trim() || POSIX_DEFAULT_SHELL;
2003
+ return { command, args: ["-c"] };
2004
+ }
2005
+ function normalizeScriptResult(payload) {
2006
+ if (payload.kind === "undefined" || payload.kind === "null") {
2007
+ return { renderedText: "", resultKind: "empty" };
2008
+ }
2009
+ if (payload.kind === "string") {
2010
+ return { renderedText: payload.value, resultKind: "string" };
2011
+ }
2012
+ if (payload.kind === "json") {
2013
+ return { renderedText: payload.value, resultKind: "json" };
2014
+ }
2015
+ if (payload.kind === "coerced") {
2016
+ return { renderedText: payload.value, resultKind: "coerced" };
2017
+ }
2018
+ return {
2019
+ renderedText: String(payload.value ?? ""),
2020
+ resultKind: "coerced"
2021
+ };
2022
+ }
2023
+ function markReusedExecutions(runtime, blockIds) {
2024
+ for (const blockId of blockIds) {
2025
+ const existing = runtime.scriptExecutions.get(blockId);
2026
+ if (!existing) {
2027
+ continue;
2028
+ }
2029
+ runtime.scriptExecutions.set(blockId, {
2030
+ ...existing,
2031
+ reusedAcrossTargets: true
2032
+ });
2033
+ }
2034
+ }
2035
+ function parseTemplateScripts(templatePath, content) {
2036
+ const blocks = [];
2037
+ let cursor = 0;
2038
+ let index = 0;
2039
+ while (cursor < content.length) {
2040
+ const nextToken = findNextScriptToken(content, cursor);
2041
+ if (!nextToken) {
1643
2042
  break;
1644
2043
  }
2044
+ if (nextToken.kind === "close") {
2045
+ throw formatParseError(
2046
+ templatePath,
2047
+ `closing ${nextToken.definition.closeTag} appears before an opening tag`
2048
+ );
2049
+ }
2050
+ const { definition } = nextToken;
2051
+ const scriptStart = nextToken.index + definition.openTag.length;
2052
+ const closeIndex = content.indexOf(definition.closeTag, scriptStart);
2053
+ if (closeIndex === -1) {
2054
+ throw formatParseError(templatePath, `missing closing ${definition.closeTag} tag`);
2055
+ }
2056
+ const scriptBody = content.slice(scriptStart, closeIndex);
2057
+ if (hasTemplateScriptMarkup(scriptBody)) {
2058
+ throw formatParseError(templatePath, "nested template script blocks are not supported");
2059
+ }
2060
+ blocks.push({
2061
+ blockId: `${templatePath}#${index}`,
2062
+ templatePath,
2063
+ index,
2064
+ evaluator: definition.kind,
2065
+ scriptBody,
2066
+ startOffset: nextToken.index,
2067
+ endOffset: closeIndex + definition.closeTag.length
2068
+ });
2069
+ index += 1;
2070
+ cursor = closeIndex + definition.closeTag.length;
2071
+ }
2072
+ return { blocks };
2073
+ }
2074
+ async function executeNodeJsScriptBlock(options) {
2075
+ const { block, runtime } = options;
2076
+ const child = spawn(process.execPath, ["--input-type=module", "--eval", RUNNER_SOURCE], {
2077
+ cwd: runtime.cwd,
2078
+ env: {
2079
+ ...process.env,
2080
+ OMNIAGENT_SCRIPT_B64: Buffer.from(block.scriptBody, "utf8").toString("base64"),
2081
+ OMNIAGENT_SCRIPT_SOURCE: options.blockLabel,
2082
+ OMNIAGENT_SCRIPT_TEMPLATE_PATH: block.templatePath
2083
+ },
2084
+ // Ignore child stdout to prevent scripts with heavy console output from deadlocking on pipe backpressure.
2085
+ stdio: ["ignore", "ignore", "pipe", "pipe"]
2086
+ });
2087
+ let stderr = "";
2088
+ let responseRaw = "";
2089
+ const stderrPipe = child.stderr;
2090
+ if (stderrPipe) {
2091
+ stderrPipe.setEncoding("utf8");
2092
+ stderrPipe.on("data", (chunk) => {
2093
+ stderr += chunk;
2094
+ });
1645
2095
  }
1646
- if (endIndex === -1) {
1647
- return contents;
2096
+ const resultPipe = child.stdio[3];
2097
+ if (resultPipe) {
2098
+ resultPipe.setEncoding("utf8");
2099
+ resultPipe.on("data", (chunk) => {
2100
+ responseRaw += chunk;
2101
+ });
1648
2102
  }
1649
- const normalizedKeys = new Set(Array.from(keysToRemove).map((key) => key.toLowerCase()));
1650
- const frontmatterLines = lines.slice(1, endIndex);
1651
- const filtered = [];
1652
- let skippingList = false;
1653
- for (const line of frontmatterLines) {
1654
- const trimmed = line.trim();
1655
- const match = trimmed.match(/^([A-Za-z0-9_-]+):\s*(.*)$/);
1656
- if (skippingList) {
1657
- if (match && !normalizedKeys.has(match[1].toLowerCase())) {
1658
- skippingList = false;
1659
- } else if (!match) {
1660
- const shouldSkip = trimmed === "" || trimmed.startsWith("-") || trimmed.startsWith("#");
1661
- if (shouldSkip) {
1662
- continue;
1663
- }
1664
- skippingList = false;
1665
- } else {
1666
- continue;
1667
- }
2103
+ const exitCode = await new Promise((resolve, reject) => {
2104
+ child.once("error", reject);
2105
+ child.once("exit", (code) => resolve(code ?? 1));
2106
+ });
2107
+ const parsed = responseRaw.trim();
2108
+ let response = null;
2109
+ if (parsed.length > 0) {
2110
+ try {
2111
+ response = JSON.parse(parsed);
2112
+ } catch {
2113
+ response = null;
1668
2114
  }
1669
- if (match) {
1670
- const [, key, rawValue] = match;
1671
- if (normalizedKeys.has(key.toLowerCase())) {
1672
- const rest = rawValue.trim();
1673
- if (!rest || rest.startsWith("#")) {
1674
- skippingList = true;
1675
- }
1676
- continue;
2115
+ }
2116
+ if (exitCode !== 0) {
2117
+ const message = response && !response.ok ? response.error : stderr.trim() || `Script process exited with code ${exitCode}.`;
2118
+ throw new TemplateScriptExecutionError(
2119
+ `Template script failed in ${block.templatePath} (${block.blockId}): ${message}`,
2120
+ { templatePath: block.templatePath, blockId: block.blockId }
2121
+ );
2122
+ }
2123
+ if (!response || !response.ok) {
2124
+ throw new TemplateScriptExecutionError(
2125
+ `Template script failed in ${block.templatePath} (${block.blockId}): invalid runner response`,
2126
+ { templatePath: block.templatePath, blockId: block.blockId }
2127
+ );
2128
+ }
2129
+ const normalized = normalizeScriptResult(response.payload);
2130
+ return normalized;
2131
+ }
2132
+ async function executeShellScriptBlock(options) {
2133
+ const { block, runtime } = options;
2134
+ const shell = resolveShellInvocation();
2135
+ const child = spawn(shell.command, [...shell.args, block.scriptBody], {
2136
+ cwd: runtime.cwd,
2137
+ env: {
2138
+ ...process.env
2139
+ },
2140
+ stdio: ["ignore", "pipe", "pipe"]
2141
+ });
2142
+ let stdout = "";
2143
+ let stderr = "";
2144
+ const stdoutPipe = child.stdout;
2145
+ if (stdoutPipe) {
2146
+ stdoutPipe.setEncoding("utf8");
2147
+ stdoutPipe.on("data", (chunk) => {
2148
+ stdout += chunk;
2149
+ });
2150
+ }
2151
+ const stderrPipe = child.stderr;
2152
+ if (stderrPipe) {
2153
+ stderrPipe.setEncoding("utf8");
2154
+ stderrPipe.on("data", (chunk) => {
2155
+ stderr += chunk;
2156
+ });
2157
+ }
2158
+ const exitCode = await new Promise((resolve, reject) => {
2159
+ child.once("error", reject);
2160
+ child.once("exit", (code) => resolve(code ?? 1));
2161
+ });
2162
+ if (exitCode !== 0) {
2163
+ const message = stderr.trim() || `Script process exited with code ${exitCode}.`;
2164
+ throw new TemplateScriptExecutionError(
2165
+ `Template script failed in ${block.templatePath} (${block.blockId}): ${message}`,
2166
+ { templatePath: block.templatePath, blockId: block.blockId }
2167
+ );
2168
+ }
2169
+ return {
2170
+ renderedText: stdout,
2171
+ resultKind: stdout.length > 0 ? "string" : "empty"
2172
+ };
2173
+ }
2174
+ async function executeScriptBlock(options) {
2175
+ const { block, runtime } = options;
2176
+ const startTime = Date.now();
2177
+ const blockLabel = `${block.templatePath}#${block.index}`;
2178
+ emitVerbose(runtime, `Evaluating template script ${blockLabel} (${block.evaluator}).`);
2179
+ let warningTimer = null;
2180
+ if (runtime.heartbeatIntervalMs > 0) {
2181
+ warningTimer = setInterval(() => {
2182
+ appendWarning(
2183
+ runtime,
2184
+ createStillRunningWarning({
2185
+ templatePath: block.templatePath,
2186
+ blockId: block.blockId
2187
+ })
2188
+ );
2189
+ }, runtime.heartbeatIntervalMs);
2190
+ warningTimer.unref();
2191
+ }
2192
+ try {
2193
+ const executed = block.evaluator === "shell" ? await executeShellScriptBlock({ block, runtime }) : await executeNodeJsScriptBlock({ block, runtime, blockLabel });
2194
+ const durationMs = Date.now() - startTime;
2195
+ emitVerbose(
2196
+ runtime,
2197
+ `Finished template script ${blockLabel} (${block.evaluator}) in ${durationMs}ms.`
2198
+ );
2199
+ return { ...executed, durationMs };
2200
+ } finally {
2201
+ if (warningTimer) {
2202
+ clearInterval(warningTimer);
2203
+ }
2204
+ }
2205
+ }
2206
+ async function evaluateTemplateScriptsUncached(request) {
2207
+ const { content, templatePath, runtime } = request;
2208
+ const parsed = parseTemplateScripts(templatePath, content);
2209
+ if (parsed.blocks.length === 0) {
2210
+ return {
2211
+ renderedContent: content,
2212
+ blockIds: []
2213
+ };
2214
+ }
2215
+ let output = "";
2216
+ let cursor = 0;
2217
+ const blockIds = [];
2218
+ for (const block of parsed.blocks) {
2219
+ output += content.slice(cursor, block.startOffset);
2220
+ cursor = block.endOffset;
2221
+ blockIds.push(block.blockId);
2222
+ runtime.scriptExecutions.set(block.blockId, {
2223
+ blockId: block.blockId,
2224
+ templatePath,
2225
+ status: "running",
2226
+ reusedAcrossTargets: false
2227
+ });
2228
+ try {
2229
+ const executed = await executeScriptBlock({ block, runtime });
2230
+ output += executed.renderedText;
2231
+ runtime.scriptExecutions.set(block.blockId, {
2232
+ blockId: block.blockId,
2233
+ templatePath,
2234
+ status: "succeeded",
2235
+ resultKind: executed.resultKind,
2236
+ renderedPreview: executed.renderedText.slice(0, PREVIEW_LIMIT),
2237
+ durationMs: executed.durationMs,
2238
+ reusedAcrossTargets: false
2239
+ });
2240
+ } catch (error) {
2241
+ const message = error instanceof Error ? error.message : String(error);
2242
+ runtime.scriptExecutions.set(block.blockId, {
2243
+ blockId: block.blockId,
2244
+ templatePath,
2245
+ status: "failed",
2246
+ errorMessage: message,
2247
+ reusedAcrossTargets: false
2248
+ });
2249
+ if (!runtime.failedTemplatePath) {
2250
+ runtime.failedTemplatePath = templatePath;
2251
+ runtime.failedBlockId = block.blockId;
2252
+ }
2253
+ if (error instanceof TemplateScriptExecutionError) {
2254
+ throw error;
1677
2255
  }
2256
+ throw new TemplateScriptExecutionError(message, {
2257
+ templatePath,
2258
+ blockId: block.blockId
2259
+ });
1678
2260
  }
1679
- if (!skippingList) {
1680
- filtered.push(line);
2261
+ }
2262
+ output += content.slice(cursor);
2263
+ return {
2264
+ renderedContent: output,
2265
+ blockIds
2266
+ };
2267
+ }
2268
+ function createTemplateScriptRuntime(options = {}) {
2269
+ return {
2270
+ runId: options.runId ?? randomUUID(),
2271
+ verbose: options.verbose ?? false,
2272
+ heartbeatIntervalMs: options.heartbeatIntervalMs ?? SCRIPT_HEARTBEAT_INTERVAL_MS,
2273
+ cwd: options.cwd ?? process.cwd(),
2274
+ cache: /* @__PURE__ */ new Map(),
2275
+ usageCounts: /* @__PURE__ */ new Map(),
2276
+ scriptExecutions: /* @__PURE__ */ new Map(),
2277
+ warnings: [],
2278
+ failedTemplatePath: null,
2279
+ failedBlockId: null,
2280
+ onWarning: options.onWarning,
2281
+ onVerbose: options.onVerbose
2282
+ };
2283
+ }
2284
+ async function evaluateTemplateScripts(request) {
2285
+ const usageCount = (request.runtime.usageCounts.get(request.templatePath) ?? 0) + 1;
2286
+ request.runtime.usageCounts.set(request.templatePath, usageCount);
2287
+ const existing = request.runtime.cache.get(request.templatePath);
2288
+ if (existing) {
2289
+ const cached = await existing;
2290
+ if (usageCount > 1 && cached.blockIds.length > 0) {
2291
+ markReusedExecutions(request.runtime, cached.blockIds);
1681
2292
  }
2293
+ return cached.renderedContent;
2294
+ }
2295
+ const evaluationPromise = evaluateTemplateScriptsUncached(request);
2296
+ request.runtime.cache.set(request.templatePath, evaluationPromise);
2297
+ try {
2298
+ const evaluated = await evaluationPromise;
2299
+ if (usageCount > 1 && evaluated.blockIds.length > 0) {
2300
+ markReusedExecutions(request.runtime, evaluated.blockIds);
2301
+ }
2302
+ return evaluated.renderedContent;
2303
+ } catch (error) {
2304
+ request.runtime.cache.delete(request.templatePath);
2305
+ throw error;
1682
2306
  }
1683
- const eol = contents.includes("\r\n") ? "\r\n" : "\n";
1684
- const outputLines = [lines[0], ...filtered, ...lines.slice(endIndex)];
1685
- return outputLines.join(eol);
2307
+ }
2308
+ function listTemplateScriptExecutions(runtime) {
2309
+ return [...runtime.scriptExecutions.values()].sort(
2310
+ (left, right) => left.blockId.localeCompare(right.blockId)
2311
+ );
1686
2312
  }
1687
2313
  const utf8Decoder$1 = new TextDecoder("utf-8", { fatal: true });
1688
2314
  const TARGET_FRONTMATTER_KEYS$2 = /* @__PURE__ */ new Set(["targets", "targetagents"]);
@@ -1736,46 +2362,152 @@ async function writeOutputFile(outputPath, content) {
1736
2362
  await writeFile(outputPath, buffer);
1737
2363
  return { status: existing ? "updated" : "created", contentHash };
1738
2364
  }
1739
- async function copySkillDirectory(options) {
1740
- await mkdir(options.destination, { recursive: true });
1741
- const entries = await readdir(options.source, { withFileTypes: true });
1742
- const selectedSkillFile = options.skillFileName.toLowerCase();
1743
- const outputSkillFile = options.outputFileName;
1744
- for (const entry2 of entries) {
1745
- const sourcePath = path.join(options.source, entry2.name);
1746
- const entryLowerName = entry2.name.toLowerCase();
1747
- const isSkillFile = entryLowerName === "skill.md" || entryLowerName === "skill.local.md";
1748
- if (isSkillFile && entryLowerName !== selectedSkillFile) {
2365
+ function markerRank(markerType) {
2366
+ if (markerType === "path") {
2367
+ return 2;
2368
+ }
2369
+ if (markerType === "suffix") {
2370
+ return 1;
2371
+ }
2372
+ return 0;
2373
+ }
2374
+ function normalizeLocalRelativeFilePath(relativePath) {
2375
+ const parts = relativePath.split(path.sep).filter(Boolean);
2376
+ if (parts.length === 0) {
2377
+ return { normalizedPath: relativePath, markerType: null };
2378
+ }
2379
+ const directoryParts = parts.slice(0, -1);
2380
+ const fileName = parts[parts.length - 1];
2381
+ let hasPathMarker = false;
2382
+ const normalizedDirectories = [];
2383
+ for (const part of directoryParts) {
2384
+ if (part === ".local") {
2385
+ hasPathMarker = true;
1749
2386
  continue;
1750
2387
  }
1751
- const destinationPath = isSkillFile ? path.join(options.destination, outputSkillFile) : path.join(options.destination, entry2.name);
1752
- if (entry2.isDirectory()) {
1753
- await copySkillDirectory({
1754
- ...options,
1755
- source: sourcePath,
1756
- destination: path.join(options.destination, entry2.name)
1757
- });
1758
- continue;
2388
+ const { baseName, hadLocalSuffix } = stripLocalPathSuffix(part);
2389
+ if (hadLocalSuffix) {
2390
+ hasPathMarker = true;
1759
2391
  }
1760
- if (!entry2.isFile()) {
2392
+ if (!baseName) {
1761
2393
  continue;
1762
2394
  }
1763
- const buffer = await readFile(sourcePath);
2395
+ normalizedDirectories.push(baseName);
2396
+ }
2397
+ let normalizedFileName = fileName;
2398
+ const isEnvPrefixedFile = fileName.toLowerCase().startsWith(".env");
2399
+ const extension = path.extname(fileName);
2400
+ let strippedSuffix = {
2401
+ outputFileName: fileName,
2402
+ hadLocalSuffix: false
2403
+ };
2404
+ if (!isEnvPrefixedFile) {
2405
+ strippedSuffix = stripLocalSuffix(fileName, extension);
2406
+ if (strippedSuffix.hadLocalSuffix) {
2407
+ normalizedFileName = strippedSuffix.outputFileName;
2408
+ }
2409
+ const strippedPath = stripLocalPathSuffix(normalizedFileName);
2410
+ if (strippedPath.hadLocalSuffix) {
2411
+ hasPathMarker = true;
2412
+ normalizedFileName = strippedPath.baseName;
2413
+ }
2414
+ }
2415
+ const normalizedParts = [...normalizedDirectories, normalizedFileName].filter(Boolean);
2416
+ const markerType = hasPathMarker ? "path" : strippedSuffix.hadLocalSuffix ? "suffix" : null;
2417
+ return {
2418
+ normalizedPath: normalizedParts.join(path.sep),
2419
+ markerType
2420
+ };
2421
+ }
2422
+ async function collectSkillCopyCandidates(options) {
2423
+ const selectedSkillFile = options.skillFileName.toLowerCase();
2424
+ const candidates = [];
2425
+ const walk = async (currentSource, relativePrefix = "") => {
2426
+ const entries = await readdir(currentSource, { withFileTypes: true });
2427
+ for (const entry2 of entries) {
2428
+ const sourcePath = path.join(currentSource, entry2.name);
2429
+ const relativePath = relativePrefix ? path.join(relativePrefix, entry2.name) : entry2.name;
2430
+ if (entry2.isDirectory()) {
2431
+ await walk(sourcePath, relativePath);
2432
+ continue;
2433
+ }
2434
+ if (!entry2.isFile()) {
2435
+ continue;
2436
+ }
2437
+ const entryLowerName = entry2.name.toLowerCase();
2438
+ const isSkillFile = entryLowerName === "skill.md" || entryLowerName === "skill.local.md";
2439
+ if (isSkillFile && entryLowerName !== selectedSkillFile) {
2440
+ continue;
2441
+ }
2442
+ const detectedMarker = detectLocalMarkerFromPath(relativePath) ?? (options.sourceType === "local" && options.markerType === "path" ? "path" : null);
2443
+ if (isSkillFile) {
2444
+ candidates.push({
2445
+ sourcePath,
2446
+ destinationPath: path.join(options.destination, options.outputFileName),
2447
+ isSkillFile: true,
2448
+ markerRank: markerRank(detectedMarker)
2449
+ });
2450
+ continue;
2451
+ }
2452
+ const normalized = normalizeLocalRelativeFilePath(relativePath);
2453
+ if (!normalized.normalizedPath) {
2454
+ continue;
2455
+ }
2456
+ const marker = normalized.markerType ?? detectedMarker;
2457
+ candidates.push({
2458
+ sourcePath,
2459
+ destinationPath: path.join(options.destination, normalized.normalizedPath),
2460
+ isSkillFile: false,
2461
+ markerRank: markerRank(marker)
2462
+ });
2463
+ }
2464
+ };
2465
+ await walk(options.source);
2466
+ return candidates;
2467
+ }
2468
+ async function copySkillDirectory(options) {
2469
+ await mkdir(options.destination, { recursive: true });
2470
+ const candidates = await collectSkillCopyCandidates({
2471
+ source: options.source,
2472
+ destination: options.destination,
2473
+ skillFileName: options.skillFileName,
2474
+ outputFileName: options.outputFileName,
2475
+ sourceType: options.sourceType,
2476
+ markerType: options.markerType
2477
+ });
2478
+ const winnerByOutputPath = /* @__PURE__ */ new Map();
2479
+ for (const candidate of candidates) {
2480
+ const key = path.normalize(candidate.destinationPath).replace(/\\/g, "/").toLowerCase();
2481
+ const existing = winnerByOutputPath.get(key);
2482
+ if (!existing || candidate.markerRank > existing.markerRank || candidate.markerRank === existing.markerRank && candidate.sourcePath.localeCompare(existing.sourcePath) > 0) {
2483
+ winnerByOutputPath.set(key, candidate);
2484
+ }
2485
+ }
2486
+ const winners = [...winnerByOutputPath.values()].sort(
2487
+ (left, right) => left.destinationPath.localeCompare(right.destinationPath)
2488
+ );
2489
+ for (const winner of winners) {
2490
+ const buffer = await readFile(winner.sourcePath);
1764
2491
  const decoded = decodeUtf8$1(buffer);
1765
2492
  if (decoded === null) {
1766
- await mkdir(path.dirname(destinationPath), { recursive: true });
1767
- await writeFile(destinationPath, buffer);
2493
+ await mkdir(path.dirname(winner.destinationPath), { recursive: true });
2494
+ await writeFile(winner.destinationPath, buffer);
1768
2495
  continue;
1769
2496
  }
1770
- const templated = applyAgentTemplating({
2497
+ const withScripts = options.templateScriptRuntime ? await evaluateTemplateScripts({
2498
+ templatePath: winner.sourcePath,
1771
2499
  content: decoded,
2500
+ runtime: options.templateScriptRuntime
2501
+ }) : decoded;
2502
+ const templated = applyAgentTemplating({
2503
+ content: withScripts,
1772
2504
  target: options.targetId,
1773
2505
  validAgents: options.validAgents,
1774
- sourcePath
2506
+ sourcePath: winner.sourcePath
1775
2507
  });
1776
- const output = isSkillFile ? stripFrontmatterFields(templated, TARGET_FRONTMATTER_KEYS$2) : templated;
1777
- await mkdir(path.dirname(destinationPath), { recursive: true });
1778
- await writeFile(destinationPath, output, "utf8");
2508
+ const output = winner.isSkillFile ? stripFrontmatterFields(templated, TARGET_FRONTMATTER_KEYS$2) : templated;
2509
+ await mkdir(path.dirname(winner.destinationPath), { recursive: true });
2510
+ await writeFile(winner.destinationPath, output, "utf8");
1779
2511
  }
1780
2512
  }
1781
2513
  const defaultSkillWriter = {
@@ -1791,7 +2523,10 @@ const defaultSkillWriter = {
1791
2523
  targetId: options.context.targetId,
1792
2524
  validAgents: options.context.validAgents,
1793
2525
  skillFileName: item.skillFileName,
1794
- outputFileName: item.outputFileName
2526
+ outputFileName: item.outputFileName,
2527
+ sourceType: item.sourceType,
2528
+ markerType: item.markerType,
2529
+ templateScriptRuntime: options.context.templateScriptRuntime
1795
2530
  });
1796
2531
  return { status: "created" };
1797
2532
  }
@@ -1803,8 +2538,13 @@ const defaultSubagentWriter = {
1803
2538
  if (!item) {
1804
2539
  return writeOutputFile(options.outputPath, options.content);
1805
2540
  }
1806
- const templated = applyAgentTemplating({
2541
+ const withScripts = options.context.templateScriptRuntime ? await evaluateTemplateScripts({
2542
+ templatePath: item.sourcePath,
1807
2543
  content: item.rawContents,
2544
+ runtime: options.context.templateScriptRuntime
2545
+ }) : item.rawContents;
2546
+ const templated = applyAgentTemplating({
2547
+ content: withScripts,
1808
2548
  target: options.context.targetId,
1809
2549
  validAgents: options.context.validAgents,
1810
2550
  sourcePath: item.sourcePath
@@ -1888,6 +2628,10 @@ function formatDisplayPath$4(repoRoot, absolutePath) {
1888
2628
  const isWithinRepo = relative && !relative.startsWith("..") && !path.isAbsolute(relative);
1889
2629
  return isWithinRepo ? relative : absolutePath;
1890
2630
  }
2631
+ function isWithinDirectory(root, candidate) {
2632
+ const relative = path.relative(root, candidate);
2633
+ return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
2634
+ }
1891
2635
  function sortCandidates(candidates) {
1892
2636
  return [...candidates].sort(
1893
2637
  (left, right) => left.source.sourcePath.localeCompare(right.source.sourcePath)
@@ -2023,6 +2767,7 @@ function normalizeInstructionTargets(targets) {
2023
2767
  return resolved;
2024
2768
  }
2025
2769
  async function syncInstructions(request) {
2770
+ const templateScriptRuntime = request.templateScriptRuntime ?? createTemplateScriptRuntime({ cwd: request.repoRoot });
2026
2771
  const targets = normalizeInstructionTargets(request.targets);
2027
2772
  const includeLocal = !(request.excludeLocal ?? false);
2028
2773
  const summarySourcePath = request.repoRoot;
@@ -2134,8 +2879,13 @@ async function syncInstructions(request) {
2134
2879
  });
2135
2880
  const outputPath = resolveInstructionOutputPath(resolvedOutputDir, filename);
2136
2881
  const key = buildOutputKey(outputPath, selection.group);
2137
- const content = applyAgentTemplating({
2882
+ const withScripts = templateScriptRuntime ? await evaluateTemplateScripts({
2883
+ templatePath: template.sourcePath,
2138
2884
  content: template.body,
2885
+ runtime: templateScriptRuntime
2886
+ }) : template.body;
2887
+ const content = applyAgentTemplating({
2888
+ content: withScripts,
2139
2889
  target: targetName,
2140
2890
  validAgents: request.validAgents,
2141
2891
  sourcePath: template.sourcePath
@@ -2391,7 +3141,8 @@ async function syncInstructions(request) {
2391
3141
  homeDir,
2392
3142
  targetId: candidate.targetName,
2393
3143
  outputType: "instructions",
2394
- validAgents: request.validAgents
3144
+ validAgents: request.validAgents,
3145
+ templateScriptRuntime
2395
3146
  }
2396
3147
  });
2397
3148
  recordCount(groupResult.counts, writeResult.status);
@@ -2433,8 +3184,12 @@ async function syncInstructions(request) {
2433
3184
  };
2434
3185
  for (const [key, entry2] of previousEntries) {
2435
3186
  const group = resolveInstructionTargetGroup(entry2.targetName);
3187
+ if (isWithinDirectory(agentsRoot, entry2.outputPath)) {
3188
+ mergedEntries.set(key, entry2);
3189
+ continue;
3190
+ }
2436
3191
  const groupSelected = activeGroups.has(group);
2437
- if (!groupSelected) {
3192
+ if (!groupSelected && !removeMissing) {
2438
3193
  mergedEntries.set(key, entry2);
2439
3194
  continue;
2440
3195
  }
@@ -2533,17 +3288,26 @@ async function syncInstructions(request) {
2533
3288
  if (managedManifest.entries.length > 0 || nextManaged.size > 0) {
2534
3289
  const updatedEntries = [];
2535
3290
  for (const entry2 of managedManifest.entries) {
2536
- if (entry2.sourceType !== "instruction" || !selectedTargetIds.has(entry2.targetId)) {
3291
+ if (entry2.sourceType !== "instruction") {
2537
3292
  updatedEntries.push(entry2);
2538
3293
  continue;
2539
3294
  }
3295
+ if (isWithinDirectory(agentsRoot, entry2.outputPath)) {
3296
+ updatedEntries.push(entry2);
3297
+ continue;
3298
+ }
3299
+ const targetSelected = selectedTargetIds.has(entry2.targetId);
2540
3300
  const key = buildManagedOutputKey(entry2);
2541
3301
  if (nextManaged.has(key)) {
2542
3302
  continue;
2543
3303
  }
3304
+ if (!targetSelected && !removeMissing) {
3305
+ updatedEntries.push(entry2);
3306
+ continue;
3307
+ }
2544
3308
  const activeSources = activeSourcesByTarget.get(entry2.targetId);
2545
3309
  const sourceStillActive = activeSources?.has(entry2.sourceId) ?? false;
2546
- if (!removeMissing || sourceStillActive) {
3310
+ if (targetSelected && (!removeMissing || sourceStillActive)) {
2547
3311
  updatedEntries.push(entry2);
2548
3312
  continue;
2549
3313
  }
@@ -2623,33 +3387,6 @@ async function syncInstructions(request) {
2623
3387
  sourceCounts
2624
3388
  };
2625
3389
  }
2626
- async function pathExists$1(candidate) {
2627
- try {
2628
- await stat(candidate);
2629
- return true;
2630
- } catch {
2631
- return false;
2632
- }
2633
- }
2634
- async function findUp(startDir, markerRelativePath) {
2635
- let current = path.resolve(startDir);
2636
- let previous = "";
2637
- while (current !== previous) {
2638
- if (await pathExists$1(path.join(current, markerRelativePath))) {
2639
- return current;
2640
- }
2641
- previous = current;
2642
- current = path.dirname(current);
2643
- }
2644
- return null;
2645
- }
2646
- async function findRepoRoot(startDir) {
2647
- const gitRoot = await findUp(startDir, ".git");
2648
- if (gitRoot) {
2649
- return gitRoot;
2650
- }
2651
- return await findUp(startDir, "package.json");
2652
- }
2653
3390
  async function readDirectoryStats(directory) {
2654
3391
  try {
2655
3392
  return await stat(directory);
@@ -2704,8 +3441,8 @@ function resolveSkillName(frontmatter, fallback) {
2704
3441
  }
2705
3442
  return fallback;
2706
3443
  }
2707
- function normalizeSkillKey$1(name) {
2708
- return name.trim().toLowerCase();
3444
+ function normalizeSkillKey$1(relativePath) {
3445
+ return path.normalize(relativePath).replace(/\\/g, "/").toLowerCase();
2709
3446
  }
2710
3447
  function resolveSkillRelativePath(skillsRoot, directoryPath) {
2711
3448
  const relativePath = path.relative(skillsRoot, directoryPath);
@@ -2868,7 +3605,7 @@ async function loadSkillCatalog(repoRoot, options = {}) {
2868
3605
  shared: sharedSkills,
2869
3606
  localPath: localPathSkills,
2870
3607
  localSuffix: localSuffixSkills,
2871
- key: (skill) => normalizeSkillKey$1(skill.name)
3608
+ key: (skill) => normalizeSkillKey$1(skill.relativePath)
2872
3609
  });
2873
3610
  const skills = includeLocal ? [...sharedEffectiveSkills, ...localEffectiveSkills] : sharedSkills;
2874
3611
  return {
@@ -2921,6 +3658,7 @@ function buildInvalidTargetWarnings$2(skills) {
2921
3658
  return warnings;
2922
3659
  }
2923
3660
  async function syncSkills(request) {
3661
+ const templateScriptRuntime = request.templateScriptRuntime ?? createTemplateScriptRuntime({ cwd: request.repoRoot });
2924
3662
  const sourcePath = resolveSharedCategoryRoot(request.repoRoot, "skills", request.agentsDir);
2925
3663
  const skillTargets = request.targets.filter(
2926
3664
  (target) => normalizeOutputDefinition(target.outputs.skills) !== null
@@ -3143,7 +3881,9 @@ async function syncSkills(request) {
3143
3881
  directoryPath: selected.skill.directoryPath,
3144
3882
  skillFileName: selected.skill.skillFileName,
3145
3883
  outputFileName: selected.skill.outputFileName,
3146
- sourcePath: selected.skill.sourcePath
3884
+ sourcePath: selected.skill.sourcePath,
3885
+ sourceType: selected.skill.sourceType,
3886
+ markerType: selected.skill.markerType
3147
3887
  };
3148
3888
  await writer.write({
3149
3889
  outputPath: selected.outputPath,
@@ -3155,7 +3895,8 @@ async function syncSkills(request) {
3155
3895
  homeDir,
3156
3896
  targetId: target.id,
3157
3897
  outputType: "skills",
3158
- validAgents: request.validAgents
3898
+ validAgents: request.validAgents,
3899
+ templateScriptRuntime
3159
3900
  }
3160
3901
  });
3161
3902
  const checksum = await hashOutputPath(selected.outputPath);
@@ -3787,9 +4528,14 @@ function buildInvalidTargetWarnings$1(commands) {
3787
4528
  }
3788
4529
  return warnings;
3789
4530
  }
3790
- function applyTemplatingToCommand(command, targetName, validAgents) {
3791
- const templatedContents = applyAgentTemplating({
4531
+ async function applyTemplatingToCommand(command, targetName, validAgents, runtime) {
4532
+ const withScripts = runtime ? await evaluateTemplateScripts({
4533
+ templatePath: command.sourcePath,
3792
4534
  content: command.rawContents,
4535
+ runtime
4536
+ }) : command.rawContents;
4537
+ const templatedContents = applyAgentTemplating({
4538
+ content: withScripts,
3793
4539
  target: targetName,
3794
4540
  validAgents,
3795
4541
  sourcePath: command.sourcePath
@@ -3847,6 +4593,7 @@ async function ensureBackupPath(outputPath) {
3847
4593
  }
3848
4594
  }
3849
4595
  async function syncSlashCommands(request) {
4596
+ const templateScriptRuntime = request.templateScriptRuntime ?? createTemplateScriptRuntime({ cwd: request.repoRoot });
3850
4597
  const catalog = await loadCommandCatalog(request.repoRoot, {
3851
4598
  includeLocal: !request.excludeLocal,
3852
4599
  agentsDir: request.agentsDir,
@@ -3989,40 +4736,32 @@ async function syncSlashCommands(request) {
3989
4736
  });
3990
4737
  commandPaths = [{ location: "project", path: path.join(basePath, "SKILL.md") }];
3991
4738
  } else {
3992
- if (commandDef.projectPath) {
3993
- const resolved = resolveCommandOutputPath({
3994
- template: commandDef.projectPath,
3995
- context: {
3996
- repoRoot: request.repoRoot,
3997
- agentsDir: agentsDirPath,
3998
- homeDir,
3999
- targetId: target.id,
4000
- itemName: command.name,
4001
- commandLocation: "project"
4002
- },
4003
- item: command,
4004
- baseDir: request.repoRoot
4005
- });
4006
- commandPaths.push({ location: "project", path: resolved });
4007
- }
4008
- if (commandDef.userPath) {
4739
+ const preferredLocation = commandDef.projectPath ? "project" : "user";
4740
+ const preferredTemplate = preferredLocation === "project" ? commandDef.projectPath : commandDef.userPath;
4741
+ const baseDir = preferredLocation === "project" ? request.repoRoot : homeDir;
4742
+ if (preferredTemplate) {
4009
4743
  const resolved = resolveCommandOutputPath({
4010
- template: commandDef.userPath,
4744
+ template: preferredTemplate,
4011
4745
  context: {
4012
4746
  repoRoot: request.repoRoot,
4013
4747
  agentsDir: agentsDirPath,
4014
4748
  homeDir,
4015
4749
  targetId: target.id,
4016
4750
  itemName: command.name,
4017
- commandLocation: "user"
4751
+ commandLocation: preferredLocation
4018
4752
  },
4019
4753
  item: command,
4020
- baseDir: homeDir
4754
+ baseDir
4021
4755
  });
4022
- commandPaths.push({ location: "user", path: resolved });
4756
+ commandPaths.push({ location: preferredLocation, path: resolved });
4023
4757
  }
4024
4758
  }
4025
- const templated = applyTemplatingToCommand(command, target.id, validAgents);
4759
+ const templated = await applyTemplatingToCommand(
4760
+ command,
4761
+ target.id,
4762
+ validAgents,
4763
+ templateScriptRuntime
4764
+ );
4026
4765
  const writer = resolveWriter(commandDef.writer, writerRegistry);
4027
4766
  const converter = resolveConverter(commandDef.converter, converterRegistry);
4028
4767
  for (const entry2 of commandPaths) {
@@ -4159,7 +4898,8 @@ async function syncSlashCommands(request) {
4159
4898
  targetId: target.id,
4160
4899
  outputType: "commands",
4161
4900
  commandLocation: selected.location,
4162
- validAgents
4901
+ validAgents,
4902
+ templateScriptRuntime
4163
4903
  }
4164
4904
  });
4165
4905
  const checksum = writeResult.contentHash ?? await hashOutputPath(selected.outputPath);
@@ -4764,6 +5504,7 @@ function formatSubagentSummary(summary, jsonOutput) {
4764
5504
  return lines.join("\n");
4765
5505
  }
4766
5506
  async function syncSubagents(request) {
5507
+ const templateScriptRuntime = request.templateScriptRuntime ?? createTemplateScriptRuntime({ cwd: request.repoRoot });
4767
5508
  const catalog = await loadSubagentCatalog(request.repoRoot, {
4768
5509
  includeLocal: !request.excludeLocal,
4769
5510
  agentsDir: request.agentsDir,
@@ -5044,7 +5785,8 @@ async function syncSubagents(request) {
5044
5785
  homeDir,
5045
5786
  targetId: target.id,
5046
5787
  outputType: "subagents",
5047
- validAgents
5788
+ validAgents,
5789
+ templateScriptRuntime
5048
5790
  }
5049
5791
  });
5050
5792
  const checksum = selected.outputKind === "skill" ? await hashOutputPath(selected.outputPath) : writeResult.contentHash ?? await hashOutputPath(selected.outputPath);
@@ -6007,6 +6749,147 @@ function formatLocalItemsOutput(items, repoRoot, jsonOutput) {
6007
6749
  }
6008
6750
  return lines.join("\n");
6009
6751
  }
6752
+ function hasTemplateScripts(content) {
6753
+ return hasTemplateScriptMarkup(content);
6754
+ }
6755
+ function addTemplateScriptSource(sources, templatePath, content) {
6756
+ if (!hasTemplateScripts(content)) {
6757
+ return;
6758
+ }
6759
+ if (sources.has(templatePath)) {
6760
+ return;
6761
+ }
6762
+ sources.set(templatePath, content);
6763
+ }
6764
+ function intersectsTargets(targets, selectedTargetIds) {
6765
+ return targets.some((target) => selectedTargetIds.has(target));
6766
+ }
6767
+ async function listAllFiles(root) {
6768
+ const entries = await readdir(root, { withFileTypes: true });
6769
+ const files = [];
6770
+ for (const entry2 of entries) {
6771
+ const entryPath = path.join(root, entry2.name);
6772
+ if (entry2.isDirectory()) {
6773
+ files.push(...await listAllFiles(entryPath));
6774
+ continue;
6775
+ }
6776
+ if (entry2.isFile()) {
6777
+ files.push(entryPath);
6778
+ }
6779
+ }
6780
+ return files;
6781
+ }
6782
+ async function gatherTemplateScriptSources(options) {
6783
+ const selectedSkillTargetIds = new Set(options.selectedSkillTargets.map((target) => target.id));
6784
+ const selectedCommandTargetIds = new Set(
6785
+ options.selectedCommandTargets.map((target) => target.id)
6786
+ );
6787
+ const selectedSubagentTargetIds = new Set(
6788
+ options.selectedSubagentTargets.map((target) => target.id)
6789
+ );
6790
+ const selectedInstructionTargetIds = new Set(
6791
+ options.selectedInstructionTargets.map((target) => target.id)
6792
+ );
6793
+ const sources = /* @__PURE__ */ new Map();
6794
+ if (options.commandsAvailable && options.selectedCommandTargets.length > 0) {
6795
+ const commandCatalog = await loadCommandCatalog(options.repoRoot, {
6796
+ includeLocal: !options.excludeLocalCommands,
6797
+ agentsDir: options.agentsDir,
6798
+ resolveTargetName: options.resolveTargetName
6799
+ });
6800
+ for (const command of commandCatalog.commands) {
6801
+ const effectiveTargets = resolveEffectiveTargets({
6802
+ defaultTargets: command.targetAgents,
6803
+ overrideOnly: options.overrideOnly,
6804
+ overrideSkip: options.overrideSkip,
6805
+ allTargets: options.allTargetIds
6806
+ });
6807
+ if (!intersectsTargets(effectiveTargets, selectedCommandTargetIds)) {
6808
+ continue;
6809
+ }
6810
+ addTemplateScriptSource(sources, command.sourcePath, command.rawContents);
6811
+ }
6812
+ }
6813
+ if (options.selectedSubagentTargets.length > 0) {
6814
+ const subagentCatalog = await loadSubagentCatalog(options.repoRoot, {
6815
+ includeLocal: !options.excludeLocalAgents,
6816
+ agentsDir: options.agentsDir,
6817
+ resolveTargetName: options.resolveTargetName
6818
+ });
6819
+ for (const subagent of subagentCatalog.subagents) {
6820
+ const effectiveTargets = resolveEffectiveTargets({
6821
+ defaultTargets: subagent.targetAgents,
6822
+ overrideOnly: options.overrideOnly,
6823
+ overrideSkip: options.overrideSkip,
6824
+ allTargets: options.allTargetIds
6825
+ });
6826
+ if (!intersectsTargets(effectiveTargets, selectedSubagentTargetIds)) {
6827
+ continue;
6828
+ }
6829
+ addTemplateScriptSource(sources, subagent.sourcePath, subagent.rawContents);
6830
+ }
6831
+ }
6832
+ if (options.selectedInstructionTargets.length > 0) {
6833
+ const templateCatalog = await loadInstructionTemplateCatalog({
6834
+ repoRoot: options.repoRoot,
6835
+ includeLocal: !options.excludeLocalInstructions,
6836
+ agentsDir: options.agentsDir,
6837
+ resolveTargetName: options.resolveTargetName
6838
+ });
6839
+ for (const template of templateCatalog.templates) {
6840
+ const effectiveTargets = resolveEffectiveTargets({
6841
+ defaultTargets: template.targets,
6842
+ overrideOnly: options.overrideOnly,
6843
+ overrideSkip: options.overrideSkip,
6844
+ allTargets: options.allTargetIds
6845
+ });
6846
+ if (!intersectsTargets(effectiveTargets, selectedInstructionTargetIds)) {
6847
+ continue;
6848
+ }
6849
+ addTemplateScriptSource(sources, template.sourcePath, template.body);
6850
+ }
6851
+ }
6852
+ if (options.skillsAvailable && options.selectedSkillTargets.length > 0) {
6853
+ const skillCatalog = await loadSkillCatalog(options.repoRoot, {
6854
+ includeLocal: !options.excludeLocalSkills,
6855
+ agentsDir: options.agentsDir,
6856
+ resolveTargetName: options.resolveTargetName
6857
+ });
6858
+ for (const skill of skillCatalog.skills) {
6859
+ const effectiveTargets = resolveEffectiveTargets({
6860
+ defaultTargets: skill.targetAgents,
6861
+ overrideOnly: options.overrideOnly,
6862
+ overrideSkip: options.overrideSkip,
6863
+ allTargets: options.allTargetIds
6864
+ });
6865
+ if (!intersectsTargets(effectiveTargets, selectedSkillTargetIds)) {
6866
+ continue;
6867
+ }
6868
+ const files = await listAllFiles(skill.directoryPath);
6869
+ for (const filePath of files) {
6870
+ const buffer = await readFile(filePath);
6871
+ const decoded = decodeUtf8(buffer);
6872
+ if (decoded === null) {
6873
+ continue;
6874
+ }
6875
+ addTemplateScriptSource(sources, filePath, decoded);
6876
+ }
6877
+ }
6878
+ }
6879
+ return [...sources.entries()].map(([templatePath, content]) => ({ templatePath, content })).sort((left, right) => left.templatePath.localeCompare(right.templatePath));
6880
+ }
6881
+ function buildSyncRunMetadata(options) {
6882
+ const warnings = [...options.runtime.warnings];
6883
+ return {
6884
+ runId: options.runtime.runId,
6885
+ status: options.status,
6886
+ failedTemplatePath: options.runtime.failedTemplatePath,
6887
+ failedBlockId: options.runtime.failedBlockId,
6888
+ partialOutputsWritten: options.partialOutputsWritten,
6889
+ scriptExecutions: listTemplateScriptExecutions(options.runtime),
6890
+ warnings
6891
+ };
6892
+ }
6010
6893
  async function assertSourceDirectory(sourcePath) {
6011
6894
  try {
6012
6895
  const stats = await stat(sourcePath);
@@ -6284,7 +7167,7 @@ function logNonInteractiveNotices(options) {
6284
7167
  logWithChannel(`${target.displayName} commands are user-only.`, options.jsonOutput);
6285
7168
  } else if (hasUser && hasProject) {
6286
7169
  logWithChannel(
6287
- `${target.displayName} commands will be written to project and user locations.`,
7170
+ `${target.displayName} commands prefer the project location (user path is fallback-only).`,
6288
7171
  options.jsonOutput
6289
7172
  );
6290
7173
  }
@@ -6344,7 +7227,7 @@ function buildCommandSummary(sourcePath, targets, status, message, excludedLocal
6344
7227
  return {
6345
7228
  sourcePath,
6346
7229
  results: normalizedTargets.map((target) => {
6347
- const verb = "Skipped";
7230
+ const verb = status === "failed" ? "Failed" : "Skipped";
6348
7231
  return {
6349
7232
  targetName: target.id,
6350
7233
  status,
@@ -6367,7 +7250,7 @@ function buildSkillsSummary(repoRoot, sourcePath, targets, status, reason, exclu
6367
7250
  const results = normalizeTargets(targets).map((target) => ({
6368
7251
  targetName: target.id,
6369
7252
  status,
6370
- message: `${"Skipped"} ${sourceDisplay} for ${target.displayName}: ${reason}`,
7253
+ message: `${status === "failed" ? "Failed" : "Skipped"} ${sourceDisplay} for ${target.displayName}: ${reason}`,
6371
7254
  error: reason
6372
7255
  }));
6373
7256
  return buildSummary(sourcePath, results, [], {
@@ -6405,6 +7288,27 @@ function buildInstructionsSummary(repoRoot, targets, status, message, excludedLo
6405
7288
  }
6406
7289
  };
6407
7290
  }
7291
+ function buildSubagentSummary(targets, status, message, excludedLocal, sourcePath) {
7292
+ const results = normalizeTargets(targets).map((target) => ({
7293
+ targetName: target.id,
7294
+ status,
7295
+ message: `${"Failed"} ${target.displayName}: ${message}`,
7296
+ error: message,
7297
+ counts: { created: 0, updated: 0, removed: 0, converted: 0, skipped: 0 },
7298
+ warnings: []
7299
+ }));
7300
+ return {
7301
+ sourcePath,
7302
+ results,
7303
+ warnings: [],
7304
+ hadFailures: status === "failed",
7305
+ sourceCounts: {
7306
+ shared: 0,
7307
+ local: 0,
7308
+ excludedLocal
7309
+ }
7310
+ };
7311
+ }
6408
7312
  function mergeWarnings(existing, additions) {
6409
7313
  if (additions.length === 0) {
6410
7314
  return existing;
@@ -6473,9 +7377,29 @@ function buildAvailabilitySubagentWarnings(skips) {
6473
7377
  function emitSyncSummary(options) {
6474
7378
  const { combined, jsonOutput, repoRoot, agentsDir, ignoreRules } = options;
6475
7379
  if (jsonOutput) {
6476
- console.log(JSON.stringify(combined, null, 2));
7380
+ console.log(
7381
+ JSON.stringify(
7382
+ {
7383
+ ...combined,
7384
+ runId: combined.syncRun.runId,
7385
+ status: combined.syncRun.status,
7386
+ failedTemplatePath: combined.syncRun.failedTemplatePath,
7387
+ failedBlockId: combined.syncRun.failedBlockId,
7388
+ partialOutputsWritten: combined.syncRun.partialOutputsWritten,
7389
+ scriptExecutions: combined.syncRun.scriptExecutions,
7390
+ warnings: combined.syncRun.warnings
7391
+ },
7392
+ null,
7393
+ 2
7394
+ )
7395
+ );
6477
7396
  } else {
6478
7397
  const outputs = [];
7398
+ if (combined.syncRun.status === "failed" && combined.syncRun.failedTemplatePath && combined.syncRun.failedBlockId) {
7399
+ outputs.push(
7400
+ `Failed template script: ${combined.syncRun.failedTemplatePath} (${combined.syncRun.failedBlockId})`
7401
+ );
7402
+ }
6479
7403
  const instructionOutput = formatInstructionSummary(combined.instructions);
6480
7404
  if (instructionOutput.length > 0) {
6481
7405
  outputs.push(instructionOutput);
@@ -6496,6 +7420,9 @@ function emitSyncSummary(options) {
6496
7420
  const warningRules = ignoreRules ?? buildAgentsIgnoreRules(repoRoot, agentsDir);
6497
7421
  outputs.push(`Warning: Missing ignore rules for local sources (${warningRules.join(", ")}).`);
6498
7422
  }
7423
+ for (const warning of combined.syncRun.warnings) {
7424
+ outputs.push(`Warning: ${warning.message}`);
7425
+ }
6499
7426
  if (outputs.length > 0) {
6500
7427
  console.log(outputs.join("\n"));
6501
7428
  }
@@ -6543,6 +7470,10 @@ const syncCommand = {
6543
7470
  type: "string",
6544
7471
  choices: ["overwrite", "rename", "skip"],
6545
7472
  describe: "Conflict resolution strategy for slash commands"
7473
+ }).option("verbose", {
7474
+ type: "boolean",
7475
+ default: false,
7476
+ describe: "Show per-script execution telemetry during sync"
6546
7477
  }).option("json", {
6547
7478
  type: "boolean",
6548
7479
  default: false,
@@ -6553,7 +7484,7 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
6553
7484
  ).example("omniagent sync", "Sync available targets (auto-detects agent CLIs on PATH)").example("omniagent sync --skip <target>", "Skip a target").example("omniagent sync --only <target>", "Sync only one target").example("omniagent sync --agentsDir ./my-custom-agents", "Use a custom agents directory").example("omniagent sync --exclude-local", "Sync shared sources only").example(
6554
7485
  "omniagent sync --exclude-local=skills,commands",
6555
7486
  "Exclude local skills and commands"
6556
- ).example("omniagent sync --list-local", "List detected local items").example("omniagent sync --yes", "Accept defaults and apply changes").example("omniagent sync --json", "Output a JSON summary"),
7487
+ ).example("omniagent sync --list-local", "List detected local items").example("omniagent sync --yes", "Accept defaults and apply changes").example("omniagent sync --verbose", "Show per-script execution telemetry").example("omniagent sync --json", "Output a JSON summary"),
6557
7488
  handler: async (argv) => {
6558
7489
  try {
6559
7490
  const skipList = parseList(argv.skip);
@@ -6573,9 +7504,21 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
6573
7504
  const excludeLocalAgents = excludeLocalCategories.has("agents");
6574
7505
  const excludeLocalInstructions = excludeLocalCategories.has("instructions");
6575
7506
  const jsonOutput = argv.json ?? false;
7507
+ const verboseOutput = argv.verbose ?? false;
6576
7508
  const yes = argv.yes ?? false;
6577
7509
  const removeMissing = argv.removeMissing ?? true;
6578
7510
  const listLocal = argv.listLocal ?? false;
7511
+ let partialOutputsWritten = false;
7512
+ const scriptRuntime = createTemplateScriptRuntime({
7513
+ verbose: verboseOutput,
7514
+ cwd: process.cwd(),
7515
+ onWarning: (warning) => {
7516
+ logWithChannel(`Warning: ${warning.message}`, jsonOutput);
7517
+ },
7518
+ onVerbose: (message) => {
7519
+ logWithChannel(message, jsonOutput);
7520
+ }
7521
+ });
6579
7522
  const startDir = process.cwd();
6580
7523
  const repoRoot = await findRepoRoot(startDir);
6581
7524
  if (!repoRoot) {
@@ -6585,6 +7528,7 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
6585
7528
  process.exit(1);
6586
7529
  return;
6587
7530
  }
7531
+ scriptRuntime.cwd = repoRoot;
6588
7532
  const agentsDirResolution = resolveAgentsDir(repoRoot, argv.agentsDir);
6589
7533
  if (agentsDirResolution.source === "override") {
6590
7534
  const validation2 = await validateAgentsDir(repoRoot, argv.agentsDir);
@@ -6802,7 +7746,12 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
6802
7746
  subagents: subagentSummary2,
6803
7747
  commands: commandsSummary2,
6804
7748
  hadFailures: false,
6805
- missingIgnoreRules: false
7749
+ missingIgnoreRules: false,
7750
+ syncRun: buildSyncRunMetadata({
7751
+ runtime: scriptRuntime,
7752
+ status: "completed",
7753
+ partialOutputsWritten
7754
+ })
6806
7755
  };
6807
7756
  emitSyncSummary({
6808
7757
  combined: combined2,
@@ -6860,6 +7809,90 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
6860
7809
  process.exit(1);
6861
7810
  return;
6862
7811
  }
7812
+ try {
7813
+ const scriptSources = await gatherTemplateScriptSources({
7814
+ repoRoot,
7815
+ agentsDir,
7816
+ allTargetIds: resolved.targets.map((target) => target.id),
7817
+ resolveTargetName,
7818
+ overrideOnly,
7819
+ overrideSkip,
7820
+ excludeLocalSkills,
7821
+ excludeLocalCommands,
7822
+ excludeLocalAgents,
7823
+ excludeLocalInstructions,
7824
+ selectedSkillTargets,
7825
+ selectedCommandTargets,
7826
+ selectedSubagentTargets,
7827
+ selectedInstructionTargets,
7828
+ skillsAvailable: hasSkillsToSync,
7829
+ commandsAvailable: hasCommandsToSync
7830
+ });
7831
+ for (const source of scriptSources) {
7832
+ await evaluateTemplateScripts({
7833
+ templatePath: source.templatePath,
7834
+ content: source.content,
7835
+ runtime: scriptRuntime
7836
+ });
7837
+ }
7838
+ } catch (error) {
7839
+ if (error instanceof TemplateScriptExecutionError) {
7840
+ const warning = {
7841
+ code: "sync_warning",
7842
+ message: error.message,
7843
+ templatePath: error.templatePath,
7844
+ blockId: error.blockId
7845
+ };
7846
+ scriptRuntime.warnings.push(warning);
7847
+ const combined2 = {
7848
+ instructions: buildInstructionsSummary(
7849
+ repoRoot,
7850
+ selectedInstructionTargets,
7851
+ "failed",
7852
+ error.message,
7853
+ excludeLocalInstructions
7854
+ ),
7855
+ skills: buildSkillsSummary(
7856
+ repoRoot,
7857
+ skillsSourcePath,
7858
+ selectedSkillTargets,
7859
+ "failed",
7860
+ error.message,
7861
+ excludeLocalSkills
7862
+ ),
7863
+ subagents: buildSubagentSummary(
7864
+ selectedSubagentTargets,
7865
+ "failed",
7866
+ error.message,
7867
+ excludeLocalAgents,
7868
+ subagentsSourcePath
7869
+ ),
7870
+ commands: buildCommandSummary(
7871
+ commandsSourcePath,
7872
+ selectedCommandTargets,
7873
+ "failed",
7874
+ error.message,
7875
+ excludeLocalCommands
7876
+ ),
7877
+ hadFailures: true,
7878
+ missingIgnoreRules: false,
7879
+ syncRun: buildSyncRunMetadata({
7880
+ runtime: scriptRuntime,
7881
+ status: "failed",
7882
+ partialOutputsWritten
7883
+ })
7884
+ };
7885
+ emitSyncSummary({
7886
+ combined: combined2,
7887
+ jsonOutput,
7888
+ repoRoot,
7889
+ agentsDir,
7890
+ ignoreRules: null
7891
+ });
7892
+ return;
7893
+ }
7894
+ throw error;
7895
+ }
6863
7896
  let missingIgnoreRules = false;
6864
7897
  let ignoreRules = null;
6865
7898
  if (hasLocalItems) {
@@ -6892,6 +7925,7 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
6892
7925
  }
6893
7926
  }
6894
7927
  }
7928
+ partialOutputsWritten = true;
6895
7929
  let commandsSummary;
6896
7930
  if (selectedCommandTargets.length === 0) {
6897
7931
  commandsSummary = {
@@ -6930,7 +7964,8 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
6930
7964
  validAgents,
6931
7965
  excludeLocal: excludeLocalCommands,
6932
7966
  resolveTargetName,
6933
- hooks: globalHooks
7967
+ hooks: globalHooks,
7968
+ templateScriptRuntime: scriptRuntime
6934
7969
  });
6935
7970
  }
6936
7971
  if (availabilityCommandSkips.length > 0) {
@@ -6951,7 +7986,8 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
6951
7986
  excludeLocal: excludeLocalAgents,
6952
7987
  includeLocalSkills,
6953
7988
  resolveTargetName,
6954
- hooks: globalHooks
7989
+ hooks: globalHooks,
7990
+ templateScriptRuntime: scriptRuntime
6955
7991
  });
6956
7992
  if (availabilitySubagentSkips.length > 0) {
6957
7993
  const subagentWarnings = mergeWarnings(
@@ -7000,6 +8036,7 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
7000
8036
  validAgents,
7001
8037
  resolveTargetName,
7002
8038
  hooks: globalHooks,
8039
+ templateScriptRuntime: scriptRuntime,
7003
8040
  confirmRemoval
7004
8041
  });
7005
8042
  } catch (error) {
@@ -7057,7 +8094,8 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
7057
8094
  excludeLocal: excludeLocalSkills,
7058
8095
  removeMissing,
7059
8096
  resolveTargetName,
7060
- hooks: globalHooks
8097
+ hooks: globalHooks,
8098
+ templateScriptRuntime: scriptRuntime
7061
8099
  });
7062
8100
  }
7063
8101
  if (availabilitySkillSkips.length > 0) {
@@ -7071,13 +8109,19 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
7071
8109
  results: [...skillsSummary.results, ...availabilitySkillResults]
7072
8110
  };
7073
8111
  }
8112
+ const hadFailures = instructionsSummary.hadFailures || skillsSummary.hadFailures || subagentSummary.hadFailures || commandsSummary.hadFailures;
7074
8113
  const combined = {
7075
8114
  instructions: instructionsSummary,
7076
8115
  skills: skillsSummary,
7077
8116
  subagents: subagentSummary,
7078
8117
  commands: commandsSummary,
7079
- hadFailures: instructionsSummary.hadFailures || skillsSummary.hadFailures || subagentSummary.hadFailures || commandsSummary.hadFailures,
7080
- missingIgnoreRules
8118
+ hadFailures,
8119
+ missingIgnoreRules,
8120
+ syncRun: buildSyncRunMetadata({
8121
+ runtime: scriptRuntime,
8122
+ status: hadFailures ? "failed" : "completed",
8123
+ partialOutputsWritten
8124
+ })
7081
8125
  };
7082
8126
  emitSyncSummary({
7083
8127
  combined,
@@ -7092,6 +8136,11 @@ Config: auto-discovered as omniagent.config.(ts|mts|cts|js|mjs|cjs) in the agent
7092
8136
  process.exit(1);
7093
8137
  return;
7094
8138
  }
8139
+ if (error instanceof TemplateScriptExecutionError) {
8140
+ console.error(`Error: ${error.message}`);
8141
+ process.exit(1);
8142
+ return;
8143
+ }
7095
8144
  throw error;
7096
8145
  }
7097
8146
  }
@@ -7745,8 +8794,28 @@ async function runShim(argv, runtime = {}) {
7745
8794
  return exitCodeFor("execution-error");
7746
8795
  }
7747
8796
  }
7748
- const VERSION = "0.1.0";
7749
- const KNOWN_COMMANDS = /* @__PURE__ */ new Set(["hello", "greet", "echo", "sync"]);
8797
+ function resolveVersion() {
8798
+ const packageJsonPaths = [
8799
+ fileURLToPath(new URL("../package.json", import.meta.url)),
8800
+ fileURLToPath(new URL("../../package.json", import.meta.url))
8801
+ ];
8802
+ for (const packageJsonPath of packageJsonPaths) {
8803
+ if (!existsSync(packageJsonPath)) {
8804
+ continue;
8805
+ }
8806
+ try {
8807
+ const contents = readFileSync(packageJsonPath, "utf8");
8808
+ const parsed = JSON.parse(contents);
8809
+ if (typeof parsed.version === "string" && parsed.version.trim().length > 0) {
8810
+ return parsed.version;
8811
+ }
8812
+ } catch {
8813
+ }
8814
+ }
8815
+ return "0.0.0";
8816
+ }
8817
+ const VERSION = resolveVersion();
8818
+ const KNOWN_COMMANDS = /* @__PURE__ */ new Set(["hello", "greet", "echo", "sync", "dev"]);
7750
8819
  const SHIM_CAPABILITIES = [
7751
8820
  "Capabilities by agent:",
7752
8821
  " codex: approval, sandbox, output, model, web",
@@ -7796,7 +8865,7 @@ function runCli(argv = process.argv, options = {}) {
7796
8865
  console.error(formatError(message, args));
7797
8866
  const exitCode = isCommandInvocation(args) ? 1 : 2;
7798
8867
  process.exit(exitCode);
7799
- }).command(helloCommand).command(greetCommand).command(echoCommand).command(syncCommand).command(
8868
+ }).command(helloCommand).command(greetCommand).command(echoCommand).command(syncCommand).command(devCommand).command(
7800
8869
  "$0",
7801
8870
  "omniagent CLI",
7802
8871
  (yargsInstance) => yargsInstance.usage("omniagent [flags] --agent <target-id> [-- <agent flags>]").example("omniagent --agent codex", "Start an interactive session (default mode).").example('omniagent -p "Summarize the repo" --agent codex', "Run a one-shot prompt.").example("omniagent --agent codex -- --some-flag", "Pass through agent-specific flags.").option("prompt", {